@aceshooting/lyra-ui 2.0.0 → 2.1.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.
Files changed (224) hide show
  1. package/README.md +87 -14
  2. package/custom-elements.json +11555 -5539
  3. package/dist/components/app-rail/app-rail-item.d.ts +30 -0
  4. package/dist/components/app-rail/app-rail-item.js +68 -0
  5. package/dist/components/app-rail/app-rail-item.styles.d.ts +1 -0
  6. package/dist/components/app-rail/app-rail-item.styles.js +65 -0
  7. package/dist/components/app-rail/app-rail.d.ts +21 -24
  8. package/dist/components/app-rail/app-rail.js +68 -132
  9. package/dist/components/app-rail/app-rail.styles.js +5 -2
  10. package/dist/components/attachment-chip/attachment-chip.d.ts +38 -11
  11. package/dist/components/attachment-chip/attachment-chip.js +73 -29
  12. package/dist/components/attachment-trigger/attachment-trigger.d.ts +13 -0
  13. package/dist/components/attachment-trigger/attachment-trigger.js +13 -1
  14. package/dist/components/attachment-trigger/attachment-trigger.styles.js +28 -6
  15. package/dist/components/chart/bar-chart.js +2 -18
  16. package/dist/components/chart/box-plot.d.ts +25 -1
  17. package/dist/components/chart/box-plot.js +136 -6
  18. package/dist/components/chart/box-plot.styles.js +13 -0
  19. package/dist/components/chart/bubble-chart.js +2 -18
  20. package/dist/components/chart/chart.d.ts +54 -10
  21. package/dist/components/chart/chart.js +182 -39
  22. package/dist/components/chart/doughnut-chart.js +2 -18
  23. package/dist/components/chart/histogram-bin.d.ts +5 -2
  24. package/dist/components/chart/histogram-bin.js +12 -4
  25. package/dist/components/chart/histogram.js +8 -16
  26. package/dist/components/chart/line-chart.js +2 -18
  27. package/dist/components/chart/lite-chart.d.ts +80 -3
  28. package/dist/components/chart/lite-chart.js +250 -53
  29. package/dist/components/chart/pie-chart.js +2 -18
  30. package/dist/components/chart/polar-area-chart.js +2 -18
  31. package/dist/components/chart/radar-chart.js +2 -18
  32. package/dist/components/chart/scatter-chart.js +2 -18
  33. package/dist/components/chat-composer/chat-composer.d.ts +42 -1
  34. package/dist/components/chat-composer/chat-composer.js +84 -3
  35. package/dist/components/chat-composer/chat-composer.styles.js +11 -1
  36. package/dist/components/chat-message/chat-message.d.ts +32 -4
  37. package/dist/components/chat-message/chat-message.js +57 -17
  38. package/dist/components/checkbox/checkbox.d.ts +66 -8
  39. package/dist/components/checkbox/checkbox.js +173 -50
  40. package/dist/components/checkbox/checkbox.styles.js +9 -2
  41. package/dist/components/chip/chip-group.js +8 -2
  42. package/dist/components/chip/chip.js +9 -2
  43. package/dist/components/citation-badge/citation-badge.js +21 -3
  44. package/dist/components/code-block/code-block.d.ts +38 -0
  45. package/dist/components/code-block/code-block.js +72 -7
  46. package/dist/components/code-block/code-loader.d.ts +44 -1
  47. package/dist/components/code-block/code-loader.js +63 -0
  48. package/dist/components/combobox/combobox.d.ts +46 -5
  49. package/dist/components/combobox/combobox.js +175 -35
  50. package/dist/components/combobox/combobox.styles.js +3 -3
  51. package/dist/components/context-meter/context-meter.js +17 -8
  52. package/dist/components/context-meter/context-meter.styles.js +0 -6
  53. package/dist/components/conversation-item/conversation-item.d.ts +13 -16
  54. package/dist/components/conversation-item/conversation-item.js +29 -20
  55. package/dist/components/date-picker/calendar-core.d.ts +24 -0
  56. package/dist/components/date-picker/calendar-core.js +51 -4
  57. package/dist/components/date-picker/date-input.d.ts +77 -10
  58. package/dist/components/date-picker/date-input.js +278 -73
  59. package/dist/components/date-picker/date-picker.d.ts +28 -4
  60. package/dist/components/date-picker/date-picker.js +95 -37
  61. package/dist/components/dialog/confirm.js +3 -6
  62. package/dist/components/dialog/dialog.d.ts +75 -31
  63. package/dist/components/dialog/dialog.js +147 -131
  64. package/dist/components/dialog/dialog.styles.js +46 -2
  65. package/dist/components/dock-panel/dock-panel.d.ts +8 -0
  66. package/dist/components/dock-panel/dock-panel.js +15 -1
  67. package/dist/components/dock-panel/dock-panel.styles.js +15 -1
  68. package/dist/components/document-preview/document-preview.d.ts +15 -7
  69. package/dist/components/document-preview/document-preview.js +29 -13
  70. package/dist/components/empty/empty.d.ts +1 -2
  71. package/dist/components/empty/empty.js +18 -12
  72. package/dist/components/empty/empty.styles.js +0 -3
  73. package/dist/components/export-button/export-button.d.ts +12 -0
  74. package/dist/components/export-button/export-button.js +33 -5
  75. package/dist/components/export-button/export-button.styles.js +5 -1
  76. package/dist/components/file-input/accept.js +2 -0
  77. package/dist/components/file-input/file-input.d.ts +10 -1
  78. package/dist/components/file-input/file-input.js +36 -5
  79. package/dist/components/flag/flag.d.ts +1 -1
  80. package/dist/components/flag/flag.js +15 -1
  81. package/dist/components/flag/flag.styles.js +1 -1
  82. package/dist/components/flag/language-map.d.ts +4 -1
  83. package/dist/components/flag/language-map.js +6 -3
  84. package/dist/components/gauge/gauge.d.ts +10 -1
  85. package/dist/components/gauge/gauge.js +28 -9
  86. package/dist/components/generation-status/generation-status.d.ts +13 -5
  87. package/dist/components/generation-status/generation-status.js +59 -5
  88. package/dist/components/graph/graph.d.ts +37 -3
  89. package/dist/components/graph/graph.js +132 -20
  90. package/dist/components/heatmap/calendar-grid.d.ts +15 -6
  91. package/dist/components/heatmap/calendar-grid.js +17 -6
  92. package/dist/components/heatmap/heatmap-scale.d.ts +5 -1
  93. package/dist/components/heatmap/heatmap-scale.js +8 -2
  94. package/dist/components/heatmap/heatmap.d.ts +166 -25
  95. package/dist/components/heatmap/heatmap.js +372 -89
  96. package/dist/components/json-viewer/json-viewer.d.ts +22 -5
  97. package/dist/components/json-viewer/json-viewer.js +115 -37
  98. package/dist/components/json-viewer/json-viewer.styles.js +2 -1
  99. package/dist/components/kbd/kbd.js +24 -7
  100. package/dist/components/live-region/live-region.d.ts +10 -1
  101. package/dist/components/live-region/live-region.js +42 -6
  102. package/dist/components/map/map.d.ts +24 -1
  103. package/dist/components/map/map.js +89 -24
  104. package/dist/components/markdown/markdown-loader.d.ts +24 -0
  105. package/dist/components/markdown/markdown-loader.js +35 -1
  106. package/dist/components/markdown/markdown.d.ts +46 -8
  107. package/dist/components/markdown/markdown.js +136 -31
  108. package/dist/components/markdown/markdown.styles.js +7 -1
  109. package/dist/components/media-card/media-card.d.ts +25 -17
  110. package/dist/components/media-card/media-card.js +43 -65
  111. package/dist/components/mention-popover/mention-popover.d.ts +4 -1
  112. package/dist/components/mention-popover/mention-popover.js +23 -5
  113. package/dist/components/menu/menu-item.d.ts +32 -3
  114. package/dist/components/menu/menu-item.js +94 -7
  115. package/dist/components/menu/menu-item.styles.js +8 -0
  116. package/dist/components/menu/menu.d.ts +25 -1
  117. package/dist/components/menu/menu.js +99 -3
  118. package/dist/components/menu/menu.styles.js +3 -2
  119. package/dist/components/model-select/model-select.d.ts +48 -3
  120. package/dist/components/model-select/model-select.js +120 -22
  121. package/dist/components/model-select/model-select.styles.js +17 -3
  122. package/dist/components/model-settings-panel/model-settings-panel.d.ts +25 -1
  123. package/dist/components/model-settings-panel/model-settings-panel.js +65 -1
  124. package/dist/components/playback/playback.d.ts +6 -2
  125. package/dist/components/playback/playback.js +72 -29
  126. package/dist/components/playback/playback.styles.js +6 -1
  127. package/dist/components/responsive-panel/responsive-panel.d.ts +29 -16
  128. package/dist/components/responsive-panel/responsive-panel.js +130 -132
  129. package/dist/components/responsive-panel/responsive-panel.styles.js +1 -1
  130. package/dist/components/result-card/result-card.d.ts +8 -2
  131. package/dist/components/result-card/result-card.js +9 -3
  132. package/dist/components/result-card/result-card.styles.js +0 -1
  133. package/dist/components/select/select.d.ts +59 -20
  134. package/dist/components/select/select.js +125 -43
  135. package/dist/components/skeleton/skeleton.d.ts +3 -0
  136. package/dist/components/skeleton/skeleton.js +7 -1
  137. package/dist/components/skeleton/skeleton.styles.js +5 -4
  138. package/dist/components/slider/slider.d.ts +57 -5
  139. package/dist/components/slider/slider.js +195 -80
  140. package/dist/components/source-card/source-card.d.ts +17 -5
  141. package/dist/components/source-card/source-card.js +55 -8
  142. package/dist/components/source-card/source-card.styles.js +1 -1
  143. package/dist/components/source-list/source-list.js +9 -1
  144. package/dist/components/sparkline/sparkline.js +34 -10
  145. package/dist/components/split/split.d.ts +141 -14
  146. package/dist/components/split/split.js +357 -52
  147. package/dist/components/split/split.styles.js +18 -1
  148. package/dist/components/stat/stat.d.ts +10 -4
  149. package/dist/components/stat/stat.js +46 -11
  150. package/dist/components/stream-status/stream-status.d.ts +27 -11
  151. package/dist/components/stream-status/stream-status.js +95 -13
  152. package/dist/components/streaming-text/streaming-text.d.ts +9 -3
  153. package/dist/components/streaming-text/streaming-text.js +25 -10
  154. package/dist/components/streaming-text/streaming-text.styles.js +3 -2
  155. package/dist/components/switch/switch.d.ts +52 -7
  156. package/dist/components/switch/switch.js +111 -40
  157. package/dist/components/switch/switch.styles.js +1 -1
  158. package/dist/components/table/table.d.ts +18 -3
  159. package/dist/components/table/table.js +88 -20
  160. package/dist/components/tabs/tabs.d.ts +23 -0
  161. package/dist/components/tabs/tabs.js +76 -12
  162. package/dist/components/tabs/tabs.styles.js +11 -0
  163. package/dist/components/thinking-panel/thinking-panel.d.ts +3 -1
  164. package/dist/components/thinking-panel/thinking-panel.js +34 -9
  165. package/dist/components/thinking-panel/thinking-panel.styles.js +6 -0
  166. package/dist/components/time-range/time-range.d.ts +59 -2
  167. package/dist/components/time-range/time-range.js +163 -67
  168. package/dist/components/toast/toast-item.d.ts +12 -3
  169. package/dist/components/toast/toast-item.js +90 -53
  170. package/dist/components/toast/toast-item.styles.js +59 -28
  171. package/dist/components/toast/toast.js +17 -5
  172. package/dist/components/toast/toaster.js +10 -1
  173. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +11 -10
  174. package/dist/components/tool-approval-dialog/tool-approval-dialog.js +50 -124
  175. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +1 -1
  176. package/dist/components/tool-call-chip/tool-call-chip.d.ts +18 -2
  177. package/dist/components/tool-call-chip/tool-call-chip.js +86 -25
  178. package/dist/components/tool-param-form/tool-param-form.d.ts +89 -28
  179. package/dist/components/tool-param-form/tool-param-form.js +350 -106
  180. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +27 -7
  181. package/dist/components/tool-result-dialog/tool-result-dialog.js +80 -115
  182. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +1 -1
  183. package/dist/components/tool-result-view/registry.d.ts +0 -8
  184. package/dist/components/tool-result-view/registry.js +7 -2
  185. package/dist/components/tool-result-view/tool-result-view.d.ts +6 -3
  186. package/dist/components/tool-result-view/tool-result-view.js +18 -4
  187. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +11 -11
  188. package/dist/components/tool-select-dialog/tool-select-dialog.js +44 -133
  189. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +13 -4
  190. package/dist/components/tree/tree-node.d.ts +4 -1
  191. package/dist/components/tree/tree-node.js +4 -1
  192. package/dist/components/tree/tree.d.ts +17 -8
  193. package/dist/components/tree/tree.js +21 -20
  194. package/dist/components/typing-indicator/typing-indicator.d.ts +12 -2
  195. package/dist/components/typing-indicator/typing-indicator.js +16 -4
  196. package/dist/components/typing-indicator/typing-indicator.styles.js +2 -2
  197. package/dist/components/virtual-list/virtual-list.d.ts +56 -19
  198. package/dist/components/virtual-list/virtual-list.js +184 -38
  199. package/dist/components/virtual-list/virtual-list.styles.js +12 -0
  200. package/dist/components/widget/widget.d.ts +5 -4
  201. package/dist/components/widget/widget.js +54 -120
  202. package/dist/components/widget/widget.styles.js +8 -2
  203. package/dist/components/word-cloud/word-cloud-layout.d.ts +7 -0
  204. package/dist/components/word-cloud/word-cloud-layout.js +20 -6
  205. package/dist/components/word-cloud/word-cloud.d.ts +22 -2
  206. package/dist/components/word-cloud/word-cloud.js +45 -6
  207. package/dist/components/word-cloud/word-cloud.styles.js +13 -1
  208. package/dist/internal/anchored-validity.d.ts +1 -0
  209. package/dist/internal/anchored-validity.js +86 -0
  210. package/dist/internal/form-associated.d.ts +7 -0
  211. package/dist/internal/form-associated.js +43 -4
  212. package/dist/internal/numbers.d.ts +12 -0
  213. package/dist/internal/numbers.js +26 -0
  214. package/dist/internal/overlay-manager.d.ts +51 -0
  215. package/dist/internal/overlay-manager.js +523 -0
  216. package/dist/internal/positioner.js +27 -3
  217. package/dist/internal/safe-url.d.ts +12 -0
  218. package/dist/internal/safe-url.js +41 -0
  219. package/dist/internal/tokens.styles.js +17 -11
  220. package/dist/lyra.d.ts +5 -2
  221. package/dist/lyra.js +3 -1
  222. package/llms-full.txt +970 -426
  223. package/llms.txt +5 -4
  224. package/package.json +19 -2
@@ -1,4 +1,4 @@
1
- import { type TemplateResult } from 'lit';
1
+ import { type PropertyValues, type TemplateResult } from 'lit';
2
2
  import { LyraElement } from '../../internal/lyra-element.js';
3
3
  import type { LyraModelCatalog } from '../model-select/model-select.js';
4
4
  import '../model-select/model-select.js';
@@ -24,6 +24,12 @@ export interface ModelSettingsChangeDetail {
24
24
  * components themselves for the exact semantics of `catalog`/`allowCustom`
25
25
  * and `temperatureMin`/`temperatureMax`/`temperatureStep`.
26
26
  *
27
+ * The panel's own `temperature` readout mirrors the slider's *live* value —
28
+ * updated on every `lyra-input` (drag/key-repeat), not just the committed
29
+ * `lyra-change` — and is re-clamped into `[temperatureMin, temperatureMax]`
30
+ * (snapped to `temperatureStep`) whenever those three properties change, so
31
+ * it can never drift from what the nested `lyra-slider` itself shows.
32
+ *
27
33
  * @customElement lyra-model-settings-panel
28
34
  * @event lyra-change - Either child control changed. `detail: { modelValue: string; inCatalog: boolean; temperature: number }` — always the full current settings, not just whatever changed.
29
35
  * @csspart base - The outermost wrapping container.
@@ -51,15 +57,33 @@ export declare class LyraModelSettingsPanel extends LyraElement {
51
57
  * the same two rows side by side with a smaller temperature caption, for
52
58
  * toolbars/sidebars where the vertical layout's height doesn't fit. */
53
59
  layout: ModelSettingsPanelLayout;
60
+ /** Disables the panel as a unit by forwarding to both the internal
61
+ * `lyra-model-select` and `lyra-slider` — a wrapping `<fieldset disabled>`
62
+ * alone would not reach either, since a form-associated control's own
63
+ * `disabled` IDL property/attribute is never mutated by fieldset cascading. */
64
+ disabled: boolean;
54
65
  /** Whether `modelValue` is present in `catalog` — recomputed fresh from
55
66
  * scratch (like `lyra-model-select`'s own `effectiveEntries`) rather than
56
67
  * cached from the last child event, so it's still correct when
57
68
  * `modelValue` was assigned directly instead of via the child's own
58
69
  * `lyra-change`. */
59
70
  private get inCatalog();
71
+ /** Clamps `raw` into `[temperatureMin, temperatureMax]`, snapped to
72
+ * `temperatureStep`'s grid anchored at the low end -- exactly mirroring
73
+ * `lyra-slider`'s own private `clampValue`/`domain` math -- so the
74
+ * mirrored `temperature` readout can never disagree with what the nested
75
+ * slider itself would clamp the same raw number to. */
76
+ private clampTemperature;
60
77
  private emitChange;
61
78
  private onModelChange;
62
79
  private onTemperatureChange;
80
+ /** Mirrors the slider's *live* (uncommitted) value into the panel's own
81
+ * `temperature` readout -- display only, no `emitChange()` -- mirroring
82
+ * native `input` (continuous) vs. `change` (committed) semantics so the
83
+ * readout tracks an in-progress drag/key-repeat instead of only updating
84
+ * once the interaction commits. */
85
+ private onTemperatureInput;
86
+ protected willUpdate(changed: PropertyValues<this>): void;
63
87
  render(): TemplateResult;
64
88
  }
65
89
  declare global {
@@ -8,6 +8,7 @@ import { html } from 'lit';
8
8
  import { property } from 'lit/decorators.js';
9
9
  import { LyraElement } from '../../internal/lyra-element.js';
10
10
  import { defineElement } from '../../internal/prefix.js';
11
+ import { finiteNumber, finiteRange } from '../../internal/numbers.js';
11
12
  import { styles } from './model-settings-panel.styles.js';
12
13
  import '../model-select/model-select.js';
13
14
  import '../slider/slider.js';
@@ -18,6 +19,15 @@ const DEFAULT_TEMPERATURE_STEP = 0.1;
18
19
  // starting point. Reassign `temperature` yourself if your provider's own
19
20
  // default differs (OpenAI and Anthropic both default to 1, which this matches).
20
21
  const DEFAULT_TEMPERATURE = 1;
22
+ /** Number of decimal digits in `n`'s shortest string representation, e.g.
23
+ * `0.1` -> 1, `5` -> 0. Mirrors lyra-slider's identical helper so the
24
+ * panel's own re-clamping of `temperature` lands on the exact same
25
+ * step-grid values the nested slider itself would compute. */
26
+ function decimalPlaces(n) {
27
+ const str = n.toString();
28
+ const dot = str.indexOf('.');
29
+ return dot === -1 ? 0 : str.length - dot - 1;
30
+ }
21
31
  /**
22
32
  * `<lyra-model-settings-panel>` — a fixed composition of `<lyra-model-select>`
23
33
  * and `<lyra-slider>` into one agent-configuration card: pick a provider's
@@ -31,6 +41,12 @@ const DEFAULT_TEMPERATURE = 1;
31
41
  * components themselves for the exact semantics of `catalog`/`allowCustom`
32
42
  * and `temperatureMin`/`temperatureMax`/`temperatureStep`.
33
43
  *
44
+ * The panel's own `temperature` readout mirrors the slider's *live* value —
45
+ * updated on every `lyra-input` (drag/key-repeat), not just the committed
46
+ * `lyra-change` — and is re-clamped into `[temperatureMin, temperatureMax]`
47
+ * (snapped to `temperatureStep`) whenever those three properties change, so
48
+ * it can never drift from what the nested `lyra-slider` itself shows.
49
+ *
34
50
  * @customElement lyra-model-settings-panel
35
51
  * @event lyra-change - Either child control changed. `detail: { modelValue: string; inCatalog: boolean; temperature: number }` — always the full current settings, not just whatever changed.
36
52
  * @csspart base - The outermost wrapping container.
@@ -57,6 +73,11 @@ export class LyraModelSettingsPanel extends LyraElement {
57
73
  * the same two rows side by side with a smaller temperature caption, for
58
74
  * toolbars/sidebars where the vertical layout's height doesn't fit. */
59
75
  this.layout = 'vertical';
76
+ /** Disables the panel as a unit by forwarding to both the internal
77
+ * `lyra-model-select` and `lyra-slider` — a wrapping `<fieldset disabled>`
78
+ * alone would not reach either, since a form-associated control's own
79
+ * `disabled` IDL property/attribute is never mutated by fieldset cascading. */
80
+ this.disabled = false;
60
81
  this.onModelChange = (e) => {
61
82
  this.modelValue = e.detail.value;
62
83
  this.emitChange();
@@ -65,6 +86,14 @@ export class LyraModelSettingsPanel extends LyraElement {
65
86
  this.temperature = e.detail.value;
66
87
  this.emitChange();
67
88
  };
89
+ /** Mirrors the slider's *live* (uncommitted) value into the panel's own
90
+ * `temperature` readout -- display only, no `emitChange()` -- mirroring
91
+ * native `input` (continuous) vs. `change` (committed) semantics so the
92
+ * readout tracks an in-progress drag/key-repeat instead of only updating
93
+ * once the interaction commits. */
94
+ this.onTemperatureInput = (e) => {
95
+ this.temperature = e.detail.value;
96
+ };
68
97
  }
69
98
  static { this.styles = [LyraElement.styles, styles]; }
70
99
  /** Whether `modelValue` is present in `catalog` — recomputed fresh from
@@ -78,6 +107,28 @@ export class LyraModelSettingsPanel extends LyraElement {
78
107
  return false;
79
108
  return catalog.some((entry) => (typeof entry === 'string' ? entry === this.modelValue : entry.id === this.modelValue));
80
109
  }
110
+ /** Clamps `raw` into `[temperatureMin, temperatureMax]`, snapped to
111
+ * `temperatureStep`'s grid anchored at the low end -- exactly mirroring
112
+ * `lyra-slider`'s own private `clampValue`/`domain` math -- so the
113
+ * mirrored `temperature` readout can never disagree with what the nested
114
+ * slider itself would clamp the same raw number to. */
115
+ clampTemperature(raw) {
116
+ const rawMin = this.temperatureMin;
117
+ const rawMax = this.temperatureMax;
118
+ const min = finiteNumber(rawMin, DEFAULT_TEMPERATURE_MIN);
119
+ const max = finiteNumber(rawMax, DEFAULT_TEMPERATURE_MAX);
120
+ const lo = Math.min(min, max);
121
+ const hi = Math.max(min, max);
122
+ const step = finiteRange(this.temperatureStep, 0, 0);
123
+ const hasStep = step > 0;
124
+ let stepped = finiteNumber(raw, lo);
125
+ if (hasStep) {
126
+ const stepsFromLo = Math.round((raw - lo) / step);
127
+ const factor = 10 ** decimalPlaces(step);
128
+ stepped = Math.round((lo + stepsFromLo * step) * factor) / factor;
129
+ }
130
+ return Math.min(hi, Math.max(lo, stepped));
131
+ }
81
132
  emitChange() {
82
133
  this.emit('lyra-change', {
83
134
  modelValue: this.modelValue,
@@ -85,6 +136,13 @@ export class LyraModelSettingsPanel extends LyraElement {
85
136
  temperature: this.temperature,
86
137
  });
87
138
  }
139
+ willUpdate(changed) {
140
+ if (changed.has('temperatureMin') || changed.has('temperatureMax') || changed.has('temperatureStep')) {
141
+ const clamped = this.clampTemperature(this.temperature);
142
+ if (clamped !== this.temperature)
143
+ this.temperature = clamped;
144
+ }
145
+ }
88
146
  render() {
89
147
  return html `
90
148
  <div part="base">
@@ -94,6 +152,7 @@ export class LyraModelSettingsPanel extends LyraElement {
94
152
  .catalog=${this.catalog}
95
153
  .value=${this.modelValue}
96
154
  .allowCustom=${this.allowCustom}
155
+ .disabled=${this.disabled}
97
156
  placeholder="Select a model…"
98
157
  @lyra-change=${this.onModelChange}
99
158
  ></lyra-model-select>
@@ -107,9 +166,11 @@ export class LyraModelSettingsPanel extends LyraElement {
107
166
  .step=${this.temperatureStep}
108
167
  .valueAsNumber=${this.temperature}
109
168
  .showValue=${false}
169
+ .disabled=${this.disabled}
170
+ @lyra-input=${this.onTemperatureInput}
110
171
  @lyra-change=${this.onTemperatureChange}
111
172
  ></lyra-slider>
112
- <span part="temperature-value">${this.temperature}</span>
173
+ <span part="temperature-value" aria-hidden="true">${this.temperature}</span>
113
174
  </div>
114
175
  </div>
115
176
  `;
@@ -142,4 +203,7 @@ __decorate([
142
203
  __decorate([
143
204
  property({ reflect: true })
144
205
  ], LyraModelSettingsPanel.prototype, "layout", void 0);
206
+ __decorate([
207
+ property({ type: Boolean, reflect: true })
208
+ ], LyraModelSettingsPanel.prototype, "disabled", void 0);
145
209
  defineElement('model-settings-panel', LyraModelSettingsPanel);
@@ -9,7 +9,9 @@ import { LyraElement } from '../../internal/lyra-element.js';
9
9
  * @event lyra-play - Fired when playback starts.
10
10
  * @event lyra-pause - Fired when playback stops (including auto-pause).
11
11
  * @event lyra-step - `detail: { index }`, fired on every tick and manual step.
12
- * @csspart base, play-button, slider
12
+ * @csspart base - The playback controls wrapper.
13
+ * @csspart play-button - The play/pause button.
14
+ * @csspart slider - The playback position slider.
13
15
  */
14
16
  export declare class LyraPlayback extends LyraElement {
15
17
  static styles: import("lit").CSSResultGroup[];
@@ -39,7 +41,9 @@ export declare class LyraPlayback extends LyraElement {
39
41
  private _playing;
40
42
  get playing(): boolean;
41
43
  set playing(next: boolean);
42
- /** Largest index reachable at the current `length` (never negative). */
44
+ /** Largest index reachable at the current `length` (never negative, and
45
+ * never NaN — a non-finite `length` falls back to 0 rather than leaking a
46
+ * literal "NaN" into the rendered slider's `max` attribute). */
43
47
  private get maxIndex();
44
48
  disconnectedCallback(): void;
45
49
  protected willUpdate(changed: PropertyValues): void;
@@ -9,14 +9,27 @@ import { property } from 'lit/decorators.js';
9
9
  import { LyraElement } from '../../internal/lyra-element.js';
10
10
  import { defineElement } from '../../internal/prefix.js';
11
11
  import { playIcon, pauseIcon } from '../../internal/icons.js';
12
+ import { finiteCount, finiteDuration, MAX_TIMEOUT_MS } from '../../internal/numbers.js';
12
13
  import { styles } from './playback.styles.js';
13
14
  const MIN_INTERVAL_MS = 16; // ~one animation frame; prevents a near-zero-delay tick loop
14
- let warnedInvalidInterval = false;
15
+ // Deduplicated per distinct bad value (like the analogous one-time warnings
16
+ // in lyra-heatmap/lyra-word-cloud) rather than a single once-ever flag, so a
17
+ // later, genuinely different bad value is never silently swallowed by an
18
+ // earlier, unrelated one.
19
+ const warnedInvalidIntervals = new Set();
15
20
  function warnInvalidInterval(value) {
16
- if (warnedInvalidInterval)
21
+ if (warnedInvalidIntervals.has(value))
17
22
  return;
18
- warnedInvalidInterval = true;
19
- console.warn(`<lyra-playback> received a non-finite or non-positive interval-ms (${value}); clamping to ${MIN_INTERVAL_MS}ms.`);
23
+ warnedInvalidIntervals.add(value);
24
+ // The clamp fires for any value outside the timer-safe range, so report the
25
+ // reason that actually applies instead of collapsing every bad value into
26
+ // one misleading "non-finite or non-positive" message.
27
+ const reason = !Number.isFinite(value)
28
+ ? 'non-finite'
29
+ : value < MIN_INTERVAL_MS
30
+ ? `below the ${MIN_INTERVAL_MS}ms floor`
31
+ : `above the ${MAX_TIMEOUT_MS}ms ceiling`;
32
+ console.warn(`<lyra-playback> interval-ms (${value}) is ${reason}; clamping to ${MIN_INTERVAL_MS}ms.`);
20
33
  }
21
34
  /**
22
35
  * `<lyra-playback>` — steps an index through `[0, length)` on a fixed
@@ -27,7 +40,9 @@ function warnInvalidInterval(value) {
27
40
  * @event lyra-play - Fired when playback starts.
28
41
  * @event lyra-pause - Fired when playback stops (including auto-pause).
29
42
  * @event lyra-step - `detail: { index }`, fired on every tick and manual step.
30
- * @csspart base, play-button, slider
43
+ * @csspart base - The playback controls wrapper.
44
+ * @csspart play-button - The play/pause button.
45
+ * @csspart slider - The playback position slider.
31
46
  */
32
47
  export class LyraPlayback extends LyraElement {
33
48
  constructor() {
@@ -64,7 +79,7 @@ export class LyraPlayback extends LyraElement {
64
79
  return;
65
80
  this._playing = next;
66
81
  if (next) {
67
- if (this.length <= 1) {
82
+ if (finiteCount(this.length) <= 1) {
68
83
  this._playing = false;
69
84
  return;
70
85
  }
@@ -78,9 +93,11 @@ export class LyraPlayback extends LyraElement {
78
93
  }
79
94
  this.requestUpdate('playing', old);
80
95
  }
81
- /** Largest index reachable at the current `length` (never negative). */
96
+ /** Largest index reachable at the current `length` (never negative, and
97
+ * never NaN — a non-finite `length` falls back to 0 rather than leaking a
98
+ * literal "NaN" into the rendered slider's `max` attribute). */
82
99
  get maxIndex() {
83
- return Math.max(0, this.length - 1);
100
+ return Math.max(0, finiteCount(this.length) - 1);
84
101
  }
85
102
  disconnectedCallback() {
86
103
  super.disconnectedCallback();
@@ -89,6 +106,19 @@ export class LyraPlayback extends LyraElement {
89
106
  willUpdate(changed) {
90
107
  if (changed.has('hidden') && this.hidden)
91
108
  this.pause();
109
+ // A non-finite `length`/`index` (e.g. externally assigned NaN) must not
110
+ // linger: `maxIndex` already falls back to a safe value for rendering,
111
+ // but `next()`/`previous()` compare `index`/`length` directly and would
112
+ // otherwise stay permanently bricked (NaN comparisons are always
113
+ // false). Checked unconditionally — not just under the `length` branch
114
+ // below — so a corrupted `index` alone still self-heals on the very next
115
+ // update, without requiring `length` to also change.
116
+ const length = finiteCount(this.length);
117
+ const index = finiteCount(this.index);
118
+ if (length !== this.length)
119
+ this.length = length;
120
+ if (index !== this.index)
121
+ this.index = index;
92
122
  if (changed.has('length')) {
93
123
  // If length is externally reduced to <= 1 while playing, the timer
94
124
  // would otherwise keep firing forever — and the play button (the only
@@ -104,7 +134,7 @@ export class LyraPlayback extends LyraElement {
104
134
  }
105
135
  /** Start playback; no-op if there's nothing to advance through. */
106
136
  play() {
107
- if (this.playing || this.length <= 1)
137
+ if (this.playing || finiteCount(this.length) <= 1)
108
138
  return;
109
139
  this.playing = true;
110
140
  }
@@ -124,24 +154,32 @@ export class LyraPlayback extends LyraElement {
124
154
  // `interval-ms` live takes effect on the very next step instead of only
125
155
  // after a pause/play cycle.
126
156
  scheduleTick() {
127
- let delay = this.intervalMs;
128
- if (!Number.isFinite(delay) || delay < MIN_INTERVAL_MS) {
129
- warnInvalidInterval(delay);
130
- delay = MIN_INTERVAL_MS;
131
- }
132
- this.timer = window.setTimeout(() => {
157
+ const rawDelay = this.intervalMs;
158
+ const delay = finiteDuration(rawDelay, MIN_INTERVAL_MS, MIN_INTERVAL_MS);
159
+ if (delay !== rawDelay)
160
+ warnInvalidInterval(rawDelay);
161
+ // Self-identifying: a synchronous pause()+play() (or a synchronous
162
+ // play() from a 'lyra-pause'/'lyra-step' listener) invoked while this
163
+ // callback is running schedules its own new timer and overwrites
164
+ // `this.timer` before this callback resumes. Only the timer that is
165
+ // still the current one is allowed to reschedule itself — every other,
166
+ // now-superseded chain quietly stops instead of spawning a second,
167
+ // untracked chain that pause()/disconnectedCallback() can never reach.
168
+ const id = window.setTimeout(() => {
133
169
  this.tick();
134
- if (this.playing)
170
+ if (this.playing && this.timer === id)
135
171
  this.scheduleTick();
136
172
  }, delay);
173
+ this.timer = id;
137
174
  }
138
175
  tick() {
139
- const next = this.index + 1;
140
- if (next >= this.length) {
176
+ const length = finiteCount(this.length);
177
+ const next = finiteCount(this.index) + 1;
178
+ if (next >= length) {
141
179
  if (this.loop)
142
180
  this.setIndex(0);
143
181
  else {
144
- this.setIndex(this.length - 1);
182
+ this.setIndex(length - 1);
145
183
  this.pause();
146
184
  }
147
185
  }
@@ -150,32 +188,37 @@ export class LyraPlayback extends LyraElement {
150
188
  }
151
189
  }
152
190
  setIndex(i) {
153
- this.index = i;
191
+ this.index = finiteCount(i, 0, this.maxIndex);
154
192
  this.emit('lyra-step', { index: this.index });
155
193
  }
156
194
  /** Advance one step without starting playback. */
157
195
  next() {
158
- if (this.index + 1 < this.length)
159
- this.setIndex(this.index + 1);
196
+ const index = finiteCount(this.index);
197
+ if (index + 1 < finiteCount(this.length))
198
+ this.setIndex(index + 1);
160
199
  }
161
200
  /** Go back one step without starting playback. */
162
201
  previous() {
163
- if (this.index > 0)
164
- this.setIndex(this.index - 1);
202
+ const index = finiteCount(this.index);
203
+ if (index > 0)
204
+ this.setIndex(index - 1);
165
205
  }
166
206
  /** Jump to an explicit index, pausing playback. */
167
207
  goTo(index) {
168
208
  this.pause();
169
- this.setIndex(Math.min(this.maxIndex, Math.max(0, index)));
209
+ this.setIndex(finiteCount(index, 0, this.maxIndex));
170
210
  }
171
211
  render() {
212
+ const maxIndex = this.maxIndex;
213
+ const index = finiteCount(this.index, 0, maxIndex);
214
+ const disabled = finiteCount(this.length) <= 1;
172
215
  return html `
173
216
  <div part="base">
174
217
  <button
175
218
  part="play-button"
176
219
  type="button"
177
220
  aria-label=${this.playing ? 'Pause' : 'Play'}
178
- ?disabled=${this.length <= 1}
221
+ ?disabled=${disabled}
179
222
  @click=${() => this.toggle()}
180
223
  >
181
224
  ${this.playing ? pauseIcon() : playIcon()}
@@ -184,10 +227,10 @@ export class LyraPlayback extends LyraElement {
184
227
  part="slider"
185
228
  type="range"
186
229
  min="0"
187
- max=${this.maxIndex}
188
- .value=${String(this.index)}
230
+ max=${maxIndex}
231
+ .value=${String(index)}
189
232
  aria-label="Playback position"
190
- ?disabled=${this.length <= 1}
233
+ ?disabled=${disabled}
191
234
  @input=${(e) => this.goTo(Number(e.target.value))}
192
235
  />
193
236
  </div>
@@ -4,6 +4,11 @@ export const styles = css `
4
4
  display: inline-flex;
5
5
  align-items: center;
6
6
  gap: var(--lyra-space-s);
7
+ /* Derived from --lyra-icon-button-size (0.35 * 2.5rem = 0.875rem, the
8
+ prior bare literal) so the rendered play/pause SVG icon (icons render
9
+ at width/height: 1em, so this font-size directly controls its pixel
10
+ size) tracks the button's own token instead of drifting from it. */
11
+ --lyra-playback-icon-size: calc(var(--lyra-icon-button-size) * 0.35);
7
12
  }
8
13
  [part='base'] {
9
14
  display: flex;
@@ -21,7 +26,7 @@ export const styles = css `
21
26
  display: inline-flex;
22
27
  align-items: center;
23
28
  justify-content: center;
24
- font-size: 0.875rem;
29
+ font-size: var(--lyra-playback-icon-size);
25
30
  }
26
31
  [part='play-button']:hover {
27
32
  border-color: var(--lyra-color-brand);
@@ -43,18 +43,26 @@ export declare function resolveEffectiveMode(mode: ResponsivePanelMode, belowBre
43
43
  * attributes and backdrop element, differ), so lit-html's diffing keeps
44
44
  * `[part="body"]` and its `<slot>` as the same DOM node across the
45
45
  * transition -- scroll position and focus inside the slotted content survive
46
- * it for free, with no extra bookkeeping required. The one exception is
47
- * focus: crossing the breakpoint while already open does *not* forcibly
48
- * move focus into the panel (unlike an `open` transition, which does) --
49
- * doing so on a resize could yank focus away from an input the user is
50
- * actively typing into inside the now-newly-modal panel, which is worse UX
51
- * than a modal that's very briefly missing its initial focus placement.
46
+ * it for free. When an open inline panel becomes modal, focus already inside
47
+ * is preserved and outside focus moves to the first available target. Closing
48
+ * still returns to the opener captured by the original inline open.
52
49
  *
53
- * The overlay presentation duplicates lyra-dialog's `role="dialog"` +
54
- * focus-trap + Escape/backdrop-dismiss + scroll-lock mechanics locally
55
- * rather than nesting a `<lyra-dialog>` inside this component's shadow DOM,
56
- * per the established precedent for every overlay-shaped component in this
57
- * family (see lyra-dialog's own module doc).
50
+ * The overlay presentation uses the library's shared overlay coordinator for
51
+ * focus trapping, Escape/backdrop dismissal, inerting, and stack ordering,
52
+ * while retaining this component's own responsive rendering and close event.
53
+ *
54
+ * Accessible name (overlay presentation only -- the inline presentation has
55
+ * no dialog semantics to name): `label`, when set, is used verbatim. When
56
+ * `label` is empty, this falls back to the `header` slot's content -- a
57
+ * heading element (`h1`–`h6` or `[role="heading"]`) among the slotted header
58
+ * content wins if present, otherwise the header slot's full text content is
59
+ * used, mirroring lyra-dialog's `detectHeading()`/`headingText` fallback (see
60
+ * dialog.ts's module doc for why this uses `aria-label`, a copied string,
61
+ * rather than `aria-labelledby`: the header content is light DOM while
62
+ * `[part="panel"]` lives in this element's shadow tree, and an ID-reference
63
+ * attribute can't resolve across that boundary). A panel opened without
64
+ * either `label` or header content still renders `role="dialog"` with no
65
+ * accessible name -- set one of the two to avoid that.
58
66
  *
59
67
  * @customElement lyra-responsive-panel
60
68
  * @slot - The panel body.
@@ -95,7 +103,8 @@ export declare class LyraResponsivePanel extends LyraElement {
95
103
  * height. */
96
104
  variant: ResponsivePanelVariant;
97
105
  /** Accessible name for the overlay presentation's `role="dialog"`. Unused in the inline
98
- * presentation, which has no dialog semantics to name. */
106
+ * presentation, which has no dialog semantics to name. When empty, falls back to the `header`
107
+ * slot's content -- see the class doc for the full fallback order. */
99
108
  label: string;
100
109
  /** CSS length passed to `matchMedia` as `(max-width: <this>)` to decide, in `mode="auto"`,
101
110
  * whether the effective presentation is `'overlay'` (below/at this width) or `'inline'`
@@ -105,19 +114,25 @@ export declare class LyraResponsivePanel extends LyraElement {
105
114
  private effectiveMode;
106
115
  private hasHeaderSlot;
107
116
  private hasFooterSlot;
117
+ /** Fallback accessible name sourced from the `header` slot's content -- see `detectHeadingText()`. */
118
+ private headingText?;
108
119
  private mediaQuery?;
109
120
  private releaseScrollLock?;
110
121
  private lastTrigger?;
111
- /** Whether the overlay-only mechanics (scroll-lock, document Escape/Tab trap) are currently
122
+ private overlayHandle?;
123
+ /** Whether the overlay-only mechanics (scroll-lock and shared overlay registration) are currently
112
124
  * engaged -- derived from `effectiveMode === 'overlay' && open`, tracked separately from those
113
125
  * two properties so willUpdate can diff "was engaged" vs "should be engaged now" regardless of
114
126
  * which of the two changed. */
115
127
  private overlayChromeActive;
128
+ private focusOverlayAfterUpdate;
116
129
  private isFirstUpdate;
117
130
  protected willUpdate(changed: PropertyValues): void;
118
131
  protected updated(changed: PropertyValues): void;
119
132
  connectedCallback(): void;
120
133
  disconnectedCallback(): void;
134
+ private activateOverlayChrome;
135
+ private deactivateOverlayChrome;
121
136
  private setupMediaQuery;
122
137
  private onMediaChange;
123
138
  /** The breakpoint-response logic itself, kept callable independently of the
@@ -126,6 +141,7 @@ export declare class LyraResponsivePanel extends LyraElement {
126
141
  private handleBreakpointChange;
127
142
  private onHeaderSlotChange;
128
143
  private onFooterSlotChange;
144
+ private detectHeadingText;
129
145
  /**
130
146
  * Close the panel. `reason` is forwarded as the `lyra-close` detail --
131
147
  * built-in overlay triggers pass `'escape'`/`'backdrop'`; a consumer's own
@@ -138,9 +154,6 @@ export declare class LyraResponsivePanel extends LyraElement {
138
154
  */
139
155
  close(reason?: ResponsivePanelCloseReason): void;
140
156
  private onBackdropClick;
141
- private onDocKeyDown;
142
- private getFocusableElements;
143
- private getActiveElement;
144
157
  render(): TemplateResult;
145
158
  }
146
159
  declare global {