@vertesia/ui 1.4.0 → 1.5.0-dev.20260713.113008Z

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 (344) hide show
  1. package/lib/core/components/InputList.d.ts.map +1 -1
  2. package/lib/core/components/InputList.js +3 -2
  3. package/lib/core/components/InputList.js.map +1 -1
  4. package/lib/core/components/SelectList.d.ts.map +1 -1
  5. package/lib/core/components/SelectList.js +1 -1
  6. package/lib/core/components/SelectList.js.map +1 -1
  7. package/lib/core/components/TagsInput.d.ts.map +1 -1
  8. package/lib/core/components/TagsInput.js +36 -4
  9. package/lib/core/components/TagsInput.js.map +1 -1
  10. package/lib/core/components/popup/Popup.js +2 -1
  11. package/lib/core/components/popup/Popup.js.map +1 -1
  12. package/lib/core/components/popup/position.d.ts.map +1 -1
  13. package/lib/core/components/popup/position.js +3 -6
  14. package/lib/core/components/popup/position.js.map +1 -1
  15. package/lib/core/components/shadcn/badge.d.ts +2 -2
  16. package/lib/core/components/shadcn/badge.d.ts.map +1 -1
  17. package/lib/core/components/shadcn/badge.js +3 -0
  18. package/lib/core/components/shadcn/badge.js.map +1 -1
  19. package/lib/core/components/shadcn/breadcrumb.d.ts.map +1 -1
  20. package/lib/core/components/shadcn/breadcrumb.js +2 -2
  21. package/lib/core/components/shadcn/breadcrumb.js.map +1 -1
  22. package/lib/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
  23. package/lib/core/components/shadcn/filters/filterBar.js +1 -3
  24. package/lib/core/components/shadcn/filters/filterBar.js.map +1 -1
  25. package/lib/core/components/shadcn/resizeable.d.ts +1 -1
  26. package/lib/core/components/shadcn/resizeable.d.ts.map +1 -1
  27. package/lib/core/components/shadcn/resizeable.js +12 -2
  28. package/lib/core/components/shadcn/resizeable.js.map +1 -1
  29. package/lib/core/components/shadcn/selectBox.d.ts +7 -1
  30. package/lib/core/components/shadcn/selectBox.d.ts.map +1 -1
  31. package/lib/core/components/shadcn/selectBox.js +29 -12
  32. package/lib/core/components/shadcn/selectBox.js.map +1 -1
  33. package/lib/core/components/table/index.d.ts.map +1 -1
  34. package/lib/core/components/table/index.js +1 -1
  35. package/lib/core/components/table/index.js.map +1 -1
  36. package/lib/core/hooks/useIntersectionObserver.d.ts.map +1 -1
  37. package/lib/core/hooks/useIntersectionObserver.js +3 -1
  38. package/lib/core/hooks/useIntersectionObserver.js.map +1 -1
  39. package/lib/features/agent/chat/AgentRightPanel.d.ts.map +1 -1
  40. package/lib/features/agent/chat/AgentRightPanel.js +3 -6
  41. package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
  42. package/lib/features/agent/chat/AnimatedThinkingDots.d.ts.map +1 -1
  43. package/lib/features/agent/chat/AnimatedThinkingDots.js +1 -1
  44. package/lib/features/agent/chat/AnimatedThinkingDots.js.map +1 -1
  45. package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
  46. package/lib/features/agent/chat/AskUserWidget.js +4 -3
  47. package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
  48. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  49. package/lib/features/agent/chat/ModernAgentConversation.js +56 -11
  50. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  51. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +8 -1
  52. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  53. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +40 -31
  54. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  55. package/lib/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.js +3 -3
  56. package/lib/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.js.map +1 -1
  57. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
  58. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +5 -1
  59. package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
  60. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +3 -7
  61. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  62. package/lib/features/agent/chat/ModernAgentOutput/PlanPanel.d.ts.map +1 -1
  63. package/lib/features/agent/chat/ModernAgentOutput/PlanPanel.js +5 -5
  64. package/lib/features/agent/chat/ModernAgentOutput/PlanPanel.js.map +1 -1
  65. package/lib/features/agent/chat/ModernAgentOutput/SlidingPlanPanel.d.ts.map +1 -1
  66. package/lib/features/agent/chat/ModernAgentOutput/SlidingPlanPanel.js +4 -4
  67. package/lib/features/agent/chat/ModernAgentOutput/SlidingPlanPanel.js.map +1 -1
  68. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.d.ts.map +1 -1
  69. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js +3 -3
  70. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js.map +1 -1
  71. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.d.ts.map +1 -1
  72. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js +156 -8
  73. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js.map +1 -1
  74. package/lib/features/agent/chat/hooks/useFileProcessing.d.ts +1 -0
  75. package/lib/features/agent/chat/hooks/useFileProcessing.d.ts.map +1 -1
  76. package/lib/features/agent/chat/hooks/useFileProcessing.js +37 -3
  77. package/lib/features/agent/chat/hooks/useFileProcessing.js.map +1 -1
  78. package/lib/features/facets/AgentRunnerFacetsNav.d.ts.map +1 -1
  79. package/lib/features/facets/AgentRunnerFacetsNav.js +1 -1
  80. package/lib/features/facets/AgentRunnerFacetsNav.js.map +1 -1
  81. package/lib/features/facets/InteractionsFacetsNav.d.ts +10 -1
  82. package/lib/features/facets/InteractionsFacetsNav.d.ts.map +1 -1
  83. package/lib/features/facets/InteractionsFacetsNav.js +14 -5
  84. package/lib/features/facets/InteractionsFacetsNav.js.map +1 -1
  85. package/lib/features/facets/PromptsFacetsNav.d.ts.map +1 -1
  86. package/lib/features/facets/PromptsFacetsNav.js +12 -1
  87. package/lib/features/facets/PromptsFacetsNav.js.map +1 -1
  88. package/lib/features/facets/RunsFacetsNav.d.ts.map +1 -1
  89. package/lib/features/facets/RunsFacetsNav.js +1 -1
  90. package/lib/features/facets/RunsFacetsNav.js.map +1 -1
  91. package/lib/features/magic-pdf/AnnotatedImageSlider.d.ts.map +1 -1
  92. package/lib/features/magic-pdf/AnnotatedImageSlider.js +1 -3
  93. package/lib/features/magic-pdf/AnnotatedImageSlider.js.map +1 -1
  94. package/lib/features/magic-pdf/MagicPdfProvider.js +2 -3
  95. package/lib/features/magic-pdf/MagicPdfProvider.js.map +1 -1
  96. package/lib/features/store/objects/DocumentSearchResults.d.ts +6 -2
  97. package/lib/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
  98. package/lib/features/store/objects/DocumentSearchResults.js +6 -6
  99. package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
  100. package/lib/features/store/objects/components/ContentOverview.js +1 -1
  101. package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
  102. package/lib/features/store/objects/components/DocumentIcon.d.ts.map +1 -1
  103. package/lib/features/store/objects/components/DocumentIcon.js +1 -1
  104. package/lib/features/store/objects/components/DocumentIcon.js.map +1 -1
  105. package/lib/features/store/objects/components/DocumentInput.js.map +1 -1
  106. package/lib/features/store/objects/search/DocumentSearchContext.d.ts +4 -4
  107. package/lib/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
  108. package/lib/features/store/objects/search/DocumentSearchContext.js +1 -1
  109. package/lib/features/store/objects/search/DocumentSearchContext.js.map +1 -1
  110. package/lib/features/store/objects/selection/ObjectsActionContext.d.ts.map +1 -1
  111. package/lib/features/store/objects/selection/ObjectsActionContext.js +2 -25
  112. package/lib/features/store/objects/selection/ObjectsActionContext.js.map +1 -1
  113. package/lib/features/store/objects/selection/ObjectsActionContextClass.d.ts +0 -2
  114. package/lib/features/store/objects/selection/ObjectsActionContextClass.d.ts.map +1 -1
  115. package/lib/features/store/objects/selection/ObjectsActionContextClass.js +1 -7
  116. package/lib/features/store/objects/selection/ObjectsActionContextClass.js.map +1 -1
  117. package/lib/features/store/objects/selection/ObjectsActionHooks.d.ts +0 -1
  118. package/lib/features/store/objects/selection/ObjectsActionHooks.d.ts.map +1 -1
  119. package/lib/features/store/objects/selection/ObjectsActionHooks.js +0 -10
  120. package/lib/features/store/objects/selection/ObjectsActionHooks.js.map +1 -1
  121. package/lib/features/store/objects/selection/SelectionActions.d.ts +4 -0
  122. package/lib/features/store/objects/selection/SelectionActions.d.ts.map +1 -1
  123. package/lib/features/store/objects/selection/SelectionActions.js +4 -18
  124. package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
  125. package/lib/features/store/objects/selection/actions/DeleteObjectsAction.d.ts.map +1 -1
  126. package/lib/features/store/objects/selection/actions/DeleteObjectsAction.js +16 -7
  127. package/lib/features/store/objects/selection/actions/DeleteObjectsAction.js.map +1 -1
  128. package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts +7 -0
  129. package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts.map +1 -0
  130. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +51 -0
  131. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +1 -0
  132. package/lib/features/store/objects/selection/actions/index.d.ts +0 -1
  133. package/lib/features/store/objects/selection/actions/index.d.ts.map +1 -1
  134. package/lib/features/store/objects/selection/actions/index.js +0 -1
  135. package/lib/features/store/objects/selection/actions/index.js.map +1 -1
  136. package/lib/features/store/types/ContentObjectTypesSearch.d.ts.map +1 -1
  137. package/lib/features/store/types/ContentObjectTypesSearch.js +55 -3
  138. package/lib/features/store/types/ContentObjectTypesSearch.js.map +1 -1
  139. package/lib/features/store/types/ContentObjectTypesTable.d.ts +5 -1
  140. package/lib/features/store/types/ContentObjectTypesTable.d.ts.map +1 -1
  141. package/lib/features/store/types/ContentObjectTypesTable.js +4 -2
  142. package/lib/features/store/types/ContentObjectTypesTable.js.map +1 -1
  143. package/lib/features/store/types/search/ObjectTypeSearchContext.js +1 -1
  144. package/lib/features/store/types/search/ObjectTypeSearchContext.js.map +1 -1
  145. package/lib/i18n/locales/ar.json +17 -0
  146. package/lib/i18n/locales/de.json +17 -0
  147. package/lib/i18n/locales/en.json +33 -0
  148. package/lib/i18n/locales/es.json +17 -0
  149. package/lib/i18n/locales/fr.json +17 -0
  150. package/lib/i18n/locales/it.json +17 -0
  151. package/lib/i18n/locales/ja.json +17 -0
  152. package/lib/i18n/locales/ko.json +17 -0
  153. package/lib/i18n/locales/pt.json +17 -0
  154. package/lib/i18n/locales/ru.json +17 -0
  155. package/lib/i18n/locales/tr.json +17 -0
  156. package/lib/i18n/locales/zh-TW.json +17 -0
  157. package/lib/i18n/locales/zh.json +17 -0
  158. package/lib/layout/Sidebar.d.ts +5 -1
  159. package/lib/layout/Sidebar.d.ts.map +1 -1
  160. package/lib/layout/Sidebar.js.map +1 -1
  161. package/lib/router/NestedRouterProvider.d.ts +1 -1
  162. package/lib/router/NestedRouterProvider.d.ts.map +1 -1
  163. package/lib/router/NestedRouterProvider.js +2 -2
  164. package/lib/router/NestedRouterProvider.js.map +1 -1
  165. package/lib/router/Redirect.d.ts +13 -0
  166. package/lib/router/Redirect.d.ts.map +1 -0
  167. package/lib/router/Redirect.js +20 -0
  168. package/lib/router/Redirect.js.map +1 -0
  169. package/lib/router/Router.d.ts.map +1 -1
  170. package/lib/router/Router.js +1 -2
  171. package/lib/router/Router.js.map +1 -1
  172. package/lib/router/index.d.ts +1 -0
  173. package/lib/router/index.d.ts.map +1 -1
  174. package/lib/router/index.js +1 -0
  175. package/lib/router/index.js.map +1 -1
  176. package/lib/session/DevSessionProvider.d.ts +8 -0
  177. package/lib/session/DevSessionProvider.d.ts.map +1 -0
  178. package/lib/session/DevSessionProvider.js +33 -0
  179. package/lib/session/DevSessionProvider.js.map +1 -0
  180. package/lib/session/UserSessionProvider.d.ts.map +1 -1
  181. package/lib/session/UserSessionProvider.js +16 -2
  182. package/lib/session/UserSessionProvider.js.map +1 -1
  183. package/lib/session/auth/composable.d.ts +10 -1
  184. package/lib/session/auth/composable.d.ts.map +1 -1
  185. package/lib/session/auth/composable.js +38 -2
  186. package/lib/session/auth/composable.js.map +1 -1
  187. package/lib/session/index.d.ts +1 -0
  188. package/lib/session/index.d.ts.map +1 -1
  189. package/lib/session/index.js +1 -0
  190. package/lib/session/index.js.map +1 -1
  191. package/lib/shell/VertesiaShell.d.ts +3 -1
  192. package/lib/shell/VertesiaShell.d.ts.map +1 -1
  193. package/lib/shell/VertesiaShell.js +5 -4
  194. package/lib/shell/VertesiaShell.js.map +1 -1
  195. package/lib/shell/login/SignInRestrictedEnvStep.d.ts +14 -0
  196. package/lib/shell/login/SignInRestrictedEnvStep.d.ts.map +1 -0
  197. package/lib/shell/login/SignInRestrictedEnvStep.js +50 -0
  198. package/lib/shell/login/SignInRestrictedEnvStep.js.map +1 -0
  199. package/lib/shell/login/SigninScreen.d.ts.map +1 -1
  200. package/lib/shell/login/SigninScreen.js +16 -3
  201. package/lib/shell/login/SigninScreen.js.map +1 -1
  202. package/lib/shell/login/productionUrl.d.ts +16 -0
  203. package/lib/shell/login/productionUrl.d.ts.map +1 -0
  204. package/lib/shell/login/productionUrl.js +19 -0
  205. package/lib/shell/login/productionUrl.js.map +1 -0
  206. package/lib/shell/login/signInUtils.d.ts.map +1 -1
  207. package/lib/shell/login/signInUtils.js +14 -1
  208. package/lib/shell/login/signInUtils.js.map +1 -1
  209. package/lib/vertesia-ui-core.js +95 -2
  210. package/lib/vertesia-ui-core.js.map +1 -1
  211. package/lib/vertesia-ui-env.js +2 -2
  212. package/lib/vertesia-ui-env.js.map +1 -1
  213. package/lib/vertesia-ui-features.js +454 -2
  214. package/lib/vertesia-ui-features.js.map +1 -1
  215. package/lib/vertesia-ui-i18n.js +41 -2
  216. package/lib/vertesia-ui-i18n.js.map +1 -1
  217. package/lib/vertesia-ui-layout.js +2 -2
  218. package/lib/vertesia-ui-layout.js.map +1 -1
  219. package/lib/vertesia-ui-router.js +2 -2
  220. package/lib/vertesia-ui-router.js.map +1 -1
  221. package/lib/vertesia-ui-session.js +2 -2
  222. package/lib/vertesia-ui-session.js.map +1 -1
  223. package/lib/vertesia-ui-shell.js +2 -2
  224. package/lib/vertesia-ui-shell.js.map +1 -1
  225. package/lib/vertesia-ui-widgets.js +9 -2
  226. package/lib/vertesia-ui-widgets.js.map +1 -1
  227. package/lib/widgets/json-view/JSONCode.d.ts.map +1 -1
  228. package/lib/widgets/json-view/JSONCode.js +1 -1
  229. package/lib/widgets/json-view/JSONCode.js.map +1 -1
  230. package/lib/widgets/json-view/JSONTable.d.ts.map +1 -1
  231. package/lib/widgets/json-view/JSONTable.js +1 -1
  232. package/lib/widgets/json-view/JSONTable.js.map +1 -1
  233. package/lib/widgets/markdown/ArtifactContentRenderer.d.ts.map +1 -1
  234. package/lib/widgets/markdown/ArtifactContentRenderer.js +1 -1
  235. package/lib/widgets/markdown/ArtifactContentRenderer.js.map +1 -1
  236. package/lib/widgets/markdown/CodeBlockPlaceholder.d.ts.map +1 -1
  237. package/lib/widgets/markdown/CodeBlockPlaceholder.js +1 -1
  238. package/lib/widgets/markdown/CodeBlockPlaceholder.js.map +1 -1
  239. package/lib/widgets/monacoEditor/foldingProviders.d.ts.map +1 -1
  240. package/lib/widgets/monacoEditor/foldingProviders.js +5 -10
  241. package/lib/widgets/monacoEditor/foldingProviders.js.map +1 -1
  242. package/lib/widgets/schema-editor/ManagedSchema.d.ts.map +1 -1
  243. package/lib/widgets/schema-editor/ManagedSchema.js +1 -2
  244. package/lib/widgets/schema-editor/ManagedSchema.js.map +1 -1
  245. package/lib/widgets/upload/UploadResultCategory.d.ts.map +1 -1
  246. package/lib/widgets/upload/UploadResultCategory.js +1 -1
  247. package/lib/widgets/upload/UploadResultCategory.js.map +1 -1
  248. package/lib/widgets/xml-viewer/components/TextElement/index.d.ts.map +1 -1
  249. package/lib/widgets/xml-viewer/components/TextElement/index.js +1 -1
  250. package/lib/widgets/xml-viewer/components/TextElement/index.js.map +1 -1
  251. package/package.json +38 -43
  252. package/src/core/components/InputList.tsx +5 -10
  253. package/src/core/components/SelectList.tsx +7 -2
  254. package/src/core/components/TagsInput.tsx +41 -4
  255. package/src/core/components/popup/Popup.tsx +3 -3
  256. package/src/core/components/popup/position.ts +3 -6
  257. package/src/core/components/shadcn/badge.tsx +3 -0
  258. package/src/core/components/shadcn/breadcrumb.tsx +2 -4
  259. package/src/core/components/shadcn/filters/filterBar.tsx +2 -3
  260. package/src/core/components/shadcn/resizeable.test.tsx +77 -0
  261. package/src/core/components/shadcn/resizeable.tsx +28 -2
  262. package/src/core/components/shadcn/selectBox.tsx +70 -36
  263. package/src/core/components/table/index.tsx +1 -2
  264. package/src/core/hooks/useIntersectionObserver.tsx +3 -1
  265. package/src/features/agent/chat/AgentRightPanel.tsx +5 -6
  266. package/src/features/agent/chat/AnimatedThinkingDots.tsx +1 -2
  267. package/src/features/agent/chat/AskUserWidget.test.tsx +49 -0
  268. package/src/features/agent/chat/AskUserWidget.tsx +32 -22
  269. package/src/features/agent/chat/ModernAgentConversation.test.tsx +198 -1
  270. package/src/features/agent/chat/ModernAgentConversation.tsx +62 -15
  271. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +60 -34
  272. package/src/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.tsx +3 -3
  273. package/src/features/agent/chat/ModernAgentOutput/MessageInput.test.tsx +25 -0
  274. package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +5 -1
  275. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +30 -30
  276. package/src/features/agent/chat/ModernAgentOutput/PlanPanel.tsx +5 -6
  277. package/src/features/agent/chat/ModernAgentOutput/SlidingPlanPanel.tsx +4 -5
  278. package/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx +3 -5
  279. package/src/features/agent/chat/ModernAgentOutput/WorkstreamTabs.tsx +326 -42
  280. package/src/features/agent/chat/hooks/useFileProcessing.test.tsx +70 -0
  281. package/src/features/agent/chat/hooks/useFileProcessing.ts +37 -6
  282. package/src/features/facets/AgentRunnerFacetsNav.tsx +1 -2
  283. package/src/features/facets/InteractionsFacetsNav.tsx +30 -5
  284. package/src/features/facets/PromptsFacetsNav.tsx +13 -1
  285. package/src/features/facets/RunsFacetsNav.tsx +1 -2
  286. package/src/features/magic-pdf/AnnotatedImageSlider.tsx +1 -2
  287. package/src/features/magic-pdf/MagicPdfProvider.tsx +4 -4
  288. package/src/features/store/objects/DocumentSearchResults.tsx +11 -1
  289. package/src/features/store/objects/components/ContentOverview.tsx +1 -2
  290. package/src/features/store/objects/components/DocumentIcon.tsx +1 -2
  291. package/src/features/store/objects/components/DocumentInput.tsx +2 -2
  292. package/src/features/store/objects/search/DocumentSearchContext.test.ts +2 -2
  293. package/src/features/store/objects/search/DocumentSearchContext.ts +3 -3
  294. package/src/features/store/objects/selection/ObjectsActionContext.tsx +2 -27
  295. package/src/features/store/objects/selection/ObjectsActionContextClass.ts +1 -7
  296. package/src/features/store/objects/selection/ObjectsActionHooks.ts +0 -11
  297. package/src/features/store/objects/selection/SelectionActions.tsx +9 -34
  298. package/src/features/store/objects/selection/actions/DeleteObjectsAction.tsx +40 -7
  299. package/src/features/store/objects/selection/actions/StartWorkflowButton.tsx +104 -0
  300. package/src/features/store/objects/selection/actions/index.ts +0 -1
  301. package/src/features/store/types/ContentObjectTypesSearch.tsx +110 -1
  302. package/src/features/store/types/ContentObjectTypesTable.tsx +42 -3
  303. package/src/features/store/types/search/ObjectTypeSearchContext.tsx +1 -1
  304. package/src/i18n/locales/ar.json +17 -0
  305. package/src/i18n/locales/de.json +17 -0
  306. package/src/i18n/locales/en.json +33 -0
  307. package/src/i18n/locales/es.json +17 -0
  308. package/src/i18n/locales/fr.json +17 -0
  309. package/src/i18n/locales/it.json +17 -0
  310. package/src/i18n/locales/ja.json +17 -0
  311. package/src/i18n/locales/ko.json +17 -0
  312. package/src/i18n/locales/pt.json +17 -0
  313. package/src/i18n/locales/ru.json +17 -0
  314. package/src/i18n/locales/tr.json +17 -0
  315. package/src/i18n/locales/zh-TW.json +17 -0
  316. package/src/i18n/locales/zh.json +17 -0
  317. package/src/layout/Sidebar.tsx +6 -1
  318. package/src/router/NestedRouterProvider.tsx +2 -2
  319. package/src/router/Redirect.tsx +29 -0
  320. package/src/router/Router.tsx +1 -2
  321. package/src/router/index.ts +1 -0
  322. package/src/session/DevSessionProvider.tsx +46 -0
  323. package/src/session/UserSessionProvider.tsx +18 -2
  324. package/src/session/auth/composable.ts +41 -3
  325. package/src/session/index.ts +1 -0
  326. package/src/shell/VertesiaShell.tsx +27 -14
  327. package/src/shell/login/SignInRestrictedEnvStep.tsx +87 -0
  328. package/src/shell/login/SigninScreen.tsx +29 -16
  329. package/src/shell/login/productionUrl.test.ts +19 -0
  330. package/src/shell/login/productionUrl.ts +18 -0
  331. package/src/shell/login/signInUtils.ts +14 -1
  332. package/src/widgets/json-view/JSONCode.tsx +1 -2
  333. package/src/widgets/json-view/JSONTable.tsx +1 -2
  334. package/src/widgets/markdown/ArtifactContentRenderer.tsx +4 -7
  335. package/src/widgets/markdown/CodeBlockPlaceholder.tsx +1 -2
  336. package/src/widgets/monacoEditor/foldingProviders.ts +10 -15
  337. package/src/widgets/schema-editor/ManagedSchema.ts +1 -2
  338. package/src/widgets/upload/UploadResultCategory.tsx +2 -3
  339. package/src/widgets/xml-viewer/components/TextElement/index.tsx +1 -2
  340. package/lib/features/store/objects/selection/actions/StartWorkflowComponent.d.ts +0 -4
  341. package/lib/features/store/objects/selection/actions/StartWorkflowComponent.d.ts.map +0 -1
  342. package/lib/features/store/objects/selection/actions/StartWorkflowComponent.js +0 -77
  343. package/lib/features/store/objects/selection/actions/StartWorkflowComponent.js.map +0 -1
  344. package/src/features/store/objects/selection/actions/StartWorkflowComponent.tsx +0 -127
@@ -54,6 +54,31 @@ describe('MessageInput', () => {
54
54
  expect(onFilesSelected).toHaveBeenCalledWith([file]);
55
55
  });
56
56
 
57
+ it('does not send or clear the text when Enter is pressed while files are still processing', () => {
58
+ const onSend = vi.fn();
59
+ renderWithProviders(<MessageInput onSend={onSend} onFilesSelected={vi.fn()} hasProcessingFiles />);
60
+
61
+ const textbox = screen.getByRole('textbox') as HTMLTextAreaElement;
62
+ fireEvent.change(textbox, { target: { value: 'please analyze this' } });
63
+ fireEvent.keyDown(textbox, { key: 'Enter' });
64
+
65
+ expect(onSend).not.toHaveBeenCalled();
66
+ // The typed text must be retained so the user doesn't have to re-type it.
67
+ expect(textbox.value).toBe('please analyze this');
68
+ });
69
+
70
+ it('sends and clears the text on Enter once files have finished processing', () => {
71
+ const onSend = vi.fn();
72
+ renderWithProviders(<MessageInput onSend={onSend} onFilesSelected={vi.fn()} />);
73
+
74
+ const textbox = screen.getByRole('textbox') as HTMLTextAreaElement;
75
+ fireEvent.change(textbox, { target: { value: 'please analyze this' } });
76
+ fireEvent.keyDown(textbox, { key: 'Enter' });
77
+
78
+ expect(onSend).toHaveBeenCalledWith('please analyze this');
79
+ expect(textbox.value).toBe('');
80
+ });
81
+
57
82
  it('renders attachment actions before the MCP slot', () => {
58
83
  renderWithProviders(<MessageInput onSend={vi.fn()} mcpSlot={<button type="button">MCP</button>} isCompleted />);
59
84
 
@@ -346,7 +346,11 @@ export default function MessageInput({
346
346
 
347
347
  const handleSend = () => {
348
348
  const message = value.trim();
349
- if (!message || disabled || isSending) return;
349
+ // Mirror the send button's disabled condition so the keyboard (Enter) path can't
350
+ // dispatch a send that the parent will reject while files are still uploading —
351
+ // otherwise the text would be cleared below and lost. The disabled button + its
352
+ // "wait for files" tooltip already explain why Enter does nothing here.
353
+ if (!message || disabled || isSending || hasProcessingFiles) return;
350
354
 
351
355
  onSend(message);
352
356
  setValue('');
@@ -359,6 +359,10 @@ function MessageItemComponent({
359
359
  // Check if message has exportable content (markdown text)
360
360
  const hasExportableContent = typeof messageContent === 'string' && messageContent.trim().length > 0;
361
361
 
362
+ // UX config for REQUEST_INPUT messages (narrowed const so it stays typed inside the JSX closures below)
363
+ const askUserUx =
364
+ message.type === AgentMessageType.REQUEST_INPUT ? (message.details as AskUserMessageDetails)?.ux : undefined;
365
+
362
366
  // PERFORMANCE: Memoize markdown components to prevent MarkdownRenderer remounts
363
367
  const markdownComponents = useMemo(
364
368
  () => ({
@@ -639,36 +643,32 @@ function MessageItemComponent({
639
643
  )}
640
644
  >
641
645
  {/* Check for REQUEST_INPUT with UX config - render AskUserWidget instead of plain text */}
642
- {message.type === AgentMessageType.REQUEST_INPUT && (message.details as AskUserMessageDetails)?.ux
643
- ? (() => {
644
- // biome-ignore lint/style/noNonNullAssertion: intentional non-null assertion; TS can't prove narrowing here
645
- const uxConfig = (message.details as AskUserMessageDetails).ux!;
646
- return (
647
- <AskUserWidget
648
- question={typeof messageContent === 'string' ? messageContent : ''}
649
- options={uxConfig.options}
650
- variant={uxConfig.variant}
651
- multiSelect={uxConfig.multiSelect}
652
- onSelect={(optionId) => onSendMessage?.(optionId)}
653
- onMultiSelect={(optionIds) => onSendMessage?.(optionIds.join(', '))}
654
- allowFreeResponse={!uxConfig.options?.length || !!uxConfig.free_response}
655
- placeholder={uxConfig.free_response?.placeholder}
656
- submitLabel={uxConfig.free_response?.submit_label}
657
- onSubmit={(value) => onSendMessage?.(value, uxConfig.free_response?.metadata)}
658
- hideBorder
659
- compact
660
- answered={requestInputAnswered}
661
- />
662
- );
663
- })()
664
- : visibleMessageContent && (
665
- <div
666
- className="message-content break-words w-full"
667
- style={{ overflowWrap: 'anywhere', wordBreak: 'break-word' }}
668
- >
669
- {renderContent(processedContent || visibleMessageContent)}
670
- </div>
671
- )}
646
+ {askUserUx ? (
647
+ <AskUserWidget
648
+ question={typeof messageContent === 'string' ? messageContent : ''}
649
+ options={askUserUx.options}
650
+ variant={askUserUx.variant}
651
+ multiSelect={askUserUx.multiSelect}
652
+ onSelect={(optionId) => onSendMessage?.(optionId)}
653
+ onMultiSelect={(optionIds) => onSendMessage?.(optionIds.join(', '))}
654
+ allowFreeResponse={!askUserUx.options?.length || !!askUserUx.free_response}
655
+ placeholder={askUserUx.free_response?.placeholder}
656
+ submitLabel={askUserUx.free_response?.submit_label}
657
+ onSubmit={(value) => onSendMessage?.(value, askUserUx.free_response?.metadata)}
658
+ hideBorder
659
+ compact
660
+ answered={requestInputAnswered}
661
+ />
662
+ ) : (
663
+ visibleMessageContent && (
664
+ <div
665
+ className="message-content break-words w-full"
666
+ style={{ overflowWrap: 'anywhere', wordBreak: 'break-word' }}
667
+ >
668
+ {renderContent(processedContent || visibleMessageContent)}
669
+ </div>
670
+ )
671
+ )}
672
672
 
673
673
  {messageAttachments.length > 0 && (
674
674
  <AttachmentPreviewList
@@ -20,16 +20,16 @@ export default function PlanPanel({ plan, workstreamStatus, isVisible }: PlanPan
20
20
  {/* Plan Steps */}
21
21
  {plan.plan && plan.plan.length > 0 ? (
22
22
  <div className="space-y-1.5">
23
- {plan.plan.map((task, index) => {
23
+ {plan.plan.map((task) => {
24
24
  // Extract task info
25
25
  const taskId = task.id.toString();
26
26
  const taskGoal = task.goal;
27
27
 
28
28
  // Determine task status - use task.status if available or lookup from workstream
29
29
  let status: 'pending' | 'in_progress' | 'completed' | 'skipped' = task.status || 'pending';
30
- if (workstreamStatus.has(taskId)) {
31
- // biome-ignore lint/style/noNonNullAssertion: intentional non-null assertion; TS can't prove narrowing here
32
- status = workstreamStatus.get(taskId)!;
30
+ const wsStatus = workstreamStatus.get(taskId);
31
+ if (wsStatus !== undefined) {
32
+ status = wsStatus;
33
33
  }
34
34
 
35
35
  // Determine status icon and style
@@ -45,8 +45,7 @@ export default function PlanPanel({ plan, workstreamStatus, isVisible }: PlanPan
45
45
  }
46
46
 
47
47
  return (
48
- // biome-ignore lint/suspicious/noArrayIndexKey: list order is stable for this render
49
- <div key={index} className="flex items-start">
48
+ <div key={taskId} className="flex items-start">
50
49
  <div className={`me-1.5 mt-0.5 ${statusColor}`}>
51
50
  <StatusIcon className="h-3.5 w-3.5" />
52
51
  </div>
@@ -61,9 +61,9 @@ export default function SlidingPlanPanel({ plan, workstreamStatus, isOpen, onClo
61
61
  // Determine task status - use task.status if available or lookup from workstream
62
62
  let status: 'pending' | 'in_progress' | 'completed' | 'skipped' =
63
63
  task.status || 'pending';
64
- if (workstreamStatus.has(taskId)) {
65
- // biome-ignore lint/style/noNonNullAssertion: intentional non-null assertion; TS can't prove narrowing here
66
- status = workstreamStatus.get(taskId)!;
64
+ const wsStatus = workstreamStatus.get(taskId);
65
+ if (wsStatus !== undefined) {
66
+ status = wsStatus;
67
67
  }
68
68
 
69
69
  // Determine status icon and style
@@ -81,8 +81,7 @@ export default function SlidingPlanPanel({ plan, workstreamStatus, isOpen, onClo
81
81
  }
82
82
 
83
83
  return (
84
- // biome-ignore lint/suspicious/noArrayIndexKey: list order is stable for this render
85
- <div key={index} className={`flex p-3 ${bgColor}`}>
84
+ <div key={`plan-step-${index}`} className={`flex p-3 ${bgColor}`}>
86
85
  <div className={`me-3 mt-0.5 flex-shrink-0 ${statusColor}`}>
87
86
  <StatusIcon className="h-4 w-4" />
88
87
  </div>
@@ -267,14 +267,13 @@ function FileDisplay({ files, className: fileClassName }: { files: string[]; cla
267
267
 
268
268
  return (
269
269
  <div className={cn('mt-2 flex flex-wrap gap-2', fileClassName)}>
270
- {files.map((file, idx) => {
270
+ {files.map((file) => {
271
271
  const fileName = file.split('/').pop()?.split('?')[0] || 'file';
272
272
  if (isImageUrl(file)) {
273
273
  return (
274
274
  <Button
275
275
  variant="unstyled"
276
- // biome-ignore lint/suspicious/noArrayIndexKey: list order is stable for this render
277
- key={idx}
276
+ key={file}
278
277
  className="cursor-pointer p-0"
279
278
  onClick={() => openImage(file, fileName)}
280
279
  title={t('agent.clickToEnlarge')}
@@ -290,8 +289,7 @@ function FileDisplay({ files, className: fileClassName }: { files: string[]; cla
290
289
  }
291
290
  return (
292
291
  <a
293
- // biome-ignore lint/suspicious/noArrayIndexKey: list order is stable for this render
294
- key={idx}
292
+ key={file}
295
293
  href={file}
296
294
  target="_blank"
297
295
  rel="noopener noreferrer"
@@ -1,7 +1,8 @@
1
1
  import type { AgentMessage } from '@vertesia/common';
2
- import { Button, cn } from '@vertesia/ui/core';
2
+ import { cn, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@vertesia/ui/core';
3
3
  import { i18nInstance, NAMESPACE, useUITranslation } from '@vertesia/ui/i18n';
4
- import { CheckCircle, Clock } from 'lucide-react';
4
+ import { CheckCircle, ChevronDown, Clock } from 'lucide-react';
5
+ import { useEffect, useLayoutEffect, useRef, useState } from 'react';
5
6
  import { getWorkstreamId } from './utils';
6
7
 
7
8
  interface WorkstreamTabsProps {
@@ -12,6 +13,322 @@ interface WorkstreamTabsProps {
12
13
  completionStatus?: Map<string, boolean>; // Optional completion status per workstream
13
14
  }
14
15
 
16
+ // Tab-item styling for the header workstream bar: filled `bg-info` active pill with
17
+ // count badges and completion icons (distinct from the right panel's underline tabs).
18
+ const TAB_ITEM_BASE =
19
+ 'flex items-center gap-1.5 px-2 py-1 text-xs font-medium whitespace-nowrap transition-colors border-b-2 shrink-0 cursor-pointer';
20
+ const TAB_ITEM_INACTIVE = 'border-transparent text-muted hover:bg-muted';
21
+ const TAB_ITEM_ACTIVE = 'border-info bg-info text-info';
22
+ const TAB_GAP_PX = 4; // matches the `gap-1` between tab items
23
+
24
+ // Shorten long workstream names for the tab row.
25
+ function truncateName(name: string) {
26
+ return name.length > 20 ? `${name.substring(0, 18)}...` : name;
27
+ }
28
+
29
+ type WorkstreamEntry = [id: string, name: string];
30
+
31
+ interface WorkstreamMoreMenuProps {
32
+ /** The overflowed workstream entries to list inside the menu. */
33
+ items: WorkstreamEntry[];
34
+ current: string;
35
+ onSelect: (id: string) => void;
36
+ label: string;
37
+ /** Whether the active workstream is one of the overflowed entries. */
38
+ active: boolean;
39
+ count?: Map<string, number>;
40
+ completionStatus?: Map<string, boolean>;
41
+ }
42
+
43
+ /** Trailing "More" dropdown of overflowed workstream tabs; opens on hover, click, or keyboard. */
44
+ function WorkstreamMoreMenu({
45
+ items,
46
+ current,
47
+ onSelect,
48
+ label,
49
+ active,
50
+ count,
51
+ completionStatus,
52
+ }: WorkstreamMoreMenuProps) {
53
+ const [open, setOpen] = useState(false);
54
+ const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
55
+ const openedByHover = useRef(false);
56
+
57
+ const cancelClose = () => {
58
+ if (closeTimer.current) {
59
+ clearTimeout(closeTimer.current);
60
+ closeTimer.current = null;
61
+ }
62
+ };
63
+ const openOnHover = () => {
64
+ cancelClose();
65
+ openedByHover.current = true;
66
+ setOpen(true);
67
+ };
68
+ // Delay the close so the pointer can travel across the gap onto the menu.
69
+ const closeAfterDelay = () => {
70
+ cancelClose();
71
+ closeTimer.current = setTimeout(() => setOpen(false), 150);
72
+ };
73
+
74
+ // Clear any pending close timer on unmount.
75
+ useEffect(() => () => clearTimeout(closeTimer.current ?? undefined), []);
76
+
77
+ return (
78
+ // Non-modal: a modal menu sets body `pointer-events: none` while open, which makes
79
+ // the hover open/close flap. It still closes on outside-click / Escape.
80
+ <DropdownMenu
81
+ modal={false}
82
+ open={open}
83
+ onOpenChange={(next) => {
84
+ // Fired by click / keyboard / dismiss (not by hover); keep our state in sync.
85
+ cancelClose();
86
+ if (next) openedByHover.current = false;
87
+ setOpen(next);
88
+ }}
89
+ >
90
+ <DropdownMenuTrigger asChild>
91
+ {/* Tab-bar primitive: raw button is the menu trigger (asChild). */}
92
+ <button
93
+ type="button"
94
+ onMouseEnter={openOnHover}
95
+ onMouseLeave={closeAfterDelay}
96
+ className={cn(TAB_ITEM_BASE, active ? TAB_ITEM_ACTIVE : TAB_ITEM_INACTIVE)}
97
+ >
98
+ {label}
99
+ <ChevronDown className="ms-0.5 size-3.5" />
100
+ </button>
101
+ </DropdownMenuTrigger>
102
+ <DropdownMenuContent
103
+ align="end"
104
+ className="w-max"
105
+ onMouseEnter={cancelClose}
106
+ onMouseLeave={closeAfterDelay}
107
+ onCloseAutoFocus={(e) => {
108
+ // Keep hover-opens from returning the focus ring to the trigger.
109
+ if (openedByHover.current) e.preventDefault();
110
+ }}
111
+ >
112
+ {items.map(([id, name]) => {
113
+ const showCount = (count?.get(id) ?? 0) > 0;
114
+ return (
115
+ <DropdownMenuItem
116
+ key={id}
117
+ onClick={() => onSelect(id)}
118
+ className={cn('flex items-center gap-2', id === current && 'text-info')}
119
+ >
120
+ <span className="truncate">{name}</span>
121
+ {showCount && (
122
+ <span className="ms-auto inline-flex items-center gap-1">
123
+ <span className="inline-flex items-center justify-center rounded-full bg-muted px-1.5 text-[10px] text-muted">
124
+ {count?.get(id)}
125
+ </span>
126
+ {completionStatus &&
127
+ id !== 'all' &&
128
+ (completionStatus.get(id) ? (
129
+ <CheckCircle className="size-3 text-success" />
130
+ ) : (
131
+ <Clock className="size-3 text-attention" />
132
+ ))}
133
+ </span>
134
+ )}
135
+ </DropdownMenuItem>
136
+ );
137
+ })}
138
+ </DropdownMenuContent>
139
+ </DropdownMenu>
140
+ );
141
+ }
142
+
143
+ interface WorkstreamOverflowBarProps {
144
+ entries: WorkstreamEntry[];
145
+ activeWorkstream: string;
146
+ onSelectWorkstream: (id: string) => void;
147
+ count?: Map<string, number>;
148
+ completionStatus?: Map<string, boolean>;
149
+ }
150
+
151
+ /**
152
+ * Workstream tabs as a horizontal row; any that don't fit collapse into a trailing
153
+ * "More" dropdown. The visible/overflow split is measured from a hidden full-width row,
154
+ * and the active tab is always promoted into view so it can never hide in the menu.
155
+ */
156
+ function WorkstreamOverflowBar({
157
+ entries,
158
+ activeWorkstream,
159
+ onSelectWorkstream,
160
+ count,
161
+ completionStatus,
162
+ }: WorkstreamOverflowBarProps) {
163
+ const { t } = useUITranslation();
164
+ const containerRef = useRef<HTMLDivElement | null>(null);
165
+ const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);
166
+ const moreRef = useRef<HTMLButtonElement | null>(null);
167
+ // `count` leading tabs are shown. When `promote` is set, the active tab is pulled
168
+ // into the last slot (before More) and `count` counts the tabs shown before it.
169
+ const [layout, setLayout] = useState<{ count: number; promote: boolean }>({
170
+ count: entries.length,
171
+ promote: false,
172
+ });
173
+
174
+ const recompute = () => {
175
+ const container = containerRef.current;
176
+ if (!container) return;
177
+ const containerWidth = container.clientWidth;
178
+ const widths = entries.map((_, i) => itemRefs.current[i]?.offsetWidth ?? 0);
179
+ const totalAll = widths.reduce((sum, w) => sum + w, 0) + TAB_GAP_PX * Math.max(0, entries.length - 1);
180
+
181
+ // How many tabs (in order, optionally skipping one) fit within `available`.
182
+ const fitCount = (available: number, skipIndex: number) => {
183
+ let used = 0;
184
+ let fitted = 0;
185
+ for (let i = 0; i < entries.length; i++) {
186
+ if (i === skipIndex) continue;
187
+ const cand = used + (fitted > 0 ? TAB_GAP_PX : 0) + widths[i];
188
+ if (cand > available) break;
189
+ used = cand;
190
+ fitted += 1;
191
+ }
192
+ return fitted;
193
+ };
194
+
195
+ let next: { count: number; promote: boolean };
196
+ if (totalAll <= containerWidth) {
197
+ next = { count: entries.length, promote: false };
198
+ } else {
199
+ const moreWidth = moreRef.current?.offsetWidth ?? 0;
200
+ const naturalCount = Math.max(1, fitCount(containerWidth - moreWidth - TAB_GAP_PX, -1));
201
+ const activeIndex = entries.findIndex(([id]) => id === activeWorkstream);
202
+ if (activeIndex < 0 || activeIndex < naturalCount) {
203
+ next = { count: naturalCount, promote: false };
204
+ } else {
205
+ // Active tab overflowed: reserve its slot at the end, fit leading tabs before it.
206
+ const leadAvailable = containerWidth - moreWidth - widths[activeIndex] - TAB_GAP_PX * 2;
207
+ next = { count: fitCount(leadAvailable, activeIndex), promote: true };
208
+ }
209
+ }
210
+ setLayout((prev) => (prev.count === next.count && prev.promote === next.promote ? prev : next));
211
+ };
212
+
213
+ // Re-measure after every render (tab/label changes) and on container resize.
214
+ const recomputeRef = useRef(recompute);
215
+ recomputeRef.current = recompute;
216
+ useLayoutEffect(() => {
217
+ recomputeRef.current();
218
+ });
219
+ useEffect(() => {
220
+ const container = containerRef.current;
221
+ if (!container || typeof ResizeObserver === 'undefined') return;
222
+ const observer = new ResizeObserver(() => recomputeRef.current());
223
+ observer.observe(container);
224
+ return () => observer.disconnect();
225
+ }, []);
226
+
227
+ const activeIndex = entries.findIndex(([id]) => id === activeWorkstream);
228
+ let visible: WorkstreamEntry[];
229
+ let overflow: WorkstreamEntry[];
230
+ if (layout.promote && activeIndex >= 0) {
231
+ // Pull the active tab into the last visible slot; the tab it displaces overflows.
232
+ const others = entries.filter((_, i) => i !== activeIndex);
233
+ visible = [...others.slice(0, layout.count), entries[activeIndex]];
234
+ overflow = others.slice(layout.count);
235
+ } else {
236
+ visible = entries.slice(0, layout.count);
237
+ overflow = entries.slice(layout.count);
238
+ }
239
+ const activeInOverflow = overflow.some(([id]) => id === activeWorkstream);
240
+ const moreLabel = t('agent.moreTabs');
241
+
242
+ // Inner tab content (name + count badge + completion icon), shared by the hidden
243
+ // measurement row and the visible row so their measured widths match.
244
+ const renderTabInner = (id: string, name: string, isActive: boolean) => {
245
+ const showCount = (count?.get(id) ?? 0) > 0;
246
+ return (
247
+ <>
248
+ {truncateName(name)}
249
+ {showCount && (
250
+ <span className="flex items-center gap-1">
251
+ <span
252
+ className={cn(
253
+ 'inline-flex items-center justify-center p-1 text-xs rounded-full',
254
+ isActive ? 'bg-info text-info' : 'bg-muted text-muted',
255
+ )}
256
+ >
257
+ {count?.get(id)}
258
+ </span>
259
+ {/* Show completion status indicator if we have it and it's not 'all' */}
260
+ {completionStatus &&
261
+ id !== 'all' &&
262
+ (completionStatus.get(id) ? (
263
+ <CheckCircle className="size-3 text-success" />
264
+ ) : (
265
+ <Clock className="size-3 text-attention" />
266
+ ))}
267
+ </span>
268
+ )}
269
+ </>
270
+ );
271
+ };
272
+
273
+ return (
274
+ <div ref={containerRef} className="relative mb-1 bg-muted border-b border-muted/20">
275
+ {/* Hidden measurement row: all tabs + More at natural width, kept separate
276
+ from the visible row so measuring can't feed back into the layout. */}
277
+ <div aria-hidden className="pointer-events-none invisible absolute start-0 top-0 flex w-max gap-1">
278
+ {entries.map(([id, name], i) => (
279
+ <button
280
+ type="button"
281
+ key={id}
282
+ tabIndex={-1}
283
+ ref={(el) => {
284
+ itemRefs.current[i] = el;
285
+ }}
286
+ className={cn(TAB_ITEM_BASE, TAB_ITEM_INACTIVE)}
287
+ >
288
+ {renderTabInner(id, name, false)}
289
+ </button>
290
+ ))}
291
+ <button type="button" tabIndex={-1} ref={moreRef} className={cn(TAB_ITEM_BASE, TAB_ITEM_INACTIVE)}>
292
+ {moreLabel}
293
+ <ChevronDown className="ms-0.5 size-3.5" />
294
+ </button>
295
+ </div>
296
+
297
+ {/* Visible row */}
298
+ <div className="flex gap-1 overflow-hidden">
299
+ {visible.map(([id, name]) => {
300
+ const isActive = activeWorkstream === id;
301
+ return (
302
+ // Tab-bar primitive: raw button mirrors the filled active-pill styling.
303
+ <button
304
+ type="button"
305
+ key={id}
306
+ aria-current={isActive ? 'page' : undefined}
307
+ onClick={() => onSelectWorkstream(id)}
308
+ title={name.length > 20 ? name : undefined}
309
+ className={cn(TAB_ITEM_BASE, isActive ? TAB_ITEM_ACTIVE : TAB_ITEM_INACTIVE)}
310
+ >
311
+ {renderTabInner(id, name, isActive)}
312
+ </button>
313
+ );
314
+ })}
315
+
316
+ {overflow.length > 0 && (
317
+ <WorkstreamMoreMenu
318
+ items={overflow}
319
+ current={activeWorkstream}
320
+ onSelect={onSelectWorkstream}
321
+ label={moreLabel}
322
+ active={activeInOverflow}
323
+ count={count}
324
+ completionStatus={completionStatus}
325
+ />
326
+ )}
327
+ </div>
328
+ </div>
329
+ );
330
+ }
331
+
15
332
  /**
16
333
  * Component that displays tabs for different workstreams
17
334
  */
@@ -58,46 +375,13 @@ export default function WorkstreamTabs({
58
375
  }
59
376
 
60
377
  return (
61
- <div className="flex overflow-x-auto gap-1 mb-1 bg-muted border-b border-muted/20 sticky top-0 z-10">
62
- {sortedWorkstreams.map(([id, name]) => (
63
- <Button
64
- variant="unstyled"
65
- key={id}
66
- className={cn(
67
- 'px-2 py-1 text-xs font-medium whitespace-nowrap transition-colors flex items-center gap-1.5',
68
- activeWorkstream === id
69
- ? 'bg-info text-info border-b-2 border-info'
70
- : 'text-muted hover:bg-muted border-b-2 border-transparent',
71
- )}
72
- onClick={() => onSelectWorkstream(id)}
73
- title={name.length > 20 ? name : undefined}
74
- >
75
- {/* Shorten long names for better UI */}
76
- {name.length > 20 ? `${name.substring(0, 18)}...` : name}
77
- {/* biome-ignore lint/style/noNonNullAssertion: intentional non-null assertion; TS can't prove narrowing here */}
78
- {count?.has(id) && count.get(id)! > 0 && (
79
- <div className="flex items-center gap-1">
80
- <span
81
- className={cn(
82
- 'inline-flex items-center justify-center p-1 text-xs rounded-full',
83
- activeWorkstream === id ? 'bg-info text-info' : 'bg-muted text-muted',
84
- )}
85
- >
86
- {count.get(id)}
87
- </span>
88
- {/* Show completion status indicator if we have it and it's not 'all' */}
89
- {completionStatus &&
90
- id !== 'all' &&
91
- (completionStatus.get(id) ? (
92
- <CheckCircle className="size-3 text-success" />
93
- ) : (
94
- <Clock className="size-3 text-attention" />
95
- ))}
96
- </div>
97
- )}
98
- </Button>
99
- ))}
100
- </div>
378
+ <WorkstreamOverflowBar
379
+ entries={sortedWorkstreams}
380
+ activeWorkstream={activeWorkstream}
381
+ onSelectWorkstream={onSelectWorkstream}
382
+ count={count}
383
+ completionStatus={completionStatus}
384
+ />
101
385
  );
102
386
  }
103
387