@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
@@ -0,0 +1,523 @@
1
+ const FOCUSABLE_SELECTOR = [
2
+ 'a[href]',
3
+ 'area[href]',
4
+ 'audio[controls]',
5
+ 'button:not([disabled])',
6
+ 'iframe',
7
+ 'input:not([disabled]):not([type="hidden"])',
8
+ 'select:not([disabled])',
9
+ 'summary',
10
+ 'textarea:not([disabled])',
11
+ 'video[controls]',
12
+ '[contenteditable]:not([contenteditable="false"])',
13
+ '[tabindex]',
14
+ ].join(', ');
15
+ const STACK_PROPERTY = '--lyra-overlay-stack-index';
16
+ const STACK_BASE = 1000;
17
+ const STACK_STEP = 2;
18
+ const states = new WeakMap();
19
+ const hostEntries = new WeakMap();
20
+ /** Returns the deepest focused descendant across open shadow roots. */
21
+ export function deepActiveElement(doc = document) {
22
+ let active = doc.activeElement;
23
+ while (active) {
24
+ const inner = active.shadowRoot?.activeElement ?? null;
25
+ if (!inner)
26
+ break;
27
+ active = inner;
28
+ }
29
+ return active;
30
+ }
31
+ function isSlot(element) {
32
+ return element.localName === 'slot' && typeof element.assignedElements === 'function';
33
+ }
34
+ function composedParent(element) {
35
+ if (element.assignedSlot)
36
+ return element.assignedSlot;
37
+ if (element.parentElement)
38
+ return element.parentElement;
39
+ const root = element.getRootNode();
40
+ return root.host ?? null;
41
+ }
42
+ export function composedContains(container, candidate) {
43
+ let current = candidate;
44
+ while (current) {
45
+ if (current === container)
46
+ return true;
47
+ current = composedParent(current);
48
+ }
49
+ return false;
50
+ }
51
+ function hasInertAncestor(element) {
52
+ let current = element;
53
+ while (current) {
54
+ if (current.inert)
55
+ return true;
56
+ current = composedParent(current);
57
+ }
58
+ return false;
59
+ }
60
+ function isRendered(element) {
61
+ if (element.hidden || element.getAttribute('aria-hidden') === 'true' || hasInertAncestor(element))
62
+ return false;
63
+ if (element.ownerDocument.defaultView?.getComputedStyle(element).visibility !== 'visible')
64
+ return false;
65
+ return element.checkVisibility ? element.checkVisibility() : element.getClientRects().length > 0;
66
+ }
67
+ function isTabbable(element) {
68
+ if (element.tabIndex < 0 || element.matches(':disabled'))
69
+ return false;
70
+ return isRendered(element);
71
+ }
72
+ function collectFocusable(element, result) {
73
+ if (element.matches(FOCUSABLE_SELECTOR) && isTabbable(element))
74
+ result.push(element);
75
+ if (isSlot(element)) {
76
+ const hasAssignedNodes = element.assignedNodes().length > 0;
77
+ const assigned = element
78
+ .assignedNodes({ flatten: true })
79
+ .filter((node) => node.nodeType === 1);
80
+ const children = hasAssignedNodes ? assigned : Array.from(element.children);
81
+ for (const child of children)
82
+ collectFocusable(child, result);
83
+ return;
84
+ }
85
+ const container = element.shadowRoot ?? element;
86
+ for (const child of Array.from(container.children))
87
+ collectFocusable(child, result);
88
+ }
89
+ /** Collects rendered focus targets through slots and nested open shadow roots in browser tab order. */
90
+ export function collectFocusableElements(root) {
91
+ const result = [];
92
+ if ('matches' in root) {
93
+ collectFocusable(root, result);
94
+ }
95
+ else {
96
+ for (const child of Array.from(root.children))
97
+ collectFocusable(child, result);
98
+ }
99
+ const radioGroups = [];
100
+ for (const element of result) {
101
+ if (element.localName !== 'input')
102
+ continue;
103
+ const input = element;
104
+ if (input.type !== 'radio' || input.name === '')
105
+ continue;
106
+ let group = radioGroups.find((candidate) => candidate.name === input.name && candidate.form === input.form && candidate.root === input.getRootNode());
107
+ if (!group) {
108
+ group = { name: input.name, form: input.form, root: input.getRootNode(), members: [] };
109
+ radioGroups.push(group);
110
+ }
111
+ group.members.push(input);
112
+ }
113
+ const excludedRadios = new Set();
114
+ for (const group of radioGroups) {
115
+ const tabStop = group.members.find((radio) => radio.checked) ?? group.members[0];
116
+ for (const radio of group.members) {
117
+ if (radio !== tabStop)
118
+ excludedRadios.add(radio);
119
+ }
120
+ }
121
+ return result
122
+ .filter((element) => !excludedRadios.has(element))
123
+ .map((element, index) => ({ element, index }))
124
+ .sort((a, b) => {
125
+ const aPositive = a.element.tabIndex > 0;
126
+ const bPositive = b.element.tabIndex > 0;
127
+ if (aPositive !== bPositive)
128
+ return aPositive ? -1 : 1;
129
+ if (aPositive && a.element.tabIndex !== b.element.tabIndex)
130
+ return a.element.tabIndex - b.element.tabIndex;
131
+ return a.index - b.index;
132
+ })
133
+ .map(({ element }) => element);
134
+ }
135
+ function tryFocus(target) {
136
+ if (!target?.isConnected || !isRendered(target) || target.matches(':disabled'))
137
+ return false;
138
+ target.focus();
139
+ const active = deepActiveElement(target.ownerDocument);
140
+ return active === target || composedContains(target, active);
141
+ }
142
+ function focusEntry(entry, backwards = false, preserveCurrent = true) {
143
+ const panel = entry.options.panel();
144
+ if (!panel)
145
+ return;
146
+ const active = deepActiveElement(entry.state.document);
147
+ if (preserveCurrent && composedContains(panel, active))
148
+ return;
149
+ const preferred = entry.options.preferredInitialFocus?.() ?? null;
150
+ if (preferred && composedContains(panel, preferred) && tryFocus(preferred))
151
+ return;
152
+ const focusable = collectFocusableElements(panel);
153
+ const ordered = backwards ? focusable.reverse() : focusable;
154
+ for (const target of ordered) {
155
+ if (tryFocus(target))
156
+ return;
157
+ }
158
+ panel.focus();
159
+ }
160
+ function handleTab(state, entry, event) {
161
+ const panel = entry.options.panel();
162
+ if (!panel)
163
+ return;
164
+ const focusable = collectFocusableElements(panel);
165
+ if (focusable.length === 0) {
166
+ event.preventDefault();
167
+ panel.focus();
168
+ return;
169
+ }
170
+ const active = deepActiveElement(state.document);
171
+ const first = focusable[0];
172
+ const last = focusable[focusable.length - 1];
173
+ const activeIndex = active ? focusable.indexOf(active) : -1;
174
+ if (activeIndex === -1) {
175
+ event.preventDefault();
176
+ (event.shiftKey ? last : first).focus();
177
+ }
178
+ else if (event.shiftKey && active === first) {
179
+ event.preventDefault();
180
+ last.focus();
181
+ }
182
+ else if (!event.shiftKey && active === last) {
183
+ event.preventDefault();
184
+ first.focus();
185
+ }
186
+ }
187
+ function scheduleInertUpdate(state) {
188
+ if (state.inertUpdateQueued)
189
+ return;
190
+ state.inertUpdateQueued = true;
191
+ queueMicrotask(() => {
192
+ state.inertUpdateQueued = false;
193
+ applyTopmostInert(state);
194
+ });
195
+ }
196
+ function handleMutations(state, records) {
197
+ let needsInertUpdate = false;
198
+ for (let index = 0; index < records.length; index++) {
199
+ const record = records[index];
200
+ if (record.type === 'childList') {
201
+ needsInertUpdate = true;
202
+ continue;
203
+ }
204
+ if (record.type !== 'attributes' || record.attributeName !== 'inert')
205
+ continue;
206
+ const element = record.target;
207
+ let nextRecord;
208
+ for (let nextIndex = index + 1; nextIndex < records.length; nextIndex++) {
209
+ const candidate = records[nextIndex];
210
+ if (candidate.type === 'attributes' && candidate.attributeName === 'inert' && candidate.target === element) {
211
+ nextRecord = candidate;
212
+ break;
213
+ }
214
+ }
215
+ const newValue = nextRecord ? nextRecord.oldValue !== null : element.hasAttribute('inert');
216
+ const pending = state.pendingInertWrites.get(element);
217
+ if (pending?.[0] === newValue) {
218
+ pending.shift();
219
+ if (pending.length === 0)
220
+ state.pendingInertWrites.delete(element);
221
+ continue;
222
+ }
223
+ if (state.inerted.has(element)) {
224
+ state.inerted.set(element, newValue);
225
+ needsInertUpdate = true;
226
+ }
227
+ }
228
+ if (needsInertUpdate)
229
+ scheduleInertUpdate(state);
230
+ }
231
+ const mutationObserverOptions = {
232
+ attributeFilter: ['inert'],
233
+ attributeOldValue: true,
234
+ attributes: true,
235
+ childList: true,
236
+ subtree: true,
237
+ };
238
+ function observeMutationRoot(state, root) {
239
+ if (!state.observer || state.observedRoots.has(root))
240
+ return;
241
+ state.observer.observe(root === state.document ? state.document.documentElement : root, mutationObserverOptions);
242
+ state.observedRoots.add(root);
243
+ }
244
+ function startState(state) {
245
+ state.document.addEventListener('keydown', state.onKeyDown);
246
+ const Observer = state.document.defaultView?.MutationObserver ?? MutationObserver;
247
+ state.observer = new Observer((records) => handleMutations(state, records));
248
+ observeMutationRoot(state, state.document);
249
+ }
250
+ function stopState(state) {
251
+ state.document.removeEventListener('keydown', state.onKeyDown);
252
+ state.observer?.disconnect();
253
+ state.observer = undefined;
254
+ state.observedRoots.clear();
255
+ state.pendingInertWrites.clear();
256
+ }
257
+ function createState(doc) {
258
+ const state = {};
259
+ state.document = doc;
260
+ state.stack = [];
261
+ state.nextStackOrder = 0;
262
+ state.inerted = new Map();
263
+ state.pendingInertWrites = new Map();
264
+ state.observedRoots = new Set();
265
+ state.inertUpdateQueued = false;
266
+ state.onKeyDown = (event) => {
267
+ if (event.defaultPrevented || event.isComposing)
268
+ return;
269
+ const entry = state.stack[state.stack.length - 1];
270
+ if (!entry)
271
+ return;
272
+ if (event.key === 'Escape') {
273
+ event.preventDefault();
274
+ entry.options.onEscape();
275
+ }
276
+ else if (event.key === 'Tab') {
277
+ if (entry.options.trapFocus === false)
278
+ entry.options.onTab?.();
279
+ else
280
+ handleTab(state, entry, event);
281
+ }
282
+ };
283
+ states.set(doc, state);
284
+ return state;
285
+ }
286
+ function stateFor(doc) {
287
+ return states.get(doc) ?? createState(doc);
288
+ }
289
+ function setInertByManager(state, element, value) {
290
+ if (element.inert === value)
291
+ return;
292
+ if (state.observer && state.observedRoots.has(element.getRootNode())) {
293
+ let pending = state.pendingInertWrites.get(element);
294
+ if (!pending) {
295
+ pending = [];
296
+ state.pendingInertWrites.set(element, pending);
297
+ }
298
+ pending.push(value);
299
+ }
300
+ else {
301
+ // No mutation record will be delivered for a detached element or for an
302
+ // element in a shadow root outside this state's observed document tree.
303
+ state.pendingInertWrites.delete(element);
304
+ }
305
+ element.inert = value;
306
+ }
307
+ function inertElement(state, element, desired) {
308
+ if (!('inert' in element))
309
+ return;
310
+ const htmlElement = element;
311
+ desired.add(htmlElement);
312
+ if (!state.inerted.has(htmlElement))
313
+ state.inerted.set(htmlElement, htmlElement.inert);
314
+ setInertByManager(state, htmlElement, true);
315
+ }
316
+ function pathParent(element) {
317
+ return element.assignedSlot ?? element.parentNode;
318
+ }
319
+ function parentPathElement(parent) {
320
+ if (parent.nodeType === Node.ELEMENT_NODE)
321
+ return parent;
322
+ return parent.host ?? null;
323
+ }
324
+ function composedChildren(parent) {
325
+ if (parent.localName === 'slot') {
326
+ return parent.assignedElements({ flatten: true });
327
+ }
328
+ return 'children' in parent ? Array.from(parent.children) : [];
329
+ }
330
+ function isShadowRoot(node) {
331
+ return node.nodeType === 11 && 'host' in node;
332
+ }
333
+ function addAllowedPath(allowed, host, doc) {
334
+ let current = host;
335
+ while (current && current !== doc.body && current !== doc.documentElement) {
336
+ const parent = pathParent(current);
337
+ if (!parent)
338
+ break;
339
+ let children = allowed.get(parent);
340
+ if (!children) {
341
+ children = new Set();
342
+ allowed.set(parent, children);
343
+ }
344
+ children.add(current);
345
+ current = parentPathElement(parent);
346
+ }
347
+ }
348
+ function applyTopmostInert(state) {
349
+ let modalIndex = -1;
350
+ for (let index = state.stack.length - 1; index >= 0; index--) {
351
+ if (state.stack[index].options.modal !== false) {
352
+ modalIndex = index;
353
+ break;
354
+ }
355
+ }
356
+ const desired = new Set();
357
+ if (modalIndex !== -1) {
358
+ const allowed = new Map();
359
+ for (const entry of state.stack.slice(modalIndex)) {
360
+ if (entry.options.host.isConnected)
361
+ addAllowedPath(allowed, entry.options.host, state.document);
362
+ }
363
+ for (const [parent, children] of allowed) {
364
+ if (isShadowRoot(parent))
365
+ observeMutationRoot(state, parent);
366
+ for (const child of composedChildren(parent)) {
367
+ if (!children.has(child))
368
+ inertElement(state, child, desired);
369
+ }
370
+ }
371
+ }
372
+ for (const [element, intended] of state.inerted) {
373
+ if (desired.has(element))
374
+ continue;
375
+ setInertByManager(state, element, intended);
376
+ state.inerted.delete(element);
377
+ }
378
+ }
379
+ function restoreStackStyle(entry) {
380
+ if (entry.previousStackValue) {
381
+ entry.options.host.style.setProperty(STACK_PROPERTY, entry.previousStackValue, entry.previousStackPriority);
382
+ }
383
+ else {
384
+ entry.options.host.style.removeProperty(STACK_PROPERTY);
385
+ }
386
+ }
387
+ function updateStackStyles(state) {
388
+ state.stack.forEach((entry, index) => {
389
+ entry.options.host.style.setProperty(STACK_PROPERTY, String(STACK_BASE + index * STACK_STEP));
390
+ });
391
+ }
392
+ function registerEntry(entry, state, preserveStackOrder = false) {
393
+ if (state.stack.length === 0)
394
+ startState(state);
395
+ if (!preserveStackOrder || entry.state !== state)
396
+ entry.stackOrder = state.nextStackOrder++;
397
+ entry.state = state;
398
+ entry.registered = true;
399
+ const nextIndex = state.stack.findIndex((candidate) => candidate.stackOrder > entry.stackOrder);
400
+ if (nextIndex === -1)
401
+ state.stack.push(entry);
402
+ else
403
+ state.stack.splice(nextIndex, 0, entry);
404
+ updateStackStyles(state);
405
+ applyTopmostInert(state);
406
+ }
407
+ function unregisterEntry(entry) {
408
+ if (!entry.registered)
409
+ return entry.wasTopmostOnSuspend;
410
+ const state = entry.state;
411
+ const index = state.stack.indexOf(entry);
412
+ const wasTopmost = index === state.stack.length - 1;
413
+ if (index !== -1)
414
+ state.stack.splice(index, 1);
415
+ entry.registered = false;
416
+ entry.wasTopmostOnSuspend = wasTopmost;
417
+ restoreStackStyle(entry);
418
+ updateStackStyles(state);
419
+ applyTopmostInert(state);
420
+ if (state.stack.length === 0)
421
+ stopState(state);
422
+ return wasTopmost;
423
+ }
424
+ function rebaseReturnTargets(entry) {
425
+ for (const candidate of entry.state.stack) {
426
+ if (candidate === entry || candidate.stackOrder <= entry.stackOrder)
427
+ continue;
428
+ if (candidate.restoreFocusTo && composedContains(entry.options.host, candidate.restoreFocusTo)) {
429
+ candidate.restoreFocusTo = entry.restoreFocusTo;
430
+ }
431
+ }
432
+ }
433
+ function restoreEntryFocus(entry) {
434
+ if (tryFocus(entry.restoreFocusTo))
435
+ return;
436
+ const next = entry.state.stack[entry.state.stack.length - 1];
437
+ if (next)
438
+ focusEntry(next, false, false);
439
+ }
440
+ function deactivateEntry(entry, restoreFocus) {
441
+ if (!entry.active)
442
+ return;
443
+ rebaseReturnTargets(entry);
444
+ const wasTopmost = unregisterEntry(entry);
445
+ entry.active = false;
446
+ entry.suspendGeneration++;
447
+ if (hostEntries.get(entry.options.host) === entry)
448
+ hostEntries.delete(entry.options.host);
449
+ if (!wasTopmost)
450
+ return;
451
+ if (restoreFocus) {
452
+ restoreEntryFocus(entry);
453
+ return;
454
+ }
455
+ const next = entry.state.stack[entry.state.stack.length - 1];
456
+ if (next)
457
+ focusEntry(next);
458
+ }
459
+ /**
460
+ * Adds an overlay to a stack scoped to its own `ownerDocument`. One document
461
+ * listener routes Escape and Tab only to the top entry, while modal inerting,
462
+ * visual stack depth, and focus return are recomputed as entries move.
463
+ */
464
+ export function activateOverlay(options) {
465
+ const previous = hostEntries.get(options.host);
466
+ const inheritedReturnTarget = previous?.active ? previous.restoreFocusTo : undefined;
467
+ if (previous?.active)
468
+ previous.handle.deactivate({ restoreFocus: false });
469
+ const doc = options.host.ownerDocument;
470
+ const active = deepActiveElement(doc);
471
+ const captured = active && typeof active.focus === 'function' ? active : null;
472
+ const entry = {};
473
+ entry.options = options;
474
+ entry.restoreFocusTo =
475
+ options.restoreFocusTo !== undefined ? options.restoreFocusTo : (inheritedReturnTarget ?? captured);
476
+ entry.active = true;
477
+ entry.registered = false;
478
+ entry.wasTopmostOnSuspend = false;
479
+ entry.suspendGeneration = 0;
480
+ entry.state = stateFor(doc);
481
+ entry.stackOrder = -1;
482
+ entry.previousStackValue = options.host.style.getPropertyValue(STACK_PROPERTY);
483
+ entry.previousStackPriority = options.host.style.getPropertyPriority(STACK_PROPERTY);
484
+ entry.handle = {
485
+ focusInitial: () => {
486
+ if (entry.active && entry.registered && entry.state.stack[entry.state.stack.length - 1] === entry) {
487
+ focusEntry(entry);
488
+ }
489
+ },
490
+ deactivate: (deactivateOptions = {}) => {
491
+ deactivateEntry(entry, deactivateOptions.restoreFocus !== false);
492
+ },
493
+ suspend: () => {
494
+ if (!entry.active || !entry.registered)
495
+ return;
496
+ unregisterEntry(entry);
497
+ const generation = ++entry.suspendGeneration;
498
+ queueMicrotask(() => {
499
+ if (entry.active && !entry.registered && entry.suspendGeneration === generation && !entry.options.host.isConnected) {
500
+ deactivateEntry(entry, true);
501
+ }
502
+ });
503
+ },
504
+ resume: () => {
505
+ if (!entry.active || entry.registered)
506
+ return;
507
+ entry.suspendGeneration++;
508
+ const state = stateFor(entry.options.host.ownerDocument);
509
+ registerEntry(entry, state, entry.state === state);
510
+ },
511
+ isTopmost: () => entry.active && entry.registered && entry.state.stack[entry.state.stack.length - 1] === entry,
512
+ isActive: () => entry.active,
513
+ dismissBackdrop: () => {
514
+ if (!entry.handle.isTopmost() || !entry.options.onBackdrop)
515
+ return false;
516
+ entry.options.onBackdrop();
517
+ return true;
518
+ },
519
+ };
520
+ hostEntries.set(options.host, entry);
521
+ registerEntry(entry, entry.state);
522
+ return entry.handle;
523
+ }
@@ -1,4 +1,4 @@
1
- import { computePosition, autoUpdate, flip, shift, offset, } from '@floating-ui/dom';
1
+ import { computePosition, autoUpdate, flip, shift, offset, size, } from '@floating-ui/dom';
2
2
  /**
3
3
  * Position `popup` (fixed) relative to `anchor` with flip/shift, keeping it
4
4
  * updated on scroll/resize. Returns a cleanup function that stops updating.
@@ -12,10 +12,34 @@ export function place(anchor, popup, opts = {}) {
12
12
  // the scroll offset (appears too far down on a scrolled page).
13
13
  strategy: 'fixed',
14
14
  placement: opts.placement ?? 'bottom-start',
15
- middleware: [offset(opts.offset ?? 4), flip(), shift({ padding: 8 })],
15
+ middleware: [
16
+ offset(opts.offset ?? 4),
17
+ flip(),
18
+ shift({ padding: 8 }),
19
+ size({
20
+ padding: 8,
21
+ apply({ availableWidth, availableHeight, elements }) {
22
+ elements.floating.style.setProperty('--lyra-positioner-available-inline-size', `${Math.max(0, availableWidth)}px`);
23
+ elements.floating.style.setProperty('--lyra-positioner-available-block-size', `${Math.max(0, availableHeight)}px`);
24
+ },
25
+ }),
26
+ ],
16
27
  }).then(({ x, y }) => {
17
28
  popup.style.left = `${x}px`;
18
29
  popup.style.top = `${y}px`;
19
30
  });
20
- return autoUpdate(anchor, popup, update);
31
+ const stopAutoUpdate = autoUpdate(anchor, popup, update);
32
+ // The visual viewport changes independently of the layout viewport when a
33
+ // mobile on-screen keyboard opens or closes. Floating UI's normal window
34
+ // resize listener does not receive those events, so keep the available-size
35
+ // CSS variables and fixed coordinates in sync with the visual viewport too.
36
+ const visualViewport = anchor.ownerDocument.defaultView?.visualViewport;
37
+ const updateFromVisualViewport = () => void update();
38
+ visualViewport?.addEventListener('resize', updateFromVisualViewport);
39
+ visualViewport?.addEventListener('scroll', updateFromVisualViewport);
40
+ return () => {
41
+ stopAutoUpdate();
42
+ visualViewport?.removeEventListener('resize', updateFromVisualViewport);
43
+ visualViewport?.removeEventListener('scroll', updateFromVisualViewport);
44
+ };
21
45
  }
@@ -0,0 +1,12 @@
1
+ /** Returns a trimmed URL safe for an `<img>`/`<video>` `src`, or `null`.
2
+ * Relative URLs and `http:`, `https:`, `blob:`, and `data:` are allowed. The
3
+ * platform parser performs the same control-character normalization as DOM
4
+ * URL sinks, preventing obfuscated schemes such as `java\tscript:`. */
5
+ export declare function safeMediaSrc(url: unknown): string | null;
6
+ /** Returns a trimmed URL safe to pass to `fetch()`, or `null`. This shares the
7
+ * resource allowlist with media sources so text/image `data:` URLs remain
8
+ * usable, while navigation-only and executable schemes are rejected. */
9
+ export declare function safeFetchUrl(url: unknown): string | null;
10
+ /** Returns a trimmed URL safe for an `<a href>`, or `null`. `data:` is
11
+ * intentionally excluded because following it can open an active document. */
12
+ export declare function safeLinkHref(url: unknown): string | null;
@@ -0,0 +1,41 @@
1
+ // URL safety is sink-specific. A `data:` URL is inert when used as media or
2
+ // fetched as data, but navigating an anchor to `data:text/html,...` creates an
3
+ // active document. Keep the link allowlist deliberately narrower.
4
+ const SAFE_RESOURCE_SCHEMES = new Set(['http:', 'https:', 'blob:', 'data:']);
5
+ const SAFE_LINK_SCHEMES = new Set(['http:', 'https:', 'blob:']);
6
+ // A fixed web base makes relative and scheme-relative inputs parse through the
7
+ // same WHATWG URL algorithm as absolute inputs. This also means malformed
8
+ // absolute URLs are rejected instead of being mistaken for relative paths.
9
+ const PARSE_BASE = 'https://lyra.invalid/';
10
+ function safeUrlOrNull(url, allowedSchemes) {
11
+ if (typeof url !== 'string')
12
+ return null;
13
+ const trimmed = url.trim();
14
+ if (trimmed === '')
15
+ return null;
16
+ try {
17
+ const protocol = new URL(trimmed, PARSE_BASE).protocol;
18
+ return allowedSchemes.has(protocol) ? trimmed : null;
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ }
24
+ /** Returns a trimmed URL safe for an `<img>`/`<video>` `src`, or `null`.
25
+ * Relative URLs and `http:`, `https:`, `blob:`, and `data:` are allowed. The
26
+ * platform parser performs the same control-character normalization as DOM
27
+ * URL sinks, preventing obfuscated schemes such as `java\tscript:`. */
28
+ export function safeMediaSrc(url) {
29
+ return safeUrlOrNull(url, SAFE_RESOURCE_SCHEMES);
30
+ }
31
+ /** Returns a trimmed URL safe to pass to `fetch()`, or `null`. This shares the
32
+ * resource allowlist with media sources so text/image `data:` URLs remain
33
+ * usable, while navigation-only and executable schemes are rejected. */
34
+ export function safeFetchUrl(url) {
35
+ return safeUrlOrNull(url, SAFE_RESOURCE_SCHEMES);
36
+ }
37
+ /** Returns a trimmed URL safe for an `<a href>`, or `null`. `data:` is
38
+ * intentionally excluded because following it can open an active document. */
39
+ export function safeLinkHref(url) {
40
+ return safeUrlOrNull(url, SAFE_LINK_SCHEMES);
41
+ }
@@ -6,7 +6,7 @@ export const tokens = css `
6
6
  --lyra-color-surface: var(--wa-color-surface-default, #fff);
7
7
  --lyra-color-text: var(--wa-color-text-normal, #1a1a1a);
8
8
  --lyra-color-text-quiet: var(--wa-color-text-quiet, #6b7280);
9
- --lyra-color-border: var(--wa-color-neutral-fill-normal, #8a8a90);
9
+ --lyra-color-border: var(--wa-color-surface-border, #8a8a90);
10
10
  --lyra-color-brand: var(--wa-color-brand-fill-loud, #0969da);
11
11
  --lyra-color-brand-quiet: var(--wa-color-brand-fill-quiet, #ddf4ff);
12
12
  --lyra-color-success: var(--wa-color-success-fill-loud, #1a7f37);
@@ -15,11 +15,13 @@ export const tokens = css `
15
15
  --lyra-color-warning-quiet: var(--wa-color-warning-fill-quiet, #fff8c5);
16
16
  --lyra-color-danger: var(--wa-color-danger-fill-loud, #cf222e);
17
17
  --lyra-color-danger-quiet: var(--wa-color-danger-fill-quiet, #ffebe9);
18
- /* Text/icon color for content painted on top of a solid brand-fill
19
- background (selected calendar days, filled badges, etc.) -- almost
20
- always white on both the light and dark brand fills above, so it's
21
- not itself light/dark-mode-swapped like the other tokens here. */
22
- --lyra-color-on-brand: var(--wa-color-on-brand, #fff);
18
+ /* Text/icon colors paired with each solid semantic fill. These are
19
+ separate tokens because a theme can choose different foregrounds per
20
+ tone, and the bright standalone dark fills require dark content. */
21
+ --lyra-color-on-brand: var(--wa-color-brand-on-loud, #fff);
22
+ --lyra-color-on-success: var(--wa-color-success-on-loud, #fff);
23
+ --lyra-color-on-warning: var(--wa-color-warning-on-loud, #fff);
24
+ --lyra-color-on-danger: var(--wa-color-danger-on-loud, #fff);
23
25
  --lyra-space-xs: var(--wa-space-xs, 0.25rem);
24
26
  --lyra-space-s: var(--wa-space-s, 0.5rem);
25
27
  --lyra-space-m: var(--wa-space-m, 0.75rem);
@@ -41,7 +43,7 @@ export const tokens = css `
41
43
  /* Focus ring — every :focus-visible rule in the library should reference
42
44
  these three instead of hardcoding its own width/color/offset. */
43
45
  --lyra-focus-ring-width: 2px;
44
- --lyra-focus-ring-color: var(--lyra-color-brand);
46
+ --lyra-focus-ring-color: var(--wa-color-focus, var(--lyra-color-brand));
45
47
  --lyra-focus-ring-offset: 2px;
46
48
 
47
49
  /* Minimum tappable box for an icon-only button (close/dismiss/nav
@@ -64,15 +66,19 @@ export const tokens = css `
64
66
  --lyra-color-surface: var(--wa-color-surface-default, #1a1a1a);
65
67
  --lyra-color-text: var(--wa-color-text-normal, #f2f2f2);
66
68
  --lyra-color-text-quiet: var(--wa-color-text-quiet, #9aa1ac);
67
- --lyra-color-border: var(--wa-color-neutral-fill-normal, #4a4a52);
68
- --lyra-color-brand: var(--wa-color-brand-fill-loud, #4d9fef);
69
+ --lyra-color-border: var(--wa-color-surface-border, #6b6b74);
70
+ --lyra-color-brand: var(--wa-color-brand-fill-loud, #4ea0f0);
69
71
  --lyra-color-brand-quiet: var(--wa-color-brand-fill-quiet, #163650);
70
72
  --lyra-color-success: var(--wa-color-success-fill-loud, #3fb950);
71
- --lyra-color-success-quiet: var(--wa-color-success-fill-quiet, #1b4721);
73
+ --lyra-color-success-quiet: var(--wa-color-success-fill-quiet, #17411e);
72
74
  --lyra-color-warning: var(--wa-color-warning-fill-loud, #d29922);
73
75
  --lyra-color-warning-quiet: var(--wa-color-warning-fill-quiet, #3b2900);
74
- --lyra-color-danger: var(--wa-color-danger-fill-loud, #f85149);
76
+ --lyra-color-danger: var(--wa-color-danger-fill-loud, #fa524a);
75
77
  --lyra-color-danger-quiet: var(--wa-color-danger-fill-quiet, #4c1210);
78
+ --lyra-color-on-brand: var(--wa-color-brand-on-loud, #111827);
79
+ --lyra-color-on-success: var(--wa-color-success-on-loud, #111827);
80
+ --lyra-color-on-warning: var(--wa-color-warning-on-loud, #111827);
81
+ --lyra-color-on-danger: var(--wa-color-danger-on-loud, #111827);
76
82
  }
77
83
  }
78
84
  :host([hidden]) {