@astryxdesign/core 0.6.2 → 0.6.3-canary.0949cb1
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 +81 -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/ChatComposer.d.ts.map +1 -1
- package/dist/Chat/ChatComposer.js +10 -5
- package/dist/Chat/ChatComposerDrawer.d.ts +19 -5
- package/dist/Chat/ChatComposerDrawer.d.ts.map +1 -1
- package/dist/Chat/ChatComposerDrawer.js +48 -23
- package/dist/Chat/ChatComposerInput.d.ts +11 -3
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +63 -16
- package/dist/Chat/ChatDictationButton.d.ts +3 -2
- package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
- package/dist/Chat/ChatDictationButton.js +6 -4
- package/dist/Chat/ChatSendButton.js +1 -1
- package/dist/Chat/index.d.ts +1 -1
- package/dist/Chat/index.d.ts.map +1 -1
- 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.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +4 -4
- 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 +8 -1
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +10 -1
- package/dist/Field/InputClearButton.d.ts +4 -2
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +4 -2
- 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 +7 -7
- 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 +8 -2
- 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 +262 -109
- 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 +126 -58
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +716 -167
- 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 +9 -7
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +7 -5
- 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 +28 -12
- package/dist/Spinner/Spinner.d.ts.map +1 -1
- package/dist/Spinner/Spinner.js +23 -7
- 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/Table/Table.d.ts +1 -0
- package/dist/Table/Table.d.ts.map +1 -1
- package/dist/Table/Table.js +29 -13
- 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 +9 -6
- package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
- package/dist/ToggleButton/ToggleButton.js +15 -27
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +3 -0
- package/dist/TreeList/TreeListItem.d.ts +6 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +12 -2
- package/dist/TreeList/TreeListTypes.d.ts +8 -1
- package/dist/TreeList/TreeListTypes.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +16 -7
- package/dist/astryx.css +18 -7
- 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/theme/generateThemeRules.d.ts.map +1 -1
- package/dist/theme/generateThemeRules.js +10 -0
- 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/af-ZA.json +530 -1
- package/locales/ar-SA.json +490 -1
- package/locales/ca-ES.json +514 -1
- package/locales/cs-CZ.json +498 -1
- package/locales/da-DK.json +522 -1
- package/locales/de-DE.json +522 -1
- package/locales/el-GR.json +498 -1
- package/locales/es-ES.json +510 -1
- package/locales/fi-FI.json +498 -1
- package/locales/fr-FR.json +534 -1
- package/locales/he-IL.json +498 -1
- package/locales/hu-HU.json +498 -1
- package/locales/it-IT.json +510 -1
- package/locales/ja-JP.json +490 -1
- package/locales/ko-KR.json +498 -1
- package/locales/nl-NL.json +534 -1
- package/locales/no-NO.json +510 -1
- package/locales/pl-PL.json +506 -1
- package/locales/pt-BR.json +506 -1
- package/locales/pt-PT.json +506 -1
- package/locales/ro-RO.json +506 -1
- package/locales/ru-RU.json +498 -1
- package/locales/sr-SP.json +498 -1
- package/locales/sv-SE.json +502 -1
- package/locales/tr-TR.json +498 -1
- package/locales/uk-UA.json +498 -1
- package/locales/vi-VN.json +502 -1
- package/locales/zh-CN.json +490 -1
- package/locales/zh-TW.json +490 -1
- package/package.json +19 -4
- 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/ChatComposer.doc.mjs +14 -0
- package/src/Chat/ChatComposer.spec.md +230 -0
- package/src/Chat/ChatComposer.test.tsx +57 -0
- package/src/Chat/ChatComposer.tsx +21 -9
- package/src/Chat/ChatComposerDrawer.doc.mjs +63 -5
- package/src/Chat/ChatComposerDrawer.spec.md +267 -0
- package/src/Chat/ChatComposerDrawer.test.tsx +81 -12
- package/src/Chat/ChatComposerDrawer.tsx +38 -9
- package/src/Chat/ChatComposerInput.doc.mjs +114 -26
- package/src/Chat/ChatComposerInput.spec.md +185 -0
- package/src/Chat/ChatComposerInput.test.tsx +113 -3
- package/src/Chat/ChatComposerInput.tsx +78 -13
- package/src/Chat/ChatComposerTokenElement.spec.md +152 -0
- package/src/Chat/ChatComposerTokenElement.test.tsx +85 -0
- package/src/Chat/ChatDictationButton.doc.mjs +3 -3
- package/src/Chat/ChatDictationButton.spec.md +160 -0
- package/src/Chat/ChatDictationButton.test.tsx +92 -27
- package/src/Chat/ChatDictationButton.tsx +8 -6
- package/src/Chat/ChatSendButton.test.tsx +1 -4
- package/src/Chat/ChatSendButton.tsx +1 -1
- package/src/Chat/index.ts +1 -0
- 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.tsx +6 -1
- package/src/DateTimeInput/DateTimeInput.tsx +6 -1
- package/src/DateTimeInput/TouchDateTimeField.tsx +6 -1
- package/src/DateTimeInput/nativePickerSegmentStyles.ts +24 -8
- package/src/Dialog/DialogHeader.doc.mjs +12 -0
- package/src/Dialog/DialogHeader.test.tsx +97 -0
- package/src/Dialog/DialogHeader.tsx +25 -3
- package/src/Dialog/modules/DialogHeader.spec.md +75 -38
- package/src/Field/InputClearButton.test.tsx +17 -3
- package/src/Field/InputClearButton.tsx +5 -2
- package/src/Field/PanelSearchInput.tsx +5 -2
- package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +1 -1
- package/src/FileInput/FileInput.tsx +11 -2
- 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/List/List.spec.md +200 -0
- package/src/Markdown/Markdown.doc.mjs +272 -0
- 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 +126 -3
- package/src/Markdown/Markdown.spec.md +197 -44
- package/src/Markdown/Markdown.test.tsx +11 -0
- package/src/Markdown/Markdown.tsx +414 -135
- package/src/Markdown/ast.test.ts +307 -0
- package/src/Markdown/ast.ts +337 -0
- package/src/Markdown/incremental.test.ts +12 -0
- package/src/Markdown/index.ts +37 -1
- package/src/Markdown/modules/remark.spec.md +269 -0
- package/src/Markdown/parser/index.ts +64 -0
- package/src/Markdown/parser.public.test.ts +103 -0
- package/src/Markdown/parser.ts +1180 -235
- 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 +13 -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.doc.mjs +2 -2
- package/src/Selector/Selector.spec.md +51 -49
- package/src/Selector/Selector.test.tsx +61 -55
- package/src/Selector/Selector.tsx +23 -14
- 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 +17 -3
- package/src/Slider/Slider.test.tsx +221 -0
- package/src/Slider/Slider.tsx +40 -9
- package/src/Spinner/Spinner.test.tsx +4 -0
- package/src/Spinner/Spinner.tsx +31 -18
- 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.spec.md +204 -0
- package/src/TabList/TabList.test.tsx +66 -0
- package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +1 -34
- package/src/Table/Table.doc.mjs +1 -1
- package/src/Table/Table.spec.md +54 -40
- package/src/Table/Table.test.tsx +83 -10
- package/src/Table/Table.tsx +30 -22
- package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +137 -0
- 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.doc.mjs +3 -3
- package/src/ToggleButton/ToggleButton.test.tsx +299 -4
- package/src/ToggleButton/ToggleButton.tsx +26 -33
- package/src/ToggleButton/ToggleButtonGroup.doc.mjs +3 -3
- package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +209 -0
- package/src/Tokenizer/Tokenizer.spec.md +142 -75
- package/src/TreeList/TreeList.doc.mjs +3 -3
- package/src/TreeList/TreeList.spec.md +123 -75
- package/src/TreeList/TreeList.test.tsx +29 -0
- package/src/TreeList/TreeList.tsx +3 -0
- package/src/TreeList/TreeListItem.tsx +15 -1
- package/src/TreeList/TreeListTypes.ts +11 -1
- 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/tailwind-theme.css +0 -6
- package/src/theme/generateThemeRules.test.ts +30 -0
- package/src/theme/generateThemeRules.ts +10 -0
- package/src/utils/interactionOverlay.stylex.ts +20 -0
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file sourceDecoration.ts
|
|
5
|
+
* @input Caller-supplied UTF-16 source ranges and non-semantic decoration payloads
|
|
6
|
+
* @output Immutable Markdown transforms that annotate Core-authored source-backed nodes
|
|
7
|
+
* @position Optional helper layer compiled onto the core transform protocol
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
MarkdownAstDataValue,
|
|
12
|
+
MarkdownAstNodeBase,
|
|
13
|
+
MarkdownAstPosition,
|
|
14
|
+
MarkdownAstRoot,
|
|
15
|
+
} from '../ast';
|
|
16
|
+
import {
|
|
17
|
+
isMarkdownPluginData,
|
|
18
|
+
markMarkdownTransformClaim,
|
|
19
|
+
markMarkdownTransformTrusted,
|
|
20
|
+
type MarkdownExtensionNode,
|
|
21
|
+
type MarkdownPluginData,
|
|
22
|
+
type MarkdownTransform,
|
|
23
|
+
} from './protocol';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Reserved node-data key and brand of the Core-owned decoration envelope.
|
|
27
|
+
* Decorations are only ever read from, and merged into, a value carrying all
|
|
28
|
+
* three: an arbitrary payload that happens to sit at this key is foreign data
|
|
29
|
+
* and is left untouched.
|
|
30
|
+
*/
|
|
31
|
+
const ENVELOPE_KEY = 'astryx:sourceDecorations';
|
|
32
|
+
const ENVELOPE_BRAND = 'astryx.markdown.sourceDecorations';
|
|
33
|
+
const ENVELOPE_VERSION = 1;
|
|
34
|
+
|
|
35
|
+
/** One decoration recorded on an annotated node. */
|
|
36
|
+
export interface MarkdownSourceDecoration {
|
|
37
|
+
/** Stable decoration name supplied by the helper's caller. */
|
|
38
|
+
readonly name: string;
|
|
39
|
+
/** Optional finite JSON-like payload copied from the matching range. */
|
|
40
|
+
readonly data?: MarkdownPluginData;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface MarkdownSourceDecorationRange {
|
|
44
|
+
/** Inclusive UTF-16 offset into the parsed source. */
|
|
45
|
+
readonly start: number;
|
|
46
|
+
/** Exclusive UTF-16 offset into the parsed source. */
|
|
47
|
+
readonly end: number;
|
|
48
|
+
/** Optional finite JSON-like payload recorded with this range. */
|
|
49
|
+
readonly data?: MarkdownPluginData;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface MarkdownSourceDecorationOptions {
|
|
53
|
+
/** Stable decoration name identifying every entry this helper records. */
|
|
54
|
+
readonly name: string;
|
|
55
|
+
/**
|
|
56
|
+
* Source ranges to decorate, as UTF-16 offsets into the same string the
|
|
57
|
+
* parser received. Order does not matter: entries are recorded in a fixed
|
|
58
|
+
* start, end, payload order so overlapping and duplicate ranges resolve the
|
|
59
|
+
* same way on every run. An empty list is a valid no-op.
|
|
60
|
+
*/
|
|
61
|
+
readonly ranges: ReadonlyArray<MarkdownSourceDecorationRange>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A decoration entry as stored: plain, frozen, JSON-like node data. */
|
|
65
|
+
type DecorationEntry = {readonly [key: string]: MarkdownAstDataValue};
|
|
66
|
+
|
|
67
|
+
interface NormalizedRange {
|
|
68
|
+
readonly start: number;
|
|
69
|
+
readonly end: number;
|
|
70
|
+
readonly entry: DecorationEntry;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Every AST node shares an optional `position` and `data`; decoration is node
|
|
75
|
+
* kind agnostic, so the walk below reads only those two fields plus children.
|
|
76
|
+
*/
|
|
77
|
+
type DecoratableNode = MarkdownAstNodeBase & {
|
|
78
|
+
readonly type: string;
|
|
79
|
+
readonly children?: ReadonlyArray<DecoratableNode>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
interface RunState {
|
|
83
|
+
readonly report: (message: string) => void;
|
|
84
|
+
hasReported: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const NO_DECORATIONS: ReadonlyArray<MarkdownSourceDecoration> = Object.freeze(
|
|
88
|
+
[],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
function isPlainObject(
|
|
92
|
+
value: MarkdownAstDataValue | undefined,
|
|
93
|
+
): value is {readonly [key: string]: MarkdownAstDataValue} {
|
|
94
|
+
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function cloneDecorationData<Data extends MarkdownPluginData>(
|
|
98
|
+
value: Data,
|
|
99
|
+
): Data {
|
|
100
|
+
if (value == null || typeof value !== 'object') {
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
if (Array.isArray(value)) {
|
|
104
|
+
return Object.freeze(value.map(cloneDecorationData)) as Data;
|
|
105
|
+
}
|
|
106
|
+
const clone: Record<string, MarkdownAstDataValue> = {};
|
|
107
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
108
|
+
clone[key] = cloneDecorationData(nested);
|
|
109
|
+
}
|
|
110
|
+
return Object.freeze(clone) as Data;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function fail(message: string): never {
|
|
114
|
+
throw new TypeError(`Markdown source decoration: ${message}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function normalizeRanges(
|
|
118
|
+
name: string,
|
|
119
|
+
ranges: ReadonlyArray<MarkdownSourceDecorationRange>,
|
|
120
|
+
): ReadonlyArray<NormalizedRange> {
|
|
121
|
+
const normalized: (NormalizedRange & {readonly key: string})[] = [];
|
|
122
|
+
const seen = new Set<string>();
|
|
123
|
+
for (const range of ranges) {
|
|
124
|
+
if (
|
|
125
|
+
!Number.isInteger(range.start) ||
|
|
126
|
+
!Number.isInteger(range.end) ||
|
|
127
|
+
range.start < 0 ||
|
|
128
|
+
range.end <= range.start
|
|
129
|
+
) {
|
|
130
|
+
fail('ranges must be ordered, non-empty, non-negative UTF-16 offsets');
|
|
131
|
+
}
|
|
132
|
+
if (range.data !== undefined && !isMarkdownPluginData(range.data)) {
|
|
133
|
+
fail('range data must be finite JSON-like plugin data');
|
|
134
|
+
}
|
|
135
|
+
const data =
|
|
136
|
+
range.data === undefined ? undefined : cloneDecorationData(range.data);
|
|
137
|
+
const key = `${range.start}\u0000${range.end}\u0000${
|
|
138
|
+
data === undefined ? '' : JSON.stringify(data)
|
|
139
|
+
}`;
|
|
140
|
+
if (seen.has(key)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
seen.add(key);
|
|
144
|
+
normalized.push({
|
|
145
|
+
start: range.start,
|
|
146
|
+
end: range.end,
|
|
147
|
+
entry: Object.freeze(
|
|
148
|
+
data === undefined ? {name} : {name, data},
|
|
149
|
+
) as DecorationEntry,
|
|
150
|
+
key,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// Sorted by start, then end, then payload: the sweep below needs start
|
|
154
|
+
// order, and the tail of the comparison makes overlapping and duplicate
|
|
155
|
+
// ranges resolve identically on every run.
|
|
156
|
+
normalized.sort(
|
|
157
|
+
(left, right) =>
|
|
158
|
+
left.start - right.start ||
|
|
159
|
+
left.end - right.end ||
|
|
160
|
+
(left.key < right.key ? -1 : left.key > right.key ? 1 : 0),
|
|
161
|
+
);
|
|
162
|
+
return Object.freeze(
|
|
163
|
+
normalized.map(({start, end, entry}) => Object.freeze({start, end, entry})),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* A node's Core-authored span, or `undefined` when it has no usable one.
|
|
169
|
+
* Read into two numbers rather than an object: this runs once per node of
|
|
170
|
+
* every decorated document.
|
|
171
|
+
*/
|
|
172
|
+
function spanStart(position: MarkdownAstPosition | undefined): number {
|
|
173
|
+
if (position === undefined) {
|
|
174
|
+
return -1;
|
|
175
|
+
}
|
|
176
|
+
const start = position.start.offset;
|
|
177
|
+
const end = position.end.offset;
|
|
178
|
+
return typeof start === 'number' &&
|
|
179
|
+
typeof end === 'number' &&
|
|
180
|
+
start >= 0 &&
|
|
181
|
+
end >= start
|
|
182
|
+
? start
|
|
183
|
+
: -1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Reads the decoration entries already on a node. `null` means the node
|
|
188
|
+
* carries data this helper does not own, so it must be left alone.
|
|
189
|
+
*/
|
|
190
|
+
function existingEntries(
|
|
191
|
+
data: MarkdownAstDataValue | undefined,
|
|
192
|
+
): ReadonlyArray<MarkdownAstDataValue> | undefined | null {
|
|
193
|
+
if (data === undefined) {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
if (!isPlainObject(data)) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
const envelope = data[ENVELOPE_KEY];
|
|
200
|
+
if (envelope === undefined) {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
if (
|
|
204
|
+
!isPlainObject(envelope) ||
|
|
205
|
+
envelope.kind !== ENVELOPE_BRAND ||
|
|
206
|
+
envelope.version !== ENVELOPE_VERSION ||
|
|
207
|
+
!Array.isArray(envelope.entries) ||
|
|
208
|
+
!envelope.entries.every(isDecorationEntry)
|
|
209
|
+
) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
return envelope.entries;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function isDecorationEntry(value: MarkdownAstDataValue): boolean {
|
|
216
|
+
return isPlainObject(value) && typeof value.name === 'string';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function envelope(
|
|
220
|
+
entries: ReadonlyArray<MarkdownAstDataValue>,
|
|
221
|
+
): MarkdownAstDataValue {
|
|
222
|
+
return {
|
|
223
|
+
kind: ENVELOPE_BRAND,
|
|
224
|
+
version: ENVELOPE_VERSION,
|
|
225
|
+
entries,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function reportSkip(state: RunState): void {
|
|
230
|
+
if (state.hasReported) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
state.hasReported = true;
|
|
234
|
+
state.report(
|
|
235
|
+
'skipped a node whose data is not a Core-owned source-decoration envelope',
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Records `entries` in the node's decoration envelope, or returns `undefined`
|
|
241
|
+
* when the node carries data this helper does not own. Skipping keeps a
|
|
242
|
+
* foreign payload intact instead of overwriting or extending it.
|
|
243
|
+
*/
|
|
244
|
+
function mergeDecorations(
|
|
245
|
+
node: DecoratableNode,
|
|
246
|
+
entries: ReadonlyArray<DecorationEntry>,
|
|
247
|
+
state: RunState,
|
|
248
|
+
): MarkdownAstDataValue | undefined {
|
|
249
|
+
const current = existingEntries(node.data);
|
|
250
|
+
if (current === null) {
|
|
251
|
+
reportSkip(state);
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
const merged = envelope(
|
|
255
|
+
current === undefined ? entries : [...current, ...entries],
|
|
256
|
+
);
|
|
257
|
+
return node.data === undefined
|
|
258
|
+
? {[ENVELOPE_KEY]: merged}
|
|
259
|
+
: {...(node.data as object), [ENVELOPE_KEY]: merged};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Whether the positioned children already sit in ascending span order, and
|
|
264
|
+
* the span-ordered index list when they do not. An earlier transform may
|
|
265
|
+
* reorder, drop, or interleave blocks, so document order is never assumed —
|
|
266
|
+
* but the ordinary case is detected in one pass and needs no index list at
|
|
267
|
+
* all.
|
|
268
|
+
*/
|
|
269
|
+
function spanOrder(
|
|
270
|
+
children: ReadonlyArray<DecoratableNode>,
|
|
271
|
+
): ReadonlyArray<number> | undefined {
|
|
272
|
+
let previous = -1;
|
|
273
|
+
for (let index = 0; index < children.length; index++) {
|
|
274
|
+
const start = spanStart(children[index].position);
|
|
275
|
+
if (start < 0) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (start < previous) {
|
|
279
|
+
const indices: number[] = [];
|
|
280
|
+
for (let scan = 0; scan < children.length; scan++) {
|
|
281
|
+
if (spanStart(children[scan].position) >= 0) {
|
|
282
|
+
indices.push(scan);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
indices.sort(
|
|
286
|
+
(left, right) =>
|
|
287
|
+
spanStart(children[left].position) -
|
|
288
|
+
spanStart(children[right].position) || left - right,
|
|
289
|
+
);
|
|
290
|
+
return indices;
|
|
291
|
+
}
|
|
292
|
+
previous = start;
|
|
293
|
+
}
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* The first slot whose node can overlap `offset` — that is, the first one
|
|
299
|
+
* ending after it. Spans ascend, so the blocks a range touches are one
|
|
300
|
+
* contiguous run and a search finds its start without walking the document.
|
|
301
|
+
* Decorating a known range therefore costs the blocks it actually covers,
|
|
302
|
+
* not the length of the document it sits in.
|
|
303
|
+
*/
|
|
304
|
+
function firstSlotEndingAfter(
|
|
305
|
+
children: ReadonlyArray<DecoratableNode>,
|
|
306
|
+
order: ReadonlyArray<number> | undefined,
|
|
307
|
+
length: number,
|
|
308
|
+
offset: number,
|
|
309
|
+
): number {
|
|
310
|
+
let low = 0;
|
|
311
|
+
let high = length;
|
|
312
|
+
while (low < high) {
|
|
313
|
+
const middle = (low + high) >>> 1;
|
|
314
|
+
const node = children[order?.[middle] ?? middle];
|
|
315
|
+
const end = node.position?.end.offset;
|
|
316
|
+
if (typeof end === 'number' && end <= offset) {
|
|
317
|
+
low = middle + 1;
|
|
318
|
+
} else {
|
|
319
|
+
high = middle;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return low;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Sweeps the start-sorted ranges across the span-ordered children, keeping
|
|
327
|
+
* the ranges that can still overlap in an active window. Each range is
|
|
328
|
+
* admitted and retired at most once, so the pass costs the blocks the ranges
|
|
329
|
+
* reach plus the matches — never the whole document per range.
|
|
330
|
+
*/
|
|
331
|
+
function decorateChildren(
|
|
332
|
+
children: ReadonlyArray<DecoratableNode>,
|
|
333
|
+
ranges: ReadonlyArray<NormalizedRange>,
|
|
334
|
+
state: RunState,
|
|
335
|
+
): ReadonlyArray<DecoratableNode> {
|
|
336
|
+
const order = spanOrder(children);
|
|
337
|
+
const length = order?.length ?? children.length;
|
|
338
|
+
let next: DecoratableNode[] | undefined;
|
|
339
|
+
let cursor = 0;
|
|
340
|
+
const active: NormalizedRange[] = [];
|
|
341
|
+
|
|
342
|
+
// Skip straight to the first block the earliest range can reach. Unordered
|
|
343
|
+
// trees fall back to a full sweep, since a sorted index list makes the
|
|
344
|
+
// search valid only over positioned nodes.
|
|
345
|
+
let slot =
|
|
346
|
+
order === undefined
|
|
347
|
+
? firstSlotEndingAfter(children, order, length, ranges[0].start)
|
|
348
|
+
: 0;
|
|
349
|
+
|
|
350
|
+
for (; slot < length; slot++) {
|
|
351
|
+
// Once every range has been admitted and retired, no later span can
|
|
352
|
+
// match: the rest of the document is left untouched.
|
|
353
|
+
if (cursor >= ranges.length && active.length === 0) {
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
const index = order?.[slot] ?? slot;
|
|
357
|
+
const child = children[index];
|
|
358
|
+
const start = spanStart(child.position);
|
|
359
|
+
if (start < 0) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const end = child.position?.end.offset as number;
|
|
363
|
+
while (cursor < ranges.length && ranges[cursor].start < end) {
|
|
364
|
+
active.push(ranges[cursor++]);
|
|
365
|
+
}
|
|
366
|
+
// Retire in place: spans are visited in ascending start order, so a range
|
|
367
|
+
// that ended before this one starts cannot match any later span either.
|
|
368
|
+
let kept = 0;
|
|
369
|
+
for (let active_slot = 0; active_slot < active.length; active_slot++) {
|
|
370
|
+
if (active[active_slot].end > start) {
|
|
371
|
+
active[kept++] = active[active_slot];
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
active.length = kept;
|
|
375
|
+
if (kept === 0) {
|
|
376
|
+
// No range reaches this block. When the next range starts beyond this
|
|
377
|
+
// block, jump to the first block it can reach rather than walking the
|
|
378
|
+
// gap between; when it starts inside this one the next block is
|
|
379
|
+
// already the answer and searching for it would cost more than
|
|
380
|
+
// stepping to it.
|
|
381
|
+
if (
|
|
382
|
+
order === undefined &&
|
|
383
|
+
cursor < ranges.length &&
|
|
384
|
+
ranges[cursor].start > end
|
|
385
|
+
) {
|
|
386
|
+
const jump = firstSlotEndingAfter(
|
|
387
|
+
children,
|
|
388
|
+
order,
|
|
389
|
+
length,
|
|
390
|
+
ranges[cursor].start,
|
|
391
|
+
);
|
|
392
|
+
if (jump > slot) {
|
|
393
|
+
slot = jump - 1;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
const decorated = decorateNode(child, active, state);
|
|
399
|
+
if (decorated === child) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
next ??= children.slice();
|
|
403
|
+
next[index] = decorated;
|
|
404
|
+
}
|
|
405
|
+
return next ?? children;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Decorates one node every range in `ranges` already overlaps. Decoration
|
|
410
|
+
* stops at the outermost positioned node, so a range never records itself
|
|
411
|
+
* twice down one branch.
|
|
412
|
+
*/
|
|
413
|
+
function decorateNode(
|
|
414
|
+
node: DecoratableNode,
|
|
415
|
+
ranges: ReadonlyArray<NormalizedRange>,
|
|
416
|
+
state: RunState,
|
|
417
|
+
): DecoratableNode {
|
|
418
|
+
// Extension nodes belong to the plugin that produced them; their data is
|
|
419
|
+
// part of their identity, so decoration never touches them.
|
|
420
|
+
if (node.type === 'extension') {
|
|
421
|
+
return node;
|
|
422
|
+
}
|
|
423
|
+
const data = mergeDecorations(
|
|
424
|
+
node,
|
|
425
|
+
ranges.map(range => range.entry),
|
|
426
|
+
state,
|
|
427
|
+
);
|
|
428
|
+
return data === undefined ? node : {...node, data};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Compiles validated source ranges into an immutable transform that records
|
|
433
|
+
* non-semantic presentation metadata on the Core-authored nodes those ranges
|
|
434
|
+
* overlap.
|
|
435
|
+
*
|
|
436
|
+
* A node is decorated only when the parser authored its source position and
|
|
437
|
+
* the range overlaps its span, so the helper never changes AST meaning,
|
|
438
|
+
* rendered output, copyable text, accessible names, focus order, navigation,
|
|
439
|
+
* or source provenance. Core authors positions on top-level blocks, so a
|
|
440
|
+
* range decorates every block it touches, whatever order those blocks sit in
|
|
441
|
+
* after earlier transforms.
|
|
442
|
+
*
|
|
443
|
+
* Decorations describe settled source, so they appear on the final snapshot
|
|
444
|
+
* of a streaming document rather than on its partial chunks: a partial
|
|
445
|
+
* block's extent is still changing, and exposing decorations from it would
|
|
446
|
+
* let one appear and then vanish. Exposure is therefore monotone — never
|
|
447
|
+
* present, then absent.
|
|
448
|
+
*
|
|
449
|
+
* Decorations are stored in one Core-owned versioned envelope and read back
|
|
450
|
+
* with `getMarkdownSourceDecorations`; data the helper does not own is never
|
|
451
|
+
* merged into or overwritten.
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* ```
|
|
455
|
+
* const reviewHits = createMarkdownPlugin({
|
|
456
|
+
* name: 'review-hits',
|
|
457
|
+
* apiVersion: 1,
|
|
458
|
+
* transform: createMarkdownSourceDecoration({
|
|
459
|
+
* name: 'review-hit',
|
|
460
|
+
* ranges: [{start: 0, end: 12, data: {reviewer: 'ada'}}],
|
|
461
|
+
* }),
|
|
462
|
+
* });
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
export function createMarkdownSourceDecoration(
|
|
466
|
+
options: MarkdownSourceDecorationOptions,
|
|
467
|
+
): MarkdownTransform<never> {
|
|
468
|
+
if (typeof options.name !== 'string' || options.name.trim() === '') {
|
|
469
|
+
fail('name must be a non-empty string');
|
|
470
|
+
}
|
|
471
|
+
if (!Array.isArray(options.ranges)) {
|
|
472
|
+
fail('ranges must be an array');
|
|
473
|
+
}
|
|
474
|
+
const ranges = normalizeRanges(options.name, options.ranges);
|
|
475
|
+
const smallestStart = ranges.length === 0 ? -1 : ranges[0].start;
|
|
476
|
+
|
|
477
|
+
const transform: MarkdownTransform<never> = (document, context) => {
|
|
478
|
+
// Decorations describe settled source. While a document streams, the
|
|
479
|
+
// block a range falls in can still be re-parsed, re-split, merged into a
|
|
480
|
+
// table or list, or withheld entirely, so a range's covering block is not
|
|
481
|
+
// stable and a decoration computed from it could appear, vanish, and
|
|
482
|
+
// reappear across chunks. Waiting for the settled snapshot makes exposure
|
|
483
|
+
// monotone by construction: absent while the source can still change,
|
|
484
|
+
// then present once it cannot.
|
|
485
|
+
if (!context.isFinal || ranges.length === 0) {
|
|
486
|
+
return document;
|
|
487
|
+
}
|
|
488
|
+
// A range starting at or past the end of the source overlaps nothing.
|
|
489
|
+
const usable =
|
|
490
|
+
ranges[ranges.length - 1].start < context.source.length
|
|
491
|
+
? ranges
|
|
492
|
+
: ranges.filter(range => range.start < context.source.length);
|
|
493
|
+
if (usable.length === 0) {
|
|
494
|
+
return document;
|
|
495
|
+
}
|
|
496
|
+
const root = document as unknown as DecoratableNode;
|
|
497
|
+
if (root.children == null) {
|
|
498
|
+
return document;
|
|
499
|
+
}
|
|
500
|
+
const state: RunState = {
|
|
501
|
+
report: message => context.report(message),
|
|
502
|
+
hasReported: false,
|
|
503
|
+
};
|
|
504
|
+
const children = decorateChildren(root.children, usable, state);
|
|
505
|
+
return children === root.children
|
|
506
|
+
? document
|
|
507
|
+
: ({
|
|
508
|
+
...document,
|
|
509
|
+
children,
|
|
510
|
+
} as unknown as MarkdownAstRoot<MarkdownExtensionNode>);
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
// Every node this helper emits is built here, from ranges validated above:
|
|
514
|
+
// it inserts no caller-supplied node and changes no existing node's meaning,
|
|
515
|
+
// provenance, or ownership. Core may therefore skip the validation and
|
|
516
|
+
// freezing it applies to plugin-authored output.
|
|
517
|
+
return markMarkdownTransformTrusted(
|
|
518
|
+
markMarkdownTransformClaim(
|
|
519
|
+
transform,
|
|
520
|
+
source => smallestStart >= 0 && source.length > smallestStart,
|
|
521
|
+
),
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Reads the decorations a source-decoration helper recorded on a node.
|
|
527
|
+
* Returns an empty list for undecorated nodes and for any value that is not
|
|
528
|
+
* a current Core-owned decoration envelope.
|
|
529
|
+
*/
|
|
530
|
+
export function getMarkdownSourceDecorations(
|
|
531
|
+
node: MarkdownAstNodeBase,
|
|
532
|
+
): ReadonlyArray<MarkdownSourceDecoration> {
|
|
533
|
+
const entries = existingEntries(node.data);
|
|
534
|
+
// `existingEntries` returns entries only from a branded envelope whose
|
|
535
|
+
// every member passed `isDecorationEntry`, so each one has a string name.
|
|
536
|
+
return entries == null || entries.length === 0
|
|
537
|
+
? NO_DECORATIONS
|
|
538
|
+
: (entries as unknown as ReadonlyArray<MarkdownSourceDecoration>);
|
|
539
|
+
}
|