@aceshooting/lyra-ui 1.1.0 → 2.0.0
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 +12 -1
- package/custom-elements.json +21460 -8489
- package/dist/components/app-rail/app-rail.d.ts +171 -0
- package/dist/components/app-rail/app-rail.js +483 -0
- package/dist/components/app-rail/app-rail.styles.d.ts +1 -0
- package/dist/components/app-rail/app-rail.styles.js +125 -0
- package/dist/components/attachment-chip/attachment-chip.d.ts +114 -0
- package/dist/components/attachment-chip/attachment-chip.js +312 -0
- package/dist/components/attachment-chip/attachment-chip.styles.d.ts +1 -0
- package/dist/components/attachment-chip/attachment-chip.styles.js +184 -0
- package/dist/components/attachment-trigger/attachment-trigger.d.ts +83 -0
- package/dist/components/attachment-trigger/attachment-trigger.js +258 -0
- package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts +1 -0
- package/dist/components/attachment-trigger/attachment-trigger.styles.js +68 -0
- package/dist/components/chart/box-plot.js +11 -1
- package/dist/components/chart/chart-loader.d.ts +44 -13
- package/dist/components/chart/chart-loader.js +65 -18
- package/dist/components/chart/chart.d.ts +12 -2
- package/dist/components/chart/chart.js +121 -14
- package/dist/components/chart/histogram-bin.d.ts +4 -1
- package/dist/components/chart/histogram-bin.js +24 -14
- package/dist/components/chart/lite-chart.d.ts +63 -0
- package/dist/components/chart/lite-chart.js +181 -23
- package/dist/components/chart/lite-chart.styles.js +9 -0
- package/dist/components/chat-composer/chat-composer.d.ts +92 -0
- package/dist/components/chat-composer/chat-composer.js +285 -0
- package/dist/components/chat-composer/chat-composer.styles.d.ts +1 -0
- package/dist/components/chat-composer/chat-composer.styles.js +135 -0
- package/dist/components/chat-message/chat-message.d.ts +113 -0
- package/dist/components/chat-message/chat-message.js +291 -0
- package/dist/components/chat-message/chat-message.styles.d.ts +1 -0
- package/dist/components/chat-message/chat-message.styles.js +203 -0
- package/dist/components/checkbox/checkbox.d.ts +59 -0
- package/dist/components/checkbox/checkbox.js +230 -0
- package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
- package/dist/components/checkbox/checkbox.styles.js +70 -0
- package/dist/components/chip/chip-group.d.ts +56 -0
- package/dist/components/chip/chip-group.js +124 -0
- package/dist/components/chip/chip-group.styles.d.ts +1 -0
- package/dist/components/chip/chip-group.styles.js +57 -0
- package/dist/components/chip/chip.d.ts +64 -0
- package/dist/components/chip/chip.js +115 -0
- package/dist/components/chip/chip.styles.d.ts +1 -0
- package/dist/components/chip/chip.styles.js +115 -0
- package/dist/components/citation-badge/citation-badge.d.ts +113 -0
- package/dist/components/citation-badge/citation-badge.js +300 -0
- package/dist/components/citation-badge/citation-badge.styles.d.ts +1 -0
- package/dist/components/citation-badge/citation-badge.styles.js +96 -0
- package/dist/components/code-block/code-block.d.ts +89 -0
- package/dist/components/code-block/code-block.js +317 -0
- package/dist/components/code-block/code-block.styles.d.ts +1 -0
- package/dist/components/code-block/code-block.styles.js +187 -0
- package/dist/components/code-block/code-loader.d.ts +44 -0
- package/dist/components/code-block/code-loader.js +79 -0
- package/dist/components/combobox/combobox.d.ts +19 -1
- package/dist/components/combobox/combobox.js +84 -14
- package/dist/components/combobox/option.d.ts +5 -1
- package/dist/components/combobox/option.js +35 -0
- package/dist/components/context-meter/context-meter.d.ts +53 -0
- package/dist/components/context-meter/context-meter.js +150 -0
- package/dist/components/context-meter/context-meter.styles.d.ts +1 -0
- package/dist/components/context-meter/context-meter.styles.js +120 -0
- package/dist/components/conversation-item/conversation-item.d.ts +128 -0
- package/dist/components/conversation-item/conversation-item.js +322 -0
- package/dist/components/conversation-item/conversation-item.styles.d.ts +1 -0
- package/dist/components/conversation-item/conversation-item.styles.js +155 -0
- package/dist/components/date-picker/date-input.d.ts +24 -0
- package/dist/components/date-picker/date-input.js +134 -22
- package/dist/components/date-picker/date-picker.d.ts +7 -0
- package/dist/components/date-picker/date-picker.js +85 -16
- package/dist/components/dialog/confirm.d.ts +40 -0
- package/dist/components/dialog/confirm.js +79 -0
- package/dist/components/dialog/dialog.d.ts +85 -0
- package/dist/components/dialog/dialog.js +272 -0
- package/dist/components/dialog/dialog.styles.d.ts +1 -0
- package/dist/components/dialog/dialog.styles.js +52 -0
- package/dist/components/dock-panel/dock-panel.d.ts +134 -0
- package/dist/components/dock-panel/dock-panel.js +345 -0
- package/dist/components/dock-panel/dock-panel.styles.d.ts +1 -0
- package/dist/components/dock-panel/dock-panel.styles.js +145 -0
- package/dist/components/document-preview/document-preview.d.ts +135 -0
- package/dist/components/document-preview/document-preview.js +372 -0
- package/dist/components/document-preview/document-preview.styles.d.ts +1 -0
- package/dist/components/document-preview/document-preview.styles.js +191 -0
- package/dist/components/export-button/csv.js +1 -1
- package/dist/components/export-button/export-button.js +2 -0
- package/dist/components/flag/flag.d.ts +42 -5
- package/dist/components/flag/flag.js +70 -3
- package/dist/components/gauge/gauge.js +19 -10
- package/dist/components/generation-status/generation-status.d.ts +109 -0
- package/dist/components/generation-status/generation-status.js +273 -0
- package/dist/components/generation-status/generation-status.styles.d.ts +1 -0
- package/dist/components/generation-status/generation-status.styles.js +84 -0
- package/dist/components/heatmap/calendar-grid.d.ts +4 -1
- package/dist/components/heatmap/calendar-grid.js +25 -12
- package/dist/components/heatmap/heatmap-scale.d.ts +6 -0
- package/dist/components/heatmap/heatmap-scale.js +7 -1
- package/dist/components/heatmap/heatmap.d.ts +89 -3
- package/dist/components/heatmap/heatmap.js +158 -29
- package/dist/components/json-viewer/json-viewer.d.ts +53 -0
- package/dist/components/json-viewer/json-viewer.js +283 -0
- package/dist/components/json-viewer/json-viewer.styles.d.ts +1 -0
- package/dist/components/json-viewer/json-viewer.styles.js +176 -0
- package/dist/components/kbd/kbd.d.ts +77 -0
- package/dist/components/kbd/kbd.js +214 -0
- package/dist/components/kbd/kbd.styles.d.ts +1 -0
- package/dist/components/kbd/kbd.styles.js +52 -0
- package/dist/components/live-region/live-region.d.ts +81 -0
- package/dist/components/live-region/live-region.js +153 -0
- package/dist/components/live-region/live-region.styles.d.ts +1 -0
- package/dist/components/live-region/live-region.styles.js +10 -0
- package/dist/components/map/map.d.ts +8 -6
- package/dist/components/map/map.js +44 -8
- package/dist/components/markdown/markdown-loader.d.ts +34 -0
- package/dist/components/markdown/markdown-loader.js +43 -0
- package/dist/components/markdown/markdown.d.ts +81 -0
- package/dist/components/markdown/markdown.js +284 -0
- package/dist/components/markdown/markdown.styles.d.ts +1 -0
- package/dist/components/markdown/markdown.styles.js +82 -0
- package/dist/components/media-card/media-card.d.ts +122 -0
- package/dist/components/media-card/media-card.js +295 -0
- package/dist/components/media-card/media-card.styles.d.ts +1 -0
- package/dist/components/media-card/media-card.styles.js +132 -0
- package/dist/components/mention-popover/mention-popover.d.ts +200 -0
- package/dist/components/mention-popover/mention-popover.js +475 -0
- package/dist/components/mention-popover/mention-popover.styles.d.ts +1 -0
- package/dist/components/mention-popover/mention-popover.styles.js +101 -0
- package/dist/components/menu/menu-item.d.ts +62 -0
- package/dist/components/menu/menu-item.js +119 -0
- package/dist/components/menu/menu-item.styles.d.ts +1 -0
- package/dist/components/menu/menu-item.styles.js +67 -0
- package/dist/components/menu/menu.d.ts +143 -0
- package/dist/components/menu/menu.js +365 -0
- package/dist/components/menu/menu.styles.d.ts +1 -0
- package/dist/components/menu/menu.styles.js +66 -0
- package/dist/components/model-select/model-select.d.ts +125 -0
- package/dist/components/model-select/model-select.js +490 -0
- package/dist/components/model-select/model-select.styles.d.ts +1 -0
- package/dist/components/model-select/model-select.styles.js +189 -0
- package/dist/components/model-settings-panel/model-settings-panel.d.ts +69 -0
- package/dist/components/model-settings-panel/model-settings-panel.js +145 -0
- package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts +1 -0
- package/dist/components/model-settings-panel/model-settings-panel.styles.js +82 -0
- package/dist/components/playback/playback.d.ts +13 -1
- package/dist/components/playback/playback.js +47 -14
- package/dist/components/responsive-panel/responsive-panel.d.ts +150 -0
- package/dist/components/responsive-panel/responsive-panel.js +365 -0
- package/dist/components/responsive-panel/responsive-panel.styles.d.ts +1 -0
- package/dist/components/responsive-panel/responsive-panel.styles.js +103 -0
- package/dist/components/result-card/result-card.d.ts +44 -0
- package/dist/components/result-card/result-card.js +83 -0
- package/dist/components/result-card/result-card.styles.d.ts +1 -0
- package/dist/components/result-card/result-card.styles.js +50 -0
- package/dist/components/result-card/result-field.d.ts +36 -0
- package/dist/components/result-card/result-field.js +87 -0
- package/dist/components/result-card/result-field.styles.d.ts +1 -0
- package/dist/components/result-card/result-field.styles.js +32 -0
- package/dist/components/select/select.d.ts +45 -1
- package/dist/components/select/select.js +114 -17
- package/dist/components/select/select.styles.js +26 -2
- package/dist/components/slider/slider.d.ts +89 -0
- package/dist/components/slider/slider.js +352 -0
- package/dist/components/slider/slider.styles.d.ts +1 -0
- package/dist/components/slider/slider.styles.js +87 -0
- package/dist/components/source-card/source-card.d.ts +92 -0
- package/dist/components/source-card/source-card.js +148 -0
- package/dist/components/source-card/source-card.styles.d.ts +1 -0
- package/dist/components/source-card/source-card.styles.js +69 -0
- package/dist/components/source-list/source-list.d.ts +63 -0
- package/dist/components/source-list/source-list.js +122 -0
- package/dist/components/source-list/source-list.styles.d.ts +1 -0
- package/dist/components/source-list/source-list.styles.js +59 -0
- package/dist/components/sparkline/sparkline.js +27 -13
- package/dist/components/split/split.d.ts +93 -1
- package/dist/components/split/split.js +284 -15
- package/dist/components/split/split.styles.js +30 -0
- package/dist/components/stat/stat.d.ts +22 -1
- package/dist/components/stat/stat.js +49 -3
- package/dist/components/stat/stat.styles.js +20 -0
- package/dist/components/stream-status/stream-status.d.ts +110 -0
- package/dist/components/stream-status/stream-status.js +210 -0
- package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
- package/dist/components/stream-status/stream-status.styles.js +103 -0
- package/dist/components/streaming-text/streaming-text.d.ts +98 -0
- package/dist/components/streaming-text/streaming-text.js +209 -0
- package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
- package/dist/components/streaming-text/streaming-text.styles.js +62 -0
- package/dist/components/switch/switch.d.ts +58 -0
- package/dist/components/switch/switch.js +179 -0
- package/dist/components/switch/switch.styles.d.ts +1 -0
- package/dist/components/switch/switch.styles.js +75 -0
- package/dist/components/table/table.d.ts +73 -4
- package/dist/components/table/table.js +169 -20
- package/dist/components/table/table.styles.js +6 -1
- package/dist/components/tabs/tabs.d.ts +51 -0
- package/dist/components/tabs/tabs.js +190 -0
- package/dist/components/tabs/tabs.styles.d.ts +1 -0
- package/dist/components/tabs/tabs.styles.js +63 -0
- package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
- package/dist/components/thinking-panel/thinking-panel.js +235 -0
- package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
- package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
- package/dist/components/toast/toast-item.d.ts +1 -0
- package/dist/components/toast/toast-item.js +31 -6
- package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
- package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
- package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
- package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
- package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
- package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
- package/dist/components/tool-param-form/tool-param-form.js +366 -0
- package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
- package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
- package/dist/components/tool-result-view/registry.d.ts +81 -0
- package/dist/components/tool-result-view/registry.js +84 -0
- package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
- package/dist/components/tool-result-view/tool-result-view.js +138 -0
- package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
- package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
- package/dist/components/tree/tree-node.d.ts +11 -2
- package/dist/components/tree/tree-node.js +16 -6
- package/dist/components/tree/tree.d.ts +8 -2
- package/dist/components/tree/tree.js +18 -11
- package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
- package/dist/components/typing-indicator/typing-indicator.js +96 -0
- package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
- package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
- package/dist/components/virtual-list/virtual-list.d.ts +173 -0
- package/dist/components/virtual-list/virtual-list.js +461 -0
- package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
- package/dist/components/virtual-list/virtual-list.styles.js +44 -0
- package/dist/components/widget/widget.d.ts +10 -0
- package/dist/components/widget/widget.js +24 -1
- package/dist/components/widget/widget.styles.js +9 -2
- package/dist/components/word-cloud/word-cloud-layout.js +14 -1
- package/dist/components/word-cloud/word-cloud.d.ts +6 -0
- package/dist/components/word-cloud/word-cloud.js +25 -14
- package/dist/components/word-cloud/word-cloud.styles.js +4 -2
- package/dist/internal/announcer.d.ts +57 -0
- package/dist/internal/announcer.js +71 -0
- package/dist/internal/form-associated.d.ts +1 -0
- package/dist/internal/form-associated.js +55 -11
- package/dist/internal/group-by-recency.d.ts +50 -0
- package/dist/internal/group-by-recency.js +80 -0
- package/dist/internal/rtl.d.ts +23 -0
- package/dist/internal/rtl.js +28 -0
- package/dist/internal/scroll-lock.d.ts +10 -5
- package/dist/internal/scroll-lock.js +30 -14
- package/dist/internal/tokens.styles.js +6 -0
- package/dist/lyra.d.ts +130 -31
- package/dist/lyra.js +94 -26
- package/llms-full.txt +3921 -32
- package/llms.txt +16 -1
- package/package.json +23 -5
- package/dist/components/chart/bar-chart.stories.d.ts +0 -5
- package/dist/components/chart/bar-chart.stories.js +0 -20
- package/dist/components/chart/box-plot.stories.d.ts +0 -5
- package/dist/components/chart/box-plot.stories.js +0 -29
- package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
- package/dist/components/chart/bubble-chart.stories.js +0 -26
- package/dist/components/chart/chart.stories.d.ts +0 -32
- package/dist/components/chart/chart.stories.js +0 -141
- package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
- package/dist/components/chart/doughnut-chart.stories.js +0 -26
- package/dist/components/chart/histogram.stories.d.ts +0 -5
- package/dist/components/chart/histogram.stories.js +0 -17
- package/dist/components/chart/line-chart.stories.d.ts +0 -22
- package/dist/components/chart/line-chart.stories.js +0 -99
- package/dist/components/chart/lite-chart.stories.d.ts +0 -12
- package/dist/components/chart/lite-chart.stories.js +0 -98
- package/dist/components/chart/pie-chart.stories.d.ts +0 -5
- package/dist/components/chart/pie-chart.stories.js +0 -26
- package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
- package/dist/components/chart/polar-area-chart.stories.js +0 -20
- package/dist/components/chart/radar-chart.stories.d.ts +0 -5
- package/dist/components/chart/radar-chart.stories.js +0 -20
- package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
- package/dist/components/chart/scatter-chart.stories.js +0 -31
- package/dist/components/combobox/combobox.stories.d.ts +0 -24
- package/dist/components/combobox/combobox.stories.js +0 -102
- package/dist/components/date-picker/date-input.stories.d.ts +0 -10
- package/dist/components/date-picker/date-input.stories.js +0 -48
- package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
- package/dist/components/date-picker/date-picker.stories.js +0 -25
- package/dist/components/empty/empty.stories.d.ts +0 -7
- package/dist/components/empty/empty.stories.js +0 -34
- package/dist/components/export-button/export-button.stories.d.ts +0 -7
- package/dist/components/export-button/export-button.stories.js +0 -37
- package/dist/components/file-input/file-input.stories.d.ts +0 -7
- package/dist/components/file-input/file-input.stories.js +0 -19
- package/dist/components/flag/flag.stories.d.ts +0 -5
- package/dist/components/flag/flag.stories.js +0 -17
- package/dist/components/gauge/gauge.stories.d.ts +0 -8
- package/dist/components/gauge/gauge.stories.js +0 -19
- package/dist/components/graph/graph.stories.d.ts +0 -8
- package/dist/components/graph/graph.stories.js +0 -68
- package/dist/components/heatmap/heatmap.stories.d.ts +0 -22
- package/dist/components/heatmap/heatmap.stories.js +0 -154
- package/dist/components/map/map.stories.d.ts +0 -15
- package/dist/components/map/map.stories.js +0 -135
- package/dist/components/playback/playback.stories.d.ts +0 -7
- package/dist/components/playback/playback.stories.js +0 -16
- package/dist/components/select/select.stories.d.ts +0 -19
- package/dist/components/select/select.stories.js +0 -75
- package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
- package/dist/components/skeleton/skeleton.stories.js +0 -16
- package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
- package/dist/components/sparkline/sparkline.stories.js +0 -39
- package/dist/components/split/split.stories.d.ts +0 -7
- package/dist/components/split/split.stories.js +0 -36
- package/dist/components/stat/stat.stories.d.ts +0 -8
- package/dist/components/stat/stat.stories.js +0 -64
- package/dist/components/table/table.stories.d.ts +0 -12
- package/dist/components/table/table.stories.js +0 -67
- package/dist/components/time-range/time-range.stories.d.ts +0 -8
- package/dist/components/time-range/time-range.stories.js +0 -26
- package/dist/components/toast/toast.stories.d.ts +0 -8
- package/dist/components/toast/toast.stories.js +0 -72
- package/dist/components/tree/tree.stories.d.ts +0 -9
- package/dist/components/tree/tree.stories.js +0 -52
- package/dist/components/widget/widget.stories.d.ts +0 -7
- package/dist/components/widget/widget.stories.js +0 -41
- package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
- package/dist/components/word-cloud/word-cloud.stories.js +0 -55
|
@@ -84,6 +84,13 @@ export const styles = css `
|
|
|
84
84
|
[part='spark'][hidden] {
|
|
85
85
|
display: none;
|
|
86
86
|
}
|
|
87
|
+
[part='sub'] {
|
|
88
|
+
font-size: 0.8125rem;
|
|
89
|
+
font-weight: 600;
|
|
90
|
+
}
|
|
91
|
+
[part='sub'][hidden] {
|
|
92
|
+
display: none;
|
|
93
|
+
}
|
|
87
94
|
[part='caption'] {
|
|
88
95
|
font-size: 0.8125rem;
|
|
89
96
|
color: var(--lyra-color-text-quiet);
|
|
@@ -127,4 +134,17 @@ export const styles = css `
|
|
|
127
134
|
:host([emphasis][variant='neutral']) [part='value'] {
|
|
128
135
|
color: var(--lyra-color-brand);
|
|
129
136
|
}
|
|
137
|
+
:host([prose]) [part='value'] {
|
|
138
|
+
font-size: 0.9375rem;
|
|
139
|
+
font-weight: 400;
|
|
140
|
+
font-family: inherit;
|
|
141
|
+
color: var(--lyra-color-text-quiet);
|
|
142
|
+
}
|
|
143
|
+
:host([prose]) [part='unit'] {
|
|
144
|
+
display: none;
|
|
145
|
+
}
|
|
146
|
+
:host([compact]) [part='base'] {
|
|
147
|
+
padding: var(--lyra-space-s);
|
|
148
|
+
gap: 0.125rem;
|
|
149
|
+
}
|
|
130
150
|
`;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { type TemplateResult, type PropertyValues } from 'lit';
|
|
2
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
3
|
+
import '../live-region/live-region.js';
|
|
4
|
+
export type StreamStatusPhase = 'idle' | 'connecting' | 'streaming' | 'stalled';
|
|
5
|
+
/**
|
|
6
|
+
* `<lyra-stream-status>` — a compact status indicator for a single streaming
|
|
7
|
+
* connection (SSE, WebSocket, long-poll, …), with built-in heartbeat-aware
|
|
8
|
+
* stall detection.
|
|
9
|
+
*
|
|
10
|
+
* The host drives `phase` directly for `idle`/`connecting`/`streaming`, and
|
|
11
|
+
* calls `recordActivity()` on every *semantic* frame received while
|
|
12
|
+
* streaming — a real content chunk, never a transport-level keep-alive ping.
|
|
13
|
+
* This component has no payload-inspection logic of its own: "ignore
|
|
14
|
+
* heartbeats" is entirely a call-site discipline — the host simply never
|
|
15
|
+
* calls `recordActivity()` for a ping, so pings never reset the stall timer
|
|
16
|
+
* and a connection that's only sending keep-alives (no real content) for
|
|
17
|
+
* longer than `stall-threshold-ms` correctly reads as stalled.
|
|
18
|
+
*
|
|
19
|
+
* Internally, an inactivity timer runs only while `phase === 'streaming'`.
|
|
20
|
+
* It's (re)armed whenever the phase transitions to `'streaming'` (directly,
|
|
21
|
+
* or via `recordActivity()` recovering from `'stalled'`) and on every
|
|
22
|
+
* subsequent `recordActivity()` call while already streaming; it's disarmed
|
|
23
|
+
* the instant `phase` becomes anything else, including a host-driven
|
|
24
|
+
* reassignment away from `'streaming'` — so a stale timer can never fire a
|
|
25
|
+
* stall transition after the host has already moved on. If it ever fires,
|
|
26
|
+
* `phase` becomes `'stalled'` and `lyra-stall` is dispatched.
|
|
27
|
+
*
|
|
28
|
+
* `phase` remains a fully public, directly settable property at all times —
|
|
29
|
+
* a host can assign `'stalled'` (or any other phase) itself as a manual
|
|
30
|
+
* override, and this component never fights that assignment. `lyra-stall`/
|
|
31
|
+
* `lyra-recover` fire on *any* transition into/out of `'stalled'`
|
|
32
|
+
* respectively, whether timer-driven or host-driven, and never for a
|
|
33
|
+
* reassignment to the same value (Lit's default `hasChanged` already skips a
|
|
34
|
+
* no-op set, same as every other reflected `@property` in this library).
|
|
35
|
+
* Like `<lyra-chat-message>`'s `status`, whatever phase this element happens
|
|
36
|
+
* to *mount* with is never itself treated as an eventful transition — only a
|
|
37
|
+
* later change fires an event or an announcement.
|
|
38
|
+
*
|
|
39
|
+
* Accessibility: phase transitions into/out of `'stalled'` are announced
|
|
40
|
+
* through an internal `<lyra-live-region>` (see that component for the
|
|
41
|
+
* throttled/coalesced-announcement machinery this composes) rather than a
|
|
42
|
+
* hand-rolled `aria-live` region. `recordActivity()` itself never announces
|
|
43
|
+
* anything, no matter how often the host calls it — only the *transition*
|
|
44
|
+
* announces, exactly once per transition, which is the entire point of
|
|
45
|
+
* routing through the throttled announcer instead of writing to a live
|
|
46
|
+
* region on every call. Entering `'stalled'` announces with `mode="assertive"`
|
|
47
|
+
* (a stall can need the user's attention, e.g. before they give up and
|
|
48
|
+
* navigate away); recovering back out of `'stalled'` announces with
|
|
49
|
+
* `mode="polite"` (good news doesn't need to interrupt). The decorative
|
|
50
|
+
* indicator dot is `aria-hidden` — it's a color/motion cue only, never the
|
|
51
|
+
* sole carrier of state.
|
|
52
|
+
*
|
|
53
|
+
* Visual: `'stalled'` is styled as a warning, not a danger — a stall is
|
|
54
|
+
* usually recoverable (the stream may resume on its own, or the host's own
|
|
55
|
+
* retry logic may kick in), so treating it as an actionable warning rather
|
|
56
|
+
* than a hard failure keeps the tone proportionate. A host that wants to
|
|
57
|
+
* escalate to danger styling after N stalls can scope its own CSS off
|
|
58
|
+
* `[phase="stalled"]`, or simply stop rendering this component and show its
|
|
59
|
+
* own danger-styled error state instead.
|
|
60
|
+
*
|
|
61
|
+
* @customElement lyra-stream-status
|
|
62
|
+
* @slot - Custom copy shown only while `phase="stalled"` (e.g. "Taking longer than usual…"). A sensible built-in default is used when nothing is slotted.
|
|
63
|
+
* @slot actions - A stop/retry button row. Always present in the template regardless of `phase` — visibility is driven purely by whether anything is slotted, not by `phase`; what to put here, and when, is entirely the host's call.
|
|
64
|
+
* @event lyra-stall - Fired whenever `phase` transitions into `'stalled'` from anything else (timer-driven, or a direct host assignment).
|
|
65
|
+
* @event lyra-recover - Fired whenever `phase` transitions out of `'stalled'` to anything else (via `recordActivity()`, or a direct host assignment).
|
|
66
|
+
* @csspart base - The root layout container.
|
|
67
|
+
* @csspart indicator - The decorative (`aria-hidden`) status dot.
|
|
68
|
+
* @csspart message - Wrapper around the default slot; only rendered while `phase="stalled"`.
|
|
69
|
+
* @csspart actions - Wrapper around the `actions` slot.
|
|
70
|
+
*/
|
|
71
|
+
export declare class LyraStreamStatus extends LyraElement {
|
|
72
|
+
static styles: import("lit").CSSResultGroup[];
|
|
73
|
+
/** Current connection phase. Settable directly by the host at any time —
|
|
74
|
+
* see the class doc for how that interacts with the internal stall timer. */
|
|
75
|
+
phase: StreamStatusPhase;
|
|
76
|
+
/** How long `phase` may stay `'streaming'` with no `recordActivity()` call
|
|
77
|
+
* before this component declares it stalled. */
|
|
78
|
+
stallThresholdMs: number;
|
|
79
|
+
private stallTimer?;
|
|
80
|
+
private hasActionsSlot;
|
|
81
|
+
private liveRegion?;
|
|
82
|
+
protected willUpdate(): void;
|
|
83
|
+
protected updated(changed: PropertyValues): void;
|
|
84
|
+
disconnectedCallback(): void;
|
|
85
|
+
/**
|
|
86
|
+
* Call on every semantic (non-heartbeat) frame received while streaming.
|
|
87
|
+
* - While `phase === 'streaming'`: (re)arms the stall timer, pushing the
|
|
88
|
+
* stall deadline `stall-threshold-ms` further out.
|
|
89
|
+
* - While `phase === 'stalled'`: recovers — `phase` becomes `'streaming'`
|
|
90
|
+
* again, `lyra-recover` fires, and the stall timer is armed fresh (same
|
|
91
|
+
* as the bullet above), all via the same `updated()` transition handling
|
|
92
|
+
* a direct host assignment would also go through.
|
|
93
|
+
* - While `phase` is `'idle'` or `'connecting'`: a no-op. A host may call
|
|
94
|
+
* this defensively before formally flipping to `'streaming'`; it must
|
|
95
|
+
* never throw or start a timer early.
|
|
96
|
+
*/
|
|
97
|
+
recordActivity(): void;
|
|
98
|
+
private onPhaseChanged;
|
|
99
|
+
private armStallTimer;
|
|
100
|
+
private disarmStallTimer;
|
|
101
|
+
private announceTransition;
|
|
102
|
+
private hasSlotted;
|
|
103
|
+
private onActionsSlotChange;
|
|
104
|
+
render(): TemplateResult;
|
|
105
|
+
}
|
|
106
|
+
declare global {
|
|
107
|
+
interface HTMLElementTagNameMap {
|
|
108
|
+
'lyra-stream-status': LyraStreamStatus;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { html, nothing } from 'lit';
|
|
8
|
+
import { property, state, query } from 'lit/decorators.js';
|
|
9
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
10
|
+
import { defineElement } from '../../internal/prefix.js';
|
|
11
|
+
import '../live-region/live-region.js';
|
|
12
|
+
import { styles } from './stream-status.styles.js';
|
|
13
|
+
const DEFAULT_STALL_MESSAGE = 'Taking longer than usual…';
|
|
14
|
+
const STALL_ANNOUNCEMENT = 'Connection stalled.';
|
|
15
|
+
const RECOVER_ANNOUNCEMENT = 'Connection restored.';
|
|
16
|
+
/**
|
|
17
|
+
* `<lyra-stream-status>` — a compact status indicator for a single streaming
|
|
18
|
+
* connection (SSE, WebSocket, long-poll, …), with built-in heartbeat-aware
|
|
19
|
+
* stall detection.
|
|
20
|
+
*
|
|
21
|
+
* The host drives `phase` directly for `idle`/`connecting`/`streaming`, and
|
|
22
|
+
* calls `recordActivity()` on every *semantic* frame received while
|
|
23
|
+
* streaming — a real content chunk, never a transport-level keep-alive ping.
|
|
24
|
+
* This component has no payload-inspection logic of its own: "ignore
|
|
25
|
+
* heartbeats" is entirely a call-site discipline — the host simply never
|
|
26
|
+
* calls `recordActivity()` for a ping, so pings never reset the stall timer
|
|
27
|
+
* and a connection that's only sending keep-alives (no real content) for
|
|
28
|
+
* longer than `stall-threshold-ms` correctly reads as stalled.
|
|
29
|
+
*
|
|
30
|
+
* Internally, an inactivity timer runs only while `phase === 'streaming'`.
|
|
31
|
+
* It's (re)armed whenever the phase transitions to `'streaming'` (directly,
|
|
32
|
+
* or via `recordActivity()` recovering from `'stalled'`) and on every
|
|
33
|
+
* subsequent `recordActivity()` call while already streaming; it's disarmed
|
|
34
|
+
* the instant `phase` becomes anything else, including a host-driven
|
|
35
|
+
* reassignment away from `'streaming'` — so a stale timer can never fire a
|
|
36
|
+
* stall transition after the host has already moved on. If it ever fires,
|
|
37
|
+
* `phase` becomes `'stalled'` and `lyra-stall` is dispatched.
|
|
38
|
+
*
|
|
39
|
+
* `phase` remains a fully public, directly settable property at all times —
|
|
40
|
+
* a host can assign `'stalled'` (or any other phase) itself as a manual
|
|
41
|
+
* override, and this component never fights that assignment. `lyra-stall`/
|
|
42
|
+
* `lyra-recover` fire on *any* transition into/out of `'stalled'`
|
|
43
|
+
* respectively, whether timer-driven or host-driven, and never for a
|
|
44
|
+
* reassignment to the same value (Lit's default `hasChanged` already skips a
|
|
45
|
+
* no-op set, same as every other reflected `@property` in this library).
|
|
46
|
+
* Like `<lyra-chat-message>`'s `status`, whatever phase this element happens
|
|
47
|
+
* to *mount* with is never itself treated as an eventful transition — only a
|
|
48
|
+
* later change fires an event or an announcement.
|
|
49
|
+
*
|
|
50
|
+
* Accessibility: phase transitions into/out of `'stalled'` are announced
|
|
51
|
+
* through an internal `<lyra-live-region>` (see that component for the
|
|
52
|
+
* throttled/coalesced-announcement machinery this composes) rather than a
|
|
53
|
+
* hand-rolled `aria-live` region. `recordActivity()` itself never announces
|
|
54
|
+
* anything, no matter how often the host calls it — only the *transition*
|
|
55
|
+
* announces, exactly once per transition, which is the entire point of
|
|
56
|
+
* routing through the throttled announcer instead of writing to a live
|
|
57
|
+
* region on every call. Entering `'stalled'` announces with `mode="assertive"`
|
|
58
|
+
* (a stall can need the user's attention, e.g. before they give up and
|
|
59
|
+
* navigate away); recovering back out of `'stalled'` announces with
|
|
60
|
+
* `mode="polite"` (good news doesn't need to interrupt). The decorative
|
|
61
|
+
* indicator dot is `aria-hidden` — it's a color/motion cue only, never the
|
|
62
|
+
* sole carrier of state.
|
|
63
|
+
*
|
|
64
|
+
* Visual: `'stalled'` is styled as a warning, not a danger — a stall is
|
|
65
|
+
* usually recoverable (the stream may resume on its own, or the host's own
|
|
66
|
+
* retry logic may kick in), so treating it as an actionable warning rather
|
|
67
|
+
* than a hard failure keeps the tone proportionate. A host that wants to
|
|
68
|
+
* escalate to danger styling after N stalls can scope its own CSS off
|
|
69
|
+
* `[phase="stalled"]`, or simply stop rendering this component and show its
|
|
70
|
+
* own danger-styled error state instead.
|
|
71
|
+
*
|
|
72
|
+
* @customElement lyra-stream-status
|
|
73
|
+
* @slot - Custom copy shown only while `phase="stalled"` (e.g. "Taking longer than usual…"). A sensible built-in default is used when nothing is slotted.
|
|
74
|
+
* @slot actions - A stop/retry button row. Always present in the template regardless of `phase` — visibility is driven purely by whether anything is slotted, not by `phase`; what to put here, and when, is entirely the host's call.
|
|
75
|
+
* @event lyra-stall - Fired whenever `phase` transitions into `'stalled'` from anything else (timer-driven, or a direct host assignment).
|
|
76
|
+
* @event lyra-recover - Fired whenever `phase` transitions out of `'stalled'` to anything else (via `recordActivity()`, or a direct host assignment).
|
|
77
|
+
* @csspart base - The root layout container.
|
|
78
|
+
* @csspart indicator - The decorative (`aria-hidden`) status dot.
|
|
79
|
+
* @csspart message - Wrapper around the default slot; only rendered while `phase="stalled"`.
|
|
80
|
+
* @csspart actions - Wrapper around the `actions` slot.
|
|
81
|
+
*/
|
|
82
|
+
export class LyraStreamStatus extends LyraElement {
|
|
83
|
+
constructor() {
|
|
84
|
+
super(...arguments);
|
|
85
|
+
/** Current connection phase. Settable directly by the host at any time —
|
|
86
|
+
* see the class doc for how that interacts with the internal stall timer. */
|
|
87
|
+
this.phase = 'idle';
|
|
88
|
+
/** How long `phase` may stay `'streaming'` with no `recordActivity()` call
|
|
89
|
+
* before this component declares it stalled. */
|
|
90
|
+
this.stallThresholdMs = 10000;
|
|
91
|
+
this.hasActionsSlot = false;
|
|
92
|
+
this.onActionsSlotChange = (e) => {
|
|
93
|
+
this.hasActionsSlot = e.target.assignedElements({ flatten: true }).length > 0;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
static { this.styles = [LyraElement.styles, styles]; }
|
|
97
|
+
willUpdate() {
|
|
98
|
+
if (!this.hasUpdated) {
|
|
99
|
+
this.hasActionsSlot = this.hasSlotted('actions');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// `changed.get('phase')` is `undefined` on the very first update, for the
|
|
103
|
+
// same reason documented on lyra-chat-message's identical `updated()`
|
|
104
|
+
// guard: Lit only records the *first* old value seen since the last
|
|
105
|
+
// completed update, and the constructor's field-initializer assignment is
|
|
106
|
+
// that first change, from the truly-unset internal slot. That naturally
|
|
107
|
+
// skips firing an event/announcement for whatever phase this element
|
|
108
|
+
// happens to mount with, while still arming the stall timer below if it
|
|
109
|
+
// mounts already `'streaming'`.
|
|
110
|
+
updated(changed) {
|
|
111
|
+
if (changed.has('phase')) {
|
|
112
|
+
this.onPhaseChanged(changed.get('phase'));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
disconnectedCallback() {
|
|
116
|
+
super.disconnectedCallback();
|
|
117
|
+
this.disarmStallTimer();
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Call on every semantic (non-heartbeat) frame received while streaming.
|
|
121
|
+
* - While `phase === 'streaming'`: (re)arms the stall timer, pushing the
|
|
122
|
+
* stall deadline `stall-threshold-ms` further out.
|
|
123
|
+
* - While `phase === 'stalled'`: recovers — `phase` becomes `'streaming'`
|
|
124
|
+
* again, `lyra-recover` fires, and the stall timer is armed fresh (same
|
|
125
|
+
* as the bullet above), all via the same `updated()` transition handling
|
|
126
|
+
* a direct host assignment would also go through.
|
|
127
|
+
* - While `phase` is `'idle'` or `'connecting'`: a no-op. A host may call
|
|
128
|
+
* this defensively before formally flipping to `'streaming'`; it must
|
|
129
|
+
* never throw or start a timer early.
|
|
130
|
+
*/
|
|
131
|
+
recordActivity() {
|
|
132
|
+
if (this.phase === 'streaming') {
|
|
133
|
+
this.armStallTimer();
|
|
134
|
+
}
|
|
135
|
+
else if (this.phase === 'stalled') {
|
|
136
|
+
this.phase = 'streaming';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
onPhaseChanged(previous) {
|
|
140
|
+
if (this.phase === 'streaming') {
|
|
141
|
+
this.armStallTimer();
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
this.disarmStallTimer();
|
|
145
|
+
}
|
|
146
|
+
if (previous === undefined)
|
|
147
|
+
return;
|
|
148
|
+
if (this.phase === 'stalled' && previous !== 'stalled') {
|
|
149
|
+
this.emit('lyra-stall');
|
|
150
|
+
this.announceTransition('assertive', STALL_ANNOUNCEMENT);
|
|
151
|
+
}
|
|
152
|
+
else if (previous === 'stalled' && this.phase !== 'stalled') {
|
|
153
|
+
this.emit('lyra-recover');
|
|
154
|
+
this.announceTransition('polite', RECOVER_ANNOUNCEMENT);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
armStallTimer() {
|
|
158
|
+
this.disarmStallTimer();
|
|
159
|
+
const threshold = this.stallThresholdMs;
|
|
160
|
+
if (!Number.isFinite(threshold) || threshold <= 0)
|
|
161
|
+
return;
|
|
162
|
+
this.stallTimer = setTimeout(() => {
|
|
163
|
+
this.stallTimer = undefined;
|
|
164
|
+
this.phase = 'stalled';
|
|
165
|
+
}, threshold);
|
|
166
|
+
}
|
|
167
|
+
disarmStallTimer() {
|
|
168
|
+
if (this.stallTimer !== undefined) {
|
|
169
|
+
clearTimeout(this.stallTimer);
|
|
170
|
+
this.stallTimer = undefined;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
announceTransition(mode, text) {
|
|
174
|
+
const region = this.liveRegion;
|
|
175
|
+
if (!region)
|
|
176
|
+
return;
|
|
177
|
+
region.mode = mode;
|
|
178
|
+
region.announce(text, { force: true });
|
|
179
|
+
}
|
|
180
|
+
hasSlotted(name) {
|
|
181
|
+
return Array.from(this.children).some((el) => el.getAttribute('slot') === name);
|
|
182
|
+
}
|
|
183
|
+
render() {
|
|
184
|
+
return html `
|
|
185
|
+
<div part="base">
|
|
186
|
+
<span part="indicator" aria-hidden="true"></span>
|
|
187
|
+
${this.phase === 'stalled'
|
|
188
|
+
? html `<div part="message"><slot>${DEFAULT_STALL_MESSAGE}</slot></div>`
|
|
189
|
+
: nothing}
|
|
190
|
+
<div part="actions" ?hidden=${!this.hasActionsSlot}>
|
|
191
|
+
<slot name="actions" @slotchange=${this.onActionsSlotChange}></slot>
|
|
192
|
+
</div>
|
|
193
|
+
<lyra-live-region></lyra-live-region>
|
|
194
|
+
</div>
|
|
195
|
+
`;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
__decorate([
|
|
199
|
+
property({ reflect: true })
|
|
200
|
+
], LyraStreamStatus.prototype, "phase", void 0);
|
|
201
|
+
__decorate([
|
|
202
|
+
property({ type: Number, attribute: 'stall-threshold-ms' })
|
|
203
|
+
], LyraStreamStatus.prototype, "stallThresholdMs", void 0);
|
|
204
|
+
__decorate([
|
|
205
|
+
state()
|
|
206
|
+
], LyraStreamStatus.prototype, "hasActionsSlot", void 0);
|
|
207
|
+
__decorate([
|
|
208
|
+
query('lyra-live-region')
|
|
209
|
+
], LyraStreamStatus.prototype, "liveRegion", void 0);
|
|
210
|
+
defineElement('stream-status', LyraStreamStatus);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import("lit").CSSResult;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const styles = css `
|
|
3
|
+
:host {
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
vertical-align: middle;
|
|
6
|
+
/* Swapped per-phase by the :host([phase]) rules below, rather than
|
|
7
|
+
repeating color/opacity per rule -- same one-custom-property-per-tone
|
|
8
|
+
shape as lyra-tool-call-chip's --lyra-tool-call-chip-accent. */
|
|
9
|
+
--lyra-stream-status-dot-color: var(--lyra-color-text-quiet);
|
|
10
|
+
--lyra-stream-status-dot-opacity: 0.35;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* 'connecting' gets a dimmer, static brand dot -- present but deliberately
|
|
14
|
+
quieter than 'streaming' so the two are never confused at a glance. */
|
|
15
|
+
:host([phase='connecting']) {
|
|
16
|
+
--lyra-stream-status-dot-color: var(--lyra-color-brand);
|
|
17
|
+
--lyra-stream-status-dot-opacity: 0.6;
|
|
18
|
+
}
|
|
19
|
+
:host([phase='streaming']) {
|
|
20
|
+
--lyra-stream-status-dot-color: var(--lyra-color-brand);
|
|
21
|
+
--lyra-stream-status-dot-opacity: 1;
|
|
22
|
+
}
|
|
23
|
+
/* Warning, not danger -- see the class doc's "Visual" section for why a
|
|
24
|
+
stall defaults to the recoverable/actionable tone. */
|
|
25
|
+
:host([phase='stalled']) {
|
|
26
|
+
--lyra-stream-status-dot-color: var(--lyra-color-warning);
|
|
27
|
+
--lyra-stream-status-dot-opacity: 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
[part='base'] {
|
|
31
|
+
display: inline-flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
gap: var(--lyra-space-s);
|
|
34
|
+
border-radius: var(--lyra-radius);
|
|
35
|
+
transition:
|
|
36
|
+
background-color var(--lyra-transition-base),
|
|
37
|
+
border-color var(--lyra-transition-base);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* The one "unmistakable" treatment the spec calls for: a stall tints the
|
|
41
|
+
whole row, not just the dot, so the message/actions read as one alert
|
|
42
|
+
unit rather than a plain dot next to unrelated-looking text. */
|
|
43
|
+
:host([phase='stalled']) [part='base'] {
|
|
44
|
+
padding: var(--lyra-space-xs) var(--lyra-space-s);
|
|
45
|
+
background: var(--lyra-color-warning-quiet);
|
|
46
|
+
border: 1px solid var(--lyra-color-warning);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[part='indicator'] {
|
|
50
|
+
flex: 0 0 auto;
|
|
51
|
+
inline-size: 0.5rem;
|
|
52
|
+
block-size: 0.5rem;
|
|
53
|
+
border-radius: 50%;
|
|
54
|
+
background: var(--lyra-stream-status-dot-color);
|
|
55
|
+
opacity: var(--lyra-stream-status-dot-opacity);
|
|
56
|
+
transition:
|
|
57
|
+
background-color var(--lyra-transition-base),
|
|
58
|
+
opacity var(--lyra-transition-base);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Only 'streaming' pulses -- a moving dot reads as "actively receiving
|
|
62
|
+
data right now"; every other phase (including 'stalled', which wants
|
|
63
|
+
steady attention via color/tint, not extra motion) stays static. Same
|
|
64
|
+
transition-cycle token/rationale as lyra-typing-indicator's pulse
|
|
65
|
+
variant -- see that component's styles for the full explanation of why
|
|
66
|
+
--lyra-transition-base (not -fast) is the right length for an ambient
|
|
67
|
+
loop. */
|
|
68
|
+
:host([phase='streaming']) [part='indicator'] {
|
|
69
|
+
animation: lyra-stream-status-pulse var(--lyra-transition-base) infinite;
|
|
70
|
+
}
|
|
71
|
+
@keyframes lyra-stream-status-pulse {
|
|
72
|
+
0%,
|
|
73
|
+
100% {
|
|
74
|
+
transform: scale(0.85);
|
|
75
|
+
opacity: 0.6;
|
|
76
|
+
}
|
|
77
|
+
50% {
|
|
78
|
+
transform: scale(1);
|
|
79
|
+
opacity: 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
@media (prefers-reduced-motion: reduce) {
|
|
83
|
+
[part='indicator'] {
|
|
84
|
+
animation: none !important;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
[part='message'] {
|
|
89
|
+
color: var(--lyra-color-warning);
|
|
90
|
+
font-size: 0.875rem;
|
|
91
|
+
font-weight: 600;
|
|
92
|
+
line-height: 1.3;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
[part='actions'] {
|
|
96
|
+
display: inline-flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
gap: var(--lyra-space-xs);
|
|
99
|
+
}
|
|
100
|
+
[part='actions'][hidden] {
|
|
101
|
+
display: none;
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { type TemplateResult, type PropertyValues } from 'lit';
|
|
2
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
3
|
+
import '../markdown/markdown.js';
|
|
4
|
+
/** Runs {@link MARKDOWN_PATTERNS} against `text`, used whenever `markdown`
|
|
5
|
+
* is left unset (auto-detect). Exported so the heuristic is directly
|
|
6
|
+
* testable without going through the component's render cycle. */
|
|
7
|
+
export declare function looksLikeMarkdown(text: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* `<lyra-streaming-text>` — a token-coalescing incremental text renderer for
|
|
10
|
+
* streaming assistant output, with an optional blinking cursor and
|
|
11
|
+
* auto-detected Markdown rendering.
|
|
12
|
+
*
|
|
13
|
+
* The host is expected to assign the *entire* current text on every update
|
|
14
|
+
* to `content`, not a delta -- this component does no accumulation or
|
|
15
|
+
* ordering of its own. That matches how most streaming-UI state (SSE/
|
|
16
|
+
* WebSocket token accumulation into a growing string in application state)
|
|
17
|
+
* is already managed.
|
|
18
|
+
*
|
|
19
|
+
* Token-by-token streaming can update `content` far faster than a human can
|
|
20
|
+
* usefully perceive a re-render -- dozens of times a second for a fast
|
|
21
|
+
* model. Re-rendering on every single assignment thrashes layout for no
|
|
22
|
+
* visible benefit, so updates funnel through `Announcer`
|
|
23
|
+
* (`../../internal/announcer.js`), reused here purely as the generic
|
|
24
|
+
* "coalesce rapid calls, flush the latest" timing primitive its own class
|
|
25
|
+
* doc says other components should reuse -- with none of its usual
|
|
26
|
+
* DOM/ARIA plumbing (that's `<lyra-live-region>`'s job, not this
|
|
27
|
+
* component's). Within any `coalesce-ms` window, only the *last* `content`
|
|
28
|
+
* value assigned actually reaches the rendered DOM. Two cases always bypass
|
|
29
|
+
* the throttle and flush immediately instead of waiting out the window: the
|
|
30
|
+
* very first `content` assignment after mount (so an already-complete
|
|
31
|
+
* message never shows an artificial startup delay), and any transition of
|
|
32
|
+
* `streaming` from `true` to `false` (so the final chunk of a finished
|
|
33
|
+
* stream can never be left stranded mid-window).
|
|
34
|
+
*
|
|
35
|
+
* `markdown` is a tri-state property, not a plain boolean: left unset (the
|
|
36
|
+
* default), it auto-detects via a lightweight regex heuristic (see
|
|
37
|
+
* `looksLikeMarkdown`) run against whatever text is currently displayed --
|
|
38
|
+
* good enough to route obviously-Markdown output through `<lyra-markdown>`
|
|
39
|
+
* without the host needing to know or declare it up front, at the cost of
|
|
40
|
+
* an occasional one-time mode flip if Markdown syntax only appears partway
|
|
41
|
+
* through a stream. Explicitly setting `markdown` to `true`/`false` always
|
|
42
|
+
* wins over the heuristic. Rendering itself is never reimplemented here:
|
|
43
|
+
* Markdown mode composes `<lyra-markdown>` (`../markdown/markdown.js`)
|
|
44
|
+
* directly, forwarding this component's own `streaming` through as that
|
|
45
|
+
* component's own forward-compatible `streaming` hint prop; plain-text mode
|
|
46
|
+
* renders into a `white-space: pre-wrap` span instead.
|
|
47
|
+
*
|
|
48
|
+
* The blinking cursor (shown only while `streaming` is `true`) degrades to
|
|
49
|
+
* a static, always-visible bar under `prefers-reduced-motion: reduce`
|
|
50
|
+
* rather than disabling the animation into an invisible frozen frame --
|
|
51
|
+
* the same pattern `<lyra-typing-indicator>`'s own `cursor` variant uses. In
|
|
52
|
+
* plain-text mode the cursor sits inline at the tail of the final
|
|
53
|
+
* character, since the text is one continuous inline flow; in Markdown
|
|
54
|
+
* mode it renders as its own trailing block below the rendered content
|
|
55
|
+
* instead of attempting to splice into whatever nested block Markdown
|
|
56
|
+
* happens to end with (a paragraph, a table cell, a list item, a fenced
|
|
57
|
+
* code block, …) -- genuinely interleaving into arbitrary rendered Markdown
|
|
58
|
+
* without reaching across `<lyra-markdown>`'s own shadow boundary isn't a
|
|
59
|
+
* tractable general solution, so this component doesn't attempt it.
|
|
60
|
+
*
|
|
61
|
+
* Purely presentational: no events, driven entirely by its properties. It
|
|
62
|
+
* also doesn't announce anything to assistive tech itself -- a host that
|
|
63
|
+
* needs streamed text announced already has `<lyra-live-region>` for that
|
|
64
|
+
* (composed, for example, inside `<lyra-chat-message>`).
|
|
65
|
+
*
|
|
66
|
+
* @customElement lyra-streaming-text
|
|
67
|
+
* @csspart base - The root container.
|
|
68
|
+
* @csspart cursor - The blinking (or, under reduced motion, static) cursor bar. Only rendered while `streaming` is `true`.
|
|
69
|
+
*/
|
|
70
|
+
export declare class LyraStreamingText extends LyraElement {
|
|
71
|
+
static styles: import("lit").CSSResultGroup[];
|
|
72
|
+
/** The full current text so far -- always the complete string, never a
|
|
73
|
+
* delta to append. */
|
|
74
|
+
content: string;
|
|
75
|
+
/** Shows the blinking cursor after the rendered text. Reflects, so a host
|
|
76
|
+
* can also target `lyra-streaming-text[streaming]` in CSS. */
|
|
77
|
+
streaming: boolean;
|
|
78
|
+
/** Trailing-edge coalesce window, in ms, for `content` updates -- see the
|
|
79
|
+
* class doc. */
|
|
80
|
+
coalesceMs: number;
|
|
81
|
+
/** `undefined` (default) auto-detects via `looksLikeMarkdown`; `true`/
|
|
82
|
+
* `false` force that rendering mode regardless of content. */
|
|
83
|
+
markdown?: boolean;
|
|
84
|
+
private displayedContent;
|
|
85
|
+
private readonly coalescer;
|
|
86
|
+
constructor();
|
|
87
|
+
protected willUpdate(changed: PropertyValues): void;
|
|
88
|
+
disconnectedCallback(): void;
|
|
89
|
+
/** Whether Markdown mode is actually in effect right now, resolving the
|
|
90
|
+
* tri-state `markdown` property against `looksLikeMarkdown`. */
|
|
91
|
+
private get effectiveMarkdown();
|
|
92
|
+
render(): TemplateResult;
|
|
93
|
+
}
|
|
94
|
+
declare global {
|
|
95
|
+
interface HTMLElementTagNameMap {
|
|
96
|
+
'lyra-streaming-text': LyraStreamingText;
|
|
97
|
+
}
|
|
98
|
+
}
|