import { Box, Text } from 'ink'
import type { ReactNode } from 'react'
import type { Theme } from '../theme.js'
function MdInline({ t, text }: { t: Theme; text: string }) {
const parts: ReactNode[] = []
const re = /(\[(.+?)\]\((https?:\/\/[^\s)]+)\)|\*\*(.+?)\*\*|`([^`]+)`|\*(.+?)\*|(https?:\/\/[^\s]+))/g
let last = 0
for (const m of text.matchAll(re)) {
const i = m.index ?? 0
if (i > last) {
parts.push({text.slice(last, i)})
}
if (m[2] && m[3]) {
parts.push(
{m[2]}
)
} else if (m[4]) {
parts.push(
{m[4]}
)
} else if (m[5]) {
parts.push(
{m[5]}
)
} else if (m[6]) {
parts.push(
{m[6]}
)
} else if (m[7]) {
parts.push(
{m[7]}
)
}
last = i + m[0].length
}
if (last < text.length) {
parts.push({text.slice(last)})
}
return {parts.length ? parts : {text}}
}
export function Md({ compact, t, text }: { compact?: boolean; t: Theme; text: string }) {
const lines = text.split('\n')
const nodes: ReactNode[] = []
let i = 0
let prevKind: 'blank' | 'code' | 'heading' | 'list' | 'paragraph' | 'quote' | 'table' | null = null
const gap = () => {
if (nodes.length && prevKind !== 'blank') {
nodes.push( )
prevKind = 'blank'
}
}
const start = (kind: Exclude) => {
if (prevKind && prevKind !== 'blank' && prevKind !== kind) {
gap()
}
prevKind = kind
}
while (i < lines.length) {
const line = lines[i]!
const key = nodes.length
if (compact && !line.trim()) {
i++
continue
}
if (!line.trim()) {
gap()
i++
continue
}
if (line.startsWith('```')) {
start('code')
const lang = line.slice(3).trim()
const block: string[] = []
for (i++; i < lines.length && !lines[i]!.startsWith('```'); i++) {
block.push(lines[i]!)
}
i++
const isDiff = lang === 'diff'
nodes.push(
{lang && !isDiff && {'─ ' + lang}}
{block.map((l, j) => (
{l}
))}
)
continue
}
const heading = line.match(/^#{1,3}\s+(.*)/)
if (heading) {
start('heading')
nodes.push(
{heading[1]}
)
i++
continue
}
const bullet = line.match(/^\s*[-*]\s(.*)/)
if (bullet) {
start('list')
nodes.push(
•
)
i++
continue
}
const numbered = line.match(/^\s*(\d+)\.\s(.*)/)
if (numbered) {
start('list')
nodes.push(
{numbered[1]}.
)
i++
continue
}
if (line.match(/^>\s?/)) {
start('quote')
const quoteLines: string[] = []
while (i < lines.length && lines[i]!.match(/^>\s?/)) {
quoteLines.push(lines[i]!.replace(/^>\s?/, ''))
i++
}
nodes.push(
{quoteLines.map((ql, qi) => (
{' │ '}
))}
)
continue
}
if (line.includes('|') && line.trim().startsWith('|')) {
start('table')
const tableRows: string[][] = []
while (i < lines.length && lines[i]!.trim().startsWith('|')) {
const row = lines[i]!.trim()
if (!/^[|\s:-]+$/.test(row)) {
tableRows.push(
row
.split('|')
.filter(Boolean)
.map(c => c.trim())
)
}
i++
}
if (tableRows.length) {
const widths = tableRows[0]!.map((_, ci) => Math.max(...tableRows.map(r => (r[ci] ?? '').length)))
nodes.push(
{tableRows.map((row, ri) => (
{row.map((cell, ci) => cell.padEnd(widths[ci] ?? 0)).join(' ')}
))}
)
}
continue
}
start('paragraph')
nodes.push()
i++
}
return {nodes}
}