@ds-mo/ui 9.1.0 → 9.2.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/README.md +0 -79
- package/dist/.build-stamp +1 -1
- package/dist/agent-patterns.json +165 -1
- package/dist/agent.json +2131 -639
- package/dist/angular/ds-agent-activity.d.ts +12 -0
- package/dist/angular/ds-agent-activity.js +38 -0
- package/dist/angular/ds-agent-response.d.ts +12 -0
- package/dist/angular/ds-agent-response.js +38 -0
- package/dist/angular/ds-agent-source-list.d.ts +12 -0
- package/dist/angular/ds-agent-source-list.js +38 -0
- package/dist/angular/ds-agent-tool-call.d.ts +12 -0
- package/dist/angular/ds-agent-tool-call.js +38 -0
- package/dist/angular/ds-attachment-list.d.ts +12 -0
- package/dist/angular/ds-attachment-list.js +38 -0
- package/dist/angular/ds-avatar.d.ts +12 -0
- package/dist/angular/ds-avatar.js +38 -0
- package/dist/angular/ds-code-block.d.ts +12 -0
- package/dist/angular/ds-code-block.js +38 -0
- package/dist/angular/ds-conversation-list-item.d.ts +19 -0
- package/dist/angular/ds-conversation-list-item.js +42 -0
- package/dist/angular/ds-conversation-list-section.d.ts +12 -0
- package/dist/angular/ds-conversation-list-section.js +38 -0
- package/dist/angular/ds-conversation-list.d.ts +12 -0
- package/dist/angular/ds-conversation-list.js +37 -0
- package/dist/angular/ds-markdown.d.ts +12 -0
- package/dist/angular/ds-markdown.js +38 -0
- package/dist/angular/ds-menu.d.ts +6 -1
- package/dist/angular/ds-menu.js +7 -4
- package/dist/angular/ds-message-bubble.d.ts +12 -0
- package/dist/angular/ds-message-bubble.js +38 -0
- package/dist/angular/ds-message-composer.d.ts +23 -0
- package/dist/angular/ds-message-composer.js +49 -0
- package/dist/angular/ds-message-scroller.d.ts +15 -0
- package/dist/angular/ds-message-scroller.js +43 -0
- package/dist/angular/ds-message.d.ts +12 -0
- package/dist/angular/ds-message.js +38 -0
- package/dist/angular/ds-panel-nav.d.ts +1 -1
- package/dist/angular/ds-panel-nav.js +3 -3
- package/dist/angular/ds-panel-tool-header.d.ts +25 -0
- package/dist/angular/ds-panel-tool-header.js +49 -0
- package/dist/angular/ds-panel-tool-search.d.ts +19 -0
- package/dist/angular/ds-panel-tool-search.js +49 -0
- package/dist/angular/ds-panel-tools.d.ts +30 -1
- package/dist/angular/ds-panel-tools.js +14 -5
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +17 -0
- package/dist/angular/proxies.d.ts +17 -0
- package/dist/angular/proxies.js +17 -0
- package/dist/components/ds-agent-activity.d.ts +11 -0
- package/dist/components/ds-agent-activity.js +2 -0
- package/dist/components/ds-agent-activity.js.map +1 -0
- package/dist/components/ds-agent-response.d.ts +11 -0
- package/dist/components/ds-agent-response.js +2 -0
- package/dist/components/ds-agent-response.js.map +1 -0
- package/dist/components/ds-agent-source-list.d.ts +11 -0
- package/dist/components/ds-agent-source-list.js +2 -0
- package/dist/components/ds-agent-source-list.js.map +1 -0
- package/dist/components/ds-agent-tool-call.d.ts +11 -0
- package/dist/components/ds-agent-tool-call.js +2 -0
- package/dist/components/ds-agent-tool-call.js.map +1 -0
- package/dist/components/ds-app-shell.js +1 -1
- package/dist/components/ds-app-shell.js.map +1 -1
- package/dist/components/ds-attachment-list.d.ts +11 -0
- package/dist/components/ds-attachment-list.js +2 -0
- package/dist/components/ds-attachment-list.js.map +1 -0
- package/dist/components/ds-avatar.d.ts +11 -0
- package/dist/components/ds-avatar.js +2 -0
- package/dist/components/ds-avatar.js.map +1 -0
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-banner.js +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-nav.js.map +1 -1
- package/dist/components/ds-button-filled.js +1 -1
- package/dist/components/ds-button-unfilled.js +1 -1
- package/dist/components/ds-card-data-viz-donut.js +1 -1
- package/dist/components/ds-card-setting.js +1 -1
- package/dist/components/ds-card-shell-data-viz.js +1 -1
- package/dist/components/ds-card.js +1 -1
- package/dist/components/ds-chart-bar.js +1 -1
- package/dist/components/ds-chart-bar.js.map +1 -1
- package/dist/components/ds-chart-donut.js +1 -1
- package/dist/components/ds-chart-legend.js +1 -1
- package/dist/components/ds-chart-line.js +1 -1
- package/dist/components/ds-checkbox.js +1 -1
- package/dist/components/ds-chip.js +1 -1
- package/dist/components/ds-code-block.d.ts +11 -0
- package/dist/components/ds-code-block.js +2 -0
- package/dist/components/ds-code-block.js.map +1 -0
- package/dist/components/ds-conversation-list-item.d.ts +11 -0
- package/dist/components/ds-conversation-list-item.js +2 -0
- package/dist/components/ds-conversation-list-item.js.map +1 -0
- package/dist/components/ds-conversation-list-section.d.ts +11 -0
- package/dist/components/ds-conversation-list-section.js +2 -0
- package/dist/components/ds-conversation-list-section.js.map +1 -0
- package/dist/components/ds-conversation-list.d.ts +11 -0
- package/dist/components/ds-conversation-list.js +2 -0
- package/dist/components/ds-conversation-list.js.map +1 -0
- package/dist/components/ds-divider.js +1 -1
- package/dist/components/ds-divider.js.map +1 -1
- package/dist/components/ds-empty-state.js +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-icon.js +1 -1
- package/dist/components/ds-input.js +1 -1
- package/dist/components/ds-markdown.d.ts +11 -0
- package/dist/components/ds-markdown.js +2 -0
- package/dist/components/ds-markdown.js.map +1 -0
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-message-bubble.d.ts +11 -0
- package/dist/components/ds-message-bubble.js +2 -0
- package/dist/components/ds-message-bubble.js.map +1 -0
- package/dist/components/ds-message-composer.d.ts +11 -0
- package/dist/components/ds-message-composer.js +2 -0
- package/dist/components/ds-message-composer.js.map +1 -0
- package/dist/components/ds-message-scroller.d.ts +11 -0
- package/dist/components/ds-message-scroller.js +2 -0
- package/dist/components/ds-message-scroller.js.map +1 -0
- package/dist/components/ds-message.d.ts +11 -0
- package/dist/components/ds-message.js +2 -0
- package/dist/components/ds-message.js.map +1 -0
- package/dist/components/ds-modal.js +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-panel-nav.js.map +1 -1
- package/dist/components/ds-panel-sub-nav.js +1 -1
- package/dist/components/ds-panel-tool-header.d.ts +11 -0
- package/dist/components/ds-panel-tool-header.js +2 -0
- package/dist/components/ds-panel-tool-header.js.map +1 -0
- package/dist/components/ds-panel-tool-search.d.ts +11 -0
- package/dist/components/ds-panel-tool-search.js +2 -0
- package/dist/components/ds-panel-tool-search.js.map +1 -0
- package/dist/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-panel-tools.js.map +1 -1
- package/dist/components/ds-radio.js +1 -1
- package/dist/components/ds-select-multi.js +1 -1
- package/dist/components/ds-select-multi.js.map +1 -1
- package/dist/components/ds-select.js +1 -1
- package/dist/components/ds-select.js.map +1 -1
- package/dist/components/ds-shell-gradient-picker.js +1 -1
- package/dist/components/ds-shell-gradient-picker.js.map +1 -1
- package/dist/components/ds-shell-gradient-swatch.js +1 -1
- package/dist/components/ds-skeleton.js +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-slider.js.map +1 -1
- package/dist/components/ds-swatch-picker.js +1 -1
- package/dist/components/ds-switch.js +1 -1
- package/dist/components/ds-tab-group.js +1 -1
- package/dist/components/ds-tab-group.js.map +1 -1
- package/dist/components/ds-tag.js +1 -1
- package/dist/components/ds-tag.js.map +1 -1
- package/dist/components/ds-text.js +1 -1
- package/dist/components/ds-tooltip-data-viz.js +1 -1
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/index.d.ts +14 -0
- package/dist/components/{p-HBIxvS5C.js → p-B1pWGQn1.js} +2 -2
- package/dist/components/p-BBu_Svz7.js +2 -0
- package/dist/components/p-BBu_Svz7.js.map +1 -0
- package/dist/components/p-BHpWYKtP.js +2 -0
- package/dist/components/p-BHpWYKtP.js.map +1 -0
- package/dist/components/{p-KRGBGTu5.js → p-BO6C8abO.js} +2 -2
- package/dist/components/p-BRfOCc_h.js +2 -0
- package/dist/components/p-BRfOCc_h.js.map +1 -0
- package/dist/components/{p-CbS_WWs2.js → p-BSOZXagj.js} +2 -2
- package/dist/components/p-BSOZXagj.js.map +1 -0
- package/dist/components/p-BhC1YyId.js +2 -0
- package/dist/components/p-BhC1YyId.js.map +1 -0
- package/dist/components/p-BjtXYFhw.js +2 -0
- package/dist/components/p-BjtXYFhw.js.map +1 -0
- package/dist/components/p-CBYuvZKM.js +2 -0
- package/dist/components/p-CBYuvZKM.js.map +1 -0
- package/dist/components/{p-CZ9wemOA.js → p-CJlgONwR.js} +2 -2
- package/dist/components/p-CKaJx0IX.js +2 -0
- package/dist/components/p-CKaJx0IX.js.map +1 -0
- package/dist/components/p-CWNdl52Y.js +2 -0
- package/dist/components/p-CWNdl52Y.js.map +1 -0
- package/dist/components/{p-DT2MK8Kt.js → p-CblAABEo.js} +2 -2
- package/dist/components/p-CeFBA7Dx.js +2 -0
- package/dist/components/p-CeFBA7Dx.js.map +1 -0
- package/dist/components/p-CiKGqsA2.js +2 -0
- package/dist/components/{p-BLzivrE3.js.map → p-CiKGqsA2.js.map} +1 -1
- package/dist/components/p-Ci_nVk5P.js +2 -0
- package/dist/components/p-Ci_nVk5P.js.map +1 -0
- package/dist/components/p-Cijzn3YQ.js +2 -0
- package/dist/components/p-Cijzn3YQ.js.map +1 -0
- package/dist/components/{p-BbV68F23.js → p-CiwdnKKp.js} +2 -2
- package/dist/components/p-DFxqk71w.js +2 -0
- package/dist/components/p-DFxqk71w.js.map +1 -0
- package/dist/components/p-DVsje4AY.js +2 -0
- package/dist/components/p-DVsje4AY.js.map +1 -0
- package/dist/components/p-DaQ0ZE_V.js +2 -0
- package/dist/components/p-DaQ0ZE_V.js.map +1 -0
- package/dist/components/p-DiooIO2B.js +2 -0
- package/dist/components/p-DiooIO2B.js.map +1 -0
- package/dist/components/p-Dj5MejHv.js +2 -0
- package/dist/components/p-Dj5MejHv.js.map +1 -0
- package/dist/components/{p-B-zhgAvL.js → p-DnMCRAwx.js} +2 -2
- package/dist/components/{p-DyZj3k9S.js → p-Dst-OliP.js} +2 -2
- package/dist/components/p-Dst-OliP.js.map +1 -0
- package/dist/components/p-T0Mzhv3-.js +2 -0
- package/dist/components/p-T0Mzhv3-.js.map +1 -0
- package/dist/components/p-a9nGf1s6.js +2 -0
- package/dist/components/p-a9nGf1s6.js.map +1 -0
- package/dist/components/p-gidVv1FX.js +2 -0
- package/dist/components/p-gidVv1FX.js.map +1 -0
- package/dist/components/p-kezp2_Xu.js +2 -0
- package/dist/components/p-kezp2_Xu.js.map +1 -0
- package/dist/components/p-rEe-3KR_.js +2 -0
- package/dist/components/p-rEe-3KR_.js.map +1 -0
- package/dist/components/{p-C7RObioO.js → p-xB0GPG3-.js} +2 -2
- package/dist/docs/components.json +8294 -4079
- package/dist/lib/components/Menu/menu-placement.d.ts +13 -0
- package/dist/lib/shell/index.d.ts +1 -1
- package/dist/lib/shell/index.js +8 -0
- package/dist/lib/shell/index.js.map +2 -2
- package/dist/mcp-data/registry/agent-activity.json +158 -0
- package/dist/mcp-data/registry/agent-response.json +211 -0
- package/dist/mcp-data/registry/agent-source-list.json +153 -0
- package/dist/mcp-data/registry/agent-tool-call.json +213 -0
- package/dist/mcp-data/registry/app-shell.json +2 -2
- package/dist/mcp-data/registry/attachment-list.json +137 -0
- package/dist/mcp-data/registry/avatar.json +154 -0
- package/dist/mcp-data/registry/bar-nav.json +2 -2
- package/dist/mcp-data/registry/button-unfilled.json +2 -2
- package/dist/mcp-data/registry/code-block.json +150 -0
- package/dist/mcp-data/registry/conversation-list-item.json +283 -0
- package/dist/mcp-data/registry/conversation-list-section.json +139 -0
- package/dist/mcp-data/registry/conversation-list.json +101 -0
- package/dist/mcp-data/registry/markdown.json +141 -0
- package/dist/mcp-data/registry/menu.json +43 -7
- package/dist/mcp-data/registry/message-bubble.json +119 -0
- package/dist/mcp-data/registry/message-composer.json +265 -0
- package/dist/mcp-data/registry/message-scroller.json +217 -0
- package/dist/mcp-data/registry/message.json +255 -0
- package/dist/mcp-data/registry/panel-nav.json +20 -2
- package/dist/mcp-data/registry/panel-tool-header.json +342 -0
- package/dist/mcp-data/registry/panel-tool-search.json +387 -0
- package/dist/mcp-data/registry/panel-tools.json +150 -10
- package/dist/mcp-data/registry/registry.json +2159 -616
- package/dist/mcp-data/registry/select.json +1 -1
- package/dist/mcp-data/registry/slider.json +4 -4
- package/dist/mcp-data/registry/switch.json +1 -1
- package/dist/mcp-data/registry/tab-group.json +2 -2
- package/dist/mcp-data/registry/tag.json +2 -2
- package/dist/mcp-data/registry/text.json +1 -1
- package/dist/react/components.d.ts +17 -0
- package/dist/react/components.js +17 -0
- package/dist/react/ds-agent-activity.d.ts +9 -0
- package/dist/react/ds-agent-activity.js +12 -0
- package/dist/react/ds-agent-response.d.ts +9 -0
- package/dist/react/ds-agent-response.js +12 -0
- package/dist/react/ds-agent-source-list.d.ts +9 -0
- package/dist/react/ds-agent-source-list.js +12 -0
- package/dist/react/ds-agent-tool-call.d.ts +9 -0
- package/dist/react/ds-agent-tool-call.js +12 -0
- package/dist/react/ds-attachment-list.d.ts +9 -0
- package/dist/react/ds-attachment-list.js +12 -0
- package/dist/react/ds-avatar.d.ts +9 -0
- package/dist/react/ds-avatar.js +12 -0
- package/dist/react/ds-code-block.d.ts +9 -0
- package/dist/react/ds-code-block.js +12 -0
- package/dist/react/ds-conversation-list-item.d.ts +14 -0
- package/dist/react/ds-conversation-list-item.js +12 -0
- package/dist/react/ds-conversation-list-section.d.ts +9 -0
- package/dist/react/ds-conversation-list-section.js +12 -0
- package/dist/react/ds-conversation-list.d.ts +9 -0
- package/dist/react/ds-conversation-list.js +12 -0
- package/dist/react/ds-markdown.d.ts +9 -0
- package/dist/react/ds-markdown.js +12 -0
- package/dist/react/ds-menu.d.ts +1 -0
- package/dist/react/ds-menu.js +1 -0
- package/dist/react/ds-message-bubble.d.ts +9 -0
- package/dist/react/ds-message-bubble.js +12 -0
- package/dist/react/ds-message-composer.d.ts +16 -0
- package/dist/react/ds-message-composer.js +16 -0
- package/dist/react/ds-message-scroller.d.ts +12 -0
- package/dist/react/ds-message-scroller.js +12 -0
- package/dist/react/ds-message.d.ts +9 -0
- package/dist/react/ds-message.js +12 -0
- package/dist/react/ds-panel-tool-header.d.ts +17 -0
- package/dist/react/ds-panel-tool-header.js +16 -0
- package/dist/react/ds-panel-tool-search.d.ts +14 -0
- package/dist/react/ds-panel-tool-search.js +16 -0
- package/dist/react/ds-panel-tools.d.ts +10 -0
- package/dist/react/ds-panel-tools.js +3 -0
- package/dist/types/components/AgentActivity/AgentActivity.d.ts +14 -0
- package/dist/types/components/AgentResponse/AgentResponse.d.ts +12 -0
- package/dist/types/components/AgentSourceList/AgentSourceList.d.ts +9 -0
- package/dist/types/components/AgentToolCall/AgentToolCall.d.ts +13 -0
- package/dist/types/components/AppShell/AppShell.d.ts +4 -0
- package/dist/types/components/AttachmentList/AttachmentList.d.ts +8 -0
- package/dist/types/components/Avatar/Avatar.d.ts +11 -0
- package/dist/types/components/BarNav/BarNav.d.ts +2 -0
- package/dist/types/components/ButtonUnfilled/ButtonUnfilled.d.ts +2 -0
- package/dist/types/components/CodeBlock/CodeBlock.d.ts +11 -0
- package/dist/types/components/ConversationList/ConversationList.d.ts +4 -0
- package/dist/types/components/ConversationListItem/ConversationListItem.d.ts +24 -0
- package/dist/types/components/ConversationListSection/ConversationListSection.d.ts +9 -0
- package/dist/types/components/Markdown/Markdown.d.ts +15 -0
- package/dist/types/components/Menu/Menu.d.ts +9 -0
- package/dist/types/components/Menu/menu-placement.d.ts +13 -0
- package/dist/types/components/Menu/menu-types.d.ts +1 -1
- package/dist/types/components/Message/Message.d.ts +17 -0
- package/dist/types/components/MessageBubble/MessageBubble.d.ts +6 -0
- package/dist/types/components/MessageComposer/MessageComposer.d.ts +29 -0
- package/dist/types/components/MessageScroller/MessageScroller.d.ts +41 -0
- package/dist/types/components/PanelNav/PanelNav.d.ts +5 -0
- package/dist/types/components/PanelToolHeader/PanelToolHeader.d.ts +34 -0
- package/dist/types/components/PanelToolSearch/PanelToolSearch.d.ts +35 -0
- package/dist/types/components/PanelTools/PanelTools.d.ts +40 -1
- package/dist/types/components/PanelTools/panel-tools-types.d.ts +20 -0
- package/dist/types/components/PanelTools/panel-tools-utils.d.ts +2 -0
- package/dist/types/components/Slider/Slider.d.ts +1 -1
- package/dist/types/components/conversation-types.d.ts +55 -0
- package/dist/types/components.d.ts +1229 -14
- package/dist/types/shell/index.d.ts +1 -1
- package/dist/types/utils/choice-list-parts.d.ts +4 -1
- package/package.json +11 -11
- package/dist/components/p-BLzivrE3.js +0 -2
- package/dist/components/p-C7UFYvsO.js +0 -2
- package/dist/components/p-C7UFYvsO.js.map +0 -1
- package/dist/components/p-CXF8yd-g.js +0 -2
- package/dist/components/p-CXF8yd-g.js.map +0 -1
- package/dist/components/p-CbS_WWs2.js.map +0 -1
- package/dist/components/p-DyZj3k9S.js.map +0 -1
- package/dist/components/p-VKIsOVza.js +0 -2
- package/dist/components/p-VKIsOVza.js.map +0 -1
- package/dist/components/p-tQ1QHeOE.js +0 -2
- package/dist/components/p-tQ1QHeOE.js.map +0 -1
- /package/dist/components/{p-HBIxvS5C.js.map → p-B1pWGQn1.js.map} +0 -0
- /package/dist/components/{p-KRGBGTu5.js.map → p-BO6C8abO.js.map} +0 -0
- /package/dist/components/{p-CZ9wemOA.js.map → p-CJlgONwR.js.map} +0 -0
- /package/dist/components/{p-DT2MK8Kt.js.map → p-CblAABEo.js.map} +0 -0
- /package/dist/components/{p-BbV68F23.js.map → p-CiwdnKKp.js.map} +0 -0
- /package/dist/components/{p-B-zhgAvL.js.map → p-DnMCRAwx.js.map} +0 -0
- /package/dist/components/{p-C7RObioO.js.map → p-xB0GPG3-.js.map} +0 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "message-composer",
|
|
4
|
+
"title": "MessageComposer",
|
|
5
|
+
"description": "Two-line auto-growing conversation textarea with submit, stop, error, and extensible leading-tool or trailing-action regions.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"meta": {
|
|
8
|
+
"library": "@ds-mo/ui",
|
|
9
|
+
"distribution": "npm",
|
|
10
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/message-composer",
|
|
11
|
+
"source": "src/wc/components/MessageComposer/MessageComposer.tsx",
|
|
12
|
+
"intentStatus": "complete",
|
|
13
|
+
"intent": {
|
|
14
|
+
"audience": "general",
|
|
15
|
+
"status": "experimental",
|
|
16
|
+
"summary": "Two-line auto-growing conversation textarea with submit, stop, error, and extensible leading-tool or trailing-action regions.",
|
|
17
|
+
"useWhen": [
|
|
18
|
+
"A conversation accepts multi-line text and needs Enter-to-submit with a streaming stop action."
|
|
19
|
+
],
|
|
20
|
+
"avoidWhen": [
|
|
21
|
+
"The text is a conventional form field submitted with unrelated fields."
|
|
22
|
+
],
|
|
23
|
+
"commonlyComposedWith": [
|
|
24
|
+
"component:ds-message-scroller",
|
|
25
|
+
"component:ds-button-filled",
|
|
26
|
+
"component:ds-button-unfilled"
|
|
27
|
+
],
|
|
28
|
+
"patterns": [
|
|
29
|
+
"pattern:conversation"
|
|
30
|
+
],
|
|
31
|
+
"accessibility": [
|
|
32
|
+
"Always provide a localized textarea label and submit or stop action names.",
|
|
33
|
+
"Shift plus Enter inserts a newline while Enter submits non-empty content.",
|
|
34
|
+
"Streaming and error states remain perceivable without relying on icon shape alone."
|
|
35
|
+
],
|
|
36
|
+
"states": [
|
|
37
|
+
"The owner controls draft value and transport state; the composer emits intent without performing transport.",
|
|
38
|
+
"Use the tools slot for leading actions such as add and the actions slot for trailing actions such as dictation; the built-in submit or stop action remains last.",
|
|
39
|
+
"The submit action defaults to brand for both agent and person-to-person conversations; change submitIntent only when the action consequence has a different semantic intent."
|
|
40
|
+
],
|
|
41
|
+
"responsiveBehavior": [
|
|
42
|
+
"The textarea begins at two body-medium lines, grows one line at a time through six lines, and then scrolls internally."
|
|
43
|
+
],
|
|
44
|
+
"references": [
|
|
45
|
+
{
|
|
46
|
+
"label": "Storybook examples",
|
|
47
|
+
"path": "src/wc/components/MessageComposer/MessageComposer.stories.ts"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"label": "Scroller overlay composition",
|
|
51
|
+
"path": "src/wc/components/MessageScroller/MessageScroller.stories.ts"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"api": {
|
|
56
|
+
"props": {
|
|
57
|
+
"isInactive": {
|
|
58
|
+
"type": "boolean",
|
|
59
|
+
"resolvedType": "boolean",
|
|
60
|
+
"attribute": "is-inactive",
|
|
61
|
+
"default": "false",
|
|
62
|
+
"required": false,
|
|
63
|
+
"mutable": false
|
|
64
|
+
},
|
|
65
|
+
"label": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"resolvedType": "string",
|
|
68
|
+
"attribute": "label",
|
|
69
|
+
"default": "'Message'",
|
|
70
|
+
"required": false,
|
|
71
|
+
"mutable": false
|
|
72
|
+
},
|
|
73
|
+
"placeholder": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"resolvedType": "string",
|
|
76
|
+
"attribute": "placeholder",
|
|
77
|
+
"default": "'Write a message'",
|
|
78
|
+
"required": false,
|
|
79
|
+
"mutable": false
|
|
80
|
+
},
|
|
81
|
+
"status": {
|
|
82
|
+
"type": "MessageComposerStatus",
|
|
83
|
+
"resolvedType": "\"error\" | \"ready\" | \"streaming\" | \"submitted\"",
|
|
84
|
+
"attribute": "status",
|
|
85
|
+
"default": "'ready'",
|
|
86
|
+
"required": false,
|
|
87
|
+
"mutable": false
|
|
88
|
+
},
|
|
89
|
+
"submitIntent": {
|
|
90
|
+
"type": "ButtonFilledIntent",
|
|
91
|
+
"resolvedType": "\"ai\" | \"brand\" | \"caution\" | \"guide\" | \"negative\" | \"neutral\" | \"positive\" | \"walkthrough\" | \"warning\"",
|
|
92
|
+
"attribute": "submit-intent",
|
|
93
|
+
"default": "'brand'",
|
|
94
|
+
"required": false,
|
|
95
|
+
"mutable": false
|
|
96
|
+
},
|
|
97
|
+
"value": {
|
|
98
|
+
"type": "string",
|
|
99
|
+
"resolvedType": "string",
|
|
100
|
+
"attribute": "value",
|
|
101
|
+
"default": "''",
|
|
102
|
+
"required": false,
|
|
103
|
+
"mutable": true
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"events": [
|
|
107
|
+
{
|
|
108
|
+
"name": "dsInput",
|
|
109
|
+
"detail": "string",
|
|
110
|
+
"bubbles": true,
|
|
111
|
+
"cancelable": true,
|
|
112
|
+
"composed": true
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "dsStop",
|
|
116
|
+
"detail": "void",
|
|
117
|
+
"bubbles": true,
|
|
118
|
+
"cancelable": true,
|
|
119
|
+
"composed": true
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"name": "dsSubmit",
|
|
123
|
+
"detail": "{ text: string }",
|
|
124
|
+
"bubbles": true,
|
|
125
|
+
"cancelable": true,
|
|
126
|
+
"composed": true
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"methods": [
|
|
130
|
+
{
|
|
131
|
+
"name": "setFocus",
|
|
132
|
+
"signature": "setFocus() => Promise<void>"
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
"slots": []
|
|
136
|
+
},
|
|
137
|
+
"props": {
|
|
138
|
+
"isInactive": {
|
|
139
|
+
"type": "boolean",
|
|
140
|
+
"resolvedType": "boolean",
|
|
141
|
+
"attribute": "is-inactive",
|
|
142
|
+
"default": "false",
|
|
143
|
+
"required": false,
|
|
144
|
+
"mutable": false
|
|
145
|
+
},
|
|
146
|
+
"label": {
|
|
147
|
+
"type": "string",
|
|
148
|
+
"resolvedType": "string",
|
|
149
|
+
"attribute": "label",
|
|
150
|
+
"default": "'Message'",
|
|
151
|
+
"required": false,
|
|
152
|
+
"mutable": false
|
|
153
|
+
},
|
|
154
|
+
"placeholder": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"resolvedType": "string",
|
|
157
|
+
"attribute": "placeholder",
|
|
158
|
+
"default": "'Write a message'",
|
|
159
|
+
"required": false,
|
|
160
|
+
"mutable": false
|
|
161
|
+
},
|
|
162
|
+
"status": {
|
|
163
|
+
"type": "MessageComposerStatus",
|
|
164
|
+
"resolvedType": "\"error\" | \"ready\" | \"streaming\" | \"submitted\"",
|
|
165
|
+
"attribute": "status",
|
|
166
|
+
"default": "'ready'",
|
|
167
|
+
"required": false,
|
|
168
|
+
"mutable": false
|
|
169
|
+
},
|
|
170
|
+
"submitIntent": {
|
|
171
|
+
"type": "ButtonFilledIntent",
|
|
172
|
+
"resolvedType": "\"ai\" | \"brand\" | \"caution\" | \"guide\" | \"negative\" | \"neutral\" | \"positive\" | \"walkthrough\" | \"warning\"",
|
|
173
|
+
"attribute": "submit-intent",
|
|
174
|
+
"default": "'brand'",
|
|
175
|
+
"required": false,
|
|
176
|
+
"mutable": false
|
|
177
|
+
},
|
|
178
|
+
"value": {
|
|
179
|
+
"type": "string",
|
|
180
|
+
"resolvedType": "string",
|
|
181
|
+
"attribute": "value",
|
|
182
|
+
"default": "''",
|
|
183
|
+
"required": false,
|
|
184
|
+
"mutable": true
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
"events": [
|
|
188
|
+
{
|
|
189
|
+
"name": "dsInput",
|
|
190
|
+
"detail": "string",
|
|
191
|
+
"bubbles": true,
|
|
192
|
+
"cancelable": true,
|
|
193
|
+
"composed": true
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"name": "dsStop",
|
|
197
|
+
"detail": "void",
|
|
198
|
+
"bubbles": true,
|
|
199
|
+
"cancelable": true,
|
|
200
|
+
"composed": true
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"name": "dsSubmit",
|
|
204
|
+
"detail": "{ text: string }",
|
|
205
|
+
"bubbles": true,
|
|
206
|
+
"cancelable": true,
|
|
207
|
+
"composed": true
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
"methods": [
|
|
211
|
+
{
|
|
212
|
+
"name": "setFocus",
|
|
213
|
+
"signature": "setFocus() => Promise<void>"
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
"slots": [],
|
|
217
|
+
"exports": {
|
|
218
|
+
"customElement": "ds-message-composer",
|
|
219
|
+
"react": "DsMessageComposer",
|
|
220
|
+
"angular": "DsMessageComposer"
|
|
221
|
+
},
|
|
222
|
+
"consumption": {
|
|
223
|
+
"install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
|
|
224
|
+
"cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
|
|
225
|
+
"customElements": {
|
|
226
|
+
"import": "import '@ds-mo/ui/dist/components/ds-message-composer.js';",
|
|
227
|
+
"example": "<ds-message-composer></ds-message-composer>"
|
|
228
|
+
},
|
|
229
|
+
"react": {
|
|
230
|
+
"import": "import { DsMessageComposer } from '@ds-mo/ui/react';",
|
|
231
|
+
"example": "<DsMessageComposer />"
|
|
232
|
+
},
|
|
233
|
+
"angular": {
|
|
234
|
+
"import": "import { DsMessageComposer } from '@ds-mo/ui/angular/ds-message-composer';",
|
|
235
|
+
"example": "<ds-message-composer></ds-message-composer>"
|
|
236
|
+
},
|
|
237
|
+
"peerDependencies": {
|
|
238
|
+
"required": [
|
|
239
|
+
"@ds-mo/tokens ^3.1.1",
|
|
240
|
+
"@ds-mo/icons ^6.2.0"
|
|
241
|
+
],
|
|
242
|
+
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
"dependencies": [
|
|
247
|
+
"@ds-mo/ui",
|
|
248
|
+
"@ds-mo/tokens"
|
|
249
|
+
],
|
|
250
|
+
"registryDependencies": [
|
|
251
|
+
"button-filled"
|
|
252
|
+
],
|
|
253
|
+
"files": [
|
|
254
|
+
{
|
|
255
|
+
"path": "src/wc/components/MessageComposer/MessageComposer.css",
|
|
256
|
+
"content": ":host {\n display: block;\n width: 100%;\n box-sizing: border-box;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.message-composer {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n width: 100%;\n margin: 0;\n}\n\n.message-composer__attachments:empty,\n.message-composer__tools:empty {\n display: none;\n}\n\n.message-composer__field {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-100);\n border-radius: var(--dimension-radius-050);\n background: var(--color-background-primary);\n box-shadow: var(--effect-elevation-elevated-md);\n overflow: hidden;\n}\n\n.message-composer--error .message-composer__field {\n box-shadow: var(--effect-elevation-elevated-md),\n inset 0 0 0 var(--dimension-stroke-width-025) var(--color-border-bold-negative);\n}\n\ntextarea {\n width: 100%;\n min-height: calc(var(--typography-lineheight-md) * 2 + var(--dimension-space-150));\n max-height: calc(var(--typography-lineheight-md) * 6 + var(--dimension-space-150));\n padding: var(--dimension-space-075);\n border: none;\n outline: none;\n resize: none;\n overflow-y: auto;\n box-sizing: border-box;\n background: transparent;\n color: var(--color-foreground-primary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n line-height: var(--typography-lineheight-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n -webkit-user-select: text;\n user-select: text;\n}\n\ntextarea::placeholder {\n color: var(--color-foreground-secondary);\n}\n\ntextarea:focus::placeholder {\n color: var(--color-foreground-quaternary);\n}\n\n.message-composer__tools {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n min-height: var(--dimension-size-400);\n}\n\n.message-composer__controls {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--dimension-space-050);\n width: 100%;\n min-width: 0;\n}\n\n.message-composer__actions {\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050);\n flex: 0 0 auto;\n margin-inline-start: auto;\n}\n",
|
|
257
|
+
"type": "registry:ui"
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"path": "src/wc/components/MessageComposer/MessageComposer.tsx",
|
|
261
|
+
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n Watch,\n} from '@stencil/core';\nimport type { ButtonFilledIntent } from '../ButtonFilled/ButtonFilled';\nimport type { MessageComposerStatus } from '../conversation-types';\n\n@Component({\n tag: 'ds-message-composer',\n styleUrl: 'MessageComposer.css',\n scoped: true,\n})\nexport class MessageComposer {\n @Element() el!: HTMLElement;\n\n @Prop({ mutable: true }) value: string = '';\n @Prop() placeholder: string = 'Write a message';\n @Prop() label: string = 'Message';\n @Prop() status: MessageComposerStatus = 'ready';\n @Prop() isInactive: boolean = false;\n @Prop() submitIntent: ButtonFilledIntent = 'brand';\n\n @Event() dsInput!: EventEmitter<string>;\n @Event() dsSubmit!: EventEmitter<{ text: string }>;\n @Event() dsStop!: EventEmitter<void>;\n\n private textarea?: HTMLTextAreaElement;\n\n componentDidLoad() {\n this.resize();\n }\n\n @Watch('value')\n onValueChange() {\n requestAnimationFrame(() => this.resize());\n }\n\n @Method()\n async setFocus() {\n this.textarea?.focus();\n }\n\n private get streaming(): boolean {\n return this.status === 'submitted' || this.status === 'streaming';\n }\n\n private resize() {\n if (!this.textarea) return;\n this.textarea.style.height = 'auto';\n this.textarea.style.height = `${this.textarea.scrollHeight}px`;\n }\n\n private handleInput = (event: Event) => {\n this.value = (event.target as HTMLTextAreaElement).value;\n this.resize();\n this.dsInput.emit(this.value);\n };\n\n private submit() {\n const text = this.value.trim();\n if (!text || this.isInactive || this.streaming) return;\n this.dsSubmit.emit({ text });\n }\n\n private handleKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Enter' || event.shiftKey || event.isComposing) return;\n event.preventDefault();\n this.submit();\n };\n\n private handleAction = () => {\n if (this.streaming) this.dsStop.emit();\n else this.submit();\n };\n\n render() {\n const error = this.status === 'error';\n return (\n <Host>\n <form\n class={{ 'message-composer': true, 'message-composer--error': error }}\n aria-busy={this.streaming ? 'true' : undefined}\n onSubmit={(event: Event) => {\n event.preventDefault();\n this.submit();\n }}\n >\n <div class=\"message-composer__attachments\">\n <slot name=\"attachments\" />\n </div>\n <div class=\"message-composer__field\">\n <textarea\n ref={element => {\n this.textarea = element;\n }}\n value={this.value}\n placeholder={this.placeholder}\n aria-label={this.label}\n aria-invalid={error ? 'true' : undefined}\n disabled={this.isInactive}\n rows={2}\n onInput={this.handleInput}\n onKeyDown={this.handleKeyDown}\n />\n <div class=\"message-composer__controls\">\n <div class=\"message-composer__tools\">\n <slot name=\"tools\" />\n </div>\n <div class=\"message-composer__actions\">\n <slot name=\"actions\" />\n <ds-button-filled\n class=\"message-composer__action\"\n variant=\"icon\"\n icon={this.streaming ? 'Pause' : 'PaperplaneSend'}\n intent={this.streaming ? 'neutral' : this.submitIntent}\n size=\"md\"\n rounded\n aria-label={this.streaming ? 'Stop response' : 'Send message'}\n isInactive={this.isInactive || (!this.streaming && !this.value.trim())}\n onDsClick={this.handleAction}\n />\n </div>\n </div>\n </div>\n </form>\n </Host>\n );\n }\n}\n",
|
|
262
|
+
"type": "registry:ui"
|
|
263
|
+
}
|
|
264
|
+
]
|
|
265
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "message-scroller",
|
|
4
|
+
"title": "MessageScroller",
|
|
5
|
+
"description": "Focusable transcript scroller that follows the live edge only while the reader remains there.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"meta": {
|
|
8
|
+
"library": "@ds-mo/ui",
|
|
9
|
+
"distribution": "npm",
|
|
10
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/message-scroller",
|
|
11
|
+
"source": "src/wc/components/MessageScroller/MessageScroller.tsx",
|
|
12
|
+
"intentStatus": "complete",
|
|
13
|
+
"intent": {
|
|
14
|
+
"audience": "general",
|
|
15
|
+
"status": "experimental",
|
|
16
|
+
"summary": "Focusable transcript scroller that follows the live edge only while the reader remains there.",
|
|
17
|
+
"useWhen": [
|
|
18
|
+
"Messages can append or stream and transcript position must remain respectful of reader intent."
|
|
19
|
+
],
|
|
20
|
+
"avoidWhen": [
|
|
21
|
+
"Content is static prose without message navigation or live updates."
|
|
22
|
+
],
|
|
23
|
+
"commonlyComposedWith": [
|
|
24
|
+
"component:ds-message",
|
|
25
|
+
"component:ds-message-composer"
|
|
26
|
+
],
|
|
27
|
+
"patterns": [
|
|
28
|
+
"pattern:conversation"
|
|
29
|
+
],
|
|
30
|
+
"accessibility": [
|
|
31
|
+
"Give each transcript a concise localized label.",
|
|
32
|
+
"New content is exposed through log semantics without stealing focus.",
|
|
33
|
+
"The scroll-to-latest control remains keyboard accessible when follow behavior is released."
|
|
34
|
+
],
|
|
35
|
+
"states": [
|
|
36
|
+
"Selection and transcript interaction release automatic following.",
|
|
37
|
+
"Opening saved history, message jumps, and prepended history preserve deliberate position.",
|
|
38
|
+
"The scroll-to-latest action is absent at the live edge, appears only after the reader moves away, and disappears again after returning to the newest message.",
|
|
39
|
+
"Place a composer container in the overlay slot so messages scroll behind it; the scroller measures its live height, reserves matching transcript clearance, and keeps scroll-to-latest above it."
|
|
40
|
+
],
|
|
41
|
+
"responsiveBehavior": [
|
|
42
|
+
"Transcript width is readable while the scroll region fills available height."
|
|
43
|
+
],
|
|
44
|
+
"references": [
|
|
45
|
+
{
|
|
46
|
+
"label": "Storybook examples",
|
|
47
|
+
"path": "src/wc/components/MessageScroller/MessageScroller.stories.ts"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"api": {
|
|
52
|
+
"props": {
|
|
53
|
+
"autoFollow": {
|
|
54
|
+
"type": "boolean",
|
|
55
|
+
"resolvedType": "boolean",
|
|
56
|
+
"attribute": "auto-follow",
|
|
57
|
+
"default": "true",
|
|
58
|
+
"required": false,
|
|
59
|
+
"mutable": false
|
|
60
|
+
},
|
|
61
|
+
"busy": {
|
|
62
|
+
"type": "boolean",
|
|
63
|
+
"resolvedType": "boolean",
|
|
64
|
+
"attribute": "busy",
|
|
65
|
+
"default": "false",
|
|
66
|
+
"required": false,
|
|
67
|
+
"mutable": false
|
|
68
|
+
},
|
|
69
|
+
"defaultPosition": {
|
|
70
|
+
"type": "MessageScrollerPosition",
|
|
71
|
+
"resolvedType": "\"end\" | \"last-anchor\" | \"start\"",
|
|
72
|
+
"attribute": "default-position",
|
|
73
|
+
"default": "'last-anchor'",
|
|
74
|
+
"required": false,
|
|
75
|
+
"mutable": false
|
|
76
|
+
},
|
|
77
|
+
"messagesLabel": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"resolvedType": "string",
|
|
80
|
+
"attribute": "messages-label",
|
|
81
|
+
"default": "'Messages'",
|
|
82
|
+
"required": false,
|
|
83
|
+
"mutable": false
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"events": [
|
|
87
|
+
{
|
|
88
|
+
"name": "dsReachStart",
|
|
89
|
+
"detail": "void",
|
|
90
|
+
"bubbles": true,
|
|
91
|
+
"cancelable": true,
|
|
92
|
+
"composed": true
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
"methods": [
|
|
96
|
+
{
|
|
97
|
+
"name": "scrollToEnd",
|
|
98
|
+
"signature": "scrollToEnd() => Promise<void>"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"name": "scrollToMessage",
|
|
102
|
+
"signature": "scrollToMessage(id: string) => Promise<boolean>"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "scrollToStart",
|
|
106
|
+
"signature": "scrollToStart() => Promise<void>"
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
"slots": []
|
|
110
|
+
},
|
|
111
|
+
"props": {
|
|
112
|
+
"autoFollow": {
|
|
113
|
+
"type": "boolean",
|
|
114
|
+
"resolvedType": "boolean",
|
|
115
|
+
"attribute": "auto-follow",
|
|
116
|
+
"default": "true",
|
|
117
|
+
"required": false,
|
|
118
|
+
"mutable": false
|
|
119
|
+
},
|
|
120
|
+
"busy": {
|
|
121
|
+
"type": "boolean",
|
|
122
|
+
"resolvedType": "boolean",
|
|
123
|
+
"attribute": "busy",
|
|
124
|
+
"default": "false",
|
|
125
|
+
"required": false,
|
|
126
|
+
"mutable": false
|
|
127
|
+
},
|
|
128
|
+
"defaultPosition": {
|
|
129
|
+
"type": "MessageScrollerPosition",
|
|
130
|
+
"resolvedType": "\"end\" | \"last-anchor\" | \"start\"",
|
|
131
|
+
"attribute": "default-position",
|
|
132
|
+
"default": "'last-anchor'",
|
|
133
|
+
"required": false,
|
|
134
|
+
"mutable": false
|
|
135
|
+
},
|
|
136
|
+
"messagesLabel": {
|
|
137
|
+
"type": "string",
|
|
138
|
+
"resolvedType": "string",
|
|
139
|
+
"attribute": "messages-label",
|
|
140
|
+
"default": "'Messages'",
|
|
141
|
+
"required": false,
|
|
142
|
+
"mutable": false
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"events": [
|
|
146
|
+
{
|
|
147
|
+
"name": "dsReachStart",
|
|
148
|
+
"detail": "void",
|
|
149
|
+
"bubbles": true,
|
|
150
|
+
"cancelable": true,
|
|
151
|
+
"composed": true
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
"methods": [
|
|
155
|
+
{
|
|
156
|
+
"name": "scrollToEnd",
|
|
157
|
+
"signature": "scrollToEnd() => Promise<void>"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"name": "scrollToMessage",
|
|
161
|
+
"signature": "scrollToMessage(id: string) => Promise<boolean>"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "scrollToStart",
|
|
165
|
+
"signature": "scrollToStart() => Promise<void>"
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
"slots": [],
|
|
169
|
+
"exports": {
|
|
170
|
+
"customElement": "ds-message-scroller",
|
|
171
|
+
"react": "DsMessageScroller",
|
|
172
|
+
"angular": "DsMessageScroller"
|
|
173
|
+
},
|
|
174
|
+
"consumption": {
|
|
175
|
+
"install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
|
|
176
|
+
"cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
|
|
177
|
+
"customElements": {
|
|
178
|
+
"import": "import '@ds-mo/ui/dist/components/ds-message-scroller.js';",
|
|
179
|
+
"example": "<ds-message-scroller></ds-message-scroller>"
|
|
180
|
+
},
|
|
181
|
+
"react": {
|
|
182
|
+
"import": "import { DsMessageScroller } from '@ds-mo/ui/react';",
|
|
183
|
+
"example": "<DsMessageScroller />"
|
|
184
|
+
},
|
|
185
|
+
"angular": {
|
|
186
|
+
"import": "import { DsMessageScroller } from '@ds-mo/ui/angular/ds-message-scroller';",
|
|
187
|
+
"example": "<ds-message-scroller></ds-message-scroller>"
|
|
188
|
+
},
|
|
189
|
+
"peerDependencies": {
|
|
190
|
+
"required": [
|
|
191
|
+
"@ds-mo/tokens ^3.1.1",
|
|
192
|
+
"@ds-mo/icons ^6.2.0"
|
|
193
|
+
],
|
|
194
|
+
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
"dependencies": [
|
|
199
|
+
"@ds-mo/ui",
|
|
200
|
+
"@ds-mo/tokens"
|
|
201
|
+
],
|
|
202
|
+
"registryDependencies": [
|
|
203
|
+
"button-unfilled"
|
|
204
|
+
],
|
|
205
|
+
"files": [
|
|
206
|
+
{
|
|
207
|
+
"path": "src/wc/components/MessageScroller/MessageScroller.css",
|
|
208
|
+
"content": ":host {\n display: block;\n width: 100%;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n}\n\n.message-scroller {\n position: relative;\n width: 100%;\n height: 100%;\n min-height: 0;\n}\n\n.message-scroller__viewport {\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: auto;\n overscroll-behavior: contain;\n box-sizing: border-box;\n}\n\n.message-scroller__viewport:focus-visible {\n outline: var(--dimension-stroke-width-025) solid var(--color-border-bold-brand);\n outline-offset: calc(-1 * var(--dimension-stroke-width-025));\n}\n\n.message-scroller__content {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-300);\n width: 100%;\n max-width: var(--dimension-panel-width-2xl);\n min-height: 100%;\n margin-inline: auto;\n padding-block-start: var(--dimension-space-200);\n padding-inline: var(--dimension-space-200);\n padding-block-end: calc(\n var(--dimension-space-200) + var(--ds-message-scroller-overlay-block-size, 0px)\n );\n box-sizing: border-box;\n}\n\n.message-scroller__overlay {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: var(--dimension-z-index-floating);\n}\n\n.message-scroller__control {\n position: absolute;\n bottom: calc(\n var(--ds-message-scroller-overlay-content-block-size, 0px) + var(--dimension-space-200)\n );\n left: 50%;\n z-index: var(--dimension-z-index-floating);\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--dimension-radius-half);\n background: var(--color-background-transparent);\n box-shadow: var(--effect-elevation-elevated-md);\n transform: translateX(-50%);\n -webkit-backdrop-filter: blur(var(--effect-blur-sm));\n backdrop-filter: blur(var(--effect-blur-sm));\n}\n",
|
|
209
|
+
"type": "registry:ui"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"path": "src/wc/components/MessageScroller/MessageScroller.tsx",
|
|
213
|
+
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n h,\n Host,\n Method,\n Prop,\n State,\n} from '@stencil/core';\nimport type { MessageScrollerPosition } from '../conversation-types';\n\nconst LIVE_EDGE_THRESHOLD = 24;\n\n@Component({\n tag: 'ds-message-scroller',\n styleUrl: 'MessageScroller.css',\n styleUrls: ['../../utils/scroll-edge-fade.css'],\n scoped: true,\n})\nexport class MessageScroller {\n @Element() el!: HTMLElement;\n\n @Prop() busy: boolean = false;\n @Prop() autoFollow: boolean = true;\n @Prop() defaultPosition: MessageScrollerPosition = 'last-anchor';\n @Prop() messagesLabel: string = 'Messages';\n\n @Event() dsReachStart!: EventEmitter<void>;\n\n @State() private showScrollToLatest = false;\n\n private viewport?: HTMLElement;\n private content?: HTMLElement;\n private overlay?: HTMLElement;\n private resizeObserver?: ResizeObserver;\n private overlayResizeObserver?: ResizeObserver;\n private mutationObserver?: MutationObserver;\n private following = true;\n private atStart = false;\n private programmatic = false;\n private previousScrollHeight = 0;\n private overlayBlockSize = 0;\n private overlayContentBlockSize = 0;\n\n componentDidLoad() {\n this.connectObservers();\n requestAnimationFrame(() => this.applyDefaultPosition());\n document.addEventListener('selectionchange', this.handleSelectionChange);\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect();\n this.overlayResizeObserver?.disconnect();\n this.mutationObserver?.disconnect();\n document.removeEventListener('selectionchange', this.handleSelectionChange);\n }\n\n @Method()\n async scrollToMessage(id: string): Promise<boolean> {\n const message = Array.from(\n this.el.querySelectorAll<HTMLElement & { messageId?: string }>('ds-message')\n ).find(item => item.messageId === id || item.getAttribute('message-id') === id);\n if (!message) return false;\n this.following = false;\n this.showScrollToLatest = true;\n message.scrollIntoView({ block: 'start', behavior: this.motionBehavior() });\n return true;\n }\n\n @Method()\n async scrollToStart() {\n this.scrollTo(0);\n this.following = false;\n this.showScrollToLatest = Boolean(\n this.viewport && this.viewport.scrollHeight > this.viewport.clientHeight\n );\n }\n\n @Method()\n async scrollToEnd() {\n this.following = true;\n this.showScrollToLatest = false;\n this.scrollTo(this.viewport?.scrollHeight ?? 0);\n }\n\n private motionBehavior(): ScrollBehavior {\n return matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth';\n }\n\n private scrollTo(top: number) {\n if (!this.viewport) return;\n this.programmatic = true;\n this.viewport.scrollTo({ top, behavior: this.motionBehavior() });\n requestAnimationFrame(() => {\n this.programmatic = false;\n });\n }\n\n private connectObservers() {\n if (!this.viewport || !this.content) return;\n this.previousScrollHeight = this.viewport.scrollHeight;\n this.resizeObserver = new ResizeObserver(() => this.handleContentGrowth());\n this.resizeObserver.observe(this.content);\n this.resizeObserver.observe(this.viewport);\n if (this.overlay) {\n this.overlayResizeObserver = new ResizeObserver(entries => {\n const blockSize = Math.ceil(entries[0]?.contentRect.height ?? 0);\n this.overlayBlockSize = blockSize;\n this.el.style.setProperty('--ds-message-scroller-overlay-block-size', `${blockSize}px`);\n this.syncOverlayContentBlockSize();\n this.handleContentGrowth();\n });\n this.overlayResizeObserver.observe(this.overlay);\n }\n this.mutationObserver = new MutationObserver(() => this.handleContentGrowth());\n this.mutationObserver.observe(this.content, {\n childList: true,\n subtree: true,\n characterData: true,\n });\n }\n\n private handleContentGrowth() {\n if (!this.viewport) return;\n const nextHeight = this.viewport.scrollHeight;\n const delta = nextHeight - this.previousScrollHeight;\n if (delta > 0 && this.viewport.scrollTop <= LIVE_EDGE_THRESHOLD && !this.following) {\n this.viewport.scrollTop += delta;\n } else if (this.autoFollow && this.following) {\n this.viewport.scrollTop = nextHeight;\n }\n this.previousScrollHeight = nextHeight;\n this.showScrollToLatest = !this.following && !this.isAtLiveEdge();\n this.syncContentFade();\n }\n\n private syncContentFade() {\n if (!this.viewport || !this.content) return;\n const fadeEnd = Math.max(this.viewport.scrollTop + this.viewport.clientHeight, 0);\n const fadeStart = Math.max(fadeEnd - this.overlayContentBlockSize, 0);\n this.content.style.setProperty('--ds-scroll-edge-fade-window-start', `${fadeStart}px`);\n this.content.style.setProperty('--ds-scroll-edge-fade-window-end', `${fadeEnd}px`);\n }\n\n private syncOverlayContentBlockSize() {\n if (!this.overlay) return;\n const assigned = this.el.querySelector<HTMLElement>('[slot=\"overlay\"]');\n if (!(assigned instanceof HTMLElement)) {\n this.overlayContentBlockSize = this.overlayBlockSize;\n } else {\n const visibleContent =\n assigned.firstElementChild instanceof HTMLElement ? assigned.firstElementChild : assigned;\n const topInset = Math.max(\n Math.ceil(visibleContent.getBoundingClientRect().top - this.overlay.getBoundingClientRect().top),\n 0\n );\n this.overlayContentBlockSize = Math.max(this.overlayBlockSize - topInset, 0);\n }\n this.el.style.setProperty(\n '--ds-message-scroller-overlay-content-block-size',\n `${this.overlayContentBlockSize}px`\n );\n }\n\n private applyDefaultPosition() {\n if (!this.viewport) return;\n if (this.defaultPosition === 'start') {\n this.viewport.scrollTop = 0;\n this.following = false;\n this.showScrollToLatest = this.viewport.scrollHeight > this.viewport.clientHeight;\n this.syncContentFade();\n return;\n }\n if (this.defaultPosition === 'last-anchor') {\n const anchors = this.el.querySelectorAll<HTMLElement>('ds-message[scroll-anchor]');\n const last = anchors.item(anchors.length - 1);\n if (last) {\n last.scrollIntoView({ block: 'start' });\n this.following = this.isAtLiveEdge();\n this.showScrollToLatest = !this.following;\n this.syncContentFade();\n return;\n }\n }\n this.viewport.scrollTop = this.viewport.scrollHeight;\n this.following = true;\n this.showScrollToLatest = false;\n this.syncContentFade();\n }\n\n private isAtLiveEdge(): boolean {\n if (!this.viewport) return true;\n return (\n this.viewport.scrollHeight - this.viewport.clientHeight - this.viewport.scrollTop <=\n LIVE_EDGE_THRESHOLD\n );\n }\n\n private handleScroll = () => {\n if (!this.viewport) return;\n this.syncContentFade();\n if (this.programmatic) return;\n this.following = this.isAtLiveEdge();\n this.showScrollToLatest = !this.following;\n const nextAtStart = this.viewport.scrollTop <= LIVE_EDGE_THRESHOLD;\n if (nextAtStart && !this.atStart) this.dsReachStart.emit();\n this.atStart = nextAtStart;\n };\n\n private releaseFollow = () => {\n this.following = false;\n this.showScrollToLatest = !this.isAtLiveEdge();\n };\n\n private handleSelectionChange = () => {\n const selection = document.getSelection();\n if (\n selection &&\n !selection.isCollapsed &&\n selection.anchorNode &&\n this.el.contains(selection.anchorNode)\n ) {\n this.following = false;\n this.showScrollToLatest = !this.isAtLiveEdge();\n }\n };\n\n render() {\n return (\n <Host>\n <div class=\"message-scroller\">\n <div\n class=\"message-scroller__viewport\"\n ref={element => {\n this.viewport = element;\n }}\n role=\"region\"\n aria-label={this.messagesLabel}\n tabIndex={0}\n onScroll={this.handleScroll}\n onPointerDown={this.releaseFollow}\n onKeyDown={(event: KeyboardEvent) => {\n if (['ArrowUp', 'PageUp', 'Home'].includes(event.key)) this.releaseFollow();\n }}\n >\n <div\n class=\"message-scroller__content scroll-edge-fade--block-window\"\n ref={element => {\n this.content = element;\n }}\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n aria-busy={this.busy ? 'true' : undefined}\n >\n <slot />\n </div>\n </div>\n <div\n class=\"message-scroller__overlay\"\n ref={element => {\n this.overlay = element;\n }}\n >\n <slot name=\"overlay\" />\n </div>\n {this.showScrollToLatest ? (\n <div class=\"message-scroller__control\">\n <ds-button-unfilled\n variant=\"icon\"\n icon=\"ChevronDown\"\n size=\"md\"\n rounded\n hasBorder={false}\n aria-label=\"Scroll to latest message\"\n onDsClick={() => void this.scrollToEnd()}\n />\n </div>\n ) : null}\n </div>\n </Host>\n );\n }\n}\n",
|
|
214
|
+
"type": "registry:ui"
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
}
|