diff --git a/dist/index.cjs b/dist/index.cjs --- a/dist/index.cjs +++ b/dist/index.cjs @@ -245,10 +245,9 @@ ]; const ALPHA_NUMERALS = "abcdefghijklmnopqrstuvwxyz"; /** -* Marker segment for ordered list lines: numeric, roman, or 1–2 letter alpha. -* Roman is matched before alpha so "iii" is roman; invalid romans like "aa" fall through to alpha. +* Orca patch: ordered-list markers are digits only, as in CommonMark, so "Dr." and "A." stay prose. */ -const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+|[ivxlcdmIVXLCDM]+|${"[a-zA-Z]{1,2}"}`; +const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+`; /** * Convert a number to lowercase roman numerals. * @example toRoman(1) // 'i' @@ -897,6 +896,26 @@ }; } /** +* Orca patch: marker columns are counted without the delimiter, so trim() left later sibling tasks indented and +* parseIndentedBlocks cut into them. Dedent by the first task; a task one column deeper is a sibling, and other +* text at or within the first task's column stays nested under the task above. +*/ +function dedentOrderedItemTaskBlock(blockLines) { + const isTaskLine = (line) => /^\s*[-+*]\s+\[[ xX]\][^\S\r\n]/.test(line); + const leadingWidth = (line) => line.length - line.trimStart().length; + const firstIndex = blockLines.findIndex((line) => line.trim() !== ""); + if (firstIndex === -1 || !isTaskLine(blockLines[firstIndex])) return null; + const firstIndent = leadingWidth(blockLines[firstIndex]); + return blockLines.slice(firstIndex).map((line) => { + if (line.trim() === "") return ""; + const indent = leadingWidth(line); + const depth = indent - firstIndent; + if (isTaskLine(line)) return depth === 1 ? line.trimStart() : line.slice(Math.min(indent, firstIndent)); + if (depth > 0) return depth === 1 ? ` ${line.slice(firstIndent)}` : line.slice(firstIndent); + return indent > 0 ? ` ${line.trimStart()}` : line; + }).join("\n").trimEnd(); +} +/** * Recursively builds a nested structure from a flat array of list items * based on their indentation levels. Creates proper markdown tokens with * nested lists where appropriate. @@ -920,7 +939,7 @@ raw: mainText, tokens: lexer.inlineTokens(mainText) }); - const additionalContent = blockLines.join("\n").trim(); + const additionalContent = dedentOrderedItemTaskBlock(blockLines) ?? blockLines.join("\n").trim(); if (additionalContent) { const blockTokens = lexer.blockTokens(additionalContent); tokens.push(...blockTokens); diff --git a/dist/index.js b/dist/index.js --- a/dist/index.js +++ b/dist/index.js @@ -233,10 +233,9 @@ ]; const ALPHA_NUMERALS = "abcdefghijklmnopqrstuvwxyz"; /** -* Marker segment for ordered list lines: numeric, roman, or 1–2 letter alpha. -* Roman is matched before alpha so "iii" is roman; invalid romans like "aa" fall through to alpha. +* Orca patch: ordered-list markers are digits only, as in CommonMark, so "Dr." and "A." stay prose. */ -const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+|[ivxlcdmIVXLCDM]+|${"[a-zA-Z]{1,2}"}`; +const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+`; /** * Convert a number to lowercase roman numerals. * @example toRoman(1) // 'i' @@ -885,6 +884,26 @@ }; } /** +* Orca patch: marker columns are counted without the delimiter, so trim() left later sibling tasks indented and +* parseIndentedBlocks cut into them. Dedent by the first task; a task one column deeper is a sibling, and other +* text at or within the first task's column stays nested under the task above. +*/ +function dedentOrderedItemTaskBlock(blockLines) { + const isTaskLine = (line) => /^\s*[-+*]\s+\[[ xX]\][^\S\r\n]/.test(line); + const leadingWidth = (line) => line.length - line.trimStart().length; + const firstIndex = blockLines.findIndex((line) => line.trim() !== ""); + if (firstIndex === -1 || !isTaskLine(blockLines[firstIndex])) return null; + const firstIndent = leadingWidth(blockLines[firstIndex]); + return blockLines.slice(firstIndex).map((line) => { + if (line.trim() === "") return ""; + const indent = leadingWidth(line); + const depth = indent - firstIndent; + if (isTaskLine(line)) return depth === 1 ? line.trimStart() : line.slice(Math.min(indent, firstIndent)); + if (depth > 0) return depth === 1 ? ` ${line.slice(firstIndent)}` : line.slice(firstIndent); + return indent > 0 ? ` ${line.trimStart()}` : line; + }).join("\n").trimEnd(); +} +/** * Recursively builds a nested structure from a flat array of list items * based on their indentation levels. Creates proper markdown tokens with * nested lists where appropriate. @@ -908,7 +927,7 @@ raw: mainText, tokens: lexer.inlineTokens(mainText) }); - const additionalContent = blockLines.join("\n").trim(); + const additionalContent = dedentOrderedItemTaskBlock(blockLines) ?? blockLines.join("\n").trim(); if (additionalContent) { const blockTokens = lexer.blockTokens(additionalContent); tokens.push(...blockTokens); diff --git a/dist/kit/index.cjs b/dist/kit/index.cjs --- a/dist/kit/index.cjs +++ b/dist/kit/index.cjs @@ -233,10 +233,9 @@ ]; const ALPHA_NUMERALS = "abcdefghijklmnopqrstuvwxyz"; /** -* Marker segment for ordered list lines: numeric, roman, or 1–2 letter alpha. -* Roman is matched before alpha so "iii" is roman; invalid romans like "aa" fall through to alpha. +* Orca patch: ordered-list markers are digits only, as in CommonMark, so "Dr." and "A." stay prose. */ -const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+|[ivxlcdmIVXLCDM]+|${"[a-zA-Z]{1,2}"}`; +const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+`; /** * Convert a number to lowercase roman numerals. * @example toRoman(1) // 'i' @@ -839,6 +838,26 @@ }; } /** +* Orca patch: marker columns are counted without the delimiter, so trim() left later sibling tasks indented and +* parseIndentedBlocks cut into them. Dedent by the first task; a task one column deeper is a sibling, and other +* text at or within the first task's column stays nested under the task above. +*/ +function dedentOrderedItemTaskBlock(blockLines) { + const isTaskLine = (line) => /^\s*[-+*]\s+\[[ xX]\][^\S\r\n]/.test(line); + const leadingWidth = (line) => line.length - line.trimStart().length; + const firstIndex = blockLines.findIndex((line) => line.trim() !== ""); + if (firstIndex === -1 || !isTaskLine(blockLines[firstIndex])) return null; + const firstIndent = leadingWidth(blockLines[firstIndex]); + return blockLines.slice(firstIndex).map((line) => { + if (line.trim() === "") return ""; + const indent = leadingWidth(line); + const depth = indent - firstIndent; + if (isTaskLine(line)) return depth === 1 ? line.trimStart() : line.slice(Math.min(indent, firstIndent)); + if (depth > 0) return depth === 1 ? ` ${line.slice(firstIndent)}` : line.slice(firstIndent); + return indent > 0 ? ` ${line.trimStart()}` : line; + }).join("\n").trimEnd(); +} +/** * Recursively builds a nested structure from a flat array of list items * based on their indentation levels. Creates proper markdown tokens with * nested lists where appropriate. @@ -862,7 +881,7 @@ raw: mainText, tokens: lexer.inlineTokens(mainText) }); - const additionalContent = blockLines.join("\n").trim(); + const additionalContent = dedentOrderedItemTaskBlock(blockLines) ?? blockLines.join("\n").trim(); if (additionalContent) { const blockTokens = lexer.blockTokens(additionalContent); tokens.push(...blockTokens); diff --git a/dist/kit/index.js b/dist/kit/index.js --- a/dist/kit/index.js +++ b/dist/kit/index.js @@ -232,10 +232,9 @@ ]; const ALPHA_NUMERALS = "abcdefghijklmnopqrstuvwxyz"; /** -* Marker segment for ordered list lines: numeric, roman, or 1–2 letter alpha. -* Roman is matched before alpha so "iii" is roman; invalid romans like "aa" fall through to alpha. +* Orca patch: ordered-list markers are digits only, as in CommonMark, so "Dr." and "A." stay prose. */ -const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+|[ivxlcdmIVXLCDM]+|${"[a-zA-Z]{1,2}"}`; +const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+`; /** * Convert a number to lowercase roman numerals. * @example toRoman(1) // 'i' @@ -838,6 +837,26 @@ }; } /** +* Orca patch: marker columns are counted without the delimiter, so trim() left later sibling tasks indented and +* parseIndentedBlocks cut into them. Dedent by the first task; a task one column deeper is a sibling, and other +* text at or within the first task's column stays nested under the task above. +*/ +function dedentOrderedItemTaskBlock(blockLines) { + const isTaskLine = (line) => /^\s*[-+*]\s+\[[ xX]\][^\S\r\n]/.test(line); + const leadingWidth = (line) => line.length - line.trimStart().length; + const firstIndex = blockLines.findIndex((line) => line.trim() !== ""); + if (firstIndex === -1 || !isTaskLine(blockLines[firstIndex])) return null; + const firstIndent = leadingWidth(blockLines[firstIndex]); + return blockLines.slice(firstIndex).map((line) => { + if (line.trim() === "") return ""; + const indent = leadingWidth(line); + const depth = indent - firstIndent; + if (isTaskLine(line)) return depth === 1 ? line.trimStart() : line.slice(Math.min(indent, firstIndent)); + if (depth > 0) return depth === 1 ? ` ${line.slice(firstIndent)}` : line.slice(firstIndent); + return indent > 0 ? ` ${line.trimStart()}` : line; + }).join("\n").trimEnd(); +} +/** * Recursively builds a nested structure from a flat array of list items * based on their indentation levels. Creates proper markdown tokens with * nested lists where appropriate. @@ -861,7 +880,7 @@ raw: mainText, tokens: lexer.inlineTokens(mainText) }); - const additionalContent = blockLines.join("\n").trim(); + const additionalContent = dedentOrderedItemTaskBlock(blockLines) ?? blockLines.join("\n").trim(); if (additionalContent) { const blockTokens = lexer.blockTokens(additionalContent); tokens.push(...blockTokens); diff --git a/dist/ordered-list/index.cjs b/dist/ordered-list/index.cjs --- a/dist/ordered-list/index.cjs +++ b/dist/ordered-list/index.cjs @@ -19,10 +19,9 @@ ]; const ALPHA_NUMERALS = "abcdefghijklmnopqrstuvwxyz"; /** -* Marker segment for ordered list lines: numeric, roman, or 1–2 letter alpha. -* Roman is matched before alpha so "iii" is roman; invalid romans like "aa" fall through to alpha. +* Orca patch: ordered-list markers are digits only, as in CommonMark, so "Dr." and "A." stay prose. */ -const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+|[ivxlcdmIVXLCDM]+|${"[a-zA-Z]{1,2}"}`; +const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+`; /** * Convert a number to lowercase roman numerals. * @example toRoman(1) // 'i' @@ -333,6 +332,26 @@ }; } /** +* Orca patch: marker columns are counted without the delimiter, so trim() left later sibling tasks indented and +* parseIndentedBlocks cut into them. Dedent by the first task; a task one column deeper is a sibling, and other +* text at or within the first task's column stays nested under the task above. +*/ +function dedentOrderedItemTaskBlock(blockLines) { + const isTaskLine = (line) => /^\s*[-+*]\s+\[[ xX]\][^\S\r\n]/.test(line); + const leadingWidth = (line) => line.length - line.trimStart().length; + const firstIndex = blockLines.findIndex((line) => line.trim() !== ""); + if (firstIndex === -1 || !isTaskLine(blockLines[firstIndex])) return null; + const firstIndent = leadingWidth(blockLines[firstIndex]); + return blockLines.slice(firstIndex).map((line) => { + if (line.trim() === "") return ""; + const indent = leadingWidth(line); + const depth = indent - firstIndent; + if (isTaskLine(line)) return depth === 1 ? line.trimStart() : line.slice(Math.min(indent, firstIndent)); + if (depth > 0) return depth === 1 ? ` ${line.slice(firstIndent)}` : line.slice(firstIndent); + return indent > 0 ? ` ${line.trimStart()}` : line; + }).join("\n").trimEnd(); +} +/** * Recursively builds a nested structure from a flat array of list items * based on their indentation levels. Creates proper markdown tokens with * nested lists where appropriate. @@ -356,7 +375,7 @@ raw: mainText, tokens: lexer.inlineTokens(mainText) }); - const additionalContent = blockLines.join("\n").trim(); + const additionalContent = dedentOrderedItemTaskBlock(blockLines) ?? blockLines.join("\n").trim(); if (additionalContent) { const blockTokens = lexer.blockTokens(additionalContent); tokens.push(...blockTokens); diff --git a/dist/ordered-list/index.js b/dist/ordered-list/index.js --- a/dist/ordered-list/index.js +++ b/dist/ordered-list/index.js @@ -18,10 +18,9 @@ ]; const ALPHA_NUMERALS = "abcdefghijklmnopqrstuvwxyz"; /** -* Marker segment for ordered list lines: numeric, roman, or 1–2 letter alpha. -* Roman is matched before alpha so "iii" is roman; invalid romans like "aa" fall through to alpha. +* Orca patch: ordered-list markers are digits only, as in CommonMark, so "Dr." and "A." stay prose. */ -const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+|[ivxlcdmIVXLCDM]+|${"[a-zA-Z]{1,2}"}`; +const ORDERED_LIST_MARKER_PATTERN = String.raw`\d+`; /** * Convert a number to lowercase roman numerals. * @example toRoman(1) // 'i' @@ -332,6 +331,26 @@ }; } /** +* Orca patch: marker columns are counted without the delimiter, so trim() left later sibling tasks indented and +* parseIndentedBlocks cut into them. Dedent by the first task; a task one column deeper is a sibling, and other +* text at or within the first task's column stays nested under the task above. +*/ +function dedentOrderedItemTaskBlock(blockLines) { + const isTaskLine = (line) => /^\s*[-+*]\s+\[[ xX]\][^\S\r\n]/.test(line); + const leadingWidth = (line) => line.length - line.trimStart().length; + const firstIndex = blockLines.findIndex((line) => line.trim() !== ""); + if (firstIndex === -1 || !isTaskLine(blockLines[firstIndex])) return null; + const firstIndent = leadingWidth(blockLines[firstIndex]); + return blockLines.slice(firstIndex).map((line) => { + if (line.trim() === "") return ""; + const indent = leadingWidth(line); + const depth = indent - firstIndent; + if (isTaskLine(line)) return depth === 1 ? line.trimStart() : line.slice(Math.min(indent, firstIndent)); + if (depth > 0) return depth === 1 ? ` ${line.slice(firstIndent)}` : line.slice(firstIndent); + return indent > 0 ? ` ${line.trimStart()}` : line; + }).join("\n").trimEnd(); +} +/** * Recursively builds a nested structure from a flat array of list items * based on their indentation levels. Creates proper markdown tokens with * nested lists where appropriate. @@ -355,7 +374,7 @@ raw: mainText, tokens: lexer.inlineTokens(mainText) }); - const additionalContent = blockLines.join("\n").trim(); + const additionalContent = dedentOrderedItemTaskBlock(blockLines) ?? blockLines.join("\n").trim(); if (additionalContent) { const blockTokens = lexer.blockTokens(additionalContent); tokens.push(...blockTokens);