@drscrewdriver/dsh-better-display 0.3.3-fork.1
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 +259 -0
- package/DESIGN.md +72 -0
- package/LICENSE +24 -0
- package/README.en.md +55 -0
- package/README.md +55 -0
- package/THIRD_PARTY_NOTICES.md +65 -0
- package/compat/harness-rc2.json +99 -0
- package/cordis.patch.yml +5 -0
- package/cordis.yml +4 -0
- package/docs/official-rendering-bridge.md +65 -0
- package/dshx.yml +5 -0
- package/lib/client.js +49292 -0
- package/lib/client.js.map +1 -0
- package/lib/dsh-better-display.js +101 -0
- package/lib/types/client/Blocks.d.ts +49 -0
- package/lib/types/client/Blocks.d.ts.map +1 -0
- package/lib/types/client/Blocks.js +143 -0
- package/lib/types/client/Blocks.js.map +1 -0
- package/lib/types/client/ChoreographedFlow.d.ts +22 -0
- package/lib/types/client/ChoreographedFlow.d.ts.map +1 -0
- package/lib/types/client/ChoreographedFlow.js +218 -0
- package/lib/types/client/ChoreographedFlow.js.map +1 -0
- package/lib/types/client/ClosedProcessSummary.d.ts +9 -0
- package/lib/types/client/ClosedProcessSummary.d.ts.map +1 -0
- package/lib/types/client/ClosedProcessSummary.js +16 -0
- package/lib/types/client/ClosedProcessSummary.js.map +1 -0
- package/lib/types/client/DiffPanel.d.ts +13 -0
- package/lib/types/client/DiffPanel.d.ts.map +1 -0
- package/lib/types/client/DiffPanel.js +115 -0
- package/lib/types/client/DiffPanel.js.map +1 -0
- package/lib/types/client/LiveFold.d.ts +10 -0
- package/lib/types/client/LiveFold.d.ts.map +1 -0
- package/lib/types/client/LiveFold.js +39 -0
- package/lib/types/client/LiveFold.js.map +1 -0
- package/lib/types/client/McpAppFrame.d.ts +32 -0
- package/lib/types/client/McpAppFrame.d.ts.map +1 -0
- package/lib/types/client/McpAppFrame.js +193 -0
- package/lib/types/client/McpAppFrame.js.map +1 -0
- package/lib/types/client/OfficialContent.d.ts +25 -0
- package/lib/types/client/OfficialContent.d.ts.map +1 -0
- package/lib/types/client/OfficialContent.js +53 -0
- package/lib/types/client/OfficialContent.js.map +1 -0
- package/lib/types/client/Reader.d.ts +3 -0
- package/lib/types/client/Reader.d.ts.map +1 -0
- package/lib/types/client/Reader.js +603 -0
- package/lib/types/client/Reader.js.map +1 -0
- package/lib/types/client/ReasoningCard.d.ts +11 -0
- package/lib/types/client/ReasoningCard.d.ts.map +1 -0
- package/lib/types/client/ReasoningCard.js +322 -0
- package/lib/types/client/ReasoningCard.js.map +1 -0
- package/lib/types/client/SettingsSection.d.ts +37 -0
- package/lib/types/client/SettingsSection.d.ts.map +1 -0
- package/lib/types/client/SettingsSection.js +63 -0
- package/lib/types/client/SettingsSection.js.map +1 -0
- package/lib/types/client/StickyLane.d.ts +8 -0
- package/lib/types/client/StickyLane.d.ts.map +1 -0
- package/lib/types/client/StickyLane.js +39 -0
- package/lib/types/client/StickyLane.js.map +1 -0
- package/lib/types/client/TimelineRail.d.ts +10 -0
- package/lib/types/client/TimelineRail.d.ts.map +1 -0
- package/lib/types/client/TimelineRail.js +168 -0
- package/lib/types/client/TimelineRail.js.map +1 -0
- package/lib/types/client/ToolActivity.d.ts +17 -0
- package/lib/types/client/ToolActivity.d.ts.map +1 -0
- package/lib/types/client/ToolActivity.js +244 -0
- package/lib/types/client/ToolActivity.js.map +1 -0
- package/lib/types/client/TurnMetrics.d.ts +11 -0
- package/lib/types/client/TurnMetrics.d.ts.map +1 -0
- package/lib/types/client/TurnMetrics.js +44 -0
- package/lib/types/client/TurnMetrics.js.map +1 -0
- package/lib/types/client/WaitClock.d.ts +16 -0
- package/lib/types/client/WaitClock.d.ts.map +1 -0
- package/lib/types/client/WaitClock.js +29 -0
- package/lib/types/client/WaitClock.js.map +1 -0
- package/lib/types/client/WaitingClock.d.ts +15 -0
- package/lib/types/client/WaitingClock.d.ts.map +1 -0
- package/lib/types/client/WaitingClock.js +30 -0
- package/lib/types/client/WaitingClock.js.map +1 -0
- package/lib/types/client/WaitingStatus.d.ts +6 -0
- package/lib/types/client/WaitingStatus.d.ts.map +1 -0
- package/lib/types/client/WaitingStatus.js +9 -0
- package/lib/types/client/WaitingStatus.js.map +1 -0
- package/lib/types/client/conversation-scroll.d.ts +19 -0
- package/lib/types/client/conversation-scroll.d.ts.map +1 -0
- package/lib/types/client/conversation-scroll.js +25 -0
- package/lib/types/client/conversation-scroll.js.map +1 -0
- package/lib/types/client/deliverables.d.ts +24 -0
- package/lib/types/client/deliverables.d.ts.map +1 -0
- package/lib/types/client/deliverables.js +104 -0
- package/lib/types/client/deliverables.js.map +1 -0
- package/lib/types/client/entry-policy.d.ts +10 -0
- package/lib/types/client/entry-policy.d.ts.map +1 -0
- package/lib/types/client/entry-policy.js +24 -0
- package/lib/types/client/entry-policy.js.map +1 -0
- package/lib/types/client/entry.d.ts +4 -0
- package/lib/types/client/entry.d.ts.map +1 -0
- package/lib/types/client/entry.js +34 -0
- package/lib/types/client/entry.js.map +1 -0
- package/lib/types/client/fold-choreography.d.ts +40 -0
- package/lib/types/client/fold-choreography.d.ts.map +1 -0
- package/lib/types/client/fold-choreography.js +48 -0
- package/lib/types/client/fold-choreography.js.map +1 -0
- package/lib/types/client/fold-intensity.d.ts +11 -0
- package/lib/types/client/fold-intensity.d.ts.map +1 -0
- package/lib/types/client/fold-intensity.js +32 -0
- package/lib/types/client/fold-intensity.js.map +1 -0
- package/lib/types/client/index.d.ts +7 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/index.js +195 -0
- package/lib/types/client/index.js.map +1 -0
- package/lib/types/client/live-turn.d.ts +58 -0
- package/lib/types/client/live-turn.d.ts.map +1 -0
- package/lib/types/client/live-turn.js +190 -0
- package/lib/types/client/live-turn.js.map +1 -0
- package/lib/types/client/markdown/MarkdownText.d.ts +41 -0
- package/lib/types/client/markdown/MarkdownText.d.ts.map +1 -0
- package/lib/types/client/markdown/MarkdownText.js +178 -0
- package/lib/types/client/markdown/MarkdownText.js.map +1 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.d.ts +9 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.d.ts.map +1 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.js +73 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.js.map +1 -0
- package/lib/types/client/markdown/incremental.d.ts +69 -0
- package/lib/types/client/markdown/incremental.d.ts.map +1 -0
- package/lib/types/client/markdown/incremental.js +108 -0
- package/lib/types/client/markdown/incremental.js.map +1 -0
- package/lib/types/client/markdown/katex.d.ts +27 -0
- package/lib/types/client/markdown/katex.d.ts.map +1 -0
- package/lib/types/client/markdown/katex.js +88 -0
- package/lib/types/client/markdown/katex.js.map +1 -0
- package/lib/types/client/markdown/mathCompatibility.d.ts +11 -0
- package/lib/types/client/markdown/mathCompatibility.d.ts.map +1 -0
- package/lib/types/client/markdown/mathCompatibility.js +293 -0
- package/lib/types/client/markdown/mathCompatibility.js.map +1 -0
- package/lib/types/client/markdown/parse.d.ts +25 -0
- package/lib/types/client/markdown/parse.d.ts.map +1 -0
- package/lib/types/client/markdown/parse.js +42 -0
- package/lib/types/client/markdown/parse.js.map +1 -0
- package/lib/types/client/markdown/render.d.ts +121 -0
- package/lib/types/client/markdown/render.d.ts.map +1 -0
- package/lib/types/client/markdown/render.js +452 -0
- package/lib/types/client/markdown/render.js.map +1 -0
- package/lib/types/client/mcp-app.d.ts +50 -0
- package/lib/types/client/mcp-app.d.ts.map +1 -0
- package/lib/types/client/mcp-app.js +328 -0
- package/lib/types/client/mcp-app.js.map +1 -0
- package/lib/types/client/message-chrome.d.ts +19 -0
- package/lib/types/client/message-chrome.d.ts.map +1 -0
- package/lib/types/client/message-chrome.js +42 -0
- package/lib/types/client/message-chrome.js.map +1 -0
- package/lib/types/client/motion.d.ts +38 -0
- package/lib/types/client/motion.d.ts.map +1 -0
- package/lib/types/client/motion.js +453 -0
- package/lib/types/client/motion.js.map +1 -0
- package/lib/types/client/native/ContextBody.d.ts +130 -0
- package/lib/types/client/native/ContextBody.d.ts.map +1 -0
- package/lib/types/client/native/ContextBody.js +398 -0
- package/lib/types/client/native/ContextBody.js.map +1 -0
- package/lib/types/client/native/ContextInjectionRow.d.ts +35 -0
- package/lib/types/client/native/ContextInjectionRow.d.ts.map +1 -0
- package/lib/types/client/native/ContextInjectionRow.js +42 -0
- package/lib/types/client/native/ContextInjectionRow.js.map +1 -0
- package/lib/types/client/native/ReferenceIcon.d.ts +16 -0
- package/lib/types/client/native/ReferenceIcon.d.ts.map +1 -0
- package/lib/types/client/native/ReferenceIcon.js +16 -0
- package/lib/types/client/native/ReferenceIcon.js.map +1 -0
- package/lib/types/client/native/tool-call-model.d.ts +58 -0
- package/lib/types/client/native/tool-call-model.d.ts.map +1 -0
- package/lib/types/client/native/tool-call-model.js +219 -0
- package/lib/types/client/native/tool-call-model.js.map +1 -0
- package/lib/types/client/native/tool-update.d.ts +23 -0
- package/lib/types/client/native/tool-update.d.ts.map +1 -0
- package/lib/types/client/native/tool-update.js +22 -0
- package/lib/types/client/native/tool-update.js.map +1 -0
- package/lib/types/client/official-slots.d.ts +71 -0
- package/lib/types/client/official-slots.d.ts.map +1 -0
- package/lib/types/client/official-slots.js +183 -0
- package/lib/types/client/official-slots.js.map +1 -0
- package/lib/types/client/open-file.d.ts +29 -0
- package/lib/types/client/open-file.d.ts.map +1 -0
- package/lib/types/client/open-file.js +71 -0
- package/lib/types/client/open-file.js.map +1 -0
- package/lib/types/client/pending-submission.d.ts +28 -0
- package/lib/types/client/pending-submission.d.ts.map +1 -0
- package/lib/types/client/pending-submission.js +28 -0
- package/lib/types/client/pending-submission.js.map +1 -0
- package/lib/types/client/platform-media.d.ts +6 -0
- package/lib/types/client/platform-media.d.ts.map +1 -0
- package/lib/types/client/platform-media.js +18 -0
- package/lib/types/client/platform-media.js.map +1 -0
- package/lib/types/client/primitive-labels.d.ts +10 -0
- package/lib/types/client/primitive-labels.d.ts.map +1 -0
- package/lib/types/client/primitive-labels.js +38 -0
- package/lib/types/client/primitive-labels.js.map +1 -0
- package/lib/types/client/projection.d.ts +41 -0
- package/lib/types/client/projection.d.ts.map +1 -0
- package/lib/types/client/projection.js +112 -0
- package/lib/types/client/projection.js.map +1 -0
- package/lib/types/client/reasoning-follow.d.ts +10 -0
- package/lib/types/client/reasoning-follow.d.ts.map +1 -0
- package/lib/types/client/reasoning-follow.js +21 -0
- package/lib/types/client/reasoning-follow.js.map +1 -0
- package/lib/types/client/resource-address.d.ts +3 -0
- package/lib/types/client/resource-address.d.ts.map +1 -0
- package/lib/types/client/resource-address.js +33 -0
- package/lib/types/client/resource-address.js.map +1 -0
- package/lib/types/client/settings-copy.d.ts +7 -0
- package/lib/types/client/settings-copy.d.ts.map +1 -0
- package/lib/types/client/settings-copy.js +53 -0
- package/lib/types/client/settings-copy.js.map +1 -0
- package/lib/types/client/settings-slots.d.ts +23 -0
- package/lib/types/client/settings-slots.d.ts.map +1 -0
- package/lib/types/client/settings-slots.js +2 -0
- package/lib/types/client/settings-slots.js.map +1 -0
- package/lib/types/client/settings.d.ts +4 -0
- package/lib/types/client/settings.d.ts.map +1 -0
- package/lib/types/client/settings.js +60 -0
- package/lib/types/client/settings.js.map +1 -0
- package/lib/types/client/skill-status.d.ts +25 -0
- package/lib/types/client/skill-status.d.ts.map +1 -0
- package/lib/types/client/skill-status.js +44 -0
- package/lib/types/client/skill-status.js.map +1 -0
- package/lib/types/client/store.d.ts +35 -0
- package/lib/types/client/store.d.ts.map +1 -0
- package/lib/types/client/store.js +37 -0
- package/lib/types/client/store.js.map +1 -0
- package/lib/types/client/stream-buffer.d.ts +37 -0
- package/lib/types/client/stream-buffer.d.ts.map +1 -0
- package/lib/types/client/stream-buffer.js +150 -0
- package/lib/types/client/stream-buffer.js.map +1 -0
- package/lib/types/client/streaming.d.ts +18 -0
- package/lib/types/client/streaming.d.ts.map +1 -0
- package/lib/types/client/streaming.js +84 -0
- package/lib/types/client/streaming.js.map +1 -0
- package/lib/types/client/timeline.d.ts +26 -0
- package/lib/types/client/timeline.d.ts.map +1 -0
- package/lib/types/client/timeline.js +48 -0
- package/lib/types/client/timeline.js.map +1 -0
- package/lib/types/client/tool-activity.d.ts +98 -0
- package/lib/types/client/tool-activity.d.ts.map +1 -0
- package/lib/types/client/tool-activity.js +301 -0
- package/lib/types/client/tool-activity.js.map +1 -0
- package/lib/types/client/types.d.ts +91 -0
- package/lib/types/client/types.d.ts.map +1 -0
- package/lib/types/client/types.js +2 -0
- package/lib/types/client/types.js.map +1 -0
- package/lib/types/client/waiting-clock.d.ts +49 -0
- package/lib/types/client/waiting-clock.d.ts.map +1 -0
- package/lib/types/client/waiting-clock.js +80 -0
- package/lib/types/client/waiting-clock.js.map +1 -0
- package/lib/types/client/word-motion.d.ts +16 -0
- package/lib/types/client/word-motion.d.ts.map +1 -0
- package/lib/types/client/word-motion.js +90 -0
- package/lib/types/client/word-motion.js.map +1 -0
- package/lib/types/client/word-timeline.d.ts +32 -0
- package/lib/types/client/word-timeline.d.ts.map +1 -0
- package/lib/types/client/word-timeline.js +119 -0
- package/lib/types/client/word-timeline.js.map +1 -0
- package/lib/types/dsh-better-display.d.ts +17 -0
- package/lib/types/dsh-better-display.d.ts.map +1 -0
- package/lib/types/dsh-better-display.js +93 -0
- package/lib/types/dsh-better-display.js.map +1 -0
- package/lib/types/skill-roots.d.ts +22 -0
- package/lib/types/skill-roots.d.ts.map +1 -0
- package/lib/types/skill-roots.js +126 -0
- package/lib/types/skill-roots.js.map +1 -0
- package/lib/types/skill-status.d.ts +36 -0
- package/lib/types/skill-status.d.ts.map +1 -0
- package/lib/types/skill-status.js +51 -0
- package/lib/types/skill-status.js.map +1 -0
- package/package.json +133 -0
- package/scripts/build-motion-fixture.mjs +26 -0
- package/scripts/check-harness-compat.mjs +71 -0
- package/scripts/link-harness-dependencies.mjs +54 -0
- package/scripts/verify-auto-fold.mjs +114 -0
- package/scripts/verify-official-bridge.mjs +121 -0
- package/scripts/verify-tool-shimmer.mjs +62 -0
- package/skills/generative-mcpapps/SKILL.md +47 -0
- package/skills/generative-mcpapps/examples/interactive_quiz.html +153 -0
- package/skills/generative-mcpapps/references/html_boilerplate.md +122 -0
- package/skills/generative-mcpapps/references/sep1865_protocol.md +54 -0
- package/src/client/Blocks.tsx +213 -0
- package/src/client/ChoreographedFlow.tsx +232 -0
- package/src/client/ClosedProcessSummary.tsx +23 -0
- package/src/client/DiffPanel.tsx +124 -0
- package/src/client/LiveFold.tsx +43 -0
- package/src/client/McpAppFrame.module.css +210 -0
- package/src/client/McpAppFrame.tsx +317 -0
- package/src/client/OfficialContent.tsx +69 -0
- package/src/client/Reader.module.css +692 -0
- package/src/client/Reader.tsx +878 -0
- package/src/client/ReasoningCard.tsx +306 -0
- package/src/client/SettingsSection.module.css +184 -0
- package/src/client/SettingsSection.tsx +194 -0
- package/src/client/StickyLane.tsx +34 -0
- package/src/client/TimelineRail.module.css +170 -0
- package/src/client/TimelineRail.tsx +262 -0
- package/src/client/ToolActivity.tsx +274 -0
- package/src/client/TurnMetrics.module.css +139 -0
- package/src/client/TurnMetrics.tsx +160 -0
- package/src/client/WaitClock.tsx +33 -0
- package/src/client/WaitingStatus.tsx +13 -0
- package/src/client/conversation-scroll.ts +28 -0
- package/src/client/deliverables.ts +122 -0
- package/src/client/entry-policy.ts +20 -0
- package/src/client/entry.tsx +40 -0
- package/src/client/fold-choreography.ts +55 -0
- package/src/client/fold-intensity.ts +36 -0
- package/src/client/index.tsx +222 -0
- package/src/client/live-turn.ts +198 -0
- package/src/client/markdown/MarkdownText.module.css +304 -0
- package/src/client/markdown/MarkdownText.tsx +195 -0
- package/src/client/markdown/cjkFriendlyStrong.ts +84 -0
- package/src/client/markdown/incremental.ts +131 -0
- package/src/client/markdown/katex.tsx +91 -0
- package/src/client/markdown/mathCompatibility.ts +350 -0
- package/src/client/markdown/parse.ts +45 -0
- package/src/client/markdown/render.tsx +647 -0
- package/src/client/markdown/upstream.json +14 -0
- package/src/client/mcp-app.ts +323 -0
- package/src/client/message-chrome.ts +47 -0
- package/src/client/motion.tsx +410 -0
- package/src/client/native/ContextBody.module.css +161 -0
- package/src/client/native/ContextBody.tsx +591 -0
- package/src/client/native/ContextInjectionRow.module.css +66 -0
- package/src/client/native/ContextInjectionRow.tsx +106 -0
- package/src/client/native/ReferenceIcon.tsx +35 -0
- package/src/client/native/tool-call-model.ts +248 -0
- package/src/client/native/tool-update.ts +32 -0
- package/src/client/native/upstream.json +41 -0
- package/src/client/official-slots.tsx +211 -0
- package/src/client/open-file.ts +94 -0
- package/src/client/pending-submission.ts +49 -0
- package/src/client/platform-media.ts +17 -0
- package/src/client/primitive-labels.ts +54 -0
- package/src/client/projection.ts +118 -0
- package/src/client/reasoning-follow.ts +28 -0
- package/src/client/settings-copy.ts +80 -0
- package/src/client/settings-slots.ts +20 -0
- package/src/client/settings.tsx +73 -0
- package/src/client/skill-status.ts +78 -0
- package/src/client/store.ts +75 -0
- package/src/client/stream-buffer.ts +136 -0
- package/src/client/streaming.tsx +70 -0
- package/src/client/style.d.ts +4 -0
- package/src/client/timeline.ts +77 -0
- package/src/client/tool-activity.ts +298 -0
- package/src/client/types.ts +89 -0
- package/src/client/waiting-clock.ts +83 -0
- package/src/client/word-motion.tsx +92 -0
- package/src/client/word-timeline.ts +113 -0
- package/src/dsh-better-display.ts +108 -0
- package/src/skill-roots.ts +138 -0
- package/src/skill-status.ts +81 -0
- package/tests/browser-auto-fold.tsx +64 -0
- package/tests/browser-closed-gap.tsx +20 -0
- package/tests/browser-motion.tsx +65 -0
- package/tests/browser-official-bridge.tsx +152 -0
- package/tests/browser-sticky-lanes.tsx +22 -0
- package/tests/browser-waiting-clock.tsx +14 -0
- package/tests/chat-flow-hook.test.ts +66 -0
- package/tests/cloud-gate.test.ts +34 -0
- package/tests/context-tool-update.test.ts +33 -0
- package/tests/conversation-scroll.test.ts +47 -0
- package/tests/deliverables.test.ts +128 -0
- package/tests/entry.test.ts +42 -0
- package/tests/fixtures/maid-composer-hide.html +31 -0
- package/tests/fold-choreography.test.ts +54 -0
- package/tests/fold-intensity.test.ts +94 -0
- package/tests/footer-precedence.test.ts +129 -0
- package/tests/history-word-regression.test.ts +16 -0
- package/tests/live-turn.test.ts +218 -0
- package/tests/mcp-apps.test.ts +157 -0
- package/tests/message-chrome.test.ts +29 -0
- package/tests/official-slots.test.ts +112 -0
- package/tests/open-file.test.ts +114 -0
- package/tests/pending-submission.test.ts +45 -0
- package/tests/platform-media.test.ts +23 -0
- package/tests/projection.test.ts +184 -0
- package/tests/reasoning-follow.test.ts +34 -0
- package/tests/settings-copy.test.ts +63 -0
- package/tests/skill-status.test.ts +137 -0
- package/tests/stock-install.test.ts +58 -0
- package/tests/stream-buffer.test.ts +102 -0
- package/tests/timeline.test.ts +59 -0
- package/tests/tool-diff.test.ts +57 -0
- package/tests/waiting-clock.test.ts +83 -0
- package/tests/word-timeline.test.ts +146 -0
- package/tsconfig.json +11 -0
- package/tsdown.config.ts +51 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Adapted from DeepSeek Harness b150a551b8d465e31e418e1b2eaf5e79bbb7d28e (MIT). See THIRD_PARTY_NOTICES.md.
|
|
2
|
+
/** Let asterisk strong emphasis close after punctuation when CJK prose continues without whitespace. */
|
|
3
|
+
|
|
4
|
+
import { attention } from 'micromark-core-commonmark'
|
|
5
|
+
import { unicodePunctuation } from 'micromark-util-character'
|
|
6
|
+
import { classifyCharacter } from 'micromark-util-classify-character'
|
|
7
|
+
import { codes, constants } from 'micromark-util-symbol'
|
|
8
|
+
import type { Construct, Extension, State, Tokenizer } from 'micromark-util-types'
|
|
9
|
+
|
|
10
|
+
const cjkCharacter = new RegExp([
|
|
11
|
+
'\\p{Script_Extensions=Han}',
|
|
12
|
+
'\\p{Script_Extensions=Hiragana}',
|
|
13
|
+
'\\p{Script_Extensions=Katakana}',
|
|
14
|
+
'\\p{Script_Extensions=Hangul}',
|
|
15
|
+
'\\p{Script_Extensions=Bopomofo}',
|
|
16
|
+
].join('|'), 'u')
|
|
17
|
+
|
|
18
|
+
function isCjkCharacter(code: number | null): boolean {
|
|
19
|
+
return code !== null && code >= 0 && cjkCharacter.test(String.fromCodePoint(code))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const tokenizeCjkFriendlyAttention: Tokenizer = function (effects, ok, nok) {
|
|
23
|
+
const configuredAttentionMarkers = this.parser.constructs.attentionMarkers.null
|
|
24
|
+
if (configuredAttentionMarkers === undefined) {
|
|
25
|
+
throw new Error('micromark CommonMark attention markers are unavailable')
|
|
26
|
+
}
|
|
27
|
+
const attentionMarkers = configuredAttentionMarkers
|
|
28
|
+
const previous = this.previous
|
|
29
|
+
const before = classifyCharacter(previous)
|
|
30
|
+
let marker: number | null = codes.eof
|
|
31
|
+
|
|
32
|
+
return start
|
|
33
|
+
|
|
34
|
+
function start(code: number | null): State | undefined {
|
|
35
|
+
/* v8 ignore next -- this text construct is dispatched only for an asterisk. */
|
|
36
|
+
if (code !== codes.asterisk) return nok(code)
|
|
37
|
+
marker = code
|
|
38
|
+
effects.enter('attentionSequence')
|
|
39
|
+
return inside(code)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function inside(code: number | null): State | undefined {
|
|
43
|
+
if (code === marker) {
|
|
44
|
+
effects.consume(code)
|
|
45
|
+
return inside
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const token = effects.exit('attentionSequence')
|
|
49
|
+
const after = classifyCharacter(code)
|
|
50
|
+
const open = !after || (after === constants.characterGroupPunctuation && Boolean(before))
|
|
51
|
+
|| attentionMarkers.includes(code)
|
|
52
|
+
const commonMarkClose = !before
|
|
53
|
+
|| (before === constants.characterGroupPunctuation && Boolean(after))
|
|
54
|
+
|| attentionMarkers.includes(previous)
|
|
55
|
+
const markerCount = token.end.offset - token.start.offset
|
|
56
|
+
const cjkStrongClose = markerCount >= 2
|
|
57
|
+
&& unicodePunctuation(previous)
|
|
58
|
+
&& isCjkCharacter(code)
|
|
59
|
+
const close = commonMarkClose || cjkStrongClose
|
|
60
|
+
|
|
61
|
+
token._open = open
|
|
62
|
+
token._close = close
|
|
63
|
+
return ok(code)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const cjkFriendlyAttention: Construct = {
|
|
68
|
+
name: 'cjkFriendlyAttention',
|
|
69
|
+
resolveAll: attention.resolveAll,
|
|
70
|
+
tokenize: tokenizeCjkFriendlyAttention,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const cjkFriendlyStrongExtension: Extension = {
|
|
74
|
+
text: { [codes.asterisk]: cjkFriendlyAttention },
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Extend CommonMark asterisk strong emphasis for punctuation-delimited CJK
|
|
79
|
+
* prose, as a micromark syntax extension for `fromMarkdown`.
|
|
80
|
+
* @returns The micromark syntax extension.
|
|
81
|
+
*/
|
|
82
|
+
export function cjkFriendlyStrong(): Extension {
|
|
83
|
+
return cjkFriendlyStrongExtension
|
|
84
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Adapted from DeepSeek Harness b150a551b8d465e31e418e1b2eaf5e79bbb7d28e (MIT). See THIRD_PARTY_NOTICES.md.
|
|
2
|
+
/**
|
|
3
|
+
* Incremental block-level markdown parsing for an append-only text stream.
|
|
4
|
+
*
|
|
5
|
+
* Re-parsing the whole accumulated document on every streaming chunk is
|
|
6
|
+
* quadratic in the final reply length. CommonMark block parsing is line-based
|
|
7
|
+
* and appended text can only reshape the parse frontier — the last top-level
|
|
8
|
+
* block (a paragraph becoming a setext heading or a table, a list continuing
|
|
9
|
+
* after a blank line, an unclosed fence swallowing lines) — so earlier blocks
|
|
10
|
+
* are final. This parser therefore freezes all but the trailing
|
|
11
|
+
* {@link UNSTABLE_TAIL_BLOCKS} blocks and re-parses only the source tail
|
|
12
|
+
* behind them: each source region is parsed O(1) times over the stream
|
|
13
|
+
* instead of once per chunk.
|
|
14
|
+
*
|
|
15
|
+
* The freeze boundary comes from the parser's own `position` offsets, never
|
|
16
|
+
* from custom source scanning. The cut sits at the *end offset* of the last
|
|
17
|
+
* frozen block (not the next block's start): a following block's start offset
|
|
18
|
+
* excludes up to three spaces of insignificant leading indentation, which is
|
|
19
|
+
* harmless to drop, but cutting at the previous end also keeps the
|
|
20
|
+
* inter-block blank lines in the tail so the sliced source stays verbatim.
|
|
21
|
+
*
|
|
22
|
+
* Known deviation, shared with any prefix-freeze scheme: micromark resolves
|
|
23
|
+
* reference-style links and footnotes document-wide at parse time, so a
|
|
24
|
+
* reference whose definition lands on the other side of the freeze boundary
|
|
25
|
+
* renders literally until the settled full parse self-heals it.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type { Root, RootContent } from 'mdast'
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Trailing blocks kept unstable. Appended text reshapes at most the last
|
|
32
|
+
* block; the second-to-last is retained as safety margin so a freeze decision
|
|
33
|
+
* never has to reason about the parse frontier.
|
|
34
|
+
*/
|
|
35
|
+
const UNSTABLE_TAIL_BLOCKS = 2
|
|
36
|
+
|
|
37
|
+
/** A top-level mdast block plus a render key that is stable across chunks. */
|
|
38
|
+
export interface PositionedBlock {
|
|
39
|
+
/** The parsed block. Positions inside it are relative to its parse slice. */
|
|
40
|
+
readonly node: RootContent
|
|
41
|
+
/**
|
|
42
|
+
* The block's start offset in the full source text. Stable from the frame
|
|
43
|
+
* a block first appears through freezing, so React reconciles rather than
|
|
44
|
+
* remounts when a block crosses the freeze boundary.
|
|
45
|
+
*/
|
|
46
|
+
readonly key: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** One {@link IncrementalMarkdownParser.update} result. */
|
|
50
|
+
export interface IncrementalBlocks {
|
|
51
|
+
/** Blocks that can no longer change; grows monotonically per generation. */
|
|
52
|
+
readonly frozen: readonly PositionedBlock[]
|
|
53
|
+
/** The re-parsed unstable tail (at most {@link UNSTABLE_TAIL_BLOCKS} blocks plus growth). */
|
|
54
|
+
readonly tail: readonly PositionedBlock[]
|
|
55
|
+
/** Bumped whenever non-append input discards the frozen prefix; callers drop caches keyed on it. */
|
|
56
|
+
readonly generation: number
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A block's render key: its absolute source start offset. A position-less
|
|
61
|
+
* node (a grammar is free to omit positions) falls back to a negative
|
|
62
|
+
* list-index key — unique within one update's tail, which is the only place
|
|
63
|
+
* the fallback can occur: freezing requires the cut block's position, so a
|
|
64
|
+
* position-less parse keeps every block in the tail (real grammars always
|
|
65
|
+
* stamp positions and never take this path).
|
|
66
|
+
*/
|
|
67
|
+
function blockKey(node: RootContent, base: number, index: number): number {
|
|
68
|
+
const offset = node.position?.start.offset
|
|
69
|
+
return offset === undefined ? -(index + 1) : base + offset
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Append-only incremental parser over a caller-supplied grammar. One instance
|
|
74
|
+
* accumulates one streaming document; non-append input resets it.
|
|
75
|
+
*/
|
|
76
|
+
export class IncrementalMarkdownParser {
|
|
77
|
+
private prevText = ''
|
|
78
|
+
private tailStart = 0
|
|
79
|
+
private frozen: PositionedBlock[] = []
|
|
80
|
+
private generation = 0
|
|
81
|
+
private cached: IncrementalBlocks | null = null
|
|
82
|
+
|
|
83
|
+
/** @param parse - Grammar shared with whatever renders the blocks, so boundaries agree. */
|
|
84
|
+
constructor(private readonly parse: (text: string) => Root) {}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Fold the current accumulated text and return the frozen/tail split.
|
|
88
|
+
* Idempotent for identical input (the previous result is returned as-is),
|
|
89
|
+
* so callers may invoke it from render paths that re-execute.
|
|
90
|
+
* @param text - The full accumulated markdown source.
|
|
91
|
+
* @returns Frozen and tail blocks with stream-stable render keys.
|
|
92
|
+
*/
|
|
93
|
+
update(text: string): IncrementalBlocks {
|
|
94
|
+
if (this.cached !== null && text === this.prevText) return this.cached
|
|
95
|
+
// Deliberate O(prefix) memcmp per update: sound divergence detection has
|
|
96
|
+
// to verify the whole retained prefix, and startsWith compares bytes two
|
|
97
|
+
// orders of magnitude faster than parsing them — the cost this class
|
|
98
|
+
// exists to remove. Passing append/reset deltas instead would push
|
|
99
|
+
// append bookkeeping across the session-projection update boundary for a check
|
|
100
|
+
// that stays sub-millisecond at realistic reply sizes.
|
|
101
|
+
if (!text.startsWith(this.prevText)) {
|
|
102
|
+
this.prevText = ''
|
|
103
|
+
this.tailStart = 0
|
|
104
|
+
this.frozen = []
|
|
105
|
+
this.generation += 1
|
|
106
|
+
}
|
|
107
|
+
this.prevText = text
|
|
108
|
+
const base = this.tailStart
|
|
109
|
+
const blocks = this.parse(text.slice(base)).children
|
|
110
|
+
let firstUnstable = Math.max(0, blocks.length - UNSTABLE_TAIL_BLOCKS)
|
|
111
|
+
if (firstUnstable > 0) {
|
|
112
|
+
const cutEnd = blocks[firstUnstable - 1]?.position?.end.offset
|
|
113
|
+
if (cutEnd === undefined) {
|
|
114
|
+
// A grammar that omits positions leaves nothing to cut at; keep the
|
|
115
|
+
// whole parse in the tail rather than guessing a boundary.
|
|
116
|
+
firstUnstable = 0
|
|
117
|
+
} else {
|
|
118
|
+
for (const node of blocks.slice(0, firstUnstable)) {
|
|
119
|
+
this.frozen.push({ node, key: blockKey(node, base, this.frozen.length) })
|
|
120
|
+
}
|
|
121
|
+
this.tailStart = base + cutEnd
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const tail = blocks.slice(firstUnstable).map((node, index) => ({
|
|
125
|
+
node,
|
|
126
|
+
key: blockKey(node, base, index),
|
|
127
|
+
}))
|
|
128
|
+
this.cached = { frozen: [...this.frozen], tail, generation: this.generation }
|
|
129
|
+
return this.cached
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Adapted from DeepSeek Harness b150a551b8d465e31e418e1b2eaf5e79bbb7d28e (MIT). See THIRD_PARTY_NOTICES.md.
|
|
2
|
+
/**
|
|
3
|
+
* TeX-to-React via KaTeX, replicating the rehype-katex pipeline this renderer
|
|
4
|
+
* replaced: the same three-arm error chain (strict render, `strict: 'ignore'`
|
|
5
|
+
* retry, error span) and a DOM-identical element tree, so settled math keeps
|
|
6
|
+
* its exact markup. KaTeX emits an HTML string; the browser's own HTML parser
|
|
7
|
+
* (`DOMParser`, applying the spec's SVG/MathML foreign-content attribute
|
|
8
|
+
* adjustments KaTeX output relies on) turns it into a tree this module maps
|
|
9
|
+
* onto React elements — KaTeX output is a static span/MathML/SVG vocabulary
|
|
10
|
+
* with no raw user HTML, the same trust shiki's tree gets in CodeBlock.
|
|
11
|
+
*
|
|
12
|
+
* React 18 has no MathML support, so the `.katex-mathml` subtree's elements
|
|
13
|
+
* land in the HTML namespace — exactly as they did under the replaced
|
|
14
|
+
* hast-util-to-jsx-runtime pipeline. The visual arm is the `.katex-html`
|
|
15
|
+
* span tree; the MathML arm serves assistive technology, which reads it by
|
|
16
|
+
* tag name regardless of namespace.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { createElement } from 'react'
|
|
20
|
+
import type { CSSProperties, ReactNode } from 'react'
|
|
21
|
+
import katex from 'katex'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Convert one inline `style` attribute string into React's style object.
|
|
25
|
+
* KaTeX emits only plain kebab-case declarations (no custom properties and no
|
|
26
|
+
* nameless declarations), so camel-casing the property is the whole mapping.
|
|
27
|
+
*/
|
|
28
|
+
function styleObject(css: string): CSSProperties {
|
|
29
|
+
const style: Record<string, string> = {}
|
|
30
|
+
for (const declaration of css.split(';')) {
|
|
31
|
+
const colon = declaration.indexOf(':')
|
|
32
|
+
if (colon === -1) continue
|
|
33
|
+
const name = declaration.slice(0, colon).trim()
|
|
34
|
+
const key = name.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase())
|
|
35
|
+
style[key] = declaration.slice(colon + 1).trim()
|
|
36
|
+
}
|
|
37
|
+
return style
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Map one parsed DOM node onto a React element (text nodes pass through). */
|
|
41
|
+
function domToReact(node: ChildNode, key: number): ReactNode {
|
|
42
|
+
if (node.nodeType === Node.TEXT_NODE) return node.textContent
|
|
43
|
+
/* v8 ignore next 2 -- KaTeX output holds only elements and text; other
|
|
44
|
+
node kinds cannot appear in its serialized vocabulary. */
|
|
45
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return null
|
|
46
|
+
const element = node as Element
|
|
47
|
+
const props: Record<string, unknown> = { key }
|
|
48
|
+
for (const attribute of element.attributes) {
|
|
49
|
+
if (attribute.name === 'class') props['className'] = attribute.value
|
|
50
|
+
else if (attribute.name === 'style') props['style'] = styleObject(attribute.value)
|
|
51
|
+
else props[attribute.name] = attribute.value
|
|
52
|
+
}
|
|
53
|
+
const children = [...element.childNodes].map(domToReact)
|
|
54
|
+
return children.length === 0
|
|
55
|
+
? createElement(element.localName, props)
|
|
56
|
+
: createElement(element.localName, props, ...children)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Render TeX source to React elements through KaTeX.
|
|
61
|
+
* @param value - The TeX source (math node value; fenced `math` blocks append
|
|
62
|
+
* their trailing newline to match the replaced pipeline's text extraction).
|
|
63
|
+
* @param displayMode - Display (block) versus inline rendering.
|
|
64
|
+
* @returns KaTeX's element tree, or the error span when the source does not
|
|
65
|
+
* parse (colored with KaTeX's stock `errorColor`, matching rehype-katex).
|
|
66
|
+
*/
|
|
67
|
+
export function renderTexToReact(value: string, displayMode: boolean): ReactNode {
|
|
68
|
+
let html: string
|
|
69
|
+
try {
|
|
70
|
+
html = katex.renderToString(value, { displayMode, throwOnError: true })
|
|
71
|
+
} catch (error) {
|
|
72
|
+
try {
|
|
73
|
+
html = katex.renderToString(value, { displayMode, strict: 'ignore', throwOnError: false })
|
|
74
|
+
} catch {
|
|
75
|
+
// KaTeX renders ParseErrors itself under throwOnError: false; only its
|
|
76
|
+
// internal errors reach here, so mirror rehype-katex's manual span.
|
|
77
|
+
/* v8 ignore next 8 */
|
|
78
|
+
return (
|
|
79
|
+
<span
|
|
80
|
+
className="katex-error"
|
|
81
|
+
style={{ color: '#cc0000' }}
|
|
82
|
+
title={String(error)}
|
|
83
|
+
>
|
|
84
|
+
{value}
|
|
85
|
+
</span>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const parsed = new DOMParser().parseFromString(html, 'text/html')
|
|
90
|
+
return [...parsed.body.childNodes].map(domToReact)
|
|
91
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
// Adapted from DeepSeek Harness b150a551b8d465e31e418e1b2eaf5e79bbb7d28e (MIT). See THIRD_PARTY_NOTICES.md.
|
|
2
|
+
/** Extend upstream dollar-only math syntax with TeX delimiters while reusing its token vocabulary. */
|
|
3
|
+
|
|
4
|
+
import { factorySpace } from 'micromark-factory-space'
|
|
5
|
+
import type {} from 'micromark-extension-math'
|
|
6
|
+
import { markdownLineEnding } from 'micromark-util-character'
|
|
7
|
+
import { codes, constants, types } from 'micromark-util-symbol'
|
|
8
|
+
import type { Construct, Extension, Previous, State, Tokenizer } from 'micromark-util-types'
|
|
9
|
+
|
|
10
|
+
// oxlint-disable typescript/no-this-alias -- micromark binds tokenizer context only on the outer callback.
|
|
11
|
+
|
|
12
|
+
const previousBackslash: Previous = function (code) {
|
|
13
|
+
if (code !== codes.backslash) return true
|
|
14
|
+
const tail = this.events.at(-1)
|
|
15
|
+
/* v8 ignore next -- a previous code necessarily has a preceding event. */
|
|
16
|
+
if (tail === undefined) return false
|
|
17
|
+
return tail[1].type === types.characterEscape
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const tokenizeBackslashMathText: Tokenizer = function (effects, ok, nok) {
|
|
21
|
+
return start
|
|
22
|
+
|
|
23
|
+
function start(code: number | null): State | undefined {
|
|
24
|
+
/* v8 ignore next -- the text construct is dispatched only for a backslash. */
|
|
25
|
+
if (code !== codes.backslash) return nok(code)
|
|
26
|
+
effects.enter('mathText')
|
|
27
|
+
effects.enter('mathTextSequence')
|
|
28
|
+
effects.consume(code)
|
|
29
|
+
return open
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function open(code: number | null): State | undefined {
|
|
33
|
+
if (code !== codes.leftParenthesis) return nok(code)
|
|
34
|
+
effects.consume(code)
|
|
35
|
+
effects.exit('mathTextSequence')
|
|
36
|
+
return between
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function between(code: number | null): State | undefined {
|
|
40
|
+
if (code === codes.eof) return nok(code)
|
|
41
|
+
if (code === codes.backslash) {
|
|
42
|
+
return effects.attempt({ partial: true, tokenize: tokenizeClose }, close, afterCloseAttempt)(code)
|
|
43
|
+
}
|
|
44
|
+
if (markdownLineEnding(code)) {
|
|
45
|
+
effects.enter(types.lineEnding)
|
|
46
|
+
effects.consume(code)
|
|
47
|
+
effects.exit(types.lineEnding)
|
|
48
|
+
return between
|
|
49
|
+
}
|
|
50
|
+
return dataStart(code)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function afterCloseAttempt(code: number | null): State | undefined {
|
|
54
|
+
return effects.check({ partial: true, tokenize: tokenizeOpen }, nok, dataStart)(code)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function dataStart(code: number | null): State | undefined {
|
|
58
|
+
effects.enter('mathTextData')
|
|
59
|
+
effects.consume(code)
|
|
60
|
+
return code === codes.backslash ? afterDataBackslash : data
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function afterDataBackslash(code: number | null): State | undefined {
|
|
64
|
+
if (code === codes.backslash) {
|
|
65
|
+
effects.consume(code)
|
|
66
|
+
return data
|
|
67
|
+
}
|
|
68
|
+
return data(code)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function data(code: number | null): State | undefined {
|
|
72
|
+
if (code === codes.eof || code === codes.backslash || markdownLineEnding(code)) {
|
|
73
|
+
effects.exit('mathTextData')
|
|
74
|
+
return between(code)
|
|
75
|
+
}
|
|
76
|
+
effects.consume(code)
|
|
77
|
+
return data
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function close(code: number | null): State | undefined {
|
|
81
|
+
effects.exit('mathText')
|
|
82
|
+
return ok(code)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function tokenizeClose(closeEffects: Parameters<Tokenizer>[0], closeOk: State, closeNok: State): State {
|
|
86
|
+
return slash
|
|
87
|
+
|
|
88
|
+
function slash(code: number | null): State | undefined {
|
|
89
|
+
/* v8 ignore next -- this partial construct is attempted only at a backslash. */
|
|
90
|
+
if (code !== codes.backslash) return closeNok(code)
|
|
91
|
+
closeEffects.enter('mathTextSequence')
|
|
92
|
+
closeEffects.consume(code)
|
|
93
|
+
return parenthesis
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function parenthesis(code: number | null): State | undefined {
|
|
97
|
+
if (code !== codes.rightParenthesis) return closeNok(code)
|
|
98
|
+
closeEffects.consume(code)
|
|
99
|
+
closeEffects.exit('mathTextSequence')
|
|
100
|
+
return closeOk
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function tokenizeOpen(openEffects: Parameters<Tokenizer>[0], openOk: State, openNok: State): State {
|
|
105
|
+
return slash
|
|
106
|
+
|
|
107
|
+
function slash(code: number | null): State | undefined {
|
|
108
|
+
/* v8 ignore next -- the opening check follows a failed close attempt at a backslash. */
|
|
109
|
+
if (code !== codes.backslash) return openNok(code)
|
|
110
|
+
openEffects.enter(types.chunkString)
|
|
111
|
+
openEffects.consume(code)
|
|
112
|
+
return parenthesis
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function parenthesis(code: number | null): State | undefined {
|
|
116
|
+
if (code !== codes.leftParenthesis) return openNok(code)
|
|
117
|
+
openEffects.consume(code)
|
|
118
|
+
openEffects.exit(types.chunkString)
|
|
119
|
+
return openOk
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function createMathFlow(marker: number, openMarker: number, closeMarker: number, multiline: boolean): Construct {
|
|
125
|
+
const tokenize: Tokenizer = function (effects, ok, nok) {
|
|
126
|
+
const self = this
|
|
127
|
+
let oddBackslashRun = false
|
|
128
|
+
const tail = self.events.at(-1)
|
|
129
|
+
const initialSize = tail?.[1].type === types.linePrefix
|
|
130
|
+
? tail[2].sliceSerialize(tail[1], true).length
|
|
131
|
+
: 0
|
|
132
|
+
|
|
133
|
+
return start
|
|
134
|
+
|
|
135
|
+
function start(code: number | null): State | undefined {
|
|
136
|
+
/* v8 ignore next -- the flow construct is dispatched only for its marker. */
|
|
137
|
+
if (code !== marker) return nok(code)
|
|
138
|
+
effects.enter('mathFlow')
|
|
139
|
+
effects.enter('mathFlowFence')
|
|
140
|
+
effects.enter('mathFlowFenceSequence')
|
|
141
|
+
effects.consume(code)
|
|
142
|
+
return open
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function open(code: number | null): State | undefined {
|
|
146
|
+
if (code !== openMarker) return nok(code)
|
|
147
|
+
effects.consume(code)
|
|
148
|
+
effects.exit('mathFlowFenceSequence')
|
|
149
|
+
effects.exit('mathFlowFence')
|
|
150
|
+
return marker === codes.dollarSign ? afterDollarOpen : content
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function afterDollarOpen(code: number | null): State | undefined {
|
|
154
|
+
return code === codes.dollarSign ? nok(code) : content(code)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function content(code: number | null): State | undefined {
|
|
158
|
+
if (code === codes.eof) return nok(code)
|
|
159
|
+
if (code === marker && (marker !== codes.dollarSign || !oddBackslashRun)) {
|
|
160
|
+
return effects.attempt(
|
|
161
|
+
{ partial: true, tokenize: tokenizeClosingFence },
|
|
162
|
+
closed,
|
|
163
|
+
afterClosingFenceAttempt,
|
|
164
|
+
)(code)
|
|
165
|
+
}
|
|
166
|
+
if (markdownLineEnding(code)) {
|
|
167
|
+
return multiline
|
|
168
|
+
? effects.attempt(nonLazyContinuation, afterContinuation, nok)(code)
|
|
169
|
+
: nok(code)
|
|
170
|
+
}
|
|
171
|
+
return valueStart(code)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function afterClosingFenceAttempt(code: number | null): State | undefined {
|
|
175
|
+
return marker === codes.backslash
|
|
176
|
+
? effects.check({ partial: true, tokenize: tokenizeOpeningFence }, nok, markerValueStart)(code)
|
|
177
|
+
: markerValueStart(code)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function afterContinuation(code: number | null): State | undefined {
|
|
181
|
+
return effects.attempt(
|
|
182
|
+
{ partial: true, tokenize: tokenizeClosingFence },
|
|
183
|
+
closed,
|
|
184
|
+
initialSize
|
|
185
|
+
? factorySpace(effects, content, types.linePrefix, initialSize + 1)
|
|
186
|
+
: content,
|
|
187
|
+
)(code)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function valueStart(code: number | null): State | undefined {
|
|
191
|
+
effects.enter('mathFlowValue')
|
|
192
|
+
oddBackslashRun = code === codes.backslash
|
|
193
|
+
effects.consume(code)
|
|
194
|
+
return value
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function markerValueStart(code: number | null): State | undefined {
|
|
198
|
+
effects.enter('mathFlowValue')
|
|
199
|
+
oddBackslashRun = false
|
|
200
|
+
effects.consume(code)
|
|
201
|
+
return valueAfterMarker
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function valueAfterMarker(code: number | null): State | undefined {
|
|
205
|
+
if (code === marker) {
|
|
206
|
+
effects.consume(code)
|
|
207
|
+
return value
|
|
208
|
+
}
|
|
209
|
+
return value(code)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function value(code: number | null): State | undefined {
|
|
213
|
+
if (code === codes.eof || code === marker || markdownLineEnding(code)) {
|
|
214
|
+
effects.exit('mathFlowValue')
|
|
215
|
+
return content(code)
|
|
216
|
+
}
|
|
217
|
+
oddBackslashRun = code === codes.backslash ? !oddBackslashRun : false
|
|
218
|
+
effects.consume(code)
|
|
219
|
+
return value
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function closed(code: number | null): State | undefined {
|
|
223
|
+
effects.exit('mathFlow')
|
|
224
|
+
return ok(code)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function tokenizeClosingFence(
|
|
228
|
+
closeEffects: Parameters<Tokenizer>[0],
|
|
229
|
+
closeOk: State,
|
|
230
|
+
closeNok: State,
|
|
231
|
+
): State {
|
|
232
|
+
return factorySpace(closeEffects, sequenceStart, types.linePrefix, constants.tabSize)
|
|
233
|
+
|
|
234
|
+
function sequenceStart(code: number | null): State | undefined {
|
|
235
|
+
if (code !== marker) return closeNok(code)
|
|
236
|
+
closeEffects.enter('mathFlowFence')
|
|
237
|
+
closeEffects.enter('mathFlowFenceSequence')
|
|
238
|
+
closeEffects.consume(code)
|
|
239
|
+
return sequenceEnd
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function sequenceEnd(code: number | null): State | undefined {
|
|
243
|
+
if (code !== closeMarker) return closeNok(code)
|
|
244
|
+
closeEffects.consume(code)
|
|
245
|
+
closeEffects.exit('mathFlowFenceSequence')
|
|
246
|
+
return factorySpace(closeEffects, after, types.whitespace)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function after(code: number | null): State | undefined {
|
|
250
|
+
if (code !== codes.eof && !markdownLineEnding(code)) return closeNok(code)
|
|
251
|
+
closeEffects.exit('mathFlowFence')
|
|
252
|
+
return closeOk(code)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function tokenizeOpeningFence(
|
|
257
|
+
openEffects: Parameters<Tokenizer>[0],
|
|
258
|
+
openOk: State,
|
|
259
|
+
openNok: State,
|
|
260
|
+
): State {
|
|
261
|
+
return sequenceStart
|
|
262
|
+
|
|
263
|
+
function sequenceStart(code: number | null): State | undefined {
|
|
264
|
+
/* v8 ignore next -- the opening check follows a failed close attempt at the marker. */
|
|
265
|
+
if (code !== marker) return openNok(code)
|
|
266
|
+
openEffects.enter(types.chunkString)
|
|
267
|
+
openEffects.consume(code)
|
|
268
|
+
return sequenceEnd
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function sequenceEnd(code: number | null): State | undefined {
|
|
272
|
+
if (code !== openMarker) return openNok(code)
|
|
273
|
+
openEffects.consume(code)
|
|
274
|
+
openEffects.exit(types.chunkString)
|
|
275
|
+
return openOk
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
concrete: true,
|
|
282
|
+
name: marker === codes.dollarSign ? 'sameLineDollarMathFlow' : 'backslashMathFlow',
|
|
283
|
+
tokenize,
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const tokenizeNonLazyContinuation: Tokenizer = function (effects, ok, nok) {
|
|
288
|
+
const self = this
|
|
289
|
+
|
|
290
|
+
return start
|
|
291
|
+
|
|
292
|
+
function start(code: number | null): State | undefined {
|
|
293
|
+
/* v8 ignore next -- continuation constructs are attempted only after a line ending. */
|
|
294
|
+
if (code === codes.eof) return ok(code)
|
|
295
|
+
/* v8 ignore next -- continuation constructs are attempted only after a line ending. */
|
|
296
|
+
if (!markdownLineEnding(code)) return nok(code)
|
|
297
|
+
effects.enter(types.lineEnding)
|
|
298
|
+
effects.consume(code)
|
|
299
|
+
effects.exit(types.lineEnding)
|
|
300
|
+
return lineStart
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function lineStart(code: number | null): State | undefined {
|
|
304
|
+
return self.parser.lazy[self.now().line] ? nok(code) : ok(code)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const nonLazyContinuation: Construct = {
|
|
309
|
+
partial: true,
|
|
310
|
+
tokenize: tokenizeNonLazyContinuation,
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const backslashMathText: Construct = {
|
|
314
|
+
name: 'backslashMathText',
|
|
315
|
+
previous: previousBackslash,
|
|
316
|
+
tokenize: tokenizeBackslashMathText,
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const backslashMathFlow = createMathFlow(
|
|
320
|
+
codes.backslash,
|
|
321
|
+
codes.leftSquareBracket,
|
|
322
|
+
codes.rightSquareBracket,
|
|
323
|
+
true,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
const sameLineDollarMathFlow = createMathFlow(
|
|
327
|
+
codes.dollarSign,
|
|
328
|
+
codes.dollarSign,
|
|
329
|
+
codes.dollarSign,
|
|
330
|
+
false,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
const backslashMath: Extension = {
|
|
334
|
+
flow: {
|
|
335
|
+
[codes.backslash]: backslashMathFlow,
|
|
336
|
+
[codes.dollarSign]: sameLineDollarMathFlow,
|
|
337
|
+
},
|
|
338
|
+
text: { [codes.backslash]: backslashMathText },
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* TeX backslash delimiters and same-line display-dollar blocks as a micromark
|
|
343
|
+
* syntax extension reusing `micromark-extension-math`'s token vocabulary; the
|
|
344
|
+
* caller must also register `math()` on the same parse so the emitted tokens
|
|
345
|
+
* compile to standard math nodes.
|
|
346
|
+
* @returns The micromark syntax extension.
|
|
347
|
+
*/
|
|
348
|
+
export function mathCompatibility(): Extension {
|
|
349
|
+
return backslashMath
|
|
350
|
+
}
|