@aceshooting/lyra-ui 1.2.0 → 2.0.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 (330) hide show
  1. package/README.md +11 -0
  2. package/custom-elements.json +23341 -11202
  3. package/dist/components/app-rail/app-rail.d.ts +171 -0
  4. package/dist/components/app-rail/app-rail.js +483 -0
  5. package/dist/components/app-rail/app-rail.styles.d.ts +1 -0
  6. package/dist/components/app-rail/app-rail.styles.js +125 -0
  7. package/dist/components/attachment-chip/attachment-chip.d.ts +114 -0
  8. package/dist/components/attachment-chip/attachment-chip.js +312 -0
  9. package/dist/components/attachment-chip/attachment-chip.styles.d.ts +1 -0
  10. package/dist/components/attachment-chip/attachment-chip.styles.js +184 -0
  11. package/dist/components/attachment-trigger/attachment-trigger.d.ts +83 -0
  12. package/dist/components/attachment-trigger/attachment-trigger.js +258 -0
  13. package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts +1 -0
  14. package/dist/components/attachment-trigger/attachment-trigger.styles.js +68 -0
  15. package/dist/components/chart/box-plot.js +11 -1
  16. package/dist/components/chart/chart-loader.d.ts +44 -13
  17. package/dist/components/chart/chart-loader.js +65 -18
  18. package/dist/components/chart/chart.d.ts +1 -2
  19. package/dist/components/chart/chart.js +67 -12
  20. package/dist/components/chart/histogram-bin.d.ts +4 -1
  21. package/dist/components/chart/histogram-bin.js +24 -14
  22. package/dist/components/chart/lite-chart.d.ts +54 -0
  23. package/dist/components/chart/lite-chart.js +140 -22
  24. package/dist/components/chart/lite-chart.styles.js +9 -0
  25. package/dist/components/chat-composer/chat-composer.d.ts +92 -0
  26. package/dist/components/chat-composer/chat-composer.js +285 -0
  27. package/dist/components/chat-composer/chat-composer.styles.d.ts +1 -0
  28. package/dist/components/chat-composer/chat-composer.styles.js +135 -0
  29. package/dist/components/chat-message/chat-message.d.ts +113 -0
  30. package/dist/components/chat-message/chat-message.js +291 -0
  31. package/dist/components/chat-message/chat-message.styles.d.ts +1 -0
  32. package/dist/components/chat-message/chat-message.styles.js +203 -0
  33. package/dist/components/checkbox/checkbox.d.ts +59 -0
  34. package/dist/components/checkbox/checkbox.js +230 -0
  35. package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
  36. package/dist/components/checkbox/checkbox.styles.js +70 -0
  37. package/dist/components/chip/chip-group.d.ts +56 -0
  38. package/dist/components/chip/chip-group.js +124 -0
  39. package/dist/components/chip/chip-group.styles.d.ts +1 -0
  40. package/dist/components/chip/chip-group.styles.js +57 -0
  41. package/dist/components/chip/chip.d.ts +64 -0
  42. package/dist/components/chip/chip.js +115 -0
  43. package/dist/components/chip/chip.styles.d.ts +1 -0
  44. package/dist/components/chip/chip.styles.js +115 -0
  45. package/dist/components/citation-badge/citation-badge.d.ts +113 -0
  46. package/dist/components/citation-badge/citation-badge.js +300 -0
  47. package/dist/components/citation-badge/citation-badge.styles.d.ts +1 -0
  48. package/dist/components/citation-badge/citation-badge.styles.js +96 -0
  49. package/dist/components/code-block/code-block.d.ts +89 -0
  50. package/dist/components/code-block/code-block.js +317 -0
  51. package/dist/components/code-block/code-block.styles.d.ts +1 -0
  52. package/dist/components/code-block/code-block.styles.js +187 -0
  53. package/dist/components/code-block/code-loader.d.ts +44 -0
  54. package/dist/components/code-block/code-loader.js +79 -0
  55. package/dist/components/combobox/combobox.d.ts +19 -1
  56. package/dist/components/combobox/combobox.js +84 -14
  57. package/dist/components/combobox/option.d.ts +5 -1
  58. package/dist/components/combobox/option.js +35 -0
  59. package/dist/components/context-meter/context-meter.d.ts +53 -0
  60. package/dist/components/context-meter/context-meter.js +150 -0
  61. package/dist/components/context-meter/context-meter.styles.d.ts +1 -0
  62. package/dist/components/context-meter/context-meter.styles.js +120 -0
  63. package/dist/components/conversation-item/conversation-item.d.ts +128 -0
  64. package/dist/components/conversation-item/conversation-item.js +322 -0
  65. package/dist/components/conversation-item/conversation-item.styles.d.ts +1 -0
  66. package/dist/components/conversation-item/conversation-item.styles.js +155 -0
  67. package/dist/components/date-picker/date-input.d.ts +24 -0
  68. package/dist/components/date-picker/date-input.js +134 -22
  69. package/dist/components/date-picker/date-picker.d.ts +7 -0
  70. package/dist/components/date-picker/date-picker.js +85 -16
  71. package/dist/components/dialog/confirm.d.ts +40 -0
  72. package/dist/components/dialog/confirm.js +79 -0
  73. package/dist/components/dialog/dialog.d.ts +85 -0
  74. package/dist/components/dialog/dialog.js +272 -0
  75. package/dist/components/dialog/dialog.styles.d.ts +1 -0
  76. package/dist/components/dialog/dialog.styles.js +52 -0
  77. package/dist/components/dock-panel/dock-panel.d.ts +134 -0
  78. package/dist/components/dock-panel/dock-panel.js +345 -0
  79. package/dist/components/dock-panel/dock-panel.styles.d.ts +1 -0
  80. package/dist/components/dock-panel/dock-panel.styles.js +145 -0
  81. package/dist/components/document-preview/document-preview.d.ts +135 -0
  82. package/dist/components/document-preview/document-preview.js +372 -0
  83. package/dist/components/document-preview/document-preview.styles.d.ts +1 -0
  84. package/dist/components/document-preview/document-preview.styles.js +191 -0
  85. package/dist/components/export-button/csv.js +1 -1
  86. package/dist/components/export-button/export-button.js +2 -0
  87. package/dist/components/flag/flag.d.ts +29 -13
  88. package/dist/components/flag/flag.js +39 -13
  89. package/dist/components/gauge/gauge.js +19 -10
  90. package/dist/components/generation-status/generation-status.d.ts +109 -0
  91. package/dist/components/generation-status/generation-status.js +273 -0
  92. package/dist/components/generation-status/generation-status.styles.d.ts +1 -0
  93. package/dist/components/generation-status/generation-status.styles.js +84 -0
  94. package/dist/components/heatmap/calendar-grid.d.ts +4 -1
  95. package/dist/components/heatmap/calendar-grid.js +25 -12
  96. package/dist/components/heatmap/heatmap-scale.d.ts +6 -0
  97. package/dist/components/heatmap/heatmap-scale.js +7 -1
  98. package/dist/components/heatmap/heatmap.d.ts +74 -0
  99. package/dist/components/heatmap/heatmap.js +135 -23
  100. package/dist/components/json-viewer/json-viewer.d.ts +53 -0
  101. package/dist/components/json-viewer/json-viewer.js +283 -0
  102. package/dist/components/json-viewer/json-viewer.styles.d.ts +1 -0
  103. package/dist/components/json-viewer/json-viewer.styles.js +176 -0
  104. package/dist/components/kbd/kbd.d.ts +77 -0
  105. package/dist/components/kbd/kbd.js +214 -0
  106. package/dist/components/kbd/kbd.styles.d.ts +1 -0
  107. package/dist/components/kbd/kbd.styles.js +52 -0
  108. package/dist/components/live-region/live-region.d.ts +81 -0
  109. package/dist/components/live-region/live-region.js +153 -0
  110. package/dist/components/live-region/live-region.styles.d.ts +1 -0
  111. package/dist/components/live-region/live-region.styles.js +10 -0
  112. package/dist/components/map/map.d.ts +8 -6
  113. package/dist/components/map/map.js +44 -8
  114. package/dist/components/markdown/markdown-loader.d.ts +34 -0
  115. package/dist/components/markdown/markdown-loader.js +43 -0
  116. package/dist/components/markdown/markdown.d.ts +81 -0
  117. package/dist/components/markdown/markdown.js +284 -0
  118. package/dist/components/markdown/markdown.styles.d.ts +1 -0
  119. package/dist/components/markdown/markdown.styles.js +82 -0
  120. package/dist/components/media-card/media-card.d.ts +122 -0
  121. package/dist/components/media-card/media-card.js +295 -0
  122. package/dist/components/media-card/media-card.styles.d.ts +1 -0
  123. package/dist/components/media-card/media-card.styles.js +132 -0
  124. package/dist/components/mention-popover/mention-popover.d.ts +200 -0
  125. package/dist/components/mention-popover/mention-popover.js +475 -0
  126. package/dist/components/mention-popover/mention-popover.styles.d.ts +1 -0
  127. package/dist/components/mention-popover/mention-popover.styles.js +101 -0
  128. package/dist/components/menu/menu-item.d.ts +62 -0
  129. package/dist/components/menu/menu-item.js +119 -0
  130. package/dist/components/menu/menu-item.styles.d.ts +1 -0
  131. package/dist/components/menu/menu-item.styles.js +67 -0
  132. package/dist/components/menu/menu.d.ts +143 -0
  133. package/dist/components/menu/menu.js +365 -0
  134. package/dist/components/menu/menu.styles.d.ts +1 -0
  135. package/dist/components/menu/menu.styles.js +66 -0
  136. package/dist/components/model-select/model-select.d.ts +125 -0
  137. package/dist/components/model-select/model-select.js +490 -0
  138. package/dist/components/model-select/model-select.styles.d.ts +1 -0
  139. package/dist/components/model-select/model-select.styles.js +189 -0
  140. package/dist/components/model-settings-panel/model-settings-panel.d.ts +69 -0
  141. package/dist/components/model-settings-panel/model-settings-panel.js +145 -0
  142. package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts +1 -0
  143. package/dist/components/model-settings-panel/model-settings-panel.styles.js +82 -0
  144. package/dist/components/playback/playback.d.ts +13 -1
  145. package/dist/components/playback/playback.js +47 -14
  146. package/dist/components/responsive-panel/responsive-panel.d.ts +150 -0
  147. package/dist/components/responsive-panel/responsive-panel.js +365 -0
  148. package/dist/components/responsive-panel/responsive-panel.styles.d.ts +1 -0
  149. package/dist/components/responsive-panel/responsive-panel.styles.js +103 -0
  150. package/dist/components/result-card/result-card.d.ts +44 -0
  151. package/dist/components/result-card/result-card.js +83 -0
  152. package/dist/components/result-card/result-card.styles.d.ts +1 -0
  153. package/dist/components/result-card/result-card.styles.js +50 -0
  154. package/dist/components/result-card/result-field.d.ts +36 -0
  155. package/dist/components/result-card/result-field.js +87 -0
  156. package/dist/components/result-card/result-field.styles.d.ts +1 -0
  157. package/dist/components/result-card/result-field.styles.js +32 -0
  158. package/dist/components/select/select.d.ts +43 -2
  159. package/dist/components/select/select.js +150 -17
  160. package/dist/components/slider/slider.d.ts +89 -0
  161. package/dist/components/slider/slider.js +352 -0
  162. package/dist/components/slider/slider.styles.d.ts +1 -0
  163. package/dist/components/slider/slider.styles.js +87 -0
  164. package/dist/components/source-card/source-card.d.ts +92 -0
  165. package/dist/components/source-card/source-card.js +148 -0
  166. package/dist/components/source-card/source-card.styles.d.ts +1 -0
  167. package/dist/components/source-card/source-card.styles.js +69 -0
  168. package/dist/components/source-list/source-list.d.ts +63 -0
  169. package/dist/components/source-list/source-list.js +122 -0
  170. package/dist/components/source-list/source-list.styles.d.ts +1 -0
  171. package/dist/components/source-list/source-list.styles.js +59 -0
  172. package/dist/components/sparkline/sparkline.js +27 -13
  173. package/dist/components/split/split.d.ts +93 -1
  174. package/dist/components/split/split.js +284 -15
  175. package/dist/components/split/split.styles.js +30 -0
  176. package/dist/components/stat/stat.d.ts +6 -1
  177. package/dist/components/stat/stat.js +8 -2
  178. package/dist/components/stream-status/stream-status.d.ts +110 -0
  179. package/dist/components/stream-status/stream-status.js +210 -0
  180. package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
  181. package/dist/components/stream-status/stream-status.styles.js +103 -0
  182. package/dist/components/streaming-text/streaming-text.d.ts +98 -0
  183. package/dist/components/streaming-text/streaming-text.js +209 -0
  184. package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
  185. package/dist/components/streaming-text/streaming-text.styles.js +62 -0
  186. package/dist/components/switch/switch.d.ts +58 -0
  187. package/dist/components/switch/switch.js +179 -0
  188. package/dist/components/switch/switch.styles.d.ts +1 -0
  189. package/dist/components/switch/switch.styles.js +75 -0
  190. package/dist/components/table/table.d.ts +73 -4
  191. package/dist/components/table/table.js +169 -20
  192. package/dist/components/table/table.styles.js +6 -1
  193. package/dist/components/tabs/tabs.d.ts +51 -0
  194. package/dist/components/tabs/tabs.js +190 -0
  195. package/dist/components/tabs/tabs.styles.d.ts +1 -0
  196. package/dist/components/tabs/tabs.styles.js +63 -0
  197. package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
  198. package/dist/components/thinking-panel/thinking-panel.js +235 -0
  199. package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
  200. package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
  201. package/dist/components/toast/toast-item.d.ts +1 -0
  202. package/dist/components/toast/toast-item.js +31 -6
  203. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
  204. package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
  205. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
  206. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
  207. package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
  208. package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
  209. package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
  210. package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
  211. package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
  212. package/dist/components/tool-param-form/tool-param-form.js +366 -0
  213. package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
  214. package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
  215. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
  216. package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
  217. package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
  218. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
  219. package/dist/components/tool-result-view/registry.d.ts +81 -0
  220. package/dist/components/tool-result-view/registry.js +84 -0
  221. package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
  222. package/dist/components/tool-result-view/tool-result-view.js +138 -0
  223. package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
  224. package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
  225. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
  226. package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
  227. package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
  228. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
  229. package/dist/components/tree/tree-node.d.ts +11 -2
  230. package/dist/components/tree/tree-node.js +16 -6
  231. package/dist/components/tree/tree.d.ts +8 -2
  232. package/dist/components/tree/tree.js +18 -11
  233. package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
  234. package/dist/components/typing-indicator/typing-indicator.js +96 -0
  235. package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
  236. package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
  237. package/dist/components/virtual-list/virtual-list.d.ts +173 -0
  238. package/dist/components/virtual-list/virtual-list.js +461 -0
  239. package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
  240. package/dist/components/virtual-list/virtual-list.styles.js +44 -0
  241. package/dist/components/word-cloud/word-cloud-layout.js +14 -1
  242. package/dist/components/word-cloud/word-cloud.d.ts +6 -0
  243. package/dist/components/word-cloud/word-cloud.js +25 -14
  244. package/dist/components/word-cloud/word-cloud.styles.js +3 -2
  245. package/dist/internal/announcer.d.ts +57 -0
  246. package/dist/internal/announcer.js +71 -0
  247. package/dist/internal/form-associated.d.ts +1 -0
  248. package/dist/internal/form-associated.js +55 -11
  249. package/dist/internal/group-by-recency.d.ts +50 -0
  250. package/dist/internal/group-by-recency.js +80 -0
  251. package/dist/internal/rtl.d.ts +23 -0
  252. package/dist/internal/rtl.js +28 -0
  253. package/dist/internal/scroll-lock.d.ts +10 -5
  254. package/dist/internal/scroll-lock.js +30 -14
  255. package/dist/internal/tokens.styles.js +6 -0
  256. package/dist/lyra.d.ts +130 -31
  257. package/dist/lyra.js +94 -26
  258. package/llms-full.txt +3836 -17
  259. package/llms.txt +16 -1
  260. package/package.json +23 -5
  261. package/dist/components/chart/bar-chart.stories.d.ts +0 -5
  262. package/dist/components/chart/bar-chart.stories.js +0 -20
  263. package/dist/components/chart/box-plot.stories.d.ts +0 -5
  264. package/dist/components/chart/box-plot.stories.js +0 -29
  265. package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
  266. package/dist/components/chart/bubble-chart.stories.js +0 -26
  267. package/dist/components/chart/chart.stories.d.ts +0 -32
  268. package/dist/components/chart/chart.stories.js +0 -141
  269. package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
  270. package/dist/components/chart/doughnut-chart.stories.js +0 -26
  271. package/dist/components/chart/histogram.stories.d.ts +0 -5
  272. package/dist/components/chart/histogram.stories.js +0 -17
  273. package/dist/components/chart/line-chart.stories.d.ts +0 -22
  274. package/dist/components/chart/line-chart.stories.js +0 -99
  275. package/dist/components/chart/lite-chart.stories.d.ts +0 -14
  276. package/dist/components/chart/lite-chart.stories.js +0 -114
  277. package/dist/components/chart/pie-chart.stories.d.ts +0 -5
  278. package/dist/components/chart/pie-chart.stories.js +0 -26
  279. package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
  280. package/dist/components/chart/polar-area-chart.stories.js +0 -20
  281. package/dist/components/chart/radar-chart.stories.d.ts +0 -5
  282. package/dist/components/chart/radar-chart.stories.js +0 -20
  283. package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
  284. package/dist/components/chart/scatter-chart.stories.js +0 -31
  285. package/dist/components/combobox/combobox.stories.d.ts +0 -24
  286. package/dist/components/combobox/combobox.stories.js +0 -102
  287. package/dist/components/date-picker/date-input.stories.d.ts +0 -10
  288. package/dist/components/date-picker/date-input.stories.js +0 -48
  289. package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
  290. package/dist/components/date-picker/date-picker.stories.js +0 -25
  291. package/dist/components/empty/empty.stories.d.ts +0 -7
  292. package/dist/components/empty/empty.stories.js +0 -34
  293. package/dist/components/export-button/export-button.stories.d.ts +0 -7
  294. package/dist/components/export-button/export-button.stories.js +0 -37
  295. package/dist/components/file-input/file-input.stories.d.ts +0 -7
  296. package/dist/components/file-input/file-input.stories.js +0 -19
  297. package/dist/components/flag/flag.stories.d.ts +0 -6
  298. package/dist/components/flag/flag.stories.js +0 -31
  299. package/dist/components/gauge/gauge.stories.d.ts +0 -8
  300. package/dist/components/gauge/gauge.stories.js +0 -19
  301. package/dist/components/graph/graph.stories.d.ts +0 -8
  302. package/dist/components/graph/graph.stories.js +0 -68
  303. package/dist/components/heatmap/heatmap.stories.d.ts +0 -27
  304. package/dist/components/heatmap/heatmap.stories.js +0 -179
  305. package/dist/components/map/map.stories.d.ts +0 -15
  306. package/dist/components/map/map.stories.js +0 -135
  307. package/dist/components/playback/playback.stories.d.ts +0 -7
  308. package/dist/components/playback/playback.stories.js +0 -16
  309. package/dist/components/select/select.stories.d.ts +0 -21
  310. package/dist/components/select/select.stories.js +0 -91
  311. package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
  312. package/dist/components/skeleton/skeleton.stories.js +0 -16
  313. package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
  314. package/dist/components/sparkline/sparkline.stories.js +0 -39
  315. package/dist/components/split/split.stories.d.ts +0 -7
  316. package/dist/components/split/split.stories.js +0 -36
  317. package/dist/components/stat/stat.stories.d.ts +0 -9
  318. package/dist/components/stat/stat.stories.js +0 -81
  319. package/dist/components/table/table.stories.d.ts +0 -12
  320. package/dist/components/table/table.stories.js +0 -67
  321. package/dist/components/time-range/time-range.stories.d.ts +0 -8
  322. package/dist/components/time-range/time-range.stories.js +0 -26
  323. package/dist/components/toast/toast.stories.d.ts +0 -8
  324. package/dist/components/toast/toast.stories.js +0 -72
  325. package/dist/components/tree/tree.stories.d.ts +0 -9
  326. package/dist/components/tree/tree.stories.js +0 -52
  327. package/dist/components/widget/widget.stories.d.ts +0 -9
  328. package/dist/components/widget/widget.stories.js +0 -74
  329. package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
  330. package/dist/components/word-cloud/word-cloud.stories.js +0 -55
@@ -5,6 +5,12 @@ export function parseIsoDate(iso) {
5
5
  const [y, m, d] = iso.split('-').map(Number);
6
6
  return new Date(Date.UTC(y, m - 1, d));
7
7
  }
8
+ /** Whether `iso` (already run through parseIsoDate) actually round-trips —
9
+ * Date.UTC silently rolls e.g. day 30 of a 28-day February into March. */
10
+ function isCalendarValid(iso, parsed) {
11
+ const [y, m, d] = iso.split('-').map(Number);
12
+ return parsed.getUTCFullYear() === y && parsed.getUTCMonth() === m - 1 && parsed.getUTCDate() === d;
13
+ }
8
14
  /**
9
15
  * Lays `days` out onto a GitHub-style Sunday-Saturday x week grid. `days`
10
16
  * need not be sorted or contiguous; the grid spans from the earliest to the
@@ -15,12 +21,15 @@ export function parseIsoDate(iso) {
15
21
  * string yields an `Invalid Date` (`getTime()` is `NaN`), and letting that
16
22
  * single `NaN` reach the min/max below would poison `firstWeekStart` (and
17
23
  * therefore every cell's `week`) for the whole grid instead of just the bad
18
- * entry.
24
+ * entry. Likewise, an entry whose `date` is numerically well-formed but
25
+ * calendar-invalid (e.g. `'2026-02-30'`, which `Date.UTC` silently rolls
26
+ * over into March) is dropped rather than silently renormalized — see
27
+ * `isCalendarValid()`.
19
28
  */
20
29
  export function buildCalendarGrid(days) {
21
30
  const parsed = days
22
31
  .map((d) => ({ ...d, dateObj: parseIsoDate(d.date) }))
23
- .filter((d) => !Number.isNaN(d.dateObj.getTime()));
32
+ .filter((d) => !Number.isNaN(d.dateObj.getTime()) && isCalendarValid(d.date, d.dateObj));
24
33
  if (parsed.length === 0) {
25
34
  return { cells: [], weekCount: 0, firstWeekStart: new Date(0), monthLabels: [] };
26
35
  }
@@ -34,18 +43,22 @@ export function buildCalendarGrid(days) {
34
43
  return { date: d.date, value: d.value, week, weekday };
35
44
  });
36
45
  const weekCount = minMax(cells.map((c) => c.week))[1] + 1;
46
+ // Label every distinct (year, month) present in the data, not just months
47
+ // that happen to contain a Sunday-anchored cell — a sparse calendar (few
48
+ // days per month, none landing on a Sunday) would otherwise render zero
49
+ // month labels for months that do have data.
37
50
  const monthLabels = [];
38
- let lastMonth = -1;
39
- for (const cell of cells.slice().sort((a, b) => a.week - b.week)) {
51
+ const seenMonths = new Set();
52
+ for (const cell of cells.slice().sort((a, b) => a.week - b.week || a.weekday - b.weekday)) {
40
53
  const cellDate = parseIsoDate(cell.date);
41
- const month = cellDate.getUTCMonth();
42
- if (cell.weekday === 0 && month !== lastMonth) {
43
- lastMonth = month;
44
- monthLabels.push({
45
- week: cell.week,
46
- label: cellDate.toLocaleString('en', { month: 'short', timeZone: 'UTC' }),
47
- });
48
- }
54
+ const key = `${cellDate.getUTCFullYear()}-${cellDate.getUTCMonth()}`;
55
+ if (seenMonths.has(key))
56
+ continue;
57
+ seenMonths.add(key);
58
+ monthLabels.push({
59
+ week: cell.week,
60
+ label: cellDate.toLocaleString(undefined, { month: 'short', timeZone: 'UTC' }),
61
+ });
49
62
  }
50
63
  return { cells, weekCount, firstWeekStart, monthLabels };
51
64
  }
@@ -12,5 +12,11 @@ export declare function minMax(values: number[]): [number, number] | null;
12
12
  * Square-root-scaled bucket index in `[0, steps-1]`, or `-1` for no-data.
13
13
  * Compresses large counts so a single heavy cell doesn't wash out the rest
14
14
  * of a sequential color ramp.
15
+ *
16
+ * Only a negative `count` means "no data" here — the true no-data sentinel
17
+ * (`v < 0 || !Number.isFinite(v)`) is already filtered out by the caller
18
+ * (`drawMatrix()`) before `sqrtStep` is ever invoked, so a real `count === 0`
19
+ * (e.g. "zero events that day") reaches this function and must bucket to the
20
+ * lowest ramp step like any other legitimate value, not render as no-data.
15
21
  */
16
22
  export declare function sqrtStep(count: number, max: number, steps: number): number;
@@ -29,9 +29,15 @@ export function minMax(values) {
29
29
  * Square-root-scaled bucket index in `[0, steps-1]`, or `-1` for no-data.
30
30
  * Compresses large counts so a single heavy cell doesn't wash out the rest
31
31
  * of a sequential color ramp.
32
+ *
33
+ * Only a negative `count` means "no data" here — the true no-data sentinel
34
+ * (`v < 0 || !Number.isFinite(v)`) is already filtered out by the caller
35
+ * (`drawMatrix()`) before `sqrtStep` is ever invoked, so a real `count === 0`
36
+ * (e.g. "zero events that day") reaches this function and must bucket to the
37
+ * lowest ramp step like any other legitimate value, not render as no-data.
32
38
  */
33
39
  export function sqrtStep(count, max, steps) {
34
- if (count <= 0 || max <= 0)
40
+ if (count < 0 || max <= 0)
35
41
  return -1;
36
42
  const ratio = Math.sqrt(count) / Math.sqrt(max);
37
43
  return Math.min(steps - 1, Math.floor(ratio * steps));
@@ -79,6 +79,12 @@ export declare function mixColor(fromColor: string, toColor: string, t: number):
79
79
  * specific cells (e.g. to call out an anomaly), each one optionally
80
80
  * surfaced in the legend too via `[part="legend-annotation"]`.
81
81
  *
82
+ * `columnX` (calendar mode only) overrides the x-origin computed for each
83
+ * week column — drawing, hit-testing, the focus ring, and month-label
84
+ * positioning all consult it consistently, so a consumer can pixel-align a
85
+ * calendar's week columns with a sibling chart's coordinate system. Unset
86
+ * (the default) keeps the original evenly-spaced formula.
87
+ *
82
88
  * @customElement lyra-heatmap
83
89
  * @event lyra-cell-click - Fired on click, or Enter/Space on the
84
90
  * focused/hovered cell. `detail: { row, col, value }` in matrix mode,
@@ -113,6 +119,19 @@ export declare class LyraHeatmap extends LyraElement {
113
119
  * (`MatrixCellPos` in matrix mode, `CalendarCellPos` in calendar mode) and its value. Falls back to
114
120
  * the built-in English "Row X, Col Y: value" / "Mon DD: value" template when unset. */
115
121
  cellText?: (pos: MatrixCellPos | CalendarCellPos, value: number) => string;
122
+ /**
123
+ * Calendar mode only: overrides the x-origin (canvas-local CSS px) of week
124
+ * column `index` (0-based, same indexing as `CalendarCellPos.week`).
125
+ * Consulted consistently by every calendar-mode geometry call site —
126
+ * drawing, hit-testing, the keyboard focus ring, and month-label
127
+ * positioning — via the private `columnXFor()` helper, so painted
128
+ * geometry and pointer hit-testing never disagree. Lets a consumer
129
+ * pixel-align a calendar's week columns with a sibling chart's bars by
130
+ * supplying that chart the same coordinate function. Unset (the default)
131
+ * keeps today's evenly-spaced `CAL_PAD_LEFT + week * (CAL_CELL + CAL_GAP)`
132
+ * formula unchanged. Ignored in matrix mode.
133
+ */
134
+ columnX?: (index: number) => number;
116
135
  private canvas?;
117
136
  private resizeObserver?;
118
137
  private dprQuery?;
@@ -125,6 +144,19 @@ export declare class LyraHeatmap extends LyraElement {
125
144
  private focusedCell;
126
145
  /** Text of the visually-hidden `aria-live="polite"` status announcement, refreshed on every focus move. */
127
146
  private liveText;
147
+ /**
148
+ * Whether `role`/`aria-label` were present on the host *before* this
149
+ * element's own code ever wrote to them — snapshotted once, in the very
150
+ * first `willUpdate()` (guarded by `hasUpdated`, which Lit only flips to
151
+ * `true` after that first update commits). Re-checking `hasAttribute()` on
152
+ * every update instead would self-poison: once this element writes its own
153
+ * default `aria-label` on the first render, `hasAttribute('aria-label')`
154
+ * is permanently `true` afterwards, which would wrongly look like an
155
+ * author-supplied value and freeze the label instead of refreshing it as
156
+ * `values`/`days` change on later updates.
157
+ */
158
+ private authorSuppliedRole;
159
+ private authorSuppliedAriaLabel;
128
160
  connectedCallback(): void;
129
161
  disconnectedCallback(): void;
130
162
  private watchDpr;
@@ -154,6 +186,40 @@ export declare class LyraHeatmap extends LyraElement {
154
186
  /** Reads the customizable canvas-drawn annotation-ring stroke color off the host's computed style. */
155
187
  private annotationColor;
156
188
  private draw;
189
+ /**
190
+ * Calendar-mode week-column x-origin (canvas-local CSS px) — `columnX(week)`
191
+ * when set, otherwise the original evenly-spaced formula. Shared by every
192
+ * calendar-mode drawing and hit-testing call site (`drawCalendar()`,
193
+ * `hitTestCalendar()`/`weekAtX()`, `cellRect()`) so painted geometry and
194
+ * interactive hit-testing never disagree — mirrors the existing
195
+ * `matrixCellSize()` invariant for matrix mode's
196
+ * `drawMatrix()`/`hitTestMatrix()`/`cellRect()`.
197
+ */
198
+ private columnXFor;
199
+ /**
200
+ * Inverse of `columnXFor()`: resolves an x position (canvas-local CSS px)
201
+ * to the week column it falls in, or `null` if it's outside every column
202
+ * (`weekCount` is 0, or `x` doesn't land inside `[0, weekCount)`'s span).
203
+ * The default spacing has a closed-form inverse (division); an arbitrary
204
+ * `columnX` override doesn't, so that case instead scans each column's
205
+ * `[columnXFor(week), columnXFor(week + 1))` span — algebraically the same
206
+ * span the default formula's division derives — so hit-testing always
207
+ * agrees with wherever `columnXFor()` actually painted that column.
208
+ */
209
+ private weekAtX;
210
+ /**
211
+ * Locale-derived weekday-axis labels for the Sunday..Saturday rows drawn
212
+ * down the left of the calendar grid. Only indices 1 (Mon), 3 (Wed), and 5
213
+ * (Fri) are filled in — matching today's sparse every-other-day label
214
+ * density — the rest stay blank. `firstWeekStart` is always a UTC Sunday
215
+ * by construction (see `buildCalendarGrid()`), so weekday index 0..6 is a
216
+ * fixed Sun..Sat positional mapping; only the label *text* for indices
217
+ * 1/3/5 is derived here, via `Intl.DateTimeFormat` on a real UTC date that
218
+ * actually falls on that weekday, so it follows the runtime locale instead
219
+ * of a hardcoded English array (see `calendarCellText()`'s tooltip text
220
+ * for the same pattern).
221
+ */
222
+ private weekdayLabels;
157
223
  private drawCalendar;
158
224
  /**
159
225
  * Effective per-cell size in matrix mode — `fitToWidth` derives it from
@@ -207,6 +273,14 @@ export declare class LyraHeatmap extends LyraElement {
207
273
  private emitCellClick;
208
274
  /** Inline `style` for `[part="tooltip"]`, centered above the given cell. */
209
275
  private tooltipStyle;
276
+ /**
277
+ * Value-equality check for a `CellPos`, since `hoverCell` is assigned a
278
+ * fresh object literal on every `pointermove` — Lit's `@state()`
279
+ * change-detection only no-ops an identical *reference*, so without this,
280
+ * `{row,col} !== {row,col}` by reference triggers a full canvas clear +
281
+ * redraw on every pixel of pointer movement, even within the same cell.
282
+ */
283
+ private samePos;
210
284
  private onPointerMove;
211
285
  private onPointerLeave;
212
286
  private onCanvasClick;
@@ -167,6 +167,12 @@ export function mixColor(fromColor, toColor, t) {
167
167
  * specific cells (e.g. to call out an anomaly), each one optionally
168
168
  * surfaced in the legend too via `[part="legend-annotation"]`.
169
169
  *
170
+ * `columnX` (calendar mode only) overrides the x-origin computed for each
171
+ * week column — drawing, hit-testing, the focus ring, and month-label
172
+ * positioning all consult it consistently, so a consumer can pixel-align a
173
+ * calendar's week columns with a sibling chart's coordinate system. Unset
174
+ * (the default) keeps the original evenly-spaced formula.
175
+ *
170
176
  * @customElement lyra-heatmap
171
177
  * @event lyra-cell-click - Fired on click, or Enter/Space on the
172
178
  * focused/hovered cell. `detail: { row, col, value }` in matrix mode,
@@ -207,21 +213,37 @@ export class LyraHeatmap extends LyraElement {
207
213
  this.focusedCell = null;
208
214
  /** Text of the visually-hidden `aria-live="polite"` status announcement, refreshed on every focus move. */
209
215
  this.liveText = '';
216
+ /**
217
+ * Whether `role`/`aria-label` were present on the host *before* this
218
+ * element's own code ever wrote to them — snapshotted once, in the very
219
+ * first `willUpdate()` (guarded by `hasUpdated`, which Lit only flips to
220
+ * `true` after that first update commits). Re-checking `hasAttribute()` on
221
+ * every update instead would self-poison: once this element writes its own
222
+ * default `aria-label` on the first render, `hasAttribute('aria-label')`
223
+ * is permanently `true` afterwards, which would wrongly look like an
224
+ * author-supplied value and freeze the label instead of refreshing it as
225
+ * `values`/`days` change on later updates.
226
+ */
227
+ this.authorSuppliedRole = false;
228
+ this.authorSuppliedAriaLabel = false;
210
229
  this.onDprChange = () => {
211
230
  this.watchDpr();
212
231
  this.draw();
213
232
  };
214
233
  this.onPointerMove = (e) => {
215
- this.hoverCell = this.hitTest(e.offsetX, e.offsetY);
234
+ const next = this.hitTest(e.offsetX, e.offsetY);
235
+ if (!this.samePos(this.hoverCell, next))
236
+ this.hoverCell = next;
216
237
  };
217
238
  this.onPointerLeave = () => {
218
239
  this.hoverCell = null;
219
240
  };
220
241
  this.onCanvasClick = (e) => {
221
- // Prefer the click's own position; fall back to whatever's already
222
- // keyboard-focused (e.g. a synthetic click dispatched via
223
- // HTMLElement.click(), which carries no real coordinates).
224
- const pos = this.hitTest(e.offsetX, e.offsetY) ?? this.focusedCell;
242
+ // Only the click's own hit-tested position counts a click that lands
243
+ // outside the grid (e.g. on the canvas's own padding) is a genuine
244
+ // out-of-grid click and should do nothing, not fall back to whatever
245
+ // cell happens to still be keyboard-focused from an earlier interaction.
246
+ const pos = this.hitTest(e.offsetX, e.offsetY);
225
247
  if (!pos)
226
248
  return;
227
249
  this.focusedCell = pos;
@@ -269,17 +291,31 @@ export class LyraHeatmap extends LyraElement {
269
291
  this.hoverCell = null;
270
292
  this.liveText = '';
271
293
  }
294
+ if (!this.hasUpdated) {
295
+ this.authorSuppliedRole = this.hasAttribute('role');
296
+ this.authorSuppliedAriaLabel = this.hasAttribute('aria-label');
297
+ }
272
298
  this.cachedValueRange = this.computeValueRange();
273
299
  const bounds = this.cachedValueRange;
274
300
  const range = bounds ? `${bounds[0]}–${bounds[1]}` : 'no data';
275
- this.setAttribute('role', 'group');
276
- if (this.mode === 'calendar') {
277
- this.setAttribute('aria-label', `Calendar heatmap of ${this.days.length} days, ${this.valueLabel} range ${range}`);
278
- }
279
- else {
280
- const rows = this.rowLabels.length;
281
- const cols = this.colLabels.length;
282
- this.setAttribute('aria-label', `Heatmap of ${rows} × ${cols} cells, ${this.valueLabel} range ${range}`);
301
+ // Only default role/aria-label when the author hasn't already supplied
302
+ // one the canvas is a genuinely focusable/interactive control
303
+ // (tabindex="0", click/keydown handlers) plus a live role="status"
304
+ // region, so unconditionally forcing a role here would tell assistive
305
+ // tech to flatten the whole subtree, hiding both from the accessibility
306
+ // tree; it would also silently overwrite any author-supplied
307
+ // role/aria-label on every update.
308
+ if (!this.authorSuppliedRole)
309
+ this.setAttribute('role', 'group');
310
+ if (!this.authorSuppliedAriaLabel) {
311
+ if (this.mode === 'calendar') {
312
+ this.setAttribute('aria-label', `Calendar heatmap of ${this.days.length} days, ${this.valueLabel} range ${range}`);
313
+ }
314
+ else {
315
+ const rows = this.rowLabels.length;
316
+ const cols = this.colLabels.length;
317
+ this.setAttribute('aria-label', `Heatmap of ${rows} × ${cols} cells, ${this.valueLabel} range ${range}`);
318
+ }
283
319
  }
284
320
  }
285
321
  /**
@@ -331,11 +367,66 @@ export class LyraHeatmap extends LyraElement {
331
367
  else
332
368
  this.drawMatrix();
333
369
  }
370
+ /**
371
+ * Calendar-mode week-column x-origin (canvas-local CSS px) — `columnX(week)`
372
+ * when set, otherwise the original evenly-spaced formula. Shared by every
373
+ * calendar-mode drawing and hit-testing call site (`drawCalendar()`,
374
+ * `hitTestCalendar()`/`weekAtX()`, `cellRect()`) so painted geometry and
375
+ * interactive hit-testing never disagree — mirrors the existing
376
+ * `matrixCellSize()` invariant for matrix mode's
377
+ * `drawMatrix()`/`hitTestMatrix()`/`cellRect()`.
378
+ */
379
+ columnXFor(week) {
380
+ return this.columnX ? this.columnX(week) : CAL_PAD_LEFT + week * (CAL_CELL + CAL_GAP);
381
+ }
382
+ /**
383
+ * Inverse of `columnXFor()`: resolves an x position (canvas-local CSS px)
384
+ * to the week column it falls in, or `null` if it's outside every column
385
+ * (`weekCount` is 0, or `x` doesn't land inside `[0, weekCount)`'s span).
386
+ * The default spacing has a closed-form inverse (division); an arbitrary
387
+ * `columnX` override doesn't, so that case instead scans each column's
388
+ * `[columnXFor(week), columnXFor(week + 1))` span — algebraically the same
389
+ * span the default formula's division derives — so hit-testing always
390
+ * agrees with wherever `columnXFor()` actually painted that column.
391
+ */
392
+ weekAtX(x, weekCount) {
393
+ if (!this.columnX) {
394
+ const week = Math.floor((x - CAL_PAD_LEFT) / (CAL_CELL + CAL_GAP));
395
+ return week >= 0 && week < weekCount ? week : null;
396
+ }
397
+ for (let week = 0; week < weekCount; week++) {
398
+ const start = this.columnXFor(week);
399
+ const end = week + 1 < weekCount ? this.columnXFor(week + 1) : start + (CAL_CELL + CAL_GAP);
400
+ if (x >= start && x < end)
401
+ return week;
402
+ }
403
+ return null;
404
+ }
405
+ /**
406
+ * Locale-derived weekday-axis labels for the Sunday..Saturday rows drawn
407
+ * down the left of the calendar grid. Only indices 1 (Mon), 3 (Wed), and 5
408
+ * (Fri) are filled in — matching today's sparse every-other-day label
409
+ * density — the rest stay blank. `firstWeekStart` is always a UTC Sunday
410
+ * by construction (see `buildCalendarGrid()`), so weekday index 0..6 is a
411
+ * fixed Sun..Sat positional mapping; only the label *text* for indices
412
+ * 1/3/5 is derived here, via `Intl.DateTimeFormat` on a real UTC date that
413
+ * actually falls on that weekday, so it follows the runtime locale instead
414
+ * of a hardcoded English array (see `calendarCellText()`'s tooltip text
415
+ * for the same pattern).
416
+ */
417
+ weekdayLabels(firstWeekStart) {
418
+ const formatter = new Intl.DateTimeFormat(undefined, { weekday: 'short', timeZone: 'UTC' });
419
+ const labels = ['', '', '', '', '', '', ''];
420
+ for (const weekday of [1, 3, 5]) {
421
+ labels[weekday] = formatter.format(new Date(firstWeekStart.getTime() + weekday * MS_PER_DAY));
422
+ }
423
+ return labels;
424
+ }
334
425
  drawCalendar() {
335
426
  if (!this.canvas)
336
427
  return;
337
- const { cells, weekCount, monthLabels } = buildCalendarGrid(this.days);
338
- const w = CAL_PAD_LEFT + Math.max(1, weekCount) * (CAL_CELL + CAL_GAP);
428
+ const { cells, weekCount, firstWeekStart, monthLabels } = buildCalendarGrid(this.days);
429
+ const w = this.columnXFor(Math.max(1, weekCount));
339
430
  const h = CAL_LABEL_H + 7 * (CAL_CELL + CAL_GAP);
340
431
  const dpr = window.devicePixelRatio || 1;
341
432
  this.canvas.width = w * dpr;
@@ -358,7 +449,7 @@ export class LyraHeatmap extends LyraElement {
358
449
  const ramp = Array.from({ length: buckets }, (_, i) => mixRgb(loRgb, hiRgb, i / (buckets - 1)));
359
450
  const noDataFill = this.noDataFill();
360
451
  for (const cell of cells) {
361
- const x = CAL_PAD_LEFT + cell.week * (CAL_CELL + CAL_GAP);
452
+ const x = this.columnXFor(cell.week);
362
453
  const y = CAL_LABEL_H + cell.weekday * (CAL_CELL + CAL_GAP);
363
454
  ctx.fillStyle =
364
455
  cell.value < 0 || !Number.isFinite(cell.value)
@@ -378,7 +469,7 @@ export class LyraHeatmap extends LyraElement {
378
469
  const match = cells.find((c) => c.date === ann.date);
379
470
  if (!match)
380
471
  continue;
381
- const x = CAL_PAD_LEFT + match.week * (CAL_CELL + CAL_GAP);
472
+ const x = this.columnXFor(match.week);
382
473
  const y = CAL_LABEL_H + match.weekday * (CAL_CELL + CAL_GAP);
383
474
  ctx.strokeRect(x + 0.5, y + 0.5, CAL_CELL - 1, CAL_CELL - 1);
384
475
  }
@@ -390,7 +481,7 @@ export class LyraHeatmap extends LyraElement {
390
481
  if (week < weekCount && weekday < 7) {
391
482
  ctx.lineWidth = RING_LINE_WIDTH;
392
483
  ctx.strokeStyle = this.focusRingColor();
393
- const x = CAL_PAD_LEFT + week * (CAL_CELL + CAL_GAP);
484
+ const x = this.columnXFor(week);
394
485
  const y = CAL_LABEL_H + weekday * (CAL_CELL + CAL_GAP);
395
486
  ctx.strokeRect(x + 0.5, y + 0.5, CAL_CELL - 1, CAL_CELL - 1);
396
487
  }
@@ -398,9 +489,9 @@ export class LyraHeatmap extends LyraElement {
398
489
  ctx.fillStyle = this.labelColor();
399
490
  ctx.font = this.labelFont();
400
491
  for (const m of monthLabels) {
401
- ctx.fillText(m.label, CAL_PAD_LEFT + m.week * (CAL_CELL + CAL_GAP), CAL_LABEL_H - 4);
492
+ ctx.fillText(m.label, this.columnXFor(m.week), CAL_LABEL_H - 4);
402
493
  }
403
- const WEEKDAY_LABELS = ['', 'Mon', '', 'Wed', '', 'Fri', ''];
494
+ const WEEKDAY_LABELS = this.weekdayLabels(firstWeekStart);
404
495
  WEEKDAY_LABELS.forEach((label, weekday) => {
405
496
  if (label)
406
497
  ctx.fillText(label, 2, CAL_LABEL_H + weekday * (CAL_CELL + CAL_GAP) + CAL_CELL - 1);
@@ -539,9 +630,9 @@ export class LyraHeatmap extends LyraElement {
539
630
  const { weekCount } = buildCalendarGrid(this.days);
540
631
  if (weekCount === 0)
541
632
  return null;
542
- const week = Math.floor((x - CAL_PAD_LEFT) / (CAL_CELL + CAL_GAP));
633
+ const week = this.weekAtX(x, weekCount);
543
634
  const weekday = Math.floor((y - CAL_LABEL_H) / (CAL_CELL + CAL_GAP));
544
- if (week < 0 || week >= weekCount || weekday < 0 || weekday > 6)
635
+ if (week === null || weekday < 0 || weekday > 6)
545
636
  return null;
546
637
  return { week, weekday };
547
638
  }
@@ -569,7 +660,7 @@ export class LyraHeatmap extends LyraElement {
569
660
  cellRect(pos) {
570
661
  if ('week' in pos) {
571
662
  return {
572
- x: CAL_PAD_LEFT + pos.week * (CAL_CELL + CAL_GAP),
663
+ x: this.columnXFor(pos.week),
573
664
  y: CAL_LABEL_H + pos.weekday * (CAL_CELL + CAL_GAP),
574
665
  w: CAL_CELL,
575
666
  h: CAL_CELL,
@@ -630,6 +721,24 @@ export class LyraHeatmap extends LyraElement {
630
721
  const rect = this.cellRect(pos);
631
722
  return { left: `${rect.x + rect.w / 2}px`, top: `${rect.y}px` };
632
723
  }
724
+ /**
725
+ * Value-equality check for a `CellPos`, since `hoverCell` is assigned a
726
+ * fresh object literal on every `pointermove` — Lit's `@state()`
727
+ * change-detection only no-ops an identical *reference*, so without this,
728
+ * `{row,col} !== {row,col}` by reference triggers a full canvas clear +
729
+ * redraw on every pixel of pointer movement, even within the same cell.
730
+ */
731
+ samePos(a, b) {
732
+ if (a === b)
733
+ return true;
734
+ if (!a || !b)
735
+ return false;
736
+ if ('week' in a && 'week' in b)
737
+ return a.week === b.week && a.weekday === b.weekday;
738
+ if ('row' in a && 'row' in b)
739
+ return a.row === b.row && a.col === b.col;
740
+ return false;
741
+ }
633
742
  onMatrixKeyDown(e) {
634
743
  const rows = this.rowLabels.length;
635
744
  const cols = this.colLabels.length;
@@ -767,6 +876,9 @@ __decorate([
767
876
  __decorate([
768
877
  property({ attribute: false })
769
878
  ], LyraHeatmap.prototype, "cellText", void 0);
879
+ __decorate([
880
+ property({ attribute: false })
881
+ ], LyraHeatmap.prototype, "columnX", void 0);
770
882
  __decorate([
771
883
  query('canvas')
772
884
  ], LyraHeatmap.prototype, "canvas", void 0);
@@ -0,0 +1,53 @@
1
+ import { type TemplateResult } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ /**
4
+ * `<lyra-json-viewer>` — a collapsible, copyable tree view for an arbitrary
5
+ * JSON-serializable value (object/array/string/number/boolean/null/
6
+ * undefined). Serves as the fallback renderer wherever a raw payload (tool
7
+ * call arguments, a tool result, an API response) needs inspecting without a
8
+ * bespoke view.
9
+ *
10
+ * Expand/collapse state is keyed by structural path (not by object identity),
11
+ * so it survives a `data` reassignment that keeps the same shape -- e.g. a
12
+ * streaming tool result being patched in place.
13
+ *
14
+ * @customElement lyra-json-viewer
15
+ * @event lyra-copy - `detail: { text }` -- fired by the top-level copy button or a per-node one. Fires even when `navigator.clipboard` silently failed or is unavailable (insecure context, older browser, stubbed-out test environment), so a consumer can still observe copy *intent*.
16
+ * @csspart base - The root scroll container; respects `max-height`.
17
+ * @csspart toolbar - The wrapper around the top-level copy button (only rendered when `copyable`).
18
+ * @csspart tree - The wrapper around the rendered node tree.
19
+ * @csspart key - An object property key or array index label.
20
+ * @csspart value - A primitive value's text -- carries `data-type` (`string`/`number`/`boolean`/`null`/`undefined`) for per-type coloring, and `data-match` while it matches `search`.
21
+ * @csspart bracket - A `{`, `}`, `[`, or `]` delimiter.
22
+ * @csspart toggle - A container node's expand/collapse button (hidden, but present for row alignment, on leaf/empty nodes).
23
+ * @csspart copy-button - A copy-to-clipboard button -- the top-level one (in `toolbar`) or a per-node one (only rendered when `copyable`).
24
+ */
25
+ export declare class LyraJsonViewer extends LyraElement {
26
+ static styles: import("lit").CSSResultGroup[];
27
+ /** The value to render. Any JSON-serializable value, plus `undefined`. */
28
+ data: unknown;
29
+ /** Nodes at or beyond this nesting depth (root = 0) start collapsed. Omit/undefined: nothing auto-collapses. */
30
+ collapsedDepth?: number;
31
+ /** A CSS length (e.g. `"20rem"`); once set, the viewer scrolls internally past this height instead of growing the page. */
32
+ maxHeight: string;
33
+ /** Shows copy-to-clipboard affordances: one for the whole value, plus one per node. */
34
+ copyable: boolean;
35
+ /** Case-insensitive substring match against keys/values; matches are highlighted and their ancestors auto-expanded. */
36
+ search: string;
37
+ /** Per-path (`JSON.stringify(path)`) explicit expand/collapse, overriding the `collapsedDepth`/search defaults once a node's toggle has been used. */
38
+ private expandedOverrides;
39
+ private isExpanded;
40
+ private toggleNode;
41
+ private writeClipboard;
42
+ private copy;
43
+ /** Builds the key/value-match sets and the ancestor-paths-of-a-match set that `search` drives. */
44
+ private computeSearch;
45
+ private renderCopyButton;
46
+ private renderNode;
47
+ render(): TemplateResult;
48
+ }
49
+ declare global {
50
+ interface HTMLElementTagNameMap {
51
+ 'lyra-json-viewer': LyraJsonViewer;
52
+ }
53
+ }