@aceshooting/lyra-ui 1.2.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 +11 -0
- package/custom-elements.json +23341 -11202
- 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 +1 -2
- package/dist/components/chart/chart.js +67 -12
- 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 +54 -0
- package/dist/components/chart/lite-chart.js +140 -22
- 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 +29 -13
- package/dist/components/flag/flag.js +39 -13
- 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 +74 -0
- package/dist/components/heatmap/heatmap.js +135 -23
- 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 +43 -2
- package/dist/components/select/select.js +150 -17
- 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 +6 -1
- package/dist/components/stat/stat.js +8 -2
- 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/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 +3 -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 +3836 -17
- 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 -14
- package/dist/components/chart/lite-chart.stories.js +0 -114
- 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 -6
- package/dist/components/flag/flag.stories.js +0 -31
- 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 -27
- package/dist/components/heatmap/heatmap.stories.js +0 -179
- 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 -21
- package/dist/components/select/select.stories.js +0 -91
- 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 -9
- package/dist/components/stat/stat.stories.js +0 -81
- 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 -9
- package/dist/components/widget/widget.stories.js +0 -74
- package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
- package/dist/components/word-cloud/word-cloud.stories.js +0 -55
|
@@ -75,6 +75,17 @@ function niceDomain(dataLo, dataHi, beginAtZero, count) {
|
|
|
75
75
|
* `getComputedStyle()`-based re-theming step is needed the way `chart.ts`
|
|
76
76
|
* needs one for its canvas.
|
|
77
77
|
*
|
|
78
|
+
* By default (`layout="fit"`) the plot always squeezes to the measured host
|
|
79
|
+
* width. Three independent, opt-in escape hatches for dense/aligned data:
|
|
80
|
+
* `layout="scroll"` (+ `barWidth`) gives every bar a fixed pixel width and
|
|
81
|
+
* lets the plot overflow the host horizontally (scrollable) instead of
|
|
82
|
+
* squeezing; `maxLabels` decimates which x-axis text labels render (bars
|
|
83
|
+
* always still render) once there are more categories than that; and
|
|
84
|
+
* `barX` lets a consumer hand in its own per-category x-coordinate function
|
|
85
|
+
* — e.g. to pixel-align this chart's bars with a sibling `lyra-heatmap`'s
|
|
86
|
+
* calendar columns — overriding the internal slot math for both bars and
|
|
87
|
+
* their labels. All three are additive and no-ops when left unset.
|
|
88
|
+
*
|
|
78
89
|
* @customElement lyra-lite-chart
|
|
79
90
|
* @event lyra-point-click - Fired when a bar/point is activated (click, or
|
|
80
91
|
* Enter/Space while focused). `detail: { datasetIndex: number, index:
|
|
@@ -104,6 +115,15 @@ export class LyraLiteChart extends LyraElement {
|
|
|
104
115
|
this.beginAtZero = true;
|
|
105
116
|
/** Stacks each category's bars into one segmented bar. Ignored for `type="line"`. */
|
|
106
117
|
this.stacked = false;
|
|
118
|
+
/** `'fit'` (default) squeezes the plot into the measured host width, unchanged from before this
|
|
119
|
+
* property existed. `'scroll'` gives every bar a fixed `barWidth` instead, letting the plot's
|
|
120
|
+
* content width exceed the host's — the host becomes horizontally scrollable
|
|
121
|
+
* (`overflow-x: auto`) so every bar stays exactly `barWidth` wide regardless of category count.
|
|
122
|
+
* Reflects to the `layout` attribute (e.g. for `:host([layout='scroll'])` host styling). */
|
|
123
|
+
this.layout = 'fit';
|
|
124
|
+
/** Fixed per-category bar width in px, used only when `layout="scroll"`. Ignored (as before this
|
|
125
|
+
* property existed) in `layout="fit"`, the default. */
|
|
126
|
+
this.barWidth = 32;
|
|
107
127
|
this.plotWidth = 0;
|
|
108
128
|
this.plotHeight = 0;
|
|
109
129
|
this.visible = true;
|
|
@@ -132,12 +152,30 @@ export class LyraLiteChart extends LyraElement {
|
|
|
132
152
|
});
|
|
133
153
|
this.intersectionObserver.observe(this);
|
|
134
154
|
}
|
|
155
|
+
// A reconnect re-creates the observer above but the `<svg>` render root
|
|
156
|
+
// content survives across disconnect/reconnect (Lit doesn't tear down the
|
|
157
|
+
// shadow root) — re-observe it here (firstUpdated() only ever runs once,
|
|
158
|
+
// on the very first render, so it can't be relied on for a reconnect).
|
|
159
|
+
if (this.svgEl)
|
|
160
|
+
this.resizeObserver.observe(this.svgEl);
|
|
135
161
|
}
|
|
136
162
|
disconnectedCallback() {
|
|
137
163
|
super.disconnectedCallback();
|
|
138
164
|
this.resizeObserver?.disconnect();
|
|
139
165
|
this.intersectionObserver?.disconnect();
|
|
140
166
|
}
|
|
167
|
+
// Kept (not deleted, despite the "delete it" alternative the fix brief
|
|
168
|
+
// offered): connectedCallback() fires *before* Lit's first render, so on
|
|
169
|
+
// the very first mount `this.svgEl` (a `@query('svg')`) is still
|
|
170
|
+
// undefined there and its guarded `observe()` call is a no-op — verified
|
|
171
|
+
// empirically: deleting firstUpdated() here hung every existing test that
|
|
172
|
+
// waits for the ResizeObserver's initial measurement. firstUpdated() runs
|
|
173
|
+
// once, after that first render, and is what actually arms the observer
|
|
174
|
+
// for the initial mount; connectedCallback()'s own observe() call (added
|
|
175
|
+
// above) only ever succeeds on a *reconnect*, when the shadow DOM (and
|
|
176
|
+
// svgEl) already exists from before the disconnect. Together the two
|
|
177
|
+
// cover first-mount and reconnect without ever double-observing the same
|
|
178
|
+
// element from the same callback path.
|
|
141
179
|
firstUpdated() {
|
|
142
180
|
if (this.svgEl)
|
|
143
181
|
this.resizeObserver?.observe(this.svgEl);
|
|
@@ -200,6 +238,14 @@ export class LyraLiteChart extends LyraElement {
|
|
|
200
238
|
this.stacked,
|
|
201
239
|
this.plotWidth,
|
|
202
240
|
this.plotHeight,
|
|
241
|
+
this.layout,
|
|
242
|
+
this.barWidth,
|
|
243
|
+
this.maxLabels,
|
|
244
|
+
// `tickFormat`/`barX` are functions -- JSON.stringify() silently drops
|
|
245
|
+
// function-valued entries, so (matching tickFormat's existing,
|
|
246
|
+
// pre-this-change exclusion) they can't be included here anyway; a
|
|
247
|
+
// consumer swapping one in/out post-mount should also change some
|
|
248
|
+
// other signature input, same pre-existing caveat as tickFormat.
|
|
203
249
|
]);
|
|
204
250
|
}
|
|
205
251
|
emitPoint(datasetIndex, index) {
|
|
@@ -223,28 +269,39 @@ export class LyraLiteChart extends LyraElement {
|
|
|
223
269
|
`;
|
|
224
270
|
});
|
|
225
271
|
}
|
|
226
|
-
|
|
272
|
+
/**
|
|
273
|
+
* `slot` is the per-category width to lay bars out against — either the
|
|
274
|
+
* measured-width-derived `plotW / n` (`layout="fit"`) or the fixed
|
|
275
|
+
* `barWidth` (`layout="scroll"`), computed once by the caller
|
|
276
|
+
* (`renderChart()`) and handed to both this method and the category-label
|
|
277
|
+
* x-position calc so the two can never drift apart from each other.
|
|
278
|
+
*/
|
|
279
|
+
renderBars(plotX, plotY, plotH, slot, lo, hi) {
|
|
227
280
|
const span = hi - lo || 1;
|
|
228
281
|
const n = this.labels.length;
|
|
229
|
-
const slot = n > 0 ? plotW / n : 0;
|
|
230
282
|
const groupCount = this.stacked ? 1 : Math.max(1, this.datasets.length);
|
|
231
283
|
const groupW = slot * (1 - BAR_GROUP_GAP);
|
|
232
284
|
const barW = (groupW - BAR_GAP * slot * (groupCount - 1)) / groupCount;
|
|
233
285
|
const bars = [];
|
|
234
286
|
for (let i = 0; i < n; i++) {
|
|
235
|
-
|
|
287
|
+
// `this.barX`, when set, overrides just the category's own x-origin
|
|
288
|
+
// (what an unset `barX` would compute as `plotX + i * slot`) -- the
|
|
289
|
+
// group-centering/per-dataset-offset math below still layers on top of
|
|
290
|
+
// it, same as it does on top of the internal formula.
|
|
291
|
+
const origin = this.barX ? this.barX(i) : plotX + i * slot;
|
|
292
|
+
const slotStart = origin + (slot - groupW) / 2;
|
|
236
293
|
let stackPos = 0; // running positive-side offset (in value units) for stacked bars
|
|
237
294
|
let stackNeg = 0; // running negative-side offset
|
|
238
295
|
this.datasets.forEach((s, di) => {
|
|
239
296
|
const v = s.data[i];
|
|
240
|
-
if (v == null)
|
|
297
|
+
if (v == null || !Number.isFinite(v))
|
|
241
298
|
return;
|
|
242
299
|
const color = this.colorFor(di, s);
|
|
243
|
-
let
|
|
300
|
+
let rectX;
|
|
244
301
|
let barValLo;
|
|
245
302
|
let barValHi;
|
|
246
303
|
if (this.stacked) {
|
|
247
|
-
|
|
304
|
+
rectX = slotStart;
|
|
248
305
|
if (v >= 0) {
|
|
249
306
|
barValLo = stackPos;
|
|
250
307
|
barValHi = stackPos + v;
|
|
@@ -257,9 +314,10 @@ export class LyraLiteChart extends LyraElement {
|
|
|
257
314
|
}
|
|
258
315
|
}
|
|
259
316
|
else {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
317
|
+
rectX = slotStart + di * (barW + BAR_GAP * slot);
|
|
318
|
+
const zeroClamped = Math.min(hi, Math.max(lo, 0));
|
|
319
|
+
barValLo = Math.min(zeroClamped, v);
|
|
320
|
+
barValHi = Math.max(zeroClamped, v);
|
|
263
321
|
}
|
|
264
322
|
const y1 = plotY + plotH - ((barValHi - lo) / span) * plotH;
|
|
265
323
|
const y2 = plotY + plotH - ((barValLo - lo) / span) * plotH;
|
|
@@ -267,7 +325,7 @@ export class LyraLiteChart extends LyraElement {
|
|
|
267
325
|
bars.push(svg `
|
|
268
326
|
<rect
|
|
269
327
|
part="bar"
|
|
270
|
-
x=${
|
|
328
|
+
x=${rectX}
|
|
271
329
|
y=${y1}
|
|
272
330
|
width=${Math.max(0, barW)}
|
|
273
331
|
height=${Math.max(0, y2 - y1)}
|
|
@@ -290,12 +348,19 @@ export class LyraLiteChart extends LyraElement {
|
|
|
290
348
|
const yFor = (v) => plotY + plotH - ((v - lo) / span) * plotH;
|
|
291
349
|
return this.datasets.map((s, di) => {
|
|
292
350
|
const color = this.colorFor(di, s);
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
351
|
+
let d = '';
|
|
352
|
+
let penDown = false;
|
|
353
|
+
s.data.forEach((v, i) => {
|
|
354
|
+
if (v == null || !Number.isFinite(v)) {
|
|
355
|
+
penDown = false;
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const cmd = penDown ? 'L' : 'M';
|
|
359
|
+
d += `${cmd}${xFor(i)},${yFor(v)} `;
|
|
360
|
+
penDown = true;
|
|
361
|
+
});
|
|
297
362
|
const dots = s.data.map((v, i) => {
|
|
298
|
-
if (v == null)
|
|
363
|
+
if (v == null || !Number.isFinite(v))
|
|
299
364
|
return nothing;
|
|
300
365
|
const label = this.labels[i];
|
|
301
366
|
return svg `
|
|
@@ -313,7 +378,7 @@ export class LyraLiteChart extends LyraElement {
|
|
|
313
378
|
><title>${s.label} — ${label}: ${v}</title></circle>
|
|
314
379
|
`;
|
|
315
380
|
});
|
|
316
|
-
return svg `<path part="line" d=${d} stroke=${color}></path>${dots}`;
|
|
381
|
+
return svg `<path part="line" d=${d.trim()} stroke=${color}></path>${dots}`;
|
|
317
382
|
});
|
|
318
383
|
}
|
|
319
384
|
render() {
|
|
@@ -327,29 +392,70 @@ export class LyraLiteChart extends LyraElement {
|
|
|
327
392
|
this.lastResult = result;
|
|
328
393
|
return result;
|
|
329
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* Step size for `maxLabels` decimation: 1 (show every label, today's
|
|
397
|
+
* unchanged default) when `maxLabels` is unset or already >= the label
|
|
398
|
+
* count, otherwise the ceil-divide spacing that lands close to
|
|
399
|
+
* `maxLabels` shown labels while always keeping index 0 and the last
|
|
400
|
+
* index (enforced by the caller, not here).
|
|
401
|
+
*/
|
|
402
|
+
labelStep(n) {
|
|
403
|
+
const max = this.maxLabels;
|
|
404
|
+
if (max == null || n <= max)
|
|
405
|
+
return 1;
|
|
406
|
+
return Math.max(1, Math.ceil((n - 1) / Math.max(1, max - 1)));
|
|
407
|
+
}
|
|
330
408
|
renderChart() {
|
|
331
|
-
const
|
|
409
|
+
const n = this.labels.length;
|
|
332
410
|
const h = this.plotHeight || 200;
|
|
333
411
|
const padLeft = PAD_LEFT + (this.yLabel ? AXIS_TITLE_SPACE : 0);
|
|
334
412
|
const padBottom = PAD_BOTTOM + (this.xLabel ? AXIS_TITLE_SPACE : 0);
|
|
335
413
|
const plotX = padLeft;
|
|
336
414
|
const plotY = PAD_TOP;
|
|
337
|
-
const plotW = Math.max(0, w - padLeft - PAD_RIGHT);
|
|
338
415
|
const plotH = Math.max(0, h - plotY - padBottom);
|
|
416
|
+
let w;
|
|
417
|
+
let plotW;
|
|
418
|
+
let slot;
|
|
419
|
+
if (this.layout === 'scroll') {
|
|
420
|
+
// Fixed-width bars: content width is driven by category count ×
|
|
421
|
+
// barWidth instead of the measured host width, and CAN exceed it --
|
|
422
|
+
// the svg gets an explicit inline-size below (not 100%) and
|
|
423
|
+
// `:host([layout='scroll']) [part='base']` (lite-chart.styles.ts)
|
|
424
|
+
// turns on `overflow-x: auto` so the host scrolls to reveal the rest.
|
|
425
|
+
slot = this.barWidth;
|
|
426
|
+
plotW = Math.max(0, n * slot);
|
|
427
|
+
w = plotX + plotW + PAD_RIGHT;
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
// 'fit' (default): squeeze to the measured host width, byte-for-byte
|
|
431
|
+
// the same computation as before `layout` existed.
|
|
432
|
+
w = this.plotWidth || 400;
|
|
433
|
+
plotW = Math.max(0, w - plotX - PAD_RIGHT);
|
|
434
|
+
slot = n > 0 ? plotW / n : 0;
|
|
435
|
+
}
|
|
339
436
|
const { lo, hi, ticks } = this.domain();
|
|
340
437
|
const grid = this.renderGrid(plotX, plotY, plotW, plotH, ticks, lo, hi);
|
|
341
438
|
const marks = this.type === 'bar'
|
|
342
|
-
? this.renderBars(plotX, plotY,
|
|
439
|
+
? this.renderBars(plotX, plotY, plotH, slot, lo, hi)
|
|
343
440
|
: this.renderLines(plotX, plotY, plotW, plotH, lo, hi);
|
|
441
|
+
const step = this.labelStep(n);
|
|
344
442
|
const categoryLabels = this.labels.map((label, i) => {
|
|
345
|
-
|
|
346
|
-
|
|
443
|
+
if (i !== 0 && i !== n - 1 && i % step !== 0)
|
|
444
|
+
return nothing; // maxLabels decimation
|
|
445
|
+
const x = this.type === 'bar' && n > 0
|
|
446
|
+
? (this.barX ? this.barX(i) : plotX + i * slot) + slot / 2 // matches renderBars()'s own per-category slot center (or the barX override, when set)
|
|
447
|
+
: plotX + (n > 1 ? (i / (n - 1)) * plotW : plotW / 2);
|
|
347
448
|
return svg `<text part="axis-label" x=${x} y=${plotY + plotH + 14} text-anchor="middle">${label}</text>`;
|
|
348
449
|
});
|
|
349
450
|
const chartLabel = this.datasets.map((d) => d.label).join(', ') || 'Chart';
|
|
350
451
|
return html `
|
|
351
452
|
<div part="base">
|
|
352
|
-
<svg
|
|
453
|
+
<svg
|
|
454
|
+
viewBox="0 0 ${w} ${h}"
|
|
455
|
+
style=${this.layout === 'scroll' ? `inline-size: ${w}px` : nothing}
|
|
456
|
+
role="group"
|
|
457
|
+
aria-label=${chartLabel}
|
|
458
|
+
>
|
|
353
459
|
${grid}
|
|
354
460
|
${categoryLabels}
|
|
355
461
|
${marks}
|
|
@@ -404,6 +510,18 @@ __decorate([
|
|
|
404
510
|
__decorate([
|
|
405
511
|
property({ attribute: false })
|
|
406
512
|
], LyraLiteChart.prototype, "tickFormat", void 0);
|
|
513
|
+
__decorate([
|
|
514
|
+
property({ reflect: true })
|
|
515
|
+
], LyraLiteChart.prototype, "layout", void 0);
|
|
516
|
+
__decorate([
|
|
517
|
+
property({ type: Number, attribute: 'bar-width' })
|
|
518
|
+
], LyraLiteChart.prototype, "barWidth", void 0);
|
|
519
|
+
__decorate([
|
|
520
|
+
property({ type: Number, attribute: 'max-labels' })
|
|
521
|
+
], LyraLiteChart.prototype, "maxLabels", void 0);
|
|
522
|
+
__decorate([
|
|
523
|
+
property({ attribute: false })
|
|
524
|
+
], LyraLiteChart.prototype, "barX", void 0);
|
|
407
525
|
__decorate([
|
|
408
526
|
state()
|
|
409
527
|
], LyraLiteChart.prototype, "plotWidth", void 0);
|
|
@@ -23,6 +23,15 @@ export const styles = css `
|
|
|
23
23
|
flex-direction: column;
|
|
24
24
|
gap: var(--lyra-space-xs);
|
|
25
25
|
}
|
|
26
|
+
/* layout="scroll": the svg below gets an explicit inline-size (its
|
|
27
|
+
computed content width, set inline per-render since it depends on
|
|
28
|
+
category count/barWidth) instead of the 100% below, and can end up
|
|
29
|
+
wider than this container -- scroll to reveal the rest instead of
|
|
30
|
+
squeezing. Scoped strictly to the reflected [layout='scroll'] attribute
|
|
31
|
+
so layout="fit" (the default) never triggers this rule. */
|
|
32
|
+
:host([layout='scroll']) [part='base'] {
|
|
33
|
+
overflow-x: auto;
|
|
34
|
+
}
|
|
26
35
|
svg {
|
|
27
36
|
flex: 1 1 auto;
|
|
28
37
|
inline-size: 100%;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { type TemplateResult, type PropertyValues } from 'lit';
|
|
2
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
3
|
+
export type ChatComposerStatus = 'idle' | 'sending' | 'streaming';
|
|
4
|
+
declare const LyraChatComposer_base: typeof LyraElement & (new (...args: any[]) => import("../../internal/form-associated.js").FormAssociatedInterface);
|
|
5
|
+
/**
|
|
6
|
+
* `<lyra-chat-composer>` — the message input for a chat/agent conversation
|
|
7
|
+
* surface: an auto-resizing textarea plus a send/stop button. Form-
|
|
8
|
+
* associated via the `FormAssociated` mixin (see `<lyra-date-input>` for the
|
|
9
|
+
* same shape), so it participates in native `<form>` submission/validation/
|
|
10
|
+
* reset like any other text control — `name`/`value`/`disabled`/`required`/
|
|
11
|
+
* `checkValidity()`/`reportValidity()` all come from that mixin.
|
|
12
|
+
*
|
|
13
|
+
* Auto-resize reads the textarea's own *computed* line-height/padding/
|
|
14
|
+
* border at runtime (`resizeTextarea()`) rather than assuming a fixed
|
|
15
|
+
* px-per-row constant, so it stays correct under a consumer's own font-size/
|
|
16
|
+
* line-height overrides. It grows between `min-rows` and `max-rows`, then
|
|
17
|
+
* switches to internal scrolling.
|
|
18
|
+
*
|
|
19
|
+
* Enter-to-send (only active while `submit-on-enter` is true, the default):
|
|
20
|
+
* plain Enter submits and is prevented from inserting a newline; Shift+Enter
|
|
21
|
+
* always inserts a newline no matter what; an IME composition step (checked
|
|
22
|
+
* via `isComposing`, with `keyCode === 229` as a defense-in-depth fallback
|
|
23
|
+
* for browsers that report `isComposing` inconsistently on the
|
|
24
|
+
* compositionend-adjacent keydown) is never treated as a submit trigger;
|
|
25
|
+
* and while `status` isn't `"idle"` Enter is left alone to insert a newline
|
|
26
|
+
* too, rather than trying to submit again -- there is nothing meaningful to
|
|
27
|
+
* submit while a previous message is still sending/streaming, and the
|
|
28
|
+
* textarea deliberately stays interactive (see `disabled` below) so a user
|
|
29
|
+
* can keep composing their next message in the meantime.
|
|
30
|
+
*
|
|
31
|
+
* `lyra-submit`'s `detail.value` is always the exact, untrimmed current
|
|
32
|
+
* value (`detail.value === value` at the moment it fires) -- trimming is
|
|
33
|
+
* left to the consumer so it never silently diverges from what `value`
|
|
34
|
+
* itself reports. Submitting does not clear `value`; the consumer clears it
|
|
35
|
+
* once the submission has actually been accepted (so e.g. a failed send can
|
|
36
|
+
* leave the text in place for retry).
|
|
37
|
+
*
|
|
38
|
+
* @customElement lyra-chat-composer
|
|
39
|
+
* @slot leading - Content rendered before the textarea (e.g. an attach-file trigger button).
|
|
40
|
+
* @slot chips - An attachment tray rendered above the input row (e.g. files queued for this message).
|
|
41
|
+
* @slot trailing - Overrides the built-in send/stop button entirely when it has assigned content.
|
|
42
|
+
* @event lyra-input - Fired on every user-driven edit of the textarea (not a programmatic `.value` assignment). `detail: { value }`.
|
|
43
|
+
* @event lyra-submit - Fired by Enter (per `submit-on-enter`) or the built-in button while `status="idle"`. `detail: { value }`.
|
|
44
|
+
* @event lyra-stop - Fired by the built-in button while `status` is `"sending"` or `"streaming"`. No detail.
|
|
45
|
+
* @csspart base - The bordered root container.
|
|
46
|
+
* @csspart chips - The wrapper around the `chips` slot. Hidden entirely when the slot is empty.
|
|
47
|
+
* @csspart row - The row holding the leading slot, textarea, and trailing slot/button.
|
|
48
|
+
* @csspart leading - The wrapper around the `leading` slot. Hidden entirely when the slot is empty.
|
|
49
|
+
* @csspart textarea - The auto-resizing `<textarea>` itself.
|
|
50
|
+
* @csspart trailing - The wrapper around the `trailing` slot and the built-in `action-button`.
|
|
51
|
+
* @csspart action-button - The built-in send/stop button. Absent from the accessibility tree's meaningful content whenever `trailing` has assigned content. Style its busy treatment via `:host([status='sending'])`/`:host([status='streaming'])`.
|
|
52
|
+
*/
|
|
53
|
+
export declare class LyraChatComposer extends LyraChatComposer_base {
|
|
54
|
+
static styles: import("lit").CSSResultGroup[];
|
|
55
|
+
placeholder: string;
|
|
56
|
+
minRows: number;
|
|
57
|
+
maxRows: number;
|
|
58
|
+
status: ChatComposerStatus;
|
|
59
|
+
submitOnEnter: boolean;
|
|
60
|
+
private hasLeadingSlot;
|
|
61
|
+
private hasChipsSlot;
|
|
62
|
+
private hasTrailingSlot;
|
|
63
|
+
private textareaEl?;
|
|
64
|
+
protected willUpdate(): void;
|
|
65
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
66
|
+
protected updated(changed: PropertyValues): void;
|
|
67
|
+
private hasSlotted;
|
|
68
|
+
/**
|
|
69
|
+
* Grows/shrinks the textarea between `min-rows` and `max-rows`, reading
|
|
70
|
+
* the *computed* line-height/padding/border back out of the element at
|
|
71
|
+
* call time instead of assuming a fixed px-per-row figure -- correct
|
|
72
|
+
* whatever font-size/line-height a consumer's own CSS ends up applying.
|
|
73
|
+
* Past `max-rows` the box stops growing and switches to internal
|
|
74
|
+
* scrolling (`overflow-y: auto`).
|
|
75
|
+
*/
|
|
76
|
+
private resizeTextarea;
|
|
77
|
+
private onTextareaInput;
|
|
78
|
+
private onTextareaKeyDown;
|
|
79
|
+
private onActionClick;
|
|
80
|
+
private submit;
|
|
81
|
+
private onLeadingSlotChange;
|
|
82
|
+
private onChipsSlotChange;
|
|
83
|
+
private onTrailingSlotChange;
|
|
84
|
+
private renderActionButton;
|
|
85
|
+
render(): TemplateResult;
|
|
86
|
+
}
|
|
87
|
+
declare global {
|
|
88
|
+
interface HTMLElementTagNameMap {
|
|
89
|
+
'lyra-chat-composer': LyraChatComposer;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export {};
|
|
@@ -0,0 +1,285 @@
|
|
|
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, svg } from 'lit';
|
|
8
|
+
import { property, state, query } from 'lit/decorators.js';
|
|
9
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
10
|
+
import { FormAssociated } from '../../internal/form-associated.js';
|
|
11
|
+
import { defineElement } from '../../internal/prefix.js';
|
|
12
|
+
import { styles } from './chat-composer.styles.js';
|
|
13
|
+
// Mirrors the shared icon set's viewBox/stroke conventions
|
|
14
|
+
// (internal/icons.ts's chevronIcon()/closeIcon()/etc.) without adding
|
|
15
|
+
// send/stop glyphs to that module -- it's off limits here -- so these
|
|
16
|
+
// one-off icons still read as part of the same visual language as the rest
|
|
17
|
+
// of the library's inline icons. Same approach lyra-checkbox's and
|
|
18
|
+
// lyra-chat-message's own local glyphs take for the identical reason.
|
|
19
|
+
const ICON_VIEW_BOX = '0 0 24 24';
|
|
20
|
+
const ICON_STROKE_WIDTH = '1.75';
|
|
21
|
+
function sendIcon() {
|
|
22
|
+
return svg `
|
|
23
|
+
<svg
|
|
24
|
+
width="1em"
|
|
25
|
+
height="1em"
|
|
26
|
+
viewBox=${ICON_VIEW_BOX}
|
|
27
|
+
fill="none"
|
|
28
|
+
stroke="currentColor"
|
|
29
|
+
stroke-width=${ICON_STROKE_WIDTH}
|
|
30
|
+
stroke-linecap="round"
|
|
31
|
+
stroke-linejoin="round"
|
|
32
|
+
aria-hidden="true"
|
|
33
|
+
focusable="false"
|
|
34
|
+
><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
/** A filled square -- the conventional "stop generating" glyph. */
|
|
38
|
+
function stopIcon() {
|
|
39
|
+
return svg `
|
|
40
|
+
<svg
|
|
41
|
+
width="1em"
|
|
42
|
+
height="1em"
|
|
43
|
+
viewBox=${ICON_VIEW_BOX}
|
|
44
|
+
fill="currentColor"
|
|
45
|
+
stroke="none"
|
|
46
|
+
aria-hidden="true"
|
|
47
|
+
focusable="false"
|
|
48
|
+
><rect x="6" y="6" width="12" height="12" rx="1.5"></rect></svg>
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `<lyra-chat-composer>` — the message input for a chat/agent conversation
|
|
53
|
+
* surface: an auto-resizing textarea plus a send/stop button. Form-
|
|
54
|
+
* associated via the `FormAssociated` mixin (see `<lyra-date-input>` for the
|
|
55
|
+
* same shape), so it participates in native `<form>` submission/validation/
|
|
56
|
+
* reset like any other text control — `name`/`value`/`disabled`/`required`/
|
|
57
|
+
* `checkValidity()`/`reportValidity()` all come from that mixin.
|
|
58
|
+
*
|
|
59
|
+
* Auto-resize reads the textarea's own *computed* line-height/padding/
|
|
60
|
+
* border at runtime (`resizeTextarea()`) rather than assuming a fixed
|
|
61
|
+
* px-per-row constant, so it stays correct under a consumer's own font-size/
|
|
62
|
+
* line-height overrides. It grows between `min-rows` and `max-rows`, then
|
|
63
|
+
* switches to internal scrolling.
|
|
64
|
+
*
|
|
65
|
+
* Enter-to-send (only active while `submit-on-enter` is true, the default):
|
|
66
|
+
* plain Enter submits and is prevented from inserting a newline; Shift+Enter
|
|
67
|
+
* always inserts a newline no matter what; an IME composition step (checked
|
|
68
|
+
* via `isComposing`, with `keyCode === 229` as a defense-in-depth fallback
|
|
69
|
+
* for browsers that report `isComposing` inconsistently on the
|
|
70
|
+
* compositionend-adjacent keydown) is never treated as a submit trigger;
|
|
71
|
+
* and while `status` isn't `"idle"` Enter is left alone to insert a newline
|
|
72
|
+
* too, rather than trying to submit again -- there is nothing meaningful to
|
|
73
|
+
* submit while a previous message is still sending/streaming, and the
|
|
74
|
+
* textarea deliberately stays interactive (see `disabled` below) so a user
|
|
75
|
+
* can keep composing their next message in the meantime.
|
|
76
|
+
*
|
|
77
|
+
* `lyra-submit`'s `detail.value` is always the exact, untrimmed current
|
|
78
|
+
* value (`detail.value === value` at the moment it fires) -- trimming is
|
|
79
|
+
* left to the consumer so it never silently diverges from what `value`
|
|
80
|
+
* itself reports. Submitting does not clear `value`; the consumer clears it
|
|
81
|
+
* once the submission has actually been accepted (so e.g. a failed send can
|
|
82
|
+
* leave the text in place for retry).
|
|
83
|
+
*
|
|
84
|
+
* @customElement lyra-chat-composer
|
|
85
|
+
* @slot leading - Content rendered before the textarea (e.g. an attach-file trigger button).
|
|
86
|
+
* @slot chips - An attachment tray rendered above the input row (e.g. files queued for this message).
|
|
87
|
+
* @slot trailing - Overrides the built-in send/stop button entirely when it has assigned content.
|
|
88
|
+
* @event lyra-input - Fired on every user-driven edit of the textarea (not a programmatic `.value` assignment). `detail: { value }`.
|
|
89
|
+
* @event lyra-submit - Fired by Enter (per `submit-on-enter`) or the built-in button while `status="idle"`. `detail: { value }`.
|
|
90
|
+
* @event lyra-stop - Fired by the built-in button while `status` is `"sending"` or `"streaming"`. No detail.
|
|
91
|
+
* @csspart base - The bordered root container.
|
|
92
|
+
* @csspart chips - The wrapper around the `chips` slot. Hidden entirely when the slot is empty.
|
|
93
|
+
* @csspart row - The row holding the leading slot, textarea, and trailing slot/button.
|
|
94
|
+
* @csspart leading - The wrapper around the `leading` slot. Hidden entirely when the slot is empty.
|
|
95
|
+
* @csspart textarea - The auto-resizing `<textarea>` itself.
|
|
96
|
+
* @csspart trailing - The wrapper around the `trailing` slot and the built-in `action-button`.
|
|
97
|
+
* @csspart action-button - The built-in send/stop button. Absent from the accessibility tree's meaningful content whenever `trailing` has assigned content. Style its busy treatment via `:host([status='sending'])`/`:host([status='streaming'])`.
|
|
98
|
+
*/
|
|
99
|
+
export class LyraChatComposer extends FormAssociated(LyraElement) {
|
|
100
|
+
constructor() {
|
|
101
|
+
super(...arguments);
|
|
102
|
+
this.placeholder = '';
|
|
103
|
+
this.minRows = 1;
|
|
104
|
+
this.maxRows = 8;
|
|
105
|
+
this.status = 'idle';
|
|
106
|
+
this.submitOnEnter = true;
|
|
107
|
+
this.hasLeadingSlot = false;
|
|
108
|
+
this.hasChipsSlot = false;
|
|
109
|
+
this.hasTrailingSlot = false;
|
|
110
|
+
this.onTextareaInput = (e) => {
|
|
111
|
+
this.value = e.target.value;
|
|
112
|
+
this.emit('lyra-input', { value: this.value });
|
|
113
|
+
};
|
|
114
|
+
this.onTextareaKeyDown = (e) => {
|
|
115
|
+
if (e.key !== 'Enter')
|
|
116
|
+
return;
|
|
117
|
+
// Shift+Enter always inserts a newline, regardless of submit-on-enter --
|
|
118
|
+
// leave the browser's own default action alone.
|
|
119
|
+
if (e.shiftKey)
|
|
120
|
+
return;
|
|
121
|
+
// submit-on-enter="false": Enter always inserts a newline too.
|
|
122
|
+
if (!this.submitOnEnter)
|
|
123
|
+
return;
|
|
124
|
+
// An IME composition step (e.g. confirming a Japanese/Chinese/Korean
|
|
125
|
+
// candidate) must never be treated as "the user pressed Enter to send" --
|
|
126
|
+
// keyCode 229 is a defense-in-depth fallback for browsers that report
|
|
127
|
+
// isComposing inconsistently on the compositionend-adjacent keydown.
|
|
128
|
+
if (e.isComposing || e.keyCode === 229)
|
|
129
|
+
return;
|
|
130
|
+
// Nothing meaningful to (re-)submit while already sending/streaming --
|
|
131
|
+
// fall through to the default newline insertion instead, so the
|
|
132
|
+
// textarea stays a normal place to keep composing the next message.
|
|
133
|
+
if (this.status !== 'idle')
|
|
134
|
+
return;
|
|
135
|
+
e.preventDefault();
|
|
136
|
+
this.submit();
|
|
137
|
+
};
|
|
138
|
+
this.onActionClick = () => {
|
|
139
|
+
if (this.effectiveDisabled)
|
|
140
|
+
return;
|
|
141
|
+
if (this.status === 'idle') {
|
|
142
|
+
this.submit();
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
this.emit('lyra-stop');
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
this.onLeadingSlotChange = (e) => {
|
|
149
|
+
this.hasLeadingSlot = e.target.assignedElements({ flatten: true }).length > 0;
|
|
150
|
+
};
|
|
151
|
+
this.onChipsSlotChange = (e) => {
|
|
152
|
+
this.hasChipsSlot = e.target.assignedElements({ flatten: true }).length > 0;
|
|
153
|
+
};
|
|
154
|
+
this.onTrailingSlotChange = (e) => {
|
|
155
|
+
this.hasTrailingSlot = e.target.assignedElements({ flatten: true }).length > 0;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
static { this.styles = [LyraElement.styles, styles]; }
|
|
159
|
+
willUpdate() {
|
|
160
|
+
if (!this.hasUpdated) {
|
|
161
|
+
this.hasLeadingSlot = this.hasSlotted('leading');
|
|
162
|
+
this.hasChipsSlot = this.hasSlotted('chips');
|
|
163
|
+
this.hasTrailingSlot = this.hasSlotted('trailing');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
firstUpdated(changed) {
|
|
167
|
+
super.firstUpdated(changed);
|
|
168
|
+
this.resizeTextarea();
|
|
169
|
+
}
|
|
170
|
+
updated(changed) {
|
|
171
|
+
super.updated(changed);
|
|
172
|
+
if (changed.has('value') || changed.has('minRows') || changed.has('maxRows')) {
|
|
173
|
+
this.resizeTextarea();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
hasSlotted(name) {
|
|
177
|
+
return Array.from(this.children).some((el) => el.getAttribute('slot') === name);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Grows/shrinks the textarea between `min-rows` and `max-rows`, reading
|
|
181
|
+
* the *computed* line-height/padding/border back out of the element at
|
|
182
|
+
* call time instead of assuming a fixed px-per-row figure -- correct
|
|
183
|
+
* whatever font-size/line-height a consumer's own CSS ends up applying.
|
|
184
|
+
* Past `max-rows` the box stops growing and switches to internal
|
|
185
|
+
* scrolling (`overflow-y: auto`).
|
|
186
|
+
*/
|
|
187
|
+
resizeTextarea() {
|
|
188
|
+
const ta = this.textareaEl;
|
|
189
|
+
if (!ta)
|
|
190
|
+
return;
|
|
191
|
+
const cs = getComputedStyle(ta);
|
|
192
|
+
const lineHeight = parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2;
|
|
193
|
+
const paddingBlock = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom);
|
|
194
|
+
const borderBlock = parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth);
|
|
195
|
+
const minRows = Math.max(1, this.minRows || 1);
|
|
196
|
+
const maxRows = Math.max(minRows, this.maxRows || minRows);
|
|
197
|
+
const minHeight = lineHeight * minRows + paddingBlock + borderBlock;
|
|
198
|
+
const maxHeight = lineHeight * maxRows + paddingBlock + borderBlock;
|
|
199
|
+
// Collapse first so a shrinking edit (e.g. deleting a wrapped line)
|
|
200
|
+
// reports its true scrollHeight rather than the previous, taller,
|
|
201
|
+
// explicitly-set height.
|
|
202
|
+
ta.style.height = 'auto';
|
|
203
|
+
// scrollHeight never includes border, so it's added back in to compare
|
|
204
|
+
// apples-to-apples against min/maxHeight, both of which are border-box
|
|
205
|
+
// figures (this component inherits box-sizing: border-box from
|
|
206
|
+
// LyraElement's tokens.styles.ts).
|
|
207
|
+
const contentHeight = ta.scrollHeight + borderBlock;
|
|
208
|
+
const next = Math.min(Math.max(contentHeight, minHeight), maxHeight);
|
|
209
|
+
ta.style.height = `${next}px`;
|
|
210
|
+
ta.style.overflowY = contentHeight > maxHeight ? 'auto' : 'hidden';
|
|
211
|
+
}
|
|
212
|
+
submit() {
|
|
213
|
+
this.emit('lyra-submit', { value: this.value });
|
|
214
|
+
}
|
|
215
|
+
renderActionButton() {
|
|
216
|
+
const busy = this.status !== 'idle';
|
|
217
|
+
return html `
|
|
218
|
+
<button
|
|
219
|
+
part="action-button"
|
|
220
|
+
type="button"
|
|
221
|
+
aria-label=${busy ? 'Stop generating' : 'Send message'}
|
|
222
|
+
?disabled=${this.effectiveDisabled}
|
|
223
|
+
@click=${this.onActionClick}
|
|
224
|
+
>
|
|
225
|
+
${busy ? stopIcon() : sendIcon()}
|
|
226
|
+
</button>
|
|
227
|
+
`;
|
|
228
|
+
}
|
|
229
|
+
render() {
|
|
230
|
+
return html `
|
|
231
|
+
<div part="base">
|
|
232
|
+
<div part="chips" ?hidden=${!this.hasChipsSlot}>
|
|
233
|
+
<slot name="chips" @slotchange=${this.onChipsSlotChange}></slot>
|
|
234
|
+
</div>
|
|
235
|
+
<div part="row">
|
|
236
|
+
<span part="leading" ?hidden=${!this.hasLeadingSlot}>
|
|
237
|
+
<slot name="leading" @slotchange=${this.onLeadingSlotChange}></slot>
|
|
238
|
+
</span>
|
|
239
|
+
<textarea
|
|
240
|
+
part="textarea"
|
|
241
|
+
aria-label=${this.placeholder || 'Message'}
|
|
242
|
+
.value=${this.value}
|
|
243
|
+
placeholder=${this.placeholder}
|
|
244
|
+
rows=${Math.max(1, this.minRows || 1)}
|
|
245
|
+
?disabled=${this.effectiveDisabled}
|
|
246
|
+
@input=${this.onTextareaInput}
|
|
247
|
+
@keydown=${this.onTextareaKeyDown}
|
|
248
|
+
></textarea>
|
|
249
|
+
<span part="trailing">
|
|
250
|
+
<slot name="trailing" @slotchange=${this.onTrailingSlotChange}></slot>
|
|
251
|
+
${this.hasTrailingSlot ? nothing : this.renderActionButton()}
|
|
252
|
+
</span>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
__decorate([
|
|
259
|
+
property()
|
|
260
|
+
], LyraChatComposer.prototype, "placeholder", void 0);
|
|
261
|
+
__decorate([
|
|
262
|
+
property({ type: Number, attribute: 'min-rows' })
|
|
263
|
+
], LyraChatComposer.prototype, "minRows", void 0);
|
|
264
|
+
__decorate([
|
|
265
|
+
property({ type: Number, attribute: 'max-rows' })
|
|
266
|
+
], LyraChatComposer.prototype, "maxRows", void 0);
|
|
267
|
+
__decorate([
|
|
268
|
+
property({ reflect: true })
|
|
269
|
+
], LyraChatComposer.prototype, "status", void 0);
|
|
270
|
+
__decorate([
|
|
271
|
+
property({ type: Boolean, reflect: true, attribute: 'submit-on-enter' })
|
|
272
|
+
], LyraChatComposer.prototype, "submitOnEnter", void 0);
|
|
273
|
+
__decorate([
|
|
274
|
+
state()
|
|
275
|
+
], LyraChatComposer.prototype, "hasLeadingSlot", void 0);
|
|
276
|
+
__decorate([
|
|
277
|
+
state()
|
|
278
|
+
], LyraChatComposer.prototype, "hasChipsSlot", void 0);
|
|
279
|
+
__decorate([
|
|
280
|
+
state()
|
|
281
|
+
], LyraChatComposer.prototype, "hasTrailingSlot", void 0);
|
|
282
|
+
__decorate([
|
|
283
|
+
query('textarea')
|
|
284
|
+
], LyraChatComposer.prototype, "textareaEl", void 0);
|
|
285
|
+
defineElement('chat-composer', LyraChatComposer);
|