feat(primitives): inline-markdown detection for editor v2 (#1594)#1602
Open
ssilvius wants to merge 3 commits into
Open
feat(primitives): inline-markdown detection for editor v2 (#1594)#1602ssilvius wants to merge 3 commits into
ssilvius wants to merge 3 commits into
Conversation
Pure function that detects inline markdown syntax (bold, italic, code, strikethrough, link) at a cursor position and returns match offsets for the overlay editor to replace syntax with formatted InlineContent. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace extractText/extractHref callbacks with textGroup/hrefGroup indices (data over behavior), and early-return on first match instead of accumulating closest candidate across all patterns. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…intraword underscores Italic * pattern now requires the delimiter to not be adjacent to another * (prevents matching inside ** bold syntax). Underscore italic split into a separate pattern with word-boundary constraints matching CommonMark behavior (foo_bar_baz no longer triggers italic). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
detectInlineMarkdown, a pure function primitive that detects inline markdown syntax at a cursor position and returns the match offsets, extracted text, and mark type. This is the foundation for the editor v2 overlay -- when a user types**hello**, the function detects the closing delimiter and returns the data needed to replace the raw syntax with formatted InlineContent. Zero dependencies, no DOM access.Acceptance criteria mapping
1. All functional tests pass
18 tests across 6 describe blocks (bold, italic, code, strikethrough, link, edge cases) all pass in vitest. Each pattern has at least one positive-match test asserting the full return shape (startOffset, endOffset, text, marks). Edge cases cover: no match at cursor, empty delimiters, empty string, escaped opening/closing delimiters, match at string boundaries, unmatched openers, cursor not at match end.
Evidence:
pnpm vitest run packages/ui/src/primitives/inline-markdown.test.ts-- 18/18 pass2. TypeScript compiles without errors
pnpm typecheckpasses clean across all workspace packages. The implementation importsInlineMarkfrom./typesand exportsInlineMarkdownMatch(interface) anddetectInlineMarkdown(function). Capture group indexing usesm[def.textGroup] ?? ''to satisfy strict null checks onRegExpMatchArrayelement access.Evidence:
pnpm typecheck-- zero errors3. All 5 inline patterns detected correctly
Five patterns defined in the
PATTERNSarray atinline-markdown.ts:18-47: link ([text](url)with href extraction), bold (**and__via backreference), strikethrough (~~), code (`), italic (*and_via backreference). Pattern order matters -- link is first (most specific), bold before italic (prevents**from matching as two italic delimiters). Each pattern is a data object withmark,pattern,textGroup, and optionalhrefGroup.Evidence: tests "detects text", "detects text", "detects text", "detects text", "detects
text", "detectstext", "detects text"4. Edge cases handled (escaped, empty, partial)
Escaped delimiters use negative lookbehind
(?<!\\)on both opening and closing positions -- tested by "ignores escaped opening delimiter" (\*not italic*returns null) and "ignores escaped closing delimiter" (*not italic\*returns null). Empty content between delimiters (****) returns null because the extracted text is empty-string-checked atinline-markdown.ts:62. Partial/unmatched delimiters (**no close) return null because the regex requires both opening and closing. Cursor position is strictly enforced:matchEnd !== cursorOffseton line 58 means detection only fires when the cursor is immediately after the closing delimiter.Evidence: tests "returns null for empty content between delimiters", "ignores escaped opening delimiter", "ignores escaped closing delimiter", "returns null for unmatched opening delimiter", "cursor must be at end of match"
5. Exports added to index files
InlineMarkdownMatch(type export) anddetectInlineMarkdown(value export) added topackages/ui/src/index.ts:17-18, sorted before the serializer exports per biome's import ordering rules.Evidence:
packages/ui/src/index.ts:17-18Not done
InlineMarkdownOptionswithonMatchcallback (specified in the issue's interface sketch) -- not implemented because the pure function signature(content, cursorOffset) => match | nullis the complete contract. The callback wrapper belongs to the editor integration (feat(primitives): document-editor -- integrate inline-markdown for overlay rendering #1595) which owns the keystroke loop.***bold italic***) -- explicitly out of scope per the issue.\\**should be literal backslash + bold opener) -- the lookbehind only checks one preceding backslash. Vanishingly rare in hand-typed editor content; worth a follow-up if real users hit it.