ngwr 14.7.0 → 14.9.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 +66 -332
- package/alert/styles/_index.scss +10 -0
- package/avatar/styles/_index.scss +18 -7
- package/badge/styles/_index.scss +7 -0
- package/badge/styles/_tag.scss +7 -1
- package/bar-chart/styles/_index.scss +1 -0
- package/button/styles/_index.scss +6 -0
- package/calendar-heatmap/styles/_index.scss +1 -0
- package/cascader/styles/_rules.scss +87 -15
- package/checkbox/styles/_index.scss +4 -0
- package/command-palette/styles/_index.scss +1 -1
- package/date-picker/styles/_index.scss +30 -0
- package/density/styles/_index.scss +4 -2
- package/divider/styles/_index.scss +2 -0
- package/donut-chart/styles/_index.scss +10 -0
- package/dropdown/styles/_index.scss +67 -5
- package/editor/styles/_index.scss +441 -0
- package/event-calendar/styles/_index.scss +209 -35
- package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -1
- package/fesm2022/ngwr-action-sheet.mjs +3 -3
- package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
- package/fesm2022/ngwr-affix.mjs +3 -3
- package/fesm2022/ngwr-affix.mjs.map +1 -1
- package/fesm2022/ngwr-alert.mjs +3 -3
- package/fesm2022/ngwr-alert.mjs.map +1 -1
- package/fesm2022/ngwr-anchor.mjs +3 -3
- package/fesm2022/ngwr-anchor.mjs.map +1 -1
- package/fesm2022/ngwr-aurora.mjs +3 -3
- package/fesm2022/ngwr-aurora.mjs.map +1 -1
- package/fesm2022/ngwr-avatar.mjs +3 -3
- package/fesm2022/ngwr-avatar.mjs.map +1 -1
- package/fesm2022/ngwr-back-top.mjs +3 -3
- package/fesm2022/ngwr-back-top.mjs.map +1 -1
- package/fesm2022/ngwr-badge.mjs +28 -13
- package/fesm2022/ngwr-badge.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart.mjs +31 -6
- package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
- package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-blur-text.mjs +3 -3
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow.mjs +3 -3
- package/fesm2022/ngwr-border-glow.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +6 -6
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-burger.mjs +3 -3
- package/fesm2022/ngwr-burger.mjs.map +1 -1
- package/fesm2022/ngwr-button-testing.mjs.map +1 -1
- package/fesm2022/ngwr-button.mjs +6 -6
- package/fesm2022/ngwr-button.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap.mjs +61 -6
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-calendar.mjs +3 -3
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +9 -9
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-carousel-testing.mjs.map +1 -1
- package/fesm2022/ngwr-carousel.mjs +6 -6
- package/fesm2022/ngwr-carousel.mjs.map +1 -1
- package/fesm2022/ngwr-cascader-testing.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +4 -4
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +18 -8
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text.mjs +3 -3
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-click-spark.mjs +3 -3
- package/fesm2022/ngwr-click-spark.mjs.map +1 -1
- package/fesm2022/ngwr-clipboard.mjs +3 -3
- package/fesm2022/ngwr-clipboard.mjs.map +1 -1
- package/fesm2022/ngwr-collapse-testing.mjs.map +1 -1
- package/fesm2022/ngwr-collapse.mjs +9 -9
- package/fesm2022/ngwr-collapse.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker.mjs +6 -6
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette.mjs +3 -3
- package/fesm2022/ngwr-command-palette.mjs.map +1 -1
- package/fesm2022/ngwr-compare.mjs +3 -3
- package/fesm2022/ngwr-compare.mjs.map +1 -1
- package/fesm2022/ngwr-confetti.mjs +3 -3
- package/fesm2022/ngwr-confetti.mjs.map +1 -1
- package/fesm2022/ngwr-config.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +12 -12
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-cookie.mjs +3 -3
- package/fesm2022/ngwr-cookie.mjs.map +1 -1
- package/fesm2022/ngwr-counter.mjs +6 -6
- package/fesm2022/ngwr-counter.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapters-fns.mjs +3 -3
- package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
- package/fesm2022/ngwr-date-adapters-luxon.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +721 -62
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-date.mjs +3 -3
- package/fesm2022/ngwr-date.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text.mjs +3 -3
- package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
- package/fesm2022/ngwr-density.mjs +6 -6
- package/fesm2022/ngwr-density.mjs.map +1 -1
- package/fesm2022/ngwr-descriptions.mjs +6 -6
- package/fesm2022/ngwr-descriptions.mjs.map +1 -1
- package/fesm2022/ngwr-dialog-testing.mjs.map +1 -1
- package/fesm2022/ngwr-dialog.mjs +15 -15
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-directives.mjs +12 -12
- package/fesm2022/ngwr-directives.mjs.map +1 -1
- package/fesm2022/ngwr-divider.mjs +3 -3
- package/fesm2022/ngwr-divider.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart.mjs +48 -6
- package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +6 -6
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer-testing.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +18 -18
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +174 -25
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-editor-testing.mjs +568 -0
- package/fesm2022/ngwr-editor-testing.mjs.map +1 -0
- package/fesm2022/ngwr-editor.mjs +2601 -0
- package/fesm2022/ngwr-editor.mjs.map +1 -0
- package/fesm2022/ngwr-empty.mjs +3 -3
- package/fesm2022/ngwr-empty.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar.mjs +14 -7
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text.mjs +3 -3
- package/fesm2022/ngwr-falling-text.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload.mjs +3 -3
- package/fesm2022/ngwr-file-upload.mjs.map +1 -1
- package/fesm2022/ngwr-form-testing.mjs.map +1 -1
- package/fesm2022/ngwr-form.mjs +61 -10
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
- package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
- package/fesm2022/ngwr-gauge-testing.mjs.map +1 -1
- package/fesm2022/ngwr-gauge.mjs +35 -6
- package/fesm2022/ngwr-gauge.mjs.map +1 -1
- package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-glitch-text.mjs +3 -3
- package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text.mjs +3 -3
- package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
- package/fesm2022/ngwr-graph-testing.mjs.map +1 -1
- package/fesm2022/ngwr-graph.mjs +6 -6
- package/fesm2022/ngwr-graph.mjs.map +1 -1
- package/fesm2022/ngwr-hotkey.mjs +6 -6
- package/fesm2022/ngwr-hotkey.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ar.mjs +31 -0
- package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-cs.mjs +31 -0
- package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-de.mjs +31 -0
- package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +39 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-es.mjs +31 -0
- package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-fr.mjs +31 -0
- package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-he.mjs +31 -0
- package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-hi.mjs +31 -0
- package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-id.mjs +31 -0
- package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-it.mjs +31 -0
- package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ja.mjs +31 -0
- package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ko.mjs +31 -0
- package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-nl.mjs +31 -0
- package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pl.mjs +31 -0
- package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pt.mjs +31 -0
- package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +31 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-sv.mjs +31 -0
- package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-tr.mjs +31 -0
- package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-uk.mjs +31 -0
- package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-vi.mjs +31 -0
- package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh-TW.mjs +31 -0
- package/fesm2022/ngwr-i18n-zh-TW.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh.mjs +31 -0
- package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +9 -9
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
- package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
- package/fesm2022/ngwr-icon.mjs +6 -6
- package/fesm2022/ngwr-icon.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper.mjs +3 -3
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
- package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-number.mjs +15 -3
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp.mjs +3 -3
- package/fesm2022/ngwr-input-otp.mjs.map +1 -1
- package/fesm2022/ngwr-input-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input.mjs +15 -15
- package/fesm2022/ngwr-input.mjs.map +1 -1
- package/fesm2022/ngwr-keyboard.mjs +3 -3
- package/fesm2022/ngwr-keyboard.mjs.map +1 -1
- package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
- package/fesm2022/ngwr-knob.mjs +3 -3
- package/fesm2022/ngwr-knob.mjs.map +1 -1
- package/fesm2022/ngwr-layout.mjs +15 -15
- package/fesm2022/ngwr-layout.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox.mjs +3 -3
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart.mjs +18 -7
- package/fesm2022/ngwr-line-chart.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +6 -6
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar.mjs +6 -6
- package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
- package/fesm2022/ngwr-markdown-testing.mjs.map +1 -1
- package/fesm2022/ngwr-markdown.mjs +798 -17
- package/fesm2022/ngwr-markdown.mjs.map +1 -1
- package/fesm2022/ngwr-marquee-testing.mjs.map +1 -1
- package/fesm2022/ngwr-marquee.mjs +3 -3
- package/fesm2022/ngwr-marquee.mjs.map +1 -1
- package/fesm2022/ngwr-media.mjs +3 -3
- package/fesm2022/ngwr-media.mjs.map +1 -1
- package/fesm2022/ngwr-mention-testing.mjs.map +1 -1
- package/fesm2022/ngwr-mention.mjs +6 -6
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-meta.mjs +6 -6
- package/fesm2022/ngwr-meta.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group-testing.mjs +7 -3
- package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group.mjs +26 -6
- package/fesm2022/ngwr-meter-group.mjs.map +1 -1
- package/fesm2022/ngwr-overlay.mjs +6 -6
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-page-header.mjs +13 -5
- package/fesm2022/ngwr-page-header.mjs.map +1 -1
- package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +3 -3
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +21 -21
- package/fesm2022/ngwr-pipes.mjs.map +1 -1
- package/fesm2022/ngwr-platform.mjs +9 -9
- package/fesm2022/ngwr-platform.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm.mjs +6 -6
- package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
- package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +578 -16
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-progress.mjs +3 -3
- package/fesm2022/ngwr-progress.mjs.map +1 -1
- package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
- package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
- package/fesm2022/ngwr-qr.mjs +3 -3
- package/fesm2022/ngwr-qr.mjs.map +1 -1
- package/fesm2022/ngwr-radio-testing.mjs.map +1 -1
- package/fesm2022/ngwr-radio.mjs +6 -6
- package/fesm2022/ngwr-radio.mjs.map +1 -1
- package/fesm2022/ngwr-rating-testing.mjs.map +1 -1
- package/fesm2022/ngwr-rating.mjs +3 -3
- package/fesm2022/ngwr-rating.mjs.map +1 -1
- package/fesm2022/ngwr-result.mjs +12 -12
- package/fesm2022/ngwr-result.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text.mjs +3 -3
- package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
- package/fesm2022/ngwr-schema-form.mjs +3 -3
- package/fesm2022/ngwr-schema-form.mjs.map +1 -1
- package/fesm2022/ngwr-scroll.mjs +3 -3
- package/fesm2022/ngwr-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
- package/fesm2022/ngwr-segmented.mjs +3 -3
- package/fesm2022/ngwr-segmented.mjs.map +1 -1
- package/fesm2022/ngwr-select-testing.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +12 -12
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text.mjs +6 -6
- package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
- package/fesm2022/ngwr-sidebar.mjs +10 -3
- package/fesm2022/ngwr-sidebar.mjs.map +1 -1
- package/fesm2022/ngwr-skeleton.mjs +3 -3
- package/fesm2022/ngwr-skeleton.mjs.map +1 -1
- package/fesm2022/ngwr-slider-testing.mjs.map +1 -1
- package/fesm2022/ngwr-slider.mjs +3 -3
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline.mjs +84 -10
- package/fesm2022/ngwr-sparkline.mjs.map +1 -1
- package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -1
- package/fesm2022/ngwr-speed-dial.mjs +3 -3
- package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
- package/fesm2022/ngwr-spinner.mjs +3 -3
- package/fesm2022/ngwr-spinner.mjs.map +1 -1
- package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -1
- package/fesm2022/ngwr-splash-cursor.mjs +3 -3
- package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
- package/fesm2022/ngwr-split-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-split-text.mjs +3 -3
- package/fesm2022/ngwr-split-text.mjs.map +1 -1
- package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
- package/fesm2022/ngwr-splitter.mjs +3 -3
- package/fesm2022/ngwr-splitter.mjs.map +1 -1
- package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -1
- package/fesm2022/ngwr-spotlight-card.mjs +6 -6
- package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
- package/fesm2022/ngwr-squircle.mjs +6 -6
- package/fesm2022/ngwr-squircle.mjs.map +1 -1
- package/fesm2022/ngwr-star-border-testing.mjs.map +1 -1
- package/fesm2022/ngwr-star-border.mjs +3 -3
- package/fesm2022/ngwr-star-border.mjs.map +1 -1
- package/fesm2022/ngwr-statistic.mjs +9 -9
- package/fesm2022/ngwr-statistic.mjs.map +1 -1
- package/fesm2022/ngwr-stepper-testing.mjs.map +1 -1
- package/fesm2022/ngwr-stepper.mjs +6 -6
- package/fesm2022/ngwr-stepper.mjs.map +1 -1
- package/fesm2022/ngwr-storage.mjs +3 -3
- package/fesm2022/ngwr-storage.mjs.map +1 -1
- package/fesm2022/ngwr-switch-testing.mjs.map +1 -1
- package/fesm2022/ngwr-switch.mjs +3 -3
- package/fesm2022/ngwr-switch.mjs.map +1 -1
- package/fesm2022/ngwr-table-testing.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +18 -18
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-router.mjs +3 -3
- package/fesm2022/ngwr-tabs-router.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +6 -6
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-textarea-testing.mjs.map +1 -1
- package/fesm2022/ngwr-textarea.mjs +3 -3
- package/fesm2022/ngwr-textarea.mjs.map +1 -1
- package/fesm2022/ngwr-theme.mjs +3 -3
- package/fesm2022/ngwr-theme.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card.mjs +6 -6
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-timeline.mjs +6 -6
- package/fesm2022/ngwr-timeline.mjs.map +1 -1
- package/fesm2022/ngwr-toast-testing.mjs.map +1 -1
- package/fesm2022/ngwr-toast.mjs +9 -9
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-toolbar.mjs +10 -3
- package/fesm2022/ngwr-toolbar.mjs.map +1 -1
- package/fesm2022/ngwr-tour-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tour.mjs +6 -6
- package/fesm2022/ngwr-tour.mjs.map +1 -1
- package/fesm2022/ngwr-transfer-testing.mjs.map +1 -1
- package/fesm2022/ngwr-transfer.mjs +3 -3
- package/fesm2022/ngwr-transfer.mjs.map +1 -1
- package/fesm2022/ngwr-tree-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +3 -3
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter.mjs +3 -3
- package/fesm2022/ngwr-typewriter.mjs.map +1 -1
- package/fesm2022/ngwr-typography.mjs +7 -3
- package/fesm2022/ngwr-typography.mjs.map +1 -1
- package/fesm2022/ngwr-utils.mjs.map +1 -1
- package/fesm2022/ngwr-validators.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
- package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-waves-testing.mjs.map +1 -1
- package/fesm2022/ngwr-waves.mjs +3 -3
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/fesm2022/ngwr-window-testing.mjs.map +1 -1
- package/fesm2022/ngwr-window.mjs +12 -12
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/form/styles/_index.scss +16 -1
- package/gauge/styles/_index.scss +1 -0
- package/i18n/ar.json +32 -1
- package/i18n/cs.json +32 -1
- package/i18n/de.json +32 -1
- package/i18n/en.json +32 -1
- package/i18n/es.json +32 -1
- package/i18n/fr.json +32 -1
- package/i18n/he.json +32 -1
- package/i18n/hi.json +32 -1
- package/i18n/id.json +32 -1
- package/i18n/it.json +32 -1
- package/i18n/ja.json +32 -1
- package/i18n/ko.json +32 -1
- package/i18n/nl.json +32 -1
- package/i18n/pl.json +32 -1
- package/i18n/pt.json +32 -1
- package/i18n/ru.json +32 -1
- package/i18n/sv.json +32 -1
- package/i18n/tr.json +32 -1
- package/i18n/uk.json +32 -1
- package/i18n/vi.json +32 -1
- package/i18n/zh-TW.json +32 -1
- package/i18n/zh.json +32 -1
- package/input/styles/_index.scss +24 -1
- package/input-number/styles/_index.scss +69 -0
- package/input-otp/styles/_index.scss +5 -5
- package/line-chart/styles/_index.scss +30 -2
- package/llms-full.txt +14 -4
- package/llms.txt +188 -132
- package/mcp/providers.js +2 -2
- package/mcp/server.js +1 -1
- package/meter-group/styles/_index.scss +1 -0
- package/overlay/styles/_arrow.scss +30 -2
- package/package.json +43 -2
- package/page-header/styles/_index.scss +37 -1
- package/pagination/styles/_index.scss +9 -5
- package/popover/styles/_index.scss +73 -5
- package/progress/styles/_index.scss +4 -0
- package/radio/styles/_index.scss +4 -1
- package/schematics/use/symbol-map.json +1 -0
- package/select/styles/_index.scss +51 -12
- package/sidebar/styles/_index.scss +44 -2
- package/skeleton/styles/_index.scss +3 -0
- package/skills/ngwr/SKILL.md +5 -5
- package/skills/ngwr/references/catalog.md +2 -0
- package/skills/ngwr/references/setup.md +3 -3
- package/sparkline/styles/_index.scss +19 -0
- package/styles.scss +1 -0
- package/table/styles/_index.scss +7 -0
- package/textarea/styles/_index.scss +12 -3
- package/theme/styles/_colors.scss +39 -13
- package/theme/styles/_contrast.scss +9 -7
- package/toast/styles/_index.scss +4 -0
- package/toolbar/styles/_index.scss +35 -1
- package/tree/styles/_index.scss +46 -7
- package/types/ngwr-badge.d.ts +22 -7
- package/types/ngwr-bar-chart.d.ts +19 -1
- package/types/ngwr-calendar-heatmap.d.ts +29 -1
- package/types/ngwr-checkbox.d.ts +2 -0
- package/types/ngwr-config.d.ts +12 -0
- package/types/ngwr-date-picker.d.ts +308 -43
- package/types/ngwr-donut-chart.d.ts +24 -1
- package/types/ngwr-dropdown.d.ts +22 -2
- package/types/ngwr-editor-testing.d.ts +378 -0
- package/types/ngwr-editor.d.ts +449 -0
- package/types/ngwr-event-calendar.d.ts +8 -0
- package/types/ngwr-form.d.ts +35 -5
- package/types/ngwr-gauge.d.ts +20 -1
- package/types/ngwr-input-number.d.ts +7 -0
- package/types/ngwr-line-chart.d.ts +9 -1
- package/types/ngwr-markdown.d.ts +61 -2
- package/types/ngwr-meter-group-testing.d.ts +3 -2
- package/types/ngwr-meter-group.d.ts +14 -1
- package/types/ngwr-page-header.d.ts +10 -2
- package/types/ngwr-popover.d.ts +154 -3
- package/types/ngwr-sidebar.d.ts +7 -0
- package/types/ngwr-sparkline.d.ts +43 -1
- package/types/ngwr-toolbar.d.ts +7 -0
- package/types/ngwr-typography.d.ts +4 -0
- package/types/ngwr-version.d.ts +1 -1
- package/typography/styles/_index.scss +16 -0
|
@@ -495,6 +495,32 @@ const MAX_LABEL_LENGTH = 999;
|
|
|
495
495
|
const MAX_DESTINATION_NESTING = 32;
|
|
496
496
|
const BARE_URL_RE = /^https?:\/\/[^\s<>[\]()]*(?:\([^\s<>()]*\)[^\s<>[\]()]*)*/i;
|
|
497
497
|
const EMAIL_AUTOLINK_RE = /^<([^\s@<>]+@[^\s@<>.]+\.[^\s@<>]+)>/;
|
|
498
|
+
/**
|
|
499
|
+
* Whitespace and control characters only: a hyphen has no business being
|
|
500
|
+
* stripped out of a host name. Written as escapes rather than literal bytes — a
|
|
501
|
+
* raw NUL inside a regex literal makes the file binary to every text tool.
|
|
502
|
+
* Stripping control characters is the entire point: a browser ignores them before
|
|
503
|
+
* resolving the scheme, so a check that reads the raw string sees a relative path
|
|
504
|
+
* and waves `java\u0000script:` straight through.
|
|
505
|
+
*/
|
|
506
|
+
// eslint-disable-next-line no-control-regex
|
|
507
|
+
const URL_IGNORED_RE = /[\s\u0000-\u001f\u007f]/g;
|
|
508
|
+
/**
|
|
509
|
+
* The character references a CommonMark renderer decodes in a link destination
|
|
510
|
+
* that can spell a scheme: every numeric one, and the named ones for the colon
|
|
511
|
+
* and for the whitespace a browser strips (`:`, `	`, `
`).
|
|
512
|
+
* Matched without regard to case and with the semicolon optional, which is wider
|
|
513
|
+
* than any renderer decodes — the net only ever catches a URL nobody writes.
|
|
514
|
+
*/
|
|
515
|
+
const SCHEME_REFERENCE_RE = /&(?:#x([0-9a-f]+)|#(\d+)|(colon|tab|newline));?/gi;
|
|
516
|
+
function decodeSchemeReferences(url) {
|
|
517
|
+
return url.replace(SCHEME_REFERENCE_RE, (_reference, hex, decimal, name) => {
|
|
518
|
+
if (name)
|
|
519
|
+
return name.toLowerCase() === 'colon' ? ':' : '\n';
|
|
520
|
+
const code = hex ? Number.parseInt(hex, 16) : Number(decimal);
|
|
521
|
+
return code > 0 && code <= 0x10ffff ? String.fromCodePoint(code) : '\ufffd';
|
|
522
|
+
});
|
|
523
|
+
}
|
|
498
524
|
/**
|
|
499
525
|
* A URL safe to put in `href` / `src`, or `null` to render the link as plain text.
|
|
500
526
|
*
|
|
@@ -505,19 +531,16 @@ const EMAIL_AUTOLINK_RE = /^<([^\s@<>]+@[^\s@<>.]+\.[^\s@<>]+)>/;
|
|
|
505
531
|
*
|
|
506
532
|
* Whitespace and control characters are stripped before the scheme is read,
|
|
507
533
|
* because `java\nscript:` and `java script:` are the same URL to a browser and
|
|
508
|
-
* different strings to a naive check.
|
|
534
|
+
* different strings to a naive check. So are character references:
|
|
535
|
+
* `javascript:` has no scheme to this check, but a CommonMark renderer
|
|
536
|
+
* decodes it in a destination, so any markdown the URL is written into — an
|
|
537
|
+
* editor's output, say — hands a browser `javascript:`. The scheme is read from
|
|
538
|
+
* the decoded form; what comes back is still the URL as written.
|
|
509
539
|
*/
|
|
510
540
|
function safeMarkdownUrl(raw, kind) {
|
|
511
541
|
const url = raw.trim();
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
// a raw NUL inside a regex literal makes the file binary to every text tool.
|
|
515
|
-
// Stripping control characters is the entire point here: a browser ignores them
|
|
516
|
-
// before resolving the scheme, so a check that reads the raw string sees a
|
|
517
|
-
// relative path and waves `java\u0000script:` straight through.
|
|
518
|
-
// eslint-disable-next-line no-control-regex
|
|
519
|
-
const bare = url.replace(/[\s\u0000-\u001f\u007f]/g, '');
|
|
520
|
-
const scheme = SCHEME_RE.exec(bare);
|
|
542
|
+
const bare = url.replace(URL_IGNORED_RE, '');
|
|
543
|
+
const scheme = SCHEME_RE.exec(decodeSchemeReferences(bare).replace(URL_IGNORED_RE, ''));
|
|
521
544
|
// No scheme at all: relative path, `#anchor`, `//host` — nothing executable.
|
|
522
545
|
if (!scheme)
|
|
523
546
|
return url;
|
|
@@ -1295,10 +1318,10 @@ class WrMarkdownHighlight {
|
|
|
1295
1318
|
this.cache.delete(oldest.value);
|
|
1296
1319
|
}
|
|
1297
1320
|
}
|
|
1298
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1299
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
1321
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMarkdownHighlight, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1322
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMarkdownHighlight, providedIn: 'root' });
|
|
1300
1323
|
}
|
|
1301
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1324
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMarkdownHighlight, decorators: [{
|
|
1302
1325
|
type: Injectable,
|
|
1303
1326
|
args: [{ providedIn: 'root' }]
|
|
1304
1327
|
}] });
|
|
@@ -1539,10 +1562,10 @@ class WrMarkdown {
|
|
|
1539
1562
|
asCells(value) {
|
|
1540
1563
|
return value;
|
|
1541
1564
|
}
|
|
1542
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1543
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: WrMarkdown, isStandalone: true, selector: "wr-markdown", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, copyable: { classPropertyName: "copyable", publicName: "copyable", isSignal: true, isRequired: false, transformFunction: null }, linkTarget: { classPropertyName: "linkTarget", publicName: "linkTarget", isSignal: true, isRequired: false, transformFunction: null }, headingIds: { classPropertyName: "headingIds", publicName: "headingIds", isSignal: true, isRequired: false, transformFunction: null }, headingIdPrefix: { classPropertyName: "headingIdPrefix", publicName: "headingIdPrefix", isSignal: true, isRequired: false, transformFunction: null }, copyLabel: { classPropertyName: "copyLabel", publicName: "copyLabel", isSignal: true, isRequired: false, transformFunction: null }, copiedLabel: { classPropertyName: "copiedLabel", publicName: "copiedLabel", isSignal: true, isRequired: false, transformFunction: null }, taskDoneLabel: { classPropertyName: "taskDoneLabel", publicName: "taskDoneLabel", isSignal: true, isRequired: false, transformFunction: null }, taskTodoLabel: { classPropertyName: "taskTodoLabel", publicName: "taskTodoLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.wr-markdown--streaming": "streaming()" }, classAttribute: "wr-markdown" }, ngImport: i0, template: "<!--\n Recursion runs through `ngTemplateOutlet` into these templates rather than\n through a child component, so the rendered DOM is exactly the document \u2014 no\n wrapper element between a `<ul>` and its `<li>`. See `asBlocks` for the whole\n reasoning.\n-->\n<ng-template #inlineList let-raw>\n @let nodes = asInlines(raw);\n\n @for (node of nodes; track $index) {\n @switch (node.kind) {\n @case ('text') {\n <!-- Wrapped, and not for tidiness: a bare interpolation on its own line is\n ONE text node that includes the indentation around it, so `a **b** c`\n rendered with a space inside every `<strong>`. Inside an element the\n whitespace becomes separate whitespace-only nodes, which Angular\n removes, and `<ng-container>` itself renders nothing. Prettier\n reformats the bare form back, so this also has to survive `--fix`. -->\n <ng-container>{{ node.value }}</ng-container>\n }\n @case ('code') {\n <!-- `dir=\"ltr\"` because code is not prose: under RTL the browser\n reorders `foo();` to `;foo()`. -->\n <code class=\"wr-markdown__code-inline\" dir=\"ltr\">{{ node.value }}</code>\n }\n @case ('strong') {\n <strong class=\"wr-markdown__strong\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></strong>\n }\n @case ('em') {\n <em class=\"wr-markdown__em\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></em>\n }\n @case ('del') {\n <del class=\"wr-markdown__del\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></del>\n }\n @case ('link') {\n <a\n class=\"wr-markdown__link\"\n [href]=\"hrefOf(node.href)\"\n [attr.title]=\"node.title\"\n [attr.target]=\"resolvedLinkTarget()\"\n [attr.rel]=\"linkRel()\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></a>\n }\n @case ('image') {\n <img\n class=\"wr-markdown__image\"\n [src]=\"node.src\"\n [alt]=\"node.alt\"\n [attr.title]=\"node.title\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n }\n @case ('break') {\n <br />\n }\n }\n }\n</ng-template>\n\n<ng-template #itemList let-raw let-tight=\"tight\">\n @let items = asItems(raw);\n\n @for (item of items; track $index) {\n <li class=\"wr-markdown__item\" [class.wr-markdown__item--task]=\"item.checked !== null\">\n @if (item.checked !== null) {\n <!-- Presentational, with the state as text beside it: a real\n `<input type=\"checkbox\">` here would be an unlabelled form control,\n which is a serious axe violation, and making it operable would\n promise an interaction a renderer cannot honour. -->\n <span class=\"wr-markdown__task\" [class.wr-markdown__task--checked]=\"item.checked\" aria-hidden=\"true\"></span>\n <span class=\"wr-markdown__sr-only\">{{ item.checked ? resolvedTaskDoneLabel() : resolvedTaskTodoLabel() }}</span>\n }\n <ng-container\n [ngTemplateOutlet]=\"blockList\"\n [ngTemplateOutletContext]=\"{ $implicit: item.children, tight: tight }\"\n />\n </li>\n }\n</ng-template>\n\n<ng-template #blockList let-raw let-tight=\"tight\">\n @let list = asBlocks(raw);\n\n @for (block of list; track $index) {\n @switch (block.kind) {\n @case ('paragraph') {\n <!-- A tight list drops the `<p>`, which is what CommonMark specifies and\n what keeps a one-line list from being double-spaced. -->\n @if (tight) {\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n } @else {\n <p class=\"wr-markdown__paragraph\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </p>\n }\n }\n @case ('heading') {\n @switch (block.level) {\n @case (1) {\n <h1 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h1>\n }\n @case (2) {\n <h2 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h2>\n }\n @case (3) {\n <h3 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h3>\n }\n @case (4) {\n <h4 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h4>\n }\n @case (5) {\n <h5 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h5>\n }\n @default {\n <h6 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h6>\n }\n }\n }\n @case ('code') {\n @let highlighted = linesOf(block.code, block.language);\n\n <div class=\"wr-markdown__code-block\">\n <pre\n class=\"wr-markdown__pre\"\n dir=\"ltr\"\n tabindex=\"0\"\n ><code class=\"wr-markdown__code\" [attr.data-language]=\"block.language\">@if (highlighted) {@for (row of highlighted; track $index) {<span class=\"wr-markdown__line\">@for (span of row; track $index) {<span\n [style.color]=\"span.color\"\n [style.font-style]=\"span.fontStyle === 'italic' ? 'italic' : null\"\n [style.font-weight]=\"span.fontStyle === 'bold' ? 'bold' : null\"\n [style.text-decoration]=\"span.fontStyle === 'underline' ? 'underline' : null\"\n >{{ span.text }}</span>}</span>@if (!$last) {{{ newline }}}}} @else {{{ block.code }}}</code></pre>\n\n @if (resolvedCopyable() && block.closed) {\n <button\n type=\"button\"\n class=\"wr-markdown__copy\"\n [wrCopyToClipboard]=\"block.code\"\n [attr.aria-label]=\"isCopied(block) ? resolvedCopiedLabel() : resolvedCopyLabel()\"\n (copied)=\"onCopied(block)\"\n >\n <svg\n class=\"wr-markdown__copy-icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @if (isCopied(block)) {\n <path d=\"M20 6 9 17l-5-5\" />\n } @else {\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n }\n </svg>\n </button>\n }\n </div>\n }\n @case ('quote') {\n <blockquote class=\"wr-markdown__quote\">\n <ng-container\n [ngTemplateOutlet]=\"blockList\"\n [ngTemplateOutletContext]=\"{ $implicit: block.children, tight: false }\"\n />\n </blockquote>\n }\n @case ('list') {\n @if (block.ordered) {\n <ol\n class=\"wr-markdown__list wr-markdown__list--ordered\"\n [attr.start]=\"block.start === 1 ? null : block.start\"\n >\n <ng-container\n [ngTemplateOutlet]=\"itemList\"\n [ngTemplateOutletContext]=\"{ $implicit: block.items, tight: block.tight }\"\n />\n </ol>\n } @else {\n <ul class=\"wr-markdown__list\">\n <ng-container\n [ngTemplateOutlet]=\"itemList\"\n [ngTemplateOutletContext]=\"{ $implicit: block.items, tight: block.tight }\"\n />\n </ul>\n }\n }\n @case ('table') {\n <!-- Its own scroller: a wide table must not push the page sideways, which\n is also what keeps the RTL layout gate quiet about it. -->\n <div class=\"wr-markdown__table-scroll\" tabindex=\"0\">\n <table class=\"wr-markdown__table\">\n <thead>\n <tr>\n @for (cell of block.head; track column; let column = $index) {\n <th class=\"wr-markdown__th\" scope=\"col\" [style.text-align]=\"block.align[column]\">\n <ng-container\n [ngTemplateOutlet]=\"inlineList\"\n [ngTemplateOutletContext]=\"{ $implicit: cell.inlines }\"\n />\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of block.rows; track $index) {\n <tr>\n @for (cell of asCells(row); track column; let column = $index) {\n <td class=\"wr-markdown__td\" [style.text-align]=\"block.align[column]\">\n <ng-container\n [ngTemplateOutlet]=\"inlineList\"\n [ngTemplateOutletContext]=\"{ $implicit: cell.inlines }\"\n />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n @case ('rule') {\n <hr class=\"wr-markdown__rule\" />\n }\n }\n }\n</ng-template>\n\n<ng-container [ngTemplateOutlet]=\"blockList\" [ngTemplateOutletContext]=\"{ $implicit: blocks(), tight: false }\" />\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: WrCopyToClipboard, selector: "[wrCopyToClipboard]", inputs: ["wrCopyToClipboard"], outputs: ["copied", "copyFailed"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1565
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMarkdown, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1566
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrMarkdown, isStandalone: true, selector: "wr-markdown", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, copyable: { classPropertyName: "copyable", publicName: "copyable", isSignal: true, isRequired: false, transformFunction: null }, linkTarget: { classPropertyName: "linkTarget", publicName: "linkTarget", isSignal: true, isRequired: false, transformFunction: null }, headingIds: { classPropertyName: "headingIds", publicName: "headingIds", isSignal: true, isRequired: false, transformFunction: null }, headingIdPrefix: { classPropertyName: "headingIdPrefix", publicName: "headingIdPrefix", isSignal: true, isRequired: false, transformFunction: null }, copyLabel: { classPropertyName: "copyLabel", publicName: "copyLabel", isSignal: true, isRequired: false, transformFunction: null }, copiedLabel: { classPropertyName: "copiedLabel", publicName: "copiedLabel", isSignal: true, isRequired: false, transformFunction: null }, taskDoneLabel: { classPropertyName: "taskDoneLabel", publicName: "taskDoneLabel", isSignal: true, isRequired: false, transformFunction: null }, taskTodoLabel: { classPropertyName: "taskTodoLabel", publicName: "taskTodoLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.wr-markdown--streaming": "streaming()" }, classAttribute: "wr-markdown" }, ngImport: i0, template: "<!--\n Recursion runs through `ngTemplateOutlet` into these templates rather than\n through a child component, so the rendered DOM is exactly the document \u2014 no\n wrapper element between a `<ul>` and its `<li>`. See `asBlocks` for the whole\n reasoning.\n-->\n<ng-template #inlineList let-raw>\n @let nodes = asInlines(raw);\n\n @for (node of nodes; track $index) {\n @switch (node.kind) {\n @case ('text') {\n <!-- Wrapped, and not for tidiness: a bare interpolation on its own line is\n ONE text node that includes the indentation around it, so `a **b** c`\n rendered with a space inside every `<strong>`. Inside an element the\n whitespace becomes separate whitespace-only nodes, which Angular\n removes, and `<ng-container>` itself renders nothing. Prettier\n reformats the bare form back, so this also has to survive `--fix`. -->\n <ng-container>{{ node.value }}</ng-container>\n }\n @case ('code') {\n <!-- `dir=\"ltr\"` because code is not prose: under RTL the browser\n reorders `foo();` to `;foo()`. -->\n <code class=\"wr-markdown__code-inline\" dir=\"ltr\">{{ node.value }}</code>\n }\n @case ('strong') {\n <strong class=\"wr-markdown__strong\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></strong>\n }\n @case ('em') {\n <em class=\"wr-markdown__em\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></em>\n }\n @case ('del') {\n <del class=\"wr-markdown__del\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></del>\n }\n @case ('link') {\n <a\n class=\"wr-markdown__link\"\n [href]=\"hrefOf(node.href)\"\n [attr.title]=\"node.title\"\n [attr.target]=\"resolvedLinkTarget()\"\n [attr.rel]=\"linkRel()\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></a>\n }\n @case ('image') {\n <img\n class=\"wr-markdown__image\"\n [src]=\"node.src\"\n [alt]=\"node.alt\"\n [attr.title]=\"node.title\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n }\n @case ('break') {\n <br />\n }\n }\n }\n</ng-template>\n\n<ng-template #itemList let-raw let-tight=\"tight\">\n @let items = asItems(raw);\n\n @for (item of items; track $index) {\n <li class=\"wr-markdown__item\" [class.wr-markdown__item--task]=\"item.checked !== null\">\n @if (item.checked !== null) {\n <!-- Presentational, with the state as text beside it: a real\n `<input type=\"checkbox\">` here would be an unlabelled form control,\n which is a serious axe violation, and making it operable would\n promise an interaction a renderer cannot honour. -->\n <span class=\"wr-markdown__task\" [class.wr-markdown__task--checked]=\"item.checked\" aria-hidden=\"true\"></span>\n <span class=\"wr-markdown__sr-only\">{{ item.checked ? resolvedTaskDoneLabel() : resolvedTaskTodoLabel() }}</span>\n }\n <ng-container\n [ngTemplateOutlet]=\"blockList\"\n [ngTemplateOutletContext]=\"{ $implicit: item.children, tight: tight }\"\n />\n </li>\n }\n</ng-template>\n\n<ng-template #blockList let-raw let-tight=\"tight\">\n @let list = asBlocks(raw);\n\n @for (block of list; track $index) {\n @switch (block.kind) {\n @case ('paragraph') {\n <!-- A tight list drops the `<p>`, which is what CommonMark specifies and\n what keeps a one-line list from being double-spaced. -->\n @if (tight) {\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n } @else {\n <p class=\"wr-markdown__paragraph\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </p>\n }\n }\n @case ('heading') {\n @switch (block.level) {\n @case (1) {\n <h1 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h1>\n }\n @case (2) {\n <h2 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h2>\n }\n @case (3) {\n <h3 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h3>\n }\n @case (4) {\n <h4 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h4>\n }\n @case (5) {\n <h5 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h5>\n }\n @default {\n <h6 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h6>\n }\n }\n }\n @case ('code') {\n @let highlighted = linesOf(block.code, block.language);\n\n <div class=\"wr-markdown__code-block\">\n <pre\n class=\"wr-markdown__pre\"\n dir=\"ltr\"\n tabindex=\"0\"\n ><code class=\"wr-markdown__code\" [attr.data-language]=\"block.language\">@if (highlighted) {@for (row of highlighted; track $index) {<span class=\"wr-markdown__line\">@for (span of row; track $index) {<span\n [style.color]=\"span.color\"\n [style.font-style]=\"span.fontStyle === 'italic' ? 'italic' : null\"\n [style.font-weight]=\"span.fontStyle === 'bold' ? 'bold' : null\"\n [style.text-decoration]=\"span.fontStyle === 'underline' ? 'underline' : null\"\n >{{ span.text }}</span>}</span>@if (!$last) {{{ newline }}}}} @else {{{ block.code }}}</code></pre>\n\n @if (resolvedCopyable() && block.closed) {\n <button\n type=\"button\"\n class=\"wr-markdown__copy\"\n [wrCopyToClipboard]=\"block.code\"\n [attr.aria-label]=\"isCopied(block) ? resolvedCopiedLabel() : resolvedCopyLabel()\"\n (copied)=\"onCopied(block)\"\n >\n <svg\n class=\"wr-markdown__copy-icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @if (isCopied(block)) {\n <path d=\"M20 6 9 17l-5-5\" />\n } @else {\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n }\n </svg>\n </button>\n }\n </div>\n }\n @case ('quote') {\n <blockquote class=\"wr-markdown__quote\">\n <ng-container\n [ngTemplateOutlet]=\"blockList\"\n [ngTemplateOutletContext]=\"{ $implicit: block.children, tight: false }\"\n />\n </blockquote>\n }\n @case ('list') {\n @if (block.ordered) {\n <ol\n class=\"wr-markdown__list wr-markdown__list--ordered\"\n [attr.start]=\"block.start === 1 ? null : block.start\"\n >\n <ng-container\n [ngTemplateOutlet]=\"itemList\"\n [ngTemplateOutletContext]=\"{ $implicit: block.items, tight: block.tight }\"\n />\n </ol>\n } @else {\n <ul class=\"wr-markdown__list\">\n <ng-container\n [ngTemplateOutlet]=\"itemList\"\n [ngTemplateOutletContext]=\"{ $implicit: block.items, tight: block.tight }\"\n />\n </ul>\n }\n }\n @case ('table') {\n <!-- Its own scroller: a wide table must not push the page sideways, which\n is also what keeps the RTL layout gate quiet about it. -->\n <div class=\"wr-markdown__table-scroll\" tabindex=\"0\">\n <table class=\"wr-markdown__table\">\n <thead>\n <tr>\n @for (cell of block.head; track column; let column = $index) {\n <th class=\"wr-markdown__th\" scope=\"col\" [style.text-align]=\"block.align[column]\">\n <ng-container\n [ngTemplateOutlet]=\"inlineList\"\n [ngTemplateOutletContext]=\"{ $implicit: cell.inlines }\"\n />\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of block.rows; track $index) {\n <tr>\n @for (cell of asCells(row); track column; let column = $index) {\n <td class=\"wr-markdown__td\" [style.text-align]=\"block.align[column]\">\n <ng-container\n [ngTemplateOutlet]=\"inlineList\"\n [ngTemplateOutletContext]=\"{ $implicit: cell.inlines }\"\n />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n @case ('rule') {\n <hr class=\"wr-markdown__rule\" />\n }\n }\n }\n</ng-template>\n\n<ng-container [ngTemplateOutlet]=\"blockList\" [ngTemplateOutletContext]=\"{ $implicit: blocks(), tight: false }\" />\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: WrCopyToClipboard, selector: "[wrCopyToClipboard]", inputs: ["wrCopyToClipboard"], outputs: ["copied", "copyFailed"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1544
1567
|
}
|
|
1545
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1568
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMarkdown, decorators: [{
|
|
1546
1569
|
type: Component,
|
|
1547
1570
|
args: [{ selector: 'wr-markdown', imports: [NgTemplateOutlet, WrCopyToClipboard], encapsulation: ViewEncapsulation.None, host: {
|
|
1548
1571
|
class: 'wr-markdown',
|
|
@@ -1550,6 +1573,764 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
1550
1573
|
}, template: "<!--\n Recursion runs through `ngTemplateOutlet` into these templates rather than\n through a child component, so the rendered DOM is exactly the document \u2014 no\n wrapper element between a `<ul>` and its `<li>`. See `asBlocks` for the whole\n reasoning.\n-->\n<ng-template #inlineList let-raw>\n @let nodes = asInlines(raw);\n\n @for (node of nodes; track $index) {\n @switch (node.kind) {\n @case ('text') {\n <!-- Wrapped, and not for tidiness: a bare interpolation on its own line is\n ONE text node that includes the indentation around it, so `a **b** c`\n rendered with a space inside every `<strong>`. Inside an element the\n whitespace becomes separate whitespace-only nodes, which Angular\n removes, and `<ng-container>` itself renders nothing. Prettier\n reformats the bare form back, so this also has to survive `--fix`. -->\n <ng-container>{{ node.value }}</ng-container>\n }\n @case ('code') {\n <!-- `dir=\"ltr\"` because code is not prose: under RTL the browser\n reorders `foo();` to `;foo()`. -->\n <code class=\"wr-markdown__code-inline\" dir=\"ltr\">{{ node.value }}</code>\n }\n @case ('strong') {\n <strong class=\"wr-markdown__strong\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></strong>\n }\n @case ('em') {\n <em class=\"wr-markdown__em\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></em>\n }\n @case ('del') {\n <del class=\"wr-markdown__del\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></del>\n }\n @case ('link') {\n <a\n class=\"wr-markdown__link\"\n [href]=\"hrefOf(node.href)\"\n [attr.title]=\"node.title\"\n [attr.target]=\"resolvedLinkTarget()\"\n [attr.rel]=\"linkRel()\"\n ><ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: node.children }\"\n /></a>\n }\n @case ('image') {\n <img\n class=\"wr-markdown__image\"\n [src]=\"node.src\"\n [alt]=\"node.alt\"\n [attr.title]=\"node.title\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n }\n @case ('break') {\n <br />\n }\n }\n }\n</ng-template>\n\n<ng-template #itemList let-raw let-tight=\"tight\">\n @let items = asItems(raw);\n\n @for (item of items; track $index) {\n <li class=\"wr-markdown__item\" [class.wr-markdown__item--task]=\"item.checked !== null\">\n @if (item.checked !== null) {\n <!-- Presentational, with the state as text beside it: a real\n `<input type=\"checkbox\">` here would be an unlabelled form control,\n which is a serious axe violation, and making it operable would\n promise an interaction a renderer cannot honour. -->\n <span class=\"wr-markdown__task\" [class.wr-markdown__task--checked]=\"item.checked\" aria-hidden=\"true\"></span>\n <span class=\"wr-markdown__sr-only\">{{ item.checked ? resolvedTaskDoneLabel() : resolvedTaskTodoLabel() }}</span>\n }\n <ng-container\n [ngTemplateOutlet]=\"blockList\"\n [ngTemplateOutletContext]=\"{ $implicit: item.children, tight: tight }\"\n />\n </li>\n }\n</ng-template>\n\n<ng-template #blockList let-raw let-tight=\"tight\">\n @let list = asBlocks(raw);\n\n @for (block of list; track $index) {\n @switch (block.kind) {\n @case ('paragraph') {\n <!-- A tight list drops the `<p>`, which is what CommonMark specifies and\n what keeps a one-line list from being double-spaced. -->\n @if (tight) {\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n } @else {\n <p class=\"wr-markdown__paragraph\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </p>\n }\n }\n @case ('heading') {\n @switch (block.level) {\n @case (1) {\n <h1 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h1>\n }\n @case (2) {\n <h2 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h2>\n }\n @case (3) {\n <h3 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h3>\n }\n @case (4) {\n <h4 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h4>\n }\n @case (5) {\n <h5 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h5>\n }\n @default {\n <h6 class=\"wr-markdown__heading\" [attr.id]=\"idOf(block.id)\">\n <ng-container [ngTemplateOutlet]=\"inlineList\" [ngTemplateOutletContext]=\"{ $implicit: block.inlines }\" />\n </h6>\n }\n }\n }\n @case ('code') {\n @let highlighted = linesOf(block.code, block.language);\n\n <div class=\"wr-markdown__code-block\">\n <pre\n class=\"wr-markdown__pre\"\n dir=\"ltr\"\n tabindex=\"0\"\n ><code class=\"wr-markdown__code\" [attr.data-language]=\"block.language\">@if (highlighted) {@for (row of highlighted; track $index) {<span class=\"wr-markdown__line\">@for (span of row; track $index) {<span\n [style.color]=\"span.color\"\n [style.font-style]=\"span.fontStyle === 'italic' ? 'italic' : null\"\n [style.font-weight]=\"span.fontStyle === 'bold' ? 'bold' : null\"\n [style.text-decoration]=\"span.fontStyle === 'underline' ? 'underline' : null\"\n >{{ span.text }}</span>}</span>@if (!$last) {{{ newline }}}}} @else {{{ block.code }}}</code></pre>\n\n @if (resolvedCopyable() && block.closed) {\n <button\n type=\"button\"\n class=\"wr-markdown__copy\"\n [wrCopyToClipboard]=\"block.code\"\n [attr.aria-label]=\"isCopied(block) ? resolvedCopiedLabel() : resolvedCopyLabel()\"\n (copied)=\"onCopied(block)\"\n >\n <svg\n class=\"wr-markdown__copy-icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n @if (isCopied(block)) {\n <path d=\"M20 6 9 17l-5-5\" />\n } @else {\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n }\n </svg>\n </button>\n }\n </div>\n }\n @case ('quote') {\n <blockquote class=\"wr-markdown__quote\">\n <ng-container\n [ngTemplateOutlet]=\"blockList\"\n [ngTemplateOutletContext]=\"{ $implicit: block.children, tight: false }\"\n />\n </blockquote>\n }\n @case ('list') {\n @if (block.ordered) {\n <ol\n class=\"wr-markdown__list wr-markdown__list--ordered\"\n [attr.start]=\"block.start === 1 ? null : block.start\"\n >\n <ng-container\n [ngTemplateOutlet]=\"itemList\"\n [ngTemplateOutletContext]=\"{ $implicit: block.items, tight: block.tight }\"\n />\n </ol>\n } @else {\n <ul class=\"wr-markdown__list\">\n <ng-container\n [ngTemplateOutlet]=\"itemList\"\n [ngTemplateOutletContext]=\"{ $implicit: block.items, tight: block.tight }\"\n />\n </ul>\n }\n }\n @case ('table') {\n <!-- Its own scroller: a wide table must not push the page sideways, which\n is also what keeps the RTL layout gate quiet about it. -->\n <div class=\"wr-markdown__table-scroll\" tabindex=\"0\">\n <table class=\"wr-markdown__table\">\n <thead>\n <tr>\n @for (cell of block.head; track column; let column = $index) {\n <th class=\"wr-markdown__th\" scope=\"col\" [style.text-align]=\"block.align[column]\">\n <ng-container\n [ngTemplateOutlet]=\"inlineList\"\n [ngTemplateOutletContext]=\"{ $implicit: cell.inlines }\"\n />\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of block.rows; track $index) {\n <tr>\n @for (cell of asCells(row); track column; let column = $index) {\n <td class=\"wr-markdown__td\" [style.text-align]=\"block.align[column]\">\n <ng-container\n [ngTemplateOutlet]=\"inlineList\"\n [ngTemplateOutletContext]=\"{ $implicit: cell.inlines }\"\n />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n @case ('rule') {\n <hr class=\"wr-markdown__rule\" />\n }\n }\n }\n</ng-template>\n\n<ng-container [ngTemplateOutlet]=\"blockList\" [ngTemplateOutletContext]=\"{ $implicit: blocks(), tight: false }\" />\n" }]
|
|
1551
1574
|
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], copyable: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyable", required: false }] }], linkTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkTarget", required: false }] }], headingIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingIds", required: false }] }], headingIdPrefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingIdPrefix", required: false }] }], copyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyLabel", required: false }] }], copiedLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "copiedLabel", required: false }] }], taskDoneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "taskDoneLabel", required: false }] }], taskTodoLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "taskTodoLabel", required: false }] }] } });
|
|
1552
1575
|
|
|
1576
|
+
/**
|
|
1577
|
+
* @license
|
|
1578
|
+
*
|
|
1579
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
1580
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
1581
|
+
*/
|
|
1582
|
+
/**
|
|
1583
|
+
* A block tree back to markdown — the inverse of {@link parseMarkdown}.
|
|
1584
|
+
*
|
|
1585
|
+
* **Written against this library's parser, not against CommonMark.** The two
|
|
1586
|
+
* disagree in places (no setext headings, no indented code, no entities, a
|
|
1587
|
+
* simplified reading of emphasis), and a serializer that escapes for the wrong
|
|
1588
|
+
* dialect writes text that comes back as something else. So every escape here is
|
|
1589
|
+
* derived from a question `parseMarkdown` actually asks — several of them by
|
|
1590
|
+
* calling the parser's own predicates — and the spec holds the pair to one
|
|
1591
|
+
* property: `parseMarkdown(serializeMarkdown(tree))` returns `tree`.
|
|
1592
|
+
*
|
|
1593
|
+
* **What markdown cannot say is normalised, never guessed at.** The output is the
|
|
1594
|
+
* nearest document this dialect can express, and reading it back gives a tree
|
|
1595
|
+
* that serializes to the same string. The losses, all of them structural:
|
|
1596
|
+
*
|
|
1597
|
+
* - Whitespace at the edges of a paragraph, heading or cell is dropped, and so is
|
|
1598
|
+
* whitespace just inside `**` / `*` / `~~` — a delimiter next to a space is not
|
|
1599
|
+
* a delimiter. A soft line break inside a text node becomes a space, and a hard
|
|
1600
|
+
* break at the very end of a block (a trailing `\` stays literal) is dropped;
|
|
1601
|
+
* inside a heading or a table cell every hard break becomes a space.
|
|
1602
|
+
* - An empty paragraph, an empty list and a table with no header cell vanish.
|
|
1603
|
+
* - Two adjacent lists of the same kind merge on the way back — there is no
|
|
1604
|
+
* raw-HTML comment to put between them. A single-item list with one block
|
|
1605
|
+
* cannot be loose, and a loose list nested in a tight one loosens it. Nesting
|
|
1606
|
+
* deeper than the parser's twelve levels reads back as text.
|
|
1607
|
+
* - Inline marks are rewritten in one canonical nesting — link outside strong
|
|
1608
|
+
* outside strikethrough outside emphasis — which is also how a mark SET, the
|
|
1609
|
+
* shape an editor holds, becomes a tree. An image carries no emphasis (none of
|
|
1610
|
+
* it paints on an image); where an emphasis run touches a strong one and
|
|
1611
|
+
* neither can switch to `_`, the emphasis is dropped rather than written as
|
|
1612
|
+
* markup that reads back as literal asterisks.
|
|
1613
|
+
* - A link or image whose URL `safeMarkdownUrl` refuses is written as its text,
|
|
1614
|
+
* exactly as the renderer would show it. A destination is percent-encoded
|
|
1615
|
+
* where this parser cannot carry a character (`<`, `>`, an unbalanced
|
|
1616
|
+
* parenthesis, a `\` it would read as an escape — and `[`, `]`, a backtick for
|
|
1617
|
+
* an image inside a link label), loses a tab or line break the way a browser's
|
|
1618
|
+
* URL parser does, a title that cannot be delimited is dropped, and a link
|
|
1619
|
+
* whose label is too long for the parser's label cap is written as plain text.
|
|
1620
|
+
* - A fence's info string keeps its first word, lowercased, and `closed` is not
|
|
1621
|
+
* written: a serialized fence is always closed. A tab that indents a line reads
|
|
1622
|
+
* back as four spaces, as the parser expands it. Heading ids are the parser's,
|
|
1623
|
+
* recomputed on the way back.
|
|
1624
|
+
*
|
|
1625
|
+
* @param blocks a block tree, typically from {@link parseMarkdown} or an editor
|
|
1626
|
+
* @returns markdown, with no trailing newline; `''` for an empty tree
|
|
1627
|
+
*/
|
|
1628
|
+
function serializeMarkdown(blocks) {
|
|
1629
|
+
return joinBlocks(blocks.map(renderBlock), false);
|
|
1630
|
+
}
|
|
1631
|
+
/** The largest number `LIST_RE` reads as an ordinal: `\d{1,9}`. */
|
|
1632
|
+
const MAX_ORDINAL = 999999999;
|
|
1633
|
+
const DELIMITER_ROW = {
|
|
1634
|
+
start: ':--',
|
|
1635
|
+
center: ':-:',
|
|
1636
|
+
end: '--:',
|
|
1637
|
+
none: '---',
|
|
1638
|
+
};
|
|
1639
|
+
function renderBlock(block) {
|
|
1640
|
+
const text = blockText(block);
|
|
1641
|
+
return text ? { block, text } : null;
|
|
1642
|
+
}
|
|
1643
|
+
function blockText(block) {
|
|
1644
|
+
switch (block.kind) {
|
|
1645
|
+
case 'paragraph':
|
|
1646
|
+
return inlineMarkdown(block.inlines, 'paragraph').split('\n').map(escapeLineStart).join('\n');
|
|
1647
|
+
case 'heading':
|
|
1648
|
+
return headingText(block.level, block.inlines);
|
|
1649
|
+
case 'code':
|
|
1650
|
+
return codeBlock(block.language, block.code);
|
|
1651
|
+
case 'quote': {
|
|
1652
|
+
const body = joinBlocks(block.children.map(renderBlock), false);
|
|
1653
|
+
// A lone `>` is how an empty quote is written, and it reads back as one.
|
|
1654
|
+
return body
|
|
1655
|
+
.split('\n')
|
|
1656
|
+
.map(line => (line ? `> ${line}` : '>'))
|
|
1657
|
+
.join('\n');
|
|
1658
|
+
}
|
|
1659
|
+
case 'list':
|
|
1660
|
+
return listText(block.ordered, block.start, block.tight, block.items);
|
|
1661
|
+
case 'table':
|
|
1662
|
+
return tableText(block.head, block.rows, block.align);
|
|
1663
|
+
case 'rule':
|
|
1664
|
+
return '---';
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Blocks joined the way the parser will split them again.
|
|
1669
|
+
*
|
|
1670
|
+
* A blank line is the safe separator, and between two siblings at one level it is
|
|
1671
|
+
* always correct but for one case: a blank line followed by ANY list marker marks
|
|
1672
|
+
* the list before it loose, even when the marker starts a list of the other kind.
|
|
1673
|
+
* So a tight list that is followed by a list is joined by a single newline. Inside
|
|
1674
|
+
* a tight list item a single newline is wanted everywhere, and is used wherever
|
|
1675
|
+
* the parser would still end the first block there.
|
|
1676
|
+
*/
|
|
1677
|
+
function joinBlocks(parts, tight) {
|
|
1678
|
+
let out = '';
|
|
1679
|
+
let previous = null;
|
|
1680
|
+
for (const part of parts) {
|
|
1681
|
+
if (!part)
|
|
1682
|
+
continue;
|
|
1683
|
+
if (previous)
|
|
1684
|
+
out += separator(previous, part, tight);
|
|
1685
|
+
out += part.text;
|
|
1686
|
+
previous = part;
|
|
1687
|
+
}
|
|
1688
|
+
return out;
|
|
1689
|
+
}
|
|
1690
|
+
function separator(previous, next, tight) {
|
|
1691
|
+
const tightList = previous.block.kind === 'list' && !previous.text.includes('\n\n');
|
|
1692
|
+
if (tightList && next.block.kind === 'list')
|
|
1693
|
+
return '\n';
|
|
1694
|
+
return tight && endsBefore(previous, next) ? '\n' : '\n\n';
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Does `previous` end where `next` begins, with no blank line between them?
|
|
1698
|
+
*
|
|
1699
|
+
* Each case is the parser's own stopping rule, asked through its own predicates: a
|
|
1700
|
+
* paragraph runs until a line interrupts it, a quote swallows any line that does
|
|
1701
|
+
* not start a block (lazy continuation), a list swallows the same and continues on
|
|
1702
|
+
* a marker of its own kind, and a table runs while a line has a pipe. A heading, a
|
|
1703
|
+
* rule and a closed fence end themselves.
|
|
1704
|
+
*/
|
|
1705
|
+
function endsBefore(previous, next) {
|
|
1706
|
+
const lines = next.text.split('\n');
|
|
1707
|
+
const first = lines[0];
|
|
1708
|
+
switch (previous.block.kind) {
|
|
1709
|
+
case 'paragraph': {
|
|
1710
|
+
// A last line holding a pipe, over a `---`, is a table header and its
|
|
1711
|
+
// delimiter row — which is the parser's answer before it asks anything else.
|
|
1712
|
+
const last = previous.text.slice(previous.text.lastIndexOf('\n') + 1);
|
|
1713
|
+
return (interruptsParagraph(first) || isTableStart(lines, 0)) && !isTableStart([last, first], 0);
|
|
1714
|
+
}
|
|
1715
|
+
case 'quote':
|
|
1716
|
+
return startsBlock(first) && !QUOTE_RE.test(first);
|
|
1717
|
+
case 'list': {
|
|
1718
|
+
const start = matchListStart(first);
|
|
1719
|
+
return startsBlock(first) && (!start || RULE_RE.test(first) || start.ordered !== previous.block.ordered);
|
|
1720
|
+
}
|
|
1721
|
+
case 'table':
|
|
1722
|
+
return !first.includes('|') || startsBlock(first);
|
|
1723
|
+
default:
|
|
1724
|
+
return true;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
function headingText(level, inlines) {
|
|
1728
|
+
const hashes = '#'.repeat(Math.min(Math.max(Math.trunc(level) || 1, 1), 6));
|
|
1729
|
+
// A `#` run at the end, after whitespace, is a closing sequence and the parser
|
|
1730
|
+
// strips it — `# Issue #` would come back as "Issue". `# C#` keeps its sharp.
|
|
1731
|
+
const text = inlineMarkdown(inlines, 'heading').replace(/(^|[ \t])(#*)#$/, '$1$2\\#');
|
|
1732
|
+
return text ? `${hashes} ${text}` : hashes;
|
|
1733
|
+
}
|
|
1734
|
+
function codeBlock(language, raw) {
|
|
1735
|
+
const info = (language ?? '').trim().split(/\s+/)[0].toLowerCase();
|
|
1736
|
+
// A backtick fence absorbs an info string that starts with a backtick into its
|
|
1737
|
+
// own run, so that one info string takes a tilde fence instead.
|
|
1738
|
+
const char = info.startsWith('`') ? '~' : '`';
|
|
1739
|
+
const code = raw.replace(/\r\n?/g, '\n');
|
|
1740
|
+
// Longer than any run of the fence character anywhere in the code, so no line
|
|
1741
|
+
// of it can close the fence early.
|
|
1742
|
+
const longest = (code.match(char === '`' ? /`+/g : /~+/g) ?? []).reduce((max, run) => Math.max(max, run.length), 0);
|
|
1743
|
+
const fence = char.repeat(Math.max(3, longest + 1));
|
|
1744
|
+
return code ? `${fence}${info}\n${code}\n${fence}` : `${fence}${info}\n${fence}`;
|
|
1745
|
+
}
|
|
1746
|
+
function listText(ordered, start, tight, items) {
|
|
1747
|
+
const first = Math.min(Math.max(Math.trunc(start) || 0, 0), MAX_ORDINAL);
|
|
1748
|
+
return items
|
|
1749
|
+
.map((item, index) => listItem(item, ordered ? `${Math.min(first + index, MAX_ORDINAL)}. ` : '- ', tight))
|
|
1750
|
+
.join(tight ? '\n' : '\n\n');
|
|
1751
|
+
}
|
|
1752
|
+
function listItem(item, marker, tight) {
|
|
1753
|
+
const task = item.checked === null ? '' : item.checked ? '[x] ' : '[ ] ';
|
|
1754
|
+
const bare = (marker + task).trimEnd();
|
|
1755
|
+
const body = joinBlocks(item.children.map(renderBlock), tight);
|
|
1756
|
+
if (!body)
|
|
1757
|
+
return bare;
|
|
1758
|
+
// Continuation lines sit at the item's content column, which for `- ` is 2 and
|
|
1759
|
+
// for `10. ` is 4 — the parser dedents by exactly the marker's width.
|
|
1760
|
+
const pad = ' '.repeat(marker.length);
|
|
1761
|
+
const lines = body.split('\n');
|
|
1762
|
+
const head = marker + task + lines[0];
|
|
1763
|
+
const indented = (rest) => rest.map(line => (line ? pad + line : line));
|
|
1764
|
+
// `- ---` is a thematic break, not an item holding one, so a first line that
|
|
1765
|
+
// would read that way moves under a bare marker.
|
|
1766
|
+
return RULE_RE.test(head) ? [bare, ...indented(lines)].join('\n') : [head, ...indented(lines.slice(1))].join('\n');
|
|
1767
|
+
}
|
|
1768
|
+
function tableText(head, rows, align) {
|
|
1769
|
+
const width = Math.min(head.length, MAX_TABLE_COLUMNS);
|
|
1770
|
+
if (!width)
|
|
1771
|
+
return '';
|
|
1772
|
+
const columns = Array.from({ length: width }, (_, index) => index);
|
|
1773
|
+
const row = (cells) => `| ${columns.map(index => cellText(cells[index]?.inlines ?? [])).join(' | ')} |`;
|
|
1774
|
+
const delimiter = `| ${columns.map(index => DELIMITER_ROW[align[index] ?? 'none']).join(' | ')} |`;
|
|
1775
|
+
return [row(head), delimiter, ...rows.map(row)].join('\n');
|
|
1776
|
+
}
|
|
1777
|
+
/**
|
|
1778
|
+
* A cell's inline markdown with every pipe escaped — inside code spans, URLs and
|
|
1779
|
+
* titles too. The row is split on pipes BEFORE any inline parsing, and the split
|
|
1780
|
+
* turns `\|` into `|` and touches nothing else, so escaping every pipe is its
|
|
1781
|
+
* exact inverse.
|
|
1782
|
+
*/
|
|
1783
|
+
function cellText(inlines) {
|
|
1784
|
+
return inlineMarkdown(inlines, 'cell').replace(/\|/g, '\\|');
|
|
1785
|
+
}
|
|
1786
|
+
/**
|
|
1787
|
+
* A backslash before the first character of a line that would otherwise start a
|
|
1788
|
+
* block, or turn the line above it into a table header.
|
|
1789
|
+
*
|
|
1790
|
+
* Every such character is ASCII punctuation, which the parser unescapes, except
|
|
1791
|
+
* the digits of an ordered marker — there the delimiter is escaped instead,
|
|
1792
|
+
* `1986\.`. The test runs on the line as the parser will see it, with indent tabs
|
|
1793
|
+
* expanded, because the parser expands them before it looks.
|
|
1794
|
+
*/
|
|
1795
|
+
function escapeLineStart(line) {
|
|
1796
|
+
const indent = /^[ \t]*/.exec(line)[0];
|
|
1797
|
+
const probe = indent.replace(/\t/g, ' ') + line.slice(indent.length);
|
|
1798
|
+
if (!startsBlock(probe) && !TABLE_DELIM_RE.test(probe))
|
|
1799
|
+
return line;
|
|
1800
|
+
const ordinal = /^\d{1,9}(?=[.)])/.exec(line.slice(indent.length));
|
|
1801
|
+
const at = indent.length + (ordinal ? ordinal[0].length : 0);
|
|
1802
|
+
return `${line.slice(0, at)}\\${line.slice(at)}`;
|
|
1803
|
+
}
|
|
1804
|
+
/** Outermost first. Emphasis never contains strong, which is the nesting `*x **y** z*` breaks on. */
|
|
1805
|
+
const LEVELS = ['link', 'strong', 'del', 'em'];
|
|
1806
|
+
const EMPHASIS = ['strong', 'del', 'em'];
|
|
1807
|
+
const NO_MARKS = { link: null, strong: false, del: false, em: false };
|
|
1808
|
+
/** The parser's own "word character" for `_` flanking. */
|
|
1809
|
+
const WORD_CHAR = /[\p{L}\p{N}]/u;
|
|
1810
|
+
/** A soft break and the indentation around it: whitespace once rendered. */
|
|
1811
|
+
const SOFT_BREAK_RE = /\s*[\r\n]\s*/g;
|
|
1812
|
+
/** What another renderer would decode as an entity; this one does not, so `\&` keeps it literal everywhere. */
|
|
1813
|
+
const ENTITY_RE = /^&(?:#\d+|#x[\da-f]+|[a-z][a-z\d]*);/i;
|
|
1814
|
+
function inlineMarkdown(nodes, context) {
|
|
1815
|
+
let leaves = settle(flatten(nodes, NO_MARKS, context, []), context);
|
|
1816
|
+
// A link whose label is past the parser's cap is not a link on the way back.
|
|
1817
|
+
// Its leaves lose the mark and the run is laid out again, since unwrapping it
|
|
1818
|
+
// can put two emphasis runs next to each other.
|
|
1819
|
+
for (;;) {
|
|
1820
|
+
const state = { tooLong: null };
|
|
1821
|
+
const out = renderNodes(group(leaves, 0, 0), state);
|
|
1822
|
+
if (!state.tooLong)
|
|
1823
|
+
return out;
|
|
1824
|
+
const [from, to] = state.tooLong;
|
|
1825
|
+
leaves = settle(leaves.map((leaf, index) => index >= from && index < to ? { ...leaf, marks: { ...leaf.marks, link: null } } : leaf), context);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
function flatten(nodes, marks, context, out) {
|
|
1829
|
+
for (const node of nodes) {
|
|
1830
|
+
switch (node.kind) {
|
|
1831
|
+
case 'text':
|
|
1832
|
+
out.push({ kind: 'text', value: node.value.replace(SOFT_BREAK_RE, ' '), marks });
|
|
1833
|
+
break;
|
|
1834
|
+
case 'code':
|
|
1835
|
+
// The parser reads a newline inside a code span as a space.
|
|
1836
|
+
out.push({ kind: 'code', value: node.value.replace(/\r\n?|\n/g, ' '), marks });
|
|
1837
|
+
break;
|
|
1838
|
+
case 'break':
|
|
1839
|
+
out.push(context === 'paragraph' ? { kind: 'break', marks } : { kind: 'text', value: ' ', marks });
|
|
1840
|
+
break;
|
|
1841
|
+
case 'image': {
|
|
1842
|
+
const src = safeMarkdownUrl(node.src, 'image');
|
|
1843
|
+
const alt = node.alt.replace(SOFT_BREAK_RE, ' ');
|
|
1844
|
+
const inLabel = marks.link !== null;
|
|
1845
|
+
// A refused source renders its alt as text, so that is what is written.
|
|
1846
|
+
out.push(src
|
|
1847
|
+
? {
|
|
1848
|
+
kind: 'image',
|
|
1849
|
+
src: encodeUrl(src, inLabel),
|
|
1850
|
+
alt,
|
|
1851
|
+
title: writableTitle(node.title, inLabel),
|
|
1852
|
+
marks: { ...NO_MARKS, link: marks.link },
|
|
1853
|
+
}
|
|
1854
|
+
: { kind: 'text', value: alt, marks });
|
|
1855
|
+
break;
|
|
1856
|
+
}
|
|
1857
|
+
case 'link': {
|
|
1858
|
+
// No link inside a link, and no link the reader would refuse anyway. The
|
|
1859
|
+
// attributes are stored as they will be WRITTEN, so two links that differ
|
|
1860
|
+
// only in what cannot be written are one link here, as they will be on the
|
|
1861
|
+
// way back.
|
|
1862
|
+
const href = marks.link ? null : safeMarkdownUrl(node.href, 'link');
|
|
1863
|
+
const next = href
|
|
1864
|
+
? { ...marks, link: { href: encodeUrl(href, false), title: writableTitle(node.title, false) } }
|
|
1865
|
+
: marks;
|
|
1866
|
+
flatten(node.children, next, context, out);
|
|
1867
|
+
break;
|
|
1868
|
+
}
|
|
1869
|
+
default:
|
|
1870
|
+
flatten(node.children, { ...marks, [node.kind]: true }, context, out);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
return out;
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* The leaves in canonical form: merged, with whitespace outside emphasis, trimmed
|
|
1877
|
+
* at the block's edges, and no code span that would open a line as a fence.
|
|
1878
|
+
*/
|
|
1879
|
+
function settle(leaves, context) {
|
|
1880
|
+
let current = leaves;
|
|
1881
|
+
// A code span turned into text can bring a space to the edge of a node, so the
|
|
1882
|
+
// pass runs again until nothing more is unfenced.
|
|
1883
|
+
for (;;) {
|
|
1884
|
+
const out = trimBlock(merge(moveEdgesOut(merge(current))));
|
|
1885
|
+
if (context !== 'paragraph')
|
|
1886
|
+
return out;
|
|
1887
|
+
const unfenced = unfence(out);
|
|
1888
|
+
if (unfenced.every((leaf, index) => leaf === out[index]))
|
|
1889
|
+
return out;
|
|
1890
|
+
current = unfenced;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
function sameLink(a, b) {
|
|
1894
|
+
return a === b || (!!a && !!b && a.href === b.href && a.title === b.title);
|
|
1895
|
+
}
|
|
1896
|
+
function sameMarks(a, b) {
|
|
1897
|
+
return a.strong === b.strong && a.del === b.del && a.em === b.em && sameLink(a.link, b.link);
|
|
1898
|
+
}
|
|
1899
|
+
/** Adjacent text (or code) with the same marks is one node; an empty one is none. */
|
|
1900
|
+
function merge(leaves) {
|
|
1901
|
+
const out = [];
|
|
1902
|
+
for (const leaf of leaves) {
|
|
1903
|
+
if ((leaf.kind === 'text' || leaf.kind === 'code') && !leaf.value)
|
|
1904
|
+
continue;
|
|
1905
|
+
const last = out[out.length - 1];
|
|
1906
|
+
if (last &&
|
|
1907
|
+
(last.kind === 'text' || last.kind === 'code') &&
|
|
1908
|
+
last.kind === leaf.kind &&
|
|
1909
|
+
sameMarks(last.marks, leaf.marks)) {
|
|
1910
|
+
out[out.length - 1] = { ...last, value: last.value + leaf.value };
|
|
1911
|
+
}
|
|
1912
|
+
else {
|
|
1913
|
+
out.push(leaf);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
return out;
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Whitespace and hard breaks at the edge of an emphasis run lose that mark.
|
|
1920
|
+
*
|
|
1921
|
+
* The parser refuses an opener followed by whitespace and a closer preceded by it,
|
|
1922
|
+
* so `** bold **` is literal asterisks; the same text written `**bold**` with the
|
|
1923
|
+
* spaces outside is what was meant. A link keeps its edges — `[` and `]` have no
|
|
1924
|
+
* flanking rule.
|
|
1925
|
+
*
|
|
1926
|
+
* The unit is the NODE the mark will become, not the run of leaves carrying it:
|
|
1927
|
+
* the canonical nesting cuts a strong run wherever a link begins or ends, and an
|
|
1928
|
+
* emphasis run wherever strong or strikethrough changes, and each piece gets
|
|
1929
|
+
* delimiters of its own. Outer marks go first, so an inner node is measured
|
|
1930
|
+
* against leaves that are already final.
|
|
1931
|
+
*/
|
|
1932
|
+
function moveEdgesOut(leaves) {
|
|
1933
|
+
let out = [...leaves];
|
|
1934
|
+
for (const mark of EMPHASIS) {
|
|
1935
|
+
const outer = EMPHASIS.slice(0, EMPHASIS.indexOf(mark));
|
|
1936
|
+
const sameNode = (a, b) => b.marks[mark] && sameLink(a.marks.link, b.marks.link) && outer.every(name => a.marks[name] === b.marks[name]);
|
|
1937
|
+
const next = [];
|
|
1938
|
+
let i = 0;
|
|
1939
|
+
while (i < out.length) {
|
|
1940
|
+
if (!out[i].marks[mark]) {
|
|
1941
|
+
next.push(out[i]);
|
|
1942
|
+
i++;
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
let j = i;
|
|
1946
|
+
while (j < out.length && sameNode(out[i], out[j]))
|
|
1947
|
+
j++;
|
|
1948
|
+
next.push(...trimRun(out.slice(i, j), mark));
|
|
1949
|
+
i = j;
|
|
1950
|
+
}
|
|
1951
|
+
out = next;
|
|
1952
|
+
}
|
|
1953
|
+
return out;
|
|
1954
|
+
}
|
|
1955
|
+
function trimRun(run, mark) {
|
|
1956
|
+
const body = [...run];
|
|
1957
|
+
const head = [];
|
|
1958
|
+
const tail = [];
|
|
1959
|
+
const unmark = (leaf) => ({ ...leaf, marks: { ...leaf.marks, [mark]: false } });
|
|
1960
|
+
while (body.length) {
|
|
1961
|
+
const leaf = body[0];
|
|
1962
|
+
if (leaf.kind === 'break') {
|
|
1963
|
+
head.push(unmark(leaf));
|
|
1964
|
+
body.shift();
|
|
1965
|
+
continue;
|
|
1966
|
+
}
|
|
1967
|
+
if (leaf.kind !== 'text')
|
|
1968
|
+
break;
|
|
1969
|
+
const space = /^\s*/.exec(leaf.value)[0];
|
|
1970
|
+
if (!space)
|
|
1971
|
+
break;
|
|
1972
|
+
head.push(unmark({ ...leaf, value: space }));
|
|
1973
|
+
if (space.length < leaf.value.length) {
|
|
1974
|
+
body[0] = { ...leaf, value: leaf.value.slice(space.length) };
|
|
1975
|
+
break;
|
|
1976
|
+
}
|
|
1977
|
+
body.shift();
|
|
1978
|
+
}
|
|
1979
|
+
while (body.length) {
|
|
1980
|
+
const leaf = body[body.length - 1];
|
|
1981
|
+
if (leaf.kind === 'break') {
|
|
1982
|
+
tail.unshift(unmark(leaf));
|
|
1983
|
+
body.pop();
|
|
1984
|
+
continue;
|
|
1985
|
+
}
|
|
1986
|
+
if (leaf.kind !== 'text')
|
|
1987
|
+
break;
|
|
1988
|
+
const space = /\s*$/.exec(leaf.value)[0];
|
|
1989
|
+
if (!space)
|
|
1990
|
+
break;
|
|
1991
|
+
tail.unshift(unmark({ ...leaf, value: space }));
|
|
1992
|
+
if (space.length < leaf.value.length) {
|
|
1993
|
+
body[body.length - 1] = { ...leaf, value: leaf.value.slice(0, -space.length) };
|
|
1994
|
+
break;
|
|
1995
|
+
}
|
|
1996
|
+
body.pop();
|
|
1997
|
+
}
|
|
1998
|
+
return [...head, ...body, ...tail];
|
|
1999
|
+
}
|
|
2000
|
+
/**
|
|
2001
|
+
* What the parser's trim would take anyway: leading whitespace, and trailing
|
|
2002
|
+
* whitespace and hard breaks (a `\` at the end of a block stays a literal
|
|
2003
|
+
* backslash). Content inside a link label is past its reach and stays.
|
|
2004
|
+
*/
|
|
2005
|
+
function trimBlock(leaves) {
|
|
2006
|
+
const out = [...leaves];
|
|
2007
|
+
let start = 0;
|
|
2008
|
+
let end = out.length;
|
|
2009
|
+
while (start < end) {
|
|
2010
|
+
const leaf = out[start];
|
|
2011
|
+
if (leaf.kind !== 'text' || leaf.marks.link)
|
|
2012
|
+
break;
|
|
2013
|
+
const value = leaf.value.replace(/^\s+/, '');
|
|
2014
|
+
if (value) {
|
|
2015
|
+
out[start] = { ...leaf, value };
|
|
2016
|
+
break;
|
|
2017
|
+
}
|
|
2018
|
+
start++;
|
|
2019
|
+
}
|
|
2020
|
+
while (end > start) {
|
|
2021
|
+
const leaf = out[end - 1];
|
|
2022
|
+
if (leaf.marks.link || (leaf.kind !== 'text' && leaf.kind !== 'break'))
|
|
2023
|
+
break;
|
|
2024
|
+
if (leaf.kind === 'break') {
|
|
2025
|
+
end--;
|
|
2026
|
+
continue;
|
|
2027
|
+
}
|
|
2028
|
+
const value = leaf.value.replace(/\s+$/, '');
|
|
2029
|
+
if (value) {
|
|
2030
|
+
out[end - 1] = { ...leaf, value };
|
|
2031
|
+
break;
|
|
2032
|
+
}
|
|
2033
|
+
end--;
|
|
2034
|
+
}
|
|
2035
|
+
return out.slice(start, end);
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* A code span that opens a paragraph line and needs three or more backticks would
|
|
2039
|
+
* read back as a fence. Nothing can be put in front of it, so it keeps its text
|
|
2040
|
+
* and loses the code mark. It takes a value holding both a single and a double
|
|
2041
|
+
* backtick run to get here.
|
|
2042
|
+
*/
|
|
2043
|
+
function unfence(leaves) {
|
|
2044
|
+
const blank = (leaf) => leaf.kind === 'text' && !leaf.value.trim();
|
|
2045
|
+
return leaves.map((leaf, index) => {
|
|
2046
|
+
if (leaf.kind !== 'code' || ticks(leaf.value) < 3)
|
|
2047
|
+
return leaf;
|
|
2048
|
+
let k = index - 1;
|
|
2049
|
+
while (k >= 0 && blank(leaves[k])) {
|
|
2050
|
+
if (!sameMarks(leaves[k].marks, leaf.marks))
|
|
2051
|
+
return leaf;
|
|
2052
|
+
k--;
|
|
2053
|
+
}
|
|
2054
|
+
const opensLine = k < 0 ? sameMarks(leaf.marks, NO_MARKS) : leaves[k].kind === 'break' && sameMarks(leaves[k].marks, leaf.marks);
|
|
2055
|
+
return opensLine ? { kind: 'text', value: leaf.value, marks: leaf.marks } : leaf;
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
/**
|
|
2059
|
+
* Leaves to a tree, one mark level at a time in {@link LEVELS} order: a maximal
|
|
2060
|
+
* run sharing the mark becomes one node, and what is inside it is grouped by the
|
|
2061
|
+
* next level. Two leaves painted the same way always land in the same shape.
|
|
2062
|
+
*/
|
|
2063
|
+
function group(leaves, level, offset) {
|
|
2064
|
+
if (level === LEVELS.length)
|
|
2065
|
+
return [...leaves];
|
|
2066
|
+
const mark = LEVELS[level];
|
|
2067
|
+
const key = (leaf) => (mark === 'link' ? leaf.marks.link : leaf.marks[mark]);
|
|
2068
|
+
const same = (a, b) => mark === 'link' ? sameLink(a.marks.link, b.marks.link) : a.marks[mark] === b.marks[mark];
|
|
2069
|
+
const out = [];
|
|
2070
|
+
let i = 0;
|
|
2071
|
+
while (i < leaves.length) {
|
|
2072
|
+
let j = i + 1;
|
|
2073
|
+
while (j < leaves.length && same(leaves[i], leaves[j]))
|
|
2074
|
+
j++;
|
|
2075
|
+
const children = group(leaves.slice(i, j), level + 1, offset + i);
|
|
2076
|
+
const on = key(leaves[i]);
|
|
2077
|
+
if (!on) {
|
|
2078
|
+
out.push(...children);
|
|
2079
|
+
}
|
|
2080
|
+
else if (mark === 'link') {
|
|
2081
|
+
out.push({ kind: 'link', link: on, from: offset + i, to: offset + j, children });
|
|
2082
|
+
}
|
|
2083
|
+
else {
|
|
2084
|
+
out.push({ kind: mark, children });
|
|
2085
|
+
}
|
|
2086
|
+
i = j;
|
|
2087
|
+
}
|
|
2088
|
+
return out;
|
|
2089
|
+
}
|
|
2090
|
+
function renderNodes(nodes, state) {
|
|
2091
|
+
const [list, chars] = delimiters(nodes);
|
|
2092
|
+
let out = '';
|
|
2093
|
+
list.forEach((node, index) => {
|
|
2094
|
+
out += renderNode(node, chars[index], list[index + 1], state);
|
|
2095
|
+
});
|
|
2096
|
+
return out;
|
|
2097
|
+
}
|
|
2098
|
+
function renderNode(node, char, next, state) {
|
|
2099
|
+
switch (node.kind) {
|
|
2100
|
+
case 'text':
|
|
2101
|
+
return escapeText(node.value, next?.kind === 'link');
|
|
2102
|
+
case 'code':
|
|
2103
|
+
return codeSpan(node.value);
|
|
2104
|
+
case 'break':
|
|
2105
|
+
return '\\\n';
|
|
2106
|
+
case 'image':
|
|
2107
|
+
return `})`;
|
|
2108
|
+
case 'link': {
|
|
2109
|
+
const auto = autolink(node.link, node.children);
|
|
2110
|
+
if (auto)
|
|
2111
|
+
return auto;
|
|
2112
|
+
const label = renderNodes(node.children, state);
|
|
2113
|
+
if (label.length >= MAX_LABEL_LENGTH) {
|
|
2114
|
+
state.tooLong ??= [node.from, node.to];
|
|
2115
|
+
return label;
|
|
2116
|
+
}
|
|
2117
|
+
return `[${label}](${destination(node.link.href, node.link.title)})`;
|
|
2118
|
+
}
|
|
2119
|
+
case 'del':
|
|
2120
|
+
return `~~${renderNodes(node.children, state)}~~`;
|
|
2121
|
+
default: {
|
|
2122
|
+
const delimiter = node.kind === 'strong' ? char.repeat(2) : char;
|
|
2123
|
+
return delimiter + renderNodes(node.children, state) + delimiter;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
/**
|
|
2128
|
+
* The delimiter character of every strong and emphasis node in a sibling list.
|
|
2129
|
+
*
|
|
2130
|
+
* `*` unless it would touch another `*` run: `*a***b**` reads as one run of three
|
|
2131
|
+
* and parses wrong. The canonical nesting means the only neighbours that can clash
|
|
2132
|
+
* are an emphasis and a strong, so one of them switches to `_` — the emphasis
|
|
2133
|
+
* first — where `_` flanking allows it: no letter or digit against it from
|
|
2134
|
+
* outside, and no `_` neighbour to clash with in turn. Where neither can switch,
|
|
2135
|
+
* the emphasis is dropped and the list is laid out again; its children are
|
|
2136
|
+
* leaves, so the drop cannot create a clash of its own.
|
|
2137
|
+
*/
|
|
2138
|
+
function delimiters(nodes) {
|
|
2139
|
+
let list = nodes;
|
|
2140
|
+
for (;;) {
|
|
2141
|
+
const chars = list.map(node => (node.kind === 'strong' || node.kind === 'em' ? '*' : null));
|
|
2142
|
+
const drop = settleClashes(list, chars);
|
|
2143
|
+
if (drop < 0)
|
|
2144
|
+
return [list, chars];
|
|
2145
|
+
const dropped = list[drop];
|
|
2146
|
+
list = [...list.slice(0, drop), ...dropped.children, ...list.slice(drop + 1)];
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
/** Index of an emphasis node to drop, or -1 once every clash is settled. */
|
|
2150
|
+
function settleClashes(list, chars) {
|
|
2151
|
+
for (let i = 0; i + 1 < list.length; i++) {
|
|
2152
|
+
if (!chars[i] || chars[i] !== chars[i + 1])
|
|
2153
|
+
continue;
|
|
2154
|
+
const emFirst = list[i].kind === 'em';
|
|
2155
|
+
const switched = (emFirst ? [i, i + 1] : [i + 1, i]).find(k => canUnderscore(list, chars, k));
|
|
2156
|
+
if (switched === undefined)
|
|
2157
|
+
return emFirst ? i : i + 1;
|
|
2158
|
+
chars[switched] = '_';
|
|
2159
|
+
}
|
|
2160
|
+
return -1;
|
|
2161
|
+
}
|
|
2162
|
+
function canUnderscore(list, chars, k) {
|
|
2163
|
+
const before = k > 0 ? edgeChar(list[k - 1], chars[k - 1], 'end') : '';
|
|
2164
|
+
const after = k + 1 < list.length ? edgeChar(list[k + 1], chars[k + 1], 'start') : '';
|
|
2165
|
+
return chars[k - 1] !== '_' && chars[k + 1] !== '_' && !WORD_CHAR.test(before) && !WORD_CHAR.test(after);
|
|
2166
|
+
}
|
|
2167
|
+
/**
|
|
2168
|
+
* The character a node puts next to its neighbour — only ever asked whether it is
|
|
2169
|
+
* a letter or a digit. Escaping inserts a backslash before punctuation and never
|
|
2170
|
+
* before a word character, so the raw text answers that question for text.
|
|
2171
|
+
*/
|
|
2172
|
+
function edgeChar(node, char, side) {
|
|
2173
|
+
switch (node.kind) {
|
|
2174
|
+
case 'text':
|
|
2175
|
+
return side === 'start' ? node.value[0] : node.value[node.value.length - 1];
|
|
2176
|
+
case 'strong':
|
|
2177
|
+
case 'em':
|
|
2178
|
+
return char ?? '';
|
|
2179
|
+
default:
|
|
2180
|
+
return '';
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
/**
|
|
2184
|
+
* Text with every character the parser would read as syntax escaped — and no
|
|
2185
|
+
* more, so prose stays prose. `snake_case` keeps its underscores, `2 * 3` its
|
|
2186
|
+
* star, `a < b` its bracket: in those positions the parser can never read them as
|
|
2187
|
+
* markup. The ends of the string are treated as the worst case, since whatever is
|
|
2188
|
+
* written next to it is not known here.
|
|
2189
|
+
*/
|
|
2190
|
+
function escapeText(value, beforeLink) {
|
|
2191
|
+
let out = '';
|
|
2192
|
+
for (let i = 0; i < value.length; i++) {
|
|
2193
|
+
const char = value[i];
|
|
2194
|
+
if (mustEscape(value, i, beforeLink))
|
|
2195
|
+
out += '\\';
|
|
2196
|
+
out += char;
|
|
2197
|
+
}
|
|
2198
|
+
// A bare URL in plain text would come back as a link. The escaped colon is
|
|
2199
|
+
// invisible once parsed, and it stops `BARE_URL_RE` from matching.
|
|
2200
|
+
return out.replace(/(https?):(?=\/\/)/gi, '$1\\:');
|
|
2201
|
+
}
|
|
2202
|
+
function mustEscape(value, i, beforeLink) {
|
|
2203
|
+
const prev = value[i - 1];
|
|
2204
|
+
const next = value[i + 1];
|
|
2205
|
+
const space = (c) => c !== undefined && /\s/.test(c);
|
|
2206
|
+
const word = (c) => c !== undefined && WORD_CHAR.test(c);
|
|
2207
|
+
switch (value[i]) {
|
|
2208
|
+
case '\\':
|
|
2209
|
+
case '`':
|
|
2210
|
+
case '[':
|
|
2211
|
+
case ']':
|
|
2212
|
+
return true;
|
|
2213
|
+
// Between two spaces a star can neither open (followed by whitespace) nor
|
|
2214
|
+
// close (preceded by it).
|
|
2215
|
+
case '*':
|
|
2216
|
+
return !(space(prev) && space(next));
|
|
2217
|
+
// Between two word characters an underscore is never a delimiter.
|
|
2218
|
+
case '_':
|
|
2219
|
+
return !(word(prev) && word(next));
|
|
2220
|
+
// Strikethrough needs a pair; a single tilde between other characters is text.
|
|
2221
|
+
case '~':
|
|
2222
|
+
return prev === undefined || prev === '~' || next === undefined || next === '~';
|
|
2223
|
+
// An autolink needs a scheme or an address right after the bracket.
|
|
2224
|
+
case '<':
|
|
2225
|
+
return !space(next);
|
|
2226
|
+
// `!` followed by a link's `[` would turn the link into an image.
|
|
2227
|
+
case '!':
|
|
2228
|
+
return beforeLink && next === undefined;
|
|
2229
|
+
case '&':
|
|
2230
|
+
return ENTITY_RE.test(value.slice(i));
|
|
2231
|
+
default:
|
|
2232
|
+
return false;
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
/** The shortest backtick run that does not occur inside the value. */
|
|
2236
|
+
function ticks(value) {
|
|
2237
|
+
const runs = new Set((value.match(/`+/g) ?? []).map(run => run.length));
|
|
2238
|
+
let length = 1;
|
|
2239
|
+
while (runs.has(length))
|
|
2240
|
+
length++;
|
|
2241
|
+
return length;
|
|
2242
|
+
}
|
|
2243
|
+
/**
|
|
2244
|
+
* A code span, padded where the parser would otherwise eat a character: it strips
|
|
2245
|
+
* one space from each end when both are present, and a backtick at either end
|
|
2246
|
+
* would merge into the fence.
|
|
2247
|
+
*/
|
|
2248
|
+
function codeSpan(value) {
|
|
2249
|
+
const fence = '`'.repeat(ticks(value));
|
|
2250
|
+
const pad = codeSpanValue(value) !== value || value.startsWith('`') || value.endsWith('`');
|
|
2251
|
+
return pad ? `${fence} ${value} ${fence}` : `${fence}${value}${fence}`;
|
|
2252
|
+
}
|
|
2253
|
+
/** `<https://…>` or `<name@host>` when the label IS the address — the form the parser reads back identically. */
|
|
2254
|
+
function autolink(link, children) {
|
|
2255
|
+
const only = children.length === 1 ? children[0] : null;
|
|
2256
|
+
if (link.title !== null || only?.kind !== 'text')
|
|
2257
|
+
return null;
|
|
2258
|
+
const whole = (re, value) => re.exec(`<${value}>`)?.[0].length === value.length + 2;
|
|
2259
|
+
if (only.value === link.href && whole(AUTOLINK_RE, link.href))
|
|
2260
|
+
return `<${link.href}>`;
|
|
2261
|
+
if (`mailto:${only.value}` === link.href && whole(EMAIL_AUTOLINK_RE, only.value))
|
|
2262
|
+
return `<${only.value}>`;
|
|
2263
|
+
return null;
|
|
2264
|
+
}
|
|
2265
|
+
/**
|
|
2266
|
+
* A URL as this parser will read it back, and as a browser resolves it.
|
|
2267
|
+
*
|
|
2268
|
+
* The destination scan counts parentheses and ends at a line break, so an angle
|
|
2269
|
+
* bracket and an unbalanced parenthesis are percent-encoded — to a browser the
|
|
2270
|
+
* same address. A tab or a line break is removed rather than encoded: the URL
|
|
2271
|
+
* parser strips them, so the address never had them, and `%0A` would be a
|
|
2272
|
+
* different one. A backslash is kept as it is — the parser keeps it, and in an
|
|
2273
|
+
* http(s) URL a browser reads it as `/`, which `%5C` is not — except where the
|
|
2274
|
+
* scan would take it for an escape and skip what follows: before a parenthesis,
|
|
2275
|
+
* and at the very end, where it would swallow the closing one. Inside a link label
|
|
2276
|
+
* the label scan also runs over an image's destination, so its brackets and
|
|
2277
|
+
* backticks are encoded too. Spaces are not encoded: {@link destination} carries
|
|
2278
|
+
* them in the `<…>` form.
|
|
2279
|
+
*/
|
|
2280
|
+
function encodeUrl(url, inLabel) {
|
|
2281
|
+
const out = url
|
|
2282
|
+
.replace(/[\t\r\n]/g, '')
|
|
2283
|
+
.replace(/\\(?=[()]|$)/g, percent)
|
|
2284
|
+
.replace(inLabel ? /[<>[\]`]/g : /[<>]/g, percent);
|
|
2285
|
+
return balanced(out) ? out : out.replace(/[()]/g, percent);
|
|
2286
|
+
}
|
|
2287
|
+
function percent(char) {
|
|
2288
|
+
return `%${char.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')}`;
|
|
2289
|
+
}
|
|
2290
|
+
/** Parentheses the destination scan accepts: balanced, never closing early, and under its nesting cap. */
|
|
2291
|
+
function balanced(url) {
|
|
2292
|
+
let depth = 0;
|
|
2293
|
+
for (const char of url) {
|
|
2294
|
+
if (char === '(' && ++depth >= MAX_DESTINATION_NESTING)
|
|
2295
|
+
return false;
|
|
2296
|
+
if (char === ')' && --depth < 0)
|
|
2297
|
+
return false;
|
|
2298
|
+
}
|
|
2299
|
+
return depth === 0;
|
|
2300
|
+
}
|
|
2301
|
+
/**
|
|
2302
|
+
* The title as it can be written, or `null` when it cannot: a line break becomes a
|
|
2303
|
+
* space, and the destination scan runs through the title counting parentheses (a
|
|
2304
|
+
* backslash skipping the next character), so one that would close the scan early
|
|
2305
|
+
* or leave it open is dropped. Inside a link label the label scan runs through it
|
|
2306
|
+
* as well, so a bracket, a backtick or a backslash drops it there.
|
|
2307
|
+
*/
|
|
2308
|
+
function writableTitle(raw, inLabel) {
|
|
2309
|
+
if (raw === null)
|
|
2310
|
+
return null;
|
|
2311
|
+
const title = raw.replace(SOFT_BREAK_RE, ' ');
|
|
2312
|
+
if (inLabel && /[[\]`\\]/.test(title))
|
|
2313
|
+
return null;
|
|
2314
|
+
let depth = 1;
|
|
2315
|
+
for (let i = 0; i < title.length; i++) {
|
|
2316
|
+
const char = title[i];
|
|
2317
|
+
if (char === '\\') {
|
|
2318
|
+
i++;
|
|
2319
|
+
continue;
|
|
2320
|
+
}
|
|
2321
|
+
if (char === '(' && ++depth > MAX_DESTINATION_NESTING)
|
|
2322
|
+
return null;
|
|
2323
|
+
if (char === ')' && --depth === 0)
|
|
2324
|
+
return null;
|
|
2325
|
+
}
|
|
2326
|
+
return depth === 1 ? title : null;
|
|
2327
|
+
}
|
|
2328
|
+
/** An encoded URL and a writable title, inside the parentheses. */
|
|
2329
|
+
function destination(url, title) {
|
|
2330
|
+
const target = /\s/.test(url) ? `<${url}>` : url;
|
|
2331
|
+
return title === null ? target : `${target} "${title}"`;
|
|
2332
|
+
}
|
|
2333
|
+
|
|
1553
2334
|
/**
|
|
1554
2335
|
* @license
|
|
1555
2336
|
*
|
|
@@ -1598,5 +2379,5 @@ function provideWrMarkdownHighlighter(highlighter) {
|
|
|
1598
2379
|
* Generated bundle index. Do not edit.
|
|
1599
2380
|
*/
|
|
1600
2381
|
|
|
1601
|
-
export { WR_MARKDOWN_HIGHLIGHTER, WrMarkdown, WrMarkdownHighlight, parseInlines, parseMarkdown, plainText, provideWrMarkdownHighlighter, safeMarkdownUrl };
|
|
2382
|
+
export { WR_MARKDOWN_HIGHLIGHTER, WrMarkdown, WrMarkdownHighlight, parseInlines, parseMarkdown, plainText, provideWrMarkdownHighlighter, safeMarkdownUrl, serializeMarkdown };
|
|
1602
2383
|
//# sourceMappingURL=ngwr-markdown.mjs.map
|