76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
function splitTableRow(line: string): string[] {
|
|
let source = line.trim()
|
|
if (source.startsWith('|')) source = source.slice(1)
|
|
if (source.endsWith('|')) source = source.slice(0, -1)
|
|
|
|
const cells: string[] = []
|
|
let cell = ''
|
|
for (let index = 0; index < source.length; index += 1) {
|
|
const char = source.charAt(index)
|
|
if (char === '\\' && source.charAt(index + 1) === '|') {
|
|
cell += '\\|'
|
|
index += 1
|
|
continue
|
|
}
|
|
if (char === '|') {
|
|
cells.push(cell.trim())
|
|
cell = ''
|
|
continue
|
|
}
|
|
cell += char
|
|
}
|
|
cells.push(cell.trim())
|
|
return cells
|
|
}
|
|
|
|
function fenceMarker(line: string): string | null {
|
|
const match = /^[ \t]{0,3}(`{3,}|~{3,})/u.exec(line)
|
|
return match?.[1] ?? null
|
|
}
|
|
|
|
function isClosingFence(line: string, marker: string): boolean {
|
|
const trimmed = line.trimStart()
|
|
return trimmed.startsWith(marker.charAt(0).repeat(marker.length))
|
|
}
|
|
|
|
function normalizedDelimiter(cell: string): string | null {
|
|
const match = /^(:?)-+(:?)$/u.exec(cell.trim())
|
|
if (!match) return null
|
|
return `${match[1]}---${match[2]}`
|
|
}
|
|
|
|
function normalizeDelimiterRow(previousLine: string | undefined, line: string): string {
|
|
if (!previousLine?.includes('|') || !line.includes('|')) return line
|
|
|
|
const headerCells = splitTableRow(previousLine)
|
|
const delimiterCells = splitTableRow(line)
|
|
if (delimiterCells.length < 2 || delimiterCells.length !== headerCells.length) return line
|
|
|
|
const normalized = delimiterCells.map(normalizedDelimiter)
|
|
if (normalized.some(cell => cell === null)) return line
|
|
return `| ${normalized.join(' | ')} |`
|
|
}
|
|
|
|
/**
|
|
* Some models emit compact Markdown table separators such as `|:--|:--|`.
|
|
* GFM requires at least three dashes, so both BlockNote and remark otherwise
|
|
* render the entire table as plain text. Repair only separator rows directly
|
|
* below a same-width header and never touch fenced examples.
|
|
*/
|
|
export function normalizeMarkdownTableDelimiters(markdown: string): string {
|
|
const lines = markdown.split(/\r?\n/u)
|
|
let activeFence: string | null = null
|
|
|
|
return lines.map((line, index) => {
|
|
const marker = fenceMarker(line)
|
|
if (activeFence) {
|
|
if (marker && isClosingFence(line, activeFence)) activeFence = null
|
|
return line
|
|
}
|
|
if (marker) {
|
|
activeFence = marker
|
|
return line
|
|
}
|
|
return normalizeDelimiterRow(lines[index - 1], line)
|
|
}).join('\n')
|
|
}
|