@aceshooting/lyra-ui 1.1.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 (335) hide show
  1. package/README.md +12 -1
  2. package/custom-elements.json +21460 -8489
  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 +12 -2
  19. package/dist/components/chart/chart.js +121 -14
  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 +63 -0
  23. package/dist/components/chart/lite-chart.js +181 -23
  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 +42 -5
  88. package/dist/components/flag/flag.js +70 -3
  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 +89 -3
  99. package/dist/components/heatmap/heatmap.js +158 -29
  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 +45 -1
  159. package/dist/components/select/select.js +114 -17
  160. package/dist/components/select/select.styles.js +26 -2
  161. package/dist/components/slider/slider.d.ts +89 -0
  162. package/dist/components/slider/slider.js +352 -0
  163. package/dist/components/slider/slider.styles.d.ts +1 -0
  164. package/dist/components/slider/slider.styles.js +87 -0
  165. package/dist/components/source-card/source-card.d.ts +92 -0
  166. package/dist/components/source-card/source-card.js +148 -0
  167. package/dist/components/source-card/source-card.styles.d.ts +1 -0
  168. package/dist/components/source-card/source-card.styles.js +69 -0
  169. package/dist/components/source-list/source-list.d.ts +63 -0
  170. package/dist/components/source-list/source-list.js +122 -0
  171. package/dist/components/source-list/source-list.styles.d.ts +1 -0
  172. package/dist/components/source-list/source-list.styles.js +59 -0
  173. package/dist/components/sparkline/sparkline.js +27 -13
  174. package/dist/components/split/split.d.ts +93 -1
  175. package/dist/components/split/split.js +284 -15
  176. package/dist/components/split/split.styles.js +30 -0
  177. package/dist/components/stat/stat.d.ts +22 -1
  178. package/dist/components/stat/stat.js +49 -3
  179. package/dist/components/stat/stat.styles.js +20 -0
  180. package/dist/components/stream-status/stream-status.d.ts +110 -0
  181. package/dist/components/stream-status/stream-status.js +210 -0
  182. package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
  183. package/dist/components/stream-status/stream-status.styles.js +103 -0
  184. package/dist/components/streaming-text/streaming-text.d.ts +98 -0
  185. package/dist/components/streaming-text/streaming-text.js +209 -0
  186. package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
  187. package/dist/components/streaming-text/streaming-text.styles.js +62 -0
  188. package/dist/components/switch/switch.d.ts +58 -0
  189. package/dist/components/switch/switch.js +179 -0
  190. package/dist/components/switch/switch.styles.d.ts +1 -0
  191. package/dist/components/switch/switch.styles.js +75 -0
  192. package/dist/components/table/table.d.ts +73 -4
  193. package/dist/components/table/table.js +169 -20
  194. package/dist/components/table/table.styles.js +6 -1
  195. package/dist/components/tabs/tabs.d.ts +51 -0
  196. package/dist/components/tabs/tabs.js +190 -0
  197. package/dist/components/tabs/tabs.styles.d.ts +1 -0
  198. package/dist/components/tabs/tabs.styles.js +63 -0
  199. package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
  200. package/dist/components/thinking-panel/thinking-panel.js +235 -0
  201. package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
  202. package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
  203. package/dist/components/toast/toast-item.d.ts +1 -0
  204. package/dist/components/toast/toast-item.js +31 -6
  205. package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
  206. package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
  207. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
  208. package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
  209. package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
  210. package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
  211. package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
  212. package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
  213. package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
  214. package/dist/components/tool-param-form/tool-param-form.js +366 -0
  215. package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
  216. package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
  217. package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
  218. package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
  219. package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
  220. package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
  221. package/dist/components/tool-result-view/registry.d.ts +81 -0
  222. package/dist/components/tool-result-view/registry.js +84 -0
  223. package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
  224. package/dist/components/tool-result-view/tool-result-view.js +138 -0
  225. package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
  226. package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
  227. package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
  228. package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
  229. package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
  230. package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
  231. package/dist/components/tree/tree-node.d.ts +11 -2
  232. package/dist/components/tree/tree-node.js +16 -6
  233. package/dist/components/tree/tree.d.ts +8 -2
  234. package/dist/components/tree/tree.js +18 -11
  235. package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
  236. package/dist/components/typing-indicator/typing-indicator.js +96 -0
  237. package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
  238. package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
  239. package/dist/components/virtual-list/virtual-list.d.ts +173 -0
  240. package/dist/components/virtual-list/virtual-list.js +461 -0
  241. package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
  242. package/dist/components/virtual-list/virtual-list.styles.js +44 -0
  243. package/dist/components/widget/widget.d.ts +10 -0
  244. package/dist/components/widget/widget.js +24 -1
  245. package/dist/components/widget/widget.styles.js +9 -2
  246. package/dist/components/word-cloud/word-cloud-layout.js +14 -1
  247. package/dist/components/word-cloud/word-cloud.d.ts +6 -0
  248. package/dist/components/word-cloud/word-cloud.js +25 -14
  249. package/dist/components/word-cloud/word-cloud.styles.js +4 -2
  250. package/dist/internal/announcer.d.ts +57 -0
  251. package/dist/internal/announcer.js +71 -0
  252. package/dist/internal/form-associated.d.ts +1 -0
  253. package/dist/internal/form-associated.js +55 -11
  254. package/dist/internal/group-by-recency.d.ts +50 -0
  255. package/dist/internal/group-by-recency.js +80 -0
  256. package/dist/internal/rtl.d.ts +23 -0
  257. package/dist/internal/rtl.js +28 -0
  258. package/dist/internal/scroll-lock.d.ts +10 -5
  259. package/dist/internal/scroll-lock.js +30 -14
  260. package/dist/internal/tokens.styles.js +6 -0
  261. package/dist/lyra.d.ts +130 -31
  262. package/dist/lyra.js +94 -26
  263. package/llms-full.txt +3921 -32
  264. package/llms.txt +16 -1
  265. package/package.json +23 -5
  266. package/dist/components/chart/bar-chart.stories.d.ts +0 -5
  267. package/dist/components/chart/bar-chart.stories.js +0 -20
  268. package/dist/components/chart/box-plot.stories.d.ts +0 -5
  269. package/dist/components/chart/box-plot.stories.js +0 -29
  270. package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
  271. package/dist/components/chart/bubble-chart.stories.js +0 -26
  272. package/dist/components/chart/chart.stories.d.ts +0 -32
  273. package/dist/components/chart/chart.stories.js +0 -141
  274. package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
  275. package/dist/components/chart/doughnut-chart.stories.js +0 -26
  276. package/dist/components/chart/histogram.stories.d.ts +0 -5
  277. package/dist/components/chart/histogram.stories.js +0 -17
  278. package/dist/components/chart/line-chart.stories.d.ts +0 -22
  279. package/dist/components/chart/line-chart.stories.js +0 -99
  280. package/dist/components/chart/lite-chart.stories.d.ts +0 -12
  281. package/dist/components/chart/lite-chart.stories.js +0 -98
  282. package/dist/components/chart/pie-chart.stories.d.ts +0 -5
  283. package/dist/components/chart/pie-chart.stories.js +0 -26
  284. package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
  285. package/dist/components/chart/polar-area-chart.stories.js +0 -20
  286. package/dist/components/chart/radar-chart.stories.d.ts +0 -5
  287. package/dist/components/chart/radar-chart.stories.js +0 -20
  288. package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
  289. package/dist/components/chart/scatter-chart.stories.js +0 -31
  290. package/dist/components/combobox/combobox.stories.d.ts +0 -24
  291. package/dist/components/combobox/combobox.stories.js +0 -102
  292. package/dist/components/date-picker/date-input.stories.d.ts +0 -10
  293. package/dist/components/date-picker/date-input.stories.js +0 -48
  294. package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
  295. package/dist/components/date-picker/date-picker.stories.js +0 -25
  296. package/dist/components/empty/empty.stories.d.ts +0 -7
  297. package/dist/components/empty/empty.stories.js +0 -34
  298. package/dist/components/export-button/export-button.stories.d.ts +0 -7
  299. package/dist/components/export-button/export-button.stories.js +0 -37
  300. package/dist/components/file-input/file-input.stories.d.ts +0 -7
  301. package/dist/components/file-input/file-input.stories.js +0 -19
  302. package/dist/components/flag/flag.stories.d.ts +0 -5
  303. package/dist/components/flag/flag.stories.js +0 -17
  304. package/dist/components/gauge/gauge.stories.d.ts +0 -8
  305. package/dist/components/gauge/gauge.stories.js +0 -19
  306. package/dist/components/graph/graph.stories.d.ts +0 -8
  307. package/dist/components/graph/graph.stories.js +0 -68
  308. package/dist/components/heatmap/heatmap.stories.d.ts +0 -22
  309. package/dist/components/heatmap/heatmap.stories.js +0 -154
  310. package/dist/components/map/map.stories.d.ts +0 -15
  311. package/dist/components/map/map.stories.js +0 -135
  312. package/dist/components/playback/playback.stories.d.ts +0 -7
  313. package/dist/components/playback/playback.stories.js +0 -16
  314. package/dist/components/select/select.stories.d.ts +0 -19
  315. package/dist/components/select/select.stories.js +0 -75
  316. package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
  317. package/dist/components/skeleton/skeleton.stories.js +0 -16
  318. package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
  319. package/dist/components/sparkline/sparkline.stories.js +0 -39
  320. package/dist/components/split/split.stories.d.ts +0 -7
  321. package/dist/components/split/split.stories.js +0 -36
  322. package/dist/components/stat/stat.stories.d.ts +0 -8
  323. package/dist/components/stat/stat.stories.js +0 -64
  324. package/dist/components/table/table.stories.d.ts +0 -12
  325. package/dist/components/table/table.stories.js +0 -67
  326. package/dist/components/time-range/time-range.stories.d.ts +0 -8
  327. package/dist/components/time-range/time-range.stories.js +0 -26
  328. package/dist/components/toast/toast.stories.d.ts +0 -8
  329. package/dist/components/toast/toast.stories.js +0 -72
  330. package/dist/components/tree/tree.stories.d.ts +0 -9
  331. package/dist/components/tree/tree.stories.js +0 -52
  332. package/dist/components/widget/widget.stories.d.ts +0 -7
  333. package/dist/components/widget/widget.stories.js +0 -41
  334. package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
  335. package/dist/components/word-cloud/word-cloud.stories.js +0 -55
@@ -0,0 +1,34 @@
1
+ import type * as MarkedModule from 'marked';
2
+ import type DOMPurifyApi from 'dompurify';
3
+ /**
4
+ * The two optional peers `<lyra-markdown>` needs, loaded independently (see
5
+ * `loadMarkdownAndSanitizer()`). Either half can be `undefined` on its own —
6
+ * a consumer who only installs `marked` (having explicitly opted out of
7
+ * sanitization via `sanitize="false"`) is a valid, supported combination.
8
+ */
9
+ export interface MarkdownDeps {
10
+ marked: typeof MarkedModule | undefined;
11
+ DOMPurify: typeof DOMPurifyApi | undefined;
12
+ }
13
+ /**
14
+ * Independently loads the optional peer dependencies `marked` (Markdown
15
+ * parsing) and `dompurify` (HTML sanitizing), mirroring `chart-loader.ts`'s
16
+ * `loadChartAndZoom()` shape for two independent optional peers. A partial
17
+ * install — most usefully `marked` alone, for a consumer who has explicitly
18
+ * set `sanitize="false"` and doesn't need `dompurify` at all — degrades to
19
+ * "that one half is missing" rather than failing outright. Exported (in
20
+ * addition to the cached `loadMarkdownDeps()` below) so both failure paths —
21
+ * and the real caught error each one logs — are directly testable without
22
+ * needing to actually uninstall either package.
23
+ */
24
+ export declare function loadMarkdownAndSanitizer(importMarked?: () => Promise<typeof MarkedModule>, importDompurify?: () => Promise<{
25
+ default: typeof DOMPurifyApi;
26
+ }>): Promise<MarkdownDeps>;
27
+ /**
28
+ * Lazily loads `marked` + `dompurify` once per page (see
29
+ * `loadMarkdownAndSanitizer()` for why each is loaded and caught
30
+ * independently). Cached the same way `chart-loader.ts`/`map-loader.ts`
31
+ * cache their promise, so every `<lyra-markdown>` instance on a page shares
32
+ * one load.
33
+ */
34
+ export declare function loadMarkdownDeps(): Promise<MarkdownDeps>;
@@ -0,0 +1,43 @@
1
+ let deps;
2
+ /**
3
+ * Independently loads the optional peer dependencies `marked` (Markdown
4
+ * parsing) and `dompurify` (HTML sanitizing), mirroring `chart-loader.ts`'s
5
+ * `loadChartAndZoom()` shape for two independent optional peers. A partial
6
+ * install — most usefully `marked` alone, for a consumer who has explicitly
7
+ * set `sanitize="false"` and doesn't need `dompurify` at all — degrades to
8
+ * "that one half is missing" rather than failing outright. Exported (in
9
+ * addition to the cached `loadMarkdownDeps()` below) so both failure paths —
10
+ * and the real caught error each one logs — are directly testable without
11
+ * needing to actually uninstall either package.
12
+ */
13
+ export async function loadMarkdownAndSanitizer(importMarked = () => import('marked'), importDompurify = () => import('dompurify')) {
14
+ let marked;
15
+ try {
16
+ marked = await importMarked();
17
+ }
18
+ catch (err) {
19
+ console.warn('<lyra-markdown> needs the optional peer dependency `marked` to parse Markdown — install it with `pnpm add marked`:', err);
20
+ }
21
+ let DOMPurify;
22
+ try {
23
+ DOMPurify = (await importDompurify()).default;
24
+ }
25
+ catch (err) {
26
+ console.warn('<lyra-markdown> needs the optional peer dependency `dompurify` to sanitize rendered HTML — install it ' +
27
+ 'with `pnpm add dompurify`. Until then, content only renders when `sanitize="false"` is explicitly set:', err);
28
+ }
29
+ return { marked, DOMPurify };
30
+ }
31
+ /**
32
+ * Lazily loads `marked` + `dompurify` once per page (see
33
+ * `loadMarkdownAndSanitizer()` for why each is loaded and caught
34
+ * independently). Cached the same way `chart-loader.ts`/`map-loader.ts`
35
+ * cache their promise, so every `<lyra-markdown>` instance on a page shares
36
+ * one load.
37
+ */
38
+ export function loadMarkdownDeps() {
39
+ if (!deps) {
40
+ deps = loadMarkdownAndSanitizer();
41
+ }
42
+ return deps;
43
+ }
@@ -0,0 +1,81 @@
1
+ import { type TemplateResult, type PropertyValues } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ /**
4
+ * `<lyra-markdown>` — sanitized Markdown-to-HTML rendering (GFM tables,
5
+ * fenced code blocks, links, blockquotes) built on the optional peer
6
+ * dependencies `marked` (parsing) and `dompurify` (sanitizing), both
7
+ * lazy-loaded via `markdown-loader.ts` on first connect.
8
+ *
9
+ * Rendering never ships unsanitized or broken markup silently:
10
+ * - If `marked` fails to load, or throws while parsing malformed input, the
11
+ * component falls back to plain text (`white-space: pre-wrap`, no HTML
12
+ * parsing at all) and fires `lyra-render-error`.
13
+ * - If `sanitize` is `true` (the default) and `dompurify` fails to load, the
14
+ * component *also* falls back to plain text + `lyra-render-error` — it
15
+ * never renders marked's raw HTML output when sanitization was requested
16
+ * (or defaulted to) but is unavailable, even though `marked` itself loaded
17
+ * fine.
18
+ * - If `sanitize` is explicitly `false`, marked's raw output renders as-is
19
+ * regardless of whether `dompurify` is installed — the consumer opted out
20
+ * of sanitization, so `dompurify`'s absence is irrelevant to that path.
21
+ *
22
+ * `heading`/`code`/`blockquote`/`table`/`link` tokens are rendered through a
23
+ * `marked` renderer override that injects `part="..."` attributes directly
24
+ * into the produced HTML — a single pass, not a second DOM walk after
25
+ * insertion.
26
+ *
27
+ * @customElement lyra-markdown
28
+ * @event lyra-link-click - Fired (and the click prevented) when a rendered
29
+ * link's `href` starts with `internal-link-prefix`. `detail: { href:
30
+ * string, internal: true }`. Ordinary external links navigate normally
31
+ * (in `link-target`) and never fire this event.
32
+ * @event lyra-render-error - Fired whenever rendering falls back to plain
33
+ * text. `detail: { error: unknown }`.
34
+ * @csspart content - The wrapper around the rendered (or plain-text
35
+ * fallback) output.
36
+ * @csspart heading - Every rendered `<h1>`–`<h6>`.
37
+ * @csspart code-block - Every rendered fenced/indented `<pre>`.
38
+ * @csspart link - Every rendered `<a>`.
39
+ * @csspart table - Every rendered `<table>`.
40
+ * @csspart blockquote - Every rendered `<blockquote>`.
41
+ */
42
+ export declare class LyraMarkdown extends LyraElement {
43
+ static styles: import("lit").CSSResultGroup[];
44
+ /** The Markdown source to render. */
45
+ content: string;
46
+ /** Sanitize marked's HTML output with DOMPurify before rendering. See the
47
+ * class doc for what happens when this is `true` (the default) but the
48
+ * `dompurify` peer isn't installed. */
49
+ sanitize: boolean;
50
+ /** Enable GitHub-flavored Markdown (tables, strikethrough, autolinks, task lists). */
51
+ gfm: boolean;
52
+ /** `target` applied to every rendered `<a>`; `rel="noopener noreferrer"` is
53
+ * always added alongside it regardless of this value. */
54
+ linkTarget: string;
55
+ /** When set, a rendered link whose `href` starts with this prefix is
56
+ * treated as internal — its click is intercepted and reported via
57
+ * `lyra-link-click` instead of navigating. Empty (the default) means
58
+ * every link is treated as external. */
59
+ internalLinkPrefix: string;
60
+ /** Forward-compatible hint for a dedicated streaming renderer expected to
61
+ * build on this component later (coalescing partial tokens as they
62
+ * arrive) — setting it has no rendering effect yet. Reflects so a
63
+ * consumer can already target `lyra-markdown[streaming]` in CSS ahead of
64
+ * that behavior landing. */
65
+ streaming: boolean;
66
+ private renderedHtml;
67
+ private deps?;
68
+ connectedCallback(): void;
69
+ protected willUpdate(changed: PropertyValues): void;
70
+ protected updated(): void;
71
+ private renderMarkdown;
72
+ private applyFallback;
73
+ private parseMarkdown;
74
+ private onContentClick;
75
+ render(): TemplateResult;
76
+ }
77
+ declare global {
78
+ interface HTMLElementTagNameMap {
79
+ 'lyra-markdown': LyraMarkdown;
80
+ }
81
+ }
@@ -0,0 +1,284 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { html } from 'lit';
8
+ import { property, state } from 'lit/decorators.js';
9
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
10
+ import { LyraElement } from '../../internal/lyra-element.js';
11
+ import { defineElement } from '../../internal/prefix.js';
12
+ import { loadMarkdownDeps } from './markdown-loader.js';
13
+ import { styles } from './markdown.styles.js';
14
+ const HTML_ESCAPES = {
15
+ '&': '&amp;',
16
+ '<': '&lt;',
17
+ '>': '&gt;',
18
+ '"': '&quot;',
19
+ "'": '&#39;',
20
+ };
21
+ function escapeHtml(text) {
22
+ return text.replace(/[&<>"']/g, (ch) => HTML_ESCAPES[ch] ?? ch);
23
+ }
24
+ /**
25
+ * Mirrors marked's own default `link()` renderer's defensive href guard: a
26
+ * malformed percent-escape or lone UTF-16 surrogate in the raw href throws
27
+ * inside `encodeURI`, and marked's default renderer responds by dropping the
28
+ * anchor (rendering the link text alone) rather than emitting a broken
29
+ * `href`. `encodeURI`'s *return value* is discarded on purpose, not applied —
30
+ * calling it a second time on an already-percent-encoded URL (the common
31
+ * case for a real markdown link) would double-encode every existing `%XX`
32
+ * escape.
33
+ */
34
+ function isValidHref(href) {
35
+ try {
36
+ encodeURI(href);
37
+ return true;
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ /**
44
+ * `<lyra-markdown>` — sanitized Markdown-to-HTML rendering (GFM tables,
45
+ * fenced code blocks, links, blockquotes) built on the optional peer
46
+ * dependencies `marked` (parsing) and `dompurify` (sanitizing), both
47
+ * lazy-loaded via `markdown-loader.ts` on first connect.
48
+ *
49
+ * Rendering never ships unsanitized or broken markup silently:
50
+ * - If `marked` fails to load, or throws while parsing malformed input, the
51
+ * component falls back to plain text (`white-space: pre-wrap`, no HTML
52
+ * parsing at all) and fires `lyra-render-error`.
53
+ * - If `sanitize` is `true` (the default) and `dompurify` fails to load, the
54
+ * component *also* falls back to plain text + `lyra-render-error` — it
55
+ * never renders marked's raw HTML output when sanitization was requested
56
+ * (or defaulted to) but is unavailable, even though `marked` itself loaded
57
+ * fine.
58
+ * - If `sanitize` is explicitly `false`, marked's raw output renders as-is
59
+ * regardless of whether `dompurify` is installed — the consumer opted out
60
+ * of sanitization, so `dompurify`'s absence is irrelevant to that path.
61
+ *
62
+ * `heading`/`code`/`blockquote`/`table`/`link` tokens are rendered through a
63
+ * `marked` renderer override that injects `part="..."` attributes directly
64
+ * into the produced HTML — a single pass, not a second DOM walk after
65
+ * insertion.
66
+ *
67
+ * @customElement lyra-markdown
68
+ * @event lyra-link-click - Fired (and the click prevented) when a rendered
69
+ * link's `href` starts with `internal-link-prefix`. `detail: { href:
70
+ * string, internal: true }`. Ordinary external links navigate normally
71
+ * (in `link-target`) and never fire this event.
72
+ * @event lyra-render-error - Fired whenever rendering falls back to plain
73
+ * text. `detail: { error: unknown }`.
74
+ * @csspart content - The wrapper around the rendered (or plain-text
75
+ * fallback) output.
76
+ * @csspart heading - Every rendered `<h1>`–`<h6>`.
77
+ * @csspart code-block - Every rendered fenced/indented `<pre>`.
78
+ * @csspart link - Every rendered `<a>`.
79
+ * @csspart table - Every rendered `<table>`.
80
+ * @csspart blockquote - Every rendered `<blockquote>`.
81
+ */
82
+ export class LyraMarkdown extends LyraElement {
83
+ constructor() {
84
+ super(...arguments);
85
+ /** The Markdown source to render. */
86
+ this.content = '';
87
+ /** Sanitize marked's HTML output with DOMPurify before rendering. See the
88
+ * class doc for what happens when this is `true` (the default) but the
89
+ * `dompurify` peer isn't installed. */
90
+ this.sanitize = true;
91
+ /** Enable GitHub-flavored Markdown (tables, strikethrough, autolinks, task lists). */
92
+ this.gfm = true;
93
+ /** `target` applied to every rendered `<a>`; `rel="noopener noreferrer"` is
94
+ * always added alongside it regardless of this value. */
95
+ this.linkTarget = '_blank';
96
+ /** When set, a rendered link whose `href` starts with this prefix is
97
+ * treated as internal — its click is intercepted and reported via
98
+ * `lyra-link-click` instead of navigating. Empty (the default) means
99
+ * every link is treated as external. */
100
+ this.internalLinkPrefix = '';
101
+ /** Forward-compatible hint for a dedicated streaming renderer expected to
102
+ * build on this component later (coalescing partial tokens as they
103
+ * arrive) — setting it has no rendering effect yet. Reflects so a
104
+ * consumer can already target `lyra-markdown[streaming]` in CSS ahead of
105
+ * that behavior landing. */
106
+ this.streaming = false;
107
+ // `null` covers both "the optional peers are still loading" and "a render
108
+ // attempt just fell back after a failure" — the two states intentionally
109
+ // look identical (plain text, see render()) since a consumer distinguishes
110
+ // them via `lyra-render-error`, not a visual difference.
111
+ this.renderedHtml = null;
112
+ // A single delegated listener on the content wrapper (not one per <a>) —
113
+ // the rendered markup is fully replaced on every content change, so a
114
+ // per-anchor listener would need re-attaching on every render anyway.
115
+ this.onContentClick = (e) => {
116
+ const prefix = this.internalLinkPrefix;
117
+ if (!prefix)
118
+ return;
119
+ const anchor = e.composedPath().find((el) => el instanceof HTMLAnchorElement);
120
+ if (!anchor)
121
+ return;
122
+ // Compared against the raw `href` *attribute*, not the `.href` IDL
123
+ // property — the property is always browser-resolved to an absolute URL
124
+ // (e.g. "https://example.com/docs") even for a relative/prefixed path,
125
+ // which would never match a relative `internal-link-prefix`.
126
+ const href = anchor.getAttribute('href') ?? '';
127
+ if (!href.startsWith(prefix))
128
+ return;
129
+ e.preventDefault();
130
+ this.emit('lyra-link-click', { href, internal: true });
131
+ };
132
+ }
133
+ static { this.styles = [LyraElement.styles, styles]; }
134
+ connectedCallback() {
135
+ super.connectedCallback();
136
+ void loadMarkdownDeps().then((resolved) => {
137
+ this.deps = resolved;
138
+ this.renderMarkdown();
139
+ });
140
+ }
141
+ // Runs before render (not updated()) so mutating the `renderedHtml` state
142
+ // property below is absorbed into the *same* update cycle instead of
143
+ // scheduling a second one -- Lit's documented pattern for deriving one
144
+ // reactive property from a change to others.
145
+ willUpdate(changed) {
146
+ if (!this.deps) {
147
+ // Still loading the optional peers — the connectedCallback promise
148
+ // above calls renderMarkdown() itself once they resolve, using
149
+ // whatever property values are current at that time.
150
+ return;
151
+ }
152
+ if (changed.has('content') || changed.has('sanitize') || changed.has('gfm') || changed.has('linkTarget')) {
153
+ this.renderMarkdown();
154
+ }
155
+ }
156
+ updated() {
157
+ if (this.deps) {
158
+ this.removeAttribute('aria-busy');
159
+ }
160
+ else {
161
+ this.setAttribute('aria-busy', 'true');
162
+ }
163
+ }
164
+ renderMarkdown() {
165
+ const deps = this.deps;
166
+ if (!deps)
167
+ return;
168
+ if (!deps.marked) {
169
+ // markdown-loader.ts already logged the specific import failure.
170
+ this.applyFallback(new Error('<lyra-markdown> could not render: the "marked" peer dependency failed to load.'));
171
+ return;
172
+ }
173
+ let rawHtml;
174
+ try {
175
+ rawHtml = this.parseMarkdown(deps.marked);
176
+ }
177
+ catch (error) {
178
+ this.applyFallback(error);
179
+ return;
180
+ }
181
+ if (!this.sanitize) {
182
+ // Consumer explicitly opted out of sanitization — dompurify's
183
+ // presence or absence is irrelevant to this path.
184
+ this.renderedHtml = rawHtml;
185
+ return;
186
+ }
187
+ if (!deps.DOMPurify) {
188
+ const error = new Error('<lyra-markdown> could not render: sanitize is enabled (the default) but the "dompurify" peer ' +
189
+ 'dependency failed to load — refusing to render unsanitized HTML. Install it with `pnpm add ' +
190
+ 'dompurify`, or set sanitize="false" to explicitly opt out of sanitization.');
191
+ console.warn(error.message);
192
+ this.applyFallback(error);
193
+ return;
194
+ }
195
+ // `target` is not in DOMPurify's default attribute allowlist (unlike
196
+ // `part`/`rel`/`class`, which already are) — without ADD_ATTR here,
197
+ // every rendered link's target="..." would be silently stripped even
198
+ // though the anchor itself survives sanitization.
199
+ this.renderedHtml = deps.DOMPurify.sanitize(rawHtml, { ADD_ATTR: ['target'] });
200
+ }
201
+ applyFallback(error) {
202
+ this.renderedHtml = null;
203
+ this.emit('lyra-render-error', { error });
204
+ }
205
+ parseMarkdown(marked) {
206
+ const linkTarget = this.linkTarget || '_blank';
207
+ const instance = new marked.Marked();
208
+ // A fresh renderer per parse (rather than a shared/cached one) so it
209
+ // always closes over the *current* `linkTarget` — this property can
210
+ // change between renders, and marked's `.use()` otherwise persists
211
+ // whatever renderer it was given for the lifetime of the instance.
212
+ instance.use({
213
+ renderer: {
214
+ heading(token) {
215
+ return `<h${token.depth} part="heading">${this.parser.parseInline(token.tokens)}</h${token.depth}>\n`;
216
+ },
217
+ code(token) {
218
+ const lang = (token.lang ?? '').trim().split(/\s+/)[0] ?? '';
219
+ const body = `${token.text.replace(/\n$/, '')}\n`;
220
+ const text = token.escaped ? body : escapeHtml(body);
221
+ const cls = lang ? ` class="language-${escapeHtml(lang)}"` : '';
222
+ return `<pre part="code-block"><code${cls}>${text}</code></pre>\n`;
223
+ },
224
+ blockquote(token) {
225
+ return `<blockquote part="blockquote">\n${this.parser.parse(token.tokens)}</blockquote>\n`;
226
+ },
227
+ table(token) {
228
+ let headerRow = '';
229
+ for (const cell of token.header)
230
+ headerRow += this.tablecell(cell);
231
+ let bodyRows = '';
232
+ for (const row of token.rows) {
233
+ let rowHtml = '';
234
+ for (const cell of row)
235
+ rowHtml += this.tablecell(cell);
236
+ bodyRows += this.tablerow({ text: rowHtml });
237
+ }
238
+ const thead = `<thead>\n${this.tablerow({ text: headerRow })}</thead>\n`;
239
+ const tbody = bodyRows ? `<tbody>${bodyRows}</tbody>\n` : '';
240
+ return `<table part="table">\n${thead}${tbody}</table>\n`;
241
+ },
242
+ link(token) {
243
+ const text = this.parser.parseInline(token.tokens);
244
+ if (!isValidHref(token.href))
245
+ return text;
246
+ const titleAttr = token.title ? ` title="${escapeHtml(token.title)}"` : '';
247
+ return (`<a part="link" href="${escapeHtml(token.href)}"${titleAttr} ` +
248
+ `target="${escapeHtml(linkTarget)}" rel="noopener noreferrer">${text}</a>`);
249
+ },
250
+ },
251
+ });
252
+ return instance.parse(this.content, { gfm: this.gfm, async: false });
253
+ }
254
+ render() {
255
+ const isFallback = this.renderedHtml === null;
256
+ return html `
257
+ <div part="content" ?data-fallback=${isFallback} @click=${this.onContentClick}>
258
+ ${isFallback ? this.content : unsafeHTML(this.renderedHtml)}
259
+ </div>
260
+ `;
261
+ }
262
+ }
263
+ __decorate([
264
+ property()
265
+ ], LyraMarkdown.prototype, "content", void 0);
266
+ __decorate([
267
+ property({ type: Boolean })
268
+ ], LyraMarkdown.prototype, "sanitize", void 0);
269
+ __decorate([
270
+ property({ type: Boolean })
271
+ ], LyraMarkdown.prototype, "gfm", void 0);
272
+ __decorate([
273
+ property({ attribute: 'link-target' })
274
+ ], LyraMarkdown.prototype, "linkTarget", void 0);
275
+ __decorate([
276
+ property({ attribute: 'internal-link-prefix' })
277
+ ], LyraMarkdown.prototype, "internalLinkPrefix", void 0);
278
+ __decorate([
279
+ property({ type: Boolean, reflect: true })
280
+ ], LyraMarkdown.prototype, "streaming", void 0);
281
+ __decorate([
282
+ state()
283
+ ], LyraMarkdown.prototype, "renderedHtml", void 0);
284
+ defineElement('markdown', LyraMarkdown);
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,82 @@
1
+ import { css } from 'lit';
2
+ export const styles = css `
3
+ :host {
4
+ display: block;
5
+ /* Contained here (rather than left as a bare font-family literal) so a
6
+ host page can retheme it -- same rationale as lyra-json-viewer's
7
+ identical --lyra-json-viewer-font, no shared --wa-*/--lyra-*
8
+ monospace token exists to resolve through. */
9
+ --lyra-markdown-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
10
+ line-height: 1.6;
11
+ }
12
+ [part='content'] {
13
+ overflow-wrap: break-word;
14
+ }
15
+ /* Shared by both the "still loading" and "fell back after a failure"
16
+ states -- see the renderedHtml field doc in markdown.ts. */
17
+ [part='content'][data-fallback] {
18
+ white-space: pre-wrap;
19
+ font-family: inherit;
20
+ }
21
+ [part='content'] > :first-child {
22
+ margin-block-start: 0;
23
+ }
24
+ [part='content'] > :last-child {
25
+ margin-block-end: 0;
26
+ }
27
+ [part='heading'] {
28
+ line-height: 1.25;
29
+ margin-block: var(--lyra-space-l) var(--lyra-space-s);
30
+ }
31
+ [part='content'] p,
32
+ [part='content'] ul,
33
+ [part='content'] ol {
34
+ margin-block: 0 var(--lyra-space-s);
35
+ }
36
+ [part='content'] code {
37
+ font-family: var(--lyra-markdown-font-mono);
38
+ font-size: 0.875em;
39
+ background: var(--lyra-color-brand-quiet);
40
+ border-radius: 0.1875rem;
41
+ padding: 0.125rem 0.3125rem;
42
+ }
43
+ [part='code-block'] {
44
+ margin-block: 0 var(--lyra-space-s);
45
+ padding: var(--lyra-space-s) var(--lyra-space-m);
46
+ border-radius: var(--lyra-radius);
47
+ background: var(--lyra-color-brand-quiet);
48
+ overflow-x: auto;
49
+ }
50
+ [part='code-block'] code {
51
+ padding: 0;
52
+ background: none;
53
+ font-size: 0.8125rem;
54
+ line-height: 1.5;
55
+ }
56
+ [part='link'] {
57
+ color: var(--lyra-color-brand);
58
+ text-underline-offset: 0.125rem;
59
+ }
60
+ [part='blockquote'] {
61
+ margin-block: 0 var(--lyra-space-s);
62
+ margin-inline: 0;
63
+ padding-inline-start: var(--lyra-space-m);
64
+ border-inline-start: 3px solid var(--lyra-color-border);
65
+ color: var(--lyra-color-text-quiet);
66
+ }
67
+ [part='table'] {
68
+ border-collapse: collapse;
69
+ margin-block: 0 var(--lyra-space-s);
70
+ inline-size: 100%;
71
+ }
72
+ [part='table'] th,
73
+ [part='table'] td {
74
+ border: 1px solid var(--lyra-color-border);
75
+ padding: var(--lyra-space-xs) var(--lyra-space-s);
76
+ text-align: start;
77
+ }
78
+ [part='table'] th {
79
+ background: var(--lyra-color-brand-quiet);
80
+ font-weight: 600;
81
+ }
82
+ `;
@@ -0,0 +1,122 @@
1
+ import { type TemplateResult } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ export type MediaCardKind = 'image' | 'video' | 'file';
4
+ export interface MediaCardOpenDetail {
5
+ src: string;
6
+ filename: string;
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. */
12
+ 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. */
16
+ export declare function safeLinkHref(url: string): string | null;
17
+ /**
18
+ * `<lyra-media-card>` — a lightweight inline preview for one already-sent,
19
+ * already-available attachment inside a rendered chat message body (e.g.
20
+ * plugged into `<lyra-chat-message>`'s `attachments` slot, or embedded
21
+ * directly by a markdown/message renderer). Distinct from two other,
22
+ * similarly-named components in this family: `<lyra-document-preview>` is a
23
+ * fuller viewer with header chrome and an async server-side-conversion
24
+ * state machine (`status="converting"`), and `<lyra-attachment-chip>` is a
25
+ * pre-send queued-file chip with upload progress. This component has
26
+ * neither concern — it only ever shows a `src` that's already final.
27
+ *
28
+ * **Format dispatch.** `kind` selects `'image'` (a plain `<img>`), `'video'`
29
+ * (a plain `<video controls>`), or `'file'` (an icon + filename chip with a
30
+ * download/open affordance). Leave `kind` unset to auto-detect from
31
+ * `mime-type` (`image/*` / `video/*` / anything else -> `'file'`);
32
+ * explicitly setting `kind` always wins over detection.
33
+ *
34
+ * **Safe-URL checking.** `src` is validated before it's ever assigned to an
35
+ * `<img>`/`<video>` `src` or an `<a href>` — only `http:`/`https:`/`blob:`
36
+ * (plus `data:` for a *media* `src` only) or a scheme-relative/relative URL
37
+ * 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.
48
+ *
49
+ * **The `video` case renders its open affordance separately from `base`.**
50
+ * `image`/`file` wrap their *entire* card in one native `<button>`/`<a>` —
51
+ * safe because an `<img>` and a plain icon+text chip have no interactive
52
+ * content of their own to conflict with. A `<video controls>` element is
53
+ * itself interactive content (its own play/seek/volume controls), and HTML
54
+ * forbids nesting interactive content inside a `<button>`/`<a>` — doing so
55
+ * anyway would also make every click on the video's own controls bubble up
56
+ * and spuriously fire `lyra-open`. So for `kind="video"`, `base` is a plain,
57
+ * non-interactive wrapper around `[part="media"]`, and a small separate
58
+ * `[part="open-button"]` (not one of this component's originally-scoped
59
+ * parts, added as the "explicit view/open affordance" the class is free to
60
+ * provide) is the thing that actually fires `lyra-open`.
61
+ *
62
+ * **Navigation.** This component never navigates on its own for `image`/
63
+ * `video` — activating the card only fires `lyra-open`; a host decides what
64
+ * "open" means (a lightbox, a new tab, whatever). The `file`-chip case is
65
+ * the one exception: when `src` passes the (stricter) href safety check, the
66
+ * chip is a real `<a href download>` so a bare drop-in still does something
67
+ * useful — but `lyra-open` fires first and is `cancelable`; a host that
68
+ * calls `preventDefault()` on it suppresses that default download/open so it
69
+ * can substitute its own handling instead.
70
+ *
71
+ * @customElement lyra-media-card
72
+ * @event lyra-open - The card (or, for `kind="video"`, its `open-button`)
73
+ * was activated. `detail: { src, filename }`. Cancelable — see the class
74
+ * doc's "Navigation" section for what calling `preventDefault()` on it does
75
+ * in the `file`-chip case.
76
+ * @csspart base - The root interactive/container element. A `<button>` for
77
+ * `kind="image"`, a plain wrapper `<div>` for `kind="video"`, and either an
78
+ * `<a>` (when `src` passes the href safety check) or a plain `<span>`
79
+ * (otherwise) for the `file`-chip fallback.
80
+ * @csspart media - The `<img>` or `<video>` element.
81
+ * @csspart file-icon - The generic file glyph, shown only in the file-chip fallback.
82
+ * @csspart filename - The filename text, shown only in the file-chip fallback.
83
+ * @csspart open-button - The explicit "open" affordance rendered next to
84
+ * `[part="media"]` for `kind="video"` only — see the class doc.
85
+ */
86
+ export declare class LyraMediaCard extends LyraElement {
87
+ static styles: import("lit").CSSResultGroup[];
88
+ /** The media URL. Always re-validated against a safe-scheme allowlist
89
+ * before use — see the class doc. */
90
+ src: string;
91
+ /** Explicit format dispatch. Leave unset to auto-detect from `mime-type`. */
92
+ kind?: MediaCardKind;
93
+ /** Drives auto-detection when `kind` is unset. */
94
+ mimeType: string;
95
+ /** Shown in the file-chip fallback, used as the download link's suggested
96
+ * filename, and folded into the accessible name of the whole card. */
97
+ filename: string;
98
+ /** Alt text for the image case (and reused as a video label fallback).
99
+ * Falls back to `filename`, then a generic per-kind description. */
100
+ alt: string;
101
+ /** Effective kind used for rendering — `kind` if explicitly set,
102
+ * otherwise detected from `mime-type`. */
103
+ private get resolvedKind();
104
+ private get displayFilename();
105
+ /** Accessible name for the card's own actionable element (`base` or, for
106
+ * video, `open-button`) — always phrased as the action it performs. */
107
+ private get accessibleLabel();
108
+ private get imgAlt();
109
+ private get videoLabel();
110
+ private emitOpen;
111
+ private onActivate;
112
+ private onLinkClick;
113
+ private renderImage;
114
+ private renderVideo;
115
+ private renderFileFallback;
116
+ render(): TemplateResult;
117
+ }
118
+ declare global {
119
+ interface HTMLElementTagNameMap {
120
+ 'lyra-media-card': LyraMediaCard;
121
+ }
122
+ }