@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.
- package/README.md +11 -0
- package/custom-elements.json +23341 -11202
- package/dist/components/app-rail/app-rail.d.ts +171 -0
- package/dist/components/app-rail/app-rail.js +483 -0
- package/dist/components/app-rail/app-rail.styles.d.ts +1 -0
- package/dist/components/app-rail/app-rail.styles.js +125 -0
- package/dist/components/attachment-chip/attachment-chip.d.ts +114 -0
- package/dist/components/attachment-chip/attachment-chip.js +312 -0
- package/dist/components/attachment-chip/attachment-chip.styles.d.ts +1 -0
- package/dist/components/attachment-chip/attachment-chip.styles.js +184 -0
- package/dist/components/attachment-trigger/attachment-trigger.d.ts +83 -0
- package/dist/components/attachment-trigger/attachment-trigger.js +258 -0
- package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts +1 -0
- package/dist/components/attachment-trigger/attachment-trigger.styles.js +68 -0
- package/dist/components/chart/box-plot.js +11 -1
- package/dist/components/chart/chart-loader.d.ts +44 -13
- package/dist/components/chart/chart-loader.js +65 -18
- package/dist/components/chart/chart.d.ts +1 -2
- package/dist/components/chart/chart.js +67 -12
- package/dist/components/chart/histogram-bin.d.ts +4 -1
- package/dist/components/chart/histogram-bin.js +24 -14
- package/dist/components/chart/lite-chart.d.ts +54 -0
- package/dist/components/chart/lite-chart.js +140 -22
- package/dist/components/chart/lite-chart.styles.js +9 -0
- package/dist/components/chat-composer/chat-composer.d.ts +92 -0
- package/dist/components/chat-composer/chat-composer.js +285 -0
- package/dist/components/chat-composer/chat-composer.styles.d.ts +1 -0
- package/dist/components/chat-composer/chat-composer.styles.js +135 -0
- package/dist/components/chat-message/chat-message.d.ts +113 -0
- package/dist/components/chat-message/chat-message.js +291 -0
- package/dist/components/chat-message/chat-message.styles.d.ts +1 -0
- package/dist/components/chat-message/chat-message.styles.js +203 -0
- package/dist/components/checkbox/checkbox.d.ts +59 -0
- package/dist/components/checkbox/checkbox.js +230 -0
- package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
- package/dist/components/checkbox/checkbox.styles.js +70 -0
- package/dist/components/chip/chip-group.d.ts +56 -0
- package/dist/components/chip/chip-group.js +124 -0
- package/dist/components/chip/chip-group.styles.d.ts +1 -0
- package/dist/components/chip/chip-group.styles.js +57 -0
- package/dist/components/chip/chip.d.ts +64 -0
- package/dist/components/chip/chip.js +115 -0
- package/dist/components/chip/chip.styles.d.ts +1 -0
- package/dist/components/chip/chip.styles.js +115 -0
- package/dist/components/citation-badge/citation-badge.d.ts +113 -0
- package/dist/components/citation-badge/citation-badge.js +300 -0
- package/dist/components/citation-badge/citation-badge.styles.d.ts +1 -0
- package/dist/components/citation-badge/citation-badge.styles.js +96 -0
- package/dist/components/code-block/code-block.d.ts +89 -0
- package/dist/components/code-block/code-block.js +317 -0
- package/dist/components/code-block/code-block.styles.d.ts +1 -0
- package/dist/components/code-block/code-block.styles.js +187 -0
- package/dist/components/code-block/code-loader.d.ts +44 -0
- package/dist/components/code-block/code-loader.js +79 -0
- package/dist/components/combobox/combobox.d.ts +19 -1
- package/dist/components/combobox/combobox.js +84 -14
- package/dist/components/combobox/option.d.ts +5 -1
- package/dist/components/combobox/option.js +35 -0
- package/dist/components/context-meter/context-meter.d.ts +53 -0
- package/dist/components/context-meter/context-meter.js +150 -0
- package/dist/components/context-meter/context-meter.styles.d.ts +1 -0
- package/dist/components/context-meter/context-meter.styles.js +120 -0
- package/dist/components/conversation-item/conversation-item.d.ts +128 -0
- package/dist/components/conversation-item/conversation-item.js +322 -0
- package/dist/components/conversation-item/conversation-item.styles.d.ts +1 -0
- package/dist/components/conversation-item/conversation-item.styles.js +155 -0
- package/dist/components/date-picker/date-input.d.ts +24 -0
- package/dist/components/date-picker/date-input.js +134 -22
- package/dist/components/date-picker/date-picker.d.ts +7 -0
- package/dist/components/date-picker/date-picker.js +85 -16
- package/dist/components/dialog/confirm.d.ts +40 -0
- package/dist/components/dialog/confirm.js +79 -0
- package/dist/components/dialog/dialog.d.ts +85 -0
- package/dist/components/dialog/dialog.js +272 -0
- package/dist/components/dialog/dialog.styles.d.ts +1 -0
- package/dist/components/dialog/dialog.styles.js +52 -0
- package/dist/components/dock-panel/dock-panel.d.ts +134 -0
- package/dist/components/dock-panel/dock-panel.js +345 -0
- package/dist/components/dock-panel/dock-panel.styles.d.ts +1 -0
- package/dist/components/dock-panel/dock-panel.styles.js +145 -0
- package/dist/components/document-preview/document-preview.d.ts +135 -0
- package/dist/components/document-preview/document-preview.js +372 -0
- package/dist/components/document-preview/document-preview.styles.d.ts +1 -0
- package/dist/components/document-preview/document-preview.styles.js +191 -0
- package/dist/components/export-button/csv.js +1 -1
- package/dist/components/export-button/export-button.js +2 -0
- package/dist/components/flag/flag.d.ts +29 -13
- package/dist/components/flag/flag.js +39 -13
- package/dist/components/gauge/gauge.js +19 -10
- package/dist/components/generation-status/generation-status.d.ts +109 -0
- package/dist/components/generation-status/generation-status.js +273 -0
- package/dist/components/generation-status/generation-status.styles.d.ts +1 -0
- package/dist/components/generation-status/generation-status.styles.js +84 -0
- package/dist/components/heatmap/calendar-grid.d.ts +4 -1
- package/dist/components/heatmap/calendar-grid.js +25 -12
- package/dist/components/heatmap/heatmap-scale.d.ts +6 -0
- package/dist/components/heatmap/heatmap-scale.js +7 -1
- package/dist/components/heatmap/heatmap.d.ts +74 -0
- package/dist/components/heatmap/heatmap.js +135 -23
- package/dist/components/json-viewer/json-viewer.d.ts +53 -0
- package/dist/components/json-viewer/json-viewer.js +283 -0
- package/dist/components/json-viewer/json-viewer.styles.d.ts +1 -0
- package/dist/components/json-viewer/json-viewer.styles.js +176 -0
- package/dist/components/kbd/kbd.d.ts +77 -0
- package/dist/components/kbd/kbd.js +214 -0
- package/dist/components/kbd/kbd.styles.d.ts +1 -0
- package/dist/components/kbd/kbd.styles.js +52 -0
- package/dist/components/live-region/live-region.d.ts +81 -0
- package/dist/components/live-region/live-region.js +153 -0
- package/dist/components/live-region/live-region.styles.d.ts +1 -0
- package/dist/components/live-region/live-region.styles.js +10 -0
- package/dist/components/map/map.d.ts +8 -6
- package/dist/components/map/map.js +44 -8
- package/dist/components/markdown/markdown-loader.d.ts +34 -0
- package/dist/components/markdown/markdown-loader.js +43 -0
- package/dist/components/markdown/markdown.d.ts +81 -0
- package/dist/components/markdown/markdown.js +284 -0
- package/dist/components/markdown/markdown.styles.d.ts +1 -0
- package/dist/components/markdown/markdown.styles.js +82 -0
- package/dist/components/media-card/media-card.d.ts +122 -0
- package/dist/components/media-card/media-card.js +295 -0
- package/dist/components/media-card/media-card.styles.d.ts +1 -0
- package/dist/components/media-card/media-card.styles.js +132 -0
- package/dist/components/mention-popover/mention-popover.d.ts +200 -0
- package/dist/components/mention-popover/mention-popover.js +475 -0
- package/dist/components/mention-popover/mention-popover.styles.d.ts +1 -0
- package/dist/components/mention-popover/mention-popover.styles.js +101 -0
- package/dist/components/menu/menu-item.d.ts +62 -0
- package/dist/components/menu/menu-item.js +119 -0
- package/dist/components/menu/menu-item.styles.d.ts +1 -0
- package/dist/components/menu/menu-item.styles.js +67 -0
- package/dist/components/menu/menu.d.ts +143 -0
- package/dist/components/menu/menu.js +365 -0
- package/dist/components/menu/menu.styles.d.ts +1 -0
- package/dist/components/menu/menu.styles.js +66 -0
- package/dist/components/model-select/model-select.d.ts +125 -0
- package/dist/components/model-select/model-select.js +490 -0
- package/dist/components/model-select/model-select.styles.d.ts +1 -0
- package/dist/components/model-select/model-select.styles.js +189 -0
- package/dist/components/model-settings-panel/model-settings-panel.d.ts +69 -0
- package/dist/components/model-settings-panel/model-settings-panel.js +145 -0
- package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts +1 -0
- package/dist/components/model-settings-panel/model-settings-panel.styles.js +82 -0
- package/dist/components/playback/playback.d.ts +13 -1
- package/dist/components/playback/playback.js +47 -14
- package/dist/components/responsive-panel/responsive-panel.d.ts +150 -0
- package/dist/components/responsive-panel/responsive-panel.js +365 -0
- package/dist/components/responsive-panel/responsive-panel.styles.d.ts +1 -0
- package/dist/components/responsive-panel/responsive-panel.styles.js +103 -0
- package/dist/components/result-card/result-card.d.ts +44 -0
- package/dist/components/result-card/result-card.js +83 -0
- package/dist/components/result-card/result-card.styles.d.ts +1 -0
- package/dist/components/result-card/result-card.styles.js +50 -0
- package/dist/components/result-card/result-field.d.ts +36 -0
- package/dist/components/result-card/result-field.js +87 -0
- package/dist/components/result-card/result-field.styles.d.ts +1 -0
- package/dist/components/result-card/result-field.styles.js +32 -0
- package/dist/components/select/select.d.ts +43 -2
- package/dist/components/select/select.js +150 -17
- package/dist/components/slider/slider.d.ts +89 -0
- package/dist/components/slider/slider.js +352 -0
- package/dist/components/slider/slider.styles.d.ts +1 -0
- package/dist/components/slider/slider.styles.js +87 -0
- package/dist/components/source-card/source-card.d.ts +92 -0
- package/dist/components/source-card/source-card.js +148 -0
- package/dist/components/source-card/source-card.styles.d.ts +1 -0
- package/dist/components/source-card/source-card.styles.js +69 -0
- package/dist/components/source-list/source-list.d.ts +63 -0
- package/dist/components/source-list/source-list.js +122 -0
- package/dist/components/source-list/source-list.styles.d.ts +1 -0
- package/dist/components/source-list/source-list.styles.js +59 -0
- package/dist/components/sparkline/sparkline.js +27 -13
- package/dist/components/split/split.d.ts +93 -1
- package/dist/components/split/split.js +284 -15
- package/dist/components/split/split.styles.js +30 -0
- package/dist/components/stat/stat.d.ts +6 -1
- package/dist/components/stat/stat.js +8 -2
- package/dist/components/stream-status/stream-status.d.ts +110 -0
- package/dist/components/stream-status/stream-status.js +210 -0
- package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
- package/dist/components/stream-status/stream-status.styles.js +103 -0
- package/dist/components/streaming-text/streaming-text.d.ts +98 -0
- package/dist/components/streaming-text/streaming-text.js +209 -0
- package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
- package/dist/components/streaming-text/streaming-text.styles.js +62 -0
- package/dist/components/switch/switch.d.ts +58 -0
- package/dist/components/switch/switch.js +179 -0
- package/dist/components/switch/switch.styles.d.ts +1 -0
- package/dist/components/switch/switch.styles.js +75 -0
- package/dist/components/table/table.d.ts +73 -4
- package/dist/components/table/table.js +169 -20
- package/dist/components/table/table.styles.js +6 -1
- package/dist/components/tabs/tabs.d.ts +51 -0
- package/dist/components/tabs/tabs.js +190 -0
- package/dist/components/tabs/tabs.styles.d.ts +1 -0
- package/dist/components/tabs/tabs.styles.js +63 -0
- package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
- package/dist/components/thinking-panel/thinking-panel.js +235 -0
- package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
- package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
- package/dist/components/toast/toast-item.d.ts +1 -0
- package/dist/components/toast/toast-item.js +31 -6
- package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
- package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
- package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
- package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
- package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
- package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
- package/dist/components/tool-param-form/tool-param-form.js +366 -0
- package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
- package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
- package/dist/components/tool-result-view/registry.d.ts +81 -0
- package/dist/components/tool-result-view/registry.js +84 -0
- package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
- package/dist/components/tool-result-view/tool-result-view.js +138 -0
- package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
- package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
- package/dist/components/tree/tree-node.d.ts +11 -2
- package/dist/components/tree/tree-node.js +16 -6
- package/dist/components/tree/tree.d.ts +8 -2
- package/dist/components/tree/tree.js +18 -11
- package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
- package/dist/components/typing-indicator/typing-indicator.js +96 -0
- package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
- package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
- package/dist/components/virtual-list/virtual-list.d.ts +173 -0
- package/dist/components/virtual-list/virtual-list.js +461 -0
- package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
- package/dist/components/virtual-list/virtual-list.styles.js +44 -0
- package/dist/components/word-cloud/word-cloud-layout.js +14 -1
- package/dist/components/word-cloud/word-cloud.d.ts +6 -0
- package/dist/components/word-cloud/word-cloud.js +25 -14
- package/dist/components/word-cloud/word-cloud.styles.js +3 -2
- package/dist/internal/announcer.d.ts +57 -0
- package/dist/internal/announcer.js +71 -0
- package/dist/internal/form-associated.d.ts +1 -0
- package/dist/internal/form-associated.js +55 -11
- package/dist/internal/group-by-recency.d.ts +50 -0
- package/dist/internal/group-by-recency.js +80 -0
- package/dist/internal/rtl.d.ts +23 -0
- package/dist/internal/rtl.js +28 -0
- package/dist/internal/scroll-lock.d.ts +10 -5
- package/dist/internal/scroll-lock.js +30 -14
- package/dist/internal/tokens.styles.js +6 -0
- package/dist/lyra.d.ts +130 -31
- package/dist/lyra.js +94 -26
- package/llms-full.txt +3836 -17
- package/llms.txt +16 -1
- package/package.json +23 -5
- package/dist/components/chart/bar-chart.stories.d.ts +0 -5
- package/dist/components/chart/bar-chart.stories.js +0 -20
- package/dist/components/chart/box-plot.stories.d.ts +0 -5
- package/dist/components/chart/box-plot.stories.js +0 -29
- package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
- package/dist/components/chart/bubble-chart.stories.js +0 -26
- package/dist/components/chart/chart.stories.d.ts +0 -32
- package/dist/components/chart/chart.stories.js +0 -141
- package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
- package/dist/components/chart/doughnut-chart.stories.js +0 -26
- package/dist/components/chart/histogram.stories.d.ts +0 -5
- package/dist/components/chart/histogram.stories.js +0 -17
- package/dist/components/chart/line-chart.stories.d.ts +0 -22
- package/dist/components/chart/line-chart.stories.js +0 -99
- package/dist/components/chart/lite-chart.stories.d.ts +0 -14
- package/dist/components/chart/lite-chart.stories.js +0 -114
- package/dist/components/chart/pie-chart.stories.d.ts +0 -5
- package/dist/components/chart/pie-chart.stories.js +0 -26
- package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
- package/dist/components/chart/polar-area-chart.stories.js +0 -20
- package/dist/components/chart/radar-chart.stories.d.ts +0 -5
- package/dist/components/chart/radar-chart.stories.js +0 -20
- package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
- package/dist/components/chart/scatter-chart.stories.js +0 -31
- package/dist/components/combobox/combobox.stories.d.ts +0 -24
- package/dist/components/combobox/combobox.stories.js +0 -102
- package/dist/components/date-picker/date-input.stories.d.ts +0 -10
- package/dist/components/date-picker/date-input.stories.js +0 -48
- package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
- package/dist/components/date-picker/date-picker.stories.js +0 -25
- package/dist/components/empty/empty.stories.d.ts +0 -7
- package/dist/components/empty/empty.stories.js +0 -34
- package/dist/components/export-button/export-button.stories.d.ts +0 -7
- package/dist/components/export-button/export-button.stories.js +0 -37
- package/dist/components/file-input/file-input.stories.d.ts +0 -7
- package/dist/components/file-input/file-input.stories.js +0 -19
- package/dist/components/flag/flag.stories.d.ts +0 -6
- package/dist/components/flag/flag.stories.js +0 -31
- package/dist/components/gauge/gauge.stories.d.ts +0 -8
- package/dist/components/gauge/gauge.stories.js +0 -19
- package/dist/components/graph/graph.stories.d.ts +0 -8
- package/dist/components/graph/graph.stories.js +0 -68
- package/dist/components/heatmap/heatmap.stories.d.ts +0 -27
- package/dist/components/heatmap/heatmap.stories.js +0 -179
- package/dist/components/map/map.stories.d.ts +0 -15
- package/dist/components/map/map.stories.js +0 -135
- package/dist/components/playback/playback.stories.d.ts +0 -7
- package/dist/components/playback/playback.stories.js +0 -16
- package/dist/components/select/select.stories.d.ts +0 -21
- package/dist/components/select/select.stories.js +0 -91
- package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
- package/dist/components/skeleton/skeleton.stories.js +0 -16
- package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
- package/dist/components/sparkline/sparkline.stories.js +0 -39
- package/dist/components/split/split.stories.d.ts +0 -7
- package/dist/components/split/split.stories.js +0 -36
- package/dist/components/stat/stat.stories.d.ts +0 -9
- package/dist/components/stat/stat.stories.js +0 -81
- package/dist/components/table/table.stories.d.ts +0 -12
- package/dist/components/table/table.stories.js +0 -67
- package/dist/components/time-range/time-range.stories.d.ts +0 -8
- package/dist/components/time-range/time-range.stories.js +0 -26
- package/dist/components/toast/toast.stories.d.ts +0 -8
- package/dist/components/toast/toast.stories.js +0 -72
- package/dist/components/tree/tree.stories.d.ts +0 -9
- package/dist/components/tree/tree.stories.js +0 -52
- package/dist/components/widget/widget.stories.d.ts +0 -9
- package/dist/components/widget/widget.stories.js +0 -74
- package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
- 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
|
+
'&': '&',
|
|
16
|
+
'<': '<',
|
|
17
|
+
'>': '>',
|
|
18
|
+
'"': '"',
|
|
19
|
+
"'": ''',
|
|
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
|
+
}
|