@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
@@ -5,9 +5,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { html, svg, nothing } from 'lit';
8
- import { property, state } from 'lit/decorators.js';
8
+ import { state } from 'lit/decorators.js';
9
9
  import { LyraElement } from '../../internal/lyra-element.js';
10
10
  import { defineElement } from '../../internal/prefix.js';
11
+ import { AnchoredValidityController, VALIDITY_ANCHOR } from '../../internal/anchored-validity.js';
11
12
  import { styles } from './checkbox.styles.js';
12
13
  // Mirrors the shared icon set's viewBox/stroke conventions
13
14
  // (internal/icons.ts's chevronIcon()/closeIcon()/etc.) without importing
@@ -77,18 +78,85 @@ function indeterminateGlyph() {
77
78
  export class LyraCheckbox extends LyraElement {
78
79
  static { this.styles = [LyraElement.styles, styles]; }
79
80
  static { this.formAssociated = true; }
81
+ static { this.properties = {
82
+ checked: { type: Boolean, reflect: true, noAccessor: true },
83
+ indeterminate: { type: Boolean, reflect: true, noAccessor: true },
84
+ disabled: { type: Boolean, reflect: true, noAccessor: true },
85
+ name: { reflect: true, noAccessor: true },
86
+ required: { type: Boolean, reflect: true, noAccessor: true },
87
+ value: { noAccessor: true },
88
+ }; }
89
+ /** Whether the control is disabled explicitly or by an ancestor fieldset. */
90
+ get effectiveDisabled() {
91
+ return this.disabled || this._fieldsetDisabled;
92
+ }
93
+ get checked() {
94
+ return this._checked;
95
+ }
96
+ set checked(next) {
97
+ const old = this._checked;
98
+ this._checked = Boolean(next);
99
+ this.syncFormState();
100
+ this.requestUpdate('checked', old);
101
+ }
102
+ get indeterminate() {
103
+ return this._indeterminate;
104
+ }
105
+ set indeterminate(next) {
106
+ const old = this._indeterminate;
107
+ this._indeterminate = Boolean(next);
108
+ this.syncFormState();
109
+ this.requestUpdate('indeterminate', old);
110
+ }
111
+ get disabled() {
112
+ return this._disabled;
113
+ }
114
+ set disabled(next) {
115
+ const old = this._disabled;
116
+ this._disabled = Boolean(next);
117
+ this.toggleAttribute('disabled', this._disabled);
118
+ this.requestUpdate('disabled', old);
119
+ }
120
+ /** The form submission key, reflected synchronously for native form APIs. */
121
+ get name() {
122
+ return this._name;
123
+ }
124
+ set name(next) {
125
+ const old = this._name;
126
+ this._name = next ?? '';
127
+ if (this._name) {
128
+ this.setAttribute('name', this._name);
129
+ }
130
+ else {
131
+ this.removeAttribute('name');
132
+ }
133
+ this.requestUpdate('name', old);
134
+ }
135
+ get required() {
136
+ return this._required;
137
+ }
138
+ set required(next) {
139
+ const old = this._required;
140
+ this._required = Boolean(next);
141
+ this.toggleAttribute('required', this._required);
142
+ this.updateValidity();
143
+ this.requestUpdate('required', old);
144
+ }
145
+ get value() {
146
+ return this._value;
147
+ }
148
+ set value(next) {
149
+ const old = this._value;
150
+ this._value = next ?? 'on';
151
+ this.syncFormState();
152
+ this.requestUpdate('value', old);
153
+ }
80
154
  constructor() {
81
155
  super();
82
- this.checked = false;
83
156
  // Visual-only mixed state, matching native `<input type="checkbox">`
84
157
  // semantics: it does not affect `checked`'s own value, and a user
85
158
  // interaction (click/keyboard) clears it back to `false`, exactly like the
86
159
  // native control does.
87
- this.indeterminate = false;
88
- this.disabled = false;
89
- this.required = false;
90
- this.name = '';
91
- this.value = 'on';
92
160
  // Tracks whether the default slot carries any real (non-whitespace)
93
161
  // content, so the label wrapper — and the gap next to the box — can
94
162
  // collapse to nothing for an icon-only/aria-label-only checkbox instead of
@@ -96,10 +164,14 @@ export class LyraCheckbox extends LyraElement {
96
164
  // `assignedNodes` (not `assignedElements`) is checked — the common case is
97
165
  // a bare slotted text label, e.g. `<lyra-checkbox>Accept terms</lyra-checkbox>`.
98
166
  this.hasLabelSlot = false;
99
- // What `form.reset()` restores to captured once, from whatever `checked`
100
- // reads at first connect (i.e. whatever the declarative `checked`
101
- // attribute parsed to, since attribute parsing happens before
102
- // `connectedCallback`). `checked` reflects, so unlike `FormAssociated`'s
167
+ // Set on the control's first `blur`; gates the `data-invalid`/`aria-invalid`
168
+ // reflection below so validity styling never flashes on first render,
169
+ // mirroring `<lyra-combobox>`/`<lyra-select>`'s identical `touched` field.
170
+ this.touched = false;
171
+ // What `form.reset()` restores to — captured once from the declarative
172
+ // `checked` content attribute at first connect. A pre-connect `.checked`
173
+ // property assignment changes live state but not the reset default, matching
174
+ // native `checked`/`defaultChecked` semantics. `checked` reflects, so unlike `FormAssociated`'s
103
175
  // non-reflecting `value` this can't be captured from
104
176
  // `attributeChangedCallback` alone — that would also fire (and wrongly
105
177
  // redefine the default) every time the property setter reflects a later
@@ -108,11 +180,18 @@ export class LyraCheckbox extends LyraElement {
108
180
  // and `<lyra-switch>`'s identical `_defaultChecked`.
109
181
  this._defaultChecked = false;
110
182
  this._defaultCaptured = false;
183
+ this._fieldsetDisabled = false;
184
+ this._name = '';
185
+ this._checked = false;
186
+ this._indeterminate = false;
187
+ this._disabled = false;
188
+ this._required = false;
189
+ this._value = 'on';
111
190
  this.onClick = () => {
112
191
  this.toggle();
113
192
  };
114
193
  this.onKeyDown = (e) => {
115
- if (this.disabled)
194
+ if (this.effectiveDisabled)
116
195
  return;
117
196
  // Space/Enter both activate, matching lyra-switch's onKeyDown (and
118
197
  // lyra-table's sortable-header/row convention) for role-based clickable
@@ -123,19 +202,50 @@ export class LyraCheckbox extends LyraElement {
123
202
  this.toggle();
124
203
  }
125
204
  };
126
- this.onSlotChange = (e) => {
127
- const nodes = e.target.assignedNodes({ flatten: true });
128
- this.hasLabelSlot = nodes.some((n) => (n.textContent ?? '').trim().length > 0);
205
+ this.onSlotChange = () => {
206
+ this.recomputeHasLabelSlot();
207
+ };
208
+ this.onBlur = () => {
209
+ this.touched = true;
210
+ this.reflectInvalid();
129
211
  };
130
212
  this.internals = this.attachInternals();
213
+ this.validityController = new AnchoredValidityController(this, this.internals, () => this[VALIDITY_ANCHOR]());
214
+ this.syncFormState();
215
+ }
216
+ get form() {
217
+ return this.internals.form;
218
+ }
219
+ get labels() {
220
+ return this.internals.labels;
221
+ }
222
+ get validity() {
223
+ return this.internals.validity;
224
+ }
225
+ get validationMessage() {
226
+ return this.internals.validationMessage;
227
+ }
228
+ get willValidate() {
229
+ return this.internals.willValidate;
230
+ }
231
+ /** @internal */
232
+ [VALIDITY_ANCHOR]() {
233
+ return this.renderRoot?.querySelector('[part="base"]') ?? null;
131
234
  }
132
235
  connectedCallback() {
133
236
  super.connectedCallback();
134
237
  if (!this._defaultCaptured) {
135
238
  this._defaultCaptured = true;
136
- this._defaultChecked = this.checked;
239
+ this._defaultChecked = this.hasAttribute('checked');
137
240
  }
138
241
  this.updateValidity();
242
+ this.labelObserver = new MutationObserver(() => this.recomputeHasLabelSlot());
243
+ this.labelObserver.observe(this, { childList: true, subtree: true, characterData: true });
244
+ }
245
+ disconnectedCallback() {
246
+ super.disconnectedCallback();
247
+ this.labelObserver?.disconnect();
248
+ this.labelObserver = undefined;
139
249
  }
140
250
  willUpdate() {
141
251
  // Seed `hasLabelSlot` from the light-DOM children synchronously before
@@ -148,28 +258,44 @@ export class LyraCheckbox extends LyraElement {
148
258
  }
149
259
  updateValidity() {
150
260
  if (this.required && !this.checked) {
151
- this.internals.setValidity({ valueMissing: true }, 'Please check this box if you want to continue.');
261
+ this.validityController.setValidity({ valueMissing: true }, 'Please check this box if you want to continue.');
152
262
  }
153
263
  else {
154
- this.internals.setValidity({});
264
+ this.validityController.setValidity({});
155
265
  }
266
+ this.reflectInvalid();
156
267
  }
157
- updated(changed) {
158
- if (changed.has('checked') || changed.has('value')) {
159
- // A native checkbox submits its `value` content attribute (default
160
- // "on") only while checked, and contributes nothing at all — not even
161
- // an empty string — while unchecked.
162
- this.internals.setFormValue(this.checked ? this.value : null);
163
- }
164
- if (changed.has('checked') || changed.has('required')) {
165
- this.updateValidity();
166
- }
268
+ // Keeps `data-invalid` (styling hook) in lockstep with `aria-invalid`
269
+ // (rendered from the same expression in `render()`) any time validity
270
+ // could have changed, rather than waiting on Lit's async `updated()` --
271
+ // consistent with this component's already-synchronous
272
+ // `syncFormState()`/setter shape.
273
+ reflectInvalid() {
274
+ this.toggleAttribute('data-invalid', this.touched && !this.internals.validity.valid);
275
+ }
276
+ syncFormState() {
277
+ // A native checkbox submits its `value` content attribute (default
278
+ // "on") only while checked, and contributes nothing at all — not even
279
+ // an empty string — while unchecked.
280
+ const state = `${this.checked ? 'checked' : 'unchecked'}${this.indeterminate ? '/indeterminate' : ''}`;
281
+ this.internals.setFormValue(this.checked ? this.value : null, state);
282
+ this.updateValidity();
167
283
  }
168
284
  formResetCallback() {
169
285
  this.checked = this._defaultChecked;
170
286
  }
287
+ formStateRestoreCallback(state, _mode) {
288
+ const oldChecked = this._checked;
289
+ const oldIndeterminate = this._indeterminate;
290
+ this._checked = state === 'checked' || state === 'checked/indeterminate';
291
+ this._indeterminate = state === 'checked/indeterminate' || state === 'unchecked/indeterminate';
292
+ this.syncFormState();
293
+ this.requestUpdate('checked', oldChecked);
294
+ this.requestUpdate('indeterminate', oldIndeterminate);
295
+ }
171
296
  formDisabledCallback(disabled) {
172
- this.disabled = disabled;
297
+ this._fieldsetDisabled = disabled;
298
+ this.requestUpdate();
173
299
  }
174
300
  checkValidity() {
175
301
  return this.internals.checkValidity();
@@ -178,25 +304,37 @@ export class LyraCheckbox extends LyraElement {
178
304
  return this.internals.reportValidity();
179
305
  }
180
306
  toggle() {
181
- if (this.disabled)
307
+ if (this.effectiveDisabled)
182
308
  return;
183
309
  this.checked = !this.checked;
184
310
  this.indeterminate = false;
185
311
  this.emit('lyra-change', { checked: this.checked });
186
312
  }
313
+ // Shared by `onSlotChange` (the "set of distributed nodes changed" case)
314
+ // and `labelObserver` (the "an already-slotted node mutated in place"
315
+ // case) so both paths agree on what counts as real label content.
316
+ recomputeHasLabelSlot() {
317
+ const slot = this.renderRoot.querySelector('slot');
318
+ if (!slot)
319
+ return;
320
+ const nodes = slot.assignedNodes({ flatten: true });
321
+ this.hasLabelSlot = nodes.some((n) => (n.textContent ?? '').trim().length > 0);
322
+ }
187
323
  render() {
188
324
  const mixed = this.indeterminate;
189
325
  return html `
190
326
  <span
191
327
  part="base"
192
328
  role="checkbox"
193
- tabindex=${this.disabled ? '-1' : '0'}
329
+ tabindex=${this.effectiveDisabled ? '-1' : '0'}
194
330
  aria-checked=${mixed ? 'mixed' : this.checked ? 'true' : 'false'}
195
331
  aria-required=${this.required ? 'true' : nothing}
196
- aria-disabled=${this.disabled ? 'true' : nothing}
332
+ aria-invalid=${this.touched && !this.internals.validity.valid ? 'true' : 'false'}
333
+ aria-disabled=${this.effectiveDisabled ? 'true' : nothing}
197
334
  aria-label=${this.getAttribute('aria-label') || nothing}
198
335
  @click=${this.onClick}
199
336
  @keydown=${this.onKeyDown}
337
+ @blur=${this.onBlur}
200
338
  >
201
339
  <span part="box"> ${mixed ? indeterminateGlyph() : this.checked ? checkmarkGlyph() : nothing} </span>
202
340
  <span part="label" ?hidden=${!this.hasLabelSlot}>
@@ -206,25 +344,10 @@ export class LyraCheckbox extends LyraElement {
206
344
  `;
207
345
  }
208
346
  }
209
- __decorate([
210
- property({ type: Boolean, reflect: true })
211
- ], LyraCheckbox.prototype, "checked", void 0);
212
- __decorate([
213
- property({ type: Boolean, reflect: true })
214
- ], LyraCheckbox.prototype, "indeterminate", void 0);
215
- __decorate([
216
- property({ type: Boolean, reflect: true })
217
- ], LyraCheckbox.prototype, "disabled", void 0);
218
- __decorate([
219
- property({ type: Boolean, reflect: true })
220
- ], LyraCheckbox.prototype, "required", void 0);
221
- __decorate([
222
- property()
223
- ], LyraCheckbox.prototype, "name", void 0);
224
- __decorate([
225
- property()
226
- ], LyraCheckbox.prototype, "value", void 0);
227
347
  __decorate([
228
348
  state()
229
349
  ], LyraCheckbox.prototype, "hasLabelSlot", void 0);
350
+ __decorate([
351
+ state()
352
+ ], LyraCheckbox.prototype, "touched", void 0);
230
353
  defineElement('checkbox', LyraCheckbox);
@@ -14,7 +14,7 @@ export const styles = css `
14
14
  outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
15
15
  outline-offset: var(--lyra-focus-ring-offset);
16
16
  }
17
- :host([disabled]) [part='base'] {
17
+ :host(:disabled) [part='base'] {
18
18
  cursor: not-allowed;
19
19
  opacity: var(--lyra-opacity-disabled);
20
20
  }
@@ -40,7 +40,7 @@ export const styles = css `
40
40
  background-color var(--lyra-transition-fast),
41
41
  border-color var(--lyra-transition-fast);
42
42
  }
43
- :host(:not([disabled])) [part='base']:hover [part='box'] {
43
+ :host(:not(:disabled)) [part='base']:hover [part='box'] {
44
44
  border-color: var(--lyra-color-brand);
45
45
  }
46
46
  :host([checked]) [part='box'],
@@ -48,6 +48,13 @@ export const styles = css `
48
48
  background: var(--lyra-color-brand);
49
49
  border-color: var(--lyra-color-brand);
50
50
  }
51
+ /* Gives a required-but-unmet checkbox a persistent visible affordance --
52
+ matching lyra-combobox/lyra-select's data-invalid styling hook --
53
+ beyond the transient native validation-bubble popup, which only shows
54
+ momentarily around reportValidity()/form submission. */
55
+ :host([data-invalid]) [part='box'] {
56
+ border-color: var(--lyra-color-danger);
57
+ }
51
58
 
52
59
  [part='checkmark'] {
53
60
  display: block;
@@ -72,10 +72,16 @@ export class LyraChipGroup extends LyraElement {
72
72
  firstUpdated() {
73
73
  // Fallback reconciliation for slot-forwarding / engines that don't fire
74
74
  // `slotchange` for content present at parse time — same idiom as
75
- // `<lyra-source-list>`'s identical `firstUpdated`.
75
+ // `<lyra-source-list>`'s identical `firstUpdated`. This can't move to
76
+ // `willUpdate` (the `<slot>` doesn't exist in the shadow DOM until after
77
+ // the first render), so when it actually corrects `childCount` (the
78
+ // slot-forwarding case, where `this.children.length` under-counts) it
79
+ // unavoidably schedules a second update pass — `updated()` (below) always
80
+ // runs right after this and already recomputes visibility from this same
81
+ // corrected count, so there's nothing left for an explicit call here to
82
+ // do.
76
83
  const slot = this.shadowRoot.querySelector('slot');
77
84
  this.childCount = slot.assignedElements({ flatten: true }).length;
78
- this.syncChildVisibility();
79
85
  }
80
86
  updated() {
81
87
  this.syncChildVisibility();
@@ -72,10 +72,17 @@ export class LyraChip extends LyraElement {
72
72
  }
73
73
  // Only the default slot's own content counts toward the remove button's
74
74
  // accessible name — text incidentally living inside the (decorative)
75
- // `icon` slot shouldn't leak into "Remove {text}".
75
+ // `icon` slot shouldn't leak into "Remove {text}". Restricting to Text and
76
+ // Element nodes also excludes Comment nodes: when a consumer interpolates
77
+ // the label via a lit-html expression (`html\`<lyra-chip>${label}</lyra-chip>\``,
78
+ // the ordinary way a data-driven label gets bound) rather than a static
79
+ // string, lit-html inserts a marker Comment node alongside the Text node in
80
+ // the light DOM. That comment's own (non-empty) data is internal
81
+ // bookkeeping, not label content, so it must never reach `textContent`.
76
82
  get labelText() {
77
83
  return Array.from(this.childNodes)
78
- .filter((n) => !(n instanceof Element && n.getAttribute('slot') === 'icon'))
84
+ .filter((n) => (n.nodeType === Node.TEXT_NODE || n instanceof Element) &&
85
+ !(n instanceof Element && n.getAttribute('slot') === 'icon'))
79
86
  .map((n) => n.textContent ?? '')
80
87
  .join('')
81
88
  .trim();
@@ -29,6 +29,17 @@ const STATUS_LABEL = {
29
29
  // keyboard blur — those are intentional dismissals, not transient pointer
30
30
  // travel, so they close immediately.
31
31
  const HIDE_DELAY_MS = 200;
32
+ // The default slot's preview content is often plain text (see the Statuses
33
+ // story: "No source has confirmed this yet.") with no wrapping element at
34
+ // all, so an Element-only check (e.g. Array.from(children)/assignedElements)
35
+ // never counts it. Mirrors result-field.ts's hasRealContent: a node counts as
36
+ // real preview content if it's an element not assigned to some other named
37
+ // slot, or non-whitespace text.
38
+ function isRealPreviewNode(n) {
39
+ return n.nodeType === Node.ELEMENT_NODE
40
+ ? !n.hasAttribute('slot')
41
+ : (n.textContent ?? '').trim().length > 0;
42
+ }
32
43
  /**
33
44
  * `<lyra-citation-badge>` — an inline `[n]` citation marker with a hover/
34
45
  * focus preview popover and confidence/verification-status coloring. Used
@@ -113,7 +124,7 @@ export class LyraCitationBadge extends LyraElement {
113
124
  this.hovering = false;
114
125
  this.focused = false;
115
126
  this.onSlotChange = (e) => {
116
- this.hasPreviewSlot = e.target.assignedElements({ flatten: true }).length > 0;
127
+ this.hasPreviewSlot = e.target.assignedNodes({ flatten: true }).some(isRealPreviewNode);
117
128
  // The slot can be emptied out from under an already-open popover (e.g. a
118
129
  // consumer clearing preview content asynchronously) — nothing left to
119
130
  // show, so don't leave an empty panel floating open.
@@ -183,7 +194,7 @@ export class LyraCitationBadge extends LyraElement {
183
194
  static { this.styles = [LyraElement.styles, styles]; }
184
195
  willUpdate() {
185
196
  if (!this.hasUpdated) {
186
- this.hasPreviewSlot = Array.from(this.children).some((el) => !el.hasAttribute('slot'));
197
+ this.hasPreviewSlot = Array.from(this.childNodes).some(isRealPreviewNode);
187
198
  }
188
199
  }
189
200
  updated(changed) {
@@ -214,10 +225,17 @@ export class LyraCitationBadge extends LyraElement {
214
225
  // method names on every HTMLElement subclass, so reusing them here would
215
226
  // be a same-name-different-signature override error.
216
227
  showPreview() {
217
- if (!this.hasPreviewSlot || this.popoverOpen)
228
+ if (!this.hasPreviewSlot)
218
229
  return;
230
+ // Cancel any pending hide unconditionally -- including when the popover
231
+ // is already open -- so hover/focus returning within the grace period
232
+ // (pointerenter -> pointerleave -> pointerenter before HIDE_DELAY_MS)
233
+ // actually cancels the scheduled hide instead of leaving it armed to
234
+ // fire later regardless of the now-restored hover/focus state.
219
235
  clearTimeout(this.hideTimer);
220
236
  this.hideTimer = undefined;
237
+ if (this.popoverOpen)
238
+ return;
221
239
  this.popoverOpen = true;
222
240
  }
223
241
  scheduleHidePreview() {
@@ -1,5 +1,6 @@
1
1
  import { type TemplateResult, type PropertyValues } from 'lit';
2
2
  import { LyraElement } from '../../internal/lyra-element.js';
3
+ import { type ShikiLanguageInput } from './code-loader.js';
3
4
  import '../skeleton/skeleton.js';
4
5
  /**
5
6
  * `<lyra-code-block>` — fenced code display with optional lazy syntax
@@ -21,11 +22,35 @@ import '../skeleton/skeleton.js';
21
22
  * is already a perfectly readable placeholder for it, so a second
22
23
  * loading-chrome state would add complexity for little practical benefit.
23
24
  *
25
+ * `languages` is an additive, opt-in escape hatch from that default path for
26
+ * a consumer whose language set is fixed and known ahead of time: a map of
27
+ * language id to an already-imported shiki grammar module (e.g. `import bash
28
+ * from 'shiki/langs/bash.mjs'`). When `language` matches a key in `languages`,
29
+ * this component seeds a fine-grained `createHighlighterCore()` highlighter
30
+ * with *only* the pre-supplied grammars (see `code-loader.ts`'s
31
+ * `loadShikiHighlighterCore()`) instead of waiting on `loadShikiHighlighter()`
32
+ * and its dynamic per-language `loadLanguage()` import. The payoff isn't
33
+ * runtime cost — the default dynamic-import path is already well-optimized
34
+ * for that — it's *build output*: shiki's main entry point bundles a dynamic
35
+ * `import()` per bundled language (~200 of them) because a bundler can't
36
+ * statically narrow which of those a `loadLanguage(lang: string)` call might
37
+ * request at runtime, so it conservatively emits a build-output chunk for
38
+ * every one of them. `shiki/core`'s fine-grained API has no such table — a
39
+ * bundler only ever sees the exact grammar modules `languages` itself
40
+ * `import`s, so a consumer who pins its full language set this way trades a
41
+ * hand-maintained list for a build output scoped to just those languages
42
+ * instead of shiki's entire bundled set. A language requested but absent
43
+ * from `languages` still falls back to the ordinary dynamic-import path
44
+ * unchanged, so this is a partial opt-in, not a replacement for it.
45
+ *
24
46
  * @customElement lyra-code-block
25
47
  * @event lyra-copy - The copy button was activated. `detail: { text }` is
26
48
  * always the raw `code` value (never the highlighted HTML), and always
27
49
  * fires regardless of whether the actual OS clipboard write succeeded —
28
50
  * same convention as `<lyra-json-viewer>`'s own copy button.
51
+ * @event lyra-toggle - The collapse/expand header button was activated.
52
+ * `detail: { collapsed }` — same event name and shape convention as
53
+ * `<lyra-thinking-panel>`'s own `lyra-toggle`.
29
54
  * @csspart base - The outer container.
30
55
  * @csspart header - The row above the code (filename/language/copy/toggle),
31
56
  * present whenever there's anything to put in it.
@@ -63,6 +88,18 @@ export declare class LyraCodeBlock extends LyraElement {
63
88
  /** A CSS length (e.g. `"20rem"`); once set, the code scrolls internally
64
89
  * past this height instead of growing the page. */
65
90
  maxHeight: string;
91
+ /** A map of language id to an already-imported shiki grammar module's
92
+ * default export (e.g. `{ bash: bashGrammar }` where `bashGrammar` came
93
+ * from a module-scope `import bash from 'shiki/langs/bash.mjs'`). When
94
+ * `language` matches a key here, highlighting for it is seeded from
95
+ * exactly this pre-supplied grammar via a fine-grained
96
+ * `createHighlighterCore()` highlighter, bypassing the default
97
+ * `loadShikiHighlighter()` singleton and its dynamic per-language
98
+ * `loadLanguage()` import entirely for that language — see the class doc
99
+ * above for the build-output rationale. A `language` value absent from
100
+ * this map (or left unset, or when `languages` itself is unset) falls
101
+ * back to that default dynamic-import path unchanged. */
102
+ languages?: Record<string, ShikiLanguageInput>;
66
103
  private highlightedHtml;
67
104
  private shikiReady;
68
105
  private justCopied;
@@ -72,6 +109,7 @@ export declare class LyraCodeBlock extends LyraElement {
72
109
  private readonly bodyId;
73
110
  connectedCallback(): void;
74
111
  disconnectedCallback(): void;
112
+ private preSuppliedGrammar;
75
113
  protected willUpdate(changed: PropertyValues): void;
76
114
  protected updated(): void;
77
115
  private syncHighlight;