@astryxdesign/core 0.6.1 → 0.6.2-canary.023c3e4
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/CHANGELOG.md +33 -0
- package/dist/BottomSheet/BottomSheet.d.ts +1 -1
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +3 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts +7 -5
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +56 -19
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +2 -2
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +12 -2
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +6 -1
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +1 -1
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +4 -4
- package/dist/DateInput/NativeDateField.d.ts.map +1 -1
- package/dist/DateInput/NativeDateField.js +16 -16
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +2 -2
- package/dist/DateRangeInput/DateRangeInput.d.ts +3 -0
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +20 -13
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +8 -8
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +4 -4
- package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts +9 -3
- package/dist/DateTimeInput/nativePickerSegmentStyles.d.ts.map +1 -1
- package/dist/DateTimeInput/nativePickerSegmentStyles.js +3 -3
- package/dist/Dialog/DialogHeader.d.ts +1 -1
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +10 -7
- package/dist/Field/PanelSearchInput.d.ts.map +1 -1
- package/dist/Field/PanelSearchInput.js +1 -1
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +16 -10
- package/dist/Kbd/Kbd.d.ts +5 -3
- package/dist/Kbd/Kbd.d.ts.map +1 -1
- package/dist/Kbd/Kbd.js +36 -42
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +6 -2
- package/dist/Markdown/Markdown.d.ts +18 -4
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.fr23.bench.d.ts +2 -0
- package/dist/Markdown/Markdown.fr23.bench.d.ts.map +1 -0
- package/dist/Markdown/Markdown.fr23.bench.js +150 -0
- package/dist/Markdown/Markdown.fr23.sampling.d.ts +2 -0
- package/dist/Markdown/Markdown.fr23.sampling.d.ts.map +1 -0
- package/dist/Markdown/Markdown.fr23.sampling.js +43 -0
- package/dist/Markdown/Markdown.js +311 -114
- package/dist/Markdown/ast.d.ts +162 -0
- package/dist/Markdown/ast.d.ts.map +1 -0
- package/dist/Markdown/ast.js +74 -0
- package/dist/Markdown/index.d.ts +5 -3
- package/dist/Markdown/index.d.ts.map +1 -1
- package/dist/Markdown/index.js +3 -2
- package/dist/Markdown/parser/index.d.ts +10 -0
- package/dist/Markdown/parser/index.d.ts.map +1 -0
- package/dist/Markdown/parser/index.js +10 -0
- package/dist/Markdown/parser.d.ts +220 -38
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +1072 -188
- package/dist/Markdown/plugins/frontmatter.d.ts +32 -0
- package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -0
- package/dist/Markdown/plugins/frontmatter.js +132 -0
- package/dist/Markdown/plugins/index.d.ts +16 -0
- package/dist/Markdown/plugins/index.d.ts.map +1 -0
- package/dist/Markdown/plugins/index.js +13 -0
- package/dist/Markdown/plugins/protocol.d.ts +191 -0
- package/dist/Markdown/plugins/protocol.d.ts.map +1 -0
- package/dist/Markdown/plugins/protocol.js +957 -0
- package/dist/Markdown/plugins/semanticFence.d.ts +36 -0
- package/dist/Markdown/plugins/semanticFence.d.ts.map +1 -0
- package/dist/Markdown/plugins/semanticFence.js +213 -0
- package/dist/Markdown/plugins/sourceDecoration.d.ts +76 -0
- package/dist/Markdown/plugins/sourceDecoration.d.ts.map +1 -0
- package/dist/Markdown/plugins/sourceDecoration.js +393 -0
- package/dist/Markdown/plugins/textTransform.d.ts +30 -0
- package/dist/Markdown/plugins/textTransform.d.ts.map +1 -0
- package/dist/Markdown/plugins/textTransform.js +374 -0
- package/dist/Markdown/remark.d.ts +196 -0
- package/dist/Markdown/remark.d.ts.map +1 -0
- package/dist/Markdown/remark.js +1062 -0
- package/dist/Markdown/url.d.ts +4 -0
- package/dist/Markdown/url.d.ts.map +1 -0
- package/dist/Markdown/url.js +25 -0
- package/dist/Markdown/utils.d.ts +4 -2
- package/dist/Markdown/utils.d.ts.map +1 -1
- package/dist/Markdown/utils.js +2 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +4 -4
- package/dist/Outline/index.d.ts +1 -0
- package/dist/Outline/index.d.ts.map +1 -1
- package/dist/Outline/parseOutlineFromMarkdown.d.ts +7 -1
- package/dist/Outline/parseOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/parseOutlineFromMarkdown.js +14 -8
- package/dist/Outline/useOutlineFromMarkdown.d.ts +3 -1
- package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
- package/dist/Outline/useOutlineFromMarkdown.js +8 -3
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +46 -30
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +36 -31
- package/dist/RadioList/RadioListItem.d.ts.map +1 -1
- package/dist/RadioList/RadioListItem.js +13 -1
- package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
- package/dist/SegmentedControl/SegmentedControlItem.js +5 -5
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +1 -1
- package/dist/SideNav/SideNav.d.ts +2 -1
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +9 -3
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +19 -10
- package/dist/Spinner/Spinner.d.ts +1 -1
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +45 -21
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +11 -0
- package/dist/TabList/Tab.d.ts +1 -1
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +20 -7
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +1 -1
- package/dist/TextInput/TextInput.d.ts.map +1 -1
- package/dist/TextInput/TextInput.js +2 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +4 -4
- package/dist/ToggleButton/ToggleButton.d.ts +2 -1
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +7 -1
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +16 -7
- package/dist/astryx.css +19 -8
- package/dist/hooks/scrollKeyboardDelegation.d.ts +3 -0
- package/dist/hooks/scrollKeyboardDelegation.d.ts.map +1 -0
- package/dist/hooks/scrollKeyboardDelegation.js +146 -0
- package/dist/hooks/useScrollableArea.d.ts +6 -2
- package/dist/hooks/useScrollableArea.d.ts.map +1 -1
- package/dist/hooks/useScrollableArea.js +17 -5
- package/dist/utils/interactionOverlay.stylex.d.ts +8 -0
- package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
- package/dist/utils/interactionOverlay.stylex.js +9 -0
- package/locales/en.json +16 -0
- package/locales/pseudo.json +12 -0
- package/package.json +22 -5
- package/scripts/agent-doc-state.mjs +1 -1
- package/src/BottomSheet/BottomSheet.doc.mjs +8 -1
- package/src/BottomSheet/BottomSheet.spec.md +46 -20
- package/src/BottomSheet/BottomSheet.test.tsx +6 -3
- package/src/BottomSheet/BottomSheet.tsx +3 -1
- package/src/BottomSheet/BottomSheetKeyboard.test.tsx +195 -0
- package/src/BottomSheet/BottomSheetPanel.test.tsx +11 -1
- package/src/BottomSheet/BottomSheetPanel.tsx +49 -16
- package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +344 -0
- package/src/Button/__tests__/Button.a11y.chromium.spec.ts +17 -1
- package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -29
- package/src/Button/__tests__/Button.a11y.renders.tsx +9 -2
- package/src/Button/__tests__/Button.a11y.states.ts +9 -0
- package/src/Chat/ChatComposerInput.doc.mjs +3 -3
- package/src/Chat/ChatComposerInput.tsx +10 -2
- package/src/CheckboxInput/CheckboxInput.doc.mjs +11 -0
- package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
- package/src/CheckboxInput/CheckboxInput.tsx +21 -1
- package/src/ClickableCard/ClickableCard.test.tsx +102 -5
- package/src/Collapsible/Collapsible.doc.mjs +11 -0
- package/src/Collapsible/Collapsible.test.tsx +21 -0
- package/src/Collapsible/Collapsible.tsx +5 -0
- package/src/CommandPalette/CommandPaletteInput.tsx +6 -1
- package/src/ComplexSelector/ComplexSelector.tsx +6 -1
- package/src/DateInput/DateInput.tsx +6 -1
- package/src/DateInput/NativeDateField.tsx +24 -8
- package/src/DateInput/TouchDateField.tsx +6 -2
- package/src/DateRangeInput/DateRangeInput.doc.mjs +35 -7
- package/src/DateRangeInput/DateRangeInput.spec.md +203 -0
- package/src/DateRangeInput/DateRangeInput.test.tsx +100 -4
- package/src/DateRangeInput/DateRangeInput.tsx +35 -21
- package/src/DateTimeInput/DateTimeInput.tsx +6 -1
- package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
- package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
- package/src/Dialog/Dialog.doc.mjs +3 -0
- package/src/Dialog/Dialog.spec.md +1 -1
- package/src/Dialog/DialogHeader.doc.mjs +38 -0
- package/src/Dialog/DialogHeader.test.tsx +49 -0
- package/src/Dialog/DialogHeader.tsx +23 -4
- package/src/Dialog/modules/DialogHeader.spec.md +152 -0
- package/src/Field/PanelSearchInput.tsx +5 -2
- package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
- package/src/FileInput/FileInput.doc.mjs +2 -0
- package/src/FileInput/FileInput.spec.md +199 -0
- package/src/FileInput/FileInput.test.tsx +14 -0
- package/src/FileInput/FileInput.tsx +24 -5
- package/src/Kbd/Kbd.doc.mjs +3 -3
- package/src/Kbd/Kbd.test.tsx +57 -1
- package/src/Kbd/Kbd.tsx +57 -37
- package/src/Layout/LayoutFooter.doc.mjs +17 -0
- package/src/Link/Link.doc.mjs +11 -0
- package/src/Link/Link.test.tsx +24 -0
- package/src/Link/Link.tsx +5 -0
- package/src/Markdown/Markdown.doc.mjs +439 -42
- package/src/Markdown/Markdown.fr23.bench.ts +174 -0
- package/src/Markdown/Markdown.fr23.perf.test.ts +249 -0
- package/src/Markdown/Markdown.fr23.sampling.ts +49 -0
- package/src/Markdown/Markdown.helpers.perf.test.ts +489 -0
- package/src/Markdown/Markdown.public.test.ts +280 -0
- package/src/Markdown/Markdown.spec.md +304 -72
- package/src/Markdown/Markdown.test.tsx +118 -3
- package/src/Markdown/Markdown.tsx +489 -129
- package/src/Markdown/ast.test.ts +307 -0
- package/src/Markdown/ast.ts +337 -0
- package/src/Markdown/incremental.test.ts +187 -7
- package/src/Markdown/index.ts +43 -1
- package/src/Markdown/modules/remark.spec.md +269 -0
- package/src/Markdown/parser/index.ts +64 -0
- package/src/Markdown/parser.perf.test.ts +3 -1
- package/src/Markdown/parser.public.test.ts +103 -0
- package/src/Markdown/parser.test.ts +122 -0
- package/src/Markdown/parser.ts +1721 -248
- package/src/Markdown/plugins/frontmatter.test.tsx +125 -0
- package/src/Markdown/plugins/frontmatter.ts +198 -0
- package/src/Markdown/plugins/index.ts +53 -0
- package/src/Markdown/plugins/protocol.test.tsx +1022 -0
- package/src/Markdown/plugins/protocol.ts +1618 -0
- package/src/Markdown/plugins/semanticFence.test.tsx +546 -0
- package/src/Markdown/plugins/semanticFence.ts +424 -0
- package/src/Markdown/plugins/sourceDecoration.test.tsx +799 -0
- package/src/Markdown/plugins/sourceDecoration.ts +539 -0
- package/src/Markdown/plugins/textTransform.test.ts +467 -0
- package/src/Markdown/plugins/textTransform.ts +554 -0
- package/src/Markdown/remark.test.tsx +1877 -0
- package/src/Markdown/remark.ts +1574 -0
- package/src/Markdown/url.ts +30 -0
- package/src/Markdown/utils.ts +19 -1
- package/src/MultiSelector/MultiSelector.test.tsx +1 -35
- package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +128 -0
- package/src/NumberInput/NumberInput.tsx +6 -1
- package/src/Outline/Outline.doc.mjs +7 -5
- package/src/Outline/Outline.spec.md +1 -1
- package/src/Outline/index.ts +1 -0
- package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +142 -0
- package/src/Outline/parseOutlineFromMarkdown.ts +40 -10
- package/src/Outline/useOutlineFromMarkdown.ts +15 -3
- package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +150 -1
- package/src/PowerSearch/PowerSearchEditPopover.tsx +51 -28
- package/src/ProgressBar/ProgressBar.test.tsx +29 -11
- package/src/ProgressBar/ProgressBar.tsx +59 -72
- package/src/RadioList/RadioList.doc.mjs +11 -0
- package/src/RadioList/RadioList.test.tsx +32 -0
- package/src/RadioList/RadioListItem.tsx +25 -1
- package/src/ScrollableArea/modules/useScrollableArea.spec.md +50 -22
- package/src/SegmentedControl/SegmentedControl.doc.mjs +2 -2
- package/src/SegmentedControl/SegmentedControl.test.tsx +31 -0
- package/src/SegmentedControl/SegmentedControlItem.tsx +6 -9
- package/src/Selector/Selector.spec.md +12 -9
- package/src/Selector/Selector.tsx +6 -1
- package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +130 -0
- package/src/Selector/__tests__/Listbox.a11y.dom.ts +38 -0
- package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +63 -0
- package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +37 -0
- package/src/Selector/__tests__/Listbox.a11y.states.ts +232 -0
- package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +121 -0
- package/src/SideNav/SideNav.doc.mjs +1 -1
- package/src/SideNav/SideNav.test.tsx +10 -0
- package/src/SideNav/SideNav.tsx +14 -2
- package/src/Slider/Slider.doc.mjs +27 -0
- package/src/Slider/Slider.spec.md +61 -47
- package/src/Slider/Slider.test.tsx +146 -0
- package/src/Slider/Slider.tsx +37 -13
- package/src/Spinner/Spinner.doc.mjs +6 -3
- package/src/Spinner/Spinner.test.tsx +41 -0
- package/src/Spinner/Spinner.tsx +62 -32
- package/src/Switch/Switch.doc.mjs +11 -0
- package/src/Switch/Switch.test.tsx +16 -0
- package/src/Switch/Switch.tsx +28 -0
- package/src/TabList/Tab.tsx +35 -7
- package/src/TabList/TabList.doc.mjs +11 -0
- package/src/TabList/TabList.test.tsx +66 -0
- package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
- package/src/TextArea/TextArea.tsx +6 -1
- package/src/TextInput/TextInput.test.tsx +20 -1
- package/src/TextInput/TextInput.tsx +6 -1
- package/src/TimeInput/TimeInput.tsx +6 -1
- package/src/ToggleButton/ToggleButton.test.tsx +133 -0
- package/src/ToggleButton/ToggleButton.tsx +9 -2
- package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
- package/src/Tokenizer/Tokenizer.spec.md +142 -75
- package/src/TreeList/TreeList.spec.md +123 -75
- package/src/Typeahead/BaseTypeahead.spec.md +4 -3
- package/src/Typeahead/BaseTypeahead.tsx +21 -7
- package/src/Typeahead/Typeahead.test.tsx +53 -0
- package/src/__tests__/PressedState.a11y.chromium.spec.ts +813 -0
- package/src/__tests__/pressState.ts +93 -0
- package/src/hooks/scrollKeyboardDelegation.test.ts +155 -0
- package/src/hooks/scrollKeyboardDelegation.ts +233 -0
- package/src/hooks/useScrollableArea.doc.mjs +15 -3
- package/src/hooks/useScrollableArea.test.tsx +59 -1
- package/src/hooks/useScrollableArea.ts +34 -10
- package/src/inputFontFloor.test.ts +106 -0
- package/src/theme/derivedVarRegistry.test.ts +6 -4
- package/src/utils/interactionOverlay.stylex.ts +20 -0
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Markdown.tsx
|
|
7
|
-
* @input Markdown string,
|
|
8
|
-
* @output Exports Markdown component and
|
|
7
|
+
* @input Markdown string, canonical AST, optional plugins and custom renderers
|
|
8
|
+
* @output Exports Markdown component, MarkdownProps, and renderer contracts
|
|
9
9
|
* @position Core implementation; renders markdown as Astryx components
|
|
10
10
|
*/
|
|
11
|
-
import { useMemo, useRef } from 'react';
|
|
11
|
+
import { Component, Suspense, useMemo, useRef } from 'react';
|
|
12
12
|
import { Fragment } from 'react';
|
|
13
13
|
import * as stylex from '@stylexjs/stylex';
|
|
14
14
|
import "../theme/tokens.stylex.js";
|
|
@@ -31,7 +31,11 @@ import { computeBoundaries, computeSegments } from "./streaming.js";
|
|
|
31
31
|
import { useTheme } from "../theme/useTheme.js";
|
|
32
32
|
import { Citation } from "../Citation/Citation.js";
|
|
33
33
|
import { useLinkComponent } from "../Link/useLinkComponent.js";
|
|
34
|
-
import {
|
|
34
|
+
import { parseInlineAst, parseMarkdownAst, parseMarkdownAstIncremental, createIncrementalState, trimStreamingArtifacts, slugify, uniqueSlug } from "./parser.js";
|
|
35
|
+
import { getMarkdownAstLegacyCodeLanguage, markdownAstText } from "./ast.js";
|
|
36
|
+
import { applyMarkdownTransforms, getMarkdownExtensionRenderer, markdownExtensionText, prepareMarkdownPlugins, reportMarkdownPluginFailure } from "./plugins/protocol.js";
|
|
37
|
+
import { getMarkdownFenceProposal } from "./plugins/semanticFence.js";
|
|
38
|
+
import { sanitizeMarkdownUrl } from "./url.js";
|
|
35
39
|
import { themeProps } from "../utils/themeProps.js";
|
|
36
40
|
import { useTranslator } from "../i18n/index.js";
|
|
37
41
|
|
|
@@ -42,7 +46,7 @@ import { useTranslator } from "../i18n/index.js";
|
|
|
42
46
|
/**
|
|
43
47
|
* A plugin that transforms text patterns into custom React elements
|
|
44
48
|
* inside Markdown. Applied to parsed text nodes only — code blocks,
|
|
45
|
-
* inline code, and other non-prose contexts are unaffected.
|
|
49
|
+
* inline code, math, and other non-prose contexts are unaffected.
|
|
46
50
|
*
|
|
47
51
|
* Follows Lexical's TextMatchTransformer architecture:
|
|
48
52
|
* - `pattern` for initial regex matching
|
|
@@ -331,17 +335,20 @@ function countInlineTextLength(nodes) {
|
|
|
331
335
|
for (const node of nodes) {
|
|
332
336
|
switch (node.type) {
|
|
333
337
|
case 'text':
|
|
334
|
-
len += node.
|
|
338
|
+
len += node.value.length;
|
|
335
339
|
break;
|
|
336
|
-
case '
|
|
337
|
-
len += node.
|
|
340
|
+
case 'inlineCode':
|
|
341
|
+
len += node.value.length;
|
|
342
|
+
break;
|
|
343
|
+
case 'inlineMath':
|
|
344
|
+
len += node.value.length;
|
|
338
345
|
break;
|
|
339
346
|
case 'image':
|
|
340
347
|
len += node.alt.length;
|
|
341
348
|
break;
|
|
342
|
-
case '
|
|
343
|
-
case '
|
|
344
|
-
case '
|
|
349
|
+
case 'strong':
|
|
350
|
+
case 'emphasis':
|
|
351
|
+
case 'delete':
|
|
345
352
|
case 'link':
|
|
346
353
|
len += countInlineTextLength(node.children);
|
|
347
354
|
break;
|
|
@@ -351,6 +358,9 @@ function countInlineTextLength(nodes) {
|
|
|
351
358
|
case 'citation':
|
|
352
359
|
len += 1;
|
|
353
360
|
break;
|
|
361
|
+
case 'extension':
|
|
362
|
+
len += node.source?.length ?? 0;
|
|
363
|
+
break;
|
|
354
364
|
}
|
|
355
365
|
}
|
|
356
366
|
return len;
|
|
@@ -367,28 +377,31 @@ function countBlockTextLength(nodes) {
|
|
|
367
377
|
case 'paragraph':
|
|
368
378
|
len += countInlineTextLength(node.children);
|
|
369
379
|
break;
|
|
370
|
-
case '
|
|
371
|
-
len += node.
|
|
380
|
+
case 'code':
|
|
381
|
+
len += node.value.length;
|
|
382
|
+
break;
|
|
383
|
+
case 'math':
|
|
384
|
+
len += node.value.length;
|
|
372
385
|
break;
|
|
373
386
|
case 'blockquote':
|
|
374
387
|
len += countBlockTextLength(node.children);
|
|
375
388
|
break;
|
|
376
389
|
case 'list':
|
|
377
|
-
for (const item of node.
|
|
390
|
+
for (const item of node.children) {
|
|
378
391
|
len += countBlockTextLength(item.children);
|
|
379
392
|
}
|
|
380
393
|
break;
|
|
381
394
|
case 'table':
|
|
382
|
-
for (const
|
|
383
|
-
|
|
384
|
-
}
|
|
385
|
-
for (const row of node.rows) {
|
|
386
|
-
for (const cell of row) {
|
|
395
|
+
for (const row of node.children) {
|
|
396
|
+
for (const cell of row.children) {
|
|
387
397
|
len += countInlineTextLength(cell.children);
|
|
388
398
|
}
|
|
389
399
|
}
|
|
390
400
|
break;
|
|
391
|
-
case '
|
|
401
|
+
case 'extension':
|
|
402
|
+
len += node.source?.length ?? 0;
|
|
403
|
+
break;
|
|
404
|
+
case 'thematicBreak':
|
|
392
405
|
break;
|
|
393
406
|
case 'image':
|
|
394
407
|
len += node.alt.length;
|
|
@@ -407,26 +420,6 @@ const headingStyles = {
|
|
|
407
420
|
};
|
|
408
421
|
|
|
409
422
|
// ---------------------------------------------------------------------------
|
|
410
|
-
// URL sanitization — block dangerous protocols
|
|
411
|
-
// ---------------------------------------------------------------------------
|
|
412
|
-
|
|
413
|
-
const DANGEROUS_URL_PATTERN = /^(javascript|data|vbscript):/i;
|
|
414
|
-
function sanitizeUrl(url) {
|
|
415
|
-
// Strip control characters before testing, the same normalization the
|
|
416
|
-
// parser's isSafeUrl applies — the anchored pattern must see the URL the
|
|
417
|
-
// way a browser will. Return the normalized value so the stripped
|
|
418
|
-
// characters don't ride along into an attribute or component override.
|
|
419
|
-
// eslint-disable-next-line no-control-regex -- control chars are the bypass
|
|
420
|
-
const normalized = url.replace(/[\x00-\x1f\x7f]/g, '').trim();
|
|
421
|
-
if (normalized.length === 0) {
|
|
422
|
-
return null;
|
|
423
|
-
}
|
|
424
|
-
if (DANGEROUS_URL_PATTERN.test(normalized)) {
|
|
425
|
-
return null;
|
|
426
|
-
}
|
|
427
|
-
return normalized;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
423
|
// ---------------------------------------------------------------------------
|
|
431
424
|
// Inline plugin matching
|
|
432
425
|
// ---------------------------------------------------------------------------
|
|
@@ -518,6 +511,42 @@ function applyInlinePlugins(text, plugins) {
|
|
|
518
511
|
}
|
|
519
512
|
return segments;
|
|
520
513
|
}
|
|
514
|
+
class MarkdownPluginBoundary extends Component {
|
|
515
|
+
state = {
|
|
516
|
+
failed: false,
|
|
517
|
+
resetKey: this.props.resetKey,
|
|
518
|
+
resetRenderer: this.props.resetRenderer
|
|
519
|
+
};
|
|
520
|
+
static getDerivedStateFromError() {
|
|
521
|
+
return {
|
|
522
|
+
failed: true
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
static getDerivedStateFromProps(props, state) {
|
|
526
|
+
return props.resetKey === state.resetKey && props.resetRenderer === state.resetRenderer ? null : {
|
|
527
|
+
failed: false,
|
|
528
|
+
resetKey: props.resetKey,
|
|
529
|
+
resetRenderer: props.resetRenderer
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
componentDidCatch(error) {
|
|
533
|
+
reportMarkdownPluginFailure(this.props.pluginName, 'render', error);
|
|
534
|
+
}
|
|
535
|
+
render() {
|
|
536
|
+
return this.state.failed ? this.props.fallback : this.props.children;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function useStableMarkdownSyntaxEntries(plugins) {
|
|
540
|
+
const identity = plugins?.syntaxIdentity ?? '';
|
|
541
|
+
const syntaxEntriesRef = useRef(undefined);
|
|
542
|
+
if (syntaxEntriesRef.current == null || syntaxEntriesRef.current.identity !== identity) {
|
|
543
|
+
syntaxEntriesRef.current = {
|
|
544
|
+
identity,
|
|
545
|
+
entries: plugins == null || identity === '' ? undefined : plugins.syntaxEntries
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
return syntaxEntriesRef.current.entries;
|
|
549
|
+
}
|
|
521
550
|
|
|
522
551
|
// ---------------------------------------------------------------------------
|
|
523
552
|
// Inline renderer
|
|
@@ -565,12 +594,12 @@ function getCitationNumber(ctx, sourceId) {
|
|
|
565
594
|
}
|
|
566
595
|
return num;
|
|
567
596
|
}
|
|
568
|
-
function renderInline(node, index, onLinkClick, cursor, citationCtx, linkComponent = 'a', inlinePlugins, components) {
|
|
597
|
+
function renderInline(node, index, onLinkClick, cursor, citationCtx, linkComponent = 'a', inlinePlugins, components, preparedPlugins) {
|
|
569
598
|
switch (node.type) {
|
|
570
599
|
case 'text':
|
|
571
600
|
{
|
|
572
601
|
if (inlinePlugins && inlinePlugins.length > 0) {
|
|
573
|
-
const segments = applyInlinePlugins(node.
|
|
602
|
+
const segments = applyInlinePlugins(node.value, inlinePlugins);
|
|
574
603
|
// If no plugin matched, fall through to the normal path
|
|
575
604
|
// O(1) guard: applyInlinePlugins returns a single text segment when
|
|
576
605
|
// nothing matched — skip the plugin path entirely in that case.
|
|
@@ -593,52 +622,64 @@ function renderInline(node, index, onLinkClick, cursor, citationCtx, linkCompone
|
|
|
593
622
|
}, index);
|
|
594
623
|
}
|
|
595
624
|
}
|
|
596
|
-
return wrapTextWithFade(node.
|
|
625
|
+
return wrapTextWithFade(node.value, cursor, index);
|
|
597
626
|
}
|
|
598
|
-
case '
|
|
627
|
+
case 'strong':
|
|
599
628
|
return /*#__PURE__*/_jsx("strong", {
|
|
600
629
|
...{
|
|
601
630
|
className: "x2mo6ok"
|
|
602
631
|
},
|
|
603
|
-
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components))
|
|
632
|
+
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins))
|
|
604
633
|
}, index);
|
|
605
|
-
case '
|
|
634
|
+
case 'emphasis':
|
|
606
635
|
return /*#__PURE__*/_jsx("em", {
|
|
607
|
-
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components))
|
|
636
|
+
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins))
|
|
608
637
|
}, index);
|
|
609
|
-
case '
|
|
638
|
+
case 'delete':
|
|
610
639
|
return /*#__PURE__*/_jsx("del", {
|
|
611
640
|
...{
|
|
612
641
|
className: "xv1l7n4"
|
|
613
642
|
},
|
|
614
|
-
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components))
|
|
643
|
+
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins))
|
|
615
644
|
}, index);
|
|
616
|
-
case '
|
|
645
|
+
case 'inlineCode':
|
|
617
646
|
{
|
|
618
647
|
// Track code content length for cursor but don't split inside code
|
|
619
|
-
cursor.offset += node.
|
|
648
|
+
cursor.offset += node.value.length;
|
|
620
649
|
const InlineCodeComp = components?.inlineCode;
|
|
621
650
|
const codeEl = InlineCodeComp ? /*#__PURE__*/_jsx(InlineCodeComp, {
|
|
622
|
-
children: node.
|
|
651
|
+
children: node.value
|
|
623
652
|
}, index) : /*#__PURE__*/_jsx(Code, {
|
|
624
|
-
children: node.
|
|
653
|
+
children: node.value
|
|
625
654
|
}, index);
|
|
626
655
|
return codeEl;
|
|
627
656
|
}
|
|
657
|
+
case 'inlineMath':
|
|
658
|
+
{
|
|
659
|
+
const MathComp = components?.math;
|
|
660
|
+
if (MathComp == null) {
|
|
661
|
+
return wrapTextWithFade(`$${node.value}$`, cursor, index);
|
|
662
|
+
}
|
|
663
|
+
cursor.offset += node.value.length;
|
|
664
|
+
return /*#__PURE__*/_jsx(MathComp, {
|
|
665
|
+
value: node.value,
|
|
666
|
+
display: "inline"
|
|
667
|
+
}, index);
|
|
668
|
+
}
|
|
628
669
|
case 'link':
|
|
629
670
|
{
|
|
630
|
-
const safeHref =
|
|
671
|
+
const safeHref = sanitizeMarkdownUrl(node.url);
|
|
631
672
|
if (safeHref == null) {
|
|
632
673
|
// Unsafe URL — render as plain text
|
|
633
674
|
return /*#__PURE__*/_jsx("span", {
|
|
634
|
-
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components))
|
|
675
|
+
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins))
|
|
635
676
|
}, index);
|
|
636
677
|
}
|
|
637
678
|
const LinkComp = components?.link;
|
|
638
679
|
if (LinkComp) {
|
|
639
680
|
return /*#__PURE__*/_jsx(LinkComp, {
|
|
640
681
|
href: safeHref,
|
|
641
|
-
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components))
|
|
682
|
+
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins))
|
|
642
683
|
}, index);
|
|
643
684
|
}
|
|
644
685
|
const isExternal = safeHref.startsWith('https://') || safeHref.startsWith('http://');
|
|
@@ -662,12 +703,12 @@ function renderInline(node, index, onLinkClick, cursor, citationCtx, linkCompone
|
|
|
662
703
|
...{
|
|
663
704
|
className: "xjse4m1 x1bvjpef"
|
|
664
705
|
},
|
|
665
|
-
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components))
|
|
706
|
+
children: node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins))
|
|
666
707
|
}, index);
|
|
667
708
|
}
|
|
668
709
|
case 'image':
|
|
669
710
|
{
|
|
670
|
-
const safeSrc =
|
|
711
|
+
const safeSrc = sanitizeMarkdownUrl(node.url);
|
|
671
712
|
if (safeSrc == null) {
|
|
672
713
|
return /*#__PURE__*/_jsxs("span", {
|
|
673
714
|
children: ["[", node.alt, "]"]
|
|
@@ -688,6 +729,33 @@ function renderInline(node, index, onLinkClick, cursor, citationCtx, linkCompone
|
|
|
688
729
|
}
|
|
689
730
|
}, index);
|
|
690
731
|
}
|
|
732
|
+
case 'extension':
|
|
733
|
+
{
|
|
734
|
+
const renderer = getMarkdownExtensionRenderer(preparedPlugins, node);
|
|
735
|
+
const fallback = wrapTextWithFade(node.source ?? markdownExtensionText(preparedPlugins, node), cursor, index);
|
|
736
|
+
if (renderer == null) {
|
|
737
|
+
return fallback;
|
|
738
|
+
}
|
|
739
|
+
let rendered;
|
|
740
|
+
try {
|
|
741
|
+
rendered = renderer.render({
|
|
742
|
+
node
|
|
743
|
+
});
|
|
744
|
+
} catch (error) {
|
|
745
|
+
reportMarkdownPluginFailure(node.plugin, 'render', error);
|
|
746
|
+
return fallback;
|
|
747
|
+
}
|
|
748
|
+
return /*#__PURE__*/_jsx(MarkdownPluginBoundary, {
|
|
749
|
+
pluginName: node.plugin,
|
|
750
|
+
resetKey: node,
|
|
751
|
+
resetRenderer: renderer.render,
|
|
752
|
+
fallback: fallback,
|
|
753
|
+
children: /*#__PURE__*/_jsx(Suspense, {
|
|
754
|
+
fallback: fallback,
|
|
755
|
+
children: rendered
|
|
756
|
+
})
|
|
757
|
+
}, index);
|
|
758
|
+
}
|
|
691
759
|
case 'break':
|
|
692
760
|
cursor.offset += 1;
|
|
693
761
|
return /*#__PURE__*/_jsx("br", {}, index);
|
|
@@ -728,10 +796,11 @@ function getElementSpacing(node, density) {
|
|
|
728
796
|
const compact = density === 'compact';
|
|
729
797
|
switch (node.type) {
|
|
730
798
|
case 'heading':
|
|
731
|
-
return node.
|
|
799
|
+
return node.depth <= 3 ? compact ? styles.spacingHeadingMajorCompact : styles.spacingHeadingMajorDefault : compact ? styles.spacingHeadingMinorCompact : styles.spacingHeadingMinorDefault;
|
|
732
800
|
case 'paragraph':
|
|
733
801
|
return compact ? styles.spacingParagraphCompact : styles.spacingParagraphDefault;
|
|
734
|
-
case '
|
|
802
|
+
case 'code':
|
|
803
|
+
case 'math':
|
|
735
804
|
return compact ? styles.spacingCodeblockCompact : styles.spacingCodeblockDefault;
|
|
736
805
|
case 'blockquote':
|
|
737
806
|
return compact ? styles.spacingBlockquoteCompact : styles.spacingBlockquoteDefault;
|
|
@@ -739,7 +808,9 @@ function getElementSpacing(node, density) {
|
|
|
739
808
|
return compact ? styles.spacingListCompact : styles.spacingListDefault;
|
|
740
809
|
case 'table':
|
|
741
810
|
return compact ? styles.spacingTableCompact : styles.spacingTableDefault;
|
|
742
|
-
case '
|
|
811
|
+
case 'extension':
|
|
812
|
+
return compact ? styles.spacingParagraphCompact : styles.spacingParagraphDefault;
|
|
813
|
+
case 'thematicBreak':
|
|
743
814
|
return compact ? styles.spacingHrCompact : styles.spacingHrDefault;
|
|
744
815
|
case 'image':
|
|
745
816
|
return compact ? styles.spacingImageCompact : styles.spacingImageDefault;
|
|
@@ -755,11 +826,16 @@ function getElementSpacing(node, density) {
|
|
|
755
826
|
* Buckets: ≤6 chars → 60px, 7–15 → 80px, >15 → 120px.
|
|
756
827
|
*/
|
|
757
828
|
function computeTableColumnMinWidths(node) {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
829
|
+
const [header, ...rows] = node.children;
|
|
830
|
+
if (header == null) {
|
|
831
|
+
return [];
|
|
832
|
+
}
|
|
833
|
+
return header.children.map((cell, colIdx) => {
|
|
834
|
+
let maxLen = countInlineTextLength(cell.children);
|
|
835
|
+
for (const row of rows) {
|
|
836
|
+
const rowCell = row.children[colIdx];
|
|
837
|
+
if (rowCell != null) {
|
|
838
|
+
const len = countInlineTextLength(rowCell.children);
|
|
763
839
|
if (len > maxLen) {
|
|
764
840
|
maxLen = len;
|
|
765
841
|
}
|
|
@@ -768,7 +844,7 @@ function computeTableColumnMinWidths(node) {
|
|
|
768
844
|
return maxLen <= 6 ? 60 : maxLen <= 15 ? 80 : 120;
|
|
769
845
|
});
|
|
770
846
|
}
|
|
771
|
-
function renderBlock(node, index, blockCount, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidthValue, contentAlign, linkComponent = 'a', inlinePlugins, components, t, headingIdMap) {
|
|
847
|
+
function renderBlock(node, index, blockCount, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidthValue, contentAlign, linkComponent = 'a', inlinePlugins, components, preparedPlugins, t, headingIdMap) {
|
|
772
848
|
const blockAlignMargin = BLOCK_ALIGN_MARGIN[contentAlign];
|
|
773
849
|
const blockAlignStyle = blockAlignMargin != null ? dynamicStyles.blockAlign(blockAlignMargin) : null;
|
|
774
850
|
const spacing = getElementSpacing(node, density);
|
|
@@ -777,8 +853,8 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
777
853
|
switch (node.type) {
|
|
778
854
|
case 'heading':
|
|
779
855
|
{
|
|
780
|
-
const level = Math.min(node.
|
|
781
|
-
const headingChildren = node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components));
|
|
856
|
+
const level = Math.min(node.depth + headingLevelStart - 1, 6);
|
|
857
|
+
const headingChildren = node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins));
|
|
782
858
|
// Only top-level headings get an id: the map is built from the same
|
|
783
859
|
// traversal parseOutlineFromMarkdown uses (which skips headings nested
|
|
784
860
|
// in blockquotes / list items), so rendered ids and outline ids stay
|
|
@@ -804,7 +880,7 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
804
880
|
}
|
|
805
881
|
case 'paragraph':
|
|
806
882
|
{
|
|
807
|
-
const paraChildren = node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components));
|
|
883
|
+
const paraChildren = node.children.map((c, i) => renderInline(c, i, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins));
|
|
808
884
|
const ParagraphComp = components?.paragraph;
|
|
809
885
|
if (ParagraphComp) {
|
|
810
886
|
return /*#__PURE__*/_jsx(ParagraphComp, {
|
|
@@ -828,34 +904,79 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
828
904
|
children: paraChildren
|
|
829
905
|
}, index);
|
|
830
906
|
}
|
|
831
|
-
case '
|
|
907
|
+
case 'code':
|
|
832
908
|
{
|
|
833
909
|
// Track codeblock content in cursor for accurate character counting
|
|
834
|
-
cursor.offset += node.
|
|
910
|
+
cursor.offset += node.value.length;
|
|
911
|
+
const language = getMarkdownAstLegacyCodeLanguage(node) ?? 'plaintext';
|
|
835
912
|
const CodeBlockComp = components?.code;
|
|
836
913
|
if (CodeBlockComp) {
|
|
837
914
|
return /*#__PURE__*/_jsx(CodeBlockComp, {
|
|
838
|
-
code: node.
|
|
839
|
-
language:
|
|
915
|
+
code: node.value,
|
|
916
|
+
language: language
|
|
840
917
|
}, index);
|
|
841
918
|
}
|
|
842
|
-
|
|
919
|
+
const fallback = /*#__PURE__*/_jsx("div", {
|
|
843
920
|
...mergeProps(themeProps('markdown-codeblock', {
|
|
844
921
|
density
|
|
845
922
|
}), stylex.props(spacing, styles.codeBlockWrapper, isFirst && styles.noMarginBlockStart, isLast && styles.noMarginBlockEnd)),
|
|
846
923
|
children: /*#__PURE__*/_jsx(CodeBlock, {
|
|
847
|
-
code: node.
|
|
848
|
-
language:
|
|
924
|
+
code: node.value,
|
|
925
|
+
language: language,
|
|
849
926
|
isCollapsible: true,
|
|
850
927
|
xstyle: [contentWidthValue != null ? dynamicStyles.blockWidth(contentWidthValue) : undefined, blockAlignStyle]
|
|
851
928
|
})
|
|
852
929
|
}, index);
|
|
930
|
+
const proposal = getMarkdownFenceProposal(node);
|
|
931
|
+
if (proposal == null) {
|
|
932
|
+
return fallback;
|
|
933
|
+
}
|
|
934
|
+
const renderer = getMarkdownExtensionRenderer(preparedPlugins, proposal.node);
|
|
935
|
+
if (renderer == null) {
|
|
936
|
+
return fallback;
|
|
937
|
+
}
|
|
938
|
+
try {
|
|
939
|
+
const rendered = renderer.render({
|
|
940
|
+
node: proposal.node
|
|
941
|
+
});
|
|
942
|
+
if (rendered == null || typeof rendered === 'boolean') {
|
|
943
|
+
return fallback;
|
|
944
|
+
}
|
|
945
|
+
return /*#__PURE__*/_jsx(MarkdownPluginBoundary, {
|
|
946
|
+
pluginName: proposal.node.plugin,
|
|
947
|
+
resetKey: proposal.node,
|
|
948
|
+
resetRenderer: renderer.render,
|
|
949
|
+
fallback: fallback,
|
|
950
|
+
children: /*#__PURE__*/_jsx(Suspense, {
|
|
951
|
+
fallback: fallback,
|
|
952
|
+
children: rendered
|
|
953
|
+
})
|
|
954
|
+
}, index);
|
|
955
|
+
} catch (error) {
|
|
956
|
+
reportMarkdownPluginFailure(proposal.node.plugin, 'render', error);
|
|
957
|
+
return fallback;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
case 'math':
|
|
961
|
+
{
|
|
962
|
+
cursor.offset += node.value.length;
|
|
963
|
+
const MathComp = components?.math;
|
|
964
|
+
if (MathComp == null) {
|
|
965
|
+
return /*#__PURE__*/_jsx("div", {
|
|
966
|
+
role: "paragraph",
|
|
967
|
+
children: `$$${node.value}$$`
|
|
968
|
+
}, index);
|
|
969
|
+
}
|
|
970
|
+
return /*#__PURE__*/_jsx(MathComp, {
|
|
971
|
+
value: node.value,
|
|
972
|
+
display: "block"
|
|
973
|
+
}, index);
|
|
853
974
|
}
|
|
854
975
|
case 'blockquote':
|
|
855
976
|
{
|
|
856
977
|
const BlockquoteComp = components?.blockquote;
|
|
857
978
|
if (BlockquoteComp) {
|
|
858
|
-
const bqC = node.children.map((c, i) => renderBlock(c, i, node.children.length, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidthValue, contentAlign, linkComponent, inlinePlugins, components, t));
|
|
979
|
+
const bqC = node.children.map((c, i) => renderBlock(c, i, node.children.length, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidthValue, contentAlign, linkComponent, inlinePlugins, components, preparedPlugins, t));
|
|
859
980
|
return /*#__PURE__*/_jsx(BlockquoteComp, {
|
|
860
981
|
children: bqC
|
|
861
982
|
}, index);
|
|
@@ -865,16 +986,16 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
865
986
|
density
|
|
866
987
|
}),
|
|
867
988
|
xstyle: [spacing, contentWidthValue != null ? dynamicStyles.proseWidth(contentWidthValue) : undefined, contentAlign !== 'start' ? dynamicStyles.proseAlign(ALIGN_MARGIN[contentAlign]) : undefined, isFirst && styles.noMarginBlockStart, isLast && styles.noMarginBlockEnd],
|
|
868
|
-
children: node.children.map((c, i) => renderBlock(c, i, node.children.length, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidthValue, contentAlign, linkComponent, inlinePlugins, components, t))
|
|
989
|
+
children: node.children.map((c, i) => renderBlock(c, i, node.children.length, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidthValue, contentAlign, linkComponent, inlinePlugins, components, preparedPlugins, t))
|
|
869
990
|
}, index);
|
|
870
991
|
}
|
|
871
992
|
case 'list':
|
|
872
993
|
{
|
|
873
994
|
// Detect task lists: all items have a checked state
|
|
874
|
-
const isTaskList = node.
|
|
995
|
+
const isTaskList = node.children.length > 0 && node.children.every(item => item.checked != null);
|
|
875
996
|
if (isTaskList) {
|
|
876
997
|
// Extract labels from task items — render as rich inline content
|
|
877
|
-
const checkedValues = node.
|
|
998
|
+
const checkedValues = node.children.map((item, i) => ({
|
|
878
999
|
item,
|
|
879
1000
|
key: `task-${i}`
|
|
880
1001
|
})).filter(({
|
|
@@ -893,13 +1014,13 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
893
1014
|
xstyle: styles.blockIndent,
|
|
894
1015
|
isReadOnly: true,
|
|
895
1016
|
density: "compact",
|
|
896
|
-
children: node.
|
|
1017
|
+
children: node.children.map((item, i) => {
|
|
897
1018
|
const firstChild = item.children[0];
|
|
898
1019
|
const isInline = item.children.length === 1 && firstChild?.type === 'paragraph';
|
|
899
1020
|
const label = isInline ? /*#__PURE__*/_jsx(_Fragment, {
|
|
900
|
-
children: firstChild.children.map((c, j) => renderInline(c, j, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components))
|
|
1021
|
+
children: firstChild.children.map((c, j) => renderInline(c, j, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins))
|
|
901
1022
|
}) : /*#__PURE__*/_jsx(_Fragment, {
|
|
902
|
-
children: item.children.map((c, j) => renderBlock(c, j, item.children.length, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidthValue, contentAlign, linkComponent, inlinePlugins, components, t))
|
|
1023
|
+
children: item.children.map((c, j) => renderBlock(c, j, item.children.length, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidthValue, contentAlign, linkComponent, inlinePlugins, components, preparedPlugins, t))
|
|
903
1024
|
});
|
|
904
1025
|
return /*#__PURE__*/_jsx(CheckboxListItem
|
|
905
1026
|
// eslint-disable-next-line @eslint-react/no-array-index-key -- markdown task items are rendered from positional AST nodes
|
|
@@ -920,7 +1041,7 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
920
1041
|
density: "compact",
|
|
921
1042
|
start: node.ordered ? node.start : undefined,
|
|
922
1043
|
xstyle: styles.blockIndent,
|
|
923
|
-
children: node.
|
|
1044
|
+
children: node.children.map((item, i) => {
|
|
924
1045
|
const firstChild = item.children[0];
|
|
925
1046
|
const isInline = item.children.length === 1 && firstChild?.type === 'paragraph';
|
|
926
1047
|
|
|
@@ -928,9 +1049,9 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
928
1049
|
// whole item as a block instead of fading individual text spans.
|
|
929
1050
|
|
|
930
1051
|
const label = isInline ? /*#__PURE__*/_jsx(_Fragment, {
|
|
931
|
-
children: firstChild.children.map((c, j) => renderInline(c, j, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components))
|
|
1052
|
+
children: firstChild.children.map((c, j) => renderInline(c, j, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins))
|
|
932
1053
|
}) : /*#__PURE__*/_jsx(_Fragment, {
|
|
933
|
-
children: item.children.map((c, j) => renderBlock(c, j, item.children.length, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidthValue, contentAlign, linkComponent, inlinePlugins, components, t))
|
|
1054
|
+
children: item.children.map((c, j) => renderBlock(c, j, item.children.length, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidthValue, contentAlign, linkComponent, inlinePlugins, components, preparedPlugins, t))
|
|
934
1055
|
});
|
|
935
1056
|
return /*#__PURE__*/_jsx(ListItem
|
|
936
1057
|
// eslint-disable-next-line @eslint-react/no-array-index-key -- markdown list items are rendered from positional AST nodes
|
|
@@ -944,6 +1065,7 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
944
1065
|
case 'table':
|
|
945
1066
|
{
|
|
946
1067
|
const colMinWidths = computeTableColumnMinWidths(node);
|
|
1068
|
+
const [header, ...rows] = node.children;
|
|
947
1069
|
return /*#__PURE__*/_jsx("div", {
|
|
948
1070
|
// Keyboard-focusable so keyboard users can scroll a horizontally
|
|
949
1071
|
// overflowing GFM table. Uses role="group" (not "region") so
|
|
@@ -960,20 +1082,20 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
960
1082
|
textOverflow: "wrap",
|
|
961
1083
|
children: [/*#__PURE__*/_jsx(TableHeader, {
|
|
962
1084
|
children: /*#__PURE__*/_jsx(TableRow, {
|
|
963
|
-
children:
|
|
1085
|
+
children: header?.children.map((h, i) => /*#__PURE__*/_jsx(TableHeaderCell
|
|
964
1086
|
// eslint-disable-next-line @eslint-react/no-array-index-key -- markdown table columns are positional by definition
|
|
965
1087
|
, {
|
|
966
|
-
xstyle: [dynamicStyles.cellMinWidth(`${colMinWidths[i]}px`), node.
|
|
967
|
-
children: h.children.map((c, j) => renderInline(c, j, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components))
|
|
1088
|
+
xstyle: [dynamicStyles.cellMinWidth(`${colMinWidths[i]}px`), node.align[i] === 'center' && cellAlignStyles.center, node.align[i] === 'right' && cellAlignStyles.end],
|
|
1089
|
+
children: h.children.map((c, j) => renderInline(c, j, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins))
|
|
968
1090
|
}, i))
|
|
969
1091
|
})
|
|
970
1092
|
}), /*#__PURE__*/_jsx(TableBody, {
|
|
971
|
-
children:
|
|
972
|
-
const cells = row.map((cell, j) => /*#__PURE__*/_jsx(TableCell
|
|
1093
|
+
children: rows.map((row, i) => {
|
|
1094
|
+
const cells = row.children.map((cell, j) => /*#__PURE__*/_jsx(TableCell
|
|
973
1095
|
// eslint-disable-next-line @eslint-react/no-array-index-key -- markdown table cells are positional by row and column
|
|
974
1096
|
, {
|
|
975
|
-
xstyle: [node.
|
|
976
|
-
children: cell.children.map((c, k) => renderInline(c, k, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components))
|
|
1097
|
+
xstyle: [node.align[j] === 'center' && cellAlignStyles.center, node.align[j] === 'right' && cellAlignStyles.end],
|
|
1098
|
+
children: cell.children.map((c, k) => renderInline(c, k, onLinkClick, cursor, citationCtx, linkComponent, inlinePlugins, components, preparedPlugins))
|
|
977
1099
|
}, j));
|
|
978
1100
|
return /*#__PURE__*/_jsx(TableRow
|
|
979
1101
|
// eslint-disable-next-line @eslint-react/no-array-index-key -- markdown table rows are rendered from positional AST nodes
|
|
@@ -985,7 +1107,37 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
985
1107
|
})
|
|
986
1108
|
}, index);
|
|
987
1109
|
}
|
|
988
|
-
case '
|
|
1110
|
+
case 'extension':
|
|
1111
|
+
{
|
|
1112
|
+
const renderer = getMarkdownExtensionRenderer(preparedPlugins, node);
|
|
1113
|
+
const fallbackText = node.source ?? markdownExtensionText(preparedPlugins, node);
|
|
1114
|
+
const fallback = wrapTextWithFade(fallbackText, cursor, index);
|
|
1115
|
+
let content = fallback;
|
|
1116
|
+
if (renderer != null) {
|
|
1117
|
+
try {
|
|
1118
|
+
const rendered = renderer.render({
|
|
1119
|
+
node
|
|
1120
|
+
});
|
|
1121
|
+
content = /*#__PURE__*/_jsx(MarkdownPluginBoundary, {
|
|
1122
|
+
pluginName: node.plugin,
|
|
1123
|
+
resetKey: node,
|
|
1124
|
+
resetRenderer: renderer.render,
|
|
1125
|
+
fallback: fallback,
|
|
1126
|
+
children: /*#__PURE__*/_jsx(Suspense, {
|
|
1127
|
+
fallback: fallback,
|
|
1128
|
+
children: rendered
|
|
1129
|
+
})
|
|
1130
|
+
});
|
|
1131
|
+
} catch (error) {
|
|
1132
|
+
reportMarkdownPluginFailure(node.plugin, 'render', error);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
return /*#__PURE__*/_jsx("div", {
|
|
1136
|
+
...stylex.props(spacing, contentWidthValue != null ? dynamicStyles.proseWidth(contentWidthValue) : null, contentAlign !== 'start' ? dynamicStyles.proseAlign(ALIGN_MARGIN[contentAlign]) : null, isFirst && styles.noMarginBlockStart, isLast && styles.noMarginBlockEnd),
|
|
1137
|
+
children: content
|
|
1138
|
+
}, index);
|
|
1139
|
+
}
|
|
1140
|
+
case 'thematicBreak':
|
|
989
1141
|
{
|
|
990
1142
|
const HrComp = components?.hr;
|
|
991
1143
|
if (HrComp) {
|
|
@@ -999,7 +1151,7 @@ function renderBlock(node, index, blockCount, density, headingLevelStart, onLink
|
|
|
999
1151
|
}
|
|
1000
1152
|
case 'image':
|
|
1001
1153
|
{
|
|
1002
|
-
const safeSrc =
|
|
1154
|
+
const safeSrc = sanitizeMarkdownUrl(node.url);
|
|
1003
1155
|
if (safeSrc == null) {
|
|
1004
1156
|
return /*#__PURE__*/_jsxs("div", {
|
|
1005
1157
|
...mergeProps(themeProps('markdown-image', {
|
|
@@ -1059,6 +1211,7 @@ export function Markdown({
|
|
|
1059
1211
|
contentWidth = 680,
|
|
1060
1212
|
contentAlign = 'start',
|
|
1061
1213
|
components,
|
|
1214
|
+
plugins,
|
|
1062
1215
|
inlinePlugins,
|
|
1063
1216
|
autolink,
|
|
1064
1217
|
xstyle,
|
|
@@ -1071,23 +1224,45 @@ export function Markdown({
|
|
|
1071
1224
|
const LinkComponent = useLinkComponent();
|
|
1072
1225
|
// Derive the set of source IDs for the parser (stable across renders when sources don't change)
|
|
1073
1226
|
const sourceIds = useMemo(() => sources ? new Set(Object.keys(sources)) : undefined, [sources]);
|
|
1074
|
-
const
|
|
1227
|
+
const preparedPlugins = useMemo(() => {
|
|
1228
|
+
if (plugins == null) {
|
|
1229
|
+
return undefined;
|
|
1230
|
+
}
|
|
1231
|
+
try {
|
|
1232
|
+
return prepareMarkdownPlugins(plugins);
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
reportMarkdownPluginFailure('configuration', 'transform', error);
|
|
1235
|
+
return undefined;
|
|
1236
|
+
}
|
|
1237
|
+
}, [plugins]);
|
|
1238
|
+
const syntaxPlugins = useStableMarkdownSyntaxEntries(preparedPlugins);
|
|
1239
|
+
const hasMathRenderer = components?.math != null;
|
|
1240
|
+
const legacyParseOptions = useMemo(() => ({
|
|
1075
1241
|
sourceIds,
|
|
1076
|
-
autolink
|
|
1077
|
-
|
|
1242
|
+
autolink,
|
|
1243
|
+
plugins: syntaxPlugins
|
|
1244
|
+
}), [sourceIds, autolink, syntaxPlugins]);
|
|
1245
|
+
const mathParseOptions = useMemo(() => ({
|
|
1246
|
+
sourceIds,
|
|
1247
|
+
autolink,
|
|
1248
|
+
math: true,
|
|
1249
|
+
plugins: syntaxPlugins
|
|
1250
|
+
}), [sourceIds, autolink, syntaxPlugins]);
|
|
1078
1251
|
|
|
1079
1252
|
// Smooth bursty streamed chunks into a steady character-by-character reveal.
|
|
1080
1253
|
// When not streaming, the hook returns children unchanged (no-op).
|
|
1081
1254
|
const smoothedText = useStreamingText(children, isStreaming);
|
|
1082
1255
|
const incrementalStateRef = useRef(createIncrementalState());
|
|
1083
|
-
// Reset incremental cache when
|
|
1084
|
-
// settled blocks were parsed with the previous setting.
|
|
1256
|
+
// Reset incremental cache when parser-affecting component options toggle —
|
|
1257
|
+
// cached settled blocks were parsed with the previous setting.
|
|
1085
1258
|
const prevAutolinkRef = useRef(autolink);
|
|
1086
|
-
|
|
1259
|
+
const prevMathRef = useRef(hasMathRenderer);
|
|
1260
|
+
if (prevAutolinkRef.current !== autolink || prevMathRef.current !== hasMathRenderer) {
|
|
1087
1261
|
incrementalStateRef.current = createIncrementalState();
|
|
1088
1262
|
prevAutolinkRef.current = autolink;
|
|
1263
|
+
prevMathRef.current = hasMathRenderer;
|
|
1089
1264
|
}
|
|
1090
|
-
const
|
|
1265
|
+
const parsedBlocks = useMemo(() => {
|
|
1091
1266
|
if (display === 'inline') {
|
|
1092
1267
|
return [];
|
|
1093
1268
|
}
|
|
@@ -1096,11 +1271,17 @@ export function Markdown({
|
|
|
1096
1271
|
incrementalStateRef.current = createIncrementalState();
|
|
1097
1272
|
return [];
|
|
1098
1273
|
}
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1274
|
+
const options = hasMathRenderer ? mathParseOptions : legacyParseOptions;
|
|
1275
|
+
const input = trimStreamingArtifacts(smoothedText, options);
|
|
1276
|
+
return (hasMathRenderer ? parseMarkdownAstIncremental(input, incrementalStateRef.current, mathParseOptions) : parseMarkdownAstIncremental(input, incrementalStateRef.current, legacyParseOptions)).children;
|
|
1101
1277
|
}
|
|
1102
|
-
return
|
|
1103
|
-
}, [display, smoothedText, children, isStreaming,
|
|
1278
|
+
return (hasMathRenderer ? parseMarkdownAst(children, mathParseOptions) : parseMarkdownAst(children, legacyParseOptions)).children;
|
|
1279
|
+
}, [display, smoothedText, children, isStreaming, hasMathRenderer, mathParseOptions, legacyParseOptions]);
|
|
1280
|
+
const transformSource = isStreaming ? trimStreamingArtifacts(smoothedText, hasMathRenderer ? mathParseOptions : legacyParseOptions) : children;
|
|
1281
|
+
const blocks = useMemo(() => applyMarkdownTransforms({
|
|
1282
|
+
type: 'root',
|
|
1283
|
+
children: parsedBlocks
|
|
1284
|
+
}, preparedPlugins, transformSource, !isStreaming, 'block').children, [parsedBlocks, preparedPlugins, transformSource, isStreaming]);
|
|
1104
1285
|
|
|
1105
1286
|
// Assign each top-level heading the slug that parseOutlineFromMarkdown
|
|
1106
1287
|
// would derive for it, so Outline hash links built from the same source
|
|
@@ -1116,19 +1297,35 @@ export function Markdown({
|
|
|
1116
1297
|
const counts = new Map();
|
|
1117
1298
|
for (const block of blocks) {
|
|
1118
1299
|
if (block.type === 'heading') {
|
|
1119
|
-
const label =
|
|
1300
|
+
const label = markdownAstText(block.children, node => markdownExtensionText(preparedPlugins, node)).trim();
|
|
1120
1301
|
map.set(block, uniqueSlug(slugify(label), counts));
|
|
1121
1302
|
}
|
|
1122
1303
|
}
|
|
1123
1304
|
return map;
|
|
1124
|
-
}, [display, blocks]);
|
|
1305
|
+
}, [display, blocks, preparedPlugins]);
|
|
1306
|
+
const parsedInlineNodes = useMemo(() => {
|
|
1307
|
+
if (display !== 'inline') {
|
|
1308
|
+
return [];
|
|
1309
|
+
}
|
|
1310
|
+
const options = hasMathRenderer ? mathParseOptions : legacyParseOptions;
|
|
1311
|
+
const input = isStreaming ? trimStreamingArtifacts(smoothedText, options) : children;
|
|
1312
|
+
return hasMathRenderer ? parseInlineAst(input, mathParseOptions) : parseInlineAst(input, legacyParseOptions);
|
|
1313
|
+
}, [display, smoothedText, children, isStreaming, hasMathRenderer, mathParseOptions, legacyParseOptions]);
|
|
1314
|
+
const inlineTransformSource = isStreaming ? trimStreamingArtifacts(smoothedText, hasMathRenderer ? mathParseOptions : legacyParseOptions) : children;
|
|
1125
1315
|
const inlineNodes = useMemo(() => {
|
|
1126
1316
|
if (display !== 'inline') {
|
|
1127
1317
|
return [];
|
|
1128
1318
|
}
|
|
1129
|
-
const
|
|
1130
|
-
|
|
1131
|
-
|
|
1319
|
+
const transformed = applyMarkdownTransforms({
|
|
1320
|
+
type: 'root',
|
|
1321
|
+
children: [{
|
|
1322
|
+
type: 'paragraph',
|
|
1323
|
+
children: parsedInlineNodes
|
|
1324
|
+
}]
|
|
1325
|
+
}, preparedPlugins, inlineTransformSource, !isStreaming, 'inline');
|
|
1326
|
+
const paragraph = transformed.children[0];
|
|
1327
|
+
return paragraph?.type === 'paragraph' ? [...paragraph.children] : parsedInlineNodes;
|
|
1328
|
+
}, [display, parsedInlineNodes, preparedPlugins, inlineTransformSource, isStreaming]);
|
|
1132
1329
|
|
|
1133
1330
|
// Track recent boundaries for stacked fade-in animation.
|
|
1134
1331
|
// The number of spans needed = ceil(animationDuration / tickInterval).
|
|
@@ -1180,7 +1377,7 @@ export function Markdown({
|
|
|
1180
1377
|
...mergeProps(themeProps('markdown', {
|
|
1181
1378
|
density
|
|
1182
1379
|
}), stylex.props(styles.root, styles.inlineRoot, xstyle), className, style),
|
|
1183
|
-
children: inlineNodes.map((node, i) => renderInline(node, i, onLinkClick, cursor, citationCtx, LinkComponent, inlinePlugins, components))
|
|
1380
|
+
children: inlineNodes.map((node, i) => renderInline(node, i, onLinkClick, cursor, citationCtx, LinkComponent, inlinePlugins, components, preparedPlugins))
|
|
1184
1381
|
});
|
|
1185
1382
|
|
|
1186
1383
|
// Store current inline nodes for next render's boundary calculation.
|
|
@@ -1199,7 +1396,7 @@ export function Markdown({
|
|
|
1199
1396
|
...mergeProps(themeProps('markdown', {
|
|
1200
1397
|
density
|
|
1201
1398
|
}), stylex.props(styles.root, xstyle), className, style),
|
|
1202
|
-
children: blocks.map((block, i) => renderBlock(block, i, blocks.length, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidth ? typeof contentWidth === 'number' ? `${contentWidth}px` : contentWidth : null, contentAlign, LinkComponent, inlinePlugins, components, t, headingIdMap))
|
|
1399
|
+
children: blocks.map((block, i) => renderBlock(block, i, blocks.length, density, headingLevelStart, onLinkClick, cursor, citationCtx, contentWidth ? typeof contentWidth === 'number' ? `${contentWidth}px` : contentWidth : null, contentAlign, LinkComponent, inlinePlugins, components, preparedPlugins, t, headingIdMap))
|
|
1203
1400
|
});
|
|
1204
1401
|
|
|
1205
1402
|
// Store current blocks for next render's boundary calculation.
|