@trinityui/design-system 2.11.1 → 2.13.1
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/CHANGELOG.md +2171 -0
- package/CONTRIBUTING.md +180 -69
- package/LICENSE +21 -0
- package/MIGRATION.md +508 -28
- package/README.md +281 -120
- package/dist/IllustratedMessage.js +546 -1341
- package/dist/accessibility.d.ts +596 -18
- package/dist/accessibility.d.ts.map +1 -1
- package/dist/accessibility.js +458 -0
- package/dist/app-shell.js +17219 -9131
- package/dist/assets/index.d.ts +1 -1
- package/dist/charts.js +8507 -6059
- package/dist/color-contrast.d.ts +35 -0
- package/dist/color-contrast.d.ts.map +1 -0
- package/dist/commonjs-helpers.js +8 -0
- package/dist/component-color-references.d.ts +175 -0
- package/dist/component-color-references.d.ts.map +1 -0
- package/dist/components/AI/AIActions.d.ts +72 -12
- package/dist/components/AI/AIActions.d.ts.map +1 -1
- package/dist/components/AI/AIAvatar.d.ts +31 -3
- package/dist/components/AI/AIAvatar.d.ts.map +1 -1
- package/dist/components/AI/AIChainOfThought.d.ts +159 -77
- package/dist/components/AI/AIChainOfThought.d.ts.map +1 -1
- package/dist/components/AI/AIChainOfThought.types.d.ts +292 -40
- package/dist/components/AI/AIChainOfThought.types.d.ts.map +1 -1
- package/dist/components/AI/AIChainOfThought.utils.d.ts +25 -7
- package/dist/components/AI/AIChainOfThought.utils.d.ts.map +1 -1
- package/dist/components/AI/AIChat.d.ts +12 -0
- package/dist/components/AI/AIChat.d.ts.map +1 -1
- package/dist/components/AI/AIChatWelcome.d.ts +254 -0
- package/dist/components/AI/AIChatWelcome.d.ts.map +1 -0
- package/dist/components/AI/AIClarificationPrompt.d.ts +134 -0
- package/dist/components/AI/AIClarificationPrompt.d.ts.map +1 -0
- package/dist/components/AI/AIConfidenceBadge.d.ts +117 -0
- package/dist/components/AI/AIConfidenceBadge.d.ts.map +1 -0
- package/dist/components/AI/AIConfigContext.d.ts +100 -0
- package/dist/components/AI/AIConfigContext.d.ts.map +1 -0
- package/dist/components/AI/AIConfigProvider.d.ts +66 -0
- package/dist/components/AI/AIConfigProvider.d.ts.map +1 -0
- package/dist/components/AI/AIContainer.d.ts +80 -11
- package/dist/components/AI/AIContainer.d.ts.map +1 -1
- package/dist/components/AI/AIExplainability.d.ts +39 -5
- package/dist/components/AI/AIExplainability.d.ts.map +1 -1
- package/dist/components/AI/AIFeedbackControl.d.ts +113 -0
- package/dist/components/AI/AIFeedbackControl.d.ts.map +1 -0
- package/dist/components/AI/AIFeedbackForm.d.ts +117 -0
- package/dist/components/AI/AIFeedbackForm.d.ts.map +1 -0
- package/dist/components/AI/AIFeedbackHostContext.d.ts +16 -0
- package/dist/components/AI/AIFeedbackHostContext.d.ts.map +1 -0
- package/dist/components/AI/AILabel.d.ts +39 -6
- package/dist/components/AI/AILabel.d.ts.map +1 -1
- package/dist/components/AI/AIMessage.d.ts +337 -42
- package/dist/components/AI/AIMessage.d.ts.map +1 -1
- package/dist/components/AI/AIPlanReview.d.ts +194 -0
- package/dist/components/AI/AIPlanReview.d.ts.map +1 -0
- package/dist/components/AI/AIQueryDisclosure.d.ts +119 -0
- package/dist/components/AI/AIQueryDisclosure.d.ts.map +1 -0
- package/dist/components/AI/AIRelatedQuestions.d.ts +50 -11
- package/dist/components/AI/AIRelatedQuestions.d.ts.map +1 -1
- package/dist/components/AI/AIResponseRenderer.d.ts +133 -21
- package/dist/components/AI/AIResponseRenderer.d.ts.map +1 -1
- package/dist/components/AI/AIResponseSourcesContext.d.ts +83 -0
- package/dist/components/AI/AIResponseSourcesContext.d.ts.map +1 -0
- package/dist/components/AI/AIReviewItem.d.ts +142 -0
- package/dist/components/AI/AIReviewItem.d.ts.map +1 -0
- package/dist/components/AI/AISourceChip.d.ts +135 -0
- package/dist/components/AI/AISourceChip.d.ts.map +1 -0
- package/dist/components/AI/AISources.d.ts +81 -11
- package/dist/components/AI/AISources.d.ts.map +1 -1
- package/dist/components/AI/AISourcesPanel.d.ts +43 -7
- package/dist/components/AI/AISourcesPanel.d.ts.map +1 -1
- package/dist/components/AI/AITextBlock.d.ts +104 -16
- package/dist/components/AI/AITextBlock.d.ts.map +1 -1
- package/dist/components/AI/AITextBlock.parser.d.ts +153 -0
- package/dist/components/AI/AITextBlock.parser.d.ts.map +1 -0
- package/dist/components/AI/AIVerificationBadge.d.ts +94 -0
- package/dist/components/AI/AIVerificationBadge.d.ts.map +1 -0
- package/dist/components/AI/AIVisuals.d.ts +92 -23
- package/dist/components/AI/AIVisuals.d.ts.map +1 -1
- package/dist/components/AI/AIVoiceInput.d.ts +42 -11
- package/dist/components/AI/AIVoiceInput.d.ts.map +1 -1
- package/dist/components/AI/AIVoiceMode.d.ts +16 -2
- package/dist/components/AI/AIVoiceMode.d.ts.map +1 -1
- package/dist/components/AI/BlockErrorBoundary.d.ts +77 -0
- package/dist/components/AI/BlockErrorBoundary.d.ts.map +1 -0
- package/dist/components/AI/ChatHeader.d.ts +41 -4
- package/dist/components/AI/ChatHeader.d.ts.map +1 -1
- package/dist/components/AI/ChatHistoryList.d.ts +25 -12
- package/dist/components/AI/ChatHistoryList.d.ts.map +1 -1
- package/dist/components/AI/ChatThread.d.ts +263 -13
- package/dist/components/AI/ChatThread.d.ts.map +1 -1
- package/dist/components/AI/InThreadContext.d.ts +14 -0
- package/dist/components/AI/InThreadContext.d.ts.map +1 -0
- package/dist/components/AI/InsightEngine.d.ts +270 -46
- package/dist/components/AI/InsightEngine.d.ts.map +1 -1
- package/dist/components/AI/InsightEngineInput.d.ts +47 -4
- package/dist/components/AI/InsightEngineInput.d.ts.map +1 -1
- package/dist/components/AI/MessageActionButton.d.ts +72 -0
- package/dist/components/AI/MessageActionButton.d.ts.map +1 -0
- package/dist/components/AI/QueryInput.d.ts.map +1 -1
- package/dist/components/AI/UserAvatar.d.ts +50 -4
- package/dist/components/AI/UserAvatar.d.ts.map +1 -1
- package/dist/components/AI/UserMessage.d.ts +32 -3
- package/dist/components/AI/UserMessage.d.ts.map +1 -1
- package/dist/components/AI/blockRendererContext.d.ts +64 -0
- package/dist/components/AI/blockRendererContext.d.ts.map +1 -0
- package/dist/components/AI/hitlBlockRenderers.d.ts +48 -0
- package/dist/components/AI/hitlBlockRenderers.d.ts.map +1 -0
- package/dist/components/AI/index.d.ts +23 -6
- package/dist/components/AI/index.d.ts.map +1 -1
- package/dist/components/AI/moleculeBlockRenderers.d.ts +15 -0
- package/dist/components/AI/moleculeBlockRenderers.d.ts.map +1 -0
- package/dist/components/AI/planReviewBlockRenderer.d.ts +8 -0
- package/dist/components/AI/planReviewBlockRenderer.d.ts.map +1 -0
- package/dist/components/AI/strings/defaults.d.ts +31 -0
- package/dist/components/AI/strings/defaults.d.ts.map +1 -0
- package/dist/components/AI/tokens.d.ts +185 -20
- package/dist/components/AI/tokens.d.ts.map +1 -1
- package/dist/components/AI/types/hitl.d.ts +2294 -0
- package/dist/components/AI/types/hitl.d.ts.map +1 -0
- package/dist/components/AI/types/index.d.ts +2 -1
- package/dist/components/AI/types/index.d.ts.map +1 -1
- package/dist/components/AI/types/response.d.ts +428 -40
- package/dist/components/AI/types/response.d.ts.map +1 -1
- package/dist/components/AI/useBlockSources.d.ts +67 -0
- package/dist/components/AI/useBlockSources.d.ts.map +1 -0
- package/dist/components/AI/useBrowserVoiceMode.d.ts +31 -2
- package/dist/components/AI/useBrowserVoiceMode.d.ts.map +1 -1
- package/dist/components/AI/useWelcomeComposerHandoff.d.ts +30 -0
- package/dist/components/AI/useWelcomeComposerHandoff.d.ts.map +1 -0
- package/dist/components/AI/utils/alphaIndex.d.ts +19 -0
- package/dist/components/AI/utils/alphaIndex.d.ts.map +1 -0
- package/dist/components/AI/utils/blockReady.d.ts +54 -0
- package/dist/components/AI/utils/blockReady.d.ts.map +1 -0
- package/dist/components/AI/utils/blocks.d.ts +28 -0
- package/dist/components/AI/utils/blocks.d.ts.map +1 -0
- package/dist/components/AI/utils/confidence.d.ts +182 -0
- package/dist/components/AI/utils/confidence.d.ts.map +1 -0
- package/dist/components/AI/utils/filters.d.ts +31 -0
- package/dist/components/AI/utils/filters.d.ts.map +1 -0
- package/dist/components/AI/utils/responseText.d.ts +8 -0
- package/dist/components/AI/utils/responseText.d.ts.map +1 -0
- package/dist/components/AI/utils/sources.d.ts +110 -0
- package/dist/components/AI/utils/sources.d.ts.map +1 -0
- package/dist/components/AIDigestCard/AIDigestCard.d.ts.map +1 -1
- package/dist/components/AIDigestCard/types.d.ts +42 -5
- package/dist/components/AIDigestCard/types.d.ts.map +1 -1
- package/dist/components/AppLayout/AIFab.d.ts +33 -6
- package/dist/components/AppLayout/AIFab.d.ts.map +1 -1
- package/dist/components/AppLayout/AIFabLayout.deprecated.d.ts.map +1 -1
- package/dist/components/AppLayout/AppLayout.d.ts +143 -8
- package/dist/components/AppLayout/AppLayout.d.ts.map +1 -1
- package/dist/components/AppLayout/InsightEnginePanel.d.ts +315 -21
- package/dist/components/AppLayout/InsightEnginePanel.d.ts.map +1 -1
- package/dist/components/AppLayout/ResizablePanel.d.ts +145 -8
- package/dist/components/AppLayout/ResizablePanel.d.ts.map +1 -1
- package/dist/components/AppLayout/ThreadHistorySidebar.d.ts +6 -0
- package/dist/components/AppLayout/ThreadHistorySidebar.d.ts.map +1 -1
- package/dist/components/AppLayout/index.d.ts +3 -3
- package/dist/components/AppLayout/index.d.ts.map +1 -1
- package/dist/components/AppLayout/insightEnginePanel.demo.d.ts +32 -0
- package/dist/components/AppLayout/insightEnginePanel.demo.d.ts.map +1 -0
- package/dist/components/AppLayout/panelPropsPath.d.ts +26 -0
- package/dist/components/AppLayout/panelPropsPath.d.ts.map +1 -0
- package/dist/components/AppLayout/panelResizeTransition.d.ts +49 -0
- package/dist/components/AppLayout/panelResizeTransition.d.ts.map +1 -0
- package/dist/components/AppLayout/toChatThreadMessage.d.ts +40 -0
- package/dist/components/AppLayout/toChatThreadMessage.d.ts.map +1 -0
- package/dist/components/AssessmentDrawer/AssessmentDrawer.d.ts +5 -1
- package/dist/components/AssessmentDrawer/AssessmentDrawer.d.ts.map +1 -1
- package/dist/components/AuditDrawer/AuditDrawer.d.ts +4 -3
- package/dist/components/AuditDrawer/AuditDrawer.d.ts.map +1 -1
- package/dist/components/AuditDrawer/index.d.ts +1 -1
- package/dist/components/AuditDrawer/index.d.ts.map +1 -1
- package/dist/components/AuditDrawer/types.d.ts +39 -5
- package/dist/components/AuditDrawer/types.d.ts.map +1 -1
- package/dist/components/Autocomplete/Autocomplete.d.ts +6 -1
- package/dist/components/Autocomplete/Autocomplete.d.ts.map +1 -1
- package/dist/components/BulkActionDock/BulkActionDock.d.ts.map +1 -1
- package/dist/components/Carousel/Carousel.d.ts +10 -2
- package/dist/components/Carousel/Carousel.d.ts.map +1 -1
- package/dist/components/Charts/AreaChart.d.ts.map +1 -1
- package/dist/components/Charts/BarChart.d.ts.map +1 -1
- package/dist/components/Charts/ChartWrapper.d.ts.map +1 -1
- package/dist/components/Charts/ComposedChart.d.ts.map +1 -1
- package/dist/components/Charts/CustomLegend.d.ts +11 -2
- package/dist/components/Charts/CustomLegend.d.ts.map +1 -1
- package/dist/components/Charts/LineChart.d.ts.map +1 -1
- package/dist/components/Charts/PieChart.d.ts.map +1 -1
- package/dist/components/Charts/RadialChart.d.ts.map +1 -1
- package/dist/components/Charts/ScatterChart.d.ts.map +1 -1
- package/dist/components/Charts/Sparkline.d.ts.map +1 -1
- package/dist/components/Charts/gaugeGeometry.d.ts +35 -0
- package/dist/components/Charts/gaugeGeometry.d.ts.map +1 -0
- package/dist/components/Charts/highcharts.d.ts +23 -0
- package/dist/components/Charts/highcharts.d.ts.map +1 -0
- package/dist/components/Charts/highchartsUtils.d.ts +101 -6
- package/dist/components/Charts/highchartsUtils.d.ts.map +1 -1
- package/dist/components/Charts/index.d.ts +1 -1
- package/dist/components/Charts/index.d.ts.map +1 -1
- package/dist/components/Charts/tokens.d.ts +21 -0
- package/dist/components/Charts/tokens.d.ts.map +1 -1
- package/dist/components/Charts/types.d.ts +7 -7
- package/dist/components/Charts/types.d.ts.map +1 -1
- package/dist/components/ChipsInput/ChipsInput.d.ts.map +1 -1
- package/dist/components/ChoiceChips/ChoiceChips.d.ts +106 -0
- package/dist/components/ChoiceChips/ChoiceChips.d.ts.map +1 -0
- package/dist/components/ClearableTextField/ClearableTextField.d.ts +23 -2
- package/dist/components/ClearableTextField/ClearableTextField.d.ts.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.d.ts +80 -0
- package/dist/components/CodeBlock/CodeBlock.d.ts.map +1 -0
- package/dist/components/Combobox/Combobox.d.ts +3 -2
- package/dist/components/Combobox/Combobox.d.ts.map +1 -1
- package/dist/components/CommandPalette/CommandPalette.d.ts +54 -3
- package/dist/components/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/components/CommandPalette/index.d.ts +1 -1
- package/dist/components/CommandPalette/index.d.ts.map +1 -1
- package/dist/components/CopyButton/CopyButton.d.ts +80 -0
- package/dist/components/CopyButton/CopyButton.d.ts.map +1 -0
- package/dist/components/DataCard/DataCard.d.ts +11 -1
- package/dist/components/DataCard/DataCard.d.ts.map +1 -1
- package/dist/components/DataTable/CellRenderers.d.ts +8 -11
- package/dist/components/DataTable/CellRenderers.d.ts.map +1 -1
- package/dist/components/DataTable/DataTable.d.ts +1 -1
- package/dist/components/DataTable/DataTable.d.ts.map +1 -1
- package/dist/components/DataTable/InlineAddRow.d.ts.map +1 -1
- package/dist/components/DataTable/columnContext.d.ts +8 -0
- package/dist/components/DataTable/columnContext.d.ts.map +1 -0
- package/dist/components/DataTable/index.d.ts +2 -2
- package/dist/components/DataTable/index.d.ts.map +1 -1
- package/dist/components/DataTable/labels.d.ts +13 -0
- package/dist/components/DataTable/labels.d.ts.map +1 -0
- package/dist/components/DataTable/types.d.ts +34 -0
- package/dist/components/DataTable/types.d.ts.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.d.ts.map +1 -1
- package/dist/components/DetailsPopover/DetailsPopover.d.ts +185 -0
- package/dist/components/DetailsPopover/DetailsPopover.d.ts.map +1 -0
- package/dist/components/DiffViewer/DiffViewer.d.ts +3 -2
- package/dist/components/DiffViewer/DiffViewer.d.ts.map +1 -1
- package/dist/components/Disclosure/Disclosure.d.ts +83 -0
- package/dist/components/Disclosure/Disclosure.d.ts.map +1 -0
- package/dist/components/DockLayout/DockLayout.d.ts +58 -0
- package/dist/components/DockLayout/DockLayout.d.ts.map +1 -1
- package/dist/components/DockLayout/index.d.ts +1 -1
- package/dist/components/DockLayout/index.d.ts.map +1 -1
- package/dist/components/DocumentCard/DocumentCard.d.ts +5 -1
- package/dist/components/DocumentCard/DocumentCard.d.ts.map +1 -1
- package/dist/components/EntityDetailPanel/EntityDetailPanel.d.ts +6 -2
- package/dist/components/EntityDetailPanel/EntityDetailPanel.d.ts.map +1 -1
- package/dist/components/FileUpload/FileUpload.d.ts +141 -6
- package/dist/components/FileUpload/FileUpload.d.ts.map +1 -1
- package/dist/components/FileUpload/index.d.ts +1 -1
- package/dist/components/FileUpload/index.d.ts.map +1 -1
- package/dist/components/FilterBar/FilterBar.d.ts +27 -1
- package/dist/components/FilterBar/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar/index.d.ts +1 -1
- package/dist/components/FilterBar/index.d.ts.map +1 -1
- package/dist/components/FlowAutoLayout/useFlowAutoLayout.d.ts.map +1 -1
- package/dist/components/GanttView/GanttView.d.ts +24 -0
- package/dist/components/GanttView/GanttView.d.ts.map +1 -1
- package/dist/components/GanttView/types.d.ts +33 -7
- package/dist/components/GanttView/types.d.ts.map +1 -1
- package/dist/components/GaugeCard/GaugeCard.d.ts +4 -3
- package/dist/components/GaugeCard/GaugeCard.d.ts.map +1 -1
- package/dist/components/GaugeCard/geometry.d.ts +59 -0
- package/dist/components/GaugeCard/geometry.d.ts.map +1 -0
- package/dist/components/GeoStatusMap/GeoStatusMap.d.ts.map +1 -1
- package/dist/components/GeoStatusMap/types.d.ts +8 -5
- package/dist/components/GeoStatusMap/types.d.ts.map +1 -1
- package/dist/components/GoToTop/GoToTop.d.ts +1 -1
- package/dist/components/GoToTop/GoToTop.d.ts.map +1 -1
- package/dist/components/HierarchicalDrillDown/HierarchicalDrillDown.d.ts.map +1 -1
- package/dist/components/HistoricalDataIndicator/HistoricalDataIndicator.d.ts.map +1 -1
- package/dist/components/HtmlToMui/HtmlToMui.d.ts.map +1 -1
- package/dist/components/Icon.d.ts +567 -10
- package/dist/components/Icon.d.ts.map +1 -1
- package/dist/components/IllustratedMessage.d.ts +52 -3
- package/dist/components/IllustratedMessage.d.ts.map +1 -1
- package/dist/components/InlineEditableText/InlineEditableText.d.ts +117 -0
- package/dist/components/InlineEditableText/InlineEditableText.d.ts.map +1 -0
- package/dist/components/InsightCard/InsightCard.d.ts.map +1 -1
- package/dist/components/InsightCard/types.d.ts +10 -3
- package/dist/components/InsightCard/types.d.ts.map +1 -1
- package/dist/components/KeyValueList/KeyValueList.d.ts +94 -0
- package/dist/components/KeyValueList/KeyValueList.d.ts.map +1 -0
- package/dist/components/LandingPage/FeatureCard.d.ts +4 -0
- package/dist/components/LandingPage/FeatureCard.d.ts.map +1 -1
- package/dist/components/LandingPage/LandingPage.d.ts +43 -4
- package/dist/components/LandingPage/LandingPage.d.ts.map +1 -1
- package/dist/components/LandingPage/heroTone.d.ts +54 -0
- package/dist/components/LandingPage/heroTone.d.ts.map +1 -0
- package/dist/components/MentionsInput/MentionsInput.d.ts.map +1 -1
- package/dist/components/MetaChip/MetaChip.d.ts +113 -0
- package/dist/components/MetaChip/MetaChip.d.ts.map +1 -0
- package/dist/components/Modal/Modal.d.ts +222 -20
- package/dist/components/Modal/Modal.d.ts.map +1 -1
- package/dist/components/Modal/index.d.ts +1 -1
- package/dist/components/Modal/index.d.ts.map +1 -1
- package/dist/components/NotificationCenter/NotificationCenter.d.ts.map +1 -1
- package/dist/components/OrgChart/OrgChart.d.ts +5 -0
- package/dist/components/OrgChart/OrgChart.d.ts.map +1 -1
- package/dist/components/OrgChart/types.d.ts +31 -7
- package/dist/components/OrgChart/types.d.ts.map +1 -1
- package/dist/components/OverflowTooltip/OverflowTooltip.d.ts +1 -1
- package/dist/components/OverflowTooltip/OverflowTooltip.d.ts.map +1 -1
- package/dist/components/PageHeader/PageHeader.d.ts.map +1 -1
- package/dist/components/PageTOC/PageTOC.d.ts +2 -1
- package/dist/components/PageTOC/PageTOC.d.ts.map +1 -1
- package/dist/components/Popper/Popper.d.ts +80 -9
- package/dist/components/Popper/Popper.d.ts.map +1 -1
- package/dist/components/Popper/index.d.ts +1 -1
- package/dist/components/Popper/index.d.ts.map +1 -1
- package/dist/components/PredictiveTextInput/PredictiveTextInput.d.ts +5 -1
- package/dist/components/PredictiveTextInput/PredictiveTextInput.d.ts.map +1 -1
- package/dist/components/SearchInput/SearchInput.d.ts +49 -0
- package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
- package/dist/components/SearchInput/index.d.ts +1 -1
- package/dist/components/SearchInput/index.d.ts.map +1 -1
- package/dist/components/SimpleTable/SimpleTable.d.ts +11 -2
- package/dist/components/SimpleTable/SimpleTable.d.ts.map +1 -1
- package/dist/components/SplitPane/SplitPane.d.ts +2 -1
- package/dist/components/SplitPane/SplitPane.d.ts.map +1 -1
- package/dist/components/StatusHero/StatusHero.d.ts.map +1 -1
- package/dist/components/StatusIndicator/Badge.d.ts +6 -1
- package/dist/components/StatusIndicator/Badge.d.ts.map +1 -1
- package/dist/components/StatusIndicator/Chip.d.ts +3 -1
- package/dist/components/StatusIndicator/Chip.d.ts.map +1 -1
- package/dist/components/StatusIndicator/Indicators.d.ts +5 -1
- package/dist/components/StatusIndicator/Indicators.d.ts.map +1 -1
- package/dist/components/StatusIndicator/Legend.d.ts.map +1 -1
- package/dist/components/StatusIndicator/StatusIndicator.d.ts +98 -4
- package/dist/components/StatusIndicator/StatusIndicator.d.ts.map +1 -1
- package/dist/components/StatusIndicator/index.d.ts +2 -2
- package/dist/components/StatusIndicator/index.d.ts.map +1 -1
- package/dist/components/StatusIndicator/types.d.ts +48 -1
- package/dist/components/StatusIndicator/types.d.ts.map +1 -1
- package/dist/components/StepList/StepList.d.ts +80 -0
- package/dist/components/StepList/StepList.d.ts.map +1 -0
- package/dist/components/StepList/StepStatusMarker.d.ts +39 -0
- package/dist/components/StepList/StepStatusMarker.d.ts.map +1 -0
- package/dist/components/StepList/stepStatus.d.ts +30 -0
- package/dist/components/StepList/stepStatus.d.ts.map +1 -0
- package/dist/components/SystemBanner/SystemBanner.d.ts.map +1 -1
- package/dist/components/Timeline/Timeline.d.ts +57 -3
- package/dist/components/Timeline/Timeline.d.ts.map +1 -1
- package/dist/components/Timeline/index.d.ts +1 -1
- package/dist/components/Timeline/index.d.ts.map +1 -1
- package/dist/components/Timeline/timelineLabels.d.ts +70 -0
- package/dist/components/Timeline/timelineLabels.d.ts.map +1 -0
- package/dist/components/Toast/Toast.d.ts.map +1 -1
- package/dist/components/TopNavHeader.d.ts +18 -1
- package/dist/components/TopNavHeader.d.ts.map +1 -1
- package/dist/components/TopNavWithSidebar.d.ts +61 -2
- package/dist/components/TopNavWithSidebar.d.ts.map +1 -1
- package/dist/components/Tour/Tour.d.ts +18 -1
- package/dist/components/Tour/Tour.d.ts.map +1 -1
- package/dist/components/TransferList/TransferList.d.ts +123 -4
- package/dist/components/TransferList/TransferList.d.ts.map +1 -1
- package/dist/components/TransferList/index.d.ts +1 -1
- package/dist/components/TransferList/index.d.ts.map +1 -1
- package/dist/components/TreeView/TreeView.d.ts +97 -8
- package/dist/components/TreeView/TreeView.d.ts.map +1 -1
- package/dist/components/TreeView/index.d.ts +1 -1
- package/dist/components/TreeView/index.d.ts.map +1 -1
- package/dist/components/TrinityLoader.d.ts.map +1 -1
- package/dist/components/icons/featherExtensions.d.ts +55 -0
- package/dist/components/icons/featherExtensions.d.ts.map +1 -0
- package/dist/components/icons/materialExtensions.d.ts +20 -0
- package/dist/components/icons/materialExtensions.d.ts.map +1 -0
- package/dist/components/internal/index.d.ts +87 -0
- package/dist/components/internal/index.d.ts.map +1 -1
- package/dist/components/navigation/MenuListWithHeading.d.ts +33 -0
- package/dist/components/navigation/MenuListWithHeading.d.ts.map +1 -0
- package/dist/components/navigation/clientSelectorLabels.d.ts +13 -0
- package/dist/components/navigation/clientSelectorLabels.d.ts.map +1 -0
- package/dist/components/navigation/components.d.ts +6 -0
- package/dist/components/navigation/components.d.ts.map +1 -1
- package/dist/components/shared/AsyncOutcomeAlert.d.ts +91 -0
- package/dist/components/shared/AsyncOutcomeAlert.d.ts.map +1 -0
- package/dist/components/shared/TrinityIconSvg.d.ts.map +1 -1
- package/dist/components/shared/TrinityLogoSvg.d.ts.map +1 -1
- package/dist/components/shared/bidi.d.ts +44 -0
- package/dist/components/shared/bidi.d.ts.map +1 -0
- package/dist/components/shared/bidiText.d.ts +25 -0
- package/dist/components/shared/bidiText.d.ts.map +1 -0
- package/dist/components/shared/containerQueries.d.ts +73 -0
- package/dist/components/shared/containerQueries.d.ts.map +1 -0
- package/dist/components/shared/createStringsContext.d.ts +69 -0
- package/dist/components/shared/createStringsContext.d.ts.map +1 -0
- package/dist/components/shared/direction.d.ts +46 -0
- package/dist/components/shared/direction.d.ts.map +1 -0
- package/dist/components/shared/focusTrap.d.ts +36 -0
- package/dist/components/shared/focusTrap.d.ts.map +1 -0
- package/dist/components/shared/getInitials.d.ts +29 -0
- package/dist/components/shared/getInitials.d.ts.map +1 -0
- package/dist/components/shared/headingLevel.d.ts +69 -0
- package/dist/components/shared/headingLevel.d.ts.map +1 -0
- package/dist/components/shared/mergeSlotProps.d.ts +50 -0
- package/dist/components/shared/mergeSlotProps.d.ts.map +1 -0
- package/dist/components/shared/mergeSx.d.ts +26 -0
- package/dist/components/shared/mergeSx.d.ts.map +1 -0
- package/dist/components/shared/motion.d.ts +110 -0
- package/dist/components/shared/motion.d.ts.map +1 -0
- package/dist/components/shared/occurrenceKeys.d.ts +23 -0
- package/dist/components/shared/occurrenceKeys.d.ts.map +1 -0
- package/dist/components/shared/primaryAction.d.ts +89 -0
- package/dist/components/shared/primaryAction.d.ts.map +1 -0
- package/dist/components/shared/resizeKeyboard.d.ts +68 -0
- package/dist/components/shared/resizeKeyboard.d.ts.map +1 -0
- package/dist/components/shared/rootProps.d.ts +117 -0
- package/dist/components/shared/rootProps.d.ts.map +1 -0
- package/dist/components/shared/safeContent.d.ts +9 -2
- package/dist/components/shared/safeContent.d.ts.map +1 -1
- package/dist/components/shared/targetSize.d.ts +64 -0
- package/dist/components/shared/targetSize.d.ts.map +1 -0
- package/dist/components/shared/useContainerInlineSize.d.ts +43 -0
- package/dist/components/shared/useContainerInlineSize.d.ts.map +1 -0
- package/dist/components/shared/useCopyAction.d.ts +35 -0
- package/dist/components/shared/useCopyAction.d.ts.map +1 -0
- package/dist/components/shared/useOverflow.d.ts +44 -0
- package/dist/components/shared/useOverflow.d.ts.map +1 -0
- package/dist/components/shared/useResizable.d.ts +106 -7
- package/dist/components/shared/useResizable.d.ts.map +1 -1
- package/dist/components/shared/warnDeprecated.d.ts +89 -15
- package/dist/components/shared/warnDeprecated.d.ts.map +1 -1
- package/dist/components/shared/zIndexRoles.d.ts +62 -0
- package/dist/components/shared/zIndexRoles.d.ts.map +1 -0
- package/dist/components/templates/AIReviewQueue/AIReviewQueueTemplate.d.ts +213 -0
- package/dist/components/templates/AIReviewQueue/AIReviewQueueTemplate.d.ts.map +1 -0
- package/dist/components/templates/AIReviewQueue/index.d.ts +2 -0
- package/dist/components/templates/AIReviewQueue/index.d.ts.map +1 -0
- package/dist/components/templates/Auth/AuthTemplate.d.ts.map +1 -1
- package/dist/components/templates/Auth/types.d.ts +4 -1
- package/dist/components/templates/Auth/types.d.ts.map +1 -1
- package/dist/components/templates/Catalog/CatalogTemplate.d.ts.map +1 -1
- package/dist/components/templates/ConnectorWizard/ConnectorWizardTemplate.d.ts.map +1 -1
- package/dist/components/templates/InsightsCenter/InsightsCenterTemplate.d.ts.map +1 -1
- package/dist/components/templates/IntegrationDetail/IntegrationDetailTemplate.d.ts.map +1 -1
- package/dist/components/templates/ListDetail/ListDetailTemplate.d.ts +168 -10
- package/dist/components/templates/ListDetail/ListDetailTemplate.d.ts.map +1 -1
- package/dist/components/templates/ListDetail/index.d.ts +1 -1
- package/dist/components/templates/ListDetail/index.d.ts.map +1 -1
- package/dist/components/templates/ListDetail/listDetailLabels.d.ts +106 -0
- package/dist/components/templates/ListDetail/listDetailLabels.d.ts.map +1 -0
- package/dist/components/templates/Onboarding/OnboardingTemplate.d.ts.map +1 -1
- package/dist/components/templates/Settings/SettingsTemplate.d.ts +0 -3
- package/dist/components/templates/Settings/SettingsTemplate.d.ts.map +1 -1
- package/dist/components/templates/Wizard/WizardTemplate.d.ts +4 -1
- package/dist/components/templates/Wizard/WizardTemplate.d.ts.map +1 -1
- package/dist/components/templates/index.d.ts +2 -1
- package/dist/components/templates/index.d.ts.map +1 -1
- package/dist/data-table.d.ts +1 -1
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +1 -0
- package/dist/data-table2.js +13522 -13465
- package/dist/data.js +213 -194
- package/dist/elk.bundled.js +47371 -0
- package/dist/essentials.d.ts +3 -3
- package/dist/essentials.d.ts.map +1 -1
- package/dist/essentials.js +179 -147
- package/dist/focus-on-replace.js +49 -0
- package/dist/form.d.ts +26 -4
- package/dist/form.d.ts.map +1 -1
- package/dist/forms.js +575 -483
- package/dist/foundation.js +3704 -0
- package/dist/hooks/storageStore.d.ts +51 -0
- package/dist/hooks/storageStore.d.ts.map +1 -0
- package/dist/hooks/useAsyncAction.d.ts +65 -0
- package/dist/hooks/useAsyncAction.d.ts.map +1 -0
- package/dist/hooks/useDisclosure.d.ts +190 -0
- package/dist/hooks/useDisclosure.d.ts.map +1 -0
- package/dist/hooks/useFocusOnReplace.d.ts +95 -0
- package/dist/hooks/useFocusOnReplace.d.ts.map +1 -0
- package/dist/hooks/useTrinityPalette.d.ts +38 -40
- package/dist/hooks/useTrinityPalette.d.ts.map +1 -1
- package/dist/hooks.d.ts +44 -6
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +301 -0
- package/dist/index.d.ts +71 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13925 -60619
- package/dist/patterns/AICopilotPattern/AICopilotPattern.d.ts +203 -0
- package/dist/patterns/AICopilotPattern/AICopilotPattern.d.ts.map +1 -0
- package/dist/patterns/AICopilotPattern/index.d.ts +3 -0
- package/dist/patterns/AICopilotPattern/index.d.ts.map +1 -0
- package/dist/patterns/index.d.ts +15 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/presets/analytics.d.ts +8 -1
- package/dist/presets/analytics.d.ts.map +1 -1
- package/dist/presets/crm.d.ts +8 -1
- package/dist/presets/crm.d.ts.map +1 -1
- package/dist/presets/index.d.ts +1 -1
- package/dist/presets/portal.d.ts +8 -1
- package/dist/presets/portal.d.ts.map +1 -1
- package/dist/safe-content.js +69 -69
- package/dist/theme-augmentation.d.ts +388 -0
- package/dist/theme-augmentation.d.ts.map +1 -0
- package/dist/theme.d.ts +116 -30
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +891 -436
- package/dist/token-color.d.ts +14 -0
- package/dist/token-color.d.ts.map +1 -0
- package/dist/token-merge.d.ts +56 -0
- package/dist/token-merge.d.ts.map +1 -0
- package/dist/token-serialization.d.ts +65 -0
- package/dist/token-serialization.d.ts.map +1 -0
- package/dist/tokens.d.ts +882 -13
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +21 -19
- package/dist/trinity.css +1077 -414
- package/package.json +111 -90
- package/dist/components/index.d.ts +0 -145
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/shared.d.ts +0 -92
- package/dist/components/shared.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,2174 @@
|
|
|
1
|
+
# @trinityui/design-system
|
|
2
|
+
|
|
3
|
+
## 2.13.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- **Behavior correction (a, b, c)**: LandingPage's built-in hero gradient passes text contrast, and the client selector follows `clientName` on every render. ([`cc88eff`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/cc88eff6bf51572a3ab1d1effbac196e96301076))
|
|
8
|
+
|
|
9
|
+
These corrections ship in a patch (2.13.1) by owner decision of 2026-10-11, as PR #10's AITextBlock correction did.
|
|
10
|
+
|
|
11
|
+
LandingPage without `backgroundImage` or `backgroundGradient` drew a navy, purple and coral gradient that left the white subtitle at 3.4:1 towards the coral end (WCAG 1.4.3 asks for 4.5:1). The built-in gradient is now navy, indigo and purple: 6.8:1 or more across the hero, the same in light and dark mode. The LandingPage stories follow the owner's review: the Default story uses Smooth Abstract 13, Hero Tones uses light gradient 2 and dark gradient 4, and the Background Gallery story is gone (the Assets pages keep the images).
|
|
12
|
+
|
|
13
|
+
TopNavHeader and TopNavWithSidebar copied `clientName` into their own state on the first render and ignored every later value, so an app that switched clients elsewhere kept showing the old name. `clientName` now controls the selector (MUI `useControlled`, as `selectedNavItem` does since 2.13.0), and the new `defaultClientName` (default `'Long Client Name 1'`, as before) seeds an uncontrolled selector that a pick changes. AppLayout passes both through.
|
|
14
|
+
|
|
15
|
+
Also in this pull request, release tooling only: the publish workflows wait up to 2 minutes for npm to show a just-published version before tagging (known issue 1.19), and the GitHub release links to release notes longer than its 125,000-character limit instead of failing (1.20).
|
|
16
|
+
|
|
17
|
+
Visual review: https://github.com/Trinity-UI-Components/trinity-design-system/pull/43
|
|
18
|
+
|
|
19
|
+
## 2.13.0
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- **Additive**: Buttons for navy brand surfaces. A primary button is navy (#050742), the same navy as a hero, the header and the sidebar, so it disappeared there. `<Button variant="contained" color="onBrand">` is a white button with a navy label and `<Button variant="outlined" color="onBrand">` a white outline. Both turn coral on hover, press to navy 100, show a white focus outline and look the same in light and dark mode, because the navy chrome does not change with the mode. ([`e94e25d`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e94e25d413e2fa9fa3bc7bc77084d9c56402f5b7))
|
|
24
|
+
|
|
25
|
+
- React: the theme adds `palette.onBrand` (`main` white, `dark` navy 100, `contrastText` navy) and draws the two variants. The package's type augmentation adds `onBrand` to `ButtonPropsColorOverrides`, so `color="onBrand"` type-checks. No new export.
|
|
26
|
+
- Tokens: `componentTokens.button.onBrand` and `componentTokens.button.onBrandOutlined` (optional keys of `TrinityButtonTokens`), and the optional `borderHover`, `borderActive`, `backgroundDisabled`, `textDisabled`, `borderDisabled` and `focusOutline` on `TrinityButtonColors`.
|
|
27
|
+
- CSS: `--trinity-button-on-brand-*` and `--trinity-button-on-brand-outlined-*` (background, hover, active and disabled fills, labels, borders and the focus outline), with the `-bg` short forms, the same in both modes. The CSS guide has a "Buttons on navy surfaces" pattern.
|
|
28
|
+
|
|
29
|
+
Contrast on navy (#050742): the label is 18.83:1 at rest, 6.34:1 on hover and 11.16:1 pressed; a disabled label is 4.68:1 (filled) and 5.12:1 (outlined); the white fill, the white border and the focus outline are 18.83:1, the coral hover fill 6.34:1 and the pressed fill 11.16:1.
|
|
30
|
+
|
|
31
|
+
Visual review: the owner's Storybook review of the On brand surfaces story; approved by the owner, 2026-10-10
|
|
32
|
+
- **Additive**: Expose the documented `LandingPage` and `FeatureCard` components and their
|
|
33
|
+
public types from the package root. ([`0349a02`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/0349a022aba62cdaea73cd86fed679a29647deba))
|
|
34
|
+
- **Behavior correction (b)**: Charts load the Highcharts accessibility module: each chart is a named region with a screen-reader summary, and its points are reachable from the keyboard. ([`d9b7c23`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d9b7c23439acd24e2d0e9888624ce764ba60f009))
|
|
35
|
+
|
|
36
|
+
Every chart set `accessibility.enabled: true`, but the Highcharts accessibility module was never loaded. Highcharts ignored the option, logged a console warning asking for the module, and drew each chart as an unnamed `role="img"` SVG (axe `svg-img-alt`): screen readers heard nothing, and points, tooltips and legend entries could not be reached from the keyboard. The chart components now render with one Highcharts instance that has the module applied. A chart is a region named by `ariaLabel` (else `title`, else the chart type, for example "Line chart"), with a summary before it (type, number of points, axes, data range). Tab moves into the chart and focuses the first point, the arrow keys move between points and series and announce each point's category, value and series, and Tab moves on to the legend entries and out of the chart. `Sparkline` keeps the summary but adds no landmark and no Tab stop, as it has no tooltip or other interaction. The console warning is gone. The charts use Highcharts' ES module build, so the package still imports on a server. The root entry grows by about 32.1 kB brotli for the module (size-limit on `dist/index.js`: 663,448 B -> 695,566 B; re-measure on the release build); `/theme`, `/tokens`, `/essentials` and `/data-table` do not load the charts and are unchanged.
|
|
37
|
+
- **Additive**: LandingPage's hero text follows its background: the new `heroTone` prop (`'onDark'` or `'onLight'`) keeps the title, subtitle and sparkle readable on dark and light backgrounds. ([`7e1d953`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/7e1d953740f94011915ce60d59601c8f8659467b))
|
|
38
|
+
|
|
39
|
+
The hero text was always white, so on the light brand gradients (white to lavender) it was invisible, including in the template's own Default story. Without `heroTone`, the light brand gradients (file names `light_brand_gradient<n>` or `light-brand-gradient-<n>`) now get `'onLight'`: the theme's navy primary for the title, its secondary text for the subtitle and the AI purple for the sparkle, 12.9:1, 7.1:1 and 3.9:1 or more on every light gradient. Every other background keeps today's `'onDark'` look: white text and the coral sparkle. Set `heroTone` for your own images and for `backgroundGradient`, because the component cannot see an image's colours. The hero colours come from the theme's roles, and a custom `heroIcon` that uses `currentColor` follows the title colour.
|
|
40
|
+
|
|
41
|
+
LandingPage and FeatureCard are new in 2.13.0, so no released version had the white-on-light hero.
|
|
42
|
+
|
|
43
|
+
Visual review: the owner's Storybook review of Templates/LandingPage; approved by the owner, 2026-10-10
|
|
44
|
+
- **Behavior correction (a)**: TopNavWithSidebar and AppLayout highlight the `selectedNavItem` you pass on every render, so the sidebar follows navigation by link, URL or the back button. ([`d9b7c23`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d9b7c23439acd24e2d0e9888624ce764ba60f009))
|
|
45
|
+
|
|
46
|
+
TopNavWithSidebar copied `selectedNavItem` into its own state on the first render and ignored every later value, although AppLayout documents the prop as controlled and the routing guide derives it from the router's location. The highlight and `aria-current="page"` stayed on the first item until the user clicked the sidebar. The selection now uses MUI `useControlled`: `selectedNavItem` controls it, and without it the sidebar keeps the selection from the new `defaultSelectedNavItem` (default `'home'`, as before). The new `onSelectedNavItemChange(itemId, previousId)` on TopNavWithSidebar fires when the user picks a different page item, in both modes; AppLayout's `onSelectedNavItemChange` now also receives `previousId`, as the routing guide shows. Toggle items (the AI panel trigger) are unchanged.
|
|
47
|
+
- **Behavior correction (b, c)**: Readable AI labels in dark mode, whole option labels in Autocomplete, named DataTable row actions and a list in TopNavWithSidebar's sidebar. ([`e62ebe0`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e62ebe0b7e1dbf02f7232663825a62155e2e1602))
|
|
48
|
+
|
|
49
|
+
InsightEngine's history labels and AIRelatedQuestions' header take the theme's text colours in both themes; in dark mode they were black on the dark surfaces. Autocomplete keeps a highlighted option label in one piece: the typed match is bold inside the word instead of a separate piece with a gap and a name of its own. DataTable names every row-action button by its action's label and the overflow menu button "More actions", which the new `labels` prop (`DataTableLabels`, key `rowActionsMenu`) replaces. TopNavWithSidebar (and AppLayout's sidebar, which renders it) puts its navigation items in a list inside the "Main navigation" landmark; it looks the same.
|
|
50
|
+
|
|
51
|
+
Also in this pull request, for the remediation program (M1 PR-7): MIGRATION.md with a Behavior corrections (2.x) section and criteria-gated v3.0, the deprecation and runnable-docs guards, code samples and guides that import what the package exports, the truth pass over the contributor, governance and process guides, the MCP section of the README, the label overrides convention in NAMING.md, and the Step 5 token-role and Workstream D decision records. Those change only Markdown docs in the packages (README.md, MIGRATION.md and CONTRIBUTING.md in the React package, README.md in the CSS package) and package.json fields an install does not use (`scripts`, `size-limit`).
|
|
52
|
+
|
|
53
|
+
Visual review: docs/remediation/M1-R2-visual-review.md
|
|
54
|
+
- **Behavior correction (b)**: SimpleTable rows keep their row semantics, row checkboxes have names, and a row action works from the keyboard. ([`e2f76a8`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e2f76a8a23e793abf155d39445e22f069423c9a3))
|
|
55
|
+
|
|
56
|
+
Selectable rows no longer take `role="checkbox"`. Each row's checkbox selects (Space from the keyboard), is named by the row's first cell, and no longer runs `onRowClick` when clicked. With `onRowClick`, the first cell holds a button named by that cell: it has a Tab stop, Enter and Space run the action, and its focus ring is drawn inside the cell. A click elsewhere on the row still selects the row or runs the action, as before; a click on a button or link inside a cell does not.
|
|
57
|
+
|
|
58
|
+
Visual review: docs/remediation/HITL-B-29-simple-table.md
|
|
59
|
+
- **Behavior correction (b)**: AssessmentDrawer, AuditDrawer and EntityDetailPanel show a "Close drawer" tooltip on their close button, and PageHeader shows a "More actions" tooltip on its more-actions button. ([`6cf0811`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/6cf08111bceebae26c7358718f2d5112f8341bf7))
|
|
60
|
+
|
|
61
|
+
Each tooltip appears on hover and on keyboard focus and shows the same string as the button's accessible name, so sighted mouse and keyboard users see the name that screen reader users hear. The names do not change.
|
|
62
|
+
|
|
63
|
+
Visual review: docs/remediation/HITL-B-54-icon-button-tooltips.md
|
|
64
|
+
- **Behavior correction (b)**: Charts, page scrolls and carousel autoplay respect the user's reduced-motion setting. ([`314a3bc`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/314a3bc8f99b968ddb64923013783e035e75926d))
|
|
65
|
+
|
|
66
|
+
When the operating system asks for reduced motion (`prefers-reduced-motion: reduce`), charts draw at once instead of animating in, PageTOC and GoToTop jump to their target instead of scrolling smoothly, Autocomplete's `scrollToIndex` jumps, and Carousel autoplay does not start by itself and slides change without a transition. Without the setting nothing changes. PieChart, DonutChart and GaugeChart now also stop their entry animation with `animate={false}`.
|
|
67
|
+
|
|
68
|
+
Visual review: docs/remediation/HITL-B-64-js-motion.md
|
|
69
|
+
- **Additive**: New accessibility toolkit exports: `REDUCED_MOTION_QUERY`, `visuallyHiddenSx`, `useAnnounce` and `liveRegionProps`. ([`056db18`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/056db1839c3efad031dc683ea2aefc7974e7c833))
|
|
70
|
+
|
|
71
|
+
- `REDUCED_MOTION_QUERY` is the media query `@media (prefers-reduced-motion: reduce)`, for `sx` and `styled` styles. Use it as a key to stop a CSS animation or transition for users who prefer reduced motion: `[REDUCED_MOTION_QUERY]: { animation: 'none' }`. It tests the same media feature as `useReducedMotion`, and the browser applies it from the first paint, also in server-rendered markup, so prefer it to the hook for CSS motion.
|
|
72
|
+
- `visuallyHiddenSx` is the visually-hidden style object that `VisuallyHidden` renders, frozen. Apply it to an element that cannot be a `VisuallyHidden`, for example a native radio input inside a styled label: `<Box component="input" type="radio" sx={visuallyHiddenSx} />`. To add styles, pass it first in an `sx` array. `VisuallyHidden`'s output does not change.
|
|
73
|
+
- `useAnnounce()` returns a stable function, `announce(message, politeness?)`, that reads a short message such as "Saved" to screen readers. It uses a pair of visually hidden regions at the end of the document, created on the first call (never during render or on a server). A repeated message is read again, and the latest message per politeness wins.
|
|
74
|
+
- `liveRegionProps(options)` returns the attributes for a live container such as a chat log: `aria-live` always, and `role` (`'log'`, `'status'` or `'alert'`, which implies assertive), `aria-relevant`, `aria-atomic` and `aria-busy` only when you ask for them, so replacing hand-written attributes keeps the DOM identical.
|
|
75
|
+
- New types: `AnnouncePoliteness`, `AnnounceFn`, `LiveRegionPropsOptions` and `LiveRegionAttributes`.
|
|
76
|
+
|
|
77
|
+
`LiveRegionProvider`, which scopes announcements to a subtree (for example inside an open dialog), is built and used inside the package, but it is not a package export yet: a new component export waits for its approved proposal.
|
|
78
|
+
|
|
79
|
+
Also in this pull request, for the remediation program (M3 PR-2): the toolkit test-helper section of TESTING_STRATEGY.md, the behavior-contract rows for these exports, the ADR-35-R10 register row (role `'alert'`), and an internal resize keyboard helper with SplitPane keyboard characterization tests (H-03), which no package entry exports.
|
|
80
|
+
- **Behavior correction (a, b, d, e)**: Corrections in the accessibility toolkit, useLocalStorage, the package entries, AIChainOfThought and PredictiveTextInput. ([`b77f378`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/b77f378903c57b9cd287c840584e44bb16ce554f))
|
|
81
|
+
|
|
82
|
+
- `useAriaLive` now announces when you render no region, as its documentation shows: `announce` goes through `useAnnounce` unless `ariaProps` (which now carry a `ref`) are spread on your own region, which then announces as before, once. `announce(message, { politeness })` is accepted, and `politeness: 'off'` no longer emits `role="status"`. New type `UseAriaLiveAnnounceOptions`.
|
|
83
|
+
- `SkipLink` sits on the theme's tooltip layer, merges every form of `sx`, shows at the inline start in right-to-left pages, and keeps focus on its target.
|
|
84
|
+
- `useRovingTabIndex`'s legacy refs-array form follows `currentIndex`, stays in range, skips disabled items and follows the text direction; the registry form takes `direction` (`'ltr' | 'rtl'`, default `theme.direction`) in place of the unreleased `isRtl`. The legacy signature is unchanged.
|
|
85
|
+
- `useLocalStorage` is hydration-safe and keeps every hook on the same key in sync; the signature is unchanged.
|
|
86
|
+
- Every JavaScript entry starts with `'use client'`, so a React Server Component can import the package and render its components (DEVELOPER_GUIDE.md shows the setup).
|
|
87
|
+
- AIChainOfThought: a phase that streams in after mount follows its own `defaultExpanded`; the output mini chart draws the new `ChainArtifact.chartData` and never invents bars.
|
|
88
|
+
- PredictiveTextInput builds option ids from the list position, and the focused `<input>` is the combobox; `AITextBlock`'s `id` documentation warns that the id is rendered verbatim.
|
|
89
|
+
- `OverflowToolTip` warns about its deprecation from an effect after the first render, not during render.
|
|
90
|
+
|
|
91
|
+
Also in this pull request, for the remediation program (M3 PR-3): tested recipes for MUI `useControlled` (B-06), the internal registry entries for `useFocusOnReplace` and the storage store (B-08, B-55), internal primary-action overlay helpers that no component uses yet (B-13), the ESLint rules `trinity/sx-spread`, `trinity/rtl-physical` and `trinity/units-px` for changed lines in `src/components` and `src/patterns` (B-18; CONTRIBUTING.md in the React package documents them), the primitive decision records and keyboard models (B-51), the hand-off spec acceptance record (B-43), and the guard registry entries and behavior-contract rows for these items.
|
|
92
|
+
|
|
93
|
+
Visual review: docs/remediation/HITL-B-12-skip-link.md, docs/remediation/I-05-chain-streamed-phase-expansion.md, docs/remediation/I-05-chain-minichart-data.md
|
|
94
|
+
- **Additive**: New toolkit hooks `useDisclosure` and `useAsyncAction`, `useClipboard`'s announcement and exported types, and AIChainOfThought's `contentFormat`. ([`2743b35`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/2743b35160495833bc3add1ed82ba763f1f592ac))
|
|
95
|
+
|
|
96
|
+
- `useDisclosure({ mode, open, defaultOpen, onOpenChange, id, popupRole, disabled, panelMountedWhenClosed })` drives expand/collapse and popup triggers. It returns the open state, `setOpen`, `toggle`, the trigger and panel ids, the anchor element and three prop getters. `getTriggerProps()` sets `aria-expanded`, `aria-controls` and, in popup mode, `aria-haspopup` (one of `'dialog'`, `'menu'`, `'listbox'`, `'tree'`, `'grid'`), with ids from `React.useId`; your own `onClick` runs first and `preventDefault()` skips the toggle. `getPanelProps({ role })` names the panel after its trigger only when the role can be named. `getPopupProps()` wires an MUI Menu, Popover or Dialog and reports Escape and click-away as `'escape'` and `'clickAway'`. New types: `OpenChangeReason` (the shared reason type for open-state callbacks), `DisclosureChangeReason`, `DisclosureMode`, `DisclosurePopupRole`, `UseDisclosureOptions`, `UseDisclosureReturn`, `DisclosureTriggerProps`, `DisclosurePanelProps` and `DisclosurePopupProps`.
|
|
97
|
+
- `useAsyncAction(fn, { onError, announce })` is one contract for controls that run an asynchronous action. It returns `run`, `status` (`'idle'`, `'pending'`, `'success'`, `'error'`), `error` and `reset`. `run(...args)` handles sync throws, rejections and thenables the same way and never rejects: it resolves to `{ status: 'success' }` or `{ status: 'error', error }`. While an action is pending, `run` returns the same promise without calling `fn` again, so a double click runs it once. Errors are never swallowed: without `onError` they go to `globalThis.reportError`. Optional `announce` strings are read to screen readers through `useAnnounce` (pending and success politely, error assertively). `run` and `reset` keep their identity. While pending, mark the control `aria-disabled` and `aria-busy` instead of `disabled`, so focus stays on it. New types: `AsyncActionStatus`, `AsyncActionResult`, `UseAsyncActionOptions` and `UseAsyncActionReturn`.
|
|
98
|
+
- `useClipboard` takes `announce: { success, error }`, which reads your strings through `useAnnounce` (success politely, error assertively), so "Copied" feedback is not visual only. `UseClipboardOptions` and `UseClipboardReturn` are now exported. Without a secure context (HTTPS or localhost) or `navigator.clipboard.writeText`, `copy` resolves to `false` and `error` starts with "Clipboard unavailable", instead of a bare `TypeError`. `copy` keeps one identity across renders. Existing calls behave as before.
|
|
99
|
+
- AIChainOfThought takes `contentFormat` (`'html' | 'markdown' | 'text'`) for thoughts and insight bodies. `'html'` (the default) keeps today's safe-content allowlist; `'markdown'` parses the strings with AITextBlock's markdown renderer and its link policy, and is recommended for new integrations; `'text'` shows them as plain text. All three treat the strings as untrusted model output.
|
|
100
|
+
|
|
101
|
+
Also in this pull request, for the remediation program (M3 PR-4): AIChainOfThought's header, types and story documentation now match the code, checked by a test (WI-42a); the internal AsyncOutcomeAlert, the inline error surface for a failed async action, which no component uses yet (B-57); the assistive-technology matrix, scripted screen-reader scenarios and the results template for the release candidate (J-21); the primitive records' exit-gate rows for these hooks (B-51); and the behavior-contract rows, the internal registry entry and the changeset check that lets a feature PR add a `docs/releases/` template.
|
|
102
|
+
- **Deprecation**: `useFocusTrap` is deprecated and will be removed in v3.0. Use MUI's focus trap instead. ([`23e612f`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/23e612ffb4714e887eca783aa1ac95be88607f37))
|
|
103
|
+
|
|
104
|
+
`Dialog`, `Popover`, `Menu` and a temporary `Drawer` trap focus themselves; for a custom surface, wrap it in `FocusTrap` from `@mui/material/Unstable_TrapFocus`. The hook keeps working until v3.0. In development it logs one warning per session, from a mount effect; production builds log nothing. Its types `UseFocusTrapOptions` and `UseFocusTrapReturn` are deprecated with it.
|
|
105
|
+
|
|
106
|
+
Also in this pull request, for the remediation program (M3 PR-5): the public-API check now enforces the tag on `useFocusTrap`, the deprecation guard reads the rows of a changeset's `migration` block, and `docs/remediation/M3-index.md` records the M3 integration.
|
|
107
|
+
- **Additive**: New `AIConfigProvider` configures the AI components: strings, confidence thresholds, locale, time zone and content-free interaction telemetry. ([`e868d82`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e868d826c2d5128c59198ba46df5ba2f6502198b))
|
|
108
|
+
|
|
109
|
+
- `AIConfigProvider` takes `strings` (a deep partial of `AIStrings`), `thresholds`, `locale`, `timeZone`, `onInteraction` and `includeContent`. It renders no element and is optional: without it, every hook returns its default and never throws. Nested providers merge over their parent.
|
|
110
|
+
- Hooks: `useAIStrings()`, `useAIThresholds()`, `useAILocale()`, `useAITimeZone()` and `useAIInteraction()`. Each takes an optional value from the calling component's own prop, which wins over every provider. `useAILocale()` returns `undefined` without a locale (formatters then use 'en-US'). `useAITimeZone()` returns 'UTC' during server rendering.
|
|
111
|
+
- Telemetry events (`AIInteractionEvent`) are content-free. Feedback comments and typed clarification answers reach `onInteraction` only when `includeContent` is set. A handler that throws is contained and logged once in development.
|
|
112
|
+
- Types: `AIConfigProviderProps`, `AIStrings` and `AIInteractionEvent`.
|
|
113
|
+
|
|
114
|
+
The AI components already read their strings, thresholds, locale and telemetry through these hooks, so a provider placed around them takes effect at once; a component's own label prop still wins. Adding a string key is a minor change; renaming or removing one is major. See MICROCOPY, "Overriding strings".
|
|
115
|
+
- **Additive**: The `theme.palette.trinity` types ship with the package, and new types for the root props contract. ([`abbe9b3`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/abbe9b36f49c1016f22eef0700d304788ba5643b))
|
|
116
|
+
|
|
117
|
+
- The MUI augmentation moves from an unemitted `src/mui-theme.d.ts` to a module that the root and `./theme` entries import, so `theme.palette.trinity` is typed for consumers. `TrinityPalette`, `TrinityStatusColor`, `TrinityInteractive` and `TrinityBorder` are root and `./theme` exports. New types: `TrinityRoleColor`, `TrinityAIRoles`, `TrinityAIPresenceRoles`, `TrinityAIGradients`, `TrinityConfidenceRoles`, `TrinityVerificationRoles`, `TrinitySurfaceRoles`, `TrinityOverlayRoles`, `TrinityStateRoles` and `ResolvedTrinityPalette`. The new palette groups are optional on `TrinityPalette`. `Theme` and `ThemeOptions` gain an optional `trinityTokens`. If your app declares its own `palette.trinity` augmentation with different member types, TypeScript now reports a conflict: delete your declaration and use the exported types.
|
|
118
|
+
- `TrinitySlotProps<El>` types a component's documented `slotProps.<slot>`: `className`, `sx`, `id`, and the slot element's HTML attributes, including `aria-*`, `data-*` and event handlers. `OwnedAttribute` lists the attributes a component keeps on its root and slots: `role`, `aria-live`, `aria-busy`, `aria-relevant`, `aria-atomic` and `aria-modal`. A value you pass for one of them is ignored, with a development warning. No component changes yet: components adopt the contract one by one, each with its own changeset.
|
|
119
|
+
- OverflowTooltip measures overflow through a shared internal hook. No API change: the tooltip still turns on only while the text overflows, measured after layout, on resize and when the value changes. It now also re-measures after any re-render, so a prop that changes the text's layout without a resize (a new `variant`, for example) is measured at once.
|
|
120
|
+
|
|
121
|
+
Also in this pull request, for the remediation program (M4 PR-2): a shared test helper, `assertValidInteractiveContent`, that fails a test when an interactive element holds another one or a button holds non-phrasing content (D-44), and the internal root props helpers `useRootProps`, `mergeSx` and `mergeSlotProps` with the `describeRootPropsContract` test helper (E-15). ADR-16-R1 (OD-E14, optional new palette groups) is filed as Proposed.
|
|
122
|
+
- **Behavior correction (a, b, c)**: Corrections in the AI molecules (ChatHeader, useBrowserVoiceMode, AILabel, StatCard, GradientIconBadge, AICircularAction, the AI list renderers, AIDigestCard) and in the token flow: deep-merged token overrides, one resolved token set for the theme and the CSS, and an alpha-aware contrast utility. ([`2d2f926`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/2d2f9267117b08685f2d05d74dffb14535f6e82e))
|
|
123
|
+
|
|
124
|
+
- **ChatHeader.** ChatHeader no longer adds a banner landmark to the page.
|
|
125
|
+
`ChatHeader` rendered MUI AppBar's default `<header>`. At body level, for example inside InsightEngine's drawer, that is a banner landmark, so an open panel gave the page a second banner (WCAG 1.3.1). The root is now a `<div>` with the same classes and styles. Nothing changes visually.
|
|
126
|
+
- **useBrowserVoiceMode, server rendering.** useBrowserVoiceMode hydrates without a mismatch and cleans up its start timer.
|
|
127
|
+
- `supported` is `false` on the server and during hydration, then the browser's value. A client-only render gets the real value on its first render. The old hook read `window` during render, so a server-rendered voice button hydrated with a mismatch.
|
|
128
|
+
- The 350 ms "connecting" timer is cleared by `stop()`, on unmount and by a repeated `start()`, so a quick stop-and-start no longer opens two recognizers.
|
|
129
|
+
- Unmuting under React Strict Mode opens one recognizer, not two.
|
|
130
|
+
- **useBrowserVoiceMode, fatal errors.** useBrowserVoiceMode stops on errors a restart cannot fix, and says why.
|
|
131
|
+
After the browser denied the microphone (`not-allowed`), found none (`audio-capture`), refused the speech service (`service-not-allowed`) or could not recognize the language (`language-not-supported`), the hook restarted recognition in a loop while the UI kept saying it was listening. Voice mode now closes and the new `error` result field is `'not-allowed'`, `'no-microphone'` or `'service-unavailable'`. The next `start()` clears it. Other errors (`no-speech`, `aborted`, `network`) keep restarting as before.
|
|
132
|
+
- **AILabel.** AILabel renders a non-interactive label as static text instead of a disabled button.
|
|
133
|
+
A label is a button only when clicking it does something: `onClick` is set, or `showPopover` is on and `popoverContent` is given. Otherwise it renders a `span` with the same look and no tab stop. `clickable={false}` used to render a disabled button, so every AIExplainability panel announced one; a clickable label with nothing to do was a button that did nothing. The ref type widens from `HTMLButtonElement` to `HTMLElement`, and an array `sx` now works.
|
|
134
|
+
- **StatCard.** StatCard renders its value as a paragraph instead of a heading.
|
|
135
|
+
A KPI value is not a heading (WCAG F43), but StatCard's value rendered `h6`, `h5` or `h4` because the element followed the typography variant. The value is now a `p` with the same size and weight. The new `valueComponent` prop picks another element when the value really titles a section.
|
|
136
|
+
- **GradientIconBadge.** GradientIconBadge shows its glow for every start color format.
|
|
137
|
+
The glow color was the start color with "33" appended. That only worked for 6-digit hex: `rgb()`, `hsl()` and short hex colors produced invalid CSS, so the shadow disappeared. The glow is now `alpha(startColor, 0.2)`, the same color for hex inputs. Colors `alpha()` cannot parse, such as `var()`, use `color-mix` instead of throwing.
|
|
138
|
+
- **AICircularAction.** AICircularAction includes its badge in the button's name and shows its tooltip on focus and while disabled.
|
|
139
|
+
- The button's name now includes the badge: "Notifications, 3". The new `badgeLabel` prop words it, as a string or as a function of the badge content (`(n) => \`${n} unread\``).
|
|
140
|
+
- The badge is an MUI Badge overlapping the circle. Without content it is a dot. Its colors are the palette's secondary pair, which fixes the light-mode contrast of its text.
|
|
141
|
+
- The tooltip opens on keyboard focus and on hover while the action is disabled.
|
|
142
|
+
- **AI list renderers.** AI list components keep focus on the same item when the list changes.
|
|
143
|
+
AISourcesSection, AIRelatedQuestions, AIActionsGroup, AIPersonaCard's capability chips, AIQuickReply and AIExplainability keyed their items by position. Removing or reordering an item moved keyboard focus to a different item (WCAG 2.4.3). Each item is now keyed by its own data (URL or title, text, label), with duplicates kept apart.
|
|
144
|
+
- **AIDigestCard colors.** AIDigestCard takes its text, hover, focus and divider colors from the theme in light and dark mode.
|
|
145
|
+
The card read fixed light-mode colors. Its category, cadence and time labels were 2.27:1 in light mode, and dark mode got light-mode grays, a light hover patch and a 2.93:1 focus ring. They now use the MUI roles `text.secondary`, `action.hover`, `primary.main` and `divider`: at least 8.41:1 for the labels and 6.72:1 for the focus ring in both modes.
|
|
146
|
+
- **AIDigestCard generatedAt.** AIDigestCard marks an ISO `generatedAt` as a `<time>` element, and its documentation says how the value is shown.
|
|
147
|
+
The prop was documented as an ISO timestamp "formatted by the consumer if shown", but the card always showed the string as given. The JSDoc now says so: pass display text or a formatted timestamp. When the value is an ISO 8601 date or date-time, the card renders it as `<time datetime>` with the same text, so assistive technology and parsers can read it.
|
|
148
|
+
- **Token overrides.** Token overrides are deep-merged over the defaults (remediation §5.1).
|
|
149
|
+
`createTrinityTheme(mode, overrides)` and `createTrinityThemePreset({ tokens })` merged overrides with a shallow spread. A partial override such as `{ darkMode: { colors: { status: { success: { text } } } } }` replaced the whole `darkMode` group, so the theme threw or got `undefined` colors. The JSDoc promised optional token overrides. Overrides now merge key by key; arrays and single values replace; omitted keys keep their defaults. In development, an unknown path, a wrong-type value or an explicit `undefined` warns once.
|
|
150
|
+
Both functions take the new `TrinityTokenOverrides` type, a deep partial of `TrinityTokens` (a wider type). Overrides reach the palette, the typography colors and the component overrides. Typography sizes and families do not come from token overrides; use `createTrinityThemePreset({ typography })`.
|
|
151
|
+
- **Resolved tokens.** One set of token overrides now gives the same colors in the MUI theme and in the runtime CSS (remediation §5.1).
|
|
152
|
+
NEW `resolveTrinityTokens(overrides)` returns the resolved token set: the defaults with the overrides deep-merged, then references re-resolved, so a member that references another role follows that role unless you override it. Every Trinity theme carries it as `theme.trinityTokens`.
|
|
153
|
+
`generateCssVariables`, `generateDarkModeCssVariables`, `injectTrinityCssVariables` and `injectDarkModeCssVariables` now take overrides or a complete token set and resolve it first. A token set that overrides a referenced role (for example `semantic.colors.border.subtle`) no longer throws "Component color reference changes light output": the card border follows the role as `var()`, and a member you override on purpose keeps your value. With no argument the output is unchanged.
|
|
154
|
+
- **Contrast utilities.** `getContrastRatio` and `validateAccessibility` read any CSS color and composite alpha.
|
|
155
|
+
They parsed hex only: an `rgba()` color was read as black and an 8-digit hex lost its alpha, so the ratios for translucent roles were silently wrong, against the WCAG computation their JSDoc promises. They now read hex (3, 4, 6 and 8 digits), `rgb()`, `rgba()`, `hsl()` and `hsla()`. A translucent background is composited over the new optional `over` color (default white); a translucent foreground is composited over the background. A color they cannot read gives `NaN` with one development warning, and `validateAccessibility` returns `false` for it. Hex results are identical.
|
|
156
|
+
|
|
157
|
+
Also in this pull request, for the remediation program (M4 PR-3): the theme-flow matrix for tests and Storybook, with the crm, portal and analytics presets built from option objects whose values do not change (E-11 part 1; the option objects stay internal until OD-E6 is decided), the AI list-key and brand-override-parity guards, AIDigestCard leaving the a11y backlog, and the legacy-runtime consumer fixture passing again (E32-F2, closed by the token resolution). ADR-26-R2 (OD-E16, `theme.trinityTokens`) and ADR-37-R1 (OD-E6) are filed as Proposed.
|
|
158
|
+
|
|
159
|
+
Visual review: docs/remediation/M-02-ai-circular-action-name.md, docs/remediation/D-01-ai-digest-card-colors.md
|
|
160
|
+
- **Additive**: New palette role groups (neutral surfaces, confidence, verification and AI), the HITL contract types, response provenance, confidence utilities, a shared HeadingLevel type and GradientText's `component` prop. ([`d71774b`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d71774b6174a8db3c32c8d056fdb1586bf3d5304))
|
|
161
|
+
|
|
162
|
+
- **GradientText.** GradientText takes a `component` prop and keeps its text visible in forced-colors mode.
|
|
163
|
+
- NEW `component` renders another element, for example `component="span"` for a value or a phrase. Without it the element follows `variant` as before, so the default stays an `h6` heading.
|
|
164
|
+
- The ref is typed `HTMLElement` and points at the rendered element.
|
|
165
|
+
- In forced-colors mode (Windows contrast themes) the gradient is dropped and the text uses the system text color, so it never turns invisible.
|
|
166
|
+
- **Neutral palette groups.** `theme.palette.trinity` exposes the neutral light and dark pairs that already exist as tokens.
|
|
167
|
+
New groups, set in both modes and in every preset: `surface` (`elevated`, `sunken`, `subtle`), `overlay` (`hover`, `hoverSubtle`, `pressed`, `selected`), `state` (`errorSubtle`, `errorEmphasis`, `warningSubtle`, `warningEmphasis`, `successSubtle`, `successEmphasis`, `infoSubtle`, `infoEmphasis`), and `border.focus`. Light values come from the semantic tokens; dark values come from the dark surface and effect tokens, so token overrides reach them. The values equal the existing `--trinity-surface-*`, `--trinity-overlay-*` and `--trinity-state-*` CSS variables. `text.tertiary` is not exposed: it fails contrast in light mode.
|
|
168
|
+
- **Confidence and verification roles.** Confidence and verification color roles, as references to the status roles.
|
|
169
|
+
`theme.palette.trinity` gains `confidence` (`high`, `medium`, `low`) and `verification` (`verified`, `golden`, `pending`, `flagged`, `rejected`), each with `text`, `background` and `border`, in both modes and every preset. They are references, not new colors: high, verified and golden use the success role, medium and pending use info, low and flagged use warning, and rejected uses error. A status override changes them too.
|
|
170
|
+
The CSS package gains `--trinity-confidence-<level>-<key>` and `--trinity-verification-<status>-<key>`, each a `var()` alias of the matching `--trinity-status-*` variable, declared in `:root` and in both dark blocks. The runtime CSS functions emit the same aliases as `--trinity-semantic-confidence-*` and `--trinity-semantic-verification-*`. No token object holds these values.
|
|
171
|
+
- **AI color roles.** Mode-aware AI color roles: `semanticTokens.colors.ai`, `darkModeTokens.colors.ai`, `theme.palette.trinity.ai` and `--trinity-ai-*`.
|
|
172
|
+
Twelve roles (`primary`, `primaryFill`, `onPrimaryFill`, `accent`, `surface`, `surfaceHover`, `surfaceEmphasis`, `surfaceEmphasisHover`, `border`, `badgeBackground`, `typingIndicator`, `focusRing`) and five decorative `presence` colors, each with a light and a dark value. The new `ai` token groups and the `TrinityAIColors` and `TrinityAIPresenceColors` types are optional additions, so token sets written before 2.13.0 keep compiling. Light values equal the existing `aiTokens` colors. In light mode `primary`, `primaryFill`, `typingIndicator` and `accent` follow `brand.secondary` and `brand.accent`, and the `surface`, `border` and `badgeBackground` tints are recomputed when you override `brand.secondary`.
|
|
173
|
+
The CSS package gains `--trinity-ai-*` in `:root` and both dark blocks; in `:root` the brand references are `var(--trinity-brand-secondary)` and `var(--trinity-brand-accent)`. The runtime CSS functions emit `--trinity-semantic-ai-*`. No component uses the roles yet, so nothing renders differently.
|
|
174
|
+
- **HITL contract types.** New HITL contract types for AI data and interactions, exported from the package root.
|
|
175
|
+
- Confidence: `AIConfidenceLevel` and `AIConfidenceThresholds`.
|
|
176
|
+
- Verification: `AIAnswerVerificationStatus` (answer states) and `AIVerificationStatus`, which adds the plan state `'confirmed-plan'`.
|
|
177
|
+
- Filters: `AIFilterOperator` and `AIPlanFilter`.
|
|
178
|
+
- Clarification: `AIClarificationReason`, `AIClarificationOption`, `AIClarificationAnswer` and `AIClarification`.
|
|
179
|
+
- Feedback: `AIFeedbackRating`, `AIFeedbackCategory`, `AIFeedbackCategoryOption`, `AIFeedbackDraft` and `AIFeedbackEvent`.
|
|
180
|
+
- Query metadata: `AIQueryMetadata`.
|
|
181
|
+
- Content-free interaction payloads: `AIFeedbackInteraction`, `AIClarificationInteraction`, `AICopyInteraction`, `AISourceOpenInteraction` and `AIQueryViewInteraction`.
|
|
182
|
+
All of them are plain JSON data. The contract: confidence is a number from 0 to 1, timestamps are ISO 8601 strings, ids are stable strings, unions are open (keep a default branch when you switch over one), and every field added later is optional. Type-only: no runtime or bundle change.
|
|
183
|
+
- **Response schema.** The AI response schema types are exported from the package root, and a response can now carry source provenance, a verification object, table row identity, cell and point references and a citation flag.
|
|
184
|
+
- New root type exports: `AIResponse`, `AIResponseSource`, `AIResponseMetadata`, `AIResponseVerification`, `AISourceCellRef`, `AISourcePointRef`, `ResponseTextBlock`, `ResponseTableBlock` and `ResponseChartBlock`. Before, they were reachable only through `AIMessageProps['response']`.
|
|
185
|
+
- `AIResponseSource` gains optional `metric`, `filters`, `period`, `dataset`, `asOf` (ISO 8601), `queryId`, `cellRefs` and `pointRefs`.
|
|
186
|
+
- `ResponseTableBlock` gains `rowKey`, so a cell reference survives sorting. `ResponseTextBlock` gains `citations` (default `false`).
|
|
187
|
+
- `AIResponseMetadata` gains `confidenceRationale` and `verification`, an `AIResponseVerification` object that takes answer states only.
|
|
188
|
+
Every new field is optional, so existing responses still type-check. No runtime change: nothing reads `citations` yet. Later releases add block types to the response block union: when you switch over block types, keep a default branch.
|
|
189
|
+
- **Confidence utilities.** New confidence utilities for 0-1 confidence values, exported from the package root.
|
|
190
|
+
- `DEFAULT_AI_CONFIDENCE_THRESHOLDS`: `{ medium: 0.6, high: 0.85 }`, placeholders owned by product.
|
|
191
|
+
- `validateConfidence(value)`: rejects NaN and non-numbers (`'not-a-number'`) and values outside 0-1 (`'out-of-range'`, for example 92).
|
|
192
|
+
- `resolveConfidenceThresholds(partial)`: fills in partial thresholds and falls back to the defaults unless `0 <= medium <= high <= 1`.
|
|
193
|
+
- `roundConfidence(value, fractionDigits)`: rounds the way Intl percent formatting does, so 0.565 becomes 0.57 ('57%'), where `Math.round(0.565 * 100)` gives 56.
|
|
194
|
+
- `getConfidenceLevel(value, thresholds)`: `'low'`, `'medium'` or `'high'`, with inclusive bounds, or `null` for an invalid value.
|
|
195
|
+
- `formatConfidence(value, locale, options)`: a locale percent through `Intl.NumberFormat`.
|
|
196
|
+
- `describeConfidence(value, options)`: validates, rounds once, then rates and formats the same rounded number, so the level and the text always agree.
|
|
197
|
+
- `normalizeConfidence(value, 'percent' | 'unit')`: converts 0-100 data explicitly; nothing guesses the scale.
|
|
198
|
+
The functions are pure (no React, DOM or console) and safe during server rendering.
|
|
199
|
+
- **HeadingLevel.** New `HeadingLevel` type (`2 | 3 | 4 | 5 | 6`) for heading-level props, exported from the package root.
|
|
200
|
+
Components that render their own title as a heading take `headingLevel?: HeadingLevel`; level 1 is reserved for the page title. The default always keeps the element the component rendered before, so adding the prop changes no DOM. The rule is in NAMING, "Heading level". Type-only: no runtime or bundle change.
|
|
201
|
+
|
|
202
|
+
Also in this pull request, for the remediation program (M4 PR-4): `AIConfigProvider`, its five hooks and the AI strings catalog, built and tested but not yet package exports: the provider is a new component, and its export waits for the E-23 component RFC vote (A-21); the Storybook page AI/AIConfigProvider documents it. The unused internal DataTable `EditableCell` is removed; it was on no package entry, and DataTable edits cells through AG Grid's editors as before (D-41). The OD-I13 waiver for GradientText's heading default waits for the owner's confirmation.
|
|
203
|
+
|
|
204
|
+
Visual review: docs/remediation/M-02-gradient-text-forced-colors.md
|
|
205
|
+
- **Behavior correction (a, b, c)**: Theme corrections: the light error red passes contrast on its own background, subtree theming in the CSS package, a fallback-complete palette resolver, an override-aware token hook, and focus rings for Checkbox, Radio and Switch. ([`9e34b4c`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9e34b4c3cd9f057d56658b22962aa907ef95cb2e))
|
|
206
|
+
|
|
207
|
+
- **Light error red.** The light error red is `#DA1E28` instead of `#DC2626`, so error text passes contrast on its own tinted background.
|
|
208
|
+
`status.error.text`, `text.error`, `border.error` and the error, failed and rejected indicator fills use `#DA1E28` in light mode: 4.57:1 on `status.error.background` (was 4.41:1) and 5.00:1 on white (was 4.82:1). The CSS package's input error border and its focus glow follow. Dark mode does not change.
|
|
209
|
+
- **Subtree theming (CSS).** `data-theme` now themes any subtree in the CSS package, as the CSS guide says.
|
|
210
|
+
INSTRUCTIONS_CSS.md promised dark mode on `<html>` "or any ancestor", light islands with `<div data-theme="light">` and dark shadow hosts. The generated CSS declared its `var()` aliases only in `:root` and had no `[data-theme="light"]` block, so inside a dark section aliases such as `--trinity-status-success-bg` kept the light value, and a light island stayed dark. The CSS now re-declares every theme-dependent alias in each theme block and adds a `[data-theme="light"]` block that restores the light values. `injectDarkModeCssVariables` does the same, and `generateDarkModeCssVariables` includes the aliases. Whole-page theming (`<html data-theme>` and the system preference) gives the same value for every variable as before, checked in Chromium. The unbuilt reference copy `src/styles/trinity.css.legacy` is deleted.
|
|
211
|
+
- **Palette resolver.** `useTrinityPalette` and `getTrinityPalette` are root exports and always return a complete, contrast-safe palette.
|
|
212
|
+
Both return the new `ResolvedTrinityPalette`: every group, including `ai` with its derived `gradient` strings, `confidence`, `verification`, `surface`, `overlay` and `state`. Under a Trinity theme they return the theme's own groups. Under any other MUI theme the missing groups come from the MUI palette: status text is `palette[x].dark` (or `.light` in dark mode) adjusted until it reads at 4.5:1 on the paper and on its own tint, instead of `.main` on `.light` (1.4-1.8:1, failing WCAG 1.4.3). The per-render console warning is gone, and results are cached per theme.
|
|
213
|
+
The CSS package gains `--trinity-ai-gradient-primary`, `-horizontal`, `-diagonal`, `-subtle`, `-text` and `-border`, built from `var(--trinity-ai-*)`, so they match the resolver's strings in both modes; the runtime CSS functions emit `--trinity-semantic-ai-gradient-*`.
|
|
214
|
+
- **useTrinityTokens.** `useTrinityTokens` reads the theme's resolved token set, so token overrides reach it.
|
|
215
|
+
The hook promised theme awareness but read the module-level tokens: under `createTrinityTheme(mode, overrides)`, `semantic`, `darkMode`, `getSemanticColor`, `effect` and `shadow` returned the defaults. It now reads `theme.trinityTokens` (the defaults under a plain MUI theme). `getSemanticColor` accepts dotted names such as `'success.text'` or `'surface'` (it returned `''` for nested roles), checks the dark value first in dark mode, and warns once in development for a path that does not exist. `getColor(light, dark)` stays as an app-level escape hatch; components read colors through `useTrinityPalette()`.
|
|
216
|
+
- **Base theme gaps.** Checkbox, Radio and Switch show the theme's keyboard focus ring, and the dark theme no longer paints light-mode or stale greys on the Accordion divider and the page body.
|
|
217
|
+
MUI focuses the hidden input inside these controls, so the theme's button focus ring never matched them: keyboard users saw only the low-contrast focus ripple (Checkbox, Radio) or a thumb colour change (Switch). They now get the same 2px ring as buttons (navy in light, coral in dark) around the icon or the track, and the system highlight colour in forced-colors mode. Pointer clicks show no ring.
|
|
218
|
+
In dark mode, the Accordion summary divider was the light theme's gray[100], a bright line on the dark page; it is now the dark subtle border. The theme also stops overriding MUI's CssBaseline body colours, so the body uses `palette.background.default` and `palette.text.primary` in every mode and preset: the dark page is the dark theme's background instead of gray[800]. Light mode with the default theme is unchanged.
|
|
219
|
+
|
|
220
|
+
Also in this pull request, for the remediation program (M4 PR-5): Storybook paints every story from the theme matrix, with preset, direction and container-width toolbars and the visual modes for the PNG compare (E-12; Storybook only, nothing in the published packages), and the non-trinity-theme and storybook-theme-matrix guards. ADR-26-R3 (the resolver logs nothing; it supersedes ADR-26-R1's development-only warning clause) is filed as Proposed.
|
|
221
|
+
|
|
222
|
+
Visual review: docs/remediation/ADR-03-R2-light-error-text.md, docs/remediation/size/E-37.md, docs/remediation/HITL-E-45-base-theme-gaps.md
|
|
223
|
+
- **Deprecation**: Deprecations for v3.0: `brandColors.neutral.darkBg` and `darkPaper`, and the five `*Dark` color keys of `aiTokens`. ([`0ee1e89`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/0ee1e89acba586f9afcd62db2df8138928ee8b2e))
|
|
224
|
+
|
|
225
|
+
### brandColors
|
|
226
|
+
|
|
227
|
+
`brandColors.neutral.darkBg` and `brandColors.neutral.darkPaper` are deprecated: they are not the dark theme's surfaces.
|
|
228
|
+
|
|
229
|
+
The dark theme paints its page with `theme.palette.background.default` and its paper with `theme.palette.background.paper`; these two constants hold older, lighter greys. Their values and types do not change, and they warn only in your editor (no runtime warning). They are removed in v3.0.
|
|
230
|
+
|
|
231
|
+
`brandColors` now has one source: the copy that the package root, essentials and `theme` entries export reads every value from the same design tokens as the `tokens` entry's `brandColors`, with the same keys, types and values as before. Editing the object never changed the theme; to rebrand, pass token overrides to `createTrinityTheme` or `createTrinityThemePreset`.
|
|
232
|
+
|
|
233
|
+
Removal in v3.0 (the release PR adds these rows to MIGRATION.md's "Deprecated props and token keys removed in v3.0" table):
|
|
234
|
+
|
|
235
|
+
| Removed in v3.0 | Replace with | Notes |
|
|
236
|
+
|---|---|---|
|
|
237
|
+
| `brandColors.neutral.darkBg` (root, essentials, `theme` and `tokens` entries) | `theme.palette.background.default` (or `palette.trinity.surface`) | Not the dark theme's page colour. The value stays the same until removal. |
|
|
238
|
+
| `brandColors.neutral.darkPaper` (root, essentials, `theme` and `tokens` entries) | `theme.palette.background.paper` (or `palette.trinity.surface.elevated`) | Not the dark theme's paper colour. The value stays the same until removal. |
|
|
239
|
+
|
|
240
|
+
### aiTokens
|
|
241
|
+
|
|
242
|
+
`aiTokens` is a static light-mode facade over the AI color roles; its five `*Dark` color keys are deprecated.
|
|
243
|
+
|
|
244
|
+
`aiTokens` keeps every value, now defined from `semanticTokens.colors.ai` (one color source). Its colors never follow the theme mode or brand overrides; components read `useTrinityPalette().ai` or `getTrinityPalette(theme).ai`. The JSDoc no longer tells you to choose `*Dark` keys with `theme.palette.mode`. `aiTokens.radius` is documented correctly: the values are multiples of `theme.shape.borderRadius` (8px in Trinity themes), so `sm: 1.5` renders 12px, not 6px; use `aiTokens.radiusPx` for fixed pixels. Nothing renders differently, and no runtime warning is added.
|
|
245
|
+
|
|
246
|
+
Replacements, for removal in v3.0:
|
|
247
|
+
|
|
248
|
+
| Deprecated key | Replacement |
|
|
249
|
+
|---|---|
|
|
250
|
+
| `aiTokens.colors.aiBackgroundDark` | `useTrinityPalette().ai.surface` (dark mode), or `ai.badgeBackground` for the light emphasis fill |
|
|
251
|
+
| `aiTokens.colors.aiBorderDark` | `useTrinityPalette().ai.border` |
|
|
252
|
+
| `aiTokens.colors.aiHoverDark` | `useTrinityPalette().ai.surfaceEmphasisHover` |
|
|
253
|
+
| `aiTokens.colors.sourceBadgeBgDark` | `useTrinityPalette().ai.badgeBackground` |
|
|
254
|
+
| `aiTokens.colors.typingDotDark` | `useTrinityPalette().ai.typingIndicator` |
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
Also in this pull request, for the remediation program (M4 PR-6): the brandColors consumer record and the Grid demo story off `darkPaper` (E-36), and the corrected `aiRadius` documentation (E-07).
|
|
258
|
+
- **Behavior correction (a, b, c)**: Theme and component corrections: MUI status palettes take Trinity's accessible status pairs, Typography colour props and the light caption and overline colours reach the text, overlays stack on named theme layers, and the voice, source and Icon components are named and operable. ([`8ca2e1c`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/8ca2e1cbb7e4d4a489841e36fbcabca20e046d1e))
|
|
259
|
+
|
|
260
|
+
- **MUI status palettes (ADR-03-R7).** MUI components in a status color use Trinity's accessible status colors in light and dark mode.
|
|
261
|
+
The Trinity theme left MUI's `palette.error`, `warning`, `success` and `info` at MUI's defaults, so Alert, Chip, TextField's error state, Checkbox, Radio, LinearProgress, contained status Buttons and every Trinity component that reads `theme.palette.<status>` (Toast, FileUpload, StatusHero and others) drew MUI's red, orange, blue and green. `palette.<status>.main` is now the mode's `status.<status>.text` token (light `#DA1E28`, `#B45309`, `#15803D`, `#0E62CA`; dark `#FCA5A5`, `#FDE68A`, `#86EFAC`, `#7DD3FC`) and `contrastText` the new `onStatus.<status>` token (white in light mode, `#09090B` in dark mode); MUI derives `light` and `dark` from `main`. Enabled contained Buttons in a status color take `contrastText` as their label. The optional token group `onStatus` (`TrinityOnStatusColors`, a root and `./tokens` type) and the CSS variables `--trinity-on-status-error`, `-warning`, `-success` and `-info` (runtime CSS: `--trinity-semantic-on-status-*`) are new, so CSS-only apps draw the same solid status pairs. A plain MUI theme keeps MUI's palettes.
|
|
262
|
+
- **Typography colour props (E-52+).** Every `color` prop on a Typography rendered its parent's colour: the theme's root rule (`color: inherit`) beat MUI's colour variants, and MUI 9.4 no longer resolves the dotted system form (`text.secondary`). With a `color` prop the text now takes that palette colour, dotted paths included; without one it still inherits. Library components use the native forms, so their secondary text renders `palette.text.secondary`.
|
|
263
|
+
- **Light caption and overline (E-35).** The light theme's `caption` and `overline` typography default to `text.secondary` (`#6B7280`, 4.83:1 on white) instead of `text.tertiary` (`#9CA3AF`, 2.54:1). Dark themes are unchanged. `text.tertiary` is for disabled or decorative use only until Step 7 re-derives it (L-01). The CSS guides say the same for `--trinity-text-secondary`.
|
|
264
|
+
- **Overlay layers (E-46+).** Toast and GoToTop sat at `z-index: 9999`, above dialogs, tooltips and the skip link, and SearchInput's suggestions at 1000, under a sticky app bar. Trinity themes now carry named layer roles on `theme.zIndex` (`aiPanel`, `appChrome`, `aiFab`, `popover`, `toast`, `skipLink`, `diagnostic`, derived from MUI's scale, ADR-03-R12; optional keys on MUI's `ZIndex` type), and each overlay reads its role from the theme it renders in.
|
|
265
|
+
- **sx composition (E-16).** ChartWrapper (and the charts on it), Sparkline, DiffViewer, HistoricalDataIndicator, FeatureCard, LandingPage, PageHeader and StatusHero dropped an array or function `sx`; they now merge it as an ordered list, the consumer's styles last. DiffViewer's `sx` is typed `SxProps<Theme>`.
|
|
266
|
+
- **Fractional spacing names (E-51+).** The CSS outputs declared `--trinity-space-0.5`, `-1.5` and `-2.5`, which browsers drop and Lightning CSS (Vite 8) rejects. Only `--trinity-space-0-5`, `-1-5` and `-2-5` are declared now, and `generateCssVariables()` names the same keys `--trinity-spacing-0-5`, `-1-5` and `-2-5`.
|
|
267
|
+
- **AIVoiceMode (WI-27, WI-50).** The full-screen variant is a modal dialog named by its title, with focus inside and Escape to close; inline it is a named group. The status is a paragraph announced politely, and only microphone changes are announced. The mute toggle is always named "Mute microphone", with `aria-pressed` for the state.
|
|
268
|
+
- **AIVoiceOverlay and AIListeningIndicator (WI-28a).** The mic is a button named "Voice input" with `aria-pressed`; the close and stop buttons are named; listening changes are announced; the pulse stops under reduced motion.
|
|
269
|
+
- **AISource (WI-20b, WI-20a).** With a handler the title is a button covering the card and the URL its own link; without one the card is plain content. The type is an image named by the type, and a `video` source shows a play glyph.
|
|
270
|
+
- **Icon (C-16, C-31).** Icon forwards its ref to the svg, hides decorative Feather icons (`aria-hidden`), names informative icons with the new `title` prop, forwards `aria-*`, `id` and `data-*`, and warns once per unknown name in development only. The new type `IconName` lists the names it renders. `color="success"`, `"warning"` and `"info"` render the palette colour (they produced an invalid CSS colour), and every palette key gives a valid colour. Literal hex and `rgb()` values in `color` still work but are deprecated (JSDoc only): use a palette key, or set the colour on a parent and pass `color="inherit"`.
|
|
271
|
+
|
|
272
|
+
Also in this pull request, for the remediation program (M4 PR-7, wave 2): M3's step 8 behavior-guard evolution (B-02's guard half, B-16, B-65+, B-17, B-19; tooling only); the ESLint theming rules and E-31's lint acceptance (E-08), the theming debt ratchet `check:theming` (E-09), the contrast matrix v2 (E-10), the AI colour migration map (E-26) and the atom characterization tests (C-01), none of which changes the published packages. ADR-11-R1, ADR-20-R1 and ADR-22-R10 (OD-E8's rule ids) are filed Proposed.
|
|
273
|
+
|
|
274
|
+
Visual review: docs/remediation/ADR-03-R7-mui-status-palettes.md, docs/remediation/HITL-E-46-zindex-roles.md, docs/remediation/L-01-text-tertiary-interim.md, docs/remediation/I-05-typography-color-props.md, docs/remediation/M-02-voice-mode-announcements.md, docs/remediation/M-02-voice-input-announcements.md, docs/remediation/A-01-ai-source-card.md, docs/remediation/HITL-C-31-icon-color.md, docs/remediation/M-02-ai-source-type.md
|
|
275
|
+
- **Additive**: New tokens and names: motion tokens, container widths, AI typography roles, the 24px target-size floor, 22 icon names, AISource's badge, meta and icon, the AI strings catalog and a provider locale for voice mode. ([`b85af49`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/b85af493b78d40479973ac43597313da9c91ab25))
|
|
276
|
+
|
|
277
|
+
- **Motion tokens (E-13).** `semanticTokens.motion` gains `duration.slower` (400ms), `easing.emphasized` (`cubic-bezier(0.16, 1, 0.3, 1)`), `easing.overshoot`, `loop.spin`, `loop.typing`, `loop.shimmer` and `loop.pulse` (800, 1200, 1500 and 2000ms), and `stagger.tight` and `stagger.normal` (50 and 100ms); `aiTokens.motion` names the AI roles `typing`, `shimmer` and `expand`. Both CSS outputs and `generateCssVariables()` declare `--trinity-easing-expo-out`, `--trinity-motion-duration-slower`, `--trinity-motion-easing-emphasized`, `--trinity-motion-easing-overshoot`, `--trinity-motion-loop-*` and `--trinity-motion-stagger-*`. The CSS guides now name only motion variables the CSS declares: they used `--trinity-easing-standard`, `-emphasized` and `-decelerated`, which never existed; use `--trinity-motion-easing-default`, `-emphasized` and `-enter`.
|
|
278
|
+
- **Container widths (E-19).** `semanticTokens.spacing.layout.containerQuery` (`narrow` 320, `compact` 360, `regular` 600) for components hosted in a narrow panel: usable at 320px, toolbars wrapping below 360px, the full layout from 600px. They are JavaScript-only (a custom property cannot be an `@container` condition), so no CSS variable is added; the CSS guide shows the widths as numbers.
|
|
279
|
+
- **AI typography roles (E-14).** `aiTokens.typography` gains `badge` (10px), `code` (the monospace data family at 12px), `numeric` (tabular figures) and `metric` (20px tabular figures, with `data-type="metric"` so a preset's metric font applies). AI text takes its size from a Typography variant and sets its colour explicitly; these roles are for text no variant fits.
|
|
280
|
+
- **Target-size floor (C-40+).** `scaleHierarchy.touchTargets.floor` (24) and `--trinity-touch-target-floor`: the minimum pointer target (WCAG 2.2 SC 2.5.8). CSS-only apps can use the shared hit-area recipe with the variable ("Target size" in INSTRUCTIONS_CSS.md).
|
|
281
|
+
- **Icon names (C-17).** `Icon` gains 22 names, so `IconName` widens: `verified`, `x-circle`, `user-check`, `history`, `edit-3`, `git-branch`, `database-search`, `thumbs-up-filled`, `thumbs-down-filled`, `help-outline`, `maximize-2`, `minimize-2`, `pin`, `unpin`, `bookmark-outline`, `delete-outline`, `chat-bubble-outline`, `file-outline`, `folder-outline`, `edit-outline`, `view-stream` and `circle`. Every name renders in both libraries; glyphs a library lacks are drawn in its style and ship in the package (no new dependency). With the names in place, Feather's `sparkles` draws a sparkle instead of a star, and InsightEnginePanel's boxed-layout toggle, which asked for `maximize-2` and `minimize-2`, shows its icon.
|
|
282
|
+
- **AISource (WI-20c).** `number` is optional (an unnumbered reference renders no number badge), and the new `badge`, `meta` and `icon` props show a chip after the title, a detail line and a custom glyph that keeps the type as its accessible name.
|
|
283
|
+
- **AI strings catalog (E-25).** `AIStrings` has a key for every user-facing literal of the AI components, AppLayout's AI panel and AIDigestCard (136 keys, one group per component, each defaulting to today's text; strings with a number or a name are functions). The scaffold key `ai.input.sourcesSelected` is now `ai.insightEngineInput.sourcesSelected`; `AIConfigProvider` is not exported yet, so no code can hold the old key. No component reads the new keys yet.
|
|
284
|
+
- **Voice mode locale (WI-28d).** `useBrowserVoiceMode` takes its language from the hook's `lang`, then the nearest `AIConfigProvider` locale, then `en-US`. Without a provider locale nothing changes.
|
|
285
|
+
|
|
286
|
+
Also in this pull request, for the remediation program (M4 PR-8, wave 2): test utilities that take a preset, direction and theme overrides (E-11 part 2), the right-to-left policy and the internal direction and bidi helpers (E-18, E-48+), the NAMING conventions for root props, slots, render props, provisional size, units, async and streaming state, block APIs and internal components (E-17, E-21, D-45, D-03), and the icon-import policy, `trinity/icon-imports` and `check:icon-imports` (C-19). None of these changes the published packages.
|
|
287
|
+
|
|
288
|
+
Visual review: docs/remediation/I-06-icon-manifest.md, docs/remediation/I-04-ai-source-extension.md
|
|
289
|
+
- **Behavior correction (a, b, c)**: Theme and AI component corrections: dark indicator fills and light borders that meet contrast, avatar text pairs, a reduced-motion backstop, and AI components that are named, operable, reachable at 24px, usable at 320px and readable in dark mode. ([`4fd2ea0`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/4fd2ea0a2be5a07020193b7bb8ca564a3de23f50))
|
|
290
|
+
|
|
291
|
+
- **Indicators and borders (E-43+).** `theme.palette.trinity.indicator` gives every StatusIndicator status its fill, the text on the fill, an optional outline and its on-page colour per mode, built from the token catalog (now the optional `TrinitySemanticColors.indicator`). In dark mode the info, new and updated fills become `#0F62FE` (they were 1.93-2.40:1); StatusIndicator and the CSS package's dark blocks follow. The light `palette.trinity.border` and `.interactive` read the semantic tokens the CSS package already uses (`border.strong` 4.83:1 on white instead of 2.54:1).
|
|
292
|
+
- **Avatar pairs and the neutral fill (E-49+).** `avatar.foregrounds` gives each avatar fill its text colour (white or gray 900, 4.72:1 or more; three fills failed with white), with `--trinity-avatar-foregrounds`; the optional `palette.trinity.surface.neutralFill` (grey 300 light, gray 600 dark, `--trinity-surface-neutral-fill`) backs icon discs.
|
|
293
|
+
- **Reduced-motion backstop (E-44+).** Every Trinity theme's CssBaseline collapses transitions and animations to 0.01ms under `prefers-reduced-motion: reduce`, except on `trinity-essential-motion`; `theme.transitions` read the motion tokens with the same values. The CSS guide gives the same rule.
|
|
294
|
+
- **AIShimmer (WI-19a).** Decorative (`aria-hidden`), theme colours in dark mode, scoped sweep that stops under reduced motion, `borderRadius` as a length or a multiplier, and the root props contract.
|
|
295
|
+
- **InsightEngineInput (WI-29a, WI-29b).** A forced-colors focus outline, popup state on the "+", source, model and voice-mode buttons, the mic pulse on the motion tokens, MUI's controlled/uncontrolled rule, and controls that wrap instead of clipping down to a 320px container; upload progress is one named progress bar per file.
|
|
296
|
+
- **AILabel, ChatHistoryList, AIPersonaCard, AIDigestCard (WI-16b, WI-23a, WI-54, WI-17, WI-30c).** AILabel's popover is a named modal dialog (new `popoverLabel`, `popoverLabelledBy`) and compact clickable pills reach the 24px target; ChatHistoryList is a list of buttons with `aria-current`; AIPersonaCard with `onClick` is a toggle button with `aria-pressed`; AIDigestCard's clickable highlights get a native button (new `actionLabel`) and keep their text structure and links.
|
|
297
|
+
- **Disabled text (WI-34).** Informative text and enabled-control icons on the AI surfaces use `text.secondary` instead of `text.disabled`.
|
|
298
|
+
- **AI colours and motion on the lines this release touched.** AIActions, AIPersonaCard, AISource, AILabel and InsightEngineInput read their hover, selection and glow colours from the theme's AI roles (identical in light; the dark roles in dark mode), and AIVoiceOverlay's pulse and AIListeningIndicator's blink use the motion tokens under scoped names.
|
|
299
|
+
- **Deprecated declarations (J-23).** `AIChatMessage`, `AIChatInput`, `AITypingIndicator` and `AIQuickReply` carry `@deprecated` on their declarations, as MIGRATION.md and their runtime warnings already said; nothing else changes.
|
|
300
|
+
|
|
301
|
+
Also in this pull request, for the remediation program (M4 PR-9, wave 2): the Storybook contract, catalog parity, status tags, HITL traceability, the definition of done, the forced-colors test project, motion freezing and the PNG compare's diff images (J-30+, J-08, J-10, J-13, J-23, J-31+, J-17, J-19), and the AI strings catalog keys for WI-16b's and WI-29a's new labels (E-25). None of these changes the published packages except the AIChat declarations above.
|
|
302
|
+
|
|
303
|
+
Visual review: docs/remediation/I-02-palette-border-indicator.md, docs/remediation/L-01-contrast-matrix-v2.md, docs/remediation/E-44-theme-motion-backstop.md, docs/remediation/M-02-ai-shimmer-decorative.md, docs/remediation/M-02-insight-input-forced-colors.md, docs/remediation/F-06-insight-input-narrow-container.md, docs/remediation/A-01-ai-persona-card-selection.md, docs/remediation/A-01-ai-digest-card-highlight.md, docs/remediation/L-01-ai-disabled-text-misuse.md, docs/remediation/A-02-ai-target-size.md
|
|
304
|
+
- **Additive**: ClearableTextField's `id` prop is optional. ([`9218ba5`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9218ba56599775ae83f3f76e30693988df0298e6))
|
|
305
|
+
|
|
306
|
+
Without an `id`, the field takes a generated one (MUI's `useId`), so its label and helper text still point at the input and two fields never share an id. Passing an `id` works as before.
|
|
307
|
+
- **Behavior correction (b)**: DataCard's clickable card works from the keyboard, its More button is named, and sparklines no longer share gradient ids. ([`6197480`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/6197480c272176ab0227a5c05f0fd112f30a51e4))
|
|
308
|
+
|
|
309
|
+
With `onClick`, the card has a primary action: a button that covers the card, named by its title, with a Tab stop, Enter and Space, and a focus ring drawn inside the card edge. The More button stays a separate button after it, named "More actions" by default with the same text as its tooltip; the new `moreActionsLabel` prop changes both. Each sparkline's gradient has its own id, so two cards with the same trend colour no longer share one.
|
|
310
|
+
|
|
311
|
+
Visual review: docs/remediation/M4-index.md
|
|
312
|
+
- **Behavior correction (b)**: Timeline's expandable items have a named "Show details" button in every variant, and clickable items work from the keyboard. ([`bd6d477`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/bd6d477ec779f59e0f0282b40944d191d34848be))
|
|
313
|
+
|
|
314
|
+
An item with expandable content (`expandedContent` or `children`) shows a button named "Show details" ("Hide details" while open) with the same text as its tooltip, `aria-expanded` and `aria-controls` pointing at the content, in the default, compact and detailed variants; the default and compact variants had no button, and the detailed one had an unnamed one. A click on the item's content still expands it. With `onItemClick`, an item without expandable content is a button named by its title, with a Tab stop, Enter and Space. The new `labels` prop (`TimelineLabels`: `expandDetails`, `collapseDetails`) changes the button's names.
|
|
315
|
+
|
|
316
|
+
Visual review: docs/remediation/M4-index.md
|
|
317
|
+
- **Behavior correction (b)**: CustomLegend and InteractiveLegend entries that toggle a series are toggle buttons, and each legend is a named group. ([`7c4fdc0`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/7c4fdc0145582a98fc1c045f2257f9a3fb7687be))
|
|
318
|
+
|
|
319
|
+
With `onClick` (CustomLegend) or `onToggle` (InteractiveLegend), each entry is a button named by its label, with a Tab stop, Enter and Space, the theme's focus ring and `aria-pressed` true while its series shows. A hidden series stays struck through as well as dimmed, so its state does not rest on opacity alone. The legend is a `group` named "Chart legend" by default; the new `legendLabel` prop names it. Without a handler, CustomLegend's entries are plain text as before.
|
|
320
|
+
|
|
321
|
+
Visual review: docs/remediation/M4-index.md
|
|
322
|
+
- **Behavior correction (b)**: DataTable's row actions menu, hover actions, inline add row and "Copy to clipboard" work with the keyboard and screen readers. ([`ede5ede`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/ede5ede09553c1771976e47bbf9badd5794f960e))
|
|
323
|
+
|
|
324
|
+
The row actions menu button is a menu button: `aria-haspopup="menu"`, `aria-expanded`, `aria-controls` while open, and its name ("More actions", `labels.rowActionsMenu`) as a tooltip; Escape closes the menu and returns focus to it. Hover actions are hidden by opacity only, so they stay in the Tab order and show when one of them has keyboard focus. The inline add row's placeholder is a button (Tab, Enter, Space) that moves focus to the first field, and the fields are named by their column header. "Copy to clipboard" in the export menu copies through `useClipboard` and announces "Copied N rows" (new `labels.rowsCopied`), or, assertively, `labels.copyFailed`.
|
|
325
|
+
|
|
326
|
+
Visual review: docs/remediation/M4-index.md
|
|
327
|
+
- **Behavior correction (b)**: DockLayout's panel header buttons are named with tooltips, tabs close with Delete, and drag and drop is announced through the toolkit. ([`aa9426a`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/aa9426a13825e25dfe7ae2f999b6136724111081))
|
|
328
|
+
|
|
329
|
+
A single panel's Minimize, Maximize, More and Close buttons are named "Minimize panel", "Maximize panel", "More panel actions" and "Close panel" (new `labels` prop, `DockLayoutLabels`), each with the same text as its tooltip; More is a menu button (`aria-haspopup="menu"`, `aria-expanded`) whose menu returns focus on Escape. A closable tab closes with Delete while it has focus (`aria-keyshortcuts="Delete"`); its close icon is a pointer-only shortcut instead of a button nested inside the tab. Drag-and-drop milestones are announced through `useAnnounce` instead of DockLayout's own live region.
|
|
330
|
+
|
|
331
|
+
Visual review: docs/remediation/M4-index.md
|
|
332
|
+
- **Behavior correction (a, b)**: The navigation menus (TopNavHeader, TopNavWithSidebar, ClientMenu, AppsMenu, UserMenu) tell screen readers what they open, never share ids, and show the names of their icon-only buttons. ([`d331de7`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d331de75b72b7fb0955eaf007337a2ff22d4bfcc))
|
|
333
|
+
|
|
334
|
+
Every popup trigger (client picker, applications menu, user menu) reports `aria-haspopup`, `aria-expanded` and, while open, `aria-controls`, with ids generated per instance, so two headers on a page no longer share `client-menu`. The client picker's list is a listbox of options named by its "Switch client" heading, which now sits above the list instead of inside it; TopNavWithSidebar's list was a menu behind a listbox trigger and is now the same listbox. The picker's trigger is named "Switch client, current: {client}" (it was "Current client: {client}. Click to switch clients"); the new `clientSelectorLabel` prop words it differently. The user menus' name and email sit above the menu and name it. TopNavWithSidebar's sidebar toggle reports the mobile drawer's state on phones ("Open/Close sidebar navigation") and points at the drawer through a generated id. Every icon-only button (search clear, open and close search, applications, theme toggle, user menu, help, notifications, sidebar toggle, close navigation) shows its name as a tooltip.
|
|
335
|
+
|
|
336
|
+
Visual review: docs/remediation/M4-index.md
|
|
337
|
+
- **Behavior correction (b)**: DockLayout panels move without dragging: a "Move to" menu reaches every other zone from the keyboard or with a single click (WCAG 2.1.1, 2.5.7). ([`e307005`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e3070055d7231e824335d2bacbcb1466715f35d7))
|
|
338
|
+
|
|
339
|
+
A single panel's More menu lists one "Move to {zone}" item per other zone, and a tab bar ends with a "Move {title}" button (a menu button with the same tooltip) for its selected tab; with `showPanelActions={false}` a single panel's header shows that button too. Each item runs the drop path, so `onLayoutChange` receives the same layout as a drag, and the move is announced. The new `zoneLabels` prop names the zones (a zone without an entry is named by its id), and `DockLayoutLabels` gains `movePanel`, `moveTo`, `dropPanelHere`, `moving`, `moved` and `moveCanceled`. The menus appear only with `draggable` (the default) and `onLayoutChange`, when another zone exists.
|
|
340
|
+
|
|
341
|
+
Visual review: docs/remediation/M4-index.md
|
|
342
|
+
- **Behavior correction (b)**: TreeView nodes move without dragging: with `draggable` and `onDrop`, a node's move menu offers Move up, Move down, Move into previous item and Move out to parent (WCAG 2.1.1, 2.5.7; OD-B17 option a). ([`857e046`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/857e0464c8600ac3da1930985ba16512e7fe398e))
|
|
343
|
+
|
|
344
|
+
Shift+F10 or the ContextMenu key on the focused node opens the menu (`aria-keyshortcuts="Shift+F10"`), and each row has an "Actions for {label}" button with the same tooltip, outside the Tab order, for pointer users. Each item calls the existing `onDrop(draggedId, targetId, position)`: Move up with the previous sibling and 'before', Move down with the next sibling and 'after', Move into previous item with the previous sibling and 'inside' (as a drag onto it does), and Move out to parent with the parent and 'after'. Only the moves that apply are listed. The move is announced, and focus returns to the node, following it when it changes parent. The new `labels` prop (`TreeViewLabels`) translates the strings.
|
|
345
|
+
|
|
346
|
+
Visual review: docs/remediation/M4-index.md
|
|
347
|
+
- **Behavior correction (a)**: Combobox, DataTable, DateRangePicker, InsightCard, SplitPane, TransferList and TreeView now apply an array or function `sx` (E-16). ([`36e18c0`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/36e18c044bd0f5b852ee04adc3a4ae058f419d9f))
|
|
348
|
+
|
|
349
|
+
Each merged the consumer's `sx` into its root styles with an object spread, which keeps only an object's keys: an array `sx` turned into keys MUI ignores and a function `sx` disappeared. They now merge it as an ordered list, the consumer's styles last, so an object `sx` renders exactly as before. The `sx` props of Combobox, SplitPane, TransferList and TreeView are typed `SxProps<Theme>` (they were `Record<string, unknown>`, which is still accepted).
|
|
350
|
+
- **Behavior correction (b, c)**: StatusIndicator reads its colours from the theme, is non-live by default with a named variant for every status, and takes root props, `icon`, `colors` and `live`. ([`3bf2fd1`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/3bf2fd17cb0531bf4ae5280bf5367c2de791604c))
|
|
351
|
+
|
|
352
|
+
The StatusIndicator family (C-02, C-03, C-04, C-05, C-32, C-33) in one release.
|
|
353
|
+
|
|
354
|
+
- **Theme colours (C-02).** Every StatusIndicator variant, and StatusLegend, now draws with `getTrinityPalette(theme).indicator`. Light output is unchanged except the differential's positive default. In dark mode, status text and icons placed straight on the page (the inline variant, the icon-only icon and the inline icon) use the dark status roles (`#FCA5A5`, `#FDE68A`, `#86EFAC`, `#7DD3FC`, `#D1D5DB`, `#C5C6D9` and indigo 200 `#B49CF6`, 4.5:1 or more on every dark surface) instead of the light catalog colours (3.00-3.65:1). The differential's defaults are the success and critical status colours of the theme: light positive `#15803D` (was `#24a148`, 3.35:1 on white), dark `#86EFAC` and `#FCA5A5`. Token overrides passed to `createTrinityTheme` or a preset now reach the rendered indicator, and under a plain MUI theme the indicator uses the resolver's MUI-derived colours.
|
|
355
|
+
- **Non-live by default, named variants (C-03).** A static indicator used to be a polite `role="status"` region, so a page of indicators was a page of live regions, `aria-label` was ignored, the dot was always named after its status, and a clickable chip lost its button role. Now visible text is read as it is; a variant without visible text (dot, `iconOnly`, `showLabel={false}`) is `role="img"` named by `aria-label`, else by `label`; a consumer `aria-label` on a variant with visible text gives `role="img"` with that name; the differential is a group named by its sentence; a chip with `onClick` or `onDelete` is a button. The new `live` prop (`true`, `'polite'` or `'assertive'`, @since 2.13.0) makes the indicator a `role="status"` or `role="alert"` region whose text content carries the status, so a change is announced. `role` and `aria-live` are owned: a consumer value is ignored with a development warning, and `live` is ignored, with a warning, on an interactive chip and on the badge.
|
|
356
|
+
- **Root props, `icon` and `colors` (C-04).** `className`, `id`, `data-*`, `aria-*` and event handlers now reach the root element of every variant (they were dropped before), and `sx` arrays and functions apply on every variant. `icon` (an `IconName`) replaces the status's glyph, for example `icon="verified"`, and `colors` (`StatusIndicatorColors`, the theme's status colour role) replaces its colours, so a verification badge can use `useTrinityPalette().verification.verified`. `StatusIndicatorProps`, `StatusVariant` and `StatusIndicatorColors` are exported from the package root and from `/essentials` (@since 2.13.0). Attributes that were silently dropped before, such as `data-testid`, now appear in the DOM.
|
|
357
|
+
- **Dot pulse under reduced motion (C-05).** `<StatusIndicator variant="dot" pulse>` no longer pulses under `prefers-reduced-motion: reduce`; the dot stays visible. The pulse uses a scoped animation name, so another component's `pulse` keyframes can no longer replace it, and its cycle is the `motion.loop.pulse` token (2s, as before). The halo is built with a real alpha colour instead of appending `80` to the fill, so `rgb()`, `hsl()` and CSS-variable colours passed through `colors` give a valid halo.
|
|
358
|
+
- **Dot ring (C-32).** `warning`, `caution` and `pending` dots had a yellow or orange fill that measured 1.68-2.46:1 on white. In light themes the dot now draws its status outline as a 1px inset ring (`#8E6A00` or `#BA4E00`, about 5:1), which is kept while it pulses. Dark themes are unchanged: their fills already reach 3:1. The CSS guide gives the same recipe for CSS-only apps.
|
|
359
|
+
- **One status engine (C-33).** StatusHero, InsightCard and HistoricalDataIndicator render their status icons through the StatusIndicator variants, so each status is named once and in the theme's colours. In StatusHero the large status icon is an image named after the status, and the breakdown icons beside their labels are hidden from assistive technology, so each count is read once with its label. InsightCard's icon circle is hidden; its severity label is read as before. HistoricalDataIndicator keeps one named group. Their look is unchanged.
|
|
360
|
+
|
|
361
|
+
Also in this pull request, for the remediation program: the internal StepList block (D-10), which no package entry reaches.
|
|
362
|
+
|
|
363
|
+
Visual review: docs/remediation/C-02-dark-status-indicators.md, docs/remediation/E-44-theme-motion-backstop.md, docs/remediation/HITL-C-33-status-indicator-adapters.md
|
|
364
|
+
- **Behavior correction (b, c)**: The base theme meets contrast and motion requirements for progress, pressed buttons, status buttons, badges and error outlines, and Icon mirrors in right-to-left, names confidence levels and takes intent sizes. ([`3bf2fd1`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/3bf2fd17cb0531bf4ae5280bf5367c2de791604c))
|
|
365
|
+
|
|
366
|
+
Theme-level atom overrides (C-20, C-27, C-15) and the Icon extensions (C-28, C-18, C-39+) in one release.
|
|
367
|
+
|
|
368
|
+
- **Progress track, reduced motion, pending and forced colors (C-20).** Every LinearProgress colour now draws on `palette.trinity.border.default` instead of a tint of the bar colour, so the bar meets 3:1 against its track (light error, warning and success were 2.62-2.89:1); `color="inherit"` and `variant="buffer"` keep MUI's track. With the user's reduced-motion setting, the indeterminate LinearProgress shows a still bar at the start, CircularProgress a still arc, and Skeleton stops pulsing and waving; value changes jump (before, the loops ended on an empty track or ring). A Button or IconButton with `aria-disabled="true"` (a pending action that stays focusable, ADR-07) is drawn like a disabled one and keeps its pointer events, so a tooltip can say why. In forced colors, a pressed Button or IconButton (`aria-pressed="true"`), a selected ToggleButton and a pressed or checked Chip draw an inset outline in the system highlight colour, which Windows contrast themes keep.
|
|
369
|
+
- **Pressed and selected ring, dark selected background (C-27).** A Button or IconButton with `aria-pressed="true"` and a selected ToggleButton draw the selected background, the brand text colour and an inset 2px ring in the interactive colour, in every variant, mode and preset; hover keeps it. Before, a pressed button looked like an unpressed one, and a selected ToggleButton was a faint tint. `palette.action.selected` reads the `overlay.selected` token: unchanged in light mode, `rgba(255, 255, 255, 0.10)` instead of `0.16` in dark mode, the value the CSS package ships as `--trinity-overlay-selected`.
|
|
370
|
+
- **Button, Badge and error outline contrast (C-15).** Contained primary and secondary Buttons use the button colour tokens in both modes, the values the CSS package ships as `--trinity-button-primary-*` and `--trinity-button-secondary-*`: dark primary is indigo with a white label (was white on coral, 2.69:1), light secondary is indigo (was white on coral, 2.97:1), and hover and active follow the tokens. Contained inherit Buttons label the grey fill with the theme's primary text colour (was white on light grey, 1.3:1). Outlined and text Buttons with `color="error"`, `"warning"`, `"success"` or `"info"` draw their own colour (they looked like primary). Badges with `color="error"` or `"success"` use the status colours and their label colour; a dark secondary badge uses the secondary palette colour. A TextField in error draws `border.error` as its outline at rest, on hover and while focused; in dark mode that is `#F87171`, as `--trinity-border-error` in the CSS package (was `#FCA5A5`).
|
|
371
|
+
- **Icon mirroring in right-to-left (C-18).** In a theme created with `direction: 'rtl'`, `back`, `forward`, `chevron-left`, `chevron-right`, `send`, `undo`, `redo`, `login` and `logout` mirror horizontally in both icon libraries; media controls and every other name do not. The mirror uses the CSS `scale` property, so an icon your `sx` rotates keeps its rotation and is mirrored once. It reads `theme.direction`, so server and client render the same. New prop `flipInRtl` (default `true`); pass `false` where a glyph means a fixed direction. Left-to-right output is unchanged.
|
|
372
|
+
- **Confidence glyphs (C-28).** Icon gains `confidence-high`, `confidence-medium` and `confidence-low`: signal bars, one more bar per level, so a confidence level never rests on colour alone (Material's SignalCellularAlt, SignalCellularAlt2Bar and SignalCellularAlt1Bar, and Feather-style signal bars drawn in the package, with no new dependency). `IconName` widens by the three names. The golden verification state keeps the existing `award` glyph; no `verified-golden` name is added.
|
|
373
|
+
- **Icon intent sizes (C-39+).** Icon's `size` gains the intent names `inline`, `control`, `navigation`, `prominent`, `hero` and `display`. They read the theme's semantic icon-size tokens (14, 16, 20, 24, 28 and 36px by default, the `--trinity-icon-*` CSS variables), so a token override reaches them. The `xs` to `xl` scale renders exactly as before.
|
|
374
|
+
|
|
375
|
+
The CSS guides gain the same recipes for CSS-only apps: the progress and pressed rules, `.btn--primary` and `.btn--secondary` on the button variables, outlined and text status buttons next to `.btn--danger`, and `.btn[aria-pressed="true"]`.
|
|
376
|
+
|
|
377
|
+
Visual review: docs/remediation/A-03-M-02-base-theme-overrides.md, docs/remediation/C-03-contained-status-button-contrast.md, docs/remediation/I-06-icon-manifest.md
|
|
378
|
+
- **Behavior correction (a, b)**: Popper is controllable and keyboard-operable, and Modal and ConfirmDialog are named, themed, localized and honest about pending and failed actions. ([`3bf2fd1`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/3bf2fd17cb0531bf4ae5280bf5367c2de791604c))
|
|
379
|
+
|
|
380
|
+
The Popper, Modal and ConfirmDialog atoms (C-06, C-07, C-08, C-09, C-10, C-11, C-12, C-13, C-14, C-25, C-26) in one release.
|
|
381
|
+
|
|
382
|
+
- **Popper state, reasons and slots (C-06).** New `open`, `defaultOpen` and `onOpenChange(open, reason, event)`, with `PopperOpenChangeReason` (exported): `trigger`, `clickAway`, `escape`, `hover` and `disabled`. A click inside the trigger is `trigger`, never `clickAway`. `onClose` keeps its type and still fires when a click away or a click on the trigger closes the Popper. `id` is optional: without it Popper uses `React.useId()`, so it no longer throws. The trigger keeps its own id, or gets `id`; the surface is `${id}-panel`, and the trigger's `aria-controls` (click) or `aria-describedby` (hover) now points at the surface itself instead of the positioned layer around it (`${id}-popper`, which keeps its id). New `aria-label` and `aria-labelledby` name the surface (it was named by the raw `id`; a click-mode Popper without a name now gets a development warning). New `disablePortal` and `slotProps` (`popper`, `paper`, `content` and `arrow`). The trigger's ref is read from its props, so React 19 no longer warns about `element.ref`. The fade uses the motion token `motion.duration.normal` (200ms, was 250ms) and is instant under reduced motion.
|
|
383
|
+
- **Popper keyboard model (C-07).** Click mode is a light-dismiss modal dialog (`aria-modal="true"`). On open, focus moves to the first control inside (or the surface), and Tab stays inside. Escape, a click outside or the trigger closes it, and focus returns to the trigger. Escape inside a nested Popper, or inside a Popper in a Dialog, closes only that Popper. Hover mode is a tooltip: focus stays on the trigger, and Escape closes the topmost hover Popper only; interactive content in a hover Popper gets a development warning because keyboard users cannot reach it. The positioned layer around the surface no longer has `role="tooltip"`; the surface carries the only overlay role.
|
|
384
|
+
- **Modal full screen on mobile (C-08).** `fullScreenMobile` was documented as "Full screen on mobile" but did nothing at any width. With it, a viewport narrower than `theme.breakpoints.values.sm` (600px by default) shows the dialog full screen with square corners; at `sm` and wider it keeps its `maxWidth` and 12px radius. The server render and the first client render are not full screen, so hydration stays stable; the viewport is read after mount. Without the prop nothing changes.
|
|
385
|
+
- **Modal root props and slots (C-10).** New `className` and rest props (HTML attributes such as `data-*`, `aria-describedby` and event handlers) on the Dialog root, where the ref points. A consumer's `aria-describedby` and `aria-labelledby` are joined after Modal's own; `role`, `aria-modal` and the other owned attributes stay Modal's. New `slotProps`: `root`, `backdrop` and `paper` (the `role="dialog"` surface), each with `className`, `sx`, `id` and HTML attributes. `sx` still styles the surface; it was spread into a nested rule, so an array or function `sx` broke, and every form now applies (object `sx` computes the same styles as before). `slotProps.paper.sx` applies after it.
|
|
386
|
+
- **Modal naming and message (C-11).** A title-less Modal rendered an empty `h2` that held only the close button, and had no way to be named: `aria-label` was not accepted. Without a title there is now no heading (the header row keeps its space as a plain `div`), and `aria-label` and `aria-labelledby` reach the dialog surface. A development warning fires when an open dialog has no name. New `message`: a node shown first in the content, as a paragraph for a string and a `div` otherwise, with its own id; ConfirmDialog renders its message through it, so a list or other block message no longer nests inside a paragraph, and its look is unchanged. New `describedBy`: `'content'` (default, as before), `'message'` or `'none'`; a consumer's `aria-describedby` is added after it. New `labels` (`ModalLabels`, exported from the root and essentials): `close`, the close button's name and tooltip (default "Close dialog").
|
|
387
|
+
- **Modal under any theme (C-12).** Modal read `theme.palette.trinity` directly to color its variant icons, so under a plain `createTheme()` (or a consumer theme without Trinity's palette) every Modal and ConfirmDialog threw a TypeError. The colors now come from `getTrinityPalette(theme)`: under a Trinity theme they are the same values as before; under any other theme they come from the resolver's contrast-safe fallback (ADR-26). Token overrides of the status colors reach the icons.
|
|
388
|
+
- **Modal icons (C-13).** Modal imported six Material icons directly, so an app that chose Feather through `IconProvider` still got Material icons in its dialogs. The variant icons and the close button now render through the Trinity `Icon`, in the same size and colors. With the default Material library nothing changes visually; the confirm variant's icon is MUI's `Help`, which draws the same glyph as the `HelpOutlined` it used before.
|
|
389
|
+
- **Modal close button (C-25).** The close button was inside the dialog's title element, so screen readers announced every dialog as "<title> Close dialog". It now sits in the dialog surface after the title, at the same spot, with a tooltip that shows its name. Its position and the title's padding use logical properties, so in right-to-left layouts the button is at the top left.
|
|
390
|
+
- **Modal reduced motion (C-26).** The dialog's Fade always ran for the theme's `enteringScreen` and `leavingScreen` durations (225ms and 195ms), so a closed dialog stayed in the DOM for 195ms even under reduced motion. With `prefers-reduced-motion: reduce` the transition is now 0ms; without it the theme's durations apply as before.
|
|
391
|
+
- **Modal async primary action (C-09).** `onPrimaryAction` may return any thenable, not only a native `Promise`. A sync throw or a rejection no longer escapes as an unhandled rejection: an inline error ("The action could not be completed. Try again.") with a "Try again" button appears at the end of the content, is announced, and the error goes to the new `onActionError` (else to `globalThis.reportError`). While the action (or `loading`) is pending, the primary, secondary and close buttons are `aria-disabled` instead of `disabled`, so focus stays on them; the primary button is `aria-busy` and its spinner is named by it. They look the same (C-20 draws `aria-disabled` like `disabled`). New `pendingClosePolicy` (`'block'` by default): while pending, the close button, the secondary action, backdrop clicks and Escape do nothing. New `errorMessage` (a node, or a function of the error) and `labels.retry`. `onClose` receives the reason: `'closeButton'`, `'secondaryAction'`, `'backdropClick'` or `'escapeKeyDown'`.
|
|
392
|
+
- **ConfirmDialog reason field and async options (C-14).** New `reasonField` (`ConfirmDialogReasonField`, exported from the root and essentials): a required, multi-line reason by default. An empty or whitespace-only reason does not confirm: the field is marked invalid, says "Enter a reason to continue." (`requiredMessage`) and takes focus, while the confirm button stays enabled. `onConfirm(reason)` receives the trimmed reason; the consumer still closes the dialog on success. `message` takes any node (a string is a paragraph, anything else a `div`) and describes the dialog alone; new `children` render below it. New `primaryDisabled`, `onActionError`, `pendingClosePolicy`, `errorMessage` and `labels` pass through to Modal, and `onConfirm` may return any thenable. ConfirmDialog forwards its ref to the Modal root; `useConfirmDialog` accepts a node message, a reason-taking `onConfirm` and `reasonField`.
|
|
393
|
+
|
|
394
|
+
Visual review: docs/remediation/I-05-modal-full-screen-mobile.md, docs/remediation/HITL-C-07-popper-keyboard-model.md, docs/remediation/HITL-C-13-modal-icons.md, docs/remediation/HITL-C-25-modal-close-button.md, docs/remediation/HITL-C-26-modal-reduced-motion.md, docs/remediation/M5-summary.md (owner review list: C-09/C-14)
|
|
395
|
+
- **Behavior correction (b, c)**: UserAvatar shows a name, initials or image, IllustratedMessage takes its colours from the theme and a heading level, SearchInput is a combobox and Tour is a real dialog. ([`3bf2fd1`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/3bf2fd17cb0531bf4ae5280bf5367c2de791604c))
|
|
396
|
+
|
|
397
|
+
The remaining atoms of the milestone (C-21, H-24, H-45, C-36+, C-37+) in one release.
|
|
398
|
+
|
|
399
|
+
- **UserAvatar (C-21).** UserAvatar takes `name`, `initials` and `src` (@since 2.13.0): with any of them it renders a MUI Avatar in the theme's avatar colours, the image has an empty `alt`, and a failed image falls back to the initials (the first letter of the first two words, safe for any Unicode letter). Without them it keeps the user icon, now on the theme's neutral fill: light is unchanged (`#E0E0E0`), dark becomes gray 600 `#374151` (the icon reaches 9.37:1 instead of 1.1:1). The root is `aria-hidden` unless you pass `aria-label` or `aria-labelledby`, which make it a named `role="img"`. It also takes `className`, HTML attributes on the root and `sx` in every form. OrgChart's node initials keep characters outside the Basic Multilingual Plane whole.
|
|
400
|
+
- **IllustratedMessage API (H-24).** New props (@since 2.13.0): `headingLevel` (2-6) sets the title element, whose default stays `h6` for small and medium and `h5` for large; `titleRef` reaches the title and makes it focusable with `tabIndex={-1}`, so a page can move focus to it after the content is replaced; `actions` renders your own actions row (for example a retry button with a pending state) instead of `primaryAction` and `secondaryAction`, with a development warning when both are passed; `slotProps.title`, `slotProps.description` and `slotProps.actions` take `className`, `sx`, `id`, `data-*` and HTML attributes. `className`, `id`, `data-*` and `aria-*` reach the root, and `sx` works as an object, array or function. The decorative illustration wrapper loses its contradictory `role="img"` (it stays `aria-hidden`); the accessibility tree is unchanged.
|
|
401
|
+
- **Illustration colours (H-45).** The fifteen illustrations (IllustratedMessage, UploadDropZone and the standalone exports) draw their main strokes in `palette.primary.main`, neutral shapes in the subtle border role, accents in `palette.secondary.main` and knock-out fills in `background.paper`. The default light theme renders exactly as before. In dark mode the white panels and light-grey shapes become the dark paper and the dark border, and the status tints become the dark status backgrounds; the status strokes keep their colours. Explicit `primaryColor`, `secondaryColor` and `accentColor` still win.
|
|
402
|
+
- **SearchInput combobox (C-36+).** The input is a `combobox` with `aria-expanded`, `aria-controls` and `aria-activedescendant`; focus stays in it. One listbox ("Search suggestions") holds the suggestions and a "Recent Searches" group. "Clear all" becomes the last option, "Clear recent searches", which Enter (or a click) activates; Delete removes the active recent search; both are announced. The remove button beside a recent search is named "Remove <term> from recent searches" and shows a tooltip, and the clear button shows "Clear search" as a tooltip. New `labels` (`SearchInputLabels`, exported from the root): `clearSearch`, `clearRecent`, `removeRecent`, `recentRemoved`, `recentCleared`. A controlled `value` now receives every change at once (typed characters were lost behind the debounce); `debounceMs` applies to an uncontrolled field, and a picked option drops a pending typed `onChange`. The clear button and Ctrl+K (Cmd+K) now move focus into the input, and Escape closes the list and keeps focus.
|
|
403
|
+
- **Tour dialog (C-37+).** The step card is the `role="dialog"` element (it was the full-screen layer around it) and takes focus when the tour opens; focus returns to where it was when it closes. A masked tour (the default) is modal: the page behind it, the highlighted target included, is hidden from assistive technology and takes no Tab or pointer input; `mask={false}` is non-modal. Escape and the arrow keys work while focus is in the step, and `onClose` receives `'closeButton'`, `'escapeKeyDown'` or `'finish'`. The scroll to each target is instant under reduced motion, also when `scrollIntoViewOptions` asks for smooth. The close button shows its name ("Close tour") as a tooltip, and Previous is `aria-disabled` (not `disabled`) on the first step, so it keeps focus.
|
|
404
|
+
|
|
405
|
+
Visual review: docs/remediation/D-01-user-avatar-contrast.md, docs/remediation/D-01-illustrated-message-dark.md, docs/remediation/M5-summary.md (owner review list: C-36+, C-37+)
|
|
406
|
+
- **Behavior correction (b, c)**: AuditDrawer, EntityDetailPanel and AssessmentDrawer follow the theme in both modes, and their filters, fields, sections and footers are keyboard- and screen-reader-operable. ([`3bf2fd1`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/3bf2fd17cb0531bf4ae5280bf5367c2de791604c))
|
|
407
|
+
|
|
408
|
+
The three drawers (D-46, D-47, D-48, D-15, D-16, D-17, C-34, D-18, D-23 and the B-42 AssessmentDrawer commit) in one release.
|
|
409
|
+
|
|
410
|
+
- **AuditDrawer colours (D-46).** AuditDrawer read static light-mode token values, so in dark mode it painted a white drawer. Its colours now come from the MUI text roles and the palette resolver: the paper and header from `surface.elevated`, the note and category chip from `surface.sunken`, borders and the rail from `border.subtle`, and an active filter chip's text from its fill's `contrastText`. Light output keeps its values except the captions that used the tertiary grey (role, timestamp, old value, arrow, empty-state hint: #9CA3AF, 2.54:1, now `text.secondary` #374151), the other secondary texts (#6B7280 to #374151), the note and category background (#FAFAFA to #F4F4F5) and the active Security chip's text (white on coral, 2.97:1, now dark). The timeline rail was 100% wide (an `sx` width of 1) and drew a grey block over each event's action, changes and note; it is now a 1px line.
|
|
411
|
+
- **AuditDrawer filters, changes, category and initials (D-15, D-16, D-17, C-34).** The filter chips render as a group named "Filter events" of native checkboxes, each checked while its id is in `activeFilterIds`; `onFilterToggle(id)` is unchanged. A checked chip shows a check icon, bold text and a 2px border instead of the severity fill; severity is the chip's icon. Field changes are a description list: the field is the term, and the value reads "Previous value <before>" (struck through) and "New value <after>". The event category takes MUI Chip's default colours. Avatar initials of names that start with an emoji or a letter outside the Basic Multilingual Plane are whole characters. New: `labels?: Partial<AuditDrawerLabels>` (`filterChips`, `previousValue`, `newValue`), @since 2.13.0. Icons render through `Icon` pinned to Feather; the close icon is 20px (it was 18px).
|
|
412
|
+
- **EntityDetailPanel colours (D-47).** EntityDetailPanel read static light-mode token values and built its status pill from fixed `rgba()` washes, so in dark mode it painted a white panel. Its colours now come from the MUI text roles and the palette resolver: surfaces from `surface.elevated`, `surface.subtle` and `surface.sunken`, borders from `border.subtle`, and the status pill from the theme's status pairs (`status.<variant>.background` and `.text`), shape unchanged. In light mode the pill backgrounds become the status backgrounds (success, warning and error text 4.15-4.27:1 to 4.57:1 or more), the secondary texts darken (#6B7280 to #374151) and the help icon and empty-tab message move from the tertiary grey (2.54:1) to `text.secondary`.
|
|
413
|
+
- **EntityDetailPanel fields and help (D-18).** Each section's fields render as a `<dl>` named by the section title. A field with `helperText` shows a focusable help button named "More information <label>" and described by its tooltip, instead of a hover-only icon. Labels and values sit side by side and stack only when the row is too narrow for both, with no viewport breakpoint, so at drawer widths of about 440-600px they pair where they used to stack. The help glyph is the Material info icon at 16px; the close icon is 20px (it was 18px), through `Icon` pinned to Feather.
|
|
414
|
+
- **AssessmentDrawer colours (D-48).** AssessmentDrawer read static light-mode token values and a purple tint that is not in the theme, so in dark mode it painted a white drawer with a light sidebar, and the dark-theme form labels inside it were unreadable. Its colours now come from the MUI palette and the palette resolver: surfaces from `surface.elevated`, `surface.subtle` and `surface.sunken`, borders from `border.subtle`, the section status icons from the theme's status texts, the active section from MUI's selected recipe (`primary` at the selected opacity) and the Review badge and reviewer notes from `primary` tints. In light mode the section labels move from the tertiary grey (2.43:1) to `text.secondary`, the other secondary texts darken (#6B7280 to #374151), the active section fill is primary at 0.08 instead of #E8E8F0, and the review tints turn from purple to the theme's primary.
|
|
415
|
+
- **AssessmentDrawer sections, footer and progress (D-23, B-42).** The section navigation is an ordered list; each section button starts with the catalog status icon and ends with its status as text ("Not started", "In progress", "Complete", "Flagged"). While `saving` or `submitting`, Save draft and Submit stay focusable: `aria-disabled` with clicks ignored, and the pending one `aria-busy`, instead of natively disabled. The close icon is 20px (it was 18px), through `Icon` pinned to Feather. The progress bar is named by the visible "<n> of <total> sections complete" caption above it with `aria-labelledby`, so screen readers announce the progress together with its value; no new string and no visual change.
|
|
416
|
+
|
|
417
|
+
Visual review: docs/remediation/D-46-audit-drawer-colors.md, docs/remediation/D-47-entity-detail-panel-colors.md, docs/remediation/D-48-assessment-drawer-colors.md, docs/remediation/M5-summary.md (owner review list: D-46/D-15..D-17/C-34, D-47/D-18, D-48/D-23/B-42)
|
|
418
|
+
- **Behavior correction (b)**: FilterBar, DiffViewer, Timeline, the wizard templates and a dozen more components get named popups, real list and disclosure semantics, focus-keeping pending states and locale-aware labels. ([`3bf2fd1`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/3bf2fd17cb0531bf4ae5280bf5367c2de791604c))
|
|
419
|
+
|
|
420
|
+
The non-AI migrations onto the shared blocks and the B-42 burn-down (D-13, D-14, D-19, D-21, D-22, H-23, H-44, B-31, B-42) in one release.
|
|
421
|
+
|
|
422
|
+
- **FilterBar labels and chips (D-13).** New `labels?: Partial<FilterBarLabels>` (new type `FilterBarLabels`, @since 2.13.0) has `selectedCount({ count })` (the chip value of a multiselect filter with more than one option selected) and `moreFilters({ count })` (the chip for the filters past `maxVisibleChips`). The defaults reproduce today's "N selected" and "+N more", so nothing changes unless you pass them. Filter chips are memoized as documented: their handlers are stable now, so opening a menu or editing one filter no longer re-renders every chip. Each filter chip is a native button reading "Status: Active", described by a hidden "Press Delete to remove"; Delete or Backspace removes the filter. The overflow chip is a native button too. The delete glyph is the circled x at 16px in a 24px target (it was a plain x). Icons render through `Icon` pinned to Material, with identical glyphs. `values` follows MUI's `useControlled` (see the burn-down below).
|
|
423
|
+
- **FilterBar popup semantics (D-14).** Quick-filter buttons and filter chips have `aria-haspopup="dialog"`, `aria-expanded`, and `aria-controls` while their editor is open; the Filters button, the "+N more" chip and the presets button have `aria-haspopup="menu"`. Only the trigger that opened an overlay reads as expanded, even when a quick filter and a chip edit the same filter. Quick filters no longer have `aria-pressed`: they open an editor, and an applied filter shows as a chip. The editor is `role="dialog"` with `aria-modal`, named by its heading (an `h2`) or, for a `renderFilterEditor` editor, by the filter label; its fields are labelled by that heading, and the multiselect options are checkboxes in a named group. An editor opened from the Filters menu's "Add filter" items opens under the menu's opener and returns focus there when it closes; Escape returns focus to the opener.
|
|
424
|
+
- **DiffViewer (D-21, D-22).** The "Unified view" and "Side-by-side view" ToggleButtons were named only by a Tooltip on their hidden icon, so a screen reader announced unnamed toggle buttons. Each button now has `aria-label` from the same string as its Tooltip, the Tooltip wraps the button (it anchors to the whole toggle instead of its icon), and the group is named "View mode". "Copy original" and "Copy modified" announce the result, show a check (or error) icon for a moment, and a refused clipboard write reports through `reportError` instead of an unhandled rejection. With `collapsible`, each @@ hunk header is a button with `aria-expanded` that Enter and Space toggle; the header tint is the info status background with its text colour, and it shows a focus ring. Icons render through `Icon` pinned to Material, with identical glyphs.
|
|
425
|
+
- **Timeline list semantics (H-23, D-19).** Without `groupByDate` the items are one `<ol>`; with it, each date is a heading (new `groupHeadingLevel`, default 3, @since 2.13.0) followed by its own `<ol>`. Items passed with `isGroupHeader` start a group the same way. Every item, including `renderItem` output, is wrapped in `<li key={item.id}>`, which also fixes the missing React key. An item with a `type` reads its type first as visually hidden text (`labels.types`, defaults Success, Warning, Error and Info). Item titles are no longer `h6` headings. Detailed metadata is a description list: each key is a term and its value the definition (WCAG 1.3.1). Spacing uses logical properties, so the layout mirrors in right-to-left themes. Nothing changes visually in left-to-right themes.
|
|
426
|
+
- **Timeline locale, labels and load more (H-44).** New props (@since 2.13.0), all with today's output as the default: `locale` (default `'en-US'`, so server and client markup match) and `timeZone` (default: the runtime's zone; server-rendered apps should pass one, or an item near midnight can land under a different day on the server) for the `groupByDate` labels; `formatGroupLabel(date)` to format them yourself; and four more `labels` keys on `TimelineLabels`: `actorPrefix({ actor })`, `unknownDate`, `loadMore` and `loadingMore`. While `loadingMore` is true the load-more button is `aria-disabled` and `aria-busy` and ignores clicks, instead of `disabled`, so a keyboard user keeps their place.
|
|
427
|
+
- **Wizards (B-31).** While `loading`, WizardTemplate's Cancel, Back and Next/Finish are `aria-disabled` with clicks ignored (Next/Finish also `aria-busy`) instead of natively disabled. ConnectorWizardTemplate's connectors are native radios in a group named "Pick a connector to set up.": arrow keys move the selection, a disabled connector cannot be chosen, and the selected card shows a check icon and a 2px border. Finish runs once while pending, and a rejected `onConnect` shows an inline error with "Try again" instead of an unhandled rejection. Icons render through `Icon` pinned to Feather; Back and Next arrows mirror in right-to-left themes.
|
|
428
|
+
- **Burn-down (B-42).** GeoStatusMap: with `onSelect`, each marker is a button named "<label> — <severity>", in the Tab order, with a focus ring and a 24px target. Carousel: Previous and Next show their names as tooltips; the pagination dots are a named group of buttons with `aria-current="step"` (no tab roles); the arrows mirror in right-to-left themes. OnboardingTemplate: with `onDotClick` the dots are buttons with `aria-current="step"`; without it they are decorative and the progress is read as text. AuthTemplate: while `primaryAction.loading` the button keeps focus (`aria-disabled`, `aria-busy`) and submits are ignored; a click without `onSubmit` now runs `onClick` once (it ran twice). InsightsCenterTemplate: the layout toggles show their names as tooltips. Autocomplete: the input id is the consumer's `id` or a generated one, no longer `<name>-autocomplete`. ChipsInput, MentionsInput, PredictiveTextInput, Combobox, PageTOC, TopNavWithSidebar, SettingsTemplate, IntegrationDetailTemplate, InsightsCenterTemplate and FilterBar follow MUI `useControlled`: switching a prop between controlled and uncontrolled warns in development.
|
|
429
|
+
|
|
430
|
+
Visual review: docs/remediation/M5-summary.md (owner review list: D-14/D-13, D-21/D-22, H-44, B-31, B-42)
|
|
431
|
+
- **Behavior correction (a, b, c)**: The AI components get disclosure buttons, description lists, a code block with a copy button, theme-resolved colours and icons, and heading levels, and the deprecated AI chat gets its policy fixes. ([`3bf2fd1`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/3bf2fd17cb0531bf4ae5280bf5367c2de791604c))
|
|
432
|
+
|
|
433
|
+
The AI migrations onto the shared blocks, the AI fixes and the deprecated-AI policy fixes (D-26 to D-35, WI-21a, WI-21b, WI-25, WI-26a, WI-32, WI-33a to WI-33e, WI-38, WI-41, WI-58+, E-26 and AILabel's ADR-03-R13 option C) in one release.
|
|
434
|
+
|
|
435
|
+
- **AILabel (ADR-03-R13 option C, WI-38).** The label's text and icon sit on the theme's `ai.primaryFill` in `ai.onPrimaryFill` (white on primaryFill: 4.5:1 or more in every mode, in either direction), and the AI gradient becomes a 1px ring around it; the label keeps its size. Dark mode, presets and overrides now reach the label (it kept the light gradient in every mode). The explainability popover takes the AI surface roles, so dark mode gets a dark popover. AILabel drew its own five-point star, outside the icon system, so it ignored the icon library setting and the manifest's AI glyph; it now renders `Icon name="sparkles"` (Material AutoAwesome, Feather's vendored Sparkles), decorative, at the same size and colour. CSS apps: the guide (docs/INSTRUCTIONS_CSS.md, Pattern 9) builds the same label from `--trinity-ai-primary-fill`, `--trinity-ai-on-primary-fill` and `--trinity-ai-gradient-primary`; no variable changes.
|
|
436
|
+
- **AI icon colours (WI-38).** AIActions, AIRelatedQuestions, AIPersonaCard, AIVisuals and AIDigestCard passed icon colours as hex values or resolved palette values. They now pass palette keys (`primary`, `text.secondary`, `primary.contrastText`, `common.white`) or inherit a resolver colour from their parent, so they follow theme overrides and dark mode. In light mode nothing changes. In dark mode the StatCard header icon moves from the light AI purple (#7841C9) to the dark AI primary role (#B985E1), and the trend icon and number use the dark status text roles. AIDigestCard's dismiss button renders `Icon name="close"` (the same glyph and 20px size) instead of importing @mui/icons-material.
|
|
437
|
+
- **AIContainer and AIPersonaCard colours (E-26, D-28).** The container's border reads `ai.border`, the filled body `ai.surface` and the filled header `ai.surfaceEmphasis` from the theme (identical in light). In dark mode the filled header kept its light tint under light text (1.15:1), which also affected AIDigestCard. AIPersonaCard's selected check sits on `ai.primaryFill`, and its capability chips render through the internal MetaChip on the theme's AI roles: `ai.primary` text on the `ai.surface` tint with an `ai.border` outline, 4.5:1 or more on the paper and on a selected card (the old tint was 4.16:1 there), with a 24px minimum height; they kept the light colours in dark mode.
|
|
438
|
+
- **AIExplainability (D-26, WI-25).** The model, confidence, last-updated and custom rows render as one `<dl>` of `<dt>` and `<dd>` pairs, values bidi-isolated; the look is unchanged. `confidence` stays on the 0-100 scale (unlike AIConfidenceBadge and the confidence utilities, which take 0-1; the JSDoc now says so). It was printed as `${confidence}%`; it now goes through `formatConfidence(confidence / 100, locale, { maximumFractionDigits: 2 })`, with the nearest `AIConfigProvider`'s `locale` or 'en-US' without one. `AIConfigProvider`'s `locale` is itself new in 2.13.0, so no existing output changes. In en-US, 85 still reads '85%' and 92.5 '92.5%'; more than two decimals are rounded (33.333 reads '33.33%'). In de-DE, 85 reads '85 %' and 92.5 '92,5 %'. New `showConfidenceLevel` (default `false`, @since 2.13.0): the confidence shows a level icon (`confidence-high`, `-medium` or `-low`, a different signal-bar shape per level) in the theme's confidence role colour, the level word from the new AI strings `ai.explainability.confidenceHigh`, `confidenceMedium` and `confidenceLow` ("High", "Medium", "Low"), then the percent in the provider's locale. The level follows the provider's thresholds and is rated on the shown, rounded value; a value outside 0-100 keeps the percent alone. Without the prop nothing changes.
|
|
439
|
+
- **Heading levels (WI-33a to WI-33e).** New `headingLevel` (2-6) sets the title element on AIContainer (default 6, which was always an `h6`), AISourcesSection (default 6), AIExplainability (default 6), ChatHeader (default 2, which was always an `h2`) and AIDigestCard (default 6, passed to AIContainer). AIContainer and ChatHeader also take `titleId`, which sets the title's id for `aria-labelledby` on a surrounding region or dialog; without it the title gets a generated id. The default output is the same element as before (with an id on it for AIContainer and ChatHeader).
|
|
440
|
+
- **Disclosures (D-27, D-29, D-30, D-31).** AIExpandableSection's header is one `<button type="button">` named by the title (and subtitle), with `aria-expanded` and `aria-controls`; Tab reaches it and Enter or Space toggles it. `expanded` and `defaultExpanded` follow MUI's controlled rule, and every toggle calls `onChange`. The chevron turns when open, the header shows a focus ring, the colours come from the theme's AI roles in dark mode too, and the panel paints its `ai.surface` tint over the paper, so body text keeps its contrast on any page background. AISourcesSection, collapsible (the default), puts one `<button type="button">` named by the title and the count inside its heading (`headingLevel`), with `aria-expanded` and `aria-controls`; the count chip and the "Show more" button use the theme's AI roles; with `collapsible={false}` the heading and the count are static and the list always shows. AIRelatedQuestions' header is a `<button type="button">` with `aria-expanded` and `aria-controls` from per-instance ids, so two instances share no id; the panel is no longer a region (one landmark per message, ADR-19), and the question list is named by the toggle. AISourcesPanel's header and each category with items are `<button type="button">` elements with `aria-expanded` and `aria-controls`; Enter and Space toggle them, and a chevron replaces the "[Show]" and "[Hide]" text; a category without items is plain text.
|
|
441
|
+
- **AISourcesPanel items (WI-21a, D-32, WI-21b, D-33, WI-58+).** Item labels were cut to 25 characters plus '...' in JavaScript before CSS cut them again, so the chip's accessible name lost the rest of the label (WCAG 2.5.3, 4.1.2) and a surrogate pair could be split. The label is now whole: CSS still ends it with an ellipsis (at 200px), a tooltip shows it all, and the chip is named "<letter> <label>" (the letter and the label used to run together, as in "aQ3 report"). Letters come from a bijective base-26 count (a ... z, aa, ab ...), where item 27 used to get '{'. A selected item whose id leaves `sources` is deselected, and a selected item shows the details of its current data. Each item chip is a button with `aria-expanded` and `aria-controls` for the item's details, never `aria-pressed`; the selected chip shows a 2px border, bold text and a decorative check icon instead of a solid primary fill. The selected item's details render as a `<dl>` of `<dt>` and `<dd>` pairs, values bidi-isolated, with a logical term-column border so it mirrors in right-to-left pages. `AISourceItem` takes `url` and `number`: `number` is shown on the item's chip in place of its letter, so AISourcesPanel can match the numbers of inline source chips, and `url` is shown with the item's details when its chip is selected, as a link (new tab, `rel="noopener noreferrer"`) for http(s), mailto, tel and relative URLs and as plain text with a development warning for anything else. Items without them render as before.
|
|
442
|
+
- **AITextBlock fenced code (D-34).** A fenced block keeps its language (the info string's first word) and renders on the sunken surface with the data.code typography: the language as a caption, a copy button that copies the code and announces once, `dir="ltr"` on the code, and a named, focusable scroll region only while the code scrolls. Icon's missing-provider warning (`useIconLibrary` without an `IconProvider`) is now development-only, so production bundles drop it. The labels come from the new AI strings `ai.textBlock.copyCode` ("Copy code") and `ai.textBlock.codeRegion` ("Code sample").
|
|
443
|
+
- **AIAvatar and AIVoiceMode (WI-26a, WI-32).** AIAvatar's root is `aria-hidden="true"` unless you pass `aria-label` or `aria-labelledby`, which make it `role="img"` with that name; `className` (@since 2.13.0), `id`, `data-*` and `aria-*` reach the root, and `sx` works as an object, array or function. AIAvatar's `pulse` and AIVoiceMode's orb animations no longer declare global `@keyframes` names, so another component's `pulse` cannot replace them; bodies and durations are unchanged, and under `prefers-reduced-motion: reduce` the animated AIAvatar keeps its glow without the pulse.
|
|
444
|
+
- **Deprecated AI chat components (WI-41, D-35).** Under the fix policy for deprecated AI components (DN-14), only security, WCAG 2.2 AA, cross-component and warning-placement fixes ship before v3.0 removes AIChatMessage, AIChatInput, AITypingIndicator, AIQuickReply and QueryInput. AIChatInput's voice, stop and send buttons are named, with matching tooltips; action and input icons use `text.secondary` instead of `text.disabled`; hovers and AI colours follow the theme in dark mode; the typing dots and QueryInput's listening mic animate under scoped names on the motion tokens and stop under reduced motion; the value follows MUI's controlled/uncontrolled rule; the deprecation warnings fire once from an effect and stay silent inside Trinity's own components. AIChatMessage's copy button renders through the internal CopyButton: the name and tooltip stay "Copy message", success is announced politely and failure assertively, a rejected `writeText` reaches the error path (`globalThis.reportError`) instead of showing as copied, and the feedback timer is cleared on unmount. Non-string content is copied as its rendered text. `onCopy` still runs on every click.
|
|
445
|
+
|
|
446
|
+
Visual review: docs/remediation/M-02-deprecated-ai-policy-fixes.md, docs/remediation/M5-summary.md (owner review list: C-21 and WI-32 AI rows, WI-41, WI-21a/WI-58+/D-31/D-32/WI-21b, D-29, D-27, WI-38/A3, WI-38/D-26/WI-25, E-26/D-28/WI-38, D-30, D-34, D-29..D-31)
|
|
447
|
+
- **Additive**: HITL data foundations: source and filter utilities, block sources and the organism contract types. ([`3072270`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/307227009db0946b3a71737ece454dcfdf3af98f))
|
|
448
|
+
|
|
449
|
+
**F-05 (TypeScript lib).** The library and app TypeScript configs add the `ES2021.Intl` lib, so source code can use the `Intl.ListFormat` types (F-05, OD-F24 A). Types only: the emitted declarations are byte-identical (`pnpm build:lib` before and after, `diff -r` of `dist/` is empty), and no runtime code changes, so nothing is released.
|
|
450
|
+
|
|
451
|
+
**F-05.** New source and filter utilities connect a response's `sources` to AISourcesPanel and format provenance the same way on the server and in the browser.
|
|
452
|
+
|
|
453
|
+
- `toSourceCategories(sources, options?)` turns `AIResponseSource[]` into `AISourceCategory[]`: categories by source type in order of first appearance (or one category with `groupBy: 'none'`), each `count` equal to its items, and each item with the source's `number` (its 1-based place in `sources`, the number inline source chips show), its `url` and its details in a fixed order (metric, filters, period, dataset, as-of date, excerpt). `useSourceCategories(sources, options?)` does the same with `AIConfigProvider`'s strings, locale and time zone, and keeps the result's identity while its inputs stay the same. Options type: `SourceCategoriesOptions`.
|
|
454
|
+
- `formatFilter(filter, options?)` writes one `AIPlanFilter` as text, for example 'Region: EMEA' or 'Revenue greater than 1,000', with numbers formatted for the locale.
|
|
455
|
+
- `formatAsOf(iso, options?)` formats an ISO 8601 date in UTC (the same calendar day in every zone) and a date-time in the given time zone with the zone's name; anything else is returned unchanged. `useFormatAsOf()` uses the provider's locale and time zone, and UTC until hydration when no time zone is set, so the markup hydrates without a mismatch.
|
|
456
|
+
- The AI strings gain the namespaces of the AI molecules: `ai.sources`, `ai.filters`, `ai.confidenceBadge`, `ai.verificationBadge`, `ai.sourceChip`, `ai.queryDisclosure`, `ai.clarificationPrompt`, `ai.feedbackForm` and `ai.feedbackControl` (114 keys). Strings that combine values are functions, so a locale can reorder the words.
|
|
457
|
+
|
|
458
|
+
Every new export is `@since 2.13.0`.
|
|
459
|
+
|
|
460
|
+
**F-08.** New `useBlockSources(blockId)` returns the sources behind one block of an AI response, so a custom table or chart renderer can put source chips on the cells and points the response cites.
|
|
461
|
+
|
|
462
|
+
- The result (`AIBlockSourcesApi`) has the block's `sources` (from `blockIds`), `getCellSources(row, column)` (from `cellRefs`; the row is the `rowKey` value, else the 0-based index), `getPointSources(series, index)` (from `pointRefs`) and `getSourceNumber(sourceId)`, the 1-based number inline source chips and AISourcesPanel show.
|
|
463
|
+
- Lookups read prebuilt maps: a large table can look up every cell while it renders.
|
|
464
|
+
- Outside an AI response the hook returns an empty API with a stable identity, never throws, and warns once in development.
|
|
465
|
+
- Numbers follow the order of the response's `sources`; append new sources while a response streams, and keep earlier ones in place. A development warning names a source that moved.
|
|
466
|
+
|
|
467
|
+
`@since 2.13.0`.
|
|
468
|
+
|
|
469
|
+
**G-02.** New types for the human-in-the-loop organisms: the query behind an answer, the plan the AI proposes before it runs, and the review queue.
|
|
470
|
+
|
|
471
|
+
- `AIQuery` is the one query shape (`query`, `language`, `metadata`).
|
|
472
|
+
- `AIPlan` (`id`, `revision`, `question`, `summary`, `steps`, `status`), `AIPlanStep`, `AIPlanStepStatus` and `AIPlanStatus`. Replanning keeps the plan's `id` and increments `revision`; a new `id` is a different plan.
|
|
473
|
+
- `AIReviewItemData`, `AIReviewStatus`, `AIReviewHistoryEntry`, `AIReviewEditableField`, `AIReviewEdits` and `AIReviewDecision`. A rejection must carry a `reason`, and an edit must change at least one field: both are compile errors otherwise.
|
|
474
|
+
- `AIActionErrorContext` is the second argument of every AI-family `onActionError(error, context)`. It is an open object, and a handler that takes only `(error)` stays assignable.
|
|
475
|
+
|
|
476
|
+
Every type is plain JSON and `@since 2.13.0`.
|
|
477
|
+
|
|
478
|
+
**F-19.** NAMING gains the "Confidence scale" rule (F-19, OD-F21 A): every confidence value in a Trinity API and the AI response schema is 0-1; display text comes only from `formatConfidence` or `describeConfidence`; 0-100 data is converted once with `normalizeConfidence(value, 'percent')`; AIExplainability's 0-100 `confidence` prop is the permanent documented exception. Documentation only, so nothing is released.
|
|
479
|
+
- **Behavior correction (a, b)**: AITextBlock: one linear parse per content, variant in markdown mode and the root props contract. ([`3072270`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/307227009db0946b3a71737ece454dcfdf3af98f))
|
|
480
|
+
|
|
481
|
+
**F-06.** AITextBlock parses markdown once per content (refactor, value-neutral).
|
|
482
|
+
|
|
483
|
+
The markdown parser moved out of AITextBlock into an internal module (`AITextBlock.parser.ts`, not exported from any entry) with no change to what it renders: characterization tests through the public component pass unchanged before and after the move. The parse is memoized on `content`, so a re-render with the same text no longer parses it again. The component's JSDoc no longer lists `_italic_` (underscores stay literal) and says that a fence without a closing line runs to the end of the content.
|
|
484
|
+
|
|
485
|
+
Bench (non-gating, Node 24, `vitest bench`), parse alone: 5 KB answer 0.055 ms; 20 KB `'[^'` 61.6 ms; 20 KB `'[a'` 61.7 ms. Render with parse: 5 KB answer 7.4 ms; 20 KB `'[^'` 61.7 ms; 20 KB `'[a'` 62.0 ms. These are F-25's "before" numbers.
|
|
486
|
+
|
|
487
|
+
**F-25.** AITextBlock parses inline markdown in linear time (refactor, value-neutral).
|
|
488
|
+
|
|
489
|
+
Markdown text with many `[` and no closing `](` on a line (for example a streamed answer full of footnote-style `[^` markers) made the inline parser rescan the rest of the line from every bracket: about 60 ms for 20 KB, re-run on every streamed token, and cubic time for repeated `[a](`. The parser is now a single forward scan whose output equals the old one part for part, proven by a differential test against a frozen copy of the old implementation on the characterization corpus and 5,000 generated strings. A 50 KB guard keeps it under 250 ms.
|
|
490
|
+
|
|
491
|
+
Bench (non-gating, Node 24, `vitest bench`), inline parse of the whole content, before -> after:
|
|
492
|
+
|
|
493
|
+
| Input | Before (regex) | After (scanner) |
|
|
494
|
+
|---|---|---|
|
|
495
|
+
| 5 KB representative answer | 0.051 ms | 0.071 ms |
|
|
496
|
+
| 20 KB `[^` repeated | 61.1 ms | 0.16 ms |
|
|
497
|
+
| 20 KB `[a` repeated | 61.5 ms | 0.16 ms |
|
|
498
|
+
| 4 KB `[a](` repeated | 410 ms | 0.15 ms |
|
|
499
|
+
| 50 KB `[^` repeated | 386 ms | 0.41 ms |
|
|
500
|
+
|
|
501
|
+
Render with parse (AITextBlock markdown, `renderToStaticMarkup`): 5 KB answer 7.4 -> 6.7 ms; 20 KB `[^` 61.7 -> 0.22 ms; 20 KB `[a` 62.0 -> 0.22 ms. F-06's baseline is in its changeset.
|
|
502
|
+
|
|
503
|
+
**WI-59+.** AITextBlock applies `variant` in markdown mode too.
|
|
504
|
+
|
|
505
|
+
With `markdown`, the `variant` prop was ignored, although its documentation and the plain-text path say it styles the block: a `caption` or `quote` block with markdown rendered as body text. Now paragraphs, list items and quoted lines take the variant's typography: `caption` is the small secondary style, `heading` an `<h3>` with the heading style, `quote` a `<blockquote>` with the quote border and italic secondary text. `body` output is unchanged; headings, code blocks and rules inside the content keep their own style. AIResponseRenderer text blocks with `markdown: true` and a `variant` (and through it AIMessage, ChatThread and InsightEngine) render in that variant's style.
|
|
506
|
+
|
|
507
|
+
A fenced code block that has not been closed yet while an answer streams renders as a provisional code block to the end of the content; once the closing fence arrives, the text after it renders as paragraphs again (unchanged behavior, now covered by a streaming test).
|
|
508
|
+
|
|
509
|
+
**F-07.** AITextBlock takes `className` and HTML attributes on its root, and its `sx` accepts every form in markdown mode.
|
|
510
|
+
|
|
511
|
+
- **NEW `className`** on the root element, and the root's HTML attributes (`data-*`, `aria-*`, event handlers) reach it in both modes. They were silently dropped before. `role` and the live-region attributes stay AITextBlock's.
|
|
512
|
+
- **`sx`** in markdown mode was spread into an object, so an array `sx` turned into keys `'0'`, `'1'` and a function `sx` was ignored, although `sx?: SxProps<Theme>` admits both (ADR-23 (a): the published type documents them; (b): the canonical root contract, remediation §5.4 and ADR-04 (4)). Every form now applies after the block's own styles; object `sx` is unchanged.
|
|
513
|
+
- **`id`**: a passed `id` is still rendered verbatim; without one the root gets a generated id that is stable between server and client (ADR-04 (10)).
|
|
514
|
+
|
|
515
|
+
AIResponseRenderer and AIDigestCard pass no `sx` or rest props, so their output is unchanged.
|
|
516
|
+
|
|
517
|
+
Visual review: WI-59+: owner Storybook review, M6 lane L3 list (ai-aitextblock--variants-in-markdown, ai-aitextblock--streaming-open-fence and the AIResponseRenderer, AIMessage and InsightEngine stories with caption markdown blocks), PNG compare against 65b5ca3d
|
|
518
|
+
- **Behavior correction (a, b, c)**: AI molecules: every AI colour through the resolver, right to left without the RTL plugin, motion from the tokens. ([`3072270`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/307227009db0946b3a71737ece454dcfdf3af98f))
|
|
519
|
+
|
|
520
|
+
**WI-36.** The AI molecules outside AIChainOfThought read every AI colour through the palette resolver (`getTrinityPalette`): AIRelatedQuestions, AIActions (AISuggestedAction, AICircularAction), AIVisuals (GradientText, StatCard, GradientIconBadge), AIAvatar, ChatHistoryList, ChatHeader, AIVoiceInput (AIVoiceOverlay, AIListeningIndicator), AIVoiceMode and InsightEngineInput, with AIContainer, AIExplainability, AISources and AISourcesPanel swept for the same rule.
|
|
521
|
+
|
|
522
|
+
- Dark mode, the crm, portal and analytics presets and token overrides now reach these colours. Before, they were the static light values: in dark mode the hover and resting tints were the light lavender (#EDE7FD) under light text, and the voice orb, the composer glow and the avatar kept their light colours.
|
|
523
|
+
- Light mode keeps its values (computed-colour snapshots per component), except the rows below.
|
|
524
|
+
- Text on the AI gradient (the `gradient` StatCard) sits on a solid AI fill with the gradient as a 1px ring (OD-E7 option C): white on the gradient's coral end was 2.96:1. Icons keep the full coral-to-purple brand gradient in light and dark: AIAvatar (owner decision 2026-10-08) and AICircularAction `gradient` (owner decision 2026-10-09, `ai.gradient.brandDiagonal`); the centered icon stays at 3:1 or more (see the M7 AIAvatar changeset and the AIFab brand gradient changeset).
|
|
525
|
+
- `sx` given as an array or a function is no longer dropped by AIRelatedQuestions, AIVisuals, ChatHistoryList, ChatHeader, AIVoiceInput, AIVoiceMode, InsightEngineInput, AIContainer, AIExplainability and AISourcesPanel.
|
|
526
|
+
- InsightEngineInput's text sizes follow the theme's type scale (portal: the input is 15px).
|
|
527
|
+
|
|
528
|
+
**WI-39.** The AI molecules outside AIChainOfThought and AITextBlock lay out right to left without stylis-plugin-rtl, and their motion comes from the motion tokens.
|
|
529
|
+
|
|
530
|
+
- Right to left (correction (a): the theme documents right-to-left support): AIRelatedQuestions and AISuggestedAction text aligns to the reading start; ChatHistoryList's options room, compact title gap and options menu, InsightEngineInput's attachment cards, chips, tools menu check mark and upload bar, and AIVoiceMode's top bar and orb highlight use logical properties or follow the theme's direction. Left to right nothing changes.
|
|
531
|
+
- Motion (correction (b): reduced motion is the canonical pattern): AIVoiceOverlay's Collapse and AIVoiceMode's full-screen Fade take a 0 timeout under reduced motion, and the theme's durations otherwise; the voice orb's loops and transitions use the motion tokens' curves and durations instead of CSS literals.
|
|
532
|
+
|
|
533
|
+
Visual review: WI-36: the owner's Storybook review of the M6 L4 story list (light, dark, crm/portal/analytics presets); WI-39: the owner's Storybook review of the M6 L4 story list (the right-to-left toolbar value); approved by the owner, 2026-10-08
|
|
534
|
+
- **Additive**: New AIConfidenceBadge, AIVerificationBadge and AIClarificationPrompt. ([`3072270`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/307227009db0946b3a71737ece454dcfdf3af98f))
|
|
535
|
+
|
|
536
|
+
**F-10.** New `AIConfidenceBadge` shows how confident the AI is in an answer: a level glyph, the level word and, with a score, the percent ("High · 92%").
|
|
537
|
+
|
|
538
|
+
- `value` is on the 0-1 scale. The level is rated on the displayed, Intl-rounded value, against the badge's `thresholds`, then `AIConfigProvider`'s, then the defaults (medium from 60%, high from 85%). A judge that returns only a category passes `level`. A value outside 0-1 shows "Confidence unavailable" and warns in development.
|
|
539
|
+
- Variants `badge` (a tinted chip), `inline` (glyph and text, for plan steps and option descriptions) and `bar` (the "Confidence" label, the text and a decorative bar); sizes `small` and `medium`; `loading` shows a placeholder and reads "Calculating confidence".
|
|
540
|
+
- With `rationale`, the badge is a button that opens a details dialog with the level, the score, the thresholds and the rationale.
|
|
541
|
+
- A screen reader reads one summary ("Confidence: High, 92%"). Every level has its own signal-bar glyph, never colour alone. The root is a `<span>`, valid inside a paragraph; `aria-label` is not set on it (use `label` and `levelLabels`, or the `ai.confidenceBadge` strings).
|
|
542
|
+
- Colours come from the theme's confidence roles, so brand themes and plain MUI themes both work.
|
|
543
|
+
|
|
544
|
+
Exports `AIConfidenceBadge` and `AIConfidenceBadgeProps`, `@since 2.13.0`.
|
|
545
|
+
|
|
546
|
+
**F-11.** New `AIVerificationBadge` says who checked an answer or a plan: "Verified", "Answered from verified dataset", "Confirmed plan", "Pending review", "Flagged for review" or "Rejected".
|
|
547
|
+
|
|
548
|
+
- Each status has its own glyph (verified, award, check, clock, flag, close) and label, none shared with the confidence glyphs, in the theme's verification colours. Variants `chip` and `inline`; sizes `small` and `medium`. Labels wrap at narrow widths.
|
|
549
|
+
- With `verifiedAt`, `datasetName`, `note` or your own `details`, the badge is a button that opens a details dialog; `verifiedAt` is formatted with the provider's locale and time zone.
|
|
550
|
+
- Not a live region, so it is safe inside a chat log. With `live`, a status change after mount is announced once ("Verification status: Verified"); the first render says nothing.
|
|
551
|
+
- The root is a `<span>`; `aria-label` is not set on it (use `label`).
|
|
552
|
+
|
|
553
|
+
Exports `AIVerificationBadge` and `AIVerificationBadgeProps`, `@since 2.13.0`.
|
|
554
|
+
|
|
555
|
+
**F-15.** New `AIClarificationPrompt` asks a clarifying question before the AI answers and returns a typed answer, `{ optionIds, otherText }`.
|
|
556
|
+
|
|
557
|
+
- Options are native radios (`selectionMode: 'single'`) or checkboxes (`'multiple'`) in a group named by the question: `question`, else `clarification.question`, else a template by reason with the term (`ai.clarificationPrompt.question.*`). An option's description, and its confidence as an inline `AIConfidenceBadge`, are read as its description.
|
|
558
|
+
- "Something else" (with `clarification.allowOther`, or the whole answer when there are no options) reveals a text field without moving focus.
|
|
559
|
+
- Variant `message` is a form with "Send answer" (Enter in the field sends; the submit never reaches an outer form). Variant `embedded` has no form and no button, for a host that owns the form: read `onChange`.
|
|
560
|
+
- A `required` question rejects an empty answer with an error message and focus on the first option. While `onSubmit` runs, the button stays focusable and busy ("Sending answer…"); a rejection shows an alert with "Try again", announced once, and keeps the answer. After a successful send, a summary ("You answered: Net revenue") replaces the controls and takes focus. The host can drive `loading`, `error`, `errorMessage` and `submitted`.
|
|
561
|
+
- `streaming` shows placeholder options and unavailable controls with the reason, and announces "Question ready for your input" once when it ends. `disabled` makes every control unavailable (never natively disabled) with `disabledReason` shown.
|
|
562
|
+
- Controlled (`value`, `onChange`) or uncontrolled (`defaultValue`). Reports content-free `clarification` interactions to `AIConfigProvider`'s `onInteraction`; the typed text only with `includeContent`.
|
|
563
|
+
|
|
564
|
+
Exports `AIClarificationPrompt` and `AIClarificationPromptProps`, `@since 2.13.0`.
|
|
565
|
+
|
|
566
|
+
Visual review: F-10: the owner's Storybook review (M6 summary list): ai-aiconfidencebadge--playground, --default, --states, --matrix, --all-variants, --level-only, --narrow, --rtl, --details-open, in light, dark, right to left and forced colours; F-11: the owner's Storybook review (M6 summary list): ai-aiverificationbadge--playground, --default, --states, --matrix, --all-variants, --with-details, --status-change-announcement, --narrow, --rtl, --details-open, in light, dark, right to left and forced colours; F-15: the owner's Storybook review (M6 summary list): ai-aiclarificationprompt--playground, --default, --states, --matrix, --loading, --error, --all-variants, --embedded, --host-driven, --narrow, --rtl, --answer-flow, in light, dark, right to left and forced colours
|
|
567
|
+
- **Behavior correction (a)**: New AISourceChip, and AITextBlock citations, underscore italic, heading offset and right-to-left layout. ([`3072270`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/307227009db0946b3a71737ece454dcfdf3af98f))
|
|
568
|
+
|
|
569
|
+
**F-12.** New `AISourceChip` names one source of an AI response at the number, sentence, table cell or chart point it supports, and opens the source's details.
|
|
570
|
+
|
|
571
|
+
- A native `<button type="button">` (valid inside paragraphs and headings) on the internal MetaChip, with the AI badge roles (`ai.badgeBackground`, `ai.primary`, an `ai.border` edge); the root carries the ref, `className`, `sx` and the button's HTML attributes.
|
|
572
|
+
- `variant="label"` (default) shows the metric (else the title) and the period, and meets 24 by 24; `variant="number"` shows the source's number: `index`, else its 1-based place in the response's `sources` (the number AISourcesPanel and `useBlockSources` show). A number chip in running text uses the meta size on the baseline (WCAG 2.5.8 inline exception). Without a number it shows its label and warns once in development.
|
|
573
|
+
- Names come from `ai.sourceChip` strings ('Source 1: Q4 report'; a label chip's name starts with its visible label).
|
|
574
|
+
- The details are a modal dialog named by the source's title: metric, filters, period, dataset, as-of date and excerpt, or 'No additional details'; `renderDetails` replaces the list. 'Open source' is a link only for http(s), mailto, tel and relative URLs (new tab, announced); any other URL shows as text. `showFavicon` (default false) loads the source host's favicon under the image policy. 'Show query' appears only with `onShowQuery` or a registered query view, and reveals the query after the dialog has closed.
|
|
575
|
+
- Open state: `open`, `defaultOpen`, `onOpenChange(open, reason, event?)` with `'trigger' | 'escape' | 'clickAway' | 'action'` (`AISourceChipOpenChangeReason`). Opening reports `{ type: 'source-open', sourceId, sourceType, variant, index }` through `AIConfigProvider`'s `onInteraction`.
|
|
576
|
+
- Types: `AISourceChipProps`, `AISourceChipOwnProps`, `AISourceChipOpenChangeReason`.
|
|
577
|
+
|
|
578
|
+
`@since 2.13.0`. The package export waits for the F-01 component proposal vote (A-21 registry entry, end batch).
|
|
579
|
+
|
|
580
|
+
**F-13.** AITextBlock can render `[^id]` citation markers as source chips, when asked.
|
|
581
|
+
|
|
582
|
+
- NEW `citations` (default `false`): each `[^id]` marker (`id`: 1 to 64 of `A-Z a-z 0-9 . _ : -`) that names a source renders as an `AISourceChip` number chip; the number is the source's place in `sources`. Markers in headings, list items, bold and italic text become chips; markers in inline code, fenced code and link text stay text; `[^s1](Q2)` is a chip followed by text.
|
|
583
|
+
- NEW `sources`: the sources markers name. Without it, the sources of the AI response the block renders in.
|
|
584
|
+
- NEW `unresolvedCitations`: `'literal'` (default) shows a marker that names no source as written; `'hide'` shows nothing for it, nor for a partial marker at the end of streamed content.
|
|
585
|
+
- NEW `onShowQuery`: passed to each chip.
|
|
586
|
+
- A chip keeps its state (an open popover) while text streams in after it.
|
|
587
|
+
|
|
588
|
+
Without `citations`, output is unchanged for all content: `[^1]` stays text even when a source has the id `1` (OD-F15: opt-in, because consumers whose models already emit footnote markers would otherwise see text turn into buttons). `@since 2.13.0`.
|
|
589
|
+
|
|
590
|
+
**WI-31a.** AITextBlock renders `_italic_` as emphasis, as its documentation always said.
|
|
591
|
+
|
|
592
|
+
- Correction (a): the header table documented `*italic*` or `_italic_`, but only `*italic*` was parsed. Now `_x_` is italic when the underscores start and end words, so `snake_case`, `file_name.ts`, `__init__` and `2_000_000` stay as written.
|
|
593
|
+
- Applies in markdown mode only; code spans and link URLs keep their underscores.
|
|
594
|
+
|
|
595
|
+
**WI-31b.** AITextBlock's new `headingOffset` moves the content's own headings down.
|
|
596
|
+
|
|
597
|
+
- NEW `headingOffset?: 0 | 1 | 2 | 3 | 4 | 5` (default `0`): in markdown mode, `#` renders `h(1 + offset)`, clamped at `h6`, so an answer inside a page or a chat thread never adds an `<h1>`. The look follows the authored level. The default keeps today's elements (NAMING, "Heading level").
|
|
598
|
+
|
|
599
|
+
`@since 2.13.0`.
|
|
600
|
+
|
|
601
|
+
**WI-39 (AITextBlock).** AITextBlock lays out right to left without stylis-plugin-rtl, and its inline code uses the code type role.
|
|
602
|
+
|
|
603
|
+
- Right to left (correction (a): the theme documents right-to-left support): list indents, the markdown quote's rule and the `quote` variant's rule use `paddingInlineStart` and `borderInlineStart`, so they sit at the reading start. Left to right nothing changes.
|
|
604
|
+
- Inline code takes `aiTypography.code` (the `data.code` family at the dense-text size) instead of `'monospace'` at `0.9em` of the surrounding text: 12px in every variant (was 12.6px in body text, 10.8px in captions).
|
|
605
|
+
|
|
606
|
+
**WI-42b.** Documentation truth for the AI components (WI-42b): the README's AI tables no longer claim streaming support for AITextBlock or 'tables, charts, actions' for AIResponseRenderer and mark the four deprecated AIChat exports with their replacements; the Welcome page and the AI agent guide no longer list internal components (HtmlToMui, StreamingHtmlToMui, SandboxedIframeRenderer, DocumentCard) or a nonexistent DateTime as package imports, and name the entries of SimpleTable and DataTable; AITextBlock's stories no longer claim markdown tables are parsed. No code change.
|
|
607
|
+
|
|
608
|
+
Visual review: F-12: the owner's Storybook review of the AI/AISourceChip stories (light, dark, right to left, forced colours); WI-31a: the owner's Storybook review of ai-aitextblock--underscore-italic and ai-aitextblock--with-markdown; WI-39 (AITextBlock): the owner's Storybook review of ai-aitextblock--markdown-rtl and the AITextBlock stories with inline code; approved by the owner, 2026-10-08
|
|
609
|
+
- **Behavior correction (b)**: New AIQueryDisclosure, AIFeedbackForm and AIFeedbackControl, and the inline async error in narrow hosts. ([`3072270`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/307227009db0946b3a71737ece454dcfdf3af98f))
|
|
610
|
+
|
|
611
|
+
**F-14.** New `AIQueryDisclosure` shows the query behind an AI answer on demand: a disclosure button, the query as left-to-right code with a copy button, and the run's facts.
|
|
612
|
+
|
|
613
|
+
- The trigger is a button with `aria-expanded` and `aria-controls` and a constant label ('Query', or 'SQL query' with `language`); no heading unless `headingLevel` is set.
|
|
614
|
+
- The code is `<pre dir="ltr">`, scrolls past `maxHeight` (default `theme.spacing(40)`) and is a focusable group named 'SQL query text' while it scrolls. Copy announces 'Query copied' or 'Copy failed' once and calls `onCopy(query)` after the write.
|
|
615
|
+
- Metadata (`AIQueryMetadata`): rows through `ai.queryDisclosure.meta.rowCount({ count })`, duration in seconds (Intl, provider locale), dataset, dialect, and executed-at through `useFormatAsOf` (SSR-stable). The tiles stack below the compact container width.
|
|
616
|
+
- `streaming` shows a Skeleton and makes the trigger `aria-disabled`, described by 'The query is still being generated'. An empty query renders nothing and warns once in development.
|
|
617
|
+
- With `metadata.queryId` inside an AI response, a source chip's 'Show query' opens the view and moves focus to its trigger.
|
|
618
|
+
- `expanded`, `defaultExpanded`, `onExpandedChange(expanded, event?)`; root props contract (ref, className, rest, sx) and `slotProps` `trigger`, `panel`, `code`, `metadata`, `copyButton`.
|
|
619
|
+
- Reports `query-view` (expand, collapse) and `copy` (success) interactions to `AIConfigProvider`; never the query text.
|
|
620
|
+
|
|
621
|
+
`@since 2.13.0`.
|
|
622
|
+
|
|
623
|
+
**F-16.** New `AIFeedbackForm` collects detailed feedback on an AI answer: problem types, a comment and an optional request to run the question again.
|
|
624
|
+
|
|
625
|
+
- An unnamed native `<form noValidate>`, so it adds no landmark (pass `aria-labelledby` to make it one). The title ('Tell us what went wrong') is text unless `headingLevel` is set.
|
|
626
|
+
- Problem types are checkboxes in a fieldset named 'Problem type': the built-in categories `wrong-number`, `wrong-interpretation`, `synthetic`, `sources`, `outdated-data`, `formatting`, `other` (text from `ai.feedbackForm.category.*`), or your own `AIFeedbackCategoryOption`s; an empty list hides the field. A multiline 'Details (optional)' field; 'Rerun with this correction' with `allowRerun`.
|
|
627
|
+
- Nothing is required by default. `requireCategory` marks the checkboxes invalid on an empty submit, shows 'Choose at least one problem type.' and focuses the first.
|
|
628
|
+
- `onSubmit(event: AIFeedbackEvent)` runs through the shared async contract: pending Submit is `aria-disabled` and `aria-busy` and keeps focus; a failure shows an inline error with 'Try again' (announced once, no implicit live role) and keeps the input; success shows 'Thanks — sent for review' and moves focus to it. `loading`, `error`, `errorMessage` and `submitted` let a host own the outcome.
|
|
629
|
+
- The submit handler calls `preventDefault` and `stopPropagation`: an ancestor React form reached through a portal never receives it. Do not nest the form in another `<form>`.
|
|
630
|
+
- Draft `value` / `defaultValue` / `onChange(draft, event?)` (`AIFeedbackDraft`); root props contract and `slotProps` `title`, `categories`, `comment`, `rerun`, `actions`, `alert`, `summary`.
|
|
631
|
+
- Standalone, reports one content-free `feedback` interaction (`detail-submit`) per submit; the comment only with `includeContent`. Inside `AIFeedbackControl` the control reports instead.
|
|
632
|
+
|
|
633
|
+
`@since 2.13.0`.
|
|
634
|
+
|
|
635
|
+
**F-17.** New `AIFeedbackControl` rates an AI answer with thumbs up and down and, in detailed mode, opens a "Tell us what went wrong" flow.
|
|
636
|
+
|
|
637
|
+
- A `role="group"` named 'Rate this response'. The thumbs are pure toggles: `aria-pressed` and constant names ('Good response', 'Poor response') that are also their tooltips. Pressed shows the filled glyph in `status.success.text` or `status.error.text` on the theme's pressed recipe; at rest the glyph is `text.secondary`. Each is at least 24 by 24. Pressing a pressed thumb clears the rating.
|
|
638
|
+
- `value` / `defaultValue` / `onChange(rating | null, event)`.
|
|
639
|
+
- `mode="detailed"`: while the rating is negative and nothing was sent, a 'Tell us what went wrong' button (the only popup trigger) opens `AIFeedbackForm` in a popover, or in a full-screen Modal below the theme's `sm` breakpoint. The surface has one heading; `categories`, `allowRerun` and `requireCategory` go to the form.
|
|
640
|
+
- `onSubmit` runs through the shared async contract; while it is pending no close path works. On success the surface closes, focus moves to thumbs-down, 'Feedback sent' is announced once and replaces the trigger as text. On failure the form's error shows and the surface stays open. `submitted` shows the sent state.
|
|
641
|
+
- `disabled` keeps every button focusable (`aria-disabled`) and describes why (`disabledReason`).
|
|
642
|
+
- Reports `feedback` interactions (rate, clear, detail-open, detail-submit, detail-cancel) to `AIConfigProvider`, content-free (the comment only with `includeContent`).
|
|
643
|
+
- Root props contract and `slotProps` `positiveButton`, `negativeButton`, `detailsTrigger`, `popover`, `dialog`, `form`, `status`.
|
|
644
|
+
|
|
645
|
+
`@since 2.13.0`.
|
|
646
|
+
|
|
647
|
+
**F-16 (AsyncOutcomeAlert).** The inline error of a failed async action (shown by Modal, ConfirmDialog, the connector wizard and the AI feedback form) no longer squeezes its message into a sideways-scrolling box in a narrow host: the 'Try again' button wraps below the message once the message would be narrower than 12rem, and long words wrap. Fails test (b): the narrow message was a scrollable region with nothing focusable (axe scrollable-region-focusable, WCAG 2.1.1). Wide hosts look the same.
|
|
648
|
+
|
|
649
|
+
Visual review: F-14: the owner's Storybook review (the story list in docs/remediation/M6-index.md); F-16: the owner's Storybook review (the story list in docs/remediation/M6-index.md); F-17: the owner's Storybook review (the story list in docs/remediation/M6-index.md); F-16 (AsyncOutcomeAlert): the owner's Storybook review: ai-aifeedbackcontrol--send-error (light, dark); wide hosts unchanged (Modal and ConnectorWizard error stories)
|
|
650
|
+
- **Additive**: The M7 enabler: AI strings for the M7 organisms, the interaction event types, AIBlockEvent and the internal InThreadContext. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
651
|
+
|
|
652
|
+
**P0.** The M7 enabler: AI strings for the M7 organisms, the G-26 interaction events, the AIBlockEvent type and the internal InThreadContext. Nothing renders differently and no component emits the new events yet.
|
|
653
|
+
|
|
654
|
+
- **AI strings.** 15 new keys with English defaults, overridable through `AIConfigProvider`'s `strings`:
|
|
655
|
+
- `ai.common.opensInNewTab` ('Opens in a new tab').
|
|
656
|
+
- `ai.message.copyUnavailableStreaming`, `regenerate`, `regenerateResponse`, `regenerateFailed`, `assistantResponse` and `assistantResponseAt({ time })`.
|
|
657
|
+
- New groups `ai.userMessage` (`label`, 'Your message') and `ai.responseRenderer` (`errorTitle`, `blockUnavailable`).
|
|
658
|
+
- `ai.chainOfThought.reasoningFor({ query })` and `thinking`; `reasoningChain` stays the name when there is no query.
|
|
659
|
+
- `ai.chatHistoryList.optionsFor({ title })`, `ai.insightEnginePanel.demoLabel` and `ai.threadHistorySidebar.searchLabel`.
|
|
660
|
+
No existing key is renamed. Composite strings are functions of named arguments.
|
|
661
|
+
- **Interaction events (G-26).** `AIInteractionEvent` gains six content-free members: `plan` (`step_edit`, `confirm`, `cancel`, `clarify`), `review` (`decide`, `edit_start`, `reject_start`), `retry`, `followup` (`select`), `message` (`send`) and `answer` (`stopped`). Each carries ids, enums, counts and flags only, never user or model text. `AIFeedbackInteraction` and `AICopyInteraction` gain an optional `messageId`, and `AICopyInteraction.target` widens to `'query' | 'response'`. The union is open: keep a default branch when you switch on `type` or `target`.
|
|
662
|
+
- **AIBlockEvent (ADR-05 (3)).** The event type an interactive response block reports: `clarification.submit`, `plan.confirm`, `plan.stepEdit`, `plan.cancel` and `query.copy`. Open union.
|
|
663
|
+
- **InThreadContext.** An internal context (ADR-02, on no package entry) that tells AI components they render inside a thread's live container. No public change.
|
|
664
|
+
|
|
665
|
+
The CSS package has no change of its own; it moves with the fixed release group.
|
|
666
|
+
- **Behavior correction (a, b, e)**: The AI panel's history is keyboard-usable with inline rename and a Star toggle; model and source options are props and can be controlled; no stray deprecation warnings; the canonical New thread glyph. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
667
|
+
|
|
668
|
+
**D-40.** The thread history's rename and Star work for keyboard and screen-reader users.
|
|
669
|
+
|
|
670
|
+
- **Rename (WCAG 4.1.2, 2.4.3).** The rename field rendered inside the row's button (nested interactive content, unnamed), committed on blur, silently dropped an empty name, and left focus on the page after Enter or Escape. It now replaces the row while editing: a field named 'Thread name' with Save and Cancel; Enter or Save commits the trimmed name, Escape or Cancel cancels, Tab leaves without saving, and focus returns to the row. An empty name shows 'Enter a thread name.' and keeps the field open.
|
|
671
|
+
- **Star (WCAG 4.1.2).** Star flipped its name and tooltip between 'Star' and 'Unstar' with no state. It is now a toggle named 'Star thread' with `aria-pressed`, drawn with the theme's pressed recipe (selected overlay, brand colour and ring) instead of the yellow icon.
|
|
672
|
+
- The row's icons render through `Icon` pinned to the Material library (D-54), so each glyph is unchanged.
|
|
673
|
+
|
|
674
|
+
**H-37.** InsightEnginePanel takes your own model and source lists.
|
|
675
|
+
|
|
676
|
+
NEW `modelOptions?: InsightModelOption[]` and `sourceOptions?: InsightSourceOption[]` replace the panel's built-in sample lists (five vendor model names; Web Search, Documents, Database, SharePoint, Projects) in the composer's model and source menus. Supplied models start on their first option. Without them the built-in lists remain as deprecated defaults: a one-time development warning (`InsightEnginePanel.defaultModelOptions`, from an effect) says so, and the defaults become empty in v3.0.
|
|
677
|
+
|
|
678
|
+
**H-38.** InsightEnginePanel's model and source selection is controllable and reaches `onSendMessage`.
|
|
679
|
+
|
|
680
|
+
NEW controlled pairs through `useControlled`: `selectedModel` / `defaultSelectedModel` / `onModelChange` and `enabledSources` / `defaultEnabledSources` / `onSourcesChange`. Uncontrolled, the panel starts on the first of `modelOptions` (else 'gpt-4o') and on the `sourceOptions` marked `enabled` (else ['web', 'documents'], today's start). `onSendMessage`'s options widen additively with `model` (the selected model's id) and `sources` (the enabled sources' ids): before, the selectors changed nothing a backend could see. Handlers written for `(message, options?) => void` compile unchanged.
|
|
681
|
+
|
|
682
|
+
**H-39.** AppLayout's AI panel no longer tells you to migrate away from components you never used, and AIFabLayout warns from an effect.
|
|
683
|
+
|
|
684
|
+
InsightEnginePanel renders the deprecated `AIChatMessage` and `AITypingIndicator` itself, so every AppLayout app that opened the panel saw their deprecation warnings. The panel now renders them inside `DeprecationSuppressionContext` (ADR-30 (5)): no warning comes from its own usage, while a direct `AIChatMessage` or `AITypingIndicator` render still warns once. `AIFabLayout` warned during render (server rendering logged it); it now warns once per session from a mount effect (ADR-30 (3)). No render-phase deprecation call is left in `src/components` (the ADR-30 (6) ratchet reaches 0).
|
|
685
|
+
|
|
686
|
+
**H-42.** The AI panel's thread history is usable from the keyboard, and shows only the actions you wired.
|
|
687
|
+
|
|
688
|
+
- **Row actions (WCAG 2.1.1).** Star, Rename and Delete mounted only while the pointer was over the row, so the keyboard never reached them, and a focused Delete unmounted (focus dropped to the page) when the pointer left. They now stay mounted and in the tab order after the row, revealed while the row is hovered or holds focus, and always on a device without hover (touch). A starred thread keeps its star visible.
|
|
689
|
+
- **Unwired actions (correction (e)).** Each action renders only when its handler is passed (`onThreadStar`, `onThreadRename`, `onThreadDelete`); before, all three rendered and did nothing without one.
|
|
690
|
+
- **'More options' (correction (e)).** The footer's 'More options' button had no handler and no menu; it is removed.
|
|
691
|
+
- **Search.** The search field is `type="search"` with the accessible name 'Search threads' (`ai.threadHistorySidebar.searchLabel`); it had only a placeholder.
|
|
692
|
+
|
|
693
|
+
**H-49+.** InsightEnginePanel's New thread button shows its glyph.
|
|
694
|
+
|
|
695
|
+
The header's New thread IconButton rendered `<Icon name="plus">`, a key neither icon map has, so the button was blank in both libraries (its tooltip and name were intact). It now renders the canonical `add` glyph. The C-17 icon-name allowlist is empty.
|
|
696
|
+
|
|
697
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
698
|
+
- **Behavior correction (b)**: The AppLayout AI panel is a named landmark with focus return, a modal drawer on phones, and a FAB that names itself and respects reduced motion. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
699
|
+
|
|
700
|
+
**H-11.** The AppLayout AI panel is a named landmark, closing it never drops focus, and its triggers report their state.
|
|
701
|
+
|
|
702
|
+
- **Landmark.** The docked AI panel is a complementary landmark. With InsightEnginePanel it is named by the panel title (AppLayout passes a generated `titleId`; a `titleId` in `panelProps` wins); with `renderAiPanel` it is named "AI assistant". No new prop.
|
|
703
|
+
- **Focus return.** Closing the panel from inside it (its Close button, or `onClose` from `renderAiPanel`), docked or fullscreen, with or without `keepAiPanelMounted`, moves focus back to the control that opened it: the FAB or the AI nav item. If that control is gone, focus goes to the main content. Focus that is already elsewhere is not moved.
|
|
704
|
+
- **AIFab.** Now honours its `open` prop as `aria-expanded`. NEW optional `controls` (string): the id of the panel it opens, rendered as `aria-controls`; AppLayout passes it while the panel is in the document.
|
|
705
|
+
- **TopNavWithSidebar nav items.** NEW optional `kind?: 'page' | 'toggle'` (default `'page'`), `expanded?: boolean` and `controls?: string`. A toggle item never becomes the selected item and never carries `aria-current="page"`; it renders `aria-expanded` / `aria-controls` when defined, still fires `onNavItemClick`, and its highlight follows `expanded`. Items without `kind` are unchanged.
|
|
706
|
+
- **AppLayout's AI nav item** (`id: 'insight-engine'`) is now such a toggle: it no longer takes `aria-current="page"` or the selection, so the page you are on keeps both. AppLayout's own `NavItem` type is unchanged.
|
|
707
|
+
|
|
708
|
+
Before: the AI nav item became the "current page" after a click (and stayed highlighted after the panel closed), and closing the panel from its Close button left keyboard focus on the page body. The CSS package has no change of its own (component behavior only).
|
|
709
|
+
|
|
710
|
+
**H-40.** On phones and small tablets the AppLayout AI panel is a modal drawer instead of a docked panel that overflowed the screen.
|
|
711
|
+
|
|
712
|
+
- **Below `theme.breakpoints.down('md')`** the panel renders in a full-width temporary drawer at the inline end: `role="dialog"`, `aria-modal`, named by the panel title (or "AI assistant" with `renderAiPanel`). Focus stays inside it, Escape (or the backdrop) closes it, and the page behind it is inert. There is no content margin, no resize handle, no fullscreen toggle on InsightEnginePanel (the drawer already fills the screen), and no FAB over it while it is open. Closing it returns focus to the control that opened it (the FAB, the AI nav item, or the navigation toggle when the nav item sat in the closed navigation drawer). `keepAiPanelMounted` keeps the drawer content mounted.
|
|
713
|
+
- **NEW `mobilePanelPresentation?: 'drawer' | 'docked'`** (AppLayout, default `'drawer'`): `'docked'` keeps the docked, resizable panel at every width, as before.
|
|
714
|
+
- The breakpoint is read with `useMediaQuery`, `false` on the server and during hydration, so server markup is unchanged and hydrates without a mismatch; the drawer appears after hydration on a phone.
|
|
715
|
+
- Desktop and tablet-landscape widths (md and up) are unchanged.
|
|
716
|
+
|
|
717
|
+
The CSS package has no change of its own (component behavior only).
|
|
718
|
+
|
|
719
|
+
**H-41.** AIFab respects reduced motion and places itself with logical insets.
|
|
720
|
+
|
|
721
|
+
- **Motion.** The FAB's `transition: all 0.3s ease` becomes `transform` and `box-shadow` over the motion tokens (`semanticTokens.motion.duration.slow`, 300ms, and `easing.default`). Under `prefers-reduced-motion: reduce` nothing animates and the hover (1.05) and press (0.95) scale is off.
|
|
722
|
+
- **Position.** The `position` names are logical: `right` is the inline end and `left` the inline start (`inset-inline-end` / `inset-inline-start`), `top` and `bottom` the block start and end. A right-to-left page puts `'bottom-right'` at the bottom left with or without stylis-plugin-rtl (before, only the plugin flipped it). `offset.x` is measured from that inline edge.
|
|
723
|
+
- **Icon.** The sparkle renders through `Icon` (`name="sparkles"`, Material, the library the file used), so it is the same AutoAwesome glyph and keeps its `AutoAwesomeIcon` test id.
|
|
724
|
+
- The default z-index is already the theme's `aiFab` layer role (E-46+); unchanged.
|
|
725
|
+
|
|
726
|
+
The CSS package has no change of its own (component styles only; the motion tokens are already in it).
|
|
727
|
+
|
|
728
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
729
|
+
- **Behavior correction (b)**: The AI components read their text from the strings catalog and follow the root props contract. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
730
|
+
|
|
731
|
+
**WI-37.** The AI components read their text from the AI strings catalog, so `AIConfigProvider` (once its export lands, E-23) can translate or reword them.
|
|
732
|
+
|
|
733
|
+
- AIRelatedQuestions, AICircularAction, ChatHistoryList, ChatHeader, AIVoiceOverlay, AIListeningIndicator, AIVoiceMode, InsightEngineInput, AIExplainability, AISource, AISourcesSection, AISourcesPanel, AILabel and AIDigestCard read their titles, labels, names, tooltips and status text through `useAIStrings` (`ai.<component>.<key>`). Without a provider every default is the text the component showed before, except the Send tooltip below.
|
|
734
|
+
- A per-instance prop still wins over the provider (`title`, `emptyMessage`, `placeholder`, `text`, `popoverLabel`, `cadenceLabel`, the voice text props).
|
|
735
|
+
- A tooltip and its button's accessible name come from one key.
|
|
736
|
+
- Counts are functions of the count, so a locale picks its own plural form with `Intl.PluralRules`: AISourcesSection's show more, AIDigestCard's show more highlights, InsightEngineInput's source count, and AISourcesPanel's header and category counts (`ai.sourcesPanel.titleWithCount`, `categoryWithCount`, new). The English defaults pluralize with `Intl.PluralRules('en-US')`.
|
|
737
|
+
- New keys: `ai.circularAction.nameWithBadge`, `ai.insightEngineInput.stopGenerating`, `ai.sourcesPanel.titleWithCount`, `ai.sourcesPanel.categoryWithCount`.
|
|
738
|
+
- InsightEngineInput's source chip, which shows only a number, is now named "2 sources selected" (`ai.insightEngineInput.sourcesSelected`).
|
|
739
|
+
- InsightEngineInput's Send tooltip reads "Send message", the same as its accessible name, and Send has `aria-keyshortcuts="Enter"` (OD-E11 option A, the plan's recommendation; it waits for the owner's decision). It read "Send (Enter)".
|
|
740
|
+
- Visual: the Send tooltip text; and where a count or a plural suffix used to be a separate text node, the text is now one node, so the browser kerns across it (sub-pixel shifts of ")" or "s", same text): AISourcesPanel, Chat Components sources panel, AIDigestCard collapsed. Review ids in the hand-off.
|
|
741
|
+
|
|
742
|
+
**WI-40.** The non-chain AI components follow the root props contract (ADR-04).
|
|
743
|
+
|
|
744
|
+
- AISuggestedAction, AICircularAction, AIActionsGroup, GradientText, StatCard, GradientIconBadge, ChatHistoryList, ChatHeader, AIVoiceOverlay, AIListeningIndicator, AIVoiceMode, InsightEngineInput, AIContainer, AIExpandableSection, AIPersonaCard, AIExplainability, AISource, AISourcesSection, AISourcesPanel, AISourceCategoryItem, AILabel, AIDigestCard and AIRelatedQuestions take `className`, an array or function `sx` (applied after the component's own styles) and the HTML attributes of their root (`id`, `data-*`, `aria-*`, event handlers), spread on the root. AIAvatar, UserAvatar, AIShimmer and AITextBlock already did.
|
|
745
|
+
- `role`, `aria-live`, `aria-busy`, `aria-relevant`, `aria-atomic` and `aria-modal` stay the component's (AIVoiceMode's dialog or group, AIDigestCard's region): a value passed for one is ignored, with one development warning. `aria-labelledby` and `aria-describedby` join the component's own ids.
|
|
746
|
+
- Split targets stay where they were: AICircularAction's ref, `className` and attributes reach its wrapper and `sx` styles the button; new `slotProps.root` styles the wrapper. AIVoiceOverlay's ref, `className`, attributes and `sx` reach its panel; new `slotProps.collapse` reaches the Collapse around it.
|
|
747
|
+
- ChatHistoryList forwards its ref in the empty state too (it was dropped). The ref type widens from `HTMLUListElement` to `HTMLElement`, because the empty state's root is a `div`; a `RefObject<HTMLUListElement>` still type-checks.
|
|
748
|
+
- AIDigestCard: a consumer `aria-label` wins over `ariaLabel` and the title.
|
|
749
|
+
- AIExpandableSection and the collapsible AISourcesSection pass these props to their Disclosure root, where `id` is the header button's id.
|
|
750
|
+
- Props that were dropped before now take effect: `className`, `data-*`, `aria-*`, `id` and event handlers passed to these components were not typed and never reached the DOM. Check any such props you passed before.
|
|
751
|
+
|
|
752
|
+
New props are `@since 2.13.0`.
|
|
753
|
+
|
|
754
|
+
**WI-55.** No release note. WI-55's literal font sizes in AISources, AISourcesPanel, AIContainer and InsightEngineInput were taken to their AI typography roles in M6 (WI-36, M6 lane 4, per the E-14 map summary; for example InsightEngineInput's attachment meta 0.68rem -> `aiTypography.meta`, 11px, as the source comment records), so this lane snaps no size. It adds a test that pins each remaining size site to its role (`aiTypography.caption` 12px, `aiTypography.meta` 11px) by computed style. No icon size is touched (docs/remediation/icon-size-map.md).
|
|
755
|
+
|
|
756
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
757
|
+
- **Behavior correction (a, b, d)**: AIResponseRenderer: schema types and guards, block renderers, theme-spaced block gap, inline response errors and per-block error isolation. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
758
|
+
|
|
759
|
+
**G-07.** The AI response schema gains three interactive blocks and four guards, and the whole schema is exported from the package root.
|
|
760
|
+
|
|
761
|
+
- **New blocks.** `QueryBlock` (`type: 'query'`, the fields of `AIQuery`), `ClarificationBlock` (`type: 'clarification'`, `clarification`, `required?`, `resolution?` for an answer already given) and `PlanReviewBlock` (`type: 'plan-review'`, `plan`, `presentation?: 'inline' | 'dialog'`). Each requires an `id` and takes an optional `status?: AIBlockStatus` (`'streaming' | 'complete'`).
|
|
762
|
+
- **Guards.** `isQueryBlock`, `isClarificationBlock`, `isPlanReviewBlock` and `isInteractiveBlock` join `isTextBlock`, `isTableBlock`, `isChartBlock`, `isImageBlock`, `isFormBlock` and `isActionsBlock`; all ten are root exports.
|
|
763
|
+
- **Root exports** (`@since 2.13.0`): `BaseBlock`, `AIBlockStatus`, `QueryBlock`, `ClarificationBlock`, `PlanReviewBlock`, `ResponseImageBlock`, `ResponseFormBlock`, `ResponseActionsBlock`, `AIResponseBlock`, `AIResponseBlockType`, `TextOnlyResponse`, `PartialAIResponse` and the ten guards.
|
|
764
|
+
- **Open unions.** `AIResponseBlock` and `AIResponseBlockType` widen. If you switch exhaustively over `AIResponseBlock`, `AIMessageProps['response']` or `AIResponseRendererProps['response']` blocks, add a `default:` branch: minor releases add block types. Table, chart, image, form and actions blocks stay consumer-rendered.
|
|
765
|
+
|
|
766
|
+
```ts
|
|
767
|
+
switch (block.type) {
|
|
768
|
+
case 'text':
|
|
769
|
+
return block.content;
|
|
770
|
+
default:
|
|
771
|
+
return null; // new block types arrive in minor releases
|
|
772
|
+
}
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
**G-08.** AIResponseRenderer resolves blocks through block renderers.
|
|
776
|
+
|
|
777
|
+
- **Resolution order.** `renderBlock` (or the deprecated `onRenderBlock`) → the new `blockRenderers` prop (one component per block type; `null` skips the type) → renderers a host provides (ChatThread and the other organisms, from M8) → the built-in text renderer → skipped, with one development warning per type.
|
|
778
|
+
- **New props:** `blockRenderers`, `onBlockEvent(event: AIBlockEvent)` (a returned promise drives the block's pending state), `readOnly`, `onActionError(error, { source: 'block', blockId })` and `idPrefix`.
|
|
779
|
+
- **New exports** (`@since 2.13.0`): `AIBlockRenderer` and `AIBlockRendererProps` (renderers are function components; hooks are allowed), `AIBlockEvent` and `getBlockDomId(prefix, blockId)`.
|
|
780
|
+
- **Sources.** The renderer provides the response's `sources` to its blocks, so `useBlockSources(block.id)` works inside a table or chart renderer.
|
|
781
|
+
- **Development warnings** (once each, from effects): a block type with no renderer (no longer on every render), an interactive block without an id, repeated block ids, and a renderer that is a new component on every render. Define renderers at module scope or memoize them.
|
|
782
|
+
|
|
783
|
+
**G-08 (block ids).** A block's id from the model is no longer written as a DOM id.
|
|
784
|
+
|
|
785
|
+
AIResponseRenderer gave each text block `id={block.id}`. Model-chosen ids repeat across messages ('intro' in every answer), which breaks ARIA references (b), and an id such as 'config' names a `window.config` global (d). Each block now keeps its id in `data-block-id`, and its DOM id is `getBlockDomId(idPrefix ?? the renderer's id, block.id)`: prefixed, with characters outside `A-Z a-z 0-9 _ -` replaced. Repeated ids inside one response get their own React keys and DOM ids.
|
|
786
|
+
|
|
787
|
+
**G-10.** AIResponseRenderer's default gap between blocks follows theme spacing.
|
|
788
|
+
|
|
789
|
+
`blockGap` defaulted to `aiSpacing.sm` (1, a theme unit) but rendered it as `1px`, contradicting its JSDoc and the token's documented 8px. Omitted, the gap is now `theme.spacing(aiSpacing.sm)`: 8px in the default theme, following the spacing unit and the presets. An explicit number keeps meaning pixels. A value below 2 or with a fraction logs one development warning.
|
|
790
|
+
|
|
791
|
+
**G-34.** AIResponseRenderer renders `AIResponse.error`.
|
|
792
|
+
|
|
793
|
+
`error` was documented as the response's error state, but nothing rendered it, so a failed response looked complete. The renderer now shows an inline error after the blocks (also for a response with no blocks): titled 'This response failed' (`ai.responseRenderer.errorTitle`), with `error.message` as plain text. The error carries no live role; standalone it is announced once, assertively; inside a ChatThread the thread's log reads it. Retry stays in AIMessage's footer.
|
|
794
|
+
|
|
795
|
+
**G-39+.** One block that fails to render no longer takes the response down.
|
|
796
|
+
|
|
797
|
+
Each block of AIResponseRenderer renders inside its own error boundary. A block whose renderer throws shows 'This part of the response is unavailable' (`ai.responseRenderer.blockUnavailable`) in its place, read-only and not announced; the other blocks and messages keep rendering. The new `onBlockError(error, { blockId, type })` reports the failure, and a development warning names the block type once. A text block whose `content` is not a string shows the same state instead of throwing.
|
|
798
|
+
|
|
799
|
+
**F-18.** Internal block renderers for query and clarification blocks (`queryBlockRenderer`, `clarificationBlockRenderer`), on no package entry. G-12's `hitlBlockRenderers` (M8) puts them on screen; that release note covers the visible change.
|
|
800
|
+
|
|
801
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
802
|
+
- **Behavior correction (b, c, e)**: AIMessage's actions work: Copy writes to the clipboard, the actions are a group with 3:1 icons, the thumbs show their pressed state, a new Regenerate action, and an AIActionsGroup row wraps in a narrow panel. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
803
|
+
|
|
804
|
+
**G-04a.** - Internal: AIMessage's footer actions (copy, good, poor, share, download) render through one internal renderer instead of four copy-pasted blocks. The markup, names, tooltips and callbacks are unchanged (the DOM of every show* combination is byte-equal). No public API change.
|
|
805
|
+
|
|
806
|
+
**G-04b.** AIMessage's Copy now writes to the clipboard.
|
|
807
|
+
|
|
808
|
+
- Without `onCopy`, Copy writes the response to the clipboard and announces "Copied" (or "Couldn't copy") once, through the nearest `LiveRegionProvider` or the document-level region. Before, it did nothing without `onCopy`.
|
|
809
|
+
- With `onCopy`, Copy calls it and writes nothing, as before; `onCopy` now receives the copied text: `onCopy?: (text: string) => void`. Handlers typed `() => void` still fit.
|
|
810
|
+
- New `copyBehavior?: 'auto' | 'clipboard' | 'callback' | 'both'` (default `'auto'`, the two rules above). `'clipboard'` always writes and does not call `onCopy`; `'callback'` only calls `onCopy(text)`; `'both'` writes, then calls `onCopy(text)` once the write settles.
|
|
811
|
+
- New `copyText?: string` sets the text. Without it, Copy uses the response as plain text (markdown removed, citation markers numbered as their chips, query blocks fenced), or for the `children` API the text of the `[data-copy-root]` element inside the content (else the whole content), without `aria-hidden` and visually hidden parts.
|
|
812
|
+
- While `streaming`, Copy is `aria-disabled` (it keeps focus), described by "Copy is unavailable while the response is streaming", and does nothing when pressed.
|
|
813
|
+
|
|
814
|
+
New props are `@since 2.13.0`.
|
|
815
|
+
|
|
816
|
+
**G-04c.** AIMessage's footer actions are a named group, not a toolbar. The row was `role="toolbar"` without the toolbar's arrow-key model; it is now `role="group"` with the same name, "AI response actions", and every action keeps its own tab stop (Tab and Shift+Tab move between them; arrow keys do nothing).
|
|
817
|
+
|
|
818
|
+
Consumer tests that query the toolbar change their query:
|
|
819
|
+
|
|
820
|
+
| Before | After |
|
|
821
|
+
|---|---|
|
|
822
|
+
| `getByRole('toolbar', { name: 'AI response actions' })` | `getByRole('group', { name: 'AI response actions' })` |
|
|
823
|
+
| `queryByRole('toolbar')` (to check the actions are hidden) | `queryByRole('group', { name: 'AI response actions' })` |
|
|
824
|
+
|
|
825
|
+
**G-04d.** AIMessage's idle action icons meet 3:1. The copy, feedback, share and download glyphs were `text.disabled`, below the 3:1 a control needs against its surface (WCAG 1.4.11). They are now `text.secondary`, 3:1 or more on the paper and default surfaces in light and dark for every preset; the hover colours are unchanged. Keyboard focus is an outline (Highlight under forced colours), and each button is a 24 by 24 px target or larger.
|
|
826
|
+
|
|
827
|
+
**G-04e.** AIMessage's feedback thumbs show which rating is pressed.
|
|
828
|
+
|
|
829
|
+
- The thumbs are `AIFeedbackControl` in simple mode: `aria-pressed`, the filled glyph and the pressed fill (an outline in Highlight under forced colours), inside the actions group as a "Rate this response" group. Their names stay "Mark as good response" and "Mark as poor response"; their tooltips now show the same text.
|
|
830
|
+
- New props: `feedback?: AIFeedbackRating | null` (controlled rating), `defaultFeedback?: AIFeedbackRating | null` (initial rating, default `null`) and `onFeedbackChange?: (value: AIFeedbackRating | null) => void`. `onFeedback(positive: boolean)` is unchanged.
|
|
831
|
+
- Pressing an unpressed thumb sets the rating and calls `onFeedback(positive)`, then `onFeedbackChange(value)`. Pressing the pressed thumb clears the rating and calls only `onFeedbackChange(null)`. Pressing the other thumb switches and calls `onFeedback` with the new value.
|
|
832
|
+
- New `slotProps.feedbackControl` (props for the thumbs' group root).
|
|
833
|
+
- The control reports its own `feedback` interaction (`rate` or `clear`) with `responseId` set to the response's id, through `AIConfigProvider`'s `onInteraction`.
|
|
834
|
+
|
|
835
|
+
New props are `@since 2.13.0`.
|
|
836
|
+
|
|
837
|
+
**G-04f.** AIMessage gains a Regenerate action.
|
|
838
|
+
|
|
839
|
+
- New `onRegenerate?: () => void | Promise<void>`. When passed, the footer shows a Regenerate action (tooltip "Regenerate", name "Regenerate response"). While a returned promise is pending, Regenerate is `aria-disabled` and `aria-busy`, keeps focus and does not run again. If it rejects, "Couldn't regenerate the response" appears below the footer with a "Try again" button; it is announced once and has no alert role, so it is safe inside a chat log. While the response streams, Regenerate is `aria-disabled` with the reason as its description.
|
|
840
|
+
- New `onActionError?: (error: unknown, context: AIActionErrorContext) => void`, called when Regenerate fails (`{ source: 'action', actionId: 'regenerate' }`) or when Copy cannot write to the clipboard (`actionId: 'copy'`). Without it, the error goes to `globalThis.reportError`.
|
|
841
|
+
- New strings `ai.message.regenerate`, `ai.message.regenerateResponse` and `ai.message.regenerateFailed` (defaults above).
|
|
842
|
+
|
|
843
|
+
New props and keys are `@since 2.13.0`.
|
|
844
|
+
|
|
845
|
+
**AIActionsGroup row wrap.** AIActionsGroup's `direction="row"` wraps (WCAG 1.4.10 Reflow).
|
|
846
|
+
|
|
847
|
+
- A row no longer overflows a narrow host such as AppLayout's docked AI panel at 320 to 360px: actions that do not fit move to the next line, spaced by the same `spacing` (now a flex gap, so wrapped lines carry no stray margin).
|
|
848
|
+
- Each action in a row sizes to its label and grows to share its line; an action wider than the row shrinks and wraps its own text (`minWidth: 0`, `maxWidth: 100%`). A consumer `sx` on an action still wins.
|
|
849
|
+
- `direction="column"` (the default) is unchanged.
|
|
850
|
+
|
|
851
|
+
**AIMessage footer wrap.** AIMessage's footer wraps in a narrow host (WCAG 1.4.10 Reflow).
|
|
852
|
+
|
|
853
|
+
- With a timestamp and every action wired, a 320px host (AppLayout's docked AI panel at its narrowest) squeezed the footer: the timestamp broke over two lines and the thumbs stacked into a column. The footer now wraps: the actions move under the timestamp, aligned to the end, and the actions group wraps as a whole if it is wider than its line, so the thumbs stay side by side.
|
|
854
|
+
- The actions are spaced by a flex gap of the same 4px, so a wrapped line carries no stray margin. Wide hosts look the same.
|
|
855
|
+
|
|
856
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
857
|
+
- **Behavior correction (b)**: The AI message, thread and renderer components follow the root props contract; chat-history options are reachable and named; the composer explains a blocked send and shows pending and Stop. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
858
|
+
|
|
859
|
+
**G-03.** AIMessage and UserMessage follow the root props contract (ADR-04).
|
|
860
|
+
|
|
861
|
+
- Both take `className`, `sx` (object, array or function form, applied after the component's own styles) and the HTML attributes of their root `<div>` (`data-*`, `aria-*`, `id`, event handlers), spread on the root. The ref keeps its target, the root.
|
|
862
|
+
- `role`, `aria-live`, `aria-busy`, `aria-relevant`, `aria-atomic` and `aria-modal` stay the component's: a value passed for one of them is ignored, with one development warning. `aria-describedby` and `aria-labelledby` are joined with the component's own ids; the root's `id` defaults to a generated one.
|
|
863
|
+
- New `slotProps`: AIMessage `{ avatar, content, footer }`, UserMessage `{ bubble }` (`className`, `sx`, `data-*`, `aria-*` and handlers per slot). AIMessage's content region keeps its role and live attributes.
|
|
864
|
+
- Props that were dropped before now take effect: an array or function `sx` on AIMessage (it was object-spread, so array entries were lost), and `className`, `data-*`, `aria-*` and event handlers passed to either component (they were not typed and never reached the DOM). Check any such props you passed before.
|
|
865
|
+
- AIMessage's `onCopy` stays the Copy action's callback; the root does not take the DOM `onCopy` clipboard event.
|
|
866
|
+
|
|
867
|
+
New props are `@since 2.13.0`.
|
|
868
|
+
|
|
869
|
+
**G-03 (thread).** ChatThread and InsightEngine follow the root props contract (ADR-04).
|
|
870
|
+
|
|
871
|
+
- **ChatThread** takes `className`, `sx` (object, array or function form, applied after its own styles) and the HTML attributes of its log `<div>` (`id`, `data-*`, `aria-*`, event handlers), spread on the log. The ref keeps its target, the log.
|
|
872
|
+
- `role`, `aria-live`, `aria-relevant` and `aria-atomic` stay ChatThread's (the log is `role="log"`, `aria-live="polite"`, `aria-relevant="additions text"`, `aria-atomic="false"`): a value passed for one of them is ignored, with one development warning naming it. `aria-busy` and `aria-modal` are ignored the same way.
|
|
873
|
+
- A consumer `aria-label` replaces the default name 'Chat messages'.
|
|
874
|
+
- New `slotProps.emptyState` styles the default empty state's container (`className`, `sx`, `data-*`, `aria-*`, handlers). A custom `emptyState` renders as given, without that container.
|
|
875
|
+
- **InsightEngine** takes `className` and the HTML attributes of the Drawer's root `<div>`, spread on it; `sx` stays on the Drawer root.
|
|
876
|
+
- New `slotProps`: `paper` (the drawer's paper), `thread` (ChatThread's log) and `composer` (the container around the input at the bottom of the panel).
|
|
877
|
+
- The paper's width and width transition now live in the paper's own slot (MUI 9 `slotProps.paper`), not in a `& .MuiDrawer-paper` rule on the root. A `& .MuiDrawer-paper` rule in your root `sx` still wins over the paper's own width, so existing overrides keep working; `slotProps.paper.sx` is the new place for paper styles.
|
|
878
|
+
- The width transition reads the motion tokens (`motion.duration.normal`, 200ms, and `motion.easing.default`): the same duration, with the token's easing curve instead of the CSS `ease-in-out` keyword.
|
|
879
|
+
- Props that were dropped before now take effect: `className`, `data-*`, `aria-*` and event handlers passed to either component (they were not typed and never reached the DOM), and an array or function `sx` on InsightEngine (it was object-spread, so array entries were lost). Check any such props you passed before.
|
|
880
|
+
- Nothing renders differently when the new props are unused; no root gets a generated `id`.
|
|
881
|
+
|
|
882
|
+
New props are `@since 2.13.0`.
|
|
883
|
+
|
|
884
|
+
**G-03 (renderer).** **AIResponseRenderer** takes `className`, `sx` (object, function or array, merged after its own styles) and the HTML attributes of its root `<div>` (`id`, `data-*`, `aria-*`, event handlers), spread on the root; it owns no attribute, and the ref keeps its target. The root's `id` defaults to a generated one, which prefixes the block DOM ids.
|
|
885
|
+
|
|
886
|
+
**WI-23b.** ChatHistoryList's options button can be found and operated by keyboard and touch (WCAG 2.4.7, 1.3.1, 4.1.2; ADR-23 test (b)).
|
|
887
|
+
|
|
888
|
+
- The options button was invisible (opacity 0) until the row was hovered, so it was invisible while focused and never shown on touch devices. It is now visible on row hover, whenever anything in the row has focus (the conversation button or the options button), while its menu is open, and always on devices that cannot hover.
|
|
889
|
+
- Each row's button is named after its conversation, 'Options for {title}' (`ai.chatHistoryList.optionsFor`), with a tooltip of the same text. It was 'Chat options' on every row.
|
|
890
|
+
- The button is a menu trigger (`aria-haspopup="menu"`, `aria-expanded`, and `aria-controls` while open); the menu is named by its button. Enter or Space opens it, Escape closes it and focus returns to the button.
|
|
891
|
+
- The Delete item shows its delete icon (it asked for 'trash', which is not an icon name, and rendered no icon). Pin and Unpin show distinct pin and unpin icons (both showed the bookmark).
|
|
892
|
+
|
|
893
|
+
Visual review: the options button now shows on keyboard focus; the menu's Pin, Unpin and Delete icons change.
|
|
894
|
+
|
|
895
|
+
Consumer tests that query `getByRole('button', { name: 'Chat options' })` need the per-row name, for example `getByRole('button', { name: 'Options for Q4 revenue' })`.
|
|
896
|
+
|
|
897
|
+
|---|---|---|---|---|
|
|
898
|
+
|
|
899
|
+
**WI-51.** InsightEngineInput takes `disabledReason?: string` for a composer that must not send yet, for example while a required clarification waits for an answer (ADR-05 (6)).
|
|
900
|
+
|
|
901
|
+
- Send stays focusable and in the tab order, `aria-disabled` rather than natively disabled, and looks like the disabled Send. Enter and Send do not submit.
|
|
902
|
+
- The field stays editable, so the user can keep drafting (DN-13).
|
|
903
|
+
- The reason shows as helper text below the composer and describes both the field and Send (`aria-describedby`). When the reason appears or changes after the composer mounted, it is announced once, politely; focus never moves.
|
|
904
|
+
- `disabled` is unchanged and is never set by `disabledReason`.
|
|
905
|
+
|
|
906
|
+
Visual only when set: the helper text below the composer and the disabled-looking Send.
|
|
907
|
+
|
|
908
|
+
New prop `@since 2.13.0`.
|
|
909
|
+
|
|
910
|
+
**WI-61+.** InsightEngineInput takes `pending?`, `generating?` and `onStop?()`, so a composer can show that a message is on its way and stop an answer being generated.
|
|
911
|
+
|
|
912
|
+
- `pending`: Send stays in place, `aria-disabled` and `aria-busy` (never natively disabled) with a spinner, so keyboard focus stays on it; Enter and Send do not submit; the field stays editable.
|
|
913
|
+
- `generating`: Send becomes a Stop button in the same place, named and tooltipped 'Stop generating', so focus stays on it; Enter does not submit.
|
|
914
|
+
- Stop calls `onStop` once per generation (repeat presses before `generating` turns off are ignored) and emits the content-free `answer.stopped` interaction through `AIConfigProvider`'s `onInteraction`.
|
|
915
|
+
- When Send leaves while it has focus (after a send with nothing pending, or when a generation ends with no draft), focus moves to the field instead of being lost to the page. Before, focus fell to the document body (WCAG 2.4.3).
|
|
916
|
+
|
|
917
|
+
Nothing passes these props through yet: InsightEngine and InsightEnginePanel forward them in a later release step (G-15, H-16).
|
|
918
|
+
|
|
919
|
+
New props `@since 2.13.0`.
|
|
920
|
+
|
|
921
|
+
| Deprecated | Replacement |
|
|
922
|
+
|---|---|
|
|
923
|
+
| `AIChatInput` `isGenerating` and `onStopGeneration` | `InsightEngineInput` `generating` and `onStop` (Stop also emits `answer.stopped`) |
|
|
924
|
+
|
|
925
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
926
|
+
- **Behavior correction (b)**: ResizablePanel and AppLayout: right-to-left geometry and keys, labels, no FAB over a fullscreen panel, a custom AI panel and keep-mounted. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
927
|
+
|
|
928
|
+
**H-10.** AppLayout can host any AI surface, and the AI panel can stay mounted while closed.
|
|
929
|
+
|
|
930
|
+
- **`renderAiPanel(api)` (new, AppLayout).** Renders your own AI surface in the AI panel instead of InsightEnginePanel, in the same resizable panel, trigger and geometry (width, content margin, FAB, fullscreen). `api` is `AppLayoutAiPanelApi`: `onClose` (closing leaves fullscreen), `isFullscreen` and `onFullscreenToggle`. When both `renderAiPanel` and `panelProps` are passed, `renderAiPanel` wins and a development warning fires once.
|
|
931
|
+
- **`keepAiPanelMounted` (new, AppLayout, default false).** Keeps the AI panel mounted while it is closed, so a draft, the conversation and the scroll position survive closing and reopening. The closed panel is hidden and inert: invisible, out of the Tab sequence and out of the accessibility tree.
|
|
932
|
+
- **`keepMounted` (new, ResizablePanel, default false).** The mechanism behind it: a closed kept-mounted panel renders with `display: none` (after your `sx`), `hidden` and `inert`.
|
|
933
|
+
- **Fix.** Closing a fullscreen AI panel from the sidebar's AI nav item now leaves fullscreen too, as the panel's Close does; before, the page content stayed hidden.
|
|
934
|
+
|
|
935
|
+
Default rendering is unchanged for AppLayout, AIFabLayout and ResizablePanel users. The CSS package has no change of its own.
|
|
936
|
+
|
|
937
|
+
**H-31.** ResizablePanel follows right-to-left layouts, guards its motion in CSS and takes a `labels` object.
|
|
938
|
+
|
|
939
|
+
- **Right to left.** `side` is a logical side: `'right'` is the inline end and `'left'` the inline start. Under `theme.direction: 'rtl'` the arrow keys and the drag follow the mirrored layout: the arrow that points toward the content widens the panel, and dragging the handle toward the content widens it (WCAG 2.1.1, 2.5.7). The keys come from the same keyboard model as SplitPane (10px, 50px with Shift, Home and End to `minWidth` and `maxWidth`). Left-to-right behavior is unchanged.
|
|
940
|
+
- **Logical edges.** The border and the resize handle use `borderInlineStart/End` and `insetInlineStart/End`, so an app without stylis-plugin-rtl gets them on the content side; left to right and with the plugin nothing moves.
|
|
941
|
+
- **Reduced motion.** The width transition has a CSS guard (`REDUCED_MOTION_QUERY`, duration 0) instead of a JS read, so the server markup is already right and nothing re-renders when the preference changes.
|
|
942
|
+
- **sx.** `sx` and `contentSx` are merged as arrays: array and function values now apply (they were dropped before).
|
|
943
|
+
- **labels (new).** `labels?: Partial<ResizablePanelLabels>` with `resizeHandle` (default 'Resize panel') and `close` (default 'Close panel', the close button's tooltip and name). `resizeHandleLabel` stays accepted; `labels.resizeHandle` wins when both are passed. `ResizablePanelLabels` is exported.
|
|
944
|
+
- The close button's glyph renders through `Icon` (the same feather glyph at 20px), hidden from assistive technology.
|
|
945
|
+
|
|
946
|
+
**H-32.** AppLayout no longer shows the AI FAB over a fullscreen AI panel, docks the panel with logical properties and accepts array and function `contentSx`.
|
|
947
|
+
|
|
948
|
+
- **No FAB in fullscreen (WCAG 2.4.11).** The FAB floated over the fullscreen panel's composer. It is not rendered while the panel is fullscreen; Exit fullscreen and Close stay in the panel header, and the FAB returns when the panel docks or closes, at the tracked width plus the gutter.
|
|
949
|
+
- **Logical geometry.** The panel docks at `insetInlineEnd` and the content keeps clear of it with `marginInlineEnd` (was `right` and `marginRight`), so right-to-left layouts are right with or without stylis-plugin-rtl. Left to right and with the plugin nothing moves. A test or a style that read the content's `marginRight` in a browser still reads the same value left to right.
|
|
950
|
+
- **contentSx.** Merged as an array: array and function values apply (they were dropped). The header offset in top-nav mode still wins over a `contentSx` padding-top, as before.
|
|
951
|
+
- Under reduced motion the content margin's transition has the same CSS guard as the panel (duration 0).
|
|
952
|
+
|
|
953
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
954
|
+
- **Behavior correction (a, b, e)**: InsightEnginePanel: one send per message, no demo content in an integrated panel, a heading with an id, no dead composer controls, no invented initials. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
955
|
+
|
|
956
|
+
**H-02.** InsightEnginePanel calls `onSendMessage` once per send, with its options.
|
|
957
|
+
|
|
958
|
+
Each submit (Enter or the send button) called `onSendMessage` twice: once with `{ proSearch }` and again with `{}`, so integrated backends received every message twice and the second call dropped `proSearch`. The panel now calls it exactly once, with `{ proSearch: true }` while deep thinking is on.
|
|
959
|
+
|
|
960
|
+
**H-04.** InsightEnginePanel no longer adds invented answers or invented thread history to an integrated chat, and labels demo mode.
|
|
961
|
+
|
|
962
|
+
`demoMode` defaulted to `true`, so a panel wired with `onSendMessage` or controlled `messages` still answered every message after 2 s with one of three canned answers (invented HCP and KOL claims), pushed them into a controlled store, and its history sidebar listed nine invented threads. Now `demoMode` left undefined is derived: off when `onSendMessage` or `messages` is passed (a one-time development warning says so), on when nothing is wired. While demo answers can appear, a visible 'Demo responses' label (`ai.insightEnginePanel.demoLabel`) sits inside the title heading, so it is also part of the heading's accessible name. The history toggles render only when `threadGroups` or `onThreadSelect` is passed, or in demo mode; the demo history appears only in demo mode.
|
|
963
|
+
|
|
964
|
+
Leaving `demoMode` undefined on an unwired panel is deprecated (a one-time development warning): the default becomes `false` in v3.0.
|
|
965
|
+
|
|
966
|
+
**H-05.** InsightEnginePanel's controlled mode keeps every message, and the panel stops scrolling the host page.
|
|
967
|
+
|
|
968
|
+
- **Messages.** The panel now uses MUI `useControlled` with a latest-value ref, so updates issued before the parent re-renders (the demo answer, two quick sends, a parent that applies its update later) build on the latest messages. In controlled mode the demo answer used to replace the conversation with a stale copy that dropped the user's message. `onMessagesChange` now reports every change in both modes (it was silent in uncontrolled mode). Passing `messages` without `onMessagesChange` logs a one-time development warning. Switching between controlled and uncontrolled `messages` or `showHistory` logs MUI's controlled-state warning.
|
|
969
|
+
- **History.** `showHistory` goes through `useControlled` too; an uncontrolled panel with `onHistoryToggle` now toggles and notifies (`onHistoryToggle` replaced the toggle before).
|
|
970
|
+
- **Typing.** The indicator follows `isTyping` directly (it was mirrored into state, so a demo answer could hide it while the parent still said `isTyping`).
|
|
971
|
+
- **Timers.** The 2 s demo timer is cleared on unmount and on New thread: no update or callback after either.
|
|
972
|
+
- **Scrolling.** New messages scroll the messages container only, instantly under reduced motion; `scrollIntoView({ behavior: 'smooth' })` also scrolled the host page.
|
|
973
|
+
|
|
974
|
+
**H-06.** InsightEnginePanel's `themeMode` prop is deprecated: the mode comes from the theme.
|
|
975
|
+
|
|
976
|
+
`themeMode` was documented ('Theme mode - defaults to MUI theme') but never read. It keeps having no effect, carries `@deprecated`, and passing it logs a one-time development warning (`InsightEnginePanel.themeMode`, from an effect). It is removed in v3.0.
|
|
977
|
+
|
|
978
|
+
**H-07.** InsightEnginePanel's composer shows attachment, web search and voice controls only when you wire them.
|
|
979
|
+
|
|
980
|
+
The composer offered three controls that did nothing: 'Add files or photos' logged 'Attachment clicked' to the console (in production too), 'Web search' was a toggle stuck on, and 'Dictate' flipped a local flag that showed 'Listening... Speak now' while nothing recorded. The panel also mounted a voice overlay that never opened. Now each control renders only from its new panel prop, and the panel no longer mounts the overlay or logs:
|
|
981
|
+
|
|
982
|
+
- `onAttachmentClick`: the 'Add files or photos' item.
|
|
983
|
+
- `webSearchEnabled` with `onWebSearchToggle`: the 'Web search' toggle.
|
|
984
|
+
- `onVoiceClick` with a controlled `listening`: the 'Dictate' button and, while `listening`, the listening indicator (its Stop calls `onVoiceClick`).
|
|
985
|
+
|
|
986
|
+
Send, Deep thinking and the model and source menus are unchanged.
|
|
987
|
+
|
|
988
|
+
**H-34.** InsightEnginePanel's title is a level-2 heading with an id, and the new `headingLevel` and `titleId` props set them.
|
|
989
|
+
|
|
990
|
+
The title kept its look (Typography `subtitle2`) but rendered as an `h6`, so the panel's only heading sat at the bottom of the page outline (WCAG 1.3.1, axe heading-order), and it had no id for a region or dialog to name itself by. It is now an `h2` by default. `headingLevel` (2 to 6) fits it to the page outline and `titleId` sets its id (from `useId` when absent). The empty-state heading ('What do you want to know?') sits one level below the title. `headingLevel={6}` restores the previous outline (both headings `h6`).
|
|
991
|
+
|
|
992
|
+
Consumer tests that queried the title as level 6 use `getByRole('heading', { level: 2 })`, or pass `headingLevel={6}`.
|
|
993
|
+
|
|
994
|
+
**H-35.** InsightEnginePanel honours its documented `placeholder` and `subtitle` props.
|
|
995
|
+
|
|
996
|
+
`placeholder` was ignored (the composer always said 'Ask anything. Type @ for mentions and / for shortcuts.') and its documented default 'Ask anything...' never appeared; `subtitle` was ignored. Now `placeholder` reaches the composer (the default stays the composer text, `ai.insightEnginePanel.composerPlaceholder`), and `subtitle` renders as a caption under the title, outside the heading, so it is not part of the heading's name. Without either prop nothing changes.
|
|
997
|
+
|
|
998
|
+
**H-36.** InsightEnginePanel no longer shows invented 'RD' initials on the user's messages.
|
|
999
|
+
|
|
1000
|
+
`userInitials` defaulted to 'RD', so every user avatar in every AppLayout chat showed someone else's initials. It now defaults to undefined and the avatar shows the generic user avatar ('U') until you pass the signed-in user's initials. H-08 (M8) changes the user avatar again; both changes go in the release notes.
|
|
1001
|
+
|
|
1002
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1003
|
+
- **Behavior correction (b, c)**: AIChainOfThought: disclosure phase headers, step lists, source chips, a key-value stats list, a named reasoning group and optional phase headings. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
1004
|
+
|
|
1005
|
+
**D-36.** AIChainOfThought's phase headers and "Thought Process" bar are native buttons named by their visible text.
|
|
1006
|
+
|
|
1007
|
+
They were `role="button"` divs whose `aria-label` ("<title> phase — <status>", "Toggle thought process") replaced the visible text, so speech-input users could not say what they saw (WCAG 2.5.3), and they controlled no panel (WCAG 4.1.2). Each is now the shared Disclosure trigger (`<button aria-expanded aria-controls>`):
|
|
1008
|
+
|
|
1009
|
+
- A phase header's name is its title, subtitle and duration, then its status as text ("Not started", "In progress", "Completed", "Failed"). The status glyph is the shared status catalog icon (an active phase shows the in-progress icon instead of a spinner), with the phase icon beside it.
|
|
1010
|
+
- The bar's name is "Thought Process" and the elapsed time; the phase dots are decorative.
|
|
1011
|
+
- The chevron, focus ring and collapse timing are the Disclosure's: the chevron points down when closed and up when open, the collapse uses the motion tokens and is instant under reduced motion or with `animated={false}`.
|
|
1012
|
+
- With `showToggle={false}` there is no bar; the phases still follow `defaultCollapsed`.
|
|
1013
|
+
|
|
1014
|
+
**D-37.** AIChainOfThought's plan steps and execution tasks are lists, and each task says its status in text.
|
|
1015
|
+
|
|
1016
|
+
- **(b) Structure, WCAG 1.3.1 and 1.4.1.** Plan steps are an ordered list (the visible numbers are hidden from screen readers; the list counts). Execution tasks are a list whose rows start with their status as text: "Not started", "In progress", "Retrying", "Completed" or "Failed". The status glyph is the shared status catalog icon; a running task shows the in-progress icon instead of a spinner.
|
|
1017
|
+
- **(c) Contrast and emphasis.** A running task is no longer tinted with the AI colour: it is weight 600 with an inline-start rule in the interactive colour. A failed task's row is `text.primary` on the error background. A pending task's text darkens from `text.disabled` to `text.secondary`. The error banner reads the theme's error and AI roles.
|
|
1018
|
+
- **Motion.** The plan steps' entrance follows reduced motion and `animated`. The error banner no longer fades in: its fade only ran when the task's running detail had been shown before, so it was removed. Insight references keep their entrance (and now define it themselves, so an insight-only chain animates them too).
|
|
1019
|
+
|
|
1020
|
+
**D-38.** AIChainOfThought's "Sources consulted" chips show the source type and use the theme's colours.
|
|
1021
|
+
|
|
1022
|
+
Each source is an outlined metadata chip reading "Type: label" (for example "Database: metrics-store"): `ChainSource.type`, documented as a badge but never shown, is now the chip's label. The chip uses MUI's outlined chip colours in both modes, so the light border moves from the divider colour to MUI's outlined border and the dark chip no longer uses a hard-coded white tint; the icon takes the theme's AI primary colour. A source without a type shows its label alone, as before.
|
|
1023
|
+
|
|
1024
|
+
**D-50.** AIChainOfThought's output stats are a description list.
|
|
1025
|
+
|
|
1026
|
+
The stats under an output summary ("Total time 3.0s", "Data processed 12,420 rows") are now a `dl`: each label, with its decorative icon, is a `dt` and each value a `dd`, so screen readers announce the pair together (WCAG 1.3.1). The row looks the same: each pair stays on one line and the values keep the code font at weight 600.
|
|
1027
|
+
|
|
1028
|
+
**WI-44.** AIChainOfThought is a group named by its query, not a live region, and announces each transition once.
|
|
1029
|
+
|
|
1030
|
+
The root was a `role="region"` landmark named "AI reasoning chain" with `aria-live="polite"`, `aria-relevant="additions text"` and `aria-atomic="false"`, so screen readers read every streamed token and every chain added a landmark (WCAG 4.1.3 noise, duplicate landmarks). Now:
|
|
1031
|
+
|
|
1032
|
+
- The root is a `role="group"` named "Reasoning for {query}" (`ai.chainOfThought.reasoningFor`), or "AI reasoning chain" without a `query`. `aria-busy` stays while a thinking phase is active.
|
|
1033
|
+
- NEW `announcements?: 'transitions' | 'stream' | 'off'` (default `'transitions'`): one polite "Thinking" when the first thinking phase becomes active and one "Response ready" when the final phases appear (`ai.chainOfThought.thinking`, `ai.common.responseReady`, overridable through `AIConfigProvider`). A chain that mounts already thinking or complete does not announce that state. `'stream'` is the 2.12 behavior; `'off'` is silent.
|
|
1034
|
+
- Inside a chat thread the thread announces: the chain announces nothing and is never a live container.
|
|
1035
|
+
- `query` is now documented as the accessible name of the reasoning group.
|
|
1036
|
+
|
|
1037
|
+
Tests that queried the region break. Replacement query: `getByRole('group', { name: /Reasoning/ })`.
|
|
1038
|
+
|
|
1039
|
+
**WI-46.** NEW `AIChainOfThought` prop `phaseHeadingLevel?: HeadingLevel`.
|
|
1040
|
+
|
|
1041
|
+
Set it (for example `phaseHeadingLevel={3}`) to wrap each phase trigger in a heading of that level, `<h3><button aria-expanded aria-controls>…</button></h3>`, so screen-reader users can move between the chain's phases by heading. The trigger keeps its name: the phase title, then its status. Unset (the default), no heading element is rendered, so the chain adds nothing to your page's outline. The "Thought Process" bar and the final response phases never get a heading.
|
|
1042
|
+
|
|
1043
|
+
**WI-56.** Links that open a new tab now say so to assistive technology, without changing their names.
|
|
1044
|
+
|
|
1045
|
+
AITextBlock's markdown links, AISource's URL link and `target="_blank"` anchors in AIChainOfThought's HTML content are described "Opens in a new tab" (`ai.common.opensInNewTab`, overridable through `AIConfigProvider`). Each component renders one hidden hint and every new-tab link references it through `aria-describedby`, so link names, and tests that query links by name, are unchanged. The internal `renderSafeHtml` takes `newTabHintId` for the same purpose.
|
|
1046
|
+
|
|
1047
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1048
|
+
- **Behavior correction (b, e)**: Chat semantics and telemetry: only wired message actions show, one log with an article per message, renderBlock on AIMessage, an InsightEngine that reflows, interaction events, citations in threads and a disclosure history toggle. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
1049
|
+
|
|
1050
|
+
**G-05.** ChatThread and InsightEngine show only the message actions you wire.
|
|
1051
|
+
|
|
1052
|
+
Before, every assistant message in a ChatThread or InsightEngine showed Copy, the rating thumbs, Share and Download, and none of them except Copy did anything: the thread had no way to pass handlers. Now:
|
|
1053
|
+
|
|
1054
|
+
- New `messageActions?: { onCopy?(message, text); copyBehavior?; onFeedback?(message, value: AIFeedbackRating | null); onShare?(message); onDownload?(message); onRegenerate?(message): void | Promise<void> }` on ChatThread, forwarded unchanged by InsightEngine. Each callback receives the `ChatMessage` it acts on.
|
|
1055
|
+
- The thumbs, Share, Download and Regenerate show only when their handler is passed. **Visual change:** without `messageActions`, an assistant message shows Copy only. To keep an action, pass its handler.
|
|
1056
|
+
- Copy always shows. Without `onCopy` it writes the message to the clipboard and announces "Copied"; with `onCopy` it calls it only (as AIMessage's `copyBehavior: 'auto'`); `copyBehavior` passes through (`'both'` writes, then calls `onCopy`). A text message copies its `content`; a structured one its response as plain text.
|
|
1057
|
+
|
|
1058
|
+
New props and the new type export `ChatThreadMessageActions` (the shape of `messageActions`, exported from the root and the AI entry) are `@since 2.13.0`.
|
|
1059
|
+
|
|
1060
|
+
- Opt-back-in: pass the handler (`messageActions.onFeedback`, `onShare`, `onDownload`)
|
|
1061
|
+
|
|
1062
|
+
**G-06.** Chat threads read each message once, and say when a streamed answer is ready.
|
|
1063
|
+
|
|
1064
|
+
Before, a ChatThread was a log that re-read streamed text (`aria-relevant="additions text"`), and every assistant message was a `region` landmark named "AI response" with its own `aria-live`, so a thread with two answers had duplicate landmarks and nested live regions; nothing said when an answer finished. Now:
|
|
1065
|
+
|
|
1066
|
+
- **ChatThread** is one log (`role="log"`, `aria-live="polite"`, `aria-relevant="additions"`): a message is read when it is added. When a streamed message completes (`streaming` turns false), "Response ready" (`ai.common.responseReady`) is announced once, through two visually hidden regions rendered after the log as its siblings. History on the first render and messages added complete are not announced. New `headingLevel?: 2 | 3 | 4 | 5 | 6` adds a visually hidden heading before each message ("Your message", "Assistant response") so screen-reader users can jump between messages; none by default.
|
|
1067
|
+
- **AIMessage** is an article (`role="article"` on its root `div`; the ref is still an `HTMLDivElement`) named "Assistant response", or "Assistant response, <timestamp>" when a timestamp shows (`ai.message.assistantResponse`, `assistantResponseAt`); `aria-busy` is on the article while it streams. Its content is no longer a region. New `liveRegion?: 'auto' | 'off' | 'polite'` (default `'auto'`): `'auto'` makes the content `aria-live="polite"` only while streaming and standalone, never inside a ChatThread; `'polite'` also inside a thread; `'off'` never. When the message is not streaming the content has no `aria-live` attribute.
|
|
1068
|
+
- **UserMessage** is an article named "Your message" (`ai.userMessage.label`).
|
|
1069
|
+
- A consumer `aria-label` on AIMessage or UserMessage replaces the default name.
|
|
1070
|
+
|
|
1071
|
+
New props are `@since 2.13.0`.
|
|
1072
|
+
|
|
1073
|
+
- AT smoke: NVDA/Firefox and VoiceOver/Safari on a streaming markdown response is the owner's manual run at rc.0 (fallback G-OD13: drop `relevant: 'additions'` and announce only the completion)
|
|
1074
|
+
|
|
1075
|
+
**G-09.** AIMessage takes `renderBlock`; its `onRenderBlock` is deprecated.
|
|
1076
|
+
|
|
1077
|
+
- New `renderBlock?: (block) => ReactNode | null | undefined` on AIMessage, passed to AIResponseRenderer: a node renders the block, `null` skips it, `undefined` falls through to the block renderers and the built-in text renderer. `@since 2.13.0`.
|
|
1078
|
+
- `onRenderBlock` keeps working as an alias, carries `@deprecated` and logs one development warning (`AIMessage.onRenderBlock`, from an effect). `renderBlock` wins when both are passed. AIMessage never triggers AIResponseRenderer's own alias warning. It is removed in v3.0.
|
|
1079
|
+
|
|
1080
|
+
**G-09 (renderer).** **AIResponseRenderer.** New `renderBlock?: (block) => ReactNode | null | undefined`, with the same semantics as `onRenderBlock`, which becomes its deprecated alias (one keyed development warning, `AIResponseRenderer.onRenderBlock`, from an effect and only when passed). `renderBlock` wins when both are passed.
|
|
1081
|
+
|
|
1082
|
+
**G-16a.** InsightEngine's composer works without `inputValue`.
|
|
1083
|
+
|
|
1084
|
+
`inputValue` defaulted to `''`, so the composer was always controlled: without `inputValue` and `onInputChange`, typing showed nothing and Send sent nothing. Now the composer keeps its own text when `inputValue` is omitted, sends the trimmed text through `onSendMessage` and clears itself; `onInputChange` still reports every change. Passing `inputValue` keeps today's controlled behavior.
|
|
1085
|
+
|
|
1086
|
+
**G-16c.** InsightEngine fits narrow screens and lays out right to left.
|
|
1087
|
+
|
|
1088
|
+
- The drawer never grows past the viewport (`max-width: 100vw`): the 400px side panel and the 800px expanded view used to push 80px or more off a 320px screen.
|
|
1089
|
+
- New `historyWidth?: number` (default 260) sets the expanded view's history column. In a panel narrower than 600px the column stacks above the thread at full width (up to 200px tall) instead of squeezing it; the panel answers to its own width (a container query), not the viewport's. `historyWidth` is the only new width prop: `sidepanelWidth` and `expandedWidth` already exist.
|
|
1090
|
+
- The history column's divider is on its inline end, so in RTL it sits between the column and the thread. Unchanged in LTR.
|
|
1091
|
+
|
|
1092
|
+
`historyWidth` is `@since 2.13.0`.
|
|
1093
|
+
|
|
1094
|
+
**G-16d.** InsightEngine's width change between the side panel and the expanded view is instant under reduced motion.
|
|
1095
|
+
|
|
1096
|
+
The drawer's width transition (motion tokens `motion.duration.normal`, `motion.easing.default`) is turned off with a CSS `@media (prefers-reduced-motion: reduce)` rule, so it also holds before hydration.
|
|
1097
|
+
|
|
1098
|
+
**G-26.** AIMessage, ChatThread and InsightEngine report interaction events through `AIConfigProvider`'s `onInteraction`.
|
|
1099
|
+
|
|
1100
|
+
- **AIMessage**: Copy reports `{ type: 'copy', target: 'response', success, messageId }`; Regenerate reports `{ type: 'retry', messageId }` once per run. The thumbs' feedback events now carry `messageId` too. New `messageId?: string` sets the id; it falls back to `response.id`. ChatThread passes each message's `id`.
|
|
1101
|
+
- **ChatThread**: picking a related question reports `{ type: 'followup', action: 'select', verified: false, index }`.
|
|
1102
|
+
- **InsightEngine**: sending a message reports `{ type: 'message', action: 'send', previousAssistantMessageId }`, the latest assistant message when the user sent it.
|
|
1103
|
+
|
|
1104
|
+
Each event is reported after the component's own callback, carries no text (ids, enums, counts and flags), and nothing is reported without a provider. The AIConfigProvider docs list the events and how to compute the success metrics from them (plan edit rate, retries, correcting follow-ups, suggestion uptake, golden coverage). `messageId` is `@since 2.13.0`.
|
|
1105
|
+
|
|
1106
|
+
**G-37+.** AIResponseRenderer and AIMessage show citations as source chips and keep headings in order inside a thread.
|
|
1107
|
+
|
|
1108
|
+
- A text block with `citations: true` renders each `[^id]` marker as a numbered source chip (AISourceChip), numbered by its place in `response.sources`. A marker that names no source shows as written. Copy keeps the numbers as `[n]`.
|
|
1109
|
+
- New `streaming?: boolean` on AIResponseRenderer (AIMessage passes its `streaming`): while true, a marker whose source has not arrived yet, or a marker cut off mid-stream, is hidden instead of shown as raw text; when the response completes, markers resolve in place and chips already shown are not re-created.
|
|
1110
|
+
- New `headingOffset?: 0 | 1 | 2 | 3 | 4 | 5` on AIResponseRenderer and AIMessage moves markdown headings down. **Visual and semantic change:** inside a ChatThread it defaults to 1, so a response's `#` is an h2 under the page's h1 (WCAG 1.3.1, heading order); standalone it stays 0.
|
|
1111
|
+
|
|
1112
|
+
New props are `@since 2.13.0`.
|
|
1113
|
+
|
|
1114
|
+
**D-39.** InsightEngine's side-panel history toggle tells assistive technology whether the history is open.
|
|
1115
|
+
|
|
1116
|
+
The "HISTORY (n)" toggle was a button without `aria-expanded` or `aria-controls`, so a screen reader could not tell whether the list was shown (WCAG 4.1.2). It is now the shared disclosure trigger: a `<button type="button" aria-expanded aria-controls>` that toggles with Enter and Space. **Visual change:** the chevron is the shared disclosure chevron (points down, turns up when open) and focus shows the theme focus ring. The label text and colour are unchanged.
|
|
1117
|
+
|
|
1118
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1119
|
+
- **Behavior correction (b)**: AIChainOfThought's atoms: shared busy indicators, a task error alert with Retry, a Run button, artifact cards, a metrics list and an ordered references list. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
1120
|
+
|
|
1121
|
+
**WI-10.** AIChainOfThought's busy and progress indicators are the shared atoms and respect reduced motion.
|
|
1122
|
+
|
|
1123
|
+
- The active state (the "Thought Process" bar while a phase is active, and the "Generating response…" row) shows MUI's CircularProgress in the AI primary colour instead of a hand-made border spinner. It is hidden from screen readers; under reduced motion it stops and leaves a static arc.
|
|
1124
|
+
- An understanding phase with no thoughts yet shows two AIShimmer lines instead of three bouncing dots, with the status word "Thinking" for screen readers. The shimmer stops under reduced motion.
|
|
1125
|
+
- The phase dots after "Thought Process" stay decorative; their pulse stops under reduced motion.
|
|
1126
|
+
- The bar's accessible name adds the progress as text after the visible title: "Thought Process, In progress, 1 of 2 steps complete" while a phase is active, "Thought Process, 2 of 3 steps complete" otherwise, then the elapsed time.
|
|
1127
|
+
|
|
1128
|
+
**WI-11a.** AIChainOfThought: new `onTaskRetry(task, phase)` retries a failed execution task, and `onActionError` receives a failed retry.
|
|
1129
|
+
|
|
1130
|
+
When set, each task whose status is `error` shows a "Retry" button in its error alert. The button runs the handler through the shared async contract: while the returned promise is pending, the button is `aria-disabled` and `aria-busy` and keeps keyboard focus; a rejection shows an inline "Couldn't retry the task" alert with "Try again". On success, update `phases` to move the task on (for example to `running` or `retrying`); if focus was on the alert, it moves to the task's row. The name keeps `onRetry` free for a message-level action.
|
|
1131
|
+
|
|
1132
|
+
A rejected retry goes to the new `onActionError(error, { source: 'task', phaseId, taskId })` (the AI family's handler, as on AIMessage; `taskId` is `<phase id>-task-<index>`). Without it, the error goes to `globalThis.reportError`.
|
|
1133
|
+
|
|
1134
|
+
```tsx
|
|
1135
|
+
<AIChainOfThought
|
|
1136
|
+
phases={phases}
|
|
1137
|
+
onTaskRetry={async (task, phase) => {
|
|
1138
|
+
await agent.retryTask(phase.id, task.label);
|
|
1139
|
+
}}
|
|
1140
|
+
onActionError={(error, { taskId }) => log.warn('Task retry failed', taskId, error)}
|
|
1141
|
+
/>
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
**WI-11a (alert).** AIChainOfThought: a failed task's error is an MUI Alert, and a new failure is announced once.
|
|
1145
|
+
|
|
1146
|
+
- The hand-made banner (an inline-start rule on a tint) is now an MUI Alert, severity error, in the task's row: the code and message first, then the resolution under a dashed rule. It is a `role="group"` named by the code and message, never `role="alert"`, so no live region nests inside a chat log.
|
|
1147
|
+
- A task that enters `error` after the chain mounts is announced once, assertively: "{task label} failed: {message}". A task already failed when the chain mounts is not announced. `announcements="off"` is silent, and `announcements="stream"` leaves it to the live container. Inside a chat thread the error is still announced, through the document's announcer, not a live region in the log.
|
|
1148
|
+
|
|
1149
|
+
**WI-11b.** AIChainOfThought: the plan's "Run" action is a contained Button.
|
|
1150
|
+
|
|
1151
|
+
`showRun` and `onRun` are unchanged. The gradient pill (a clickable MUI Chip) is now `<Button variant="contained" size="small">` with the play icon: a native `<button>` that Enter and Space activate, in the theme's primary colours, with the theme's focus ring. The hover lift and glow are gone.
|
|
1152
|
+
|
|
1153
|
+
**WI-11c.** AIChainOfThought: clickable artifact cards are keyboard buttons, and the artifact count is a chip.
|
|
1154
|
+
|
|
1155
|
+
- An artifact with `onClick` now has a real primary action: a button named by the artifact's label covers the card, so Tab reaches it and Enter or Space activates it, and the card shows the theme's focus ring inside its border. Clicking anywhere on the card still calls `onClick`. The card's content stays plain text (no block content inside a button). Without `onClick` the card is plain content, as before.
|
|
1156
|
+
- The count after "Generated Artifacts" is an outlined small chip instead of a hand-made circle.
|
|
1157
|
+
- The artifact label takes the `body2` size (14px, was 13.1px; E-14 OD-E10 option A).
|
|
1158
|
+
|
|
1159
|
+
**WI-11d.** AIChainOfThought: insight metrics are a labelled list, and the values are no longer headings.
|
|
1160
|
+
|
|
1161
|
+
Each metric value was an `<h6>`. The metrics are now a list named "Key metrics", one item per metric: the value as a gradient `<span>` (GradientText) and its label as a caption. The value uses the AI metric type role (20px, semibold, tabular figures; was 20.8px monospace), so it is 0.8px smaller and uses the body font. The separator above the list takes the theme's AI border colour.
|
|
1162
|
+
|
|
1163
|
+
**WI-11e.** AIChainOfThought: insight references are an ordered list of AISource cards.
|
|
1164
|
+
|
|
1165
|
+
The "Sources & References" block rendered its own cards in plain divs. Each reference is now an `AISource` card (title, badge chip, meta, description and icon) inside an `<ol>`, so screen readers announce the list and its count. The badge is an outlined chip instead of a coloured caption, the icon carries a text name ("Other source"), and the card border and spacing are AISource's. `InsightReference` is unchanged.
|
|
1166
|
+
|
|
1167
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1168
|
+
- **Additive**: The AI strings catalog gains `ai.blocks`; the shared block gate and ready announcement (internal). ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1169
|
+
|
|
1170
|
+
**G-12 (gate half).** The AI strings catalog gains the `ai.blocks` namespace (G-12, gate half):
|
|
1171
|
+
|
|
1172
|
+
- `ai.blocks.ready` ('Ready for your input'): announced once when an interactive response block finishes streaming and becomes usable, for a block that has no text of its own. Clarification and plan review keep their own copy (`ai.clarificationPrompt.ready`, `ai.planReview`).
|
|
1173
|
+
- `ai.blocks.answerToContinue` ('Answer the question above to continue'): the composer's visible reason while the latest answer asks a required question.
|
|
1174
|
+
|
|
1175
|
+
Both are overridable through `AIConfigProvider`'s `strings` (`{ blocks: { ready, answerToContinue } }`). `AIStrings` is a read type, so adding keys is minor. New keys are `@since 2.13.0` with the catalog.
|
|
1176
|
+
|
|
1177
|
+
Internal only (no package entry exports them; the internal registry lists them under AI ORGANISMS INTERNALS): `useBlockReadyAnnouncement` and `BlockReadyAnnouncementSuppressed` (src/components/AI/utils/blockReady.ts), `getPendingRequiredClarification` and `useRequiredClarificationGate` (src/components/AI/utils/blocks.ts). The block renderers (W1-A), InsightEngine (W1-IE), ChatThread's composer hosts (W2-T) and InsightEnginePanel (W2-AL) adopt them; their own changesets describe the visible behavior.
|
|
1178
|
+
|
|
1179
|
+
React only: the CSS package carries tokens and themes, not components or copy, so it does not change.
|
|
1180
|
+
|
|
1181
|
+
No MIGRATION row.
|
|
1182
|
+
- **Behavior correction (b, e)**: AIChainOfThought: actions only with handlers, attachments in the content column, a plan-review phase, verified related questions, resolver colours, catalog strings, type roles and reduced motion, the root props contract and AIConfidenceBadge's bar. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1183
|
+
|
|
1184
|
+
**G-21.** AIChainOfThought's response shows only the actions it can run, and its sources, related questions and suggested actions sit in the response's content column.
|
|
1185
|
+
|
|
1186
|
+
- Dead buttons removed: the response AIMessage showed Share, Download and the thumbs even when the chain had no handler for them. Now the thumbs show with `onFeedback`, `onFeedbackChange` or a controlled `feedback`; Share with `onShare`; Download with `onDownload`. Copy always shows: without `onCopy` it writes the final phases' visible text to the clipboard and announces the result; with `onCopy` it stays callback-only (`onCopy` now receives the copied text; a `() => void` handler still fits).
|
|
1187
|
+
- Alignment: sources, related questions and suggested actions move into AIMessage's `attachments` slot. The chain's three `calc(32px + 16px)` insets are gone, so they start at the content column (avatar plus gap), about 10px from where they were. Suggested actions are keyed by label and place, so a repeated label keeps both buttons.
|
|
1188
|
+
- NEW pass-through props (all `@since 2.13.0`): `onRegenerate` (the Regenerate action, with its pending state, failure alert and `onActionError` context `{ source: 'action', actionId: 'regenerate' }`), `actions: AIMessageAction[]`, `badges`, `feedbackControl`, `feedback`, `defaultFeedback`, `onFeedbackChange`, `copyText`. `onActionError` also receives the response's action failures.
|
|
1189
|
+
|
|
1190
|
+
React only: no token, role or default-string change; the CSS package is unchanged.
|
|
1191
|
+
|
|
1192
|
+
**G-22 and G-43+ (step 4).** AIChainOfThought hosts a plan the user reviews before it runs, and shows when the chain waits for the user.
|
|
1193
|
+
|
|
1194
|
+
- NEW `PlanReviewPhase { type: 'plan-review'; plan: AIPlan; presentation? }` in `ChainPhase` (root type export). It is always a thinking phase: never in the response AIMessage, never a response (no Copy, no footer, no "Response ready"), whatever `thinkingPhaseIds` says. Its body is the `plan-review` block renderer (AIPlanReview through `hitlBlockRenderers`) with a block whose id is the phase id; `plan.confirm`, `plan.stepEdit`, `plan.clarify` and `plan.cancel` reach the NEW `onBlockEvent`. NEW `blockRenderers` merges over a host's provided renderers.
|
|
1195
|
+
- WIDENED `PhaseStatus` and `StepStatus` with `'awaiting-input'`. Without an explicit `status` (which still wins), a plan-review phase is `awaiting-input` for plan status 'review' (or none), `active` for 'replanning', `completed` otherwise; an execution phase is error > awaiting-input > all completed > active > pending. Exhaustive `switch` statements over `ChainPhase['type']`, `PhaseStatus` or `StepStatus` need a default branch:
|
|
1196
|
+
|
|
1197
|
+
```ts
|
|
1198
|
+
switch (phase.type) {
|
|
1199
|
+
case 'understanding': /* … */ break;
|
|
1200
|
+
default: /* plan-review and any later type */ break;
|
|
1201
|
+
}
|
|
1202
|
+
```
|
|
1203
|
+
- While a phase waits for the user, the thought process and that phase stay open (a forced open that overrides the user's collapse, by design), the auto-collapse waits (also with `defaultCollapsed`), and the indicator under the phases reads "Plan's ready — needs your confirmation" (a plan, requirement copy R9) or "Needs your input", with an icon instead of the spinner. A phase that starts waiting after mount is announced once, politely, with the same text (`announcements="transitions"`, also inside a thread through its announcer); the hosted renderer's own ready announcement is suppressed so the plan is read once. An awaiting-input phase or task shows StepList's attention marker with the text "Needs your input".
|
|
1204
|
+
- NEW `ExecutionTask.id?` (the row key and `onActionError`'s `taskId`; default `<phase id>-task-<index>`; a duplicate warns once) and `ExecutionTask.retryable?` (default true; false hides Retry for that task). `onTaskRetry(task, phase)` is unchanged.
|
|
1205
|
+
- NEW `showQuery` (default false): shows the query above the thought process.
|
|
1206
|
+
- Narrow hosts: below the compact container width (360px) the plan-review body keeps 16px of inline-start padding instead of the 72px phase indent, so the plan keeps its width in a 320px AI panel.
|
|
1207
|
+
- NEW string keys `ai.chainOfThought.planReady` and `ai.chainOfThought.awaitingInput` (React only).
|
|
1208
|
+
|
|
1209
|
+
`PlanPhase` is unchanged. All new types and props are `@since 2.13.0`. No token or role change; the CSS package is unchanged.
|
|
1210
|
+
|
|
1211
|
+
**G-24c.** AIChainOfThought's related questions can be verified suggestions.
|
|
1212
|
+
|
|
1213
|
+
- WIDENED `relatedQuestions` to `Array<string | AIRelatedQuestion>`: an object carries an `id` and `verified` (the 'Verified' badge, part of the button's name). Strings work as before.
|
|
1214
|
+
- NEW `onQuestionSelect?: (question: AIRelatedQuestion, index: number) => void`, passed to AIRelatedQuestions; a string question arrives as `{ text }`. `onQuestionClick` still receives the text.
|
|
1215
|
+
- The chain owns the callback, so it reports the content-free `followup.select` interaction (`{ verified, index, questionId? }`) once per pick through AIConfigProvider's `onInteraction`.
|
|
1216
|
+
|
|
1217
|
+
`@since 2.13.0`. React only; the CSS package is unchanged.
|
|
1218
|
+
|
|
1219
|
+
**WI-12.** AIChainOfThought reads every colour from the palette resolver, so presets, brand overrides and plain MUI themes reach the whole chain.
|
|
1220
|
+
|
|
1221
|
+
- No colour literal, `palette.mode` branch or static `aiTokens.colors` / `aiTokens.gradient` remains in the component; the `statusColors` constant and its lint waiver are gone.
|
|
1222
|
+
- Roles (E-26 map rows): neutral washes `overlay.hoverSubtle` and `overlay.hover`; the insight card and its highlights `ai.surface` (solid, replacing the two-stop tints); the insight border and the references rule `ai.border`; the artifact chart and the confidence bar `ai.gradient.diagonal` / `.horizontal`; the artifact icon tile `ai.surface`; the output summary glyph `status.success.text`; the insight sparkle `ai.accent`; an artifact card's hover lift `theme.shadows[4]`.
|
|
1223
|
+
- Contrast fixes (shown failing in the contrast matrix over paper, elevated and sunken surfaces in every preset): inline code in thoughts measured 4.3:1 (light, sunken surface) and 3.6-4.3:1 (dark; the 2.12 washes were already under 4.5:1 there) on the neutral washes, and the insight highlight 4.3:1 on the AI tint in the dark analytics preset. Both are now paper chips (`background.paper`; code with a divider border), where `ai.primary` is 4.5:1 or more in every theme. The insight's confidence bar sits on a paper inset (the confidence roles measured 4.48:1 on the tinted card).
|
|
1224
|
+
- The `<mark>` highlight keeps its asserted ratios (at least 1.5:1 against the surface, text.primary at least 7:1); its tint is now chosen by the paper surface's luminance instead of `palette.mode`.
|
|
1225
|
+
- Light-mode shade shifts (for the owner's Storybook review): thought bubbles, the output summary, artifact cards and the active phase card from a 2% to a 4% neutral wash; inline code from a 6% wash to a paper chip with a divider border; the insight card from a 4% purple-to-coral gradient to the solid 5% AI surface, its highlights from a 10% gradient to a paper chip; the output summary glyph from #24A148 to #15803D (3.3:1 to 5:1 on its surface); the insight sparkle from #FBBF24 (1.7:1) to the AI accent #FF6150. Dark mode moves to the dark roles (the chart and confidence gradients use the dark AI accent and fill).
|
|
1226
|
+
|
|
1227
|
+
No new token or role: every role used already has its CSS variable, so the CSS package is unchanged.
|
|
1228
|
+
|
|
1229
|
+
**WI-13.** AIChainOfThought's text is translatable through AIConfigProvider's `strings` (`ai.chainOfThought.*`).
|
|
1230
|
+
|
|
1231
|
+
- Every user-visible string in the chain now reads the catalog: the thought-process title and progress, the phase status words, "Sources consulted", "Generated Artifacts", the insight's fallback title, "Confidence", "Sources & References", "Key metrics", "Generating response…", a retrying task's "Retrying", the task Retry button and its failure text, and the assertive "{label} failed: {message}" announcement.
|
|
1232
|
+
- NEW keys (`@since 2.13.0`): `progressSummary({ completed, total, locale })` (English plural rules by default; an override receives the AI locale for its own `Intl.PluralRules`), `retrying`, `retryTask`, `retryTaskFailed`, `taskFailed({ label, message })`, `metrics`, and `phaseStatus.{pending, active, completed, error}` (an awaiting-input phase reads `awaitingInput`). No raw status value reaches an accessible name.
|
|
1233
|
+
- `phaseToggle` stays in the catalog, unused: a phase trigger is named by its visible title, then its status text (WCAG 2.5.3).
|
|
1234
|
+
|
|
1235
|
+
String keys are React only; the CSS package is unchanged.
|
|
1236
|
+
|
|
1237
|
+
**WI-14a.** AIChainOfThought uses the AI type roles, logical spacing and the motion tokens, and every entrance respects reduced motion.
|
|
1238
|
+
|
|
1239
|
+
- Type: the inline rem sizes go to aiTypography roles (OD-E10 option A snaps): thought bubbles and the insight body 13.6px to `body` 14px (line height 1.7/1.8 to 1.5); inline code 12.8px to `code` 12px; section captions stay 12px (`caption`); the output summary 14.4px to `bodyEmphasis` 14px; its description 12.8px to `caption` 12px; the insight title 15.2px to `heading` 16px; artifact meta and the confidence value take `code` 12px; the waiting indicator 13.1px to `caption` 12px.
|
|
1240
|
+
- Layout: a phase is a grid shared with its header; the body sits in the column after the 72px header lead (unchanged in LTR and mirrored in RTL), with `text-align: start`. Below the compact container width (360px) every phase body keeps 16px of inline-start padding instead of 72px, so a 320px AI panel keeps the body's width.
|
|
1241
|
+
- Spacing: every margin, padding and border key is logical (block and inline).
|
|
1242
|
+
- Motion: thoughts, artifact cards and the insight rise in on a scoped keyframes name (no global `cot-fadeInUp`) with `motion.duration.slow` / `slower` and `easing.emphasized`, staggered by `motion.stagger.normal`; the references slide in from the inline start (right in RTL); the artifact card hover and the phase card use token transitions on named properties. Under `prefers-reduced-motion` every entrance, the card lift and the phase fades stop (fade timeouts 0).
|
|
1243
|
+
|
|
1244
|
+
No token or role change; the CSS package is unchanged.
|
|
1245
|
+
|
|
1246
|
+
**WI-14b.** AIChainOfThought follows the root props contract: props it used to drop now take effect.
|
|
1247
|
+
|
|
1248
|
+
- `className`, `id`, `data-*`, `aria-*` (with `aria-describedby` and `aria-labelledby` reaching the root) and event handlers are spread on the root `<div>`. The props type is the chain's own props plus `Omit<HTMLAttributes<HTMLDivElement>, own | 'color'>`; the chain's own `onCopy` (the response Copy callback) shadows the DOM clipboard handler by design.
|
|
1249
|
+
- `role="group"`, `aria-busy` and, with `announcements="stream"`, the live attributes stay the component's: a consumer value does not replace them (one development warning). A consumer `aria-label` replaces the group name ("Reasoning for {query}").
|
|
1250
|
+
- `sx` takes the object, array and function forms and still styles the root.
|
|
1251
|
+
- NEW `slotProps` (`@since 2.13.0`): `root`, `toggle` (the thought-process disclosure), `phaseCard` (each phase card) and `response` (the box around the response message), each with `className`, `data-*` and `sx`.
|
|
1252
|
+
- The root now always has an `id` (the consumer's, else a generated one).
|
|
1253
|
+
|
|
1254
|
+
React only; the CSS package is unchanged.
|
|
1255
|
+
|
|
1256
|
+
**WI-11f.** AIChainOfThought's insight confidence uses AIConfidenceBadge's bar.
|
|
1257
|
+
|
|
1258
|
+
- The hand-rolled confidence bar (a thin 80px LinearProgress with the percent) is replaced by `AIConfidenceBadge variant="bar"`: the 'Confidence' label, the level word and percent ('Medium · 82%'), the level's own signal-bar glyph and a decorative bar, coloured by the confidence roles. Screen readers read 'Confidence: Medium, 82%' instead of a progress bar.
|
|
1259
|
+
- The level follows the provider's confidence thresholds (defaults: medium from 60%, high from 85%).
|
|
1260
|
+
|
|
1261
|
+
No token or role change; the CSS package is unchanged.
|
|
1262
|
+
|
|
1263
|
+
Type changes (G-29 type-change note): `ChainPhase` adds `PlanReviewPhase` (`'plan-review'`); `PhaseStatus` and `StepStatus` add `'awaiting-input'`; `AIChainOfThoughtProps['relatedQuestions']` widens to `Array<string | AIRelatedQuestion>`. Add a default branch to exhaustive switches over `phase.type` and the status unions. The rest-spread line (ADR-04): AIChainOfThought now spreads unknown props on its root (WI-14b).
|
|
1264
|
+
|
|
1265
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M8-index.md (light, dark; RTL, the docked 320px and 800px panel and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1266
|
+
- **Additive**: NEW AIReviewItem: one answer in the golden-dataset review with the reviewer's decision. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1267
|
+
|
|
1268
|
+
**G-19.** NEW `AIReviewItem`: one answer in the golden-dataset review, with what produced it and the reviewer's decision (requirements R24-R26).
|
|
1269
|
+
|
|
1270
|
+
- Content: the question as the heading (`headingLevel`, default 2; `headingRef` reaches it), a status chip (icon and text), the details (asked by, asked at in the provider's locale and time zone, confidence, verification), the plan (`AIPlanReview` read-only), the query (`AIQueryDisclosure`, expanded), the answer read-only (`AIResponseRenderer` with `hitlBlockRenderers`, or plain text), the users' feedback (rating, problem chips, comment) and the history (a compact, non-interactive Timeline; each entry is text plus an icon).
|
|
1271
|
+
- Decisions in a group named "Decision", through `onDecision(decision: AIReviewDecision)` (a promise drives pending and the error):
|
|
1272
|
+
- Approve.
|
|
1273
|
+
- Edit: one field per `editableFields` entry (default `['answer']`; `'query'` only when the item has a query). "Review changes" shows a unified diff of each changed field in a named, focusable region; with no change it shows a message and the button stays enabled. Submit sends an `AIReviewEdits` payload; Cancel returns focus to Edit. `edits.answer` is plain text: the service rebuilds its structure.
|
|
1274
|
+
- Reject: a dialog with a required reason (ConfirmDialog's `reasonField`): an empty reason is marked invalid, explained and focused; a failure keeps the dialog open with its alert; `onDecision` gets the trimmed reason.
|
|
1275
|
+
- `canDecide` defaults to `Boolean(onDecision)`; when false, `decisionBlockedReason` (or "You can't decide on this item.") replaces the controls. A decided item shows no controls.
|
|
1276
|
+
- Pending: every decision control is `aria-disabled`, never `disabled`, and keeps focus; `loading` shows the same state from outside. After a decision the summary replaces the controls, is announced, and takes focus with `focusOnResolve="summary"` (default) or leaves focus to the host with `"none"`. Failures show an inline alert with Try again and call `onActionError(error, { source: 'decision', itemId })`.
|
|
1277
|
+
- Props: `item`, `canDecide`, `decisionBlockedReason`, `onDecision`, `editableFields`, `focusOnResolve`, `loading`, `headingLevel`, `headingRef`, `blockRenderers`, `onActionError`, `approveLabel`, `editLabel`, `rejectLabel`, `size` ('small' | 'medium'), `className`, `sx`, `slotProps` (`header`, `details`, `decisions`, `history`) and HTML attributes on the root.
|
|
1278
|
+
- Strings: the new `ai.reviewItem` group (53 keys) through `AIConfigProvider`.
|
|
1279
|
+
- Telemetry (`AIReviewInteraction`): decide, edit_start, reject_start; the item id, the decision, the flag count and the edited field names only.
|
|
1280
|
+
|
|
1281
|
+
New exports `AIReviewItem` and `AIReviewItemProps`, both `@since 2.13.0`. React only: no token, role or default change, so the CSS package is unchanged (components and copy are not part of it). The reject path uses ConfirmDialog's `reasonField` (C-14), whose release waits for its own sign-offs.
|
|
1282
|
+
|
|
1283
|
+
**G-27 (theme-flow tests).** Tests only, nothing shipped changes (G-27 theme-flow verification): a `success.main` override reaches AIMessage's pressed positive thumb; a brand primary reaches AIPlanReview's and AIReviewItem's accents; AIMessage, ChatThread, InsightEngine, UserMessage, AIResponseRenderer, AIPlanReview and AIReviewItem render under a plain MUI `createTheme()` with valid colours; the two new organisms render in every mode, preset and direction.
|
|
1284
|
+
|
|
1285
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M8-index.md (light, dark; RTL, the docked 320px and 800px panel and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1286
|
+
- **Behavior correction (b, c, e)**: AppLayout's AI panel (InsightEnginePanel) renders its conversation with ChatThread and AIMessage, carries the HITL contract and the reasoning, reads its colours and strings from the theme and the catalog, and takes `streaming` / `loading`. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1287
|
+
|
|
1288
|
+
**WH-08.** AppLayout's AI panel (InsightEnginePanel) renders its conversation with ChatThread and AIMessage.
|
|
1289
|
+
|
|
1290
|
+
The panel drops the deprecated AIChatMessage and AITypingIndicator. Its messages are one log (`role="log"`) with an article per message and a visually hidden heading per message one level under the panel title; ChatThread is the panel's scroll container and follows new content only while the reader is at the end. The boxed layout in fullscreen centres the messages at 900px while the scrollbar stays at the panel's edge. `isTyping` (and the demo's delay) shows a streaming entry, AIMessage's shimmer placeholder, with no actions. Existing apps keep working: the panel's own `ChatMessage` type, `messages` / `initialMessages`, `onMessagesChange`, `onSendMessage`, `aiAvatar`, `userInitials` and `boxedLayout` are unchanged.
|
|
1291
|
+
|
|
1292
|
+
New optional props: `messageActions` (`InsightEnginePanelMessageActions`: `onCopy`, `copyBehavior`, `onFeedback`, `onFeedbackSubmit`, `onShare`, `onDownload`, `onRegenerate`, `getActions`, each called with the panel's own message), the `onRegenerate` shorthand (a returned promise shows the action busy; a rejection shows "Couldn't regenerate the response" with Try again) and `formatTimestamp` (no timestamp by default). The panel's root takes `className`, `sx` and its div's HTML attributes (ADR-04). In demo mode the thumbs and Regenerate work (demo-only handlers); any handler you pass wins.
|
|
1293
|
+
|
|
1294
|
+
UserMessage's text now takes the bubble's contrast colour (it was the theme's secondary text colour on the bubble: 3.89:1 on the analytics preset). In ChatThread, an empty streaming message (a placeholder) shows no actions until its content arrives.
|
|
1295
|
+
|
|
1296
|
+
**WH-09.** AppLayout's AI panel carries the human-in-the-loop contract: structured answers, interactive blocks, a gated composer and the reasoning.
|
|
1297
|
+
|
|
1298
|
+
The panel's `ChatMessage` gains optional `response` (an `AIResponse`: its blocks render, with citations from `response.sources` and the confidence and verification badges from `response.metadata`), `streaming` and `feedback`. New optional props, forwarded to ChatThread: `onBlockEvent(message, event)` with the panel's own message (the query, clarification and plan-review blocks render by default), `blockRenderers`, `renderBlock`, `getMessageSlots(message, defaults)` and `onActionError`. A required clarification in the latest answer makes the composer unavailable, with 'Answer the question above to continue' shown, until it is answered (only while `onBlockEvent` is passed). `getReasoning(message)` puts AIChainOfThought (markdown by default) in the message, ahead of its sources and related questions; its block events go to `onBlockEvent` and its failures to `onActionError`. Every new prop is optional; without them the panel renders as before.
|
|
1299
|
+
|
|
1300
|
+
**WH-12 (InsightEnginePanel).** AppLayout's AI panel reads its AI colours through the palette resolver.
|
|
1301
|
+
|
|
1302
|
+
The panel surface (`ai.gradient.subtle`), the empty state's tile (`ai.surface`) and icon (`ai.primary`) come from `getTrinityPalette`, so the dark theme, the presets, a brand primary and a plain `createTheme()` reach them (dark surfaces gain the dark AI surface). The Pro chip is a MetaChip in `ai.primaryFill` with `ai.onPrimaryFill` text at the badge size (it was white 10px text on the static gradient). The open history toggle shows `ai.primary` on `ai.surfaceEmphasis`. No API change; React only: every role has its CSS variable, so the CSS package does not change.
|
|
1303
|
+
|
|
1304
|
+
**WH-13 (InsightEnginePanel).** AppLayout's AI panel reads every string from `ai.insightEnginePanel`.
|
|
1305
|
+
|
|
1306
|
+
The default title, the empty state's heading and description, the Pro chip and the disclaimer join the header controls and the composer placeholder in the catalog: an `AIConfigProvider` override reaches them, and the `title` and `placeholder` props still win. The defaults are unchanged. `ai.insightEnginePanel.searching` is no longer rendered (the streaming entry replaced the typing indicator) and stays in the catalog.
|
|
1307
|
+
|
|
1308
|
+
**WH-50+.** AppLayout's AI panel takes `streaming` and `loading`, deprecates `isTyping` and the message field `isLoading`, and reports each send.
|
|
1309
|
+
|
|
1310
|
+
`streaming` replaces `isTyping` (it wins when both are passed); `loading` makes the log aria-busy and, without messages, shows decorative placeholders. Passing `isTyping`, or a message with `isLoading`, logs a one-time development warning (`InsightEnginePanel.isTyping`, `InsightEnginePanel.isLoading`). After `onSendMessage` the panel emits the content-free `{ type: 'message', action: 'send', previousAssistantMessageId? }` interaction through `AIConfigProvider`'s `onInteraction`, as InsightEngine does.
|
|
1311
|
+
|
|
1312
|
+
Integration (owner panel check): the panel's `ChatMessage.relatedQuestions` widens to `Array<string | AIRelatedQuestion>` like `ChatThreadMessage`'s, so the panel shows verified related questions (the 'Verified' badge is part of the button's name; picking one still puts its text in the composer). Type change (G-29): the panel's `ChatMessage` gains optional `response`, `streaming`, `feedback` and the widened `relatedQuestions`; `InsightEnginePanelProps` gains `messageActions`, `onRegenerate`, `onBlockEvent`, `blockRenderers`, `renderBlock`, `getMessageSlots`, `getReasoning`, `onActionError`, `streaming`, `loading` and `formatTimestamp`, and deprecates `isTyping` and the message field `isLoading`.
|
|
1313
|
+
|
|
1314
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M8-index.md (light, dark; RTL, the docked 320px and 800px panel and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1315
|
+
- **Behavior correction (b)**: AIMessage: one string per action for its tooltip and name (four tooltip deltas), and a development warning for dead Feedback, Share and Download buttons (v3.0 defaults). ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1316
|
+
|
|
1317
|
+
**G-28 (AIMessage).** AIMessage's action text comes from the AI strings catalog (`ai.message.*`, read through `useAIStrings`), and each action has one string that is both its tooltip and its accessible name.
|
|
1318
|
+
|
|
1319
|
+
Listed tooltip deltas (the tooltip and the name no longer differ; the names are unchanged):
|
|
1320
|
+
|
|
1321
|
+
| Action | Tooltip before | Tooltip now (= name) | Key |
|
|
1322
|
+
|---|---|---|---|
|
|
1323
|
+
| Copy | `Copy` | `Copy response` | `ai.message.copyResponse` |
|
|
1324
|
+
| Share | `Share` | `Share response` | `ai.message.shareResponse` |
|
|
1325
|
+
| Download | `Download` | `Download response` | `ai.message.downloadResponse` |
|
|
1326
|
+
| Regenerate | `Regenerate` | `Regenerate response` | `ai.message.regenerateResponse` |
|
|
1327
|
+
|
|
1328
|
+
- An unavailable action (Copy or Regenerate while streaming) shows its name, then the reason, in its tooltip; the reason stays its description (`aria-describedby`).
|
|
1329
|
+
- The actions group's name reads `ai.message.actionsLabel` ('AI response actions', unchanged).
|
|
1330
|
+
- NEW key `ai.message.regenerateUnavailableStreaming` ('Regenerate is unavailable while the response is streaming', the same text as before, now overridable). `AIStrings['message']` gains it (minor, a read type).
|
|
1331
|
+
- `ai.message.regenerate` ('Regenerate') is no longer read by AIMessage; it stays in the catalog.
|
|
1332
|
+
- Every key can be overridden with `AIConfigProvider strings={{ message: { ... } }}`; a nested provider's key wins and the outer one fills the rest. Per-instance props keep precedence (the root `aria-label`, `slotProps.actions['aria-label']`).
|
|
1333
|
+
- The thumbs already used one key each (`ai.message.markGood`, `markPoor`; G-04e).
|
|
1334
|
+
|
|
1335
|
+
Consumer tests that find a tooltip by 'Copy', 'Share', 'Download' or 'Regenerate' should look for the '... response' text, or find the button by its name (unchanged).
|
|
1336
|
+
|
|
1337
|
+
React only: string keys are not in the CSS package (it carries tokens and themes, not copy). No token, role or default change.
|
|
1338
|
+
|
|
1339
|
+
Stories: no layout change. The tooltips in AI/AIMessage (and AIMessage inside ChatThread, AIChainOfThought and the AI panel) read the new text on hover; AllActions gains a play that hovers each action and checks its tooltip equals its name.
|
|
1340
|
+
|
|
1341
|
+
**G-25.** AIMessage warns in development when Feedback, Share or Download shows without its handler. In v3.0 the defaults of `showFeedback`, `showShare` and `showDownload` become `Boolean(handler)`, so these dead buttons will hide.
|
|
1342
|
+
|
|
1343
|
+
- A keyed warning per condition, once per session, from an effect, development only: `AIMessage.showFeedback-without-onFeedback` (the thumbs show, neither `onFeedback` nor `onFeedbackChange` is passed, the rating is not controlled with `feedback` and no `feedbackControl` replaces them), `AIMessage.showShare-without-onShare` and `AIMessage.showDownload-without-onDownload`.
|
|
1344
|
+
- It fires for the default and for an explicit `true` alike. Copy never warns: without `onCopy` it writes to the clipboard, and `showCopy` keeps its `true` default in v3.0.
|
|
1345
|
+
- To silence it, pass the handler, or set the `show*` prop to `false`.
|
|
1346
|
+
- ChatThread and AIChainOfThought pass the `show*` booleans from their handlers, so they never trigger it; neither does any Trinity story (test:storybook fails a story that logs it).
|
|
1347
|
+
|
|
1348
|
+
No API change; React only (no token, role or default change, so the CSS package is unchanged). No string key: the warning is developer text.
|
|
1349
|
+
|
|
1350
|
+
MIGRATION row (v3.0, "Other changes planned for v3.0"; the release PR puts it in place of the ADR-13 row):
|
|
1351
|
+
| `AIMessage` `showFeedback` / `showShare` / `showDownload` defaults become `Boolean(handler)` | Default `true`. Since 2.13.0 a development warning fires once per condition when one shows without its handler, for the default and an explicit `true` (`AIMessage.showFeedback-without-onFeedback`, `AIMessage.showShare-without-onShare`, `AIMessage.showDownload-without-onDownload`). `showCopy` stays `true` | G-25 (ADR-13 (4), (5)) |
|
|
1352
|
+
|
|
1353
|
+
Migration snippet:
|
|
1354
|
+
```tsx
|
|
1355
|
+
// Before (2.x): Share and Download show and do nothing; the thumbs record nothing.
|
|
1356
|
+
<AIMessage response={response} />
|
|
1357
|
+
// After: wire the actions you want, hide the rest.
|
|
1358
|
+
<AIMessage response={response} onFeedback={sendFeedback} onShare={share} showDownload={false} />
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M8-index.md (light, dark; RTL, the docked 320px and 800px panel and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1362
|
+
- **Behavior correction (b)**: The HITL requirement copy is pinned and protected, and the AI string defaults follow docs/MICROCOPY.md in one listed pass. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1363
|
+
|
|
1364
|
+
**G-42+.** The HITL requirement copy is pinned verbatim and protected (G-42+). The requirements quote it (R5, R9, R17, R26); a test fails when one of these defaults changes, and docs/MICROCOPY.md lists them under "Requirement copy (protected)": change them only with product sign-off. A consumer can still override any of them with `AIConfigProvider strings`.
|
|
1365
|
+
|
|
1366
|
+
| Requirement | Key | Default |
|
|
1367
|
+
|---|---|---|
|
|
1368
|
+
| R5 | `ai.planReview.confirm` | Confirm and run (unchanged) |
|
|
1369
|
+
| R5 | `ai.planReview.gatingNote` | No query runs until you confirm the plan. (unchanged) |
|
|
1370
|
+
| R9 | `ai.planReview.ready`, `ai.chainOfThought.planReady` | Plan's ready — needs your confirmation (unchanged) |
|
|
1371
|
+
| R17 | `ai.clarificationPrompt.question.ambiguousTerm({ count, term })` | `Did you mean ${term}?` (was `Is this what you mean by “${term}”?` for one option, `“${term}” can mean ${count} things. Which one do you mean?` otherwise) |
|
|
1372
|
+
| R17 | `ai.clarificationPrompt.question.missingValue({ term })` | `What about ${term}?` (was `Which ${term} do you want?`) |
|
|
1373
|
+
| R17 | `ai.clarificationPrompt.question.unfamiliarTerm({ term })` | `What did you mean by ${term}?` (was `What do you mean by “${term}”?`) |
|
|
1374
|
+
| R26 | `ai.verificationBadge.status.golden` | Answered from the verified dataset (was 'Answered from verified dataset') |
|
|
1375
|
+
|
|
1376
|
+
- Two M6 defaults change (F-15's three question templates, F-11's golden label). No key is added, renamed or removed; the type of each key is unchanged. `ambiguousTerm` keeps its `{ count, term }` signature so a locale can pluralize; the English text no longer uses the count.
|
|
1377
|
+
- AIClarificationPrompt's question is also its group's accessible name: consumer tests that find the group by the old question should use the new text. AIVerificationBadge's golden chip and its details button read the new label.
|
|
1378
|
+
|
|
1379
|
+
React only: string defaults are not in the CSS package (it carries tokens and themes, not copy). No token, role or theme change.
|
|
1380
|
+
|
|
1381
|
+
Stories (light, dark): AI/AIClarificationPrompt (every story with a term), AI/AIVerificationBadge (golden), the clarifications in AI/AIPlanReview and AIChainOfThought's plan review, and the AI panel's HITL content (Templates/InsightEnginePanel ContentHitl).
|
|
1382
|
+
|
|
1383
|
+
**E-47+.** The AI string defaults follow docs/MICROCOPY.md (E-47+): one listed pass, after every AI string wiring landed. No key is added, renamed or removed; the requirement copy (G-42+) is excluded.
|
|
1384
|
+
|
|
1385
|
+
| Key | Before | Now |
|
|
1386
|
+
|---|---|---|
|
|
1387
|
+
| `ai.chainOfThought.thoughtProcess` | Thought Process | Thought process |
|
|
1388
|
+
| `ai.chainOfThought.generatedArtifacts` | Generated Artifacts | Generated artifacts |
|
|
1389
|
+
| `ai.chainOfThought.sourcesAndReferences` | Sources & References | Sources & references |
|
|
1390
|
+
| `ai.chatHistoryList.addToProject` | Add to Project | Add to project |
|
|
1391
|
+
| `ai.relatedQuestions.title` | Related Questions | Related questions |
|
|
1392
|
+
| `ai.insightEngine.history` | HISTORY | History (still upper case on screen, from CSS) |
|
|
1393
|
+
| `ai.insightEngine.historyWithCount({ count })` | HISTORY (2) | History (2) (still upper case on screen, from CSS) |
|
|
1394
|
+
| `ai.insightEngine.placeholder` | Ask anything... | Ask anything… |
|
|
1395
|
+
| `ai.insightEnginePanel.placeholder` | Ask anything... | Ask anything… (not rendered: the panel's composer uses `composerPlaceholder`) |
|
|
1396
|
+
| `ai.insightEnginePanel.searching` | Searching... | Searching… (not rendered since H-08) |
|
|
1397
|
+
| `ai.listeningIndicator.text` | Listening... Speak now | Listening… Speak now |
|
|
1398
|
+
| `ai.voiceOverlay.listening` | Listening... | Listening… |
|
|
1399
|
+
| `ai.threadHistorySidebar.searchPlaceholder` | Search threads... | Search threads… |
|
|
1400
|
+
| `ai.threadHistorySidebar.threadCount({ count })` | 1 threads | 1 thread (other counts unchanged: '3 threads') |
|
|
1401
|
+
|
|
1402
|
+
- Accessible names change with the text where the text names a control: AIChainOfThought's toggle ('Thought process …'), AIRelatedQuestions' trigger ('Related questions'), InsightEngine's history toggle ('History (2)'; screen readers read the DOM text, not the CSS upper case), ChatHistoryList's 'Add to project' menu item. Consumer tests that query the old text should use the new one.
|
|
1403
|
+
- The tooltip and name deltas listed in MICROCOPY were already shipped by their wiring items (G-04e, G-28, WI-23b, WI-37), and the '[Hide]'/'[Show]' brackets by D-31.
|
|
1404
|
+
- Every key can still be overridden with `AIConfigProvider strings`; a component's own prop (InsightEngine `placeholder`, AIRelatedQuestions `title`, AIVoiceOverlay `listeningText`, AIListeningIndicator `text`) still wins.
|
|
1405
|
+
|
|
1406
|
+
React only: string defaults are not in the CSS package (it carries tokens and themes, not copy). No token, role or theme change.
|
|
1407
|
+
|
|
1408
|
+
Stories (light, dark; text only, no layout change): AI/AIChainOfThought (the toggle, artifacts), AI/InsightEngine (history labels, composer placeholder), AI/AIRelatedQuestions, AI/Overview (panel history search and count, the listening indicator), AI/InsightEngineInput Compact (its explicit placeholder uses the ellipsis character), and the AI panel stories that show these surfaces.
|
|
1409
|
+
|
|
1410
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M8-index.md (light, dark; RTL, the docked 320px and 800px panel and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1411
|
+
- **Additive**: NEW `favorite` color role in the React theme and the CSS package. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1412
|
+
|
|
1413
|
+
**WH-12 (favorite role).** NEW `favorite` color role for a pinned, starred or favorite mark, in the React theme and the CSS package.
|
|
1414
|
+
|
|
1415
|
+
The role colors a state glyph such as the filled star of a starred thread. It references the mode's warning text (light `#B45309`, dark `#FDE68A`), so it reaches 3:1 on every Trinity surface (4.56:1 or more in light mode, 12.11:1 or more in dark mode), but it is its own role: restyling the status roles does not change what the star means.
|
|
1416
|
+
|
|
1417
|
+
- React: `palette.trinity.favorite` in every Trinity theme and preset, and `favorite` on `getTrinityPalette(theme)` / `useTrinityPalette()`. Under a plain MUI theme (`createTheme()`) it falls back to the resolver's readable warning text.
|
|
1418
|
+
- Tokens: optional `semantic.colors.favorite` and `darkMode.colors.favorite`, each equal to the mode's `status.warning.text`. An override of the warning text reaches the role unless `favorite` itself is overridden.
|
|
1419
|
+
- CSS: `--trinity-favorite`, a `var()` alias of `--trinity-status-warning-text`, declared in `:root` and re-declared in the dark, light-island and system-dark blocks; the runtime CSS functions emit `--trinity-semantic-favorite` the same way. `INSTRUCTIONS_CSS.md` ("Colors — favorite") shows the filled and outline star pattern.
|
|
1420
|
+
|
|
1421
|
+
Draw the pinned state as a filled icon and name the state on the control, so color is not the only cue.
|
|
1422
|
+
- **Additive**: AIMessage gains badges, feedbackControl, attachments and app actions, forwards the HITL block props, and shows a streaming placeholder; UserMessage takes an avatar. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1423
|
+
|
|
1424
|
+
**G-11.** AIMessage has slots for badges, a feedback control, attachments and app actions, and passes the HITL block props to its renderer.
|
|
1425
|
+
|
|
1426
|
+
- NEW `badges?: ReactNode`: non-interactive content at the footer's start, before the timestamp (AIConfidenceBadge, AIVerificationBadge).
|
|
1427
|
+
- NEW `feedbackControl?: ReactNode`: replaces the built-in thumbs in the actions group (for example a detailed AIFeedbackControl), also with `showFeedback={false}`. Not the same as `slotProps.feedbackControl`, which still styles the built-in thumbs.
|
|
1428
|
+
- NEW `attachments?: ReactNode`: content below the response inside the content column, aligned with the text (AISourcesPanel, AIRelatedQuestions, AIChainOfThought reasoning).
|
|
1429
|
+
- NEW `actions?: AIMessageAction[]`: app actions such as "Add to golden dataset", rendered as icon buttons after the built-in ones with the same markup, focus ring and 24px target. `hidden` removes one; `disabled` makes it `aria-disabled` (it keeps focus) and `disabledReason` is its description and shows in its tooltip; `pressed` makes it a toggle (`aria-pressed`, with a fill and an inset ring, an inset Highlight outline under forced colours); a promise from `onClick` makes it `aria-busy` until it settles, and a rejection calls `onActionError(error, { source: 'action', actionId: id })`. The tooltip is the label (one string). Without `icon`, the generic action glyph (`zap`) shows.
|
|
1430
|
+
- NEW type `AIMessageAction` (`{ id; label; icon?; onClick; disabled?; disabledReason?; hidden?; pressed? }`), exported from the package root.
|
|
1431
|
+
- NEW `blockRenderers`, `onBlockEvent` and `readOnly`, passed to AIResponseRenderer with `onActionError` (an action inside a block reports `{ source: 'block', blockId }`). AIMessage never reads `response.metadata`.
|
|
1432
|
+
- `slotProps` gains `badges`, `actions` (the actions group; its role stays) and `attachments`.
|
|
1433
|
+
- Rendering rule: AIMessage renders when it has a response, children, attachments or badges (before: response or children only). A message with only attachments is valid.
|
|
1434
|
+
- Narrow hosts: with a slot present, the content column gets an inner `container-type: inline-size` wrapper and, below the compact container width (360px), the badges take their own row. Without any slot the DOM is unchanged.
|
|
1435
|
+
|
|
1436
|
+
Docs: AIActions' docs no longer say AIMessage wraps AIActions as its `actions` prop: AIMessageAction is data AIMessage renders; AIActions / AISuggestedAction are follow-up suggestions.
|
|
1437
|
+
|
|
1438
|
+
New exports and props are `@since 2.13.0`. React only: no token, role or default change, so the CSS package is unchanged. No new string key (an app action's label is the app's).
|
|
1439
|
+
|
|
1440
|
+
Existing AIMessage stories render the same (no PNG change); new stories: GoldenDatasetAction (+ Dark), PanelDockedNarrow (+ Dark, Rtl), PanelDockedWide, PanelFullscreen (+ Dark, Rtl).
|
|
1441
|
+
|
|
1442
|
+
**G-13 (AIMessage half).** AIAvatar's size map and AIMessage's content inset become the one source of the message avatar column (G-13, AIMessage half).
|
|
1443
|
+
|
|
1444
|
+
- `AI_AVATAR_SIZE_PX` (AIAvatar.tsx) and `getMessageContentInset(theme)` (AIMessage.tsx, `calc(32px + theme.spacing(aiSpacing.md))`) are `@internal` and registered in the internal registry (ADR-02); no package entry exports them. ChatThread uses them in its half of G-13 to align thread-level items with the content column.
|
|
1445
|
+
|
|
1446
|
+
No public API or visual change.
|
|
1447
|
+
|
|
1448
|
+
**G-38+ (message side).** AIMessage shows a placeholder while it streams with no content yet, and UserMessage takes a custom avatar.
|
|
1449
|
+
|
|
1450
|
+
- AIMessage: while `streaming` and with no content yet (no response block with content, and no children or an empty-string child), it shows two decorative AIShimmer lines (`aria-hidden`, never announced; the article is already `aria-busy`; the sweep stops under reduced motion). Before, `<AIMessage streaming />` with no response or children rendered nothing. A host's typing entry can now be `<AIMessage streaming />`. Streaming with content renders as before.
|
|
1451
|
+
- NEW UserMessage `avatar?: ReactNode`: replaces the default icon avatar, for example `<UserAvatar size="small" initials="JD" />`; without it the DOM is unchanged. Make a custom avatar fill the 32px avatar column, as AIMessage's `avatar` does (now documented).
|
|
1452
|
+
|
|
1453
|
+
New props are `@since 2.13.0`. React only: no token, role or default change, so the CSS package is unchanged.
|
|
1454
|
+
|
|
1455
|
+
Behaviour change (G-29): an app that rendered `<AIMessage streaming />` without content and relied on it rendering nothing now sees the placeholder; pass `streaming={false}` or skip the message until content arrives.
|
|
1456
|
+
|
|
1457
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M8-index.md (light, dark; RTL, the docked 320px and 800px panel and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1458
|
+
- **Additive**: AIRelatedQuestions shows verified suggestions and reports the selected question as an object. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1459
|
+
|
|
1460
|
+
**G-24a.** AIRelatedQuestions shows verified suggestions and reports the selected question as an object.
|
|
1461
|
+
|
|
1462
|
+
- WIDENED `questions`: `Array<string | AIRelatedQuestion>`. Strings and objects can be mixed; a list of strings renders exactly as before.
|
|
1463
|
+
- NEW type `AIRelatedQuestion` (`{ id?: string; text: string; verified?: boolean }`), exported from the package root.
|
|
1464
|
+
- A verified question shows AIVerificationBadge (inline, small, status `verified`) after its text, inside the question's button: a glyph and the word "Verified" (the `ai.verificationBadge.status.verified` string), never colour alone, and part of the button's accessible name. The badge adds no live region.
|
|
1465
|
+
- NEW `onQuestionSelect(question, index)`: fires alongside the unchanged `onQuestionClick(text)`; a string question arrives as `{ text }`.
|
|
1466
|
+
- List keys use the question's `id`, else its text and occurrence, so focus stays with the same question when the list changes.
|
|
1467
|
+
- Telemetry is unchanged: ChatThread, which owns the follow-up callback, keeps emitting `followup.select` (G-OD16); AIRelatedQuestions emits nothing.
|
|
1468
|
+
- `AIResponse.relatedQuestions` is not widened (ADR-05-R2, G-OD8). ChatThread and InsightEngine pass verified questions through in a later change (G-24b).
|
|
1469
|
+
|
|
1470
|
+
Type change (G-29): code that reads `AIRelatedQuestionsProps['questions']` as `string[]` needs to narrow each item (`typeof q === 'string' ? q : q.text`).
|
|
1471
|
+
|
|
1472
|
+
New exports and props are `@since 2.13.0`. React only: no token, role or default changes, so the CSS package is unchanged.
|
|
1473
|
+
|
|
1474
|
+
A list of strings renders exactly as before (no PNG change); only questions passed as objects with `verified: true` show the new badge.
|
|
1475
|
+
|
|
1476
|
+
Type change (G-29 type-change note): `AIRelatedQuestionsProps['questions']` widens from `string[]` to `Array<string | AIRelatedQuestion>`. Nothing changes for code that passes strings; code that reads the prop type as `string[]` narrows each item (`typeof q === 'string' ? q : q.text`).
|
|
1477
|
+
- **Additive**: NEW AIPlanReview and hitlBlockRenderers: the plan to confirm before a query runs, and the built-in renderers for interactive response blocks. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1478
|
+
|
|
1479
|
+
**G-18 and G-43+ (steps 1-3).** NEW `AIPlanReview`: the plan the AI proposes before it runs a query, for the user to review, correct and confirm (requirements R1-R10). No query runs until the user confirms.
|
|
1480
|
+
|
|
1481
|
+
- Steps as an ordered list: a visible number, the status as an icon plus visible text ("Ready", "Needs your input", "Edited", "Updating"), the confidence (AIConfidenceBadge, against `thresholds` or the provider's), and chips "Metric: …", "Field: …", "Filter: …", "Period: …".
|
|
1482
|
+
- Inline edit for steps with `editable: true`: an "Edit" button named "Edit step N", Enter saves through `onStepEdit` (a promise drives pending and the error), Escape discards; an empty step is invalid.
|
|
1483
|
+
- An embedded clarifying question per step (`clarification`); answers are `clarificationAnswers` (controlled) or `defaultClarificationAnswers`, and `onClarificationChange(stepId, answer)` fires per committed answer, once per distinct answer.
|
|
1484
|
+
- Deadlock-free gating: Confirm and run waits only for something the user can do (answer a needs-input step's question, or edit an editable needs-input step) or for data (streaming, replanning). Low confidence alone shows a warning and never blocks. Confirm is `aria-disabled` with the visible reason linked, never `disabled`.
|
|
1485
|
+
- Replans keep `plan.id` and bump `revision`: edited steps are counted across revisions (`AIPlanConfirmEvent.edited`, `editedStepIds`); edit and answer drafts reset per revision.
|
|
1486
|
+
- `onConfirm(event: AIPlanConfirmEvent)` and `onCancel()` through the async contract: pending keeps focus and sets `aria-busy`; success shows the resolved summary and focuses it; failure shows an inline alert with Try again (`onActionError(error, { source: 'confirm' })`).
|
|
1487
|
+
- `presentation="dialog"`: a card with a "Review plan" trigger that opens the plan in a Modal (Escape and the backdrop are blocked while the confirm runs); `open`, `defaultOpen`, `onOpenChange(open, reason)` with `'confirmed'` and `'cancelled'` among the reasons.
|
|
1488
|
+
- `streaming`, `readOnly`, plan statuses `replanning`, `superseded`, `cancelled` (read-only with text), `confirmed` and `running` (the resolved summary). Announces "Plan's ready — needs your confirmation" once when a streamed plan becomes ready.
|
|
1489
|
+
- Props: `headingLevel` (default 3), `size`, `summary`, `detailsDefaultExpanded`, `confirmLabel`, `cancelLabel`, `gatingNote`, `blockId`, `className`, `sx`, `slotProps` (`header`, `steps`, `step`, `footer`, `details`, `dialog`) and HTML attributes on the root.
|
|
1490
|
+
- Strings: the new `ai.planReview` group (40 keys) through `AIConfigProvider`; "Confirm and run", "No query runs until you confirm the plan." and "Plan's ready — needs your confirmation" are requirement copy.
|
|
1491
|
+
- Telemetry (`AIPlanInteraction`): step_edit, confirm, cancel, clarify; ids, counts and flags only.
|
|
1492
|
+
- Contract (types/hitl.ts header): a plan step below the medium confidence threshold must carry `clarification: { reason: 'low-confidence' }`; AIPlanReview warns in development when one does not.
|
|
1493
|
+
|
|
1494
|
+
New exports `AIPlanReview`, `AIPlanReviewProps`, `AIPlanConfirmEvent`, `AIPlanReviewOpenChangeReason`, all `@since 2.13.0`. React only: no token, role or default change, so the CSS package is unchanged (components and copy are not part of it).
|
|
1495
|
+
|
|
1496
|
+
**G-12 (renderer half).** NEW `hitlBlockRenderers`: the built-in renderers for the interactive response blocks. Pass it as `blockRenderers` to AIResponseRenderer (or spread it into your own map):
|
|
1497
|
+
|
|
1498
|
+
- `query` → AIQueryDisclosure (sends `query.copy`)
|
|
1499
|
+
- `clarification` → AIClarificationPrompt (sends `clarification.submit`)
|
|
1500
|
+
- `plan-review` → AIPlanReview (sends `plan.confirm`, `plan.stepEdit`, `plan.cancel`)
|
|
1501
|
+
|
|
1502
|
+
A module-scope, frozen constant (a stable identity, so blocks never remount). Each block streams with its controls unavailable and announces once when it becomes interactive; a recorded answer or a resolved plan renders read-only, so a reloaded thread is not interactive; with no `onBlockEvent` the blocks render read-only and a development warning names the missing handler; after a send, focus moves to the block's resolved summary. A rejected event shows the block's error and reaches `onActionError` (`{ source: 'confirm', blockId }` for a plan confirm, `{ source: 'block', blockId }` otherwise).
|
|
1503
|
+
|
|
1504
|
+
Correction in the same change: a clarification block whose answer the app writes back into the data (`resolution`) after a send no longer logs two MUI controlled/uncontrolled errors; the summary keeps focus.
|
|
1505
|
+
|
|
1506
|
+
New export `hitlBlockRenderers`, `@since 2.13.0`. React only; no token, role or default change.
|
|
1507
|
+
|
|
1508
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M8-index.md (light, dark; RTL, the docked 320px and 800px panel and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1509
|
+
- **Additive**: `AIBlockEvent` gains `plan.clarify` and `plan.confirm.clarificationAnswers` (lands only after ADR-05-R6 is ratified). ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1510
|
+
|
|
1511
|
+
**G-41+.** `AIBlockEvent` widens (an open union; handlers with a `default` branch keep compiling):
|
|
1512
|
+
|
|
1513
|
+
- NEW member `{ type: 'plan.clarify'; blockId; planId; revision?; stepId; answer: AIClarificationAnswer }`: the user answered a plan step's question before confirming, so the backend can replan (a new revision of the same plan). The plan-review block renderer sends it once per distinct committed answer.
|
|
1514
|
+
- `plan.confirm` gains optional `clarificationAnswers?: Record<string, AIClarificationAnswer>`, equal to AIPlanReview's `AIPlanConfirmEvent.clarificationAnswers`.
|
|
1515
|
+
|
|
1516
|
+
If ADR-05-R6 is rejected, the one G-41+ commit is reverted and the union keeps its five members.
|
|
1517
|
+
- **Behavior correction (b, c)**: AppLayout's thread history sidebar and AI FAB read their colours, sizes and strings from the theme's roles and the AI strings catalog. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1518
|
+
|
|
1519
|
+
**WH-12 (ThreadHistorySidebar, AIFab).** ThreadHistorySidebar and AIFab read their AI colours through the palette resolver (getTrinityPalette), so the dark theme, the presets, a brand override and a plain `createTheme()` all reach them.
|
|
1520
|
+
|
|
1521
|
+
- ThreadHistorySidebar: the dark-only 95% paper and the `palette.mode` branch go (the root is `background.paper`). The selected row is `ai.primary` at the theme's selected overlay opacity (light 0.08, unchanged; dark 0.16 of the dark `ai.primary`, was 0.15 of the light purple) and selected + hover opacity under the pointer (was no hover change). The selected border, the selected row's glyph and the focused search outline are `ai.primary` (3:1 or more on the paper and on the tint, both modes, every preset). The selected border is now `border-inline-start` and the root's divider `border-inline-end`.
|
|
1522
|
+
- ThreadHistorySidebar star (D13 revised): a starred thread's filled star uses the `favorite` role (ADR-03-R10, light #B45309, dark #FDE68A) instead of the '#F59E0B' literal, read-only and on the pressed Star toggle; the filled glyph stays the non-colour cue. (Applied at integration with W1-F's role: wire patch H-12-star-ThreadHistorySidebar.patch.)
|
|
1523
|
+
- AIFab keeps its brand gradient (owner decision 2026-10-09): `ai.gradient.brandDiagonal`, coral 800 to purple 700 at 135deg, the same in light and dark, under the sparkle in `ai.onPrimaryFill`, with no ring; over the sparkle's box white is 3.5:1 or more at every size (2.97:1 only at the top-left corner the icon never reaches). The glow is `ai.focusRing` (was the static coral and purple glow). The disabled FAB keeps `action.disabledBackground`, without the gradient, and draws its sparkle in `action.disabled`.
|
|
1524
|
+
|
|
1525
|
+
No API change. React only: every role already has its CSS variable (`--trinity-semantic-ai-*`, W1-F's `--trinity-favorite`), so the CSS package does not change.
|
|
1526
|
+
|
|
1527
|
+
Theming ratchet: the two files' color-source and mode-branch rows reach 0 (`ai-colour-sources` 26 -> 16).
|
|
1528
|
+
|
|
1529
|
+
**WH-13 (ThreadHistorySidebar, AIFab, AppLayout).** ThreadHistorySidebar, AIFab and AppLayout's AI panel labels read the AI strings catalog (`useAIStrings`), so an `AIConfigProvider` translates or white-labels them.
|
|
1530
|
+
|
|
1531
|
+
- ThreadHistorySidebar reads `ai.threadHistorySidebar.*`: `title`, `newThread`, `closeHistory`, `searchPlaceholder`, `searchLabel`, `starThread`, `rename`, `delete`, `threadName`, `threadNameRequired`, `emptyHistory`, `emptySearch` and `threadCount` (a function of `{ count }`, so an override pluralizes with its own `Intl.PluralRules`). Each tooltip and its button's name come from one key.
|
|
1532
|
+
- AIFab's tooltip and name default to `ai.fab.tooltip`; the `tooltip` prop still wins (ADR-06 (9)).
|
|
1533
|
+
- AppLayout names the `renderAiPanel` landmark and the mobile drawer from `ai.appLayout.panelLabel` and passes the AI panel's resize handle name (ResizablePanel `labels.resizeHandle`) from `ai.appLayout.resizeHandle`.
|
|
1534
|
+
- NEW keys (`@since 2.13.0`): `ai.threadHistorySidebar.starThread` ('Star thread'), `ai.threadHistorySidebar.threadName` ('Thread name'), `ai.threadHistorySidebar.threadNameRequired` ('Enter a thread name.'), `ai.appLayout.panelLabel` ('AI assistant').
|
|
1535
|
+
|
|
1536
|
+
Defaults are verbatim: no rendered text changes. The recorded alignment deltas ('Search threads…', '1 thread') are the MICROCOPY alignment item's. React only: strings are not part of the CSS package.
|
|
1537
|
+
|
|
1538
|
+
**WH-48.** ThreadHistorySidebar's type and icon sizes come from roles instead of literals.
|
|
1539
|
+
|
|
1540
|
+
- The group label: 0.7rem (11.2px) -> the aiTypography `meta` size (11px); its letter spacing 0.5px -> `letterSpacing.wider` (0.05em, 0.55px).
|
|
1541
|
+
- The thread preview: 0.7rem (11.2px, below the caption scale) -> the aiTypography `caption` size (12px).
|
|
1542
|
+
- The row glyph: 18px -> Icon size `control` (16px); the empty-state folder: 40px -> `display` (36px), the nearest intents docs/remediation/icon-size-map.md proposes.
|
|
1543
|
+
|
|
1544
|
+
No API change. React only: the sizes are existing tokens with CSS variables, so the CSS package does not change.
|
|
1545
|
+
|
|
1546
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M8-index.md (light, dark; RTL, the docked 320px and 800px panel and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1547
|
+
- **Behavior correction (b)**: ChatThread hosts the HITL contract: message slots and badges, interactive blocks rendered by default, verified related questions, detailed feedback, auto-scroll, `ChatThreadMessage`, and one re-render per streamed token. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1548
|
+
|
|
1549
|
+
**G-13 (ChatThread, step i).** ChatThread fills each assistant message's slots and hosts interactive blocks.
|
|
1550
|
+
|
|
1551
|
+
- Per-message slot mapping: `response.metadata.confidence` shows an AIConfidenceBadge (with `metadata.confidenceRationale` as its rationale) and `response.metadata.verification` an AIVerificationBadge, in the message's `badges`; the message's sources and related questions are its `attachments`. Without `sources` / `relatedQuestions` on the message, `response.sources` (through `toSourceCategories`) and `response.relatedQuestions` are shown; when both are present the message's own win, with a development warning.
|
|
1552
|
+
- NEW `onBlockEvent(message, event)`: what the user did in an interactive block, with its message. The returned promise drives the block's pending state; a rejection shows the block's error and reaches `onActionError` once.
|
|
1553
|
+
- NEW `blockRenderers` and `renderBlock`, passed to each message's AIResponseRenderer.
|
|
1554
|
+
- NEW `getMessageSlots(message, defaults)`: override (a node), empty (`null`) or extend (`defaults`) the `badges`, `feedbackControl` and `attachments` of a message. A one-argument function still fits (D26b). This is the supported way to put AIChainOfThought reasoning into a message: `attachments: <>{reasoning}{defaults.attachments}</>`.
|
|
1555
|
+
- NEW `messageActions.getActions(message)`: app actions (AIMessage `actions`) after the built-in ones.
|
|
1556
|
+
- NEW `onActionError(error, context)`: a failed Regenerate, Copy, app action or block event, with the message's `messageId` added to the context.
|
|
1557
|
+
- NEW `ChatMessage.feedback`: the rating already given; it seeds the thumbs (AIMessage `defaultFeedback`), so writing the rating back after a click raises no controlled-switch warning.
|
|
1558
|
+
- NEW type `ChatThreadMessageSlots`.
|
|
1559
|
+
- Heading outline (D27): with `headingLevel`, a response's markdown headings move down by the same number of levels, so `#` sits one level under its message heading (`headingLevel={3}` under a panel's h2: `#` is an h4). Without `headingLevel` nothing changes.
|
|
1560
|
+
- An inner `container-type: inline-size` wrapper holds the messages inside the log (the ref stays on the log); suggested-action keys are the label plus the index.
|
|
1561
|
+
|
|
1562
|
+
Visible changes (G-OD11, release-note rows):
|
|
1563
|
+
- Per-message sources and related questions move about 10px into the content column: they start where the message text starts (the 32px avatar column plus the gap, 44px), not at 33.5px; they now sit inside the message article, above its footer. Thread-level related questions and suggested actions start at the same 44px, in both directions (logical inset).
|
|
1564
|
+
- Existing `metadata.confidence` / `metadata.verification` payloads start showing a badge in the message footer. Suppress with `getMessageSlots={() => ({ badges: null })}`.
|
|
1565
|
+
- A message whose response carries `sources` or `relatedQuestions` (and none of its own) now shows them.
|
|
1566
|
+
|
|
1567
|
+
New props and types are `@since 2.13.0`. React only: no token, role, default or string change, so the CSS package is unchanged.
|
|
1568
|
+
|
|
1569
|
+
**G-14 (ChatThread side).** ChatThread's message type is exported from the package root as `ChatThreadMessage`.
|
|
1570
|
+
|
|
1571
|
+
- NEW root type exports `ChatThreadMessage` (ChatThread's and InsightEngine's message: today's shape plus G-13's `feedback`) and `ChatThreadMessageSlots` (G-13).
|
|
1572
|
+
- `ChatThreadMessageActions` is generic in its message type: `ChatThreadMessageActions<TMessage extends { id: string } = ChatThreadMessage>`, so a host with its own message shape types its callbacks with the same interface. Existing uses (no type argument) are unchanged.
|
|
1573
|
+
- The root `ChatMessage` is still AppLayout's AI panel message and is unchanged. It is a different shape (`timestamp: Date`, `isLoading`): map it to `ChatThreadMessage` explicitly; it is not assignable as is.
|
|
1574
|
+
- Internally ChatThread's message type was named `ChatMessage`, which no package entry could import (the root name resolved to AppLayout's type); it is renamed with no alias. Code snippets in the ChatThread stories import `ChatThreadMessage`.
|
|
1575
|
+
|
|
1576
|
+
MIGRATION row: a snippet that imported `ChatMessage` from '@trinityui/design-system' for ChatThread or InsightEngine got AppLayout's panel type; import `ChatThreadMessage` instead.
|
|
1577
|
+
|
|
1578
|
+
`@since 2.13.0`. React only: types, no CSS package change.
|
|
1579
|
+
|
|
1580
|
+
**G-38+ (thread side).** ChatThread is a complete host for the AI panel's conversation (G-38+, thread side).
|
|
1581
|
+
|
|
1582
|
+
- NEW `slotProps.content`: the messages' container inside the log (`container-type: inline-size`). Constrain it there (a boxed layout's `maxWidth` with `marginInline: 'auto'`); the log stays the scroll container, so its scrollbar stays at the host's edge.
|
|
1583
|
+
- NEW auto-scroll (ADR-19 (4), reverses the plan's "left alone" entry): when the messages change, the log scrolls to its end only while the reader is at the end. Scrolling up stops following; scrolling back to the end resumes it. Smooth, instant under reduced motion; focus never moves; only the log scrolls, never the page. A consumer `onScroll` on the root still runs. Visible: a thread taller than its host now opens scrolled to its latest message (InsightEngine's side panel, expanded view and history stories show the end of the conversation instead of its start).
|
|
1584
|
+
- NEW `assistantAvatar` / `userAvatar`: forwarded to AIMessage `avatar` and UserMessage `avatar`. Make them fill the 32px avatar column.
|
|
1585
|
+
- An assistant message with empty `content` (and no response) passes no children to AIMessage: a streaming one shows AIMessage's decorative typing placeholder; a complete one renders nothing. Before, an empty Typography was rendered.
|
|
1586
|
+
- The log has `min-height: 0`, so in a flex column host it scrolls itself instead of growing past the host.
|
|
1587
|
+
|
|
1588
|
+
`@since 2.13.0`. React only: no token, role, default or string change, so the CSS package is unchanged.
|
|
1589
|
+
|
|
1590
|
+
**G-24b (ChatThread half).** ChatThread passes verified related questions through (G-24b, ChatThread half).
|
|
1591
|
+
|
|
1592
|
+
- WIDENED `ChatThreadMessage.relatedQuestions` and `ChatThreadProps.relatedQuestions` to `Array<string | AIRelatedQuestion>`: strings still work; an object adds an id and the `verified` flag (the Verified badge, part of the button's name).
|
|
1593
|
+
- NEW `onRelatedQuestionSelect(question, index)`, forwarded to AIRelatedQuestions' `onQuestionSelect` (a string is passed as `{ text }`), called after `onRelatedQuestionClick`, which is unchanged (the text).
|
|
1594
|
+
- Telemetry: ChatThread keeps owning `followup.select` (one event per pick) and now reports the picked question's `verified` flag and its index in its list, instead of a fixed `verified: false`.
|
|
1595
|
+
|
|
1596
|
+
Readers of the widened fields need narrowing (`typeof q === 'string' ? q : q.text`): listed for G-29.
|
|
1597
|
+
|
|
1598
|
+
`@since 2.13.0`. React only: no CSS package change.
|
|
1599
|
+
|
|
1600
|
+
**G-32 (ChatThread half).** Detailed feedback in the composed chat (G-32, R18, R20, R21, R22): one prop.
|
|
1601
|
+
|
|
1602
|
+
- NEW `messageActions.onFeedbackSubmit(message, event: AIFeedbackEvent)`. When passed, each assistant message shows AIFeedbackControl in detailed mode in its `feedbackControl` slot instead of the plain thumbs: after a thumbs-down, 'Tell us what went wrong' opens the form (categories, a comment, 'Rerun with this correction', which sets `event.rerun`). The rating still reaches `onFeedback(message, rating)`; a message's `feedback` seeds it. Return your request's promise: the form stays open while it runs; a rejection shows the form's error ("Couldn't send feedback.", Try again) and reaches `onActionError` with `{ source: 'action', actionId: 'feedback', messageId }`. The control's interaction events carry the message id. While a message streams the control is unavailable (aria-disabled, with the control's reason).
|
|
1603
|
+
- `getMessageSlots` replaces it (a node) or removes it (`feedbackControl: null`) per message.
|
|
1604
|
+
- InsightEngine forwards `messageActions` unchanged, so it gets the feature with no new prop; AppLayout's panel inherits it through `panelProps`.
|
|
1605
|
+
- Without `onFeedbackSubmit` nothing changes: the built-in simple thumbs show when `onFeedback` is passed.
|
|
1606
|
+
|
|
1607
|
+
Known: AIFeedbackControl also passes a rejected submit to `globalThis.reportError` (it has no error sink of its own), so a failure reaches both `onActionError` and `reportError`.
|
|
1608
|
+
|
|
1609
|
+
New stories: FeedbackFlagAndRerun (+ Dark), FeedbackReviewQueueHandoff.
|
|
1610
|
+
|
|
1611
|
+
`@since 2.13.0`. React only: no token, role, default or string change, so the CSS package is unchanged.
|
|
1612
|
+
|
|
1613
|
+
**G-13 (step ii).** ChatThread renders the interactive blocks by default (G-13 step (ii)).
|
|
1614
|
+
|
|
1615
|
+
- ChatThread provides `hitlBlockRenderers` to every message: a `query` block renders as AIQueryDisclosure, a `clarification` block as AIClarificationPrompt and a `plan-review` block as AIPlanReview, with no setup. Their events reach `onBlockEvent(message, event)`; without `onBlockEvent` they render read-only (with the renderers' development warning).
|
|
1616
|
+
- `blockRenderers` merges over the defaults: an entry wins for its type, and `null` turns a default off (`blockRenderers={{ clarification: null }}`). The merged map is rebuilt only when the consumer's object changes (keep it at module scope or in `useMemo`).
|
|
1617
|
+
- Before, a response with these blocks rendered nothing for them inside ChatThread unless the app passed renderers.
|
|
1618
|
+
|
|
1619
|
+
New stories: WithHITLBlocks; the thread in AppLayout's AI panel: PanelDockedNarrow, PanelDockedWide, PanelFullscreen (each + Dark, Rtl); DockedPanelWidths gains the interactive blocks and the response's sources.
|
|
1620
|
+
|
|
1621
|
+
React only: no CSS package change.
|
|
1622
|
+
|
|
1623
|
+
**G-28 (ChatThread literals).** ChatThread reads its two texts from the AI strings catalog (G-28, ChatThread literals).
|
|
1624
|
+
|
|
1625
|
+
- The log's name 'Chat messages' reads `ai.chatThread.logLabel` and the default empty state's 'Start a conversation' reads `ai.chatThread.empty`, through `useAIStrings` (keys since E-25; defaults verbatim, so nothing renders differently). An AIConfigProvider can translate or rebrand them; the nearest provider wins; a per-instance `aria-label` still wins over both.
|
|
1626
|
+
- ChatThread holds no user-facing literal any more (the inventory is clean for ChatThread.tsx; the two map rows move to the keys without a literal).
|
|
1627
|
+
|
|
1628
|
+
React only: string keys are React-side; the CSS package carries no copy.
|
|
1629
|
+
|
|
1630
|
+
**G-40+.** ChatThread re-renders only the message that changes (G-40+).
|
|
1631
|
+
|
|
1632
|
+
- Each message renders in a memoized row: while one message streams, the other rows do not re-render. In a 200-message thread a streamed token costs about 2 ms instead of 130 ms in the jsdom measurement (docs/remediation/G-perf-budget.md; recorded, not gated).
|
|
1633
|
+
- Event handlers (`messageActions` callbacks, `onBlockEvent`, `onActionError`, `onRelatedQuestionClick`, `onRelatedQuestionSelect`) may be inline: ChatThread passes stable wrappers that call the latest handler. Render-time functions (`messageActions.getActions`, `getMessageSlots`, `renderBlock`) and the avatar elements are compared by identity: keep them stable in long threads.
|
|
1634
|
+
- Keep a message's object while it does not change (a new object for the streaming message only), as the docs recommend.
|
|
1635
|
+
|
|
1636
|
+
No virtualization. React only: no CSS package change.
|
|
1637
|
+
|
|
1638
|
+
Type changes (G-29 type-change note): `ChatThreadMessage` (renamed from ChatThread's unexported `ChatMessage`, no alias), `ChatThreadMessage.relatedQuestions` and `ChatThreadProps.relatedQuestions` widen to `Array<string | AIRelatedQuestion>`, and `ChatThreadMessageActions` is generic (`ChatThreadMessageActions<TMessage extends { id: string } = ChatThreadMessage>`).
|
|
1639
|
+
|
|
1640
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M8-index.md (light, dark; RTL, the docked 320px and 800px panel and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1641
|
+
- **Behavior correction (a, b)**: InsightEngine fits any host (embedded, phones), wires the HITL contract, sends asynchronously with a reported failure, and reads its texts from the AI strings catalog. ([`9aabd78`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9aabd782555d9206826b5155f541229741f3a60b))
|
|
1642
|
+
|
|
1643
|
+
**WH-15.** InsightEngine fits any host and works on phones.
|
|
1644
|
+
|
|
1645
|
+
- New `presentation?: 'drawer' | 'embedded'` (default `'drawer'`). `'embedded'` renders the header, body and composer with no drawer, filling the parent (width and height 100%, min-width 0, no fixed or viewport width): use it inside a host panel such as AppLayout's AI panel (`renderAiPanel`), a 320px column or a popover. A closed embedded panel stays mounted and hidden. The surface is a region named by the title.
|
|
1646
|
+
- In the drawer presentation, below the `sm` breakpoint the drawer is a full-width modal dialog named by the title: focus is trapped, Escape and the backdrop close it, and focus returns to the control that opened it. There is no expand toggle on phones. New `mobileVariant?: 'temporary' | 'persistent'` (default `'temporary'`) keeps the non-modal drawer instead. At and above `sm` the persistent 400/800px drawer is unchanged (its paper is now a region named by the title).
|
|
1647
|
+
- `onClose` receives the reason: `'closeButton'`, `'escapeKeyDown'` or `'backdropClick'`. A handler that takes no argument still fits.
|
|
1648
|
+
- New controlled `viewMode` / `onViewModeChange`; `defaultViewMode` still seeds the uncontrolled mode.
|
|
1649
|
+
- New `slotProps.root` and `slotProps.body` next to `paper`, `thread` and `composer`.
|
|
1650
|
+
- The ref target depends on the presentation: the docked Drawer root on desktop (as before), the Modal root on phones, the root `div` when embedded.
|
|
1651
|
+
|
|
1652
|
+
`presentation`, `mobileVariant`, `viewMode`, `onViewModeChange`, `slotProps.root` and `slotProps.body` are `@since 2.13.0`. React only: no token, theme or CSS package change.
|
|
1653
|
+
|
|
1654
|
+
**G-15.** InsightEngine wires the thread's HITL contract and makes `onError` real (G-15).
|
|
1655
|
+
|
|
1656
|
+
- New props forwarded to ChatThread: `emptyState`, `onBlockEvent`, `blockRenderers`, `renderBlock`, `getMessageSlots`, `headingLevel`.
|
|
1657
|
+
- While the latest answer asks a required clarification and `onBlockEvent` is wired, the composer is blocked with a visible reason, 'Answer the question above to continue' (`ai.blocks.answerToContinue`): Send is `aria-disabled` (never disabled) and described by the reason, Enter does not submit, and the field stays editable. A resolved answer opens it, with or without writing the answer back into `messages`; a rejected answer keeps it. Without `onBlockEvent` there is no gate.
|
|
1658
|
+
- New `composerBlockedReason?: string | null`: a string always blocks with that reason, `null` never blocks.
|
|
1659
|
+
- `onError` now fires: a failed message action or block event reaches it, always as an `Error` (a non-Error rejection is wrapped, with the value as `cause`). The failure still shows inline where it happened.
|
|
1660
|
+
- Focus stays in the composer after a send; a new or streaming response never moves it.
|
|
1661
|
+
|
|
1662
|
+
All new props are `@since 2.13.0`. React only: no token, theme or CSS package change.
|
|
1663
|
+
|
|
1664
|
+
**G-24b (InsightEngine half).** InsightEngine passes verified related questions through (G-24b, InsightEngine half).
|
|
1665
|
+
|
|
1666
|
+
- `relatedQuestions` widens to `Array<string | AIRelatedQuestion>`: `{ id, text, verified }` objects next to strings; a verified question shows the 'Verified' badge, part of its accessible name.
|
|
1667
|
+
- New `onRelatedQuestionSelect(question, index)`, forwarded to ChatThread: called with the picked question (a string as `{ text }`) and its index, after `onRelatedQuestionClick`, which is unchanged.
|
|
1668
|
+
|
|
1669
|
+
`onRelatedQuestionSelect` is `@since 2.13.0`. React only: no token, theme or CSS package change.
|
|
1670
|
+
|
|
1671
|
+
**G-32 (InsightEngine half).** InsightEngine supports detailed feedback with no new prop (G-32, InsightEngine half): `messageActions` is forwarded to ChatThread unchanged, so `messageActions.onFeedbackSubmit` renders the detailed AIFeedbackControl in each answer, and a failed submit shows the form's alert and reaches `onError` as an `Error`. Documented with a recipe story.
|
|
1672
|
+
|
|
1673
|
+
React only: no token, theme or CSS package change.
|
|
1674
|
+
|
|
1675
|
+
**WH-16.** InsightEngine sends asynchronously and reports a failed send (H-16).
|
|
1676
|
+
|
|
1677
|
+
- `onSendMessage` widens to `((value: string) => void) | ((value: string) => Promise<void>)` (additive: a handler written before, including one that returns some other value, still compiles). Return the promise of your request: while it is pending, Send stays in place `aria-disabled` and `aria-busy` with a spinner (never disabled, so focus stays) and Enter does not send again.
|
|
1678
|
+
- When it rejects or throws, an alert between the thread and the composer says "Your message wasn't sent. Check your connection and try again." (`ai.insightEngine.sendFailed`, new key) with 'Try again' (`ai.common.tryAgain`) that sends the same text again; it is announced assertively through the nearest live region and never takes focus. The composer text is not restored (a controlled `inputValue` stays as you set it).
|
|
1679
|
+
- `onError` keeps its `(error: Error) => void` type and now receives every failed send, always as an `Error` (a non-Error value wrapped, with `cause`). Without `onError` the error goes to `globalThis.reportError`; no unhandled rejection escapes.
|
|
1680
|
+
- `message.send` (onInteraction) is emitted after `onSendMessage` runs, as before; a send ignored while one is pending emits nothing, and 'Try again' emits it again.
|
|
1681
|
+
|
|
1682
|
+
`ai.insightEngine.sendFailed` is `@since 2.13.0`. React only: string keys are React-side; no token, theme or CSS package change.
|
|
1683
|
+
|
|
1684
|
+
Not in this change: InsightEngine does not forward `generating` / `onStop` (the Stop button is reachable on InsightEngineInput only).
|
|
1685
|
+
|
|
1686
|
+
**WH-17.** InsightEngine's composer placeholder is a prop (H-17, reduced to the placeholder).
|
|
1687
|
+
|
|
1688
|
+
- New `placeholder?: string`. Default: the `ai.insightEngine.placeholder` string ('Ask anything...', unchanged), from the nearest AIConfigProvider; the prop wins over the provider. It is also the field's accessible name while a reason blocks the composer.
|
|
1689
|
+
- Verified, no change: the history toggle is the shared Disclosure trigger (a native `button type="button"`, `aria-expanded` toggled by Enter and Space, `aria-controls` naming the collapse region), in both presentations.
|
|
1690
|
+
|
|
1691
|
+
`placeholder` is `@since 2.13.0`. React only: string keys are React-side; the CSS package carries no copy.
|
|
1692
|
+
|
|
1693
|
+
**G-28 (InsightEngine literals).** InsightEngine reads its texts from the AI strings catalog (G-28, InsightEngine literals).
|
|
1694
|
+
|
|
1695
|
+
- The title ('Insight Engine', `ai.insightEngine.title`), the expanded view's history label ('HISTORY', `ai.insightEngine.history`), the side panel's history toggle ('HISTORY (n)', `ai.insightEngine.historyWithCount({ count })`) and the composer placeholder ('Ask anything...', `ai.insightEngine.placeholder`, since H-17) read through `useAIStrings`, so an AIConfigProvider can translate or rebrand them; the nearest provider wins; the `title` and `placeholder` props still win over both. Defaults are verbatim: nothing renders differently.
|
|
1696
|
+
- The two history labels are upper case from CSS (`text-transform: uppercase`), so the label keeps its look whatever the string's case (the MICROCOPY pass may change the default text to 'History' without a visual change).
|
|
1697
|
+
- InsightEngine holds no user-facing literal any more (the strings inventory is clean for InsightEngine.tsx; the four map rows move to the keys without a literal).
|
|
1698
|
+
|
|
1699
|
+
React only: string keys are React-side; the CSS package carries no copy.
|
|
1700
|
+
|
|
1701
|
+
G-14 (InsightEngine side) and G-16e carry no separate entry: G-14 renames InsightEngine's internal uses with ChatThread's (pr8), and G-16e moves the `MOCK_MESSAGES` demo array out of the component (it was never a package export). Type change (G-29): `InsightEngineProps.relatedQuestions` widens to `Array<string | AIRelatedQuestion>`.
|
|
1702
|
+
|
|
1703
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M8-index.md (light, dark; RTL, the docked 320px and 800px panel and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
1704
|
+
- **Behavior correction (a, b, e)**: ListDetailTemplate is accessible, generic, controllable and ready for queues (H-19, H-43, H-20, H-21, H-22). ([`e76442e`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e76442eb90baf9d5da9731920d13b37dd0cfe8d3))
|
|
1705
|
+
|
|
1706
|
+
**WH-19.** ListDetailTemplate is accessible: named icon buttons and search, a valid list, aria-current selection, unread and status as text, a busy loading state, and a badge of 0 that renders in its chip.
|
|
1707
|
+
|
|
1708
|
+
- NEW `labels?: Partial<ListDetailTemplateLabels>` (exported type, `@since 2.13.0`; NAMING.md "Label overrides (non-AI)"): `search`, `clearSearch`, `filterButton({ count })`, `back`, `unread`, `status` (per value), `loading` and `detail`. `searchPlaceholder` and `emptyListMessage` stay flat props.
|
|
1709
|
+
- Clear search, filter and the mobile Back button have an accessible name and a tooltip with the same text. The filter name carries the active count ("Filters, 2 active"); the visible badge is hidden from screen readers.
|
|
1710
|
+
- The search field is `type="search"` with an accessible name.
|
|
1711
|
+
- Each item is an `li` of the list; separators are a border on each `li` instead of `<li class="MuiDivider-root">` elements, with the same look.
|
|
1712
|
+
- The selected item has `aria-current="true"`; unread and status are read as visually hidden text after the title.
|
|
1713
|
+
- While `loading`, the list is `aria-busy` with visually hidden "Loading" text.
|
|
1714
|
+
- A `badge` of `0` renders inside its chip (it rendered a bare "0" next to the title); an empty-string badge renders nothing.
|
|
1715
|
+
- The avatar image is decorative (`alt=""`); the detail's scroll area takes focus only while its content scrolls; icons render through `Icon` (the Back arrow mirrors right to left); the selected accent and the touched spacing use logical properties.
|
|
1716
|
+
|
|
1717
|
+
**WH-43.** ListDetailTemplate shows listActions without the search, shows its empty-detail placeholder whenever nothing is selected, adds list empty and error slots, and is generic over its item type.
|
|
1718
|
+
|
|
1719
|
+
- `listActions` render whenever provided; with `showSearch={false}` they get their own row in the list header (they disappeared).
|
|
1720
|
+
- The detail pane follows the selection: with no `selectedId` it shows `emptyDetailContent` (or the default placeholder) even when `detailContent` is set.
|
|
1721
|
+
- NEW `listEmptyContent` (replaces the `emptyListMessage` text when the list is empty after the search) and `listErrorContent` (renders instead of the list; `loading` still shows the skeleton), both `@since 2.13.0`.
|
|
1722
|
+
- `ListDetailTemplateProps<T extends ListDetailItem = ListDetailItem>`: `items`, `onSelect` and `renderListItem` use your item type, inferred from `items` or given as `<ListDetailTemplate<MyItem>>`. Existing non-generic usage compiles unchanged. The component's declared type is a generic call signature (the forwardRef result is cast to it, `ref` included), so static members such as `displayName` are not on the declared type: read them through a cast.
|
|
1723
|
+
|
|
1724
|
+
**WH-20.** ListDetailTemplate wires custom rows to the selection, keeps focus on the selected row when the mobile detail closes, names the detail drawer, and can collapse by its container's width.
|
|
1725
|
+
|
|
1726
|
+
- `renderListItem(item, selected, helpers)`: NEW third argument `ListDetailItemHelpers` (exported, `@since 2.13.0`) with `select()` and `getItemProps()` (`onClick`, `aria-current`, `id`). Two-argument renderers work as before. A custom row that uses the helpers opens the detail drawer on a small screen.
|
|
1727
|
+
- NEW `detailOpen` / `defaultDetailOpen` / `onDetailOpenChange(open)`: the drawer's open state, controlled or uncontrolled (closed until a row is selected, as before).
|
|
1728
|
+
- On a small screen the list stays mounted under the full-width drawer; Back, Escape and the backdrop return focus to the selected row, never to the page body. The drawer is a named modal dialog (by the detail header, else `labels.detail`, "Details"), always has Back, and slides instantly under reduced motion.
|
|
1729
|
+
- NEW `collapseMode?: 'viewport' | 'container'` (default `'viewport'`, the `md` breakpoint as before) and `collapseBelow?: number`. `'container'` collapses when the template's own width cannot fit the list column, the split gap and a compact detail column (the theme's `compact` container width, 360px by default); the server render and the first client render show the split view.
|
|
1730
|
+
|
|
1731
|
+
**WH-21.** ListDetailTemplate takes className, sx, HTML attributes and slotProps.
|
|
1732
|
+
|
|
1733
|
+
- `className`, `sx` (applied after the template's own styles; array and function forms work) and every HTML attribute of the root `div` (`id`, `aria-*`, `data-*`, event handlers), typed `Omit<React.HTMLAttributes<HTMLDivElement>, own props | 'color'>`. The template's owned attributes stay its own (ADR-04).
|
|
1734
|
+
- NEW `slotProps` (`@since 2.13.0`): `listPanel`, `detailPanel`, `search` (the input), `list` (the `ul`) and `drawerPaper`, each taking `className`, `sx`, `id`, `data-*` and HTML attributes.
|
|
1735
|
+
- Props the root used to drop now take effect: an `onClick`, `id` or `aria-label` passed to ListDetailTemplate reaches its root element.
|
|
1736
|
+
|
|
1737
|
+
The DOM is unchanged when the new props are not used.
|
|
1738
|
+
|
|
1739
|
+
**WH-22.** ListDetailTemplate supports paged and server-filtered queues: load more, a set size, a server-search switch and result announcements.
|
|
1740
|
+
|
|
1741
|
+
- NEW `totalCount`: every row gets `aria-setsize` (this value, else the rows shown) and `aria-posinset`.
|
|
1742
|
+
- NEW `hasMore`, `onLoadMore` (returns nothing or a promise) and `loadingMore`: a Load more button follows the list inside its scroll area. While loading it reads "Loading more" and is `aria-disabled` and `aria-busy` (it keeps focus; presses are ignored and a second load never starts); a rejection keeps the button and is reported through `globalThis.reportError`. When new items arrive, "{n} more items loaded" is announced; if the button goes away while focused, focus moves to the first new row.
|
|
1743
|
+
- NEW `filterItems?: false | ((item, query) => boolean)`: `false` shows every item as given, for a server-side search; a function replaces the default title and subtitle match.
|
|
1744
|
+
- After the search changes, the result count ("3 results") is announced politely once the search has rested.
|
|
1745
|
+
- NEW labels keys `loadMore`, `loadingMore`, `moreLoaded({ count })` and `resultsCount({ count })`.
|
|
1746
|
+
|
|
1747
|
+
No new dependency; list virtualization stays deferred.
|
|
1748
|
+
|
|
1749
|
+
Visual review: owner Storybook review of templates-listdetailtemplate--default, --with-filters, --loading, --empty, --narrow-list (M9 W1-LD hand-off; PNG compare 343bb81e vs lane tip: Default, WithFilters, Loading, Empty and CustomListWidth identical in all nine capture variants); owner Storybook review of templates-listdetailtemplate--list-actions-without-search, --empty-detail-with-detail-content, --list-empty-content, --list-error (M9 W1-LD hand-off); owner Storybook review of templates-listdetailtemplate--custom-item-renderer, --mobile-drawer, --container-collapse (M9 W1-LD hand-off); owner Storybook review of templates-listdetailtemplate--server-filtering, --load-more, --thousand (M9 W1-LD hand-off)
|
|
1750
|
+
- **Additive**: The pattern layer, AICopilotPattern and the AppLayout AI panel guide (H-28, H-29, H-14). ([`e76442e`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e76442eb90baf9d5da9731920d13b37dd0cfe8d3))
|
|
1751
|
+
|
|
1752
|
+
**WH-28.** The pattern layer exists: `src/patterns/` with its barrel in the library build, docs/PATTERNS.md, and the `Patterns/` story category reserved for it.
|
|
1753
|
+
|
|
1754
|
+
**WH-28.** `src/patterns/index.ts` is the pattern barrel (ADR-12 (1)), compiled by the library build through `tsconfig.lib.json`'s include list; nothing in it is public until the root entry re-exports it (H-27). docs/PATTERNS.md holds the pattern-versus-template rule, the thin-composition contract (ADR-12 (3)), the index of the five planned patterns (AICopilotPattern in progress; Dashboard, DataTable, Filter and EmptyState not started; every owner 'unassigned', IMPLEMENTATION_PLAN.md:101 is blank, OD-H6 (a)), cross-links to the templates, and the root-only export waiver of the GOVERNANCE.md:256 subpath item (signed by the owner for the Core Maintainers, 2026-10-09: PROP-015, issue #26). The two guide stories that used `Patterns/` move to `Feedback/Illustrated Message` and `Feedback/Empty States` with their story ids pinned (`meta.id`), so every id, link and baseline is unchanged. Wires (integration): the NAMING.md 'Patterns' paragraph, the README Patterns section and docs row, the IMPLEMENTATION_PLAN Phase 2 checklist and revision history, the GOVERNANCE waiver reference, and the stale 'PATTERN — NOT exported, copy from Storybook source' line of src/index.ts. No API, behavior or style change, so no release (.changeset/README.md, "Published files without an API or behavior change").
|
|
1755
|
+
|
|
1756
|
+
**WH-29.** AICopilotPattern, the human-in-the-loop reference composition, is new (the first pattern in `src/patterns`); InsightEngine takes `headerActions`, and its `onClose` is optional in the embedded presentation.
|
|
1757
|
+
|
|
1758
|
+
**WH-29 (AICopilotPattern).** NEW `AICopilotPattern` (`forwardRef<HTMLDivElement>`, `AICopilotPatternProps`, `AICopilotPatternSlotProps`, @since 2.13.0), exported from the root entry at the M9 gate (H-27; root only, no subpath: the PATTERNS.md waiver). InsightEngine with `presentation` 'drawer' (default) or 'embedded', so the same component works as a drawer and inside AppLayout's `renderAiPanel`. ChatThread renders the query, clarification and plan-review blocks by default; `onBlockEvent` and `messageActions` are forwarded unchanged (the golden-dataset handoff is an app action in `messageActions.getActions`, shown only to the users the app allows); confidence, verification and sources come from ChatThread's message slots; `getReasoning(message)` returns AIChainOfThought props shown in the answer's attachments ahead of its sources (markdown by default; the chain's plan events reach `onBlockEvent`, its failures `onError`). `suggestedPrompts` are the start screen's starters and send through `onSendMessage`, which runs on the engine's async path (pending Send, the inline 'message wasn't sent' alert with Try again, `onError` with an `Error`). A picked related question goes into the composer. Props: `presentation`, `open`, `onClose`, `title`, `viewMode` / `defaultViewMode` / `onViewModeChange`, `messages`, `inputValue` / `defaultInputValue` / `onInputChange`, `onSendMessage`, `onError`, `onBlockEvent`, `messageActions`, `getReasoning`, `suggestedPrompts`, `onSuggestedPromptSelect`, `onRelatedQuestionSelect`, `chats` / `activeChatId` / `onSelectChat`, `renderEmptyState`, `renderHeaderActions`, `slotProps` (`root`, `engine`, `thread`, `composer`), `className`, `sx`. The pattern holds no business state and adds no live region, landmark or focus code (tested). New string: `ai.copilotPattern.headerActions` ('Conversation actions'), the name of the group around `renderHeaderActions`.
|
|
1759
|
+
|
|
1760
|
+
**WH-29 (InsightEngine).** NEW `headerActions` (ChatHeader `actions`): the host's controls in the panel header, before the expand toggle and Close. `onClose` is optional (it was required): an embedded panel without it shows no Close button, and a drawer without it warns once in development. Panels that pass `onClose` are unchanged.
|
|
1761
|
+
|
|
1762
|
+
React-only: strings and components are not part of the CSS package; no token, colour or motion changes.
|
|
1763
|
+
|
|
1764
|
+
**WH-14.** The AppLayout AI panel guide: which of the three panel routes to use, and the before and after of each 2.13.0 panel correction.
|
|
1765
|
+
|
|
1766
|
+
**WH-14.** NEW docs/APPLAYOUT_AI_PANEL.md: a decision table for the routes (the integrated InsightEnginePanel through `panelProps`; the same panel with the HITL props; `renderAiPanel` with InsightEngine or AICopilotPattern, `presentation="embedded"`), a snippet per route, and before / after snippets for one send (H-02), the derived demo mode (H-04), no dead controls (H-07, H-08), the title heading (H-34), no invented initials (H-36, H-08), timestamps opt-in (H-08, additive), sources collapsed (H-08), the phone drawer (H-40), `onError` firing (G-15, H-16) and `streaming` (H-50+). Templates/AppLayout gains a 'Choosing an AI panel route' docs section and AI/Overview's 'Best' wording is route-neutral. The InsightEnginePanel / InsightEngine convergence RFC is a draft file for the owner to post (docs/remediation/H-14-convergence-rfc-draft.md: options, prop inventory, criteria, a recommendation); it is decided at the v3.0 exit-criteria review (MASTER_PLAN section 10), not in this release. Shipped docs changed: none in the package (docs/ and stories only); no API, behavior or style change, so no release.
|
|
1767
|
+
|
|
1768
|
+
**MIGRATION link addendum (for the release PR).** Each AppLayout-affecting correction already has its row in the M7 and M8 drafts. When the release PR puts those rows into MIGRATION.md, append to each row's "What to do" cell: `See [the AppLayout AI panel guide](docs/APPLAYOUT_AI_PANEL.md#<anchor>).` with these anchors (all checked by check-docs' link checker):
|
|
1769
|
+
|
|
1770
|
+
| Draft | Row (first cell) | Anchor |
|
|
1771
|
+
|---|---|---|
|
|
1772
|
+
| m7-pr6-insight-engine-panel-state.md | `InsightEnginePanel` `onSendMessage` | `#one-send-per-message-h-02` |
|
|
1773
|
+
| m7-pr6-insight-engine-panel-state.md | `InsightEnginePanel` `demoMode` (integrated) | `#demo-mode-is-derived-h-04` |
|
|
1774
|
+
| m7-pr6-insight-engine-panel-state.md | `InsightEnginePanel` `demoMode` default (v3.0) | `#demo-mode-is-derived-h-04` |
|
|
1775
|
+
| m7-pr6-insight-engine-panel-state.md | `InsightEnginePanel` composer | `#no-dead-controls-h-07-h-08` |
|
|
1776
|
+
| m7-pr6-insight-engine-panel-state.md | `InsightEnginePanel` title | `#the-title-is-a-real-heading-h-34` |
|
|
1777
|
+
| m7-pr6-insight-engine-panel-state.md | `InsightEnginePanel` `userInitials` | `#no-invented-initials-h-36-h-08` |
|
|
1778
|
+
| m7-pr11-applayout-ai-panel-a11y.md | `AppLayout` AI panel below the md breakpoint (also `AIFabLayout`) | `#the-phone-drawer-h-40` |
|
|
1779
|
+
| m8-pr12-applayout-panel.md | `InsightEnginePanel` message actions | `#no-dead-controls-h-07-h-08` |
|
|
1780
|
+
| m8-pr12-applayout-panel.md | `InsightEnginePanel` sources | `#sources-start-collapsed-h-08` |
|
|
1781
|
+
| m8-pr12-applayout-panel.md | `InsightEnginePanel` avatars | `#no-invented-initials-h-36-h-08` |
|
|
1782
|
+
| m8-pr12-applayout-panel.md | `InsightEnginePanelOwnProps.isTyping` (`InsightEnginePanel` `isTyping`) | `#istyping-becomes-streaming-h-50` |
|
|
1783
|
+
| m8-pr9-insight-engine.md | `InsightEngine` `onError` | `#onerror-now-fires-g-15-h-16-insightengine-and-the-pattern` |
|
|
1784
|
+
| m8-pr9-insight-engine.md | `InsightEngine` `onSendMessage` returning a promise | `#onerror-now-fires-g-15-h-16-insightengine-and-the-pattern` |
|
|
1785
|
+
|
|
1786
|
+
The timestamps section of the guide needs no row: the panel never showed `ChatMessage.timestamp` before H-08 and shows none by default after it (OD-H8 (a)); `formatTimestamp` is additive (m8-pr12).
|
|
1787
|
+
|
|
1788
|
+
Visual review: the owner's local Storybook review of `ai-insightengine--header-actions`, `--header-actions-dark`, `--header-actions-rtl` (this lane) and of `patterns-aicopilot--*` (H-30, lane W2-CP), light, dark and RTL, docked at 320px and 800px and fullscreen; approved by the owner, 2026-10-09
|
|
1789
|
+
- **Additive**: NEW: AIMessage's built-in, permission-gated "Add to golden dataset" action (GA-01, the HITL spec's golden-dataset approval). Pass `goldenDataset={{ canAdd, onAdd, added? }}`: ([`e76442e`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e76442eb90baf9d5da9731920d13b37dd0cfe8d3))
|
|
1790
|
+
|
|
1791
|
+
- It renders only when `canAdd` is true. Your app decides who may add (for example users with the data scientist role) and passes the result; the design system never decides roles. Without the permission nothing renders.
|
|
1792
|
+
- An icon button ('award') after Regenerate, named and titled "Add to golden dataset". While the promise `onAdd` returns runs, it is `aria-busy` and `aria-disabled` and keeps focus; a second press does not call `onAdd` again.
|
|
1793
|
+
- Added: the action is pressed and unavailable with the reason "Added to golden dataset" (announced once), and the message shows the golden AIVerificationBadge labelled "Added to golden dataset" (`showBadge: false` leaves it out). `added` controls the state; without it AIMessage keeps its own, starting at `defaultAdded`.
|
|
1794
|
+
- Failed: "Couldn't add to the golden dataset" with Try again below the footer; the error reaches `onActionError` with `{ source: 'action', actionId: 'golden' }`.
|
|
1795
|
+
- While the message streams, the action is unavailable with the reason.
|
|
1796
|
+
- Each settled add reports `{ type: 'golden', action: 'add', success, messageId }` through AIConfigProvider's `onInteraction` (content-free; a new member of the open `AIInteractionEvent` union).
|
|
1797
|
+
|
|
1798
|
+
ChatThread, InsightEngine and AppLayout's AI panel (InsightEnginePanel, docked and fullscreen) take it per message through `messageActions.getGoldenDataset(message)`; the panel passes its own message, and a rejection adds the `messageId` to `onActionError`'s context.
|
|
1799
|
+
|
|
1800
|
+
New exports (types): `AIMessageGoldenDataset`, `AIMessageGoldenDatasetContext`, `@since 2.13.0`. New props: AIMessage `goldenDataset`, `ChatThreadMessageActions.getGoldenDataset`, `@since 2.13.0`. New strings: `ai.message.addToGolden`, `addedToGolden`, `addToGoldenFailed`, `addToGoldenUnavailableStreaming`. React only; no token, role or default change, so the CSS package does not change.
|
|
1801
|
+
|
|
1802
|
+
Visual review: the owner's Storybook review of the GA-01 stories (new rendering only when an app passes `goldenDataset` with `canAdd: true`, or an added message)
|
|
1803
|
+
- **Additive**: AIReviewQueueTemplate, the golden-dataset review queue, with its Storybook catalog (H-25, H-26). ([`e76442e`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e76442eb90baf9d5da9731920d13b37dd0cfe8d3))
|
|
1804
|
+
|
|
1805
|
+
**WH-25.** NEW: `AIReviewQueueTemplate`, the golden-dataset review queue (H-25; requirements R24 and R25). The answers users flagged, each row with its question, answer, query, status, flag count and problem types, confidence, the golden badge and the time it was asked; each opens in `AIReviewItem` to approve, edit (with the diff) or reject (with a required reason), and every decision is logged in the item's history.
|
|
1806
|
+
|
|
1807
|
+
- Filter by status with native checkboxes (`statusFilter` / `defaultStatusFilter`, default `['pending']`, `onStatusFilterChange`, counts from `statusCounts` or counted from `items`) and search the question, answer and query (`searchValue` / `defaultSearchValue` / `onSearchChange`). `filterMode="server"` shows `items` as given and leaves filtering to you. `hasMore`, `onLoadMore`, `loadingMore` and `totalCount` page the queue (every row reads "n of total").
|
|
1808
|
+
- `onDecision(decision)`: return the request's promise. While it runs the item's controls are `aria-disabled`; a rejection shows the item's error and keeps the item and the focus; on success the queue selects the next answer (else the previous one), moves focus to its heading (or to the empty-state heading after the last one) and announces the outcome and the answers left (`advanceOnDecision`, default true). `canDecide` (boolean or per item) is your permission check, as AIMessage's `goldenDataset.canAdd` is; `history` adds your decision log (`AIReviewQueueHistoryEntry`, an `AIReviewHistoryEntry` with `itemId`) to each item's history.
|
|
1809
|
+
- States: an empty queue, "No results" with Clear filters, a load error with Try again (`error: { title?, description?, onRetry? }`, the button busy while the retry runs), `loading` (skeleton) and `detailLoading`; `emptyState` and `filteredEmptyState` replace the messages.
|
|
1810
|
+
- Below 744px (the template's own width) it shows the list and a detail drawer as wide as the queue, so it works in AppLayout's AI panel docked (320 to 800px, the drawer covers the panel) or fullscreen, beside the panel, and on a full page.
|
|
1811
|
+
- `renderListItem(item, selected, helpers)` renders your own rows with ListDetailTemplate's helpers; `detailActions`, `headingLevel`, `onActionError`, `className`, `sx` and `slotProps` (`listPanel`, `detailPanel`, `filter`) follow the root props contract.
|
|
1812
|
+
- No virtualization: it is deferred (MASTER_PLAN section 10); page large queues with `hasMore` / `onLoadMore`.
|
|
1813
|
+
|
|
1814
|
+
New exports: `AIReviewQueueTemplate`, `AIReviewQueueTemplateProps`, `AIReviewQueueTemplateOwnProps`, `AIReviewQueueHistoryEntry`, `AIReviewQueueError`, `@since 2.13.0` (root export with H-27). New strings: the `ai.reviewQueue` group (22 keys). React only; no token, role or default change, so the CSS package does not change.
|
|
1815
|
+
|
|
1816
|
+
**WH-26.** Docs: the Templates/AIReviewQueueTemplate story (H-26) with the six STORYBOOK_STANDARDS sections, a local mock review service whose approve, edit and reject flows each log a decision and a history entry, the reject dialog and the edit diff open, the empty, filtered-empty, loading and error states, server mode with Load more, 1,000 answers with the recorded render time, mobile, 320 / 400 / 800px containers, the theme matrix, right to left, dark, AppLayout's AI panel docked at 320 and 800px and fullscreen, and the golden dataset reached from the chat (GA-01) and from the queue. COMPONENT_PICKER lists the queue; CatalogTemplate's guidance points review flows to it. The component proposal is drafted for the Core Maintainers vote (docs/remediation/H-26-aireviewqueue-rfc-draft.md, not posted).
|
|
1817
|
+
|
|
1818
|
+
Visual review: owner Storybook review of the templates-aireviewqueuetemplate--* stories listed in the M9 W2-RQ hand-off
|
|
1819
|
+
- **Behavior correction (b, c)**: The AI FAB and AICircularAction keep their brand gradient, and the FAB hides while the AI panel is open (owner decisions 2026-10-09). ([`e76442e`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e76442eb90baf9d5da9731920d13b37dd0cfe8d3))
|
|
1820
|
+
|
|
1821
|
+
**Brand gradient.** AIFab and AICircularAction's `gradient` variant keep the brand gradient under a white icon, in light and dark; new `ai.gradient.brandDiagonal`.
|
|
1822
|
+
|
|
1823
|
+
The AI launcher (AIFab) and the gradient AICircularAction had moved to a solid AI fill with the gradient as a thin ring, an extension of the gradient-text rule to icons. The owner decided on 2026-10-09 that icons keep the full gradient, as AIAvatar does: coral 800 to purple 700 at 135deg, the same in light and dark, under the icon in `ai.onPrimaryFill` (white), with no ring. In light the gradient and the icon render as on main (43412b6d) (the glow stays the `ai.focusRing` role); dark now matches light. The glow, the hover and press scale, the focus outline, the disabled colours and reduced motion are unchanged.
|
|
1824
|
+
|
|
1825
|
+
- New resolver gradient `ai.gradient.brandDiagonal` (`--trinity-ai-gradient-brand-diagonal` in the CSS package): `ai.avatarFill.start` to `.end` at 135deg. A brand override reaches it in light (the stops reference `brand.accent` and `brand.secondary`); a token set can restyle it through `ai.avatarFill`.
|
|
1826
|
+
- Contrast (WCAG 1.4.11, 3:1): the centred icon never reaches the gradient's top-left corner, where white is 2.97:1. Over the icon's box white is 3.5:1 or more on every FAB size (3.77:1 at the default large) and 3.4:1 or more on every AICircularAction size, up to 6.18:1 at the bottom-right end. The resolver's `ai.gradient.diagonal` is not used: in dark mode it starts at the lighter dark accent, 2.6:1 under the icon.
|
|
1827
|
+
- CSS: INSTRUCTIONS_CSS.md Pattern 12 (`.ai-fab`), a quick-reference row in TRINITY_NEW_APP_CSS.md and the CSS agent guide, a migration bullet in TRINITY_EXISTING_APP_CSS.md.
|
|
1828
|
+
|
|
1829
|
+
**FAB hidden while the panel is open.** AppLayout hides the AI FAB while the AI panel is open and brings it back, with focus, when the panel closes; new `showFabWhenPanelOpen`.
|
|
1830
|
+
|
|
1831
|
+
The owner reported on 2026-10-09 that the FAB stayed visible while the AI panel was open. AppLayout rendered it beside the docked panel (offset by the panel width) as a second close control, and hid it only in fullscreen and over the mobile drawer. It is now hidden whenever the panel is open: docked at any width, fullscreen and the mobile drawer. The panel's own Close button (Escape in the drawer, or the AI nav item in sidebar layouts) closes it.
|
|
1832
|
+
|
|
1833
|
+
- Focus never falls to the page: opening the panel from the FAB moves focus into the panel (InsightEnginePanel focuses its composer; a `renderAiPanel` surface that takes no focus itself gets focus on the panel landmark, with `tabindex="-1"` for that focus only). Closing returns focus to the control that opened the panel: the FAB, which comes back, or the AI nav item; if that is gone, the main content.
|
|
1834
|
+
- `aria-expanded` and `aria-controls` stay correct: the shown FAB reports `aria-expanded="false"` and points `aria-controls` at the panel only while the panel is in the document (`keepAiPanelMounted`); the AI nav item keeps its toggle state.
|
|
1835
|
+
- AIFab itself is unchanged; its docs say AppLayout hides it while the panel is open.
|
|
1836
|
+
- CSS: INSTRUCTIONS_CSS.md Pattern 12 hides `.ai-fab` (the `hidden` attribute) while the panel is open and describes the focus moves; the quick-reference rows say so.
|
|
1837
|
+
|
|
1838
|
+
Visual review: the owner's Storybook review (templates-applayout--top-nav-with-fab, templates-applayout--top-nav-with-fab-dark, ai-aiactions--circular-actions, ai-aiactions--circular-actions-dark, tokens-ai-semantic-roles--gradient-css-parity); the owner's Storybook review (templates-applayout--top-nav-with-fab, templates-applayout--top-nav-with-fab-dark, templates-applayout--top-nav-resizable-ai-panel, templates-applayout--top-nav-fullscreen-ai-panel, templates-applayout--ai-panel-focus-return-narrowest, templates-applayout--ai-panel-focus-return-widest, templates-applayout--ai-panel-focus-return-fullscreen, templates-applayout--mobile-ai-panel-drawer, templates-applayout--top-nav-fab-beside-panel); approved by the owner, 2026-10-09
|
|
1839
|
+
- **Additive**: AppLayout can dock the AI panel at the start: new `aiPanelSide` (`'end'` by default, or `'start'`). ([`e76442e`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e76442eb90baf9d5da9731920d13b37dd0cfe8d3))
|
|
1840
|
+
|
|
1841
|
+
The owner asked on 2026-10-09 for an option to put the chat on the left and the content on the right. `aiPanelSide="start"` docks the AI panel at the inline start (the left edge left to right, the right edge right to left); the default `'end'` renders exactly as before. The value is logical, like AIFab's `position` and ResizablePanel's `side`, so a right-to-left app needs no branch, with or without stylis-plugin-rtl.
|
|
1842
|
+
|
|
1843
|
+
- The content keeps clear of the panel with `marginInlineStart`; the resize handle is the panel's inner edge (facing the content); a drag and the arrow keys move it in their own direction (Right Arrow widens a start panel left to right, Left Arrow right to left; Home and End unchanged), and the splitter's `aria-valuenow` is still the panel width.
|
|
1844
|
+
- The AI FAB moves to the bottom start corner (still hidden while the panel is open, focus still returns to it); its tooltip opens toward the page. Below md the drawer slides in from the start edge.
|
|
1845
|
+
- With `navStyle="sidebar"` the order is navigation, AI panel, content: the panel docks beside the sidebar at its current width and moves with it when the sidebar collapses or expands (the sidebar's 225ms timing, a 0 duration under reduced motion); the content clears both, and a start FAB keeps clear of the sidebar.
|
|
1846
|
+
- Fullscreen, the panel landmark, focus handling, `showFabWhenPanelOpen`, `keepAiPanelMounted` and `renderAiPanel` work the same on both sides.
|
|
1847
|
+
- AIFab: a FAB in a start corner (`position` `'bottom-left'` or `'top-left'`) opens its tooltip toward the page (placement `right`); it used `left` and relied on the popper flipping it.
|
|
1848
|
+
- CSS: INSTRUCTIONS_CSS.md Pattern 12 describes the start-docked layout with logical properties (no new variables).
|
|
1849
|
+
|
|
1850
|
+
Visual review: the owner's Storybook review (templates-applayout--ai-panel-start-narrowest, templates-applayout--ai-panel-start-narrowest-dark, templates-applayout--ai-panel-start-narrowest-rtl, templates-applayout--ai-panel-start-widest, templates-applayout--ai-panel-start-widest-rtl, templates-applayout--ai-panel-start-fullscreen, templates-applayout--ai-panel-start-fullscreen-dark, templates-applayout--sidebar-ai-panel-start-narrowest, templates-applayout--sidebar-ai-panel-start-narrowest-dark, templates-applayout--sidebar-ai-panel-start-narrowest-rtl, templates-applayout--sidebar-ai-panel-start-widest, templates-applayout--sidebar-ai-panel-start-fullscreen, templates-applayout--sidebar-ai-panel-start-fullscreen-rtl); approved by the owner, 2026-10-09
|
|
1851
|
+
- **Additive**: AIReviewQueueTemplate and AICopilotPattern import from the package root; the behavior contract's burn-down is complete. ([`e76442e`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e76442eb90baf9d5da9731920d13b37dd0cfe8d3))
|
|
1852
|
+
|
|
1853
|
+
**H-27.** `AIReviewQueueTemplate` (with `AIReviewQueueTemplateProps`, `AIReviewQueueTemplateOwnProps`, `AIReviewQueueHistoryEntry`, `AIReviewQueueError`) joins the page templates and `AICopilotPattern` (with `AICopilotPatternProps`, `AICopilotPatternOwnProps`, `AICopilotPatternSlotProps`) is the first export of the new patterns block, both `@since 2.13.0` and from `@trinityui/design-system` only (no subpath; the waiver in docs/PATTERNS.md is not signed yet). The release artifact check requires both, and the packed consumer fixture imports and renders both (Vite app and SSR, light and dark).
|
|
1854
|
+
|
|
1855
|
+
**H-46.** The template and the pattern have no `manualChunks` rule: they land in `index.js`, the only entry that exports them, so `theme`, `tokens`, `essentials` and `data-table` do not carry them (the measured alternatives grew those entries by 6 to 9 KB; ADR-18-R2 and ADR-12-R1 are Proposed). The five size ratchets are re-measured (docs/remediation/size/M9-integration.md).
|
|
1856
|
+
|
|
1857
|
+
**B-49.** Every rule of the behavior contract is strict, with no finding and no baseline shard left; `check:behavior` fails on any new hand-rolled behavior. No runtime change.
|
|
1858
|
+
- **Behavior correction (c)**: AIAvatar keeps its coral-to-purple brand gradient in light and dark mode (owner decision 2026-10-08), with a white icon. ([`f6c70fc`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/f6c70fc58539d6bcb95b88e80d8890ae117e2170))
|
|
1859
|
+
|
|
1860
|
+
The fill is the new `ai.gradient.avatar` (CSS: `--trinity-ai-gradient-avatar`), built from the new `ai.avatarFill.start` and `.end` roles (`--trinity-ai-avatar-fill-start` and `-end`): coral 800 to purple 700, top to bottom, in both modes. The icon is `ai.onPrimaryFill` (white) in both modes; in dark mode it was #F4F4F5. In light mode the stops follow `brand.accent` and `brand.secondary`, so a brand override reaches the avatar; `semantic.colors.ai.avatarFill` and `darkMode.colors.ai.avatarFill` override them directly.
|
|
1861
|
+
|
|
1862
|
+
The centred icon never reaches the gradient's top edge (white is 2.97:1 there). Where it sits, white is 3.35:1 or more at every size (14% down at xs, 29% at large) and 6.18:1 at the bottom, above the 3:1 that WCAG 1.4.11 requires of a graphical object. The avatar does not use the resolver's `ai.gradient.primary`: in dark mode that runs coral 400 to purple 600, 2.5-2.9:1 under the icon's upper half.
|
|
1863
|
+
|
|
1864
|
+
Visual review: local Storybook review of `ai-aiavatar--all-sizes`, `ai-aiavatar--all-sizes-dark`, `ai-aiavatar--animated-dark` and `tokens-ai-semantic-roles--roles` (DN-08); approved by the owner, 2026-10-09
|
|
1865
|
+
- **Additive**: AIChainOfThought takes the owner's new look: one rounded card with a "Thought process" header (the AI mark, a status line such as "Reasoning in progress…" or "Done · 4.2s", one progress dot per phase and a chevron) and a vertical timeline of phase cards with soft markers (a green check, a purple spinner, a gray dot, red on failure). Plan steps are numbered chips, tasks show a gray check and a right-aligned time, a failed or retrying task is a red box with its code and message, output artifacts are file cards ("CSV · 5 rows") and the insight ends with "Insight confidence" and a bar. The props, events and accessibility are unchanged; a chain that mounts complete now starts open (pass `defaultCollapsed` to start it closed), and final phases still `pending` are drawn as title-only cards without announcing a response. ([`f6c70fc`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/f6c70fc58539d6bcb95b88e80d8890ae117e2170))
|
|
1866
|
+
|
|
1867
|
+
New theme role `ai.progress` (`palette.trinity.ai.progress`, CSS `--trinity-ai-progress`): the completed share of an AI progress graphic, teal #008A7E in light and #14B8A6 in dark. New strings under `ai.chainOfThought`: `reasoningInProgress`, `done`, `stepFailed`, `statusWithElapsed`, `insightConfidence`, `artifactMeta`.
|
|
1868
|
+
|
|
1869
|
+
Insight confidence now shows its label, the percent (coloured by its level, which stays in `data-cot-confidence`) and a decorative bar instead of `AIConfidenceBadge`, and generated artifacts are a list named "Generated artifacts" of file cards (the count chip is gone; the list conveys the count). Thoughts render as plain text lines. Without the header (`showToggle={false}`) the "Generating response…" indicator stays as before.
|
|
1870
|
+
|
|
1871
|
+
Visual review: the owner's local Storybook review of the AIChainOfThought story ids in docs/remediation/post-M8-owner-design.md (light, dark, RTL; the AppLayout AI panel docked at 320px and 800px and fullscreen), with the lane's mockup compares (mean pixel difference 9-12); approved by the owner, 2026-10-09
|
|
1872
|
+
- **Additive**: AIChatWelcome, the AI start screen, is new and is the default start screen of AppLayout's AI panel (InsightEnginePanel, docked and fullscreen) and of InsightEngine; a new `ai.canvasFill` role and `ai.gradient.canvas` paint its canvas. ([`f6c70fc`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/f6c70fc58539d6bcb95b88e80d8890ae117e2170))
|
|
1873
|
+
|
|
1874
|
+
**AIChatWelcome.**
|
|
1875
|
+
|
|
1876
|
+
NEW `AIChatWelcome`: the AI start screen, shown before the first message, in two variants.
|
|
1877
|
+
|
|
1878
|
+
- `variant="showcase"` (default): a centred greeting (`greeting`, or 'Hi {name}, how can I help you today?' with `userName`) and `subtitle`, the composer centred under it, category cards (a tinted icon tile, the title, the question) and a disclaimer.
|
|
1879
|
+
- `variant="context"`: the greeting, the composer with scope chips on it (`scope`: project, sources, date range; a chip is a button only with `onClick`), then Continue (`recentThreads` with `onRecentThreadClick`), New since yesterday (`updates`: what changed and the action) and Try asking (category chips), and a trust note.
|
|
1880
|
+
- Layout by the container width, never the viewport: from 600px of container the wide layout; below it a smaller greeting, compressed sections (stacked pill suggestions, or one update row and two chips) and the composer at the bottom.
|
|
1881
|
+
- Suggestions call `onSuggestionSelect(prompt, item)`, then send (`suggestionBehavior="send"`, through `onSendMessage`) or fill the composer (`"fill"`).
|
|
1882
|
+
- The composer is an `InsightEngineInput` (`composerProps`, `inputValue` / `defaultInputValue` / `onInputChange`), or your node through `composer`; `initialFocus` (`'composer'` by default) focuses its field on mount.
|
|
1883
|
+
- `disclaimer` (a node, or `null`), `guidelinesHref` and `guidelinesLabel` (an underlined link), `headingLevel` (default 2), `className`, `sx`, `slotProps` (`header`, `composer`, `suggestions`, `footer`) and HTML attributes on the root.
|
|
1884
|
+
- Strings: the new `ai.chatWelcome` group (11 keys) through `AIConfigProvider`.
|
|
1885
|
+
- Accessibility: the greeting is a heading; every suggestion, update, chip and thread is a button; the suggestion lists are named; text 4.5:1 and icons 3:1 in light and dark; right to left mirrored; thread and update titles bidi-isolated.
|
|
1886
|
+
|
|
1887
|
+
New exports `AIChatWelcome`, `AIChatWelcomeProps`, `AIChatWelcomeOwnProps`, `AIChatWelcomeOptions`, `AIChatWelcomeSlotProps`, `AIChatWelcomeSuggestion`, `AIChatWelcomeThread`, `AIChatWelcomeUpdate`, `AIChatWelcomeScopeItem`, `AIChatWelcomeTone` and `AIChatWelcomeVariant`, all `@since 2.13.0`. The canvas uses the new `ai.gradient.canvas` role (below); the component itself is React only.
|
|
1888
|
+
|
|
1889
|
+
**The hosts' start screen.**
|
|
1890
|
+
|
|
1891
|
+
`InsightEnginePanel` (AppLayout's AI panel, docked and fullscreen) and `InsightEngine` show `AIChatWelcome` as their start screen before the first message, with their composer centred in it. After the first message the composer moves to the bottom; focus follows when it was in the start screen, and the composer slides into place (no motion under reduced motion).
|
|
1892
|
+
|
|
1893
|
+
- `InsightEnginePanel`: NEW `welcome` (`AIChatWelcomeOptions | false`, default `{}`) and `emptyState` (a node), both `@since 2.13.0`. An `emptyState` wins and renders in the thread with the composer at the bottom; `welcome={false}` keeps the previous empty state ('What do you want to know?'). The context variant's Continue lists the first two threads of `threadGroups` and opens them through `onThreadSelect`. Through AppLayout: `panelProps.welcome`, `panelProps.emptyState`.
|
|
1894
|
+
- `InsightEngine`: NEW `welcome` (`AIChatWelcomeOptions | false`, default `{}`), `@since 2.13.0`. `emptyState` keeps its meaning and wins; `welcome={false}` keeps ChatThread's 'Start a conversation'. An uncontrolled composer's text is now held by InsightEngine (still cleared after a send), so a suggestion can fill it. The start screen takes focus only while the panel is open.
|
|
1895
|
+
- No log is rendered before the first message; the thread and its log appear with it. The panel shows no start screen while `loading` or while an answer is awaited.
|
|
1896
|
+
|
|
1897
|
+
To keep the previous screen: pass `welcome={false}` (or your own `emptyState`).
|
|
1898
|
+
|
|
1899
|
+
**The canvas role.**
|
|
1900
|
+
|
|
1901
|
+
NEW `ai.canvasFill` color role and the `ai.gradient.canvas` gradient: the AI start screen's canvas wash (white to a faint lavender and pink toward the bottom right), in the React theme and the CSS package.
|
|
1902
|
+
|
|
1903
|
+
- React: `palette.trinity.ai.canvasFill` (`start`, `end`) and `ai.gradient.canvas` on `getTrinityPalette(theme)` / `useTrinityPalette()`, in every Trinity theme and preset. Under a plain MUI theme the fill is a faint primary and secondary tint.
|
|
1904
|
+
- Tokens: optional `semantic.colors.ai.canvasFill` (purple 50, coral 50) and `darkMode.colors.ai.canvasFill` (16% purple 700, 10% coral 800 over the dark paper). A token set without it falls back to the defaults of the same mode.
|
|
1905
|
+
- CSS: `--trinity-ai-canvas-fill-start`, `--trinity-ai-canvas-fill-end` and `--trinity-ai-gradient-canvas` in every theme block and in the runtime CSS functions. `INSTRUCTIONS_CSS.md`, Pattern 11 (AI start screen) shows the canvas, the greeting and the suggestion cards; `TRINITY_NEW_APP_CSS.md` and `TRINITY_EXISTING_APP_CSS.md` point to it.
|
|
1906
|
+
|
|
1907
|
+
The canvas is decorative: text on it keeps 4.5:1 (text.secondary 8.8:1 light, 10.2:1 dark). Figma variables follow with D3 (registered in the parity known gaps).
|
|
1908
|
+
|
|
1909
|
+
Visual review: the owner's local Storybook review of the AI/AIChatWelcome, Templates/InsightEnginePanel (StartScreen*, Empty), AI/InsightEngine and AppLayout panel story ids in docs/remediation/post-M8-owner-design.md (both variants; light, dark and RTL; docked at 320px and 800px and fullscreen); approved by the owner, 2026-10-09
|
|
1910
|
+
- **Additive**: New public API from remediation step 3: labels types and props for TransferList, CommandPalette and FileUpload, the `useRovingTabIndex` registry overload, `VisuallyHidden` ref and attribute passthrough, new ResizablePanel props (controlled width, resize callbacks, handle label, `disableResize`), TreeView `aria-label` and `aria-labelledby`, and ClearableTextField `clearLabel`. ([`d267bb2`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d267bb2b37c2395f3276bfd2e911097a2cb9d328))
|
|
1911
|
+
|
|
1912
|
+
- `TransferListLabels`, `CommandPaletteLabels` and `FileUploadLabels` type the new `labels` prop of each component: one `labels` object per component, where every string can be replaced and count strings are functions of `{ count }`.
|
|
1913
|
+
- `useRovingTabIndex` gains a registry overload for composite widgets whose items register themselves, with three new types: `UseRovingTabIndexRegistryOptions`, `UseRovingTabIndexRegistryReturn` and `UseRovingTabIndexItemOptions`. The existing call signature and its types are unchanged.
|
|
1914
|
+
- `VisuallyHidden` forwards its ref and passes standard HTML attributes through: `VisuallyHiddenProps` now extends the HTML attributes and adds `id` and `sx`, and `children` is optional so a void element (`as="input"`) can render.
|
|
1915
|
+
- `ResizablePanel` accepts a controlled `width` with `onWidthChange` (without `width` it keeps managing its own width from `defaultWidth`), `onResizeStart` and `onResizeEnd` for pointer drags, `resizeHandleLabel` for the keyboard window splitter's name (default "Resize panel"), and `disableResize`, which removes the handle while keeping the width.
|
|
1916
|
+
- `TreeView` accepts `aria-label` and `aria-labelledby` for the tree's accessible name (default "Tree view").
|
|
1917
|
+
- `ClearableTextField` accepts `clearLabel`, the clear button's name and tooltip (default "Clear input").
|
|
1918
|
+
|
|
1919
|
+
This changeset is a backfill: step 3 landed before every PR carried a changeset. The step-3 behavior corrections that ship in the same release are listed in MIGRATION.md, in the rows for 2.13.0 of the "Behavior corrections (2.x)" table, one record per change in `docs/remediation/STEP3-*.md`.
|
|
1920
|
+
|
|
1921
|
+
### Patch Changes
|
|
1922
|
+
|
|
1923
|
+
- **Behavior correction (b)**: AITextBlock underlines markdown links all the time, not only on hover. ([`76cc090`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/76cc09023820693d74e9aea59e81c597a8d1bfe2))
|
|
1924
|
+
|
|
1925
|
+
Links sit inside running text, and their color alone did not set them apart from it (1.06:1 against the body text in light mode, 2.14:1 in dark mode), which fails WCAG 1.4.1 (Use of Color). Every AI surface that renders markdown through AITextBlock (AIResponseRenderer, AIMessage, AIDigestCard, InsightEngine) gets the underline.
|
|
1926
|
+
|
|
1927
|
+
Visual review: https://github.com/Trinity-UI-Components/trinity-design-system/pull/10
|
|
1928
|
+
- **No API or behavior change**: Repository cleanup: dead files, a stale CSS script and outdated guidance removed; no API, style or behavior change. ([`d9b7c23`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d9b7c23439acd24e2d0e9888624ce764ba60f009))
|
|
1929
|
+
|
|
1930
|
+
- The presets module's doc comment now imports `crmPreset` from `@trinityui/design-system` (it named a `@trinitydesign/design-system/presets` subpath that never existed). This comment reaches `dist/presets/index.d.ts`, so the changeset releases a `patch` with `Category: none`.
|
|
1931
|
+
- README.md (shipped) drops the "MUI Compatibility" row; that page's content moved into docs/COMPONENT_PICKER.md, and the Component Picker row says so. The architecture diagram names trinity-generated.css instead of trinity-base.css.
|
|
1932
|
+
- The `build:css` script and `scripts/generate-css.ts` are gone: the script overwrote `dist/trinity.css` (the `./css` export) with hard-coded CSS. `pnpm build` and `build:css:sync` are unchanged.
|
|
1933
|
+
- `dist/components/shared.d.ts` leaves the tarball with its orphan module; no package entry or exports path reached it.
|
|
1934
|
+
- **Behavior correction (b)**: InsightEngineInput's attachment Remove and Retry buttons have a 24 by 24px pointer target. ([`d9b7c23`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d9b7c23439acd24e2d0e9888624ce764ba60f009))
|
|
1935
|
+
|
|
1936
|
+
The two icon buttons on each attachment card are 22 by 22px, below the WCAG 2.2 SC 2.5.8 floor. They keep their size and look and take the shared target-size hit area, so a press up to 1px outside the visible button still reaches it.
|
|
1937
|
+
- **Behavior correction (b)**: UploadDropZone and StatusLegend apply an array or function `sx`. ([`d9b7c23`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d9b7c23439acd24e2d0e9888624ce764ba60f009))
|
|
1938
|
+
|
|
1939
|
+
Both spread `sx` into an object, which dropped a function `sx` and turned an array `sx` into unused keys. They merge it now, in the same order as before (the component's styles, then yours; StatusLegend keeps `flexWrap: 'wrap'` last).
|
|
1940
|
+
- **No API or behavior change**: Smaller bundles and a lighter install. No API change. Every component, hook and token works and renders as before. Record: `docs/remediation/size/cleanup-size-fixes.md`. ([`d9b7c23`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d9b7c23439acd24e2d0e9888624ce764ba60f009))
|
|
1941
|
+
|
|
1942
|
+
- One root import no longer costs the whole package. `useFlowAutoLayout` loads ELK on the first ELK layout, as the lazy chunk `dist/elk.bundled.js`, so other imports no longer include it at module load. The build marks module-level `forwardRef`, `memo`, `createContext`, `styled`, `keyframes` and theme-factory calls `/* @__PURE__ */`, so your bundler drops the components you do not import. Measured with esbuild, brotli: root `Icon` 662.8 kB -> 58.1 kB, `AIMessage` 662.4 kB -> 85.0 kB, the cheapest root export 662.2 kB -> 33.8 kB. The full import is unchanged: 691.2 kB -> 691.8 kB, of which 349.6 kB loads up front.
|
|
1943
|
+
- `@trinityui/design-system/tokens`, `/theme` and `/essentials` no longer load the AI and app-shell chunk; they get a new shared `foundation` chunk instead (ADR-18-R1). Brotli: `/tokens` 128.5 kB -> 7.8 kB, `/theme` 137.1 kB -> 18.2 kB, `/essentials` 145.5 kB -> 57.6 kB, `SimpleTable` 128.9 kB -> 9.6 kB, `/data-table` 715.0 kB -> 609.5 kB. Every entry still starts with `'use client'`.
|
|
1944
|
+
- `dependencies` drops the nine libraries the build already bundles: `@dagrejs/dagre`, `@tanstack/react-virtual`, `ag-grid-community`, `ag-grid-enterprise`, `ag-grid-react`, `d3-hierarchy`, `elkjs`, `highcharts` and `highcharts-react-official`. The package's code for them is unchanged. An install no longer downloads them: 148.02 MB -> 8.10 MB of dependencies on disk. If your app imports one of these libraries itself, add it to your own `package.json`.
|
|
1945
|
+
- The tarball no longer contains `dist/trinity-generated.css`, a byte-identical copy of `dist/trinity.css` that no export named and 2.11.1 did not ship. Tarball 2,551,259 B -> 2,538,909 B.
|
|
1946
|
+
- **No API or behavior change**: The owner's cleanup decisions of 2026-10-09; no API, style or behavior change. ([`d9b7c23`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d9b7c23439acd24e2d0e9888624ce764ba60f009))
|
|
1947
|
+
|
|
1948
|
+
- The package description in `package.json` (shipped) now says WCAG 2.2 AA instead of WCAG 2.1 AA, as README and the guides do. This is the only change that reaches the package, so the changeset releases a `patch` with `Category: none`.
|
|
1949
|
+
- Repository only, not in the tarball: the Storybook background images are recompressed (64.3 MB to 15.8 MB), the unshipped `trinity-base.css` and its demo page move to `docs/archive/`, `scripts/generate-css-theme.ts` is deleted, `check:tokens:figma` also checks the hex values in the `figma/` exports, and the screenshot tool has no `rtl-light` mode.
|
|
1950
|
+
- **No API or behavior change**: Build with a patched toolchain. Dependency security updates for the
|
|
1951
|
+
repository's development and build tools (`pnpm audit` at 9505c2c: 54
|
|
1952
|
+
advisories, none on a production path). The published packages'
|
|
1953
|
+
`dependencies` and `peerDependencies` do not change, and no component
|
|
1954
|
+
behavior changes. Record: `docs/remediation/SEC-DEPS-2026-10.md`. ([`d5fb4cb`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d5fb4cb9846317771057f86220b5eaa550acbeda))
|
|
1955
|
+
|
|
1956
|
+
- Removes the unused `conventional-changelog-cli` devDependency and the
|
|
1957
|
+
`changelog:generate` script that called it. Changesets writes
|
|
1958
|
+
`CHANGELOG.md`. This removes `handlebars@4.7.8` and
|
|
1959
|
+
`@conventional-changelog/git-client@1.0.1`.
|
|
1960
|
+
- Builds with vite 7.3.6, rollup 4.59.1, postcss 8.5.29 and nanoid 3.3.20.
|
|
1961
|
+
Rollup 4.59 orders imports and assigns internal short names in nine
|
|
1962
|
+
`dist/*.js` files differently. Their raw sizes are unchanged except
|
|
1963
|
+
`dist/tokens.js` (5 B smaller); the brotli size of the root entry grows
|
|
1964
|
+
by 580 B and the essentials entry shrinks by 131 B.
|
|
1965
|
+
- Uses esbuild 0.28.2 everywhere (vite, Storybook, tsx, and the CSS
|
|
1966
|
+
package's minifier, whose devDependency moves from `^0.27.3` to
|
|
1967
|
+
`^0.28.2`). `dist/` and the CSS package's `dist/` are byte-identical
|
|
1968
|
+
before and after this update.
|
|
1969
|
+
- Resolves minimatch 3.1.5 and 10.2.6 and brace-expansion 1.1.21 and
|
|
1970
|
+
5.0.12 (ESLint, eslint-plugin-jsx-a11y, typescript-eslint and the
|
|
1971
|
+
Storybook docgen plugin's glob).
|
|
1972
|
+
- Resolves @babel/core 7.29.7, browserslist 4.28.9 and
|
|
1973
|
+
baseline-browser-mapping 2.11.27 (react-docgen, `@vitejs/plugin-react`
|
|
1974
|
+
and eslint-plugin-react-hooks).
|
|
1975
|
+
- Resolves yaml 1.10.3 (cosmiconfig under the `@emotion/react` dev copy's
|
|
1976
|
+
babel macro plugin) and lodash-es 4.18.1 (vitest-axe).
|
|
1977
|
+
- Resolves source-map-js 1.2.2 (the Tailwind vite plugin, jsdom's
|
|
1978
|
+
css-tree and the vitest coverage provider's magicast).
|
|
1979
|
+
- **Behavior correction (b)**: `useFlowAutoLayout` and `layoutFlowElements` with `engine: 'elk'` lay the graph out in the requested direction. ([`d9b7c23`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d9b7c23439acd24e2d0e9888624ce764ba60f009))
|
|
1980
|
+
|
|
1981
|
+
ELK names its directions `DOWN`, `UP`, `RIGHT` and `LEFT`. The hook passed its own `TB`, `BT`, `LR` and `RL` to ELK unchanged, ELK did not recognise them, and every ELK layout came out left to right, including the default `TB`. The hook now maps each direction to ELK's name. The dagre and d3 engines are unchanged, and so are the source and target handle positions, which already followed the direction. No component in the package calls ELK, so nothing the package renders changes.
|
|
1982
|
+
- **Behavior correction (a)**: `--trinity-input-border-*` and `--trinity-switch-track-border` follow dark mode in the static stylesheet. ([`641e3e0`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/641e3e0f4e2239fb27906d39e199d621766a1cd3))
|
|
1983
|
+
|
|
1984
|
+
The CSS guides promise that dark mode adapts automatically. In `trinity.css` (both packages), `--trinity-input-border-default`, `-hover`, `-focus` and `-error` were fixed light colours, so they kept those colours in dark mode, while their long twins `--trinity-input-border-color-*` followed the theme. Each short name is now `var(--trinity-input-border-color-*)`, like the other short aliases (`--trinity-card-bg`, `--trinity-input-bg-*`, `--trinity-status-*-bg`). In light mode the default border is now `#D4D4D8` (was `#E5E7EB`) and the hover border is now `#6B7280` (was `#9CA3AF`), the values their long twins already had. Focus and error do not change in light mode.
|
|
1985
|
+
|
|
1986
|
+
`--trinity-switch-track-border` comes from a new token, `component.switch.track.border`: gray 300 `#D4D4D8` in light mode, and `darkMode.colors.switch.track.border` gray 500 `#6B7280` in dark mode. These are the colours the React Switch already draws, and it now reads them from the tokens. The runtime CSS gains `--trinity-component-switch-track-border` in its light and dark blocks.
|
|
1987
|
+
|
|
1988
|
+
The CSS guides and the CSS package README also say which style to use for numbers. The `--trinity-data-numeric-*` variables are the monospace style for numbers in table cells. A headline or KPI number keeps the brand font (`var(--trinity-font-family)`) and adds `font-variant-numeric: tabular-nums`, which is how the React components render headline numbers.
|
|
1989
|
+
|
|
1990
|
+
Visual review: Storybook CSS Theme / Theme Aware Component Surfaces (src/stories/CSSTheme.stories.tsx), the "Input border (short alias)" and "Switch track border" tiles: the play check reads their computed border colours in light and dark
|
|
1991
|
+
- **Behavior correction (b)**: `--trinity-focus-ring` is a box-shadow in dark mode too, so `box-shadow: var(--trinity-focus-ring)` draws the focus ring in both themes. ([`d53b848`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d53b84895887207bff5b44065f3fb8b7424f0317))
|
|
1992
|
+
|
|
1993
|
+
The static stylesheet (`trinity.css` in both packages) declares `--trinity-focus-ring` as a box-shadow (`0 0 0 3px rgba(120, 65, 201, 0.25)`) in `:root`. Earlier 2.13.0 builds declared it as the bare colour `#A96ADA` in `[data-theme="dark"]` and in the `prefers-color-scheme: dark` block. A box-shadow cannot be a bare colour, so the declaration became invalid and the focus ring disappeared in dark mode (WCAG 2.4.7). `--trinity-focus-ring-default` reads that variable, so it had the same problem. Both now hold `0 0 0 3px rgba(169, 106, 218, 0.45)`, the same 3px ring in the dark focus colour (purple 400 at 45%, also used by `--trinity-focus-glow` and `--trinity-ai-focus-ring`). Published 2.12.1 never declared a dark value, so it kept the light ring in dark mode.
|
|
1994
|
+
|
|
1995
|
+
The value comes from a new dark token, `darkMode.effects.focus.ring` (also `darkModeEffects.focus.ring`). The runtime dark CSS (`injectDarkModeCssVariables()`, `generateDarkModeCssVariables()`) now declares `--trinity-semantic-effect-focus-ring` in its dark block, and `useTrinityTokens().effect('focus', 'ring')` returns it under a dark theme. The colour role `colors.focus.ring` (`#A96ADA` in dark) is unchanged. In the runtime CSS it is `--trinity-semantic-focus-ring`.
|
|
1996
|
+
|
|
1997
|
+
Visual review: Storybook CSS Theme / Dark Effect Matrix (src/stories/CSSTheme.stories.tsx), the "Focus ring" tile: its play check reads the computed dark box-shadow of `box-shadow: var(--trinity-focus-ring)`, which was "none" before this fix
|
|
1998
|
+
- **Behavior correction (b)**: Button colours and the TextField error outline from the Trinity theme yield to your `sx` and to your theme's component overrides again. ([`1191b86`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/1191b86224062e12535f9bc6a0f7d0069d1750c9))
|
|
1999
|
+
|
|
2000
|
+
The Button colour rules added in this release (contained primary, secondary and inherit, and the status colours of every variant) were scoped by a chain of `:not()` selectors and written as nested rules, so they outranked a plain `sx={{ backgroundColor }}`, `color` or `borderColor`, an `sx` `'&:hover'` colour and an app theme's `MuiButton.styleOverrides.contained['&:hover']`. The OutlinedInput error outline likewise beat an override of MUI's own `&.Mui-error .MuiOutlinedInput-notchedOutline` shape on hover and focus. The rules now weigh no more than MUI's own colour rules (their scope sits in `:where()`), and the resting colours set the custom properties MUI's Button reads, so your `sx` and your later overrides win, as they did in 2.11.1. The default colours do not change, and disabled, pending (`aria-disabled="true"`) and pressed (`aria-pressed="true"`) Buttons keep their state colours. A plain `sx` background applies at rest; on hover the Trinity hover colours still apply unless your `sx` sets `'&:hover'` too.
|
|
2001
|
+
- **No API or behavior change**: Both packages now ship an MIT `LICENSE` file (copyright holder: Trinity Design), and the type declarations carry `@since` tags for the API added since 2.11.1. ([`2038504`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/20385048a4ed5c31f73468f35410d332bc2fd6ab))
|
|
2002
|
+
|
|
2003
|
+
No API or behavior changes. The unreachable `src/components/index.ts` barrel was removed; no package entry exported it.
|
|
2004
|
+
- **Behavior correction (b)**: TransferList's and SimpleTable's select-all checkboxes report their partly selected state on every supported MUI 9.x. ([`fca7488`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/fca74889151b7fe7583ac07a900583a3b59425d3))
|
|
2005
|
+
|
|
2006
|
+
- `TransferList`: the header checkbox sets `aria-checked` on its input itself: `"mixed"` while only some enabled items are checked, `"true"` or `"false"` otherwise, always equal to its native state. Before, it relied on MUI, whose Checkbox writes `aria-checked="mixed"` only from a later 9.x release, so with `@mui/material` 9.0.0, the declared floor, screen readers read a partly selected panel as unchecked.
|
|
2007
|
+
- `SimpleTable`: the same fix, and the native `indeterminate` property now matches. Before, with `@mui/material` 9.0.0 a partly selected table read as unchecked, and with later 9.x the input said `"mixed"` while its native state said unchecked (axe `aria-conditional-attr`).
|
|
2008
|
+
|
|
2009
|
+
Nothing changes visually. Record: docs/remediation/HITL-B-23-select-all-mixed-state.md.
|
|
2010
|
+
|
|
2011
|
+
Also in this pull request, for the remediation program (M4 PR-1): the B-23 slice that turns the `min-peers` job green, so A-39 stage 2b can go live.
|
|
2012
|
+
- **No API or behavior change**: The built package splits two shared modules into their own chunks, `hooks.js` and `focus-on-replace.js`, so the main entry no longer loads the DataTable (AG Grid) chunk and the data-table entry no longer loads the forms chunk. ([`d951007`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/d95100783a98a61d99be701f5f7074889ea91493))
|
|
2013
|
+
|
|
2014
|
+
No API or behavior changes. Without the split, DataTable's use of `useClipboard` made the bundler place the whole hooks module inside the data-table chunk, and the main entry's size more than doubled (602,988 to 1,165,422 brotli bytes); the main entry now measures 605,751.
|
|
2015
|
+
- **Behavior correction (b)**: GanttView's group toggle shows its name as a tooltip. ([`a5452ba`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/a5452ba2169d2e71ffc18353ef60f519b01f6942))
|
|
2016
|
+
|
|
2017
|
+
The icon-only button that collapses and expands a group is named "Collapse group" or "Expand group"; it now shows the same text as a tooltip on hover and keyboard focus, so sighted users see the name screen reader users hear. Its chevron is drawn through Trinity's Icon with the same Feather glyph, at Icon's extra-small size (16px; it was 14px).
|
|
2018
|
+
|
|
2019
|
+
Visual review: docs/remediation/M4-index.md
|
|
2020
|
+
- **No API or behavior change**: OrgChart draws its expander and collapsed-node chevrons through Trinity's Icon. ([`956bc02`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/956bc0262110c357f208b7d6d12b065e77ae8734))
|
|
2021
|
+
|
|
2022
|
+
The glyph is the same Feather chevron, at Icon's extra-small size (16px; it was 14px) inside the unchanged 24px circle. Nothing else changes: the expander keeps its name, tooltip and keyboard path from 2.13.0's step 3.
|
|
2023
|
+
|
|
2024
|
+
Visual review: docs/remediation/M4-index.md
|
|
2025
|
+
- **Behavior correction (b)**: CatalogTemplate's cards are real buttons named by their title, and its search clear and filter-drawer close buttons are named. ([`0071104`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/00711044835fdeb8484223966e1647e379954363))
|
|
2026
|
+
|
|
2027
|
+
With `onItemClick`, each default card is one `ButtonBase` named by its title (it was a `role="button"` Paper with a hand-written key handler, named by all of its text). The search field is a `ClearableTextField`: its clear button is named "Clear search" with the same tooltip and returns focus to the field after a keyboard clear. The mobile filter drawer's close button is named "Close filters" with the same tooltip.
|
|
2028
|
+
|
|
2029
|
+
Visual review: docs/remediation/M4-index.md
|
|
2030
|
+
- **Behavior correction (b)**: An interactive InsightCard no longer nests its dismiss button and actions inside a button, and the dismiss button shows its name as a tooltip. ([`28ab931`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/28ab93160d9a23292ea3bebf44aaa27eeb097f70))
|
|
2031
|
+
|
|
2032
|
+
With `interactive`, the card holds a primary action: a button that covers the card, named by `ariaLabel` or the title, with Enter and Space and the theme's focus ring drawn inside the card edge. The dismiss button and the `actions` are separate buttons after it in the Tab order. The dismiss button shows "Dismiss insight" as its tooltip.
|
|
2033
|
+
|
|
2034
|
+
Visual review: docs/remediation/M4-index.md
|
|
2035
|
+
- **Behavior correction (b)**: Combobox's "Select All" and "Create" are options in its list, so the keyboard reaches them. ([`92441e2`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/92441e29e3360dbf47042b080e84fbcfa0034e5b))
|
|
2036
|
+
|
|
2037
|
+
"Select All" is the first option while nothing is typed (multi-select with `showSelectAll`), and "Create "…"" is the last option while the typed text can be created (`creatable`). Arrow keys and Enter reach both without leaving the input, and screen readers hear them as options; "Select All" reports `aria-selected` while every enabled option is selected. Both get option styling instead of the separate row and button above and below the list. The check boxes drawn in multi-select options are decorative marks, no longer unlabeled checkbox inputs inside the options.
|
|
2038
|
+
|
|
2039
|
+
Visual review: docs/remediation/M4-index.md
|
|
2040
|
+
- **Behavior correction (b)**: FileUpload's dropzone is a ButtonBase, and UploadDropZone's Browse button runs `onBrowse` itself. ([`29f9456`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/29f94561b12e93c5e373c618e1f1a4162a70cc26))
|
|
2041
|
+
|
|
2042
|
+
FileUpload's dropzone keeps its name, Enter, Space and drag and drop, and now draws the theme's focus ring; disabled, it also takes no drags. UploadDropZone's Browse button runs `onBrowse` once and no longer relies on the zone's click; a click elsewhere on the zone still browses.
|
|
2043
|
+
|
|
2044
|
+
Visual review: docs/remediation/M4-index.md
|
|
2045
|
+
- **No API or behavior change**: TreeView draws its chevrons, folder and file icons and its search icon through Trinity's Icon, with the same Material glyphs, sizes and colours; nothing renders differently. ([`fe0c19e`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/fe0c19e9ca3b76fc310a759453dc5e16d5ec6b37))
|
|
2046
|
+
|
|
2047
|
+
The defaults of `defaultLeafIcon`, `defaultBranchIcon` and `defaultBranchOpenIcon` are now `Icon` elements that render the same glyphs as before.
|
|
2048
|
+
- **Behavior correction (b)**: SettingsTemplate's phone-width navigation button is named and tells screen readers it opens a dialog. ([`6093756`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/6093756f785982b62fa39fabaae48afad9b7f8f3))
|
|
2049
|
+
|
|
2050
|
+
The menu button that opens the settings navigation drawer below the `md` breakpoint is named "Open settings navigation", with the same text as its tooltip, and reports `aria-haspopup="dialog"`, `aria-expanded` and, while the drawer is open, `aria-controls`. The drawer is a modal dialog named by the page title; Escape closes it and returns focus to the button.
|
|
2051
|
+
|
|
2052
|
+
Visual review: docs/remediation/M4-index.md
|
|
2053
|
+
- **Behavior correction (b)**: DateRangePicker's calendar and NotificationCenter's popover are named dialogs their triggers point at, and clickable notifications work from the keyboard. ([`9bcad4b`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/9bcad4bba6b6c6b6ef017eef0721ae4a321e1990))
|
|
2054
|
+
|
|
2055
|
+
DateRangePicker's trigger keeps your `id` and now also reports `aria-controls` while open; the calendar popover is a dialog named by the trigger, and the month buttons show their names as tooltips. NotificationCenter's bell shows its name as a tooltip and reports `aria-controls` while open; the popover is a dialog named by its title, anchors to the bell also when a parent opens it with `open={true}` (it opened at the page corner), and Escape returns focus to the bell. A notification with `onClick` is a button named by its title (Tab, Enter, Space), beside its dismiss button and actions; the dismiss button shows its name as a tooltip. The rules between notifications are list-item borders instead of separator items inside the list.
|
|
2056
|
+
|
|
2057
|
+
Visual review: docs/remediation/M4-index.md
|
|
2058
|
+
- **Behavior correction (b)**: BulkActionDock announces its selection count once per change, stops its entry animation under reduced motion, and shows a tooltip on Clear selection; TrinityLoader moves onto the toolkit with the same markup. ([`e9c415a`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e9c415a05d59f8dbf3a33bd7b94866c6e9ea8071))
|
|
2059
|
+
|
|
2060
|
+
The dock is no longer one large live region: a change of the selected count is announced once ("3 selected", or `selectionLabel`), through the nearest live region. Its slide-in stops when the user prefers reduced motion, and the icon-only Clear selection button shows its name as a tooltip. TrinityLoader's status region has the same role, politeness and label as before.
|
|
2061
|
+
|
|
2062
|
+
Visual review: docs/remediation/M4-index.md
|
|
2063
|
+
- **Behavior correction (b)**: The Trinity logo and mark in TopNavHeader, TopNavWithSidebar and TrinityLogo give each instance its own gradient and clip ids. ([`87d4dd8`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/87d4dd86adc65cb77edbbdae8cc34a9c6124a565))
|
|
2064
|
+
|
|
2065
|
+
Two logos on one page shared the ids `trinity-logo-gradient`, `trinity-logo-clip` and `trinity-icon-gradient`, so the second could resolve the first's definitions (and lose them when the first unmounted). The ids now come from `useId`; the drawing is unchanged.
|
|
2066
|
+
- **Behavior correction (b)**: HierarchicalDrillDown, SystemBanner and Tour keep their controlled state with MUI's useControlled, and SystemBanner reads its stored dismissal without a hydration mismatch. ([`68cfcad`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/68cfcad48eb815c7877ad5138fe7ca1a74dd046c))
|
|
2067
|
+
|
|
2068
|
+
`selectedPath` (HierarchicalDrillDown), `open` (SystemBanner) and `current` (Tour) decide on the first render whether the prop controls the state; switching between a value and `undefined` afterwards now logs MUI's controlled/uncontrolled warning in development. SystemBanner reads a stored dismissal (`storageKey`) through `useSyncExternalStore`: a client-only render hides a dismissed banner at once as before, and a server-rendered page shows it for the hydration render, then hides it, instead of hydrating with different markup.
|
|
2069
|
+
- **No API or behavior change**: HierarchicalDrillDown's lane chevrons and Tour's close icon are drawn through Trinity's Icon with the same glyphs. ([`009a454`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/009a454f3601cce0139b44e1363ce11a5d61a82f))
|
|
2070
|
+
|
|
2071
|
+
Tour's close icon is unchanged (Material close, 20px). HierarchicalDrillDown's Feather chevron moves from 14px to Icon's smallest size, 16px, with the same colours.
|
|
2072
|
+
|
|
2073
|
+
Visual review: docs/remediation/M4-index.md
|
|
2074
|
+
- **Behavior correction (a)**: DataTable's `onRowUpdate(newRow, oldRow)` now receives the row before the edit as `oldRow`, as its JSDoc says. ([`5d500bf`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/5d500bf756d38c2484bb966c92de696623e2a98b))
|
|
2075
|
+
|
|
2076
|
+
AG Grid changes the row object before DataTable hears about the edit, and DataTable passed that one object as both arguments. It now passes two fresh copies, with the edited field restored to its previous value in `oldRow` (B-66+, through the internal `rowUpdateUtils`). The internal, unreferenced `DragHandleCell` is deleted; no package entry exported it.
|
|
2077
|
+
- **Behavior correction (b)**: SplitPane keeps its controlled `size` through MUI's useControlled and resizes from the keyboard through the shared keyboard model, with the same keys and results. ([`1360c9f`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/1360c9f7b80e3f41eccfee041316c18a6bdc219d))
|
|
2078
|
+
|
|
2079
|
+
Nothing changes for keyboard and pointer users: arrows move the separator by 10px (50px with Shift), Home and End go to the bounds, the horizontal arrows swap in right-to-left layouts, and Enter resets. Switching `size` between a number and `undefined` after the first render now logs MUI's controlled/uncontrolled warning in development.
|
|
2080
|
+
- **Behavior correction (e)**: InsightEngineInput no longer throws when a controlled `value` or `attachments` prop switches to `undefined`; it renders empty and logs MUI's controlled/uncontrolled warning in development. ([`05bc0fa`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/05bc0fae4c6cf662d58ce2a2e9d28a3eec4e9791))
|
|
2081
|
+
- **Behavior correction (b)**: AI icon-only buttons show their names as tooltips (WI-57+, BEHAVIOR_CONTRACT "Icon-only buttons: the visible name"). ([`5673141`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/5673141d7984d20efa85affab52c0884e2a1eec2))
|
|
2082
|
+
|
|
2083
|
+
ChatHeader's close tooltip says "Close panel" (it said "Close"); ChatHistoryList's options button shows "Chat options"; AIDigestCard's dismiss button shows "Dismiss digest"; InsightEngineInput's attachment Retry tooltip says "Retry upload of {file}" (it said "Retry upload") and its Remove button shows "Remove {file}". The names do not change.
|
|
2084
|
+
|
|
2085
|
+
Visual review: docs/remediation/M4-index.md
|
|
2086
|
+
- **Behavior correction (b)**: AI components hosted in a flex row without a width, a grid `auto` column or a popover keep a usable width. ([`3072270`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/307227009db0946b3a71737ece454dcfdf3af98f))
|
|
2087
|
+
|
|
2088
|
+
AIClarificationPrompt was 0px wide in those hosts: its narrow-container wrapper is a size container, which cannot size from its content (ADR-11 (6) hosting rule, test (b)). The shared wrapper now gives the container the narrow container width (320px, `semanticTokens.spacing.layout.containerQuery.narrow`) as its intrinsic width, so AIClarificationPrompt, AIFeedbackForm, AIQueryDisclosure and InsightEngineInput are 320px wide where a host sizes them from content, and unchanged wherever the host sets a width. Found by F-22's hosting check (AI/HITL Conformance, ZeroWidthHosting).
|
|
2089
|
+
|
|
2090
|
+
Visual review: the owner's Storybook review: ai-hitl-conformance--zero-width-hosting (light, dark); AIClarificationPrompt, AIFeedbackForm, AIQueryDisclosure and InsightEngineInput look the same in hosts that set a width
|
|
2091
|
+
- **Behavior correction (b)**: AILabel on the shared details surface, with a keyboard-only focus ring and no hover grow under reduced motion. ([`3072270`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/307227009db0946b3a71737ece454dcfdf3af98f))
|
|
2092
|
+
|
|
2093
|
+
**WI-60+.** AILabel's explainability popover is the shared details surface, its focus ring shows for keyboard focus only, and it no longer grows on hover under reduced motion.
|
|
2094
|
+
|
|
2095
|
+
- The popover is the same anchored details dialog the HITL badges and source chips use (ADR-15 (2)): the same name (`popoverLabel`, `popoverLabelledBy`), keys and focus return as before. It is at most 360px wide (the compact container width) and never wider than the screen minus 32px; the fixed 280px minimum is gone, so a short explanation gets a narrower popover.
|
|
2096
|
+
- The focus ring (2px in the AI primary colour) is drawn on `:focus-visible`, so a pointer click no longer leaves a ring on the label.
|
|
2097
|
+
- The hover grow (`scale(1.02)`) does not run under `prefers-reduced-motion: reduce`; the hover glow stays.
|
|
2098
|
+
|
|
2099
|
+
**F-09.** DetailsPopover, the ninth internal shared block (F-09, ADR-02-R1): an anchored details dialog and its trigger hook for AILabel and the HITL molecules. Internal only (no package export, no per-block barrel), so nothing in the published API changes.
|
|
2100
|
+
|
|
2101
|
+
- `useDetailsPopover()` (on `useDisclosure`'s popup mode) gives the trigger `aria-haspopup="dialog"`, `aria-expanded` and `aria-controls` while open, and reports `onOpenChange(open, reason, event?)` with B-05's reasons plus `'action'`.
|
|
2102
|
+
- `DetailsPopover` is a MUI Popover whose paper is a named modal dialog (a visible heading, h2 by default via `HeadingLevel`), with a `LiveRegionProvider` inside, centre anchors, the compact container width capped at the viewport minus `spacing(4)`, motion-token durations (0 under reduced motion), the theme focus ring on the dialog when it holds focus itself (Highlight under forced colours), `preventClose`, `onAfterClose(reason)` after the exit transition, `fallbackFocusRef`, and `slotProps.paper` / `slotProps.heading`.
|
|
2103
|
+
- Story `Internal/Details Popover` (Default, States, OpenState with a play), built only with STORYBOOK_INCLUDE_INTERNAL=1.
|
|
2104
|
+
|
|
2105
|
+
**F-22 (harness).** HITL molecule conformance harness (F-22, part 1 of 3): test helpers only, nothing in the built packages changes.
|
|
2106
|
+
|
|
2107
|
+
- `src/testing/hitlConformance.ts` (jsdom): `renderInLogHost` (a `role="log"` host inside a `LiveRegionProvider`, as ChatThread hosts a molecule), `assertNoLandmarks` (no landmark roles, explicit or implicit, and no named form), `assertNoHeadingsOutsideDialogs`, `expectOneAnnouncement`.
|
|
2108
|
+
- `src/testing/hitlConformanceStory.tsx` (browser-safe, for stories): the hosting decorators `withFlexRowHost`, `withGridAutoHost`, `withPopoverHost` (MUI's PopoverPaper), `withNarrowHost` (the 320px narrow container token), and the play helpers `assertRenderedWidth`, `assertNoHorizontalOverflow`, `assertTargetSize` (24 by 24), `assertFocusOutline` (at least `semanticTokens.borders.width.focus`) and `assertForcedColorsVisible`.
|
|
2109
|
+
- Self-tested on DetailsPopover and MetaChip with negative fixtures; the Internal/Details Popover OpenState play uses the play helpers in the browser. The per-molecule cases come with F-10..F-17 (wave 2), the registry test and the AI/HITL Conformance story at the M6 gate step.
|
|
2110
|
+
|
|
2111
|
+
Visual review: WI-60+: the owner's Storybook review (M6 summary list): ai-ailabel--open-state, --with-popover, --playground, --default, --interactive-sizes, --all-sizes, --matrix, light and dark
|
|
2112
|
+
- **Behavior correction (b)**: HITL molecules keep a width in content-sized hosts; the conformance suite and the usage guidance. ([`3072270`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/307227009db0946b3a71737ece454dcfdf3af98f))
|
|
2113
|
+
|
|
2114
|
+
**F-22 (container width).** AI components hosted in a flex row without a width, a grid `auto` column or a popover keep a usable width.
|
|
2115
|
+
|
|
2116
|
+
AIClarificationPrompt was 0px wide in those hosts: its narrow-container wrapper is a size container, which cannot size from its content (ADR-11 (6) hosting rule, test (b)). The shared wrapper now gives the container the narrow container width (320px, `semanticTokens.spacing.layout.containerQuery.narrow`) as its intrinsic width, so AIClarificationPrompt, AIFeedbackForm, AIQueryDisclosure and InsightEngineInput are 320px wide where a host sizes them from content, and unchanged wherever the host sets a width. Found by F-22's hosting check (AI/HITL Conformance, ZeroWidthHosting).
|
|
2117
|
+
|
|
2118
|
+
**F-20, F-21, F-22 (suite).** Usage guidance, the HITL conformance suite and the release checks for the AI molecules (F-20, F-21, F-22). Documentation, tests and stories only.
|
|
2119
|
+
|
|
2120
|
+
- **F-20.** NAMING gains "Confidence display" and "Source attribution layout" and one "Why each AI molecule is not a variant" section for all seven molecules; MICROCOPY has one key table per M6 namespace (`ai.sources`, `ai.filters`, `ai.confidenceBadge`, `ai.verificationBadge`, `ai.sourceChip`, `ai.queryDisclosure`, `ai.clarificationPrompt`, `ai.feedbackForm`, `ai.feedbackControl`); each molecule story links them.
|
|
2121
|
+
- **F-22.** `hitlMolecules.conformance.test.tsx` runs the seven molecules in every listed state inside a chat log (no nested live region, landmark or stray heading; one announcement per transition), with AISourceChip's 'Show query' revealing the real AIQueryDisclosure; the `AI/HITL Conformance` stories measure hosting widths, overflow at 320px, target size, focus outlines and forced colours in the browser.
|
|
2122
|
+
- **F-21.** The packed consumer smoke imports and server-renders the seven molecules in both themes and both directions; the size ratchets are re-measured with a record (docs/remediation/size/M6-integration.md).
|
|
2123
|
+
|
|
2124
|
+
Visual review: F-22 (container width): the owner's Storybook review: ai-hitl-conformance--zero-width-hosting (light, dark); AIClarificationPrompt, AIFeedbackForm, AIQueryDisclosure and InsightEngineInput look the same in hosts that set a width
|
|
2125
|
+
- **Behavior correction (b)**: Component icons follow the app's IconProvider everywhere. ([`38628b6`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/38628b6db03f7bda47109e5e64d557baadd706df))
|
|
2126
|
+
|
|
2127
|
+
**C-38+.** Component icons follow your app's `IconProvider` everywhere (ADR-10, ADR-23 (b) visible correction).
|
|
2128
|
+
|
|
2129
|
+
While the icon migration ran, some components fixed each icon to the library their old direct import came from (`library="material"` or `library="feather"` on the internal `<Icon>`), so they ignored the `IconProvider` you set. Those fixed choices are gone: every component now draws its icons from the nearest `IconProvider` (Material when there is none).
|
|
2130
|
+
|
|
2131
|
+
- In a Material app (no `IconProvider`, or `defaultLibrary="material"`) these components change from Feather to Material glyphs: AuditDrawer, EntityDetailPanel, AssessmentDrawer, GanttView, OrgChart, CatalogTemplate, NotificationCenter, BulkActionDock, HierarchicalDrillDown, WizardTemplate, ConnectorWizardTemplate, OnboardingTemplate and ResizablePanel (the AI panel's close button).
|
|
2132
|
+
- In a Feather app (`<IconProvider defaultLibrary="feather">`) these components change from Material to Feather glyphs: FilterBar, ThreadHistorySidebar (the AI panel's conversation history), DocumentCard, DiffViewer, ClearableTextField, InsightCard, Timeline, Combobox, TreeView, SettingsTemplate, Tour, Carousel and the AI FAB.
|
|
2133
|
+
- Icon names, sizes, colors, accessible names and layout are unchanged; only the drawing of each glyph follows your library. No action is needed unless a visual test or screenshot baseline captured the old glyphs: update those baselines.
|
|
2134
|
+
- The `Icon` component's own `library` prop is unchanged and still pins a single icon in your code.
|
|
2135
|
+
|
|
2136
|
+
Visual review: the owner's Storybook review of the story ids in docs/remediation/M7-index.md (light, dark; RTL, docked panel widths and fullscreen where listed), with the lanes' PNG compares against their bases
|
|
2137
|
+
- **Behavior correction (b)**: The deprecated AIChatMessage and QueryInput show each icon button's name as its tooltip. ([`e76442e`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/e76442eb90baf9d5da9731920d13b37dd0cfe8d3))
|
|
2138
|
+
|
|
2139
|
+
The tooltips of six icon buttons said something shorter than the button's accessible name, so a voice-control user who said what they saw did not hit the button. Each tooltip is now the button's name: AIChatMessage's "Mark as good response", "Mark as bad response", "Share message" and "Regenerate response", and QueryInput's "Clear input" and "Send message". QueryInput's Send button no longer shows "(Enter)" in its tooltip; it declares Enter as its shortcut with `aria-keyshortcuts` (Enter still sends, as before), the resolution InsightEngineInput uses. QueryInput's Clear and dictation buttons now sit in a wrapper like Send, so their tooltips also show while the input is disabled, without an MUI warning, and none of the three wrappers repeats the button's name.
|
|
2140
|
+
|
|
2141
|
+
Both components stay deprecated: migrate to AIMessage and InsightEngineInput.
|
|
2142
|
+
|
|
2143
|
+
Visual review: the owner's local Storybook review at integration (AIChatMessage and QueryInput are deprecated and have no story; the tooltip text, the keyboard tooltip and the wrappers are checked in AIChat.test.tsx and QueryInput.test.tsx); approved by the owner, 2026-10-09
|
|
2144
|
+
- **Behavior correction (a, b, c)**: GaugeCard draws its track and value arc on one circle inside the card, and GaugeChart fits its semicircle and centres its value. ([`5231508`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/523150817954213ac06a1ccfc2aea42e92f9db8c))
|
|
2145
|
+
|
|
2146
|
+
GaugeCard's arcs used the wrong SVG sweep flag, so every arc was drawn on the circle mirrored across its own chord: the track and the value arc sat on different centres, rings spilled out of the card and the value label was off centre. The gauge is now the 260° arc its documentation describes, from 7 o'clock over the top to 5 o'clock, at every size and value (values outside `min`..`max` are clamped, and negative and custom ranges are measured from `min`). The value arc is the track cut to length, so it cannot drift off the track, and the value is centred on the circle. The track, card border, label, eyebrow and caption follow the theme mode (they were light-mode colors in dark mode, and the eyebrow and caption failed contrast), the `neutral` severity uses `text.secondary` (the old `text.disabled` was 2.5:1), `label` and `eyebrow` render in a `div`, so block content is valid markup, and the loading arc and value transition stop when the user prefers reduced motion.
|
|
2147
|
+
|
|
2148
|
+
GaugeChart sizes its semicircle to the largest that fits the chart and places the value inside the arc's hole, sized to fit; before, the gauge took about half the space and the value overlapped the arc in small charts.
|
|
2149
|
+
|
|
2150
|
+
GeoStatusMap sizes its SVG with CSS (full width, height from the `viewBox`) instead of the invalid `height="auto"` attribute that browsers reported as an error. Its region is named by `ariaLabel`; before, the legend title "Status" named it, and with `showLegend={false}` the name pointed at nothing. Its default canvas, base map, border, legend and marker outline colors follow the theme mode, neutral markers use `text.secondary`, and `pulse` markers hold still under reduced motion. The stories now draw the real US state outlines (us-atlas, U.S. Census Bureau data) in the Albers USA projection with Alaska and Hawaii insets, and project the sites through the same projection; the geometry is a Storybook asset and is not in the package.
|
|
2151
|
+
|
|
2152
|
+
Visual review: docs/remediation/OWNER-ring-charts-and-map.md
|
|
2153
|
+
- **Additive**: Restore React 19.2 support: the `react` and `react-dom` peer ranges widen from
|
|
2154
|
+
`^19.3.0` to `^19.2.4`. Release 2.10.7 narrowed the range from `^19.2.7` to
|
|
2155
|
+
`^19.3.0` in a patch release, which blocked installs on React 19.2.x. The new
|
|
2156
|
+
floor, 19.2.4, covers the React 19.2 versions that known consumers run. No
|
|
2157
|
+
runtime code changes. ([`921fde2`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/921fde2032e4f9f4cbb13663db7d777cc176608b))
|
|
2158
|
+
- **No API or behavior change**: Build with a patched release tool. Resolves shell-quote 1.11.0 for
|
|
2159
|
+
GHSA-pqg4-j6r4-53mv / CVE-2026-102422 (critical): `quote()` command
|
|
2160
|
+
injection through a line terminator in a token after a `{ comment }`
|
|
2161
|
+
token. The only copy is under `@changesets/cli` > `launch-editor`, a
|
|
2162
|
+
development tool. The published packages' `dependencies` and
|
|
2163
|
+
`peerDependencies` do not change, and no component behavior changes.
|
|
2164
|
+
Record: `docs/remediation/SEC-DEPS-2026-10.md`, row 55. ([`7697d4e`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/7697d4e77b4be8f95c7ebdb5cd68ade9907e538e))
|
|
2165
|
+
- Make generated token CSS byte-reproducible by removing wall-clock timestamps
|
|
2166
|
+
and enforcing exact consecutive-generation checks. ([`6bd9648`](https://github.com/Trinity-UI-Components/trinity-design-system/commit/6bd96480df8a4525d682e0fec48710c96c563792))
|
|
2167
|
+
|
|
2168
|
+
## Releases up to 2.11.1 (conventional-changelog era)
|
|
2169
|
+
|
|
2170
|
+
The entries from here down, up to and including 2.11.1, were generated from commit titles by conventional-changelog, which rewrote this file on every run. From the next release on, `changeset version` adds each release above this heading from the reviewed changesets in `.changeset/` ([docs/CHANGESETS.md](docs/CHANGESETS.md)); `docs/releases/<version>.md` lists the same changes grouped by ADR-23 category.
|
|
2171
|
+
|
|
1
2172
|
## [2.11.1](https://github.com/Trinity-UI-Components/trinity-design-system/compare/v1.0.0...v2.11.1) (2026-10-05)
|
|
2
2173
|
|
|
3
2174
|
### Features
|