@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.
- package/lib/core/components/InputList.d.ts.map +1 -1
- package/lib/core/components/InputList.js +3 -2
- package/lib/core/components/InputList.js.map +1 -1
- package/lib/core/components/SelectList.d.ts.map +1 -1
- package/lib/core/components/SelectList.js +1 -1
- package/lib/core/components/SelectList.js.map +1 -1
- package/lib/core/components/TagsInput.d.ts.map +1 -1
- package/lib/core/components/TagsInput.js +36 -4
- package/lib/core/components/TagsInput.js.map +1 -1
- package/lib/core/components/popup/Popup.js +2 -1
- package/lib/core/components/popup/Popup.js.map +1 -1
- package/lib/core/components/popup/position.d.ts.map +1 -1
- package/lib/core/components/popup/position.js +3 -6
- package/lib/core/components/popup/position.js.map +1 -1
- package/lib/core/components/shadcn/badge.d.ts +2 -2
- package/lib/core/components/shadcn/badge.d.ts.map +1 -1
- package/lib/core/components/shadcn/badge.js +3 -0
- package/lib/core/components/shadcn/badge.js.map +1 -1
- package/lib/core/components/shadcn/breadcrumb.d.ts.map +1 -1
- package/lib/core/components/shadcn/breadcrumb.js +2 -2
- package/lib/core/components/shadcn/breadcrumb.js.map +1 -1
- package/lib/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
- package/lib/core/components/shadcn/filters/filterBar.js +1 -3
- package/lib/core/components/shadcn/filters/filterBar.js.map +1 -1
- package/lib/core/components/shadcn/resizeable.d.ts +1 -1
- package/lib/core/components/shadcn/resizeable.d.ts.map +1 -1
- package/lib/core/components/shadcn/resizeable.js +12 -2
- package/lib/core/components/shadcn/resizeable.js.map +1 -1
- package/lib/core/components/shadcn/selectBox.d.ts +7 -1
- package/lib/core/components/shadcn/selectBox.d.ts.map +1 -1
- package/lib/core/components/shadcn/selectBox.js +29 -12
- package/lib/core/components/shadcn/selectBox.js.map +1 -1
- package/lib/core/components/table/index.d.ts.map +1 -1
- package/lib/core/components/table/index.js +1 -1
- package/lib/core/components/table/index.js.map +1 -1
- package/lib/core/hooks/useIntersectionObserver.d.ts.map +1 -1
- package/lib/core/hooks/useIntersectionObserver.js +3 -1
- package/lib/core/hooks/useIntersectionObserver.js.map +1 -1
- package/lib/features/agent/chat/AgentRightPanel.d.ts.map +1 -1
- package/lib/features/agent/chat/AgentRightPanel.js +3 -6
- package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
- package/lib/features/agent/chat/AnimatedThinkingDots.d.ts.map +1 -1
- package/lib/features/agent/chat/AnimatedThinkingDots.js +1 -1
- package/lib/features/agent/chat/AnimatedThinkingDots.js.map +1 -1
- package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
- package/lib/features/agent/chat/AskUserWidget.js +4 -3
- package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.js +56 -11
- package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +8 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +40 -31
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.js +3 -3
- package/lib/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +5 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +3 -7
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/PlanPanel.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/PlanPanel.js +5 -5
- package/lib/features/agent/chat/ModernAgentOutput/PlanPanel.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/SlidingPlanPanel.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/SlidingPlanPanel.js +4 -4
- package/lib/features/agent/chat/ModernAgentOutput/SlidingPlanPanel.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js +3 -3
- package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js +156 -8
- package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js.map +1 -1
- package/lib/features/agent/chat/hooks/useFileProcessing.d.ts +1 -0
- package/lib/features/agent/chat/hooks/useFileProcessing.d.ts.map +1 -1
- package/lib/features/agent/chat/hooks/useFileProcessing.js +37 -3
- package/lib/features/agent/chat/hooks/useFileProcessing.js.map +1 -1
- package/lib/features/facets/AgentRunnerFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/AgentRunnerFacetsNav.js +1 -1
- package/lib/features/facets/AgentRunnerFacetsNav.js.map +1 -1
- package/lib/features/facets/InteractionsFacetsNav.d.ts +10 -1
- package/lib/features/facets/InteractionsFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/InteractionsFacetsNav.js +14 -5
- package/lib/features/facets/InteractionsFacetsNav.js.map +1 -1
- package/lib/features/facets/PromptsFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/PromptsFacetsNav.js +12 -1
- package/lib/features/facets/PromptsFacetsNav.js.map +1 -1
- package/lib/features/facets/RunsFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/RunsFacetsNav.js +1 -1
- package/lib/features/facets/RunsFacetsNav.js.map +1 -1
- package/lib/features/magic-pdf/AnnotatedImageSlider.d.ts.map +1 -1
- package/lib/features/magic-pdf/AnnotatedImageSlider.js +1 -3
- package/lib/features/magic-pdf/AnnotatedImageSlider.js.map +1 -1
- package/lib/features/magic-pdf/MagicPdfProvider.js +2 -3
- package/lib/features/magic-pdf/MagicPdfProvider.js.map +1 -1
- package/lib/features/store/objects/DocumentSearchResults.d.ts +6 -2
- package/lib/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
- package/lib/features/store/objects/DocumentSearchResults.js +6 -6
- package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.js +1 -1
- package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
- package/lib/features/store/objects/components/DocumentIcon.d.ts.map +1 -1
- package/lib/features/store/objects/components/DocumentIcon.js +1 -1
- package/lib/features/store/objects/components/DocumentIcon.js.map +1 -1
- package/lib/features/store/objects/components/DocumentInput.js.map +1 -1
- package/lib/features/store/objects/search/DocumentSearchContext.d.ts +4 -4
- package/lib/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
- package/lib/features/store/objects/search/DocumentSearchContext.js +1 -1
- package/lib/features/store/objects/search/DocumentSearchContext.js.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContext.d.ts.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContext.js +2 -25
- package/lib/features/store/objects/selection/ObjectsActionContext.js.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContextClass.d.ts +0 -2
- package/lib/features/store/objects/selection/ObjectsActionContextClass.d.ts.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContextClass.js +1 -7
- package/lib/features/store/objects/selection/ObjectsActionContextClass.js.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionHooks.d.ts +0 -1
- package/lib/features/store/objects/selection/ObjectsActionHooks.d.ts.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionHooks.js +0 -10
- package/lib/features/store/objects/selection/ObjectsActionHooks.js.map +1 -1
- package/lib/features/store/objects/selection/SelectionActions.d.ts +4 -0
- package/lib/features/store/objects/selection/SelectionActions.d.ts.map +1 -1
- package/lib/features/store/objects/selection/SelectionActions.js +4 -18
- package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
- package/lib/features/store/objects/selection/actions/DeleteObjectsAction.d.ts.map +1 -1
- package/lib/features/store/objects/selection/actions/DeleteObjectsAction.js +16 -7
- package/lib/features/store/objects/selection/actions/DeleteObjectsAction.js.map +1 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts +7 -0
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.d.ts.map +1 -0
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +51 -0
- package/lib/features/store/objects/selection/actions/StartWorkflowButton.js.map +1 -0
- package/lib/features/store/objects/selection/actions/index.d.ts +0 -1
- package/lib/features/store/objects/selection/actions/index.d.ts.map +1 -1
- package/lib/features/store/objects/selection/actions/index.js +0 -1
- package/lib/features/store/objects/selection/actions/index.js.map +1 -1
- package/lib/features/store/types/ContentObjectTypesSearch.d.ts.map +1 -1
- package/lib/features/store/types/ContentObjectTypesSearch.js +55 -3
- package/lib/features/store/types/ContentObjectTypesSearch.js.map +1 -1
- package/lib/features/store/types/ContentObjectTypesTable.d.ts +5 -1
- package/lib/features/store/types/ContentObjectTypesTable.d.ts.map +1 -1
- package/lib/features/store/types/ContentObjectTypesTable.js +4 -2
- package/lib/features/store/types/ContentObjectTypesTable.js.map +1 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.js +1 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.js.map +1 -1
- package/lib/i18n/locales/ar.json +17 -0
- package/lib/i18n/locales/de.json +17 -0
- package/lib/i18n/locales/en.json +33 -0
- package/lib/i18n/locales/es.json +17 -0
- package/lib/i18n/locales/fr.json +17 -0
- package/lib/i18n/locales/it.json +17 -0
- package/lib/i18n/locales/ja.json +17 -0
- package/lib/i18n/locales/ko.json +17 -0
- package/lib/i18n/locales/pt.json +17 -0
- package/lib/i18n/locales/ru.json +17 -0
- package/lib/i18n/locales/tr.json +17 -0
- package/lib/i18n/locales/zh-TW.json +17 -0
- package/lib/i18n/locales/zh.json +17 -0
- package/lib/layout/Sidebar.d.ts +5 -1
- package/lib/layout/Sidebar.d.ts.map +1 -1
- package/lib/layout/Sidebar.js.map +1 -1
- package/lib/router/NestedRouterProvider.d.ts +1 -1
- package/lib/router/NestedRouterProvider.d.ts.map +1 -1
- package/lib/router/NestedRouterProvider.js +2 -2
- package/lib/router/NestedRouterProvider.js.map +1 -1
- package/lib/router/Redirect.d.ts +13 -0
- package/lib/router/Redirect.d.ts.map +1 -0
- package/lib/router/Redirect.js +20 -0
- package/lib/router/Redirect.js.map +1 -0
- package/lib/router/Router.d.ts.map +1 -1
- package/lib/router/Router.js +1 -2
- package/lib/router/Router.js.map +1 -1
- package/lib/router/index.d.ts +1 -0
- package/lib/router/index.d.ts.map +1 -1
- package/lib/router/index.js +1 -0
- package/lib/router/index.js.map +1 -1
- package/lib/session/DevSessionProvider.d.ts +8 -0
- package/lib/session/DevSessionProvider.d.ts.map +1 -0
- package/lib/session/DevSessionProvider.js +33 -0
- package/lib/session/DevSessionProvider.js.map +1 -0
- package/lib/session/UserSessionProvider.d.ts.map +1 -1
- package/lib/session/UserSessionProvider.js +16 -2
- package/lib/session/UserSessionProvider.js.map +1 -1
- package/lib/session/auth/composable.d.ts +10 -1
- package/lib/session/auth/composable.d.ts.map +1 -1
- package/lib/session/auth/composable.js +38 -2
- package/lib/session/auth/composable.js.map +1 -1
- package/lib/session/index.d.ts +1 -0
- package/lib/session/index.d.ts.map +1 -1
- package/lib/session/index.js +1 -0
- package/lib/session/index.js.map +1 -1
- package/lib/shell/VertesiaShell.d.ts +3 -1
- package/lib/shell/VertesiaShell.d.ts.map +1 -1
- package/lib/shell/VertesiaShell.js +5 -4
- package/lib/shell/VertesiaShell.js.map +1 -1
- package/lib/shell/login/SignInRestrictedEnvStep.d.ts +14 -0
- package/lib/shell/login/SignInRestrictedEnvStep.d.ts.map +1 -0
- package/lib/shell/login/SignInRestrictedEnvStep.js +50 -0
- package/lib/shell/login/SignInRestrictedEnvStep.js.map +1 -0
- package/lib/shell/login/SigninScreen.d.ts.map +1 -1
- package/lib/shell/login/SigninScreen.js +16 -3
- package/lib/shell/login/SigninScreen.js.map +1 -1
- package/lib/shell/login/productionUrl.d.ts +16 -0
- package/lib/shell/login/productionUrl.d.ts.map +1 -0
- package/lib/shell/login/productionUrl.js +19 -0
- package/lib/shell/login/productionUrl.js.map +1 -0
- package/lib/shell/login/signInUtils.d.ts.map +1 -1
- package/lib/shell/login/signInUtils.js +14 -1
- package/lib/shell/login/signInUtils.js.map +1 -1
- package/lib/vertesia-ui-core.js +95 -2
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-env.js +2 -2
- package/lib/vertesia-ui-env.js.map +1 -1
- package/lib/vertesia-ui-features.js +454 -2
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-i18n.js +41 -2
- package/lib/vertesia-ui-i18n.js.map +1 -1
- package/lib/vertesia-ui-layout.js +2 -2
- package/lib/vertesia-ui-layout.js.map +1 -1
- package/lib/vertesia-ui-router.js +2 -2
- package/lib/vertesia-ui-router.js.map +1 -1
- package/lib/vertesia-ui-session.js +2 -2
- package/lib/vertesia-ui-session.js.map +1 -1
- package/lib/vertesia-ui-shell.js +2 -2
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/lib/vertesia-ui-widgets.js +9 -2
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/lib/widgets/json-view/JSONCode.d.ts.map +1 -1
- package/lib/widgets/json-view/JSONCode.js +1 -1
- package/lib/widgets/json-view/JSONCode.js.map +1 -1
- package/lib/widgets/json-view/JSONTable.d.ts.map +1 -1
- package/lib/widgets/json-view/JSONTable.js +1 -1
- package/lib/widgets/json-view/JSONTable.js.map +1 -1
- package/lib/widgets/markdown/ArtifactContentRenderer.d.ts.map +1 -1
- package/lib/widgets/markdown/ArtifactContentRenderer.js +1 -1
- package/lib/widgets/markdown/ArtifactContentRenderer.js.map +1 -1
- package/lib/widgets/markdown/CodeBlockPlaceholder.d.ts.map +1 -1
- package/lib/widgets/markdown/CodeBlockPlaceholder.js +1 -1
- package/lib/widgets/markdown/CodeBlockPlaceholder.js.map +1 -1
- package/lib/widgets/monacoEditor/foldingProviders.d.ts.map +1 -1
- package/lib/widgets/monacoEditor/foldingProviders.js +5 -10
- package/lib/widgets/monacoEditor/foldingProviders.js.map +1 -1
- package/lib/widgets/schema-editor/ManagedSchema.d.ts.map +1 -1
- package/lib/widgets/schema-editor/ManagedSchema.js +1 -2
- package/lib/widgets/schema-editor/ManagedSchema.js.map +1 -1
- package/lib/widgets/upload/UploadResultCategory.d.ts.map +1 -1
- package/lib/widgets/upload/UploadResultCategory.js +1 -1
- package/lib/widgets/upload/UploadResultCategory.js.map +1 -1
- package/lib/widgets/xml-viewer/components/TextElement/index.d.ts.map +1 -1
- package/lib/widgets/xml-viewer/components/TextElement/index.js +1 -1
- package/lib/widgets/xml-viewer/components/TextElement/index.js.map +1 -1
- package/package.json +38 -43
- package/src/core/components/InputList.tsx +5 -10
- package/src/core/components/SelectList.tsx +7 -2
- package/src/core/components/TagsInput.tsx +41 -4
- package/src/core/components/popup/Popup.tsx +3 -3
- package/src/core/components/popup/position.ts +3 -6
- package/src/core/components/shadcn/badge.tsx +3 -0
- package/src/core/components/shadcn/breadcrumb.tsx +2 -4
- package/src/core/components/shadcn/filters/filterBar.tsx +2 -3
- package/src/core/components/shadcn/resizeable.test.tsx +77 -0
- package/src/core/components/shadcn/resizeable.tsx +28 -2
- package/src/core/components/shadcn/selectBox.tsx +70 -36
- package/src/core/components/table/index.tsx +1 -2
- package/src/core/hooks/useIntersectionObserver.tsx +3 -1
- package/src/features/agent/chat/AgentRightPanel.tsx +5 -6
- package/src/features/agent/chat/AnimatedThinkingDots.tsx +1 -2
- package/src/features/agent/chat/AskUserWidget.test.tsx +49 -0
- package/src/features/agent/chat/AskUserWidget.tsx +32 -22
- package/src/features/agent/chat/ModernAgentConversation.test.tsx +198 -1
- package/src/features/agent/chat/ModernAgentConversation.tsx +62 -15
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +60 -34
- package/src/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.tsx +3 -3
- package/src/features/agent/chat/ModernAgentOutput/MessageInput.test.tsx +25 -0
- package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +5 -1
- package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +30 -30
- package/src/features/agent/chat/ModernAgentOutput/PlanPanel.tsx +5 -6
- package/src/features/agent/chat/ModernAgentOutput/SlidingPlanPanel.tsx +4 -5
- package/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx +3 -5
- package/src/features/agent/chat/ModernAgentOutput/WorkstreamTabs.tsx +326 -42
- package/src/features/agent/chat/hooks/useFileProcessing.test.tsx +70 -0
- package/src/features/agent/chat/hooks/useFileProcessing.ts +37 -6
- package/src/features/facets/AgentRunnerFacetsNav.tsx +1 -2
- package/src/features/facets/InteractionsFacetsNav.tsx +30 -5
- package/src/features/facets/PromptsFacetsNav.tsx +13 -1
- package/src/features/facets/RunsFacetsNav.tsx +1 -2
- package/src/features/magic-pdf/AnnotatedImageSlider.tsx +1 -2
- package/src/features/magic-pdf/MagicPdfProvider.tsx +4 -4
- package/src/features/store/objects/DocumentSearchResults.tsx +11 -1
- package/src/features/store/objects/components/ContentOverview.tsx +1 -2
- package/src/features/store/objects/components/DocumentIcon.tsx +1 -2
- package/src/features/store/objects/components/DocumentInput.tsx +2 -2
- package/src/features/store/objects/search/DocumentSearchContext.test.ts +2 -2
- package/src/features/store/objects/search/DocumentSearchContext.ts +3 -3
- package/src/features/store/objects/selection/ObjectsActionContext.tsx +2 -27
- package/src/features/store/objects/selection/ObjectsActionContextClass.ts +1 -7
- package/src/features/store/objects/selection/ObjectsActionHooks.ts +0 -11
- package/src/features/store/objects/selection/SelectionActions.tsx +9 -34
- package/src/features/store/objects/selection/actions/DeleteObjectsAction.tsx +40 -7
- package/src/features/store/objects/selection/actions/StartWorkflowButton.tsx +104 -0
- package/src/features/store/objects/selection/actions/index.ts +0 -1
- package/src/features/store/types/ContentObjectTypesSearch.tsx +110 -1
- package/src/features/store/types/ContentObjectTypesTable.tsx +42 -3
- package/src/features/store/types/search/ObjectTypeSearchContext.tsx +1 -1
- package/src/i18n/locales/ar.json +17 -0
- package/src/i18n/locales/de.json +17 -0
- package/src/i18n/locales/en.json +33 -0
- package/src/i18n/locales/es.json +17 -0
- package/src/i18n/locales/fr.json +17 -0
- package/src/i18n/locales/it.json +17 -0
- package/src/i18n/locales/ja.json +17 -0
- package/src/i18n/locales/ko.json +17 -0
- package/src/i18n/locales/pt.json +17 -0
- package/src/i18n/locales/ru.json +17 -0
- package/src/i18n/locales/tr.json +17 -0
- package/src/i18n/locales/zh-TW.json +17 -0
- package/src/i18n/locales/zh.json +17 -0
- package/src/layout/Sidebar.tsx +6 -1
- package/src/router/NestedRouterProvider.tsx +2 -2
- package/src/router/Redirect.tsx +29 -0
- package/src/router/Router.tsx +1 -2
- package/src/router/index.ts +1 -0
- package/src/session/DevSessionProvider.tsx +46 -0
- package/src/session/UserSessionProvider.tsx +18 -2
- package/src/session/auth/composable.ts +41 -3
- package/src/session/index.ts +1 -0
- package/src/shell/VertesiaShell.tsx +27 -14
- package/src/shell/login/SignInRestrictedEnvStep.tsx +87 -0
- package/src/shell/login/SigninScreen.tsx +29 -16
- package/src/shell/login/productionUrl.test.ts +19 -0
- package/src/shell/login/productionUrl.ts +18 -0
- package/src/shell/login/signInUtils.ts +14 -1
- package/src/widgets/json-view/JSONCode.tsx +1 -2
- package/src/widgets/json-view/JSONTable.tsx +1 -2
- package/src/widgets/markdown/ArtifactContentRenderer.tsx +4 -7
- package/src/widgets/markdown/CodeBlockPlaceholder.tsx +1 -2
- package/src/widgets/monacoEditor/foldingProviders.ts +10 -15
- package/src/widgets/schema-editor/ManagedSchema.ts +1 -2
- package/src/widgets/upload/UploadResultCategory.tsx +2 -3
- package/src/widgets/xml-viewer/components/TextElement/index.tsx +1 -2
- package/lib/features/store/objects/selection/actions/StartWorkflowComponent.d.ts +0 -4
- package/lib/features/store/objects/selection/actions/StartWorkflowComponent.d.ts.map +0 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowComponent.js +0 -77
- package/lib/features/store/objects/selection/actions/StartWorkflowComponent.js.map +0 -1
- 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
|
-
|
|
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
|
-
{
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
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
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
status =
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
status =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
<
|
|
62
|
-
{sortedWorkstreams
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|