@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
@@ -4,8 +4,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { html } from 'lit';
7
+ import { html, nothing } from 'lit';
8
8
  import { property, state } from 'lit/decorators.js';
9
+ import { repeat } from 'lit/directives/repeat.js';
9
10
  import { LyraElement } from '../../internal/lyra-element.js';
10
11
  import { defineElement } from '../../internal/prefix.js';
11
12
  import { isRtl } from '../../internal/rtl.js';
@@ -18,6 +19,22 @@ import { styles } from './tabs.styles.js';
18
19
  * `slot` name found among the current children — a child with no `label`,
19
20
  * or a name with no matching child, simply never produces a tab.
20
21
  *
22
+ * A tab button's *visible* content can carry a leading icon without ever
23
+ * changing its *accessible name* (which always stays exactly `label`'s
24
+ * text, nothing else): give a tab an extra direct-child sibling of
25
+ * `<lyra-tabs>` carrying `slot="<id>-icon"` (that sibling's own content --
26
+ * an inline SVG, an emoji span, a custom icon element, anything -- is
27
+ * entirely up to the consumer). It's rendered ahead of the label inside
28
+ * that tab's button, wrapped in an `aria-hidden="true"` part so it's
29
+ * excluded from accessible-name computation no matter what it contains. A
30
+ * tab with no matching `<id>-icon` sibling renders no icon wrapper at all,
31
+ * so existing text-only tabs are completely unaffected. (A named slot,
32
+ * rather than a second attribute holding an icon-name lookup, was chosen
33
+ * because this library's `internal/icons.ts` is a small closed set of
34
+ * chrome glyphs for this library's *own* components, not a public
35
+ * name-keyed registry -- a slot lets a consumer supply an arbitrary,
36
+ * domain-specific icon instead of being limited to that internal set.)
37
+ *
21
38
  * Implements the WAI-ARIA APG tabs pattern with automatic activation:
22
39
  * Left/Right (swapped under RTL) move focus *and* selection together,
23
40
  * Home/End jump to the first/last enabled tab, and a roving `tabindex`
@@ -25,10 +42,12 @@ import { styles } from './tabs.styles.js';
25
42
  *
26
43
  * @customElement lyra-tabs
27
44
  * @slot - Direct children with `slot="<id>" label="<text>"` (and optionally `disabled`); one becomes each tab's panel.
45
+ * @slot <id>-icon - Optional sibling direct child supplying a tab's leading icon content; excluded from the tab button's accessible name.
28
46
  * @event lyra-tabs-change - `detail: { tabId }`, fired when the active tab changes via click or keyboard.
29
47
  * @csspart base - The root wrapper around the tablist and panels.
30
48
  * @csspart tablist - The `role="tablist"` row of tab buttons.
31
49
  * @csspart tab - A single tab button.
50
+ * @csspart tab-icon - The optional leading-icon wrapper inside a tab button; only rendered when that tab has a matching `<id>-icon` sibling.
32
51
  * @csspart panel - A single `role="tabpanel"` wrapper (one per tab, hidden unless active).
33
52
  */
34
53
  export class LyraTabs extends LyraElement {
@@ -38,17 +57,27 @@ export class LyraTabs extends LyraElement {
38
57
  this.active = '';
39
58
  this.tabs = [];
40
59
  this.baseId = nextId('tabs');
60
+ this.nextOpaqueId = 0;
61
+ this.idsBySlot = new Map();
41
62
  /** Rebuilds `tabs` from the current direct children. First child wins when two share a `slot` name (matches native slot assignment: both would render into the one panel, but only one label can back the button). */
42
63
  this.syncTabs = () => {
43
64
  const seen = new Set();
44
65
  const next = [];
45
- for (const child of Array.from(this.children)) {
66
+ const children = Array.from(this.children);
67
+ for (const child of children) {
46
68
  const slotName = child.getAttribute('slot');
47
69
  const label = child.getAttribute('label');
48
70
  if (!slotName || !label || seen.has(slotName))
49
71
  continue;
50
72
  seen.add(slotName);
51
- next.push({ slotName, label, disabled: child.hasAttribute('disabled') });
73
+ const iconSlot = this.iconSlotName(slotName);
74
+ const hasIcon = children.some((c) => c.getAttribute('slot') === iconSlot);
75
+ next.push({ slotName, label, disabled: child.hasAttribute('disabled'), hasIcon });
76
+ }
77
+ const liveSlots = new Set(next.map((tab) => tab.slotName));
78
+ for (const slotName of this.idsBySlot.keys()) {
79
+ if (!liveSlots.has(slotName))
80
+ this.idsBySlot.delete(slotName);
52
81
  }
53
82
  this.tabs = next;
54
83
  };
@@ -95,10 +124,24 @@ export class LyraTabs extends LyraElement {
95
124
  // until *this* component has already rendered one for it, and neither
96
125
  // `slotchange` nor any Lit lifecycle hook observes a plain attribute
97
126
  // edit (label/disabled) on a light-DOM child at all. A mutation observer
98
- // on the host is the only thing that sees either case.
99
- this.mutationObserver = new MutationObserver(this.syncTabs);
127
+ // on the host is the only thing that sees either case. `attributes: true`
128
+ // alone only reports mutations on the observed node itself (`this`), never
129
+ // on its children, so `subtree: true` is required too -- but that widens
130
+ // `childList`/`attributeFilter` to the *entire* descendant tree, including
131
+ // each panel's own projected content. A panel can legitimately churn its
132
+ // own children/attributes fast (a streaming log, a live JSON preview), so
133
+ // every record is filtered down to direct-child mutations only before
134
+ // triggering a resync, keeping panel-internal churn from forcing a tabs
135
+ // recompute and re-render on every unrelated mutation.
136
+ this.mutationObserver = new MutationObserver((records) => {
137
+ const isDirectChild = (node) => node.parentNode === this;
138
+ const relevant = records.some((r) => (r.type === 'childList' ? r.target === this : isDirectChild(r.target)));
139
+ if (relevant)
140
+ this.syncTabs();
141
+ });
100
142
  this.mutationObserver.observe(this, {
101
143
  childList: true,
144
+ subtree: true,
102
145
  attributes: true,
103
146
  attributeFilter: ['slot', 'label', 'disabled'],
104
147
  });
@@ -135,10 +178,23 @@ export class LyraTabs extends LyraElement {
135
178
  }
136
179
  }
137
180
  tabId(slotName) {
138
- return `${this.baseId}-tab-${slotName}`;
181
+ return this.idsFor(slotName).tab;
139
182
  }
140
183
  panelId(slotName) {
141
- return `${this.baseId}-panel-${slotName}`;
184
+ return this.idsFor(slotName).panel;
185
+ }
186
+ idsFor(slotName) {
187
+ const existing = this.idsBySlot.get(slotName);
188
+ if (existing)
189
+ return existing;
190
+ const token = `${this.baseId}-${this.nextOpaqueId++}`;
191
+ const ids = { tab: `${token}-tab`, panel: `${token}-panel` };
192
+ this.idsBySlot.set(slotName, ids);
193
+ return ids;
194
+ }
195
+ /** Derives a tab's optional icon-sibling `slot` name from its own `slotName` -- see the class doc. */
196
+ iconSlotName(slotName) {
197
+ return `${slotName}-icon`;
142
198
  }
143
199
  renderTab(tab) {
144
200
  const selected = tab.slotName === this.active;
@@ -153,9 +209,9 @@ export class LyraTabs extends LyraElement {
153
209
  aria-controls=${this.panelId(tab.slotName)}
154
210
  tabindex=${selected ? '0' : '-1'}
155
211
  @click=${() => this.selectTab(tab)}
156
- >
157
- ${tab.label}
158
- </button>`;
212
+ >${tab.hasIcon
213
+ ? html `<span part="tab-icon" aria-hidden="true"><slot name=${this.iconSlotName(tab.slotName)}></slot></span>`
214
+ : nothing}${tab.label}</button>`;
159
215
  }
160
216
  renderPanel(tab) {
161
217
  const selected = tab.slotName === this.active;
@@ -171,12 +227,20 @@ export class LyraTabs extends LyraElement {
171
227
  </div>`;
172
228
  }
173
229
  render() {
230
+ // Keyed by `slotName`, not a plain `.map()`: a plain array binding reuses
231
+ // each rendered DOM node by *position*, so removing e.g. the first tab
232
+ // would silently relabel the still-focused second button's DOM node into
233
+ // the (unfocused) third tab's button, leaving real keyboard focus stuck
234
+ // on a tabindex="-1"/aria-selected="false" button instead of following
235
+ // the tab it used to represent. Keying by identity keeps each tab's own
236
+ // DOM node (and any focus on it) attached to that same tab across
237
+ // additions/removals anywhere in the list.
174
238
  return html `
175
239
  <div part="base">
176
240
  <div part="tablist" role="tablist" aria-orientation="horizontal" @keydown=${this.onTabListKeyDown}>
177
- ${this.tabs.map((tab) => this.renderTab(tab))}
241
+ ${repeat(this.tabs, (tab) => tab.slotName, (tab) => this.renderTab(tab))}
178
242
  </div>
179
- ${this.tabs.map((tab) => this.renderPanel(tab))}
243
+ ${repeat(this.tabs, (tab) => tab.slotName, (tab) => this.renderPanel(tab))}
180
244
  </div>
181
245
  `;
182
246
  }
@@ -29,10 +29,21 @@ export const styles = css `
29
29
  color: var(--lyra-color-text-quiet);
30
30
  cursor: pointer;
31
31
  white-space: nowrap;
32
+ /* inline-flex only matters once a tab-icon part is also present (gap
33
+ has no effect with a single child) -- a text-only tab lays out
34
+ identically to the previous plain inline-block button. */
35
+ display: inline-flex;
36
+ align-items: center;
37
+ gap: var(--lyra-space-xs);
32
38
  transition:
33
39
  color var(--lyra-transition-fast),
34
40
  border-color var(--lyra-transition-fast);
35
41
  }
42
+ [part='tab-icon'] {
43
+ display: inline-flex;
44
+ flex: 0 0 auto;
45
+ align-items: center;
46
+ }
36
47
  [part='tab']:hover:not([aria-disabled='true']) {
37
48
  color: var(--lyra-color-text);
38
49
  }
@@ -77,7 +77,9 @@ export interface ThinkingPanelToggleDetail {
77
77
  * @csspart label - The `label` text.
78
78
  * @csspart duration - The formatted duration / "Thinking…" placeholder, when shown.
79
79
  * @csspart toggle - The chevron indicator inside the header.
80
- * @csspart body - The wrapper around the default slot, `hidden` while collapsed.
80
+ * @csspart body - The wrapper around the default slot, `hidden` while
81
+ * collapsed. Independently keyboard-focusable (`tabindex="0"`, `role="group"`
82
+ * named from `label`) since it's its own capped-height scrollable region.
81
83
  */
82
84
  export declare class LyraThinkingPanel extends LyraElement {
83
85
  static styles: import("lit").CSSResultGroup[];
@@ -102,7 +102,9 @@ function formatDuration(ms) {
102
102
  * @csspart label - The `label` text.
103
103
  * @csspart duration - The formatted duration / "Thinking…" placeholder, when shown.
104
104
  * @csspart toggle - The chevron indicator inside the header.
105
- * @csspart body - The wrapper around the default slot, `hidden` while collapsed.
105
+ * @csspart body - The wrapper around the default slot, `hidden` while
106
+ * collapsed. Independently keyboard-focusable (`tabindex="0"`, `role="group"`
107
+ * named from `label`) since it's its own capped-height scrollable region.
106
108
  */
107
109
  export class LyraThinkingPanel extends LyraElement {
108
110
  constructor() {
@@ -139,7 +141,13 @@ export class LyraThinkingPanel extends LyraElement {
139
141
  return;
140
142
  this.scrollRafId = requestAnimationFrame(() => {
141
143
  this.scrollRafId = undefined;
142
- this.scrollToBottom();
144
+ // Re-check the same guard here, not just in the caller: mode/expanded/
145
+ // stickToBottom can all change in the ~1-frame window between this
146
+ // mutation being observed and this callback actually firing (e.g. the
147
+ // reader scrolling away mid-frame), and this is the only place that
148
+ // reflects state as of the moment the scroll would actually happen.
149
+ if (this.mode === 'live' && this.expanded && this.stickToBottom)
150
+ this.scrollToBottom();
143
151
  });
144
152
  };
145
153
  this.toggle = () => {
@@ -167,12 +175,12 @@ export class LyraThinkingPanel extends LyraElement {
167
175
  }
168
176
  }
169
177
  updated(changed) {
170
- if (changed.has('expanded') && this.expanded && this.mode === 'live') {
171
- // Opening a live panel always jumps straight to the newest content and
172
- // resets the sticky-bottom flag, so it starts following again
173
- // regardless of wherever the body happened to be scrolled the last
174
- // time it was collapsed. `'post-hoc'` deliberately skips this -- see
175
- // the class doc's Auto-scroll section.
178
+ if ((changed.has('expanded') || changed.has('mode')) && this.expanded && this.mode === 'live') {
179
+ // Opening a live panel -- or a still-expanded panel later becoming
180
+ // `'live'` -- always jumps straight to the newest content and resets
181
+ // the sticky-bottom flag, so it starts following again regardless of
182
+ // wherever the body happened to be scrolled beforehand. `'post-hoc'`
183
+ // deliberately skips this -- see the class doc's Auto-scroll section.
176
184
  this.stickToBottom = true;
177
185
  this.scrollToBottom();
178
186
  }
@@ -194,6 +202,15 @@ export class LyraThinkingPanel extends LyraElement {
194
202
  }
195
203
  return this.mode === 'live' ? { text: 'Thinking…', pending: true } : null;
196
204
  }
205
+ // `[part="body"]` is `tabindex="0"` because it's a capped-height,
206
+ // independently-scrollable region -- with no other focusable content of
207
+ // its own (e.g. plain text, or a non-interactive `<lyra-streaming-text>`),
208
+ // it would otherwise be unreachable by keyboard, same reasoning
209
+ // code-block.ts's and virtual-list.ts's own identical `[part="body"]`/
210
+ // `[part="base"]` tabindex document. `role="group"` (rather than a page
211
+ // landmark role) plus `aria-label` gives it an accessible name without
212
+ // claiming navigation significance for what is, structurally, just one
213
+ // part of a larger message.
197
214
  render() {
198
215
  const duration = this.durationDisplay;
199
216
  return html `
@@ -213,7 +230,15 @@ export class LyraThinkingPanel extends LyraElement {
213
230
  >`
214
231
  : nothing}
215
232
  </button>
216
- <div part="body" id=${this.bodyId} ?hidden=${!this.expanded} @scroll=${this.onScroll}>
233
+ <div
234
+ part="body"
235
+ id=${this.bodyId}
236
+ role="group"
237
+ aria-label=${this.label}
238
+ tabindex="0"
239
+ ?hidden=${!this.expanded}
240
+ @scroll=${this.onScroll}
241
+ >
217
242
  <slot></slot>
218
243
  </div>
219
244
  </div>
@@ -95,6 +95,12 @@ export const styles = css `
95
95
  [part='body'] {
96
96
  max-block-size: var(--lyra-thinking-panel-max-block-size);
97
97
  overflow-y: auto;
98
+ /* Reaching this region's own top/bottom edge stops there instead of
99
+ chaining the scroll into the host page -- this is precisely the
100
+ component that tracks and respects the reader scrolling within it, so
101
+ it shouldn't hand that scroll off to the page underneath it. Same
102
+ convention as virtual-list.styles.ts's own auto-scrolling region. */
103
+ overscroll-behavior: contain;
98
104
  padding: var(--lyra-space-m);
99
105
  border-block-start: 1px solid var(--lyra-color-border);
100
106
  color: var(--lyra-color-text-quiet);
@@ -16,26 +16,72 @@ export interface TimeRangePreset {
16
16
  * shortcut that sets both handles at once, the continuous brush underneath
17
17
  * is unaffected and both interaction modes coexist.
18
18
  *
19
+ * Form-associated (via a directly-attached `ElementInternals`, mirroring
20
+ * `<lyra-combobox>`'s minimal pattern rather than the single-string-value
21
+ * `FormAssociated` mixin, which doesn't fit a two-handle range): an ancestor
22
+ * `<fieldset disabled>` disables both handles and every preset button
23
+ * through `effectiveDisabled`, the same way it would a native `<input>`,
24
+ * without touching the consumer-facing `disabled` property/attribute itself.
25
+ *
19
26
  * @customElement lyra-time-range
20
27
  * @event lyra-input - Fired continuously while dragging or on each arrow-key press. `detail: { start, end }`.
21
28
  * @event lyra-change - Fired on release / keyup-commit, or when a preset button is clicked. `detail: { start, end }`.
22
- * @csspart base, track, range, handle-start, handle-end, presets, preset-button
29
+ * @csspart base - The time-range wrapper.
30
+ * @csspart track - The complete range track.
31
+ * @csspart range - The selected range.
32
+ * @csspart handle-start - The start handle.
33
+ * @csspart handle-end - The end handle.
34
+ * @csspart presets - The preset controls wrapper.
35
+ * @csspart preset-button - A preset button.
23
36
  */
24
37
  export declare class LyraTimeRange extends LyraElement {
38
+ static formAssociated: boolean;
25
39
  static styles: import("lit").CSSResultGroup[];
40
+ static properties: {
41
+ disabled: {
42
+ type: BooleanConstructor;
43
+ reflect: boolean;
44
+ noAccessor: boolean;
45
+ };
46
+ };
26
47
  min: number;
27
48
  max: number;
28
49
  start: number;
29
50
  end: number;
30
51
  step: number;
31
- disabled: boolean;
52
+ /** Accessible name for the start handle, used as its `aria-label`.
53
+ * Overridable for i18n/custom copy; defaults to the same literal text
54
+ * this component always rendered before the property existed. */
55
+ startLabel: string;
56
+ /** Accessible name for the end handle, used as its `aria-label`.
57
+ * Overridable for i18n/custom copy; defaults to the same literal text
58
+ * this component always rendered before the property existed. */
59
+ endLabel: string;
32
60
  /**
33
61
  * Optional discrete presets rendered as a `[part="presets"]` button row
34
62
  * above the track. Purely additive: leaving this empty (the default)
35
63
  * renders nothing extra and leaves the continuous brush untouched.
36
64
  */
37
65
  presets: TimeRangePreset[];
66
+ private internals;
67
+ private _disabled;
68
+ private _fieldsetDisabled;
38
69
  private drags;
70
+ constructor();
71
+ get disabled(): boolean;
72
+ set disabled(next: boolean);
73
+ /** Effective disabled state: this element's own `disabled` OR an ancestor
74
+ * `<fieldset disabled>`'s inherited state -- mirrors native `<input>`,
75
+ * whose own `disabled` IDL property/attribute is never mutated by a
76
+ * fieldset. */
77
+ get effectiveDisabled(): boolean;
78
+ /**
79
+ * Called by the browser when an ancestor `<fieldset disabled>` toggles.
80
+ * Tracked separately from the consumer's own `disabled` (see
81
+ * `effectiveDisabled`) so a consumer's explicit `disabled` survives the
82
+ * fieldset re-enabling instead of being permanently overwritten.
83
+ */
84
+ formDisabledCallback(disabled: boolean): void;
39
85
  disconnectedCallback(): void;
40
86
  private domain;
41
87
  private percentOf;
@@ -50,6 +96,17 @@ export declare class LyraTimeRange extends LyraElement {
50
96
  /**
51
97
  * Apply a discrete preset: sets both handles and emits the same
52
98
  * lyra-input/lyra-change pair a committed drag or keyboard step would.
99
+ *
100
+ * Deliberately bypasses `setValue()`/`clamp()` — a preset is an explicit
101
+ * discrete jump to the caller's own exact numbers, not a stepped nudge, so
102
+ * routing it through `clamp()`'s step-grid rounding would silently round
103
+ * a preset's values away from what the caller specified (and desync the
104
+ * active-button match in render(), which compares against these exact
105
+ * numbers). Only the domain bounds and the start<=end invariant apply
106
+ * here, not `step`. Both handles are also assigned before either event
107
+ * fires — routing them through two sequential `setValue()` calls instead
108
+ * would emit an extra lyra-input whose detail still held the stale
109
+ * pre-preset value for whichever handle hadn't been assigned yet.
53
110
  */
54
111
  private applyPreset;
55
112
  private onKeyDown;