@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,14 +5,11 @@ export interface MediaCardOpenDetail {
5
5
  src: string;
6
6
  filename: string;
7
7
  }
8
- /** Validates `url` is safe to assign as an `<img>`/`<video>` `src`. Returns
9
- * the trimmed url when safe, `null` otherwise -- see the "safe-URL
10
- * checking" comment above for the `data:` judgement call and why `URL` is
11
- * used instead of a regex. */
8
+ /** Validates `url` for an `<img>`/`<video>` source. Kept as a public wrapper
9
+ * so existing imports and generated API metadata remain stable. */
12
10
  export declare function safeMediaSrc(url: string): string | null;
13
- /** Validates `url` is safe to assign as an `<a href>` -- stricter than
14
- * `safeMediaSrc()`, see the "safe-URL checking" comment above. Returns the
15
- * trimmed url when safe, `null` otherwise. */
11
+ /** Validates `url` for the library's download/open link sinks. Kept as a
12
+ * public wrapper so existing imports and generated API metadata remain stable. */
16
13
  export declare function safeLinkHref(url: string): string | null;
17
14
  /**
18
15
  * `<lyra-media-card>` — a lightweight inline preview for one already-sent,
@@ -35,16 +32,19 @@ export declare function safeLinkHref(url: string): string | null;
35
32
  * `<img>`/`<video>` `src` or an `<a href>` — only `http:`/`https:`/`blob:`
36
33
  * (plus `data:` for a *media* `src` only) or a scheme-relative/relative URL
37
34
  * with no scheme at all pass; anything else (`javascript:`, `vbscript:`,
38
- * and similarly suspicious schemes) is rejected. See `safeMediaSrc()` /
39
- * `safeLinkHref()` and their shared "safe-URL checking" comment for exactly
40
- * why `data:` gets two different answers depending on which DOM sink it's
41
- * headed for, and why the check is built on `new URL()` rather than a
42
- * hand-rolled scheme regex. An `image`/`video` `kind` whose `src` fails the
43
- * media-src check falls back to the generic file-chip rendering (which then
44
- * separately re-validates `src` against the stricter href allowlist for its
45
- * own download affordance) this is the "plain preview unavailable state"
46
- * a dangerous URL degrades to, rather than ever reaching a real media/anchor
47
- * sink.
35
+ * and similarly suspicious schemes) is rejected. `safeMediaSrc()` and
36
+ * `safeLinkHref()` share a platform-URL-based validator; their sink-specific
37
+ * allowlists explain why `data:` gets two different answers depending on
38
+ * where it is used. An `image`/`video` `kind` whose `src` fails the
39
+ * media-src check falls back to the generic file-chip rendering — this is
40
+ * the "plain preview unavailable state" a dangerous URL degrades to, rather
41
+ * than ever reaching a real media/anchor sink. That fallback also re-checks
42
+ * `src` against the stricter href allowlist for its own download affordance,
43
+ * but since the href allowlist is a strict subset of the media-src allowlist,
44
+ * that re-check can only ever change the outcome for a direct/auto-detected
45
+ * `kind="file"` `src` — an `image`/`video` `src` that already failed the
46
+ * wider media-src check necessarily fails the narrower href check too, so
47
+ * the fallback for those two kinds is always the plain, unclickable `span`.
48
48
  *
49
49
  * **The `video` case renders its open affordance separately from `base`.**
50
50
  * `image`/`file` wrap their *entire* card in one native `<button>`/`<a>` —
@@ -98,6 +98,10 @@ export declare class LyraMediaCard extends LyraElement {
98
98
  /** Alt text for the image case (and reused as a video label fallback).
99
99
  * Falls back to `filename`, then a generic per-kind description. */
100
100
  alt: string;
101
+ /** A CSS length (e.g. `"16rem"`); once set, overrides the
102
+ * `--lyra-media-card-max-height` custom property for this instance only —
103
+ * same contract as `<lyra-document-preview>`'s identically-named prop. */
104
+ maxHeight: string;
101
105
  /** Effective kind used for rendering — `kind` if explicitly set,
102
106
  * otherwise detected from `mime-type`. */
103
107
  private get resolvedKind();
@@ -107,6 +111,10 @@ export declare class LyraMediaCard extends LyraElement {
107
111
  private get accessibleLabel();
108
112
  private get imgAlt();
109
113
  private get videoLabel();
114
+ /** Per-instance override for `--lyra-media-card-max-height`, applied
115
+ * inline on `[part="base"]` -- the only mechanism that reliably wins
116
+ * over the `:host{}`-declared default from outside the shadow root. */
117
+ private get baseStyle();
110
118
  private emitOpen;
111
119
  private onActivate;
112
120
  private onLinkClick;
@@ -4,65 +4,22 @@ 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, svg } from 'lit';
7
+ import { html, nothing, svg } 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
11
  import { expandIcon } from '../../internal/icons.js';
12
+ import { safeLinkHref as validateLinkHref, safeMediaSrc as validateMediaSrc, } from '../../internal/safe-url.js';
12
13
  import { styles } from './media-card.styles.js';
13
- // -- safe-URL checking -------------------------------------------------
14
- //
15
- // `new URL()` is used (rather than a hand-rolled "does this look like a
16
- // scheme" regex) specifically because the platform's own URL parser already
17
- // implements the WHATWG URL Standard's input normalization -- stripping
18
- // ASCII tab/newline and leading/trailing C0-control-or-space from the input
19
- // before it ever looks for a scheme. A naive regex checked against the raw
20
- // string is exactly what a crafted value like `"java\tscript:alert(1)"` is
21
- // designed to defeat: the tab breaks a `/^[a-z]+:/`-style match (so the
22
- // string would wrongly fall through to the "no scheme, must be relative"
23
- // branch) while a browser attribute sink still normalizes it down to a
24
- // working `javascript:` URL. Delegating to `URL` means this check can never
25
- // drift out of sync with what the browser itself will actually do with the
26
- // string. `new URL(url)` throws when `url` has no scheme and no base to
27
- // resolve against, which is exactly the "relative or scheme-relative" case
28
- // this component allows unconditionally -- see `schemeOf()` below.
29
- const SAFE_MEDIA_SRC_SCHEMES = new Set(['http:', 'https:', 'blob:', 'data:']);
30
- // Deliberately narrower than the media-src allowlist: `data:` is excluded.
31
- // A `data:` URI is inert as an `<img>`/`<video>` *source* (a browser never
32
- // executes script from a media element's `src`), but a `data:text/html;...`
33
- // URI navigated to directly via a clicked `<a href>` runs as a full document
34
- // and can execute script -- so the same scheme gets a different verdict
35
- // depending on which DOM sink it's headed for.
36
- const SAFE_LINK_HREF_SCHEMES = new Set(['http:', 'https:', 'blob:']);
37
- function schemeOf(url) {
38
- try {
39
- return new URL(url).protocol;
40
- }
41
- catch {
42
- return null;
43
- }
44
- }
45
- function safeUrlOrNull(url, allowedSchemes) {
46
- const trimmed = url.trim();
47
- if (trimmed === '')
48
- return null;
49
- const scheme = schemeOf(trimmed);
50
- if (scheme === null)
51
- return trimmed; // no scheme -- relative or scheme-relative, always allowed
52
- return allowedSchemes.has(scheme) ? trimmed : null;
53
- }
54
- /** Validates `url` is safe to assign as an `<img>`/`<video>` `src`. Returns
55
- * the trimmed url when safe, `null` otherwise -- see the "safe-URL
56
- * checking" comment above for the `data:` judgement call and why `URL` is
57
- * used instead of a regex. */
14
+ /** Validates `url` for an `<img>`/`<video>` source. Kept as a public wrapper
15
+ * so existing imports and generated API metadata remain stable. */
58
16
  export function safeMediaSrc(url) {
59
- return safeUrlOrNull(url, SAFE_MEDIA_SRC_SCHEMES);
17
+ return validateMediaSrc(url);
60
18
  }
61
- /** Validates `url` is safe to assign as an `<a href>` -- stricter than
62
- * `safeMediaSrc()`, see the "safe-URL checking" comment above. Returns the
63
- * trimmed url when safe, `null` otherwise. */
19
+ /** Validates `url` for the library's download/open link sinks. Kept as a
20
+ * public wrapper so existing imports and generated API metadata remain stable. */
64
21
  export function safeLinkHref(url) {
65
- return safeUrlOrNull(url, SAFE_LINK_HREF_SCHEMES);
22
+ return validateLinkHref(url);
66
23
  }
67
24
  function detectKind(mimeType) {
68
25
  const mt = mimeType.trim().toLowerCase();
@@ -118,16 +75,19 @@ function fileGlyph() {
118
75
  * `<img>`/`<video>` `src` or an `<a href>` — only `http:`/`https:`/`blob:`
119
76
  * (plus `data:` for a *media* `src` only) or a scheme-relative/relative URL
120
77
  * with no scheme at all pass; anything else (`javascript:`, `vbscript:`,
121
- * and similarly suspicious schemes) is rejected. See `safeMediaSrc()` /
122
- * `safeLinkHref()` and their shared "safe-URL checking" comment for exactly
123
- * why `data:` gets two different answers depending on which DOM sink it's
124
- * headed for, and why the check is built on `new URL()` rather than a
125
- * hand-rolled scheme regex. An `image`/`video` `kind` whose `src` fails the
126
- * media-src check falls back to the generic file-chip rendering (which then
127
- * separately re-validates `src` against the stricter href allowlist for its
128
- * own download affordance) this is the "plain preview unavailable state"
129
- * a dangerous URL degrades to, rather than ever reaching a real media/anchor
130
- * sink.
78
+ * and similarly suspicious schemes) is rejected. `safeMediaSrc()` and
79
+ * `safeLinkHref()` share a platform-URL-based validator; their sink-specific
80
+ * allowlists explain why `data:` gets two different answers depending on
81
+ * where it is used. An `image`/`video` `kind` whose `src` fails the
82
+ * media-src check falls back to the generic file-chip rendering — this is
83
+ * the "plain preview unavailable state" a dangerous URL degrades to, rather
84
+ * than ever reaching a real media/anchor sink. That fallback also re-checks
85
+ * `src` against the stricter href allowlist for its own download affordance,
86
+ * but since the href allowlist is a strict subset of the media-src allowlist,
87
+ * that re-check can only ever change the outcome for a direct/auto-detected
88
+ * `kind="file"` `src` — an `image`/`video` `src` that already failed the
89
+ * wider media-src check necessarily fails the narrower href check too, so
90
+ * the fallback for those two kinds is always the plain, unclickable `span`.
131
91
  *
132
92
  * **The `video` case renders its open affordance separately from `base`.**
133
93
  * `image`/`file` wrap their *entire* card in one native `<button>`/`<a>` —
@@ -180,6 +140,10 @@ export class LyraMediaCard extends LyraElement {
180
140
  /** Alt text for the image case (and reused as a video label fallback).
181
141
  * Falls back to `filename`, then a generic per-kind description. */
182
142
  this.alt = '';
143
+ /** A CSS length (e.g. `"16rem"`); once set, overrides the
144
+ * `--lyra-media-card-max-height` custom property for this instance only —
145
+ * same contract as `<lyra-document-preview>`'s identically-named prop. */
146
+ this.maxHeight = '';
183
147
  this.onActivate = () => {
184
148
  this.emitOpen();
185
149
  };
@@ -214,19 +178,29 @@ export class LyraMediaCard extends LyraElement {
214
178
  get videoLabel() {
215
179
  return this.alt || this.filename || 'Video attachment';
216
180
  }
181
+ /** Per-instance override for `--lyra-media-card-max-height`, applied
182
+ * inline on `[part="base"]` -- the only mechanism that reliably wins
183
+ * over the `:host{}`-declared default from outside the shadow root. */
184
+ get baseStyle() {
185
+ return this.maxHeight ? `--lyra-media-card-max-height:${this.maxHeight}` : nothing;
186
+ }
217
187
  emitOpen() {
218
- return this.emit('lyra-open', { src: this.src, filename: this.filename });
188
+ // Matches whichever safe-URL sink actually rendered (see the class doc):
189
+ // falls back to a trimmed raw src so a whitespace-padded, otherwise-unsafe
190
+ // src still reports the same value the DOM would show if it were safe.
191
+ const src = safeMediaSrc(this.src) ?? safeLinkHref(this.src) ?? this.src.trim();
192
+ return this.emit('lyra-open', { src, filename: this.filename });
219
193
  }
220
194
  renderImage(src) {
221
195
  return html `
222
- <button part="base" type="button" aria-label=${this.accessibleLabel} @click=${this.onActivate}>
196
+ <button part="base" type="button" style=${this.baseStyle} aria-label=${this.accessibleLabel} @click=${this.onActivate}>
223
197
  <img part="media" src=${src} alt=${this.imgAlt} />
224
198
  </button>
225
199
  `;
226
200
  }
227
201
  renderVideo(src) {
228
202
  return html `
229
- <div part="base">
203
+ <div part="base" style=${this.baseStyle}>
230
204
  <video part="media" controls src=${src} aria-label=${this.videoLabel}></video>
231
205
  <button part="open-button" type="button" aria-label=${this.accessibleLabel} @click=${this.onActivate}>
232
206
  ${expandIcon()}
@@ -246,6 +220,7 @@ export class LyraMediaCard extends LyraElement {
246
220
  <a
247
221
  part="base"
248
222
  href=${href}
223
+ style=${this.baseStyle}
249
224
  download=${this.filename || ''}
250
225
  aria-label=${this.accessibleLabel}
251
226
  @click=${this.onLinkClick}
@@ -258,7 +233,7 @@ export class LyraMediaCard extends LyraElement {
258
233
  // doc describes: the filename/icon still display, but nothing is
259
234
  // clickable (there's nothing safe to point a download/open affordance
260
235
  // at, and no built-in lightbox to fall back to for this kind).
261
- return html `<span part="base">${content}</span>`;
236
+ return html `<span part="base" style=${this.baseStyle}>${content}</span>`;
262
237
  }
263
238
  render() {
264
239
  const kind = this.resolvedKind;
@@ -292,4 +267,7 @@ __decorate([
292
267
  __decorate([
293
268
  property()
294
269
  ], LyraMediaCard.prototype, "alt", void 0);
270
+ __decorate([
271
+ property({ attribute: 'max-height' })
272
+ ], LyraMediaCard.prototype, "maxHeight", void 0);
295
273
  defineElement('media-card', LyraMediaCard);
@@ -177,7 +177,10 @@ export declare class LyraMentionPopover extends LyraElement {
177
177
  * The host's own text-control keydown handler calls this while the
178
178
  * popover is open. Returns `true` when the key was intercepted
179
179
  * (`preventDefault()` already called) and the host should not also act on
180
- * it; `false` when the host's normal handling should proceed untouched.
180
+ * it; `false` when the host's normal handling should proceed untouched --
181
+ * including ArrowDown/ArrowUp/Enter/Tab when there are zero filtered rows
182
+ * to act on, so e.g. the host's own textarea still moves its caret a line
183
+ * normally rather than having the keystroke silently eaten.
181
184
  */
182
185
  handleKeyDown(e: KeyboardEvent): boolean;
183
186
  private clampedIndex;
@@ -297,6 +297,16 @@ export class LyraMentionPopover extends LyraElement {
297
297
  else if (this.open && (changed.has('anchor') || changed.has('query'))) {
298
298
  this.reposition();
299
299
  }
300
+ // Keyed separately from the open/anchor/query branch above: a plain
301
+ // ArrowDown/ArrowUp only ever changes activeIndex (see handleKeyDown), so
302
+ // this must fire on its own rather than piggyback on a reposition. The
303
+ // popup's own [part='listbox'] is height-capped and scrollable (see
304
+ // mention-popover.styles.ts) -- without this, arrowing past its visible
305
+ // rows would silently move the highlight off-screen. `block: 'nearest'`
306
+ // makes this a no-op whenever the active row is already fully visible.
307
+ if (changed.has('activeIndex')) {
308
+ this.renderRoot.querySelector('[part="option"][data-active]')?.scrollIntoView({ block: 'nearest' });
309
+ }
300
310
  }
301
311
  disconnectedCallback() {
302
312
  super.disconnectedCallback();
@@ -334,7 +344,10 @@ export class LyraMentionPopover extends LyraElement {
334
344
  * The host's own text-control keydown handler calls this while the
335
345
  * popover is open. Returns `true` when the key was intercepted
336
346
  * (`preventDefault()` already called) and the host should not also act on
337
- * it; `false` when the host's normal handling should proceed untouched.
347
+ * it; `false` when the host's normal handling should proceed untouched --
348
+ * including ArrowDown/ArrowUp/Enter/Tab when there are zero filtered rows
349
+ * to act on, so e.g. the host's own textarea still moves its caret a line
350
+ * normally rather than having the keystroke silently eaten.
338
351
  */
339
352
  handleKeyDown(e) {
340
353
  if (!this.open)
@@ -342,14 +355,19 @@ export class LyraMentionPopover extends LyraElement {
342
355
  const rows = this.filteredItems;
343
356
  switch (e.key) {
344
357
  case 'ArrowDown':
358
+ // Nothing to navigate -- let the key fall through to the host's own
359
+ // control (e.g. move the caret to the next line), matching Enter/Tab's
360
+ // identical "no active row" fallthrough right below.
361
+ if (!rows.length)
362
+ return false;
345
363
  e.preventDefault();
346
- if (rows.length)
347
- this.activeIndex = Math.min(rows.length - 1, this.clampedIndex(rows) + 1);
364
+ this.activeIndex = Math.min(rows.length - 1, this.clampedIndex(rows) + 1);
348
365
  return true;
349
366
  case 'ArrowUp':
367
+ if (!rows.length)
368
+ return false;
350
369
  e.preventDefault();
351
- if (rows.length)
352
- this.activeIndex = Math.max(0, this.clampedIndex(rows) - 1);
370
+ this.activeIndex = Math.max(0, this.clampedIndex(rows) - 1);
353
371
  return true;
354
372
  case 'Enter':
355
373
  case 'Tab': {
@@ -1,5 +1,10 @@
1
- import { type TemplateResult } from 'lit';
1
+ import { type PropertyValues, type TemplateResult } from 'lit';
2
2
  import { LyraElement } from '../../internal/lyra-element.js';
3
+ export type MenuItemType = 'normal' | 'checkbox';
4
+ export interface MenuItemChangeDetail {
5
+ value: string;
6
+ checked: boolean;
7
+ }
3
8
  /**
4
9
  * `<lyra-menu-item>` — a single action row inside `<lyra-menu>`'s default
5
10
  * slot. Not meaningful on its own (there is no standalone "click a menu item"
@@ -23,6 +28,18 @@ import { LyraElement } from '../../internal/lyra-element.js';
23
28
  * itself, so `select()` fires identically whether the item was reached by
24
29
  * mouse or keyboard.
25
30
  *
31
+ * `type="checkbox"` (mirroring `wa-dropdown-item`'s identical `type` option)
32
+ * renders `role="menuitemcheckbox"` in place of `role="menuitem"`, with
33
+ * `aria-checked` reflecting `checked` and a checkmark glyph shown once
34
+ * `checked` is `true`. Activating a `checkbox`-type item (click, or the
35
+ * parent's Enter/Space handling via `select()`) toggles `checked` and fires
36
+ * `lyra-menu-item-change` *in addition to* the usual `lyra-menu-item-select`
37
+ * — the latter is never suppressed, so a parent `<lyra-menu>` still closes
38
+ * and re-fires its consolidated `lyra-menu-select` exactly as it does for a
39
+ * `type="normal"` item. `type="normal"` (the default) renders and behaves
40
+ * exactly as before this option existed — no role, rendering, or event
41
+ * differences.
42
+ *
26
43
  * @customElement lyra-menu-item
27
44
  * @slot - The item's label content.
28
45
  * @slot icon - Optional leading icon.
@@ -34,9 +51,14 @@ import { LyraElement } from '../../internal/lyra-element.js';
34
51
  * (`detail: { value }`) rather than requiring a consumer to listen on every
35
52
  * individual item — listen there instead unless you specifically need a
36
53
  * per-item handler.
54
+ * @event lyra-menu-item-change - A `type="checkbox"` item was activated and
55
+ * its `checked` state toggled. `detail: { value, checked }` — the item's own
56
+ * `value` and its new `checked` value. Fired in addition to (never instead
57
+ * of) `lyra-menu-item-select` above. Never fired for `type="normal"`.
37
58
  * @csspart base - The row (`role` lives on the host — see the class doc).
38
59
  * @csspart icon - Wrapper around the `icon` slot. Not rendered at all when the slot is empty.
39
60
  * @csspart label - Wrapper around the default slot.
61
+ * @csspart checkmark - The checkmark glyph shown when a `type="checkbox"` item is `checked`. Not rendered at all for `type="normal"`.
40
62
  */
41
63
  export declare class LyraMenuItem extends LyraElement {
42
64
  static styles: import("lit").CSSResultGroup[];
@@ -46,11 +68,18 @@ export declare class LyraMenuItem extends LyraElement {
46
68
  disabled: boolean;
47
69
  /** Visual treatment for a dangerous action (e.g. "Delete") — tints the row with `--lyra-color-danger`. */
48
70
  destructive: boolean;
71
+ /** `'checkbox'` renders `role="menuitemcheckbox"` with a toggleable `checked` state and a
72
+ * checkmark glyph, mirroring `wa-dropdown-item`'s identical `type` option — see the class doc. */
73
+ type: MenuItemType;
74
+ /** Whether a `type="checkbox"` item is checked. Meaningless (ignored) for `type="normal"`. */
75
+ checked: boolean;
49
76
  private hasIconSlot;
50
77
  connectedCallback(): void;
51
- protected willUpdate(): void;
78
+ protected willUpdate(changed: PropertyValues): void;
52
79
  /** Fires `lyra-menu-item-select` (no-op while `disabled`). Called by this element's own
53
- * click handler, and by `<lyra-menu>`'s Enter/Space keydown handling of the active item. */
80
+ * click handler, and by `<lyra-menu>`'s Enter/Space keydown handling of the active item.
81
+ * For `type="checkbox"`, also toggles `checked` and fires `lyra-menu-item-change` first --
82
+ * see the class doc. */
54
83
  select(): void;
55
84
  private onIconSlotChange;
56
85
  render(): TemplateResult;
@@ -4,11 +4,37 @@ 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, svg } from 'lit';
8
8
  import { property, state } from 'lit/decorators.js';
9
9
  import { LyraElement } from '../../internal/lyra-element.js';
10
10
  import { defineElement } from '../../internal/prefix.js';
11
11
  import { styles } from './menu-item.styles.js';
12
+ // Mirrors the shared icon set's viewBox/stroke conventions
13
+ // (internal/icons.ts's chevronIcon()/closeIcon()/etc.) without adding a
14
+ // checkmark glyph to that module -- it's off limits here -- so this one-off
15
+ // icon still reads as part of the same visual language as the rest of the
16
+ // library's inline icons. Same approach lyra-checkbox's own local
17
+ // checkmark/indeterminate glyphs (and lyra-chat-message's local retryIcon())
18
+ // take for the identical reason.
19
+ const GLYPH_VIEW_BOX = '0 0 24 24';
20
+ const GLYPH_STROKE_WIDTH = '1.75';
21
+ function checkmarkGlyph() {
22
+ return svg `
23
+ <svg
24
+ part="checkmark"
25
+ width="1em"
26
+ height="1em"
27
+ viewBox=${GLYPH_VIEW_BOX}
28
+ fill="none"
29
+ stroke="currentColor"
30
+ stroke-width=${GLYPH_STROKE_WIDTH}
31
+ stroke-linecap="round"
32
+ stroke-linejoin="round"
33
+ aria-hidden="true"
34
+ focusable="false"
35
+ ><polyline points="5 12.5 10 17.5 19 6.5"></polyline></svg>
36
+ `;
37
+ }
12
38
  /**
13
39
  * `<lyra-menu-item>` — a single action row inside `<lyra-menu>`'s default
14
40
  * slot. Not meaningful on its own (there is no standalone "click a menu item"
@@ -32,6 +58,18 @@ import { styles } from './menu-item.styles.js';
32
58
  * itself, so `select()` fires identically whether the item was reached by
33
59
  * mouse or keyboard.
34
60
  *
61
+ * `type="checkbox"` (mirroring `wa-dropdown-item`'s identical `type` option)
62
+ * renders `role="menuitemcheckbox"` in place of `role="menuitem"`, with
63
+ * `aria-checked` reflecting `checked` and a checkmark glyph shown once
64
+ * `checked` is `true`. Activating a `checkbox`-type item (click, or the
65
+ * parent's Enter/Space handling via `select()`) toggles `checked` and fires
66
+ * `lyra-menu-item-change` *in addition to* the usual `lyra-menu-item-select`
67
+ * — the latter is never suppressed, so a parent `<lyra-menu>` still closes
68
+ * and re-fires its consolidated `lyra-menu-select` exactly as it does for a
69
+ * `type="normal"` item. `type="normal"` (the default) renders and behaves
70
+ * exactly as before this option existed — no role, rendering, or event
71
+ * differences.
72
+ *
35
73
  * @customElement lyra-menu-item
36
74
  * @slot - The item's label content.
37
75
  * @slot icon - Optional leading icon.
@@ -43,9 +81,14 @@ import { styles } from './menu-item.styles.js';
43
81
  * (`detail: { value }`) rather than requiring a consumer to listen on every
44
82
  * individual item — listen there instead unless you specifically need a
45
83
  * per-item handler.
84
+ * @event lyra-menu-item-change - A `type="checkbox"` item was activated and
85
+ * its `checked` state toggled. `detail: { value, checked }` — the item's own
86
+ * `value` and its new `checked` value. Fired in addition to (never instead
87
+ * of) `lyra-menu-item-select` above. Never fired for `type="normal"`.
46
88
  * @csspart base - The row (`role` lives on the host — see the class doc).
47
89
  * @csspart icon - Wrapper around the `icon` slot. Not rendered at all when the slot is empty.
48
90
  * @csspart label - Wrapper around the default slot.
91
+ * @csspart checkmark - The checkmark glyph shown when a `type="checkbox"` item is `checked`. Not rendered at all for `type="normal"`.
49
92
  */
50
93
  export class LyraMenuItem extends LyraElement {
51
94
  constructor() {
@@ -56,6 +99,11 @@ export class LyraMenuItem extends LyraElement {
56
99
  this.disabled = false;
57
100
  /** Visual treatment for a dangerous action (e.g. "Delete") — tints the row with `--lyra-color-danger`. */
58
101
  this.destructive = false;
102
+ /** `'checkbox'` renders `role="menuitemcheckbox"` with a toggleable `checked` state and a
103
+ * checkmark glyph, mirroring `wa-dropdown-item`'s identical `type` option — see the class doc. */
104
+ this.type = 'normal';
105
+ /** Whether a `type="checkbox"` item is checked. Meaningless (ignored) for `type="normal"`. */
106
+ this.checked = false;
59
107
  // [part='icon'] never matches a bare :empty selector -- see menu-item.styles.ts's
60
108
  // own comment on that part. Same fix as lyra-tool-call-chip's hasDetailSlot.
61
109
  this.hasIconSlot = false;
@@ -74,23 +122,55 @@ export class LyraMenuItem extends LyraElement {
74
122
  if (this.tabIndex !== 0)
75
123
  this.tabIndex = -1;
76
124
  }
77
- willUpdate() {
78
- // role/aria-disabled live on the host (see the class doc), so they're
79
- // plain imperative attribute writes here rather than part of render()'s
80
- // shadow-DOM template -- mirrors lyra-tree-node's identical willUpdate.
81
- this.setAttribute('role', 'menuitem');
125
+ willUpdate(changed) {
126
+ // role/aria-disabled/aria-checked live on the host (see the class doc),
127
+ // so they're plain imperative attribute writes here rather than part of
128
+ // render()'s shadow-DOM template -- mirrors lyra-tree-node's identical
129
+ // willUpdate.
130
+ const isCheckbox = this.type === 'checkbox';
131
+ this.setAttribute('role', isCheckbox ? 'menuitemcheckbox' : 'menuitem');
132
+ if (isCheckbox) {
133
+ this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
134
+ }
135
+ else {
136
+ // Kept absent entirely for type="normal" -- see the class doc's "no
137
+ // role, rendering, or event differences" guarantee for that default.
138
+ this.removeAttribute('aria-checked');
139
+ }
82
140
  if (this.disabled) {
83
141
  this.setAttribute('aria-disabled', 'true');
142
+ // Defense-in-depth mirroring connectedCallback's baseline above:
143
+ // <lyra-menu>'s roving-tabindex bookkeeping (activeIndex) only gets a
144
+ // chance to resync once real focus actually moves (via its own
145
+ // focusin listener), so a `disabled` flip must proactively strip this
146
+ // item out of the roving target and drop any focus it's currently
147
+ // holding right here -- regardless of what the parent's activeIndex
148
+ // still thinks -- so a disabled item can never remain the roving
149
+ // target or retain focus.
150
+ if (changed.has('disabled')) {
151
+ this.tabIndex = -1;
152
+ if (document.activeElement === this)
153
+ this.blur();
154
+ }
84
155
  }
85
156
  else {
86
157
  this.removeAttribute('aria-disabled');
87
158
  }
159
+ if (changed.has('disabled')) {
160
+ this.emit('lyra-menu-item-state-change', { disabled: this.disabled, hidden: this.hidden });
161
+ }
88
162
  }
89
163
  /** Fires `lyra-menu-item-select` (no-op while `disabled`). Called by this element's own
90
- * click handler, and by `<lyra-menu>`'s Enter/Space keydown handling of the active item. */
164
+ * click handler, and by `<lyra-menu>`'s Enter/Space keydown handling of the active item.
165
+ * For `type="checkbox"`, also toggles `checked` and fires `lyra-menu-item-change` first --
166
+ * see the class doc. */
91
167
  select() {
92
168
  if (this.disabled)
93
169
  return;
170
+ if (this.type === 'checkbox') {
171
+ this.checked = !this.checked;
172
+ this.emit('lyra-menu-item-change', { value: this.value, checked: this.checked });
173
+ }
94
174
  this.emit('lyra-menu-item-select');
95
175
  }
96
176
  render() {
@@ -100,6 +180,7 @@ export class LyraMenuItem extends LyraElement {
100
180
  <slot name="icon" @slotchange=${this.onIconSlotChange}></slot>
101
181
  </span>
102
182
  <span part="label"><slot></slot></span>
183
+ ${this.type === 'checkbox' && this.checked ? checkmarkGlyph() : nothing}
103
184
  </span>
104
185
  `;
105
186
  }
@@ -113,6 +194,12 @@ __decorate([
113
194
  __decorate([
114
195
  property({ type: Boolean, reflect: true })
115
196
  ], LyraMenuItem.prototype, "destructive", void 0);
197
+ __decorate([
198
+ property()
199
+ ], LyraMenuItem.prototype, "type", void 0);
200
+ __decorate([
201
+ property({ type: Boolean, reflect: true })
202
+ ], LyraMenuItem.prototype, "checked", void 0);
116
203
  __decorate([
117
204
  state()
118
205
  ], LyraMenuItem.prototype, "hasIconSlot", void 0);
@@ -64,4 +64,12 @@ export const styles = css `
64
64
  text-overflow: ellipsis;
65
65
  white-space: nowrap;
66
66
  }
67
+ /* Only ever present in the DOM at all for a checked type="checkbox" item
68
+ (see menu-item.ts's render()) -- no [hidden]-toggling needed, unlike
69
+ [part='icon'] above, since there's no always-present <slot> child here
70
+ to keep visually empty in between. */
71
+ [part='checkmark'] {
72
+ flex: 0 0 auto;
73
+ color: var(--lyra-color-brand);
74
+ }
67
75
  `;
@@ -42,7 +42,10 @@ export interface MenuSelectDetail {
42
42
  * Home/End jump to the first/last non-disabled item. Enter/Space activate
43
43
  * the focused item. Escape closes and returns focus to the trigger. Tab
44
44
  * closes the menu without trapping focus (the browser's own default Tab
45
- * behavior proceeds untouched).
45
+ * behavior proceeds untouched). A printable keypress runs type-ahead:
46
+ * roving focus jumps to the next non-disabled item whose text starts with
47
+ * the accumulated buffer, cycling from just after the active item (mirrors
48
+ * `<lyra-select>`'s identical listbox type-ahead).
46
49
  * - A click outside both the trigger and the open popup closes it (mirrors
47
50
  * `<lyra-select>`'s `onDocPointer` exactly) — this does *not* refocus the
48
51
  * trigger, since the outside click itself already moved focus somewhere
@@ -102,9 +105,12 @@ export declare class LyraMenu extends LyraElement {
102
105
  private activeIndex;
103
106
  private triggerEl?;
104
107
  private cleanup?;
108
+ private itemStateObserver?;
105
109
  private _isFirstUpdate;
106
110
  private pendingFocus;
107
111
  private readonly listId;
112
+ private typeAheadBuffer;
113
+ private typeAheadTimer?;
108
114
  protected willUpdate(): void;
109
115
  protected updated(changed: PropertyValues): void;
110
116
  private reposition;
@@ -123,6 +129,9 @@ export declare class LyraMenu extends LyraElement {
123
129
  * this component's own shadow root -- see the class doc. */
124
130
  private syncTriggerA11y;
125
131
  private onItemsSlotChange;
132
+ private isNavigable;
133
+ /** Rehomes roving focus immediately when an active item becomes disabled or hidden. */
134
+ private onItemStateChange;
126
135
  private onItemSelect;
127
136
  /** Flips exactly one non-disabled item's `tabIndex` to `0` (the roving
128
137
  * target) and every other item's to `-1` -- see `<lyra-menu-item>`'s doc
@@ -133,7 +142,22 @@ export declare class LyraMenu extends LyraElement {
133
142
  * stays on the trigger (see onTriggerKeyDown's Escape safety net). */
134
143
  private focusRoving;
135
144
  private setActiveItem;
145
+ /** Resyncs `activeIndex` (and the roving `tabindex`) to wherever real DOM
146
+ * focus actually lands, for any path that doesn't go through
147
+ * `setActiveItem()` -- e.g. a real mousedown on an item, which focuses it
148
+ * even while `disabled` (`tabIndex="-1"` remains mouse-focusable per
149
+ * spec). Without this, `activeIndex` goes stale the moment focus moves any
150
+ * other way, and subsequent Arrow-key navigation computes its next item
151
+ * from that stale position instead of from where focus actually is. A
152
+ * no-op for `setActiveItem()`'s own `.focus()` call, since `activeIndex`
153
+ * there is already set to match before focus moves. */
154
+ private onListFocusIn;
136
155
  private onListKeyDown;
156
+ /** Standard WAI-ARIA APG menu-button type-ahead: moves the roving focus to
157
+ * the next non-disabled item whose text content starts with the
158
+ * accumulated buffer, cycling from just after the currently active item
159
+ * -- mirrors `<lyra-select>`'s identical listbox type-ahead. */
160
+ private typeAhead;
137
161
  render(): TemplateResult;
138
162
  }
139
163
  declare global {