mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-07 02:51:50 +00:00
fix(tui): track rendered spaces for selection copy
- add a written-cell bitmap so selection can distinguish rendered spaces from blank padding - preserve code indentation without markdown-specific rendering hacks
This commit is contained in:
parent
1735ced93b
commit
bd66e55a02
4 changed files with 57 additions and 28 deletions
|
|
@ -436,6 +436,13 @@ export type Screen = Size & {
|
||||||
*/
|
*/
|
||||||
noSelect: Uint8Array
|
noSelect: Uint8Array
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-cell written bitmap. A written plain space and never-written padding
|
||||||
|
* share the same packed cell value, so selection needs this side channel to
|
||||||
|
* preserve code indentation without selecting blank UI margins.
|
||||||
|
*/
|
||||||
|
written: Uint8Array
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per-ROW soft-wrap continuation marker. softWrap[r]=N>0 means row r
|
* Per-ROW soft-wrap continuation marker. softWrap[r]=N>0 means row r
|
||||||
* is a word-wrap continuation of row r-1 (the `\n` before it was
|
* is a word-wrap continuation of row r-1 (the `\n` before it was
|
||||||
|
|
@ -475,6 +482,14 @@ export function isEmptyCellAt(screen: Screen, x: number, y: number): boolean {
|
||||||
return isEmptyCellByIndex(screen, y * screen.width + x)
|
return isEmptyCellByIndex(screen, y * screen.width + x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isWrittenCellAt(screen: Screen, x: number, y: number): boolean {
|
||||||
|
if (x < 0 || y < 0 || x >= screen.width || y >= screen.height) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return screen.written[y * screen.width + x] === 1
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a Cell (view object) represents an empty cell.
|
* Check if a Cell (view object) represents an empty cell.
|
||||||
*/
|
*/
|
||||||
|
|
@ -533,6 +548,7 @@ export function createScreen(
|
||||||
emptyStyleId: styles.none,
|
emptyStyleId: styles.none,
|
||||||
damage: undefined,
|
damage: undefined,
|
||||||
noSelect: new Uint8Array(size),
|
noSelect: new Uint8Array(size),
|
||||||
|
written: new Uint8Array(size),
|
||||||
softWrap: new Int32Array(height)
|
softWrap: new Int32Array(height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -566,6 +582,7 @@ export function resetScreen(screen: Screen, width: number, height: number): void
|
||||||
screen.cells = new Int32Array(buf)
|
screen.cells = new Int32Array(buf)
|
||||||
screen.cells64 = new BigInt64Array(buf)
|
screen.cells64 = new BigInt64Array(buf)
|
||||||
screen.noSelect = new Uint8Array(size)
|
screen.noSelect = new Uint8Array(size)
|
||||||
|
screen.written = new Uint8Array(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (screen.softWrap.length < height) {
|
if (screen.softWrap.length < height) {
|
||||||
|
|
@ -575,6 +592,7 @@ export function resetScreen(screen: Screen, width: number, height: number): void
|
||||||
// Reset all cells — single fill call, no loop
|
// Reset all cells — single fill call, no loop
|
||||||
screen.cells64.fill(EMPTY_CELL_VALUE, 0, size)
|
screen.cells64.fill(EMPTY_CELL_VALUE, 0, size)
|
||||||
screen.noSelect.fill(0, 0, size)
|
screen.noSelect.fill(0, 0, size)
|
||||||
|
screen.written.fill(0, 0, size)
|
||||||
screen.softWrap.fill(0, 0, height)
|
screen.softWrap.fill(0, 0, height)
|
||||||
|
|
||||||
// Update dimensions
|
// Update dimensions
|
||||||
|
|
@ -770,6 +788,7 @@ export function setCellAt(screen: Screen, x: number, y: number, cell: Cell): voi
|
||||||
if ((cells[spacerCI + 1]! & WIDTH_MASK) === CellWidth.SpacerTail) {
|
if ((cells[spacerCI + 1]! & WIDTH_MASK) === CellWidth.SpacerTail) {
|
||||||
cells[spacerCI] = EMPTY_CHAR_INDEX
|
cells[spacerCI] = EMPTY_CHAR_INDEX
|
||||||
cells[spacerCI + 1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow)
|
cells[spacerCI + 1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow)
|
||||||
|
screen.written[y * screen.width + spacerX] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -787,6 +806,7 @@ export function setCellAt(screen: Screen, x: number, y: number, cell: Cell): voi
|
||||||
if ((cells[wideCI + 1]! & WIDTH_MASK) === CellWidth.Wide) {
|
if ((cells[wideCI + 1]! & WIDTH_MASK) === CellWidth.Wide) {
|
||||||
cells[wideCI] = EMPTY_CHAR_INDEX
|
cells[wideCI] = EMPTY_CHAR_INDEX
|
||||||
cells[wideCI + 1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow)
|
cells[wideCI + 1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow)
|
||||||
|
screen.written[y * screen.width + x - 1] = 0
|
||||||
clearedWideX = x - 1
|
clearedWideX = x - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -795,6 +815,7 @@ export function setCellAt(screen: Screen, x: number, y: number, cell: Cell): voi
|
||||||
// Pack cell data into cells array
|
// Pack cell data into cells array
|
||||||
cells[ci] = internCharString(screen, cell.char)
|
cells[ci] = internCharString(screen, cell.char)
|
||||||
cells[ci + 1] = packWord1(cell.styleId, internHyperlink(screen, cell.hyperlink), cell.width)
|
cells[ci + 1] = packWord1(cell.styleId, internHyperlink(screen, cell.hyperlink), cell.width)
|
||||||
|
screen.written[y * screen.width + x] = 1
|
||||||
|
|
||||||
// Track damage - expand bounds in place instead of allocating new objects
|
// Track damage - expand bounds in place instead of allocating new objects
|
||||||
// Include the main cell position and any cleared orphan cells
|
// Include the main cell position and any cleared orphan cells
|
||||||
|
|
@ -841,11 +862,13 @@ export function setCellAt(screen: Screen, x: number, y: number, cell: Cell): voi
|
||||||
if (spacerX + 1 < screen.width && (cells[orphanCI + 1]! & WIDTH_MASK) === CellWidth.SpacerTail) {
|
if (spacerX + 1 < screen.width && (cells[orphanCI + 1]! & WIDTH_MASK) === CellWidth.SpacerTail) {
|
||||||
cells[orphanCI] = EMPTY_CHAR_INDEX
|
cells[orphanCI] = EMPTY_CHAR_INDEX
|
||||||
cells[orphanCI + 1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow)
|
cells[orphanCI + 1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow)
|
||||||
|
screen.written[y * screen.width + spacerX + 1] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cells[spacerCI] = SPACER_CHAR_INDEX
|
cells[spacerCI] = SPACER_CHAR_INDEX
|
||||||
cells[spacerCI + 1] = packWord1(screen.emptyStyleId, 0, CellWidth.SpacerTail)
|
cells[spacerCI + 1] = packWord1(screen.emptyStyleId, 0, CellWidth.SpacerTail)
|
||||||
|
screen.written[y * screen.width + spacerX] = 1
|
||||||
|
|
||||||
// Expand damage to include SpacerTail so diff() scans it
|
// Expand damage to include SpacerTail so diff() scans it
|
||||||
const d = screen.damage
|
const d = screen.damage
|
||||||
|
|
@ -929,6 +952,8 @@ export function blitRegion(
|
||||||
const dstCells = dst.cells
|
const dstCells = dst.cells
|
||||||
const srcNoSel = src.noSelect
|
const srcNoSel = src.noSelect
|
||||||
const dstNoSel = dst.noSelect
|
const dstNoSel = dst.noSelect
|
||||||
|
const srcWritten = src.written
|
||||||
|
const dstWritten = dst.written
|
||||||
|
|
||||||
// softWrap is per-row — copy the row range regardless of stride/width.
|
// softWrap is per-row — copy the row range regardless of stride/width.
|
||||||
// Partial-width blits still carry the row's wrap provenance since the
|
// Partial-width blits still carry the row's wrap provenance since the
|
||||||
|
|
@ -947,6 +972,7 @@ export function blitRegion(
|
||||||
const nsStart = regionY * src.width
|
const nsStart = regionY * src.width
|
||||||
const nsLen = (maxY - regionY) * src.width
|
const nsLen = (maxY - regionY) * src.width
|
||||||
dstNoSel.set(srcNoSel.subarray(nsStart, nsStart + nsLen), nsStart)
|
dstNoSel.set(srcNoSel.subarray(nsStart, nsStart + nsLen), nsStart)
|
||||||
|
dstWritten.set(srcWritten.subarray(nsStart, nsStart + nsLen), nsStart)
|
||||||
} else {
|
} else {
|
||||||
// Per-row copy for partial-width or mismatched-stride regions
|
// Per-row copy for partial-width or mismatched-stride regions
|
||||||
let srcRowCI = regionY * srcStride + (regionX << 1)
|
let srcRowCI = regionY * srcStride + (regionX << 1)
|
||||||
|
|
@ -957,6 +983,7 @@ export function blitRegion(
|
||||||
for (let y = regionY; y < maxY; y++) {
|
for (let y = regionY; y < maxY; y++) {
|
||||||
dstCells.set(srcCells.subarray(srcRowCI, srcRowCI + rowBytes), dstRowCI)
|
dstCells.set(srcCells.subarray(srcRowCI, srcRowCI + rowBytes), dstRowCI)
|
||||||
dstNoSel.set(srcNoSel.subarray(srcRowNS, srcRowNS + rowLen), dstRowNS)
|
dstNoSel.set(srcNoSel.subarray(srcRowNS, srcRowNS + rowLen), dstRowNS)
|
||||||
|
dstWritten.set(srcWritten.subarray(srcRowNS, srcRowNS + rowLen), dstRowNS)
|
||||||
srcRowCI += srcStride
|
srcRowCI += srcStride
|
||||||
dstRowCI += dstStride
|
dstRowCI += dstStride
|
||||||
srcRowNS += src.width
|
srcRowNS += src.width
|
||||||
|
|
@ -989,6 +1016,7 @@ export function blitRegion(
|
||||||
if ((srcCells[srcLastCI + 1]! & WIDTH_MASK) === CellWidth.Wide) {
|
if ((srcCells[srcLastCI + 1]! & WIDTH_MASK) === CellWidth.Wide) {
|
||||||
dstCells[dstSpacerCI] = SPACER_CHAR_INDEX
|
dstCells[dstSpacerCI] = SPACER_CHAR_INDEX
|
||||||
dstCells[dstSpacerCI + 1] = packWord1(dst.emptyStyleId, 0, CellWidth.SpacerTail)
|
dstCells[dstSpacerCI + 1] = packWord1(dst.emptyStyleId, 0, CellWidth.SpacerTail)
|
||||||
|
dstWritten[y * dst.width + maxX] = 1
|
||||||
wroteSpacerOutsideRegion = true
|
wroteSpacerOutsideRegion = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1030,6 +1058,7 @@ export function clearRegion(
|
||||||
|
|
||||||
const cells = screen.cells
|
const cells = screen.cells
|
||||||
const cells64 = screen.cells64
|
const cells64 = screen.cells64
|
||||||
|
const written = screen.written
|
||||||
const screenWidth = screen.width
|
const screenWidth = screen.width
|
||||||
const rowBase = startY * screenWidth
|
const rowBase = startY * screenWidth
|
||||||
let damageMinX = startX
|
let damageMinX = startX
|
||||||
|
|
@ -1040,6 +1069,7 @@ export function clearRegion(
|
||||||
if (startX === 0 && maxX === screenWidth) {
|
if (startX === 0 && maxX === screenWidth) {
|
||||||
// Full-width: single fill, no boundary checks needed
|
// Full-width: single fill, no boundary checks needed
|
||||||
cells64.fill(EMPTY_CELL_VALUE, rowBase, rowBase + (maxY - startY) * screenWidth)
|
cells64.fill(EMPTY_CELL_VALUE, rowBase, rowBase + (maxY - startY) * screenWidth)
|
||||||
|
written.fill(0, rowBase, rowBase + (maxY - startY) * screenWidth)
|
||||||
} else {
|
} else {
|
||||||
// Partial-width: single loop handles boundary cleanup and fill per row.
|
// Partial-width: single loop handles boundary cleanup and fill per row.
|
||||||
const stride = screenWidth << 1 // 2 Int32s per cell
|
const stride = screenWidth << 1 // 2 Int32s per cell
|
||||||
|
|
@ -1062,6 +1092,7 @@ export function clearRegion(
|
||||||
if ((cells[prevW1]! & WIDTH_MASK) === CellWidth.Wide) {
|
if ((cells[prevW1]! & WIDTH_MASK) === CellWidth.Wide) {
|
||||||
cells[prevW1 - 1] = EMPTY_CHAR_INDEX
|
cells[prevW1 - 1] = EMPTY_CHAR_INDEX
|
||||||
cells[prevW1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow)
|
cells[prevW1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow)
|
||||||
|
written[y * screenWidth + startX - 1] = 0
|
||||||
damageMinX = startX - 1
|
damageMinX = startX - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1078,12 +1109,14 @@ export function clearRegion(
|
||||||
if ((cells[nextW1]! & WIDTH_MASK) === CellWidth.SpacerTail) {
|
if ((cells[nextW1]! & WIDTH_MASK) === CellWidth.SpacerTail) {
|
||||||
cells[nextW1 - 1] = EMPTY_CHAR_INDEX
|
cells[nextW1 - 1] = EMPTY_CHAR_INDEX
|
||||||
cells[nextW1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow)
|
cells[nextW1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow)
|
||||||
|
written[y * screenWidth + maxX] = 0
|
||||||
damageMaxX = maxX + 1
|
damageMaxX = maxX + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cells64.fill(EMPTY_CELL_VALUE, fillStart, fillStart + rowLen)
|
cells64.fill(EMPTY_CELL_VALUE, fillStart, fillStart + rowLen)
|
||||||
|
written.fill(0, fillStart, fillStart + rowLen)
|
||||||
leftEdge += stride
|
leftEdge += stride
|
||||||
rightEdge += stride
|
rightEdge += stride
|
||||||
fillStart += screenWidth
|
fillStart += screenWidth
|
||||||
|
|
@ -1120,12 +1153,14 @@ export function shiftRows(screen: Screen, top: number, bottom: number, n: number
|
||||||
const w = screen.width
|
const w = screen.width
|
||||||
const cells64 = screen.cells64
|
const cells64 = screen.cells64
|
||||||
const noSel = screen.noSelect
|
const noSel = screen.noSelect
|
||||||
|
const written = screen.written
|
||||||
const sw = screen.softWrap
|
const sw = screen.softWrap
|
||||||
const absN = Math.abs(n)
|
const absN = Math.abs(n)
|
||||||
|
|
||||||
if (absN > bottom - top) {
|
if (absN > bottom - top) {
|
||||||
cells64.fill(EMPTY_CELL_VALUE, top * w, (bottom + 1) * w)
|
cells64.fill(EMPTY_CELL_VALUE, top * w, (bottom + 1) * w)
|
||||||
noSel.fill(0, top * w, (bottom + 1) * w)
|
noSel.fill(0, top * w, (bottom + 1) * w)
|
||||||
|
written.fill(0, top * w, (bottom + 1) * w)
|
||||||
sw.fill(0, top, bottom + 1)
|
sw.fill(0, top, bottom + 1)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
@ -1135,17 +1170,21 @@ export function shiftRows(screen: Screen, top: number, bottom: number, n: number
|
||||||
// SU: row top+n..bottom → top..bottom-n; clear bottom-n+1..bottom
|
// SU: row top+n..bottom → top..bottom-n; clear bottom-n+1..bottom
|
||||||
cells64.copyWithin(top * w, (top + n) * w, (bottom + 1) * w)
|
cells64.copyWithin(top * w, (top + n) * w, (bottom + 1) * w)
|
||||||
noSel.copyWithin(top * w, (top + n) * w, (bottom + 1) * w)
|
noSel.copyWithin(top * w, (top + n) * w, (bottom + 1) * w)
|
||||||
|
written.copyWithin(top * w, (top + n) * w, (bottom + 1) * w)
|
||||||
sw.copyWithin(top, top + n, bottom + 1)
|
sw.copyWithin(top, top + n, bottom + 1)
|
||||||
cells64.fill(EMPTY_CELL_VALUE, (bottom - n + 1) * w, (bottom + 1) * w)
|
cells64.fill(EMPTY_CELL_VALUE, (bottom - n + 1) * w, (bottom + 1) * w)
|
||||||
noSel.fill(0, (bottom - n + 1) * w, (bottom + 1) * w)
|
noSel.fill(0, (bottom - n + 1) * w, (bottom + 1) * w)
|
||||||
|
written.fill(0, (bottom - n + 1) * w, (bottom + 1) * w)
|
||||||
sw.fill(0, bottom - n + 1, bottom + 1)
|
sw.fill(0, bottom - n + 1, bottom + 1)
|
||||||
} else {
|
} else {
|
||||||
// SD: row top..bottom+n → top-n..bottom; clear top..top-n-1
|
// SD: row top..bottom+n → top-n..bottom; clear top..top-n-1
|
||||||
cells64.copyWithin((top - n) * w, top * w, (bottom + n + 1) * w)
|
cells64.copyWithin((top - n) * w, top * w, (bottom + n + 1) * w)
|
||||||
noSel.copyWithin((top - n) * w, top * w, (bottom + n + 1) * w)
|
noSel.copyWithin((top - n) * w, top * w, (bottom + n + 1) * w)
|
||||||
|
written.copyWithin((top - n) * w, top * w, (bottom + n + 1) * w)
|
||||||
sw.copyWithin(top - n, top, bottom + n + 1)
|
sw.copyWithin(top - n, top, bottom + n + 1)
|
||||||
cells64.fill(EMPTY_CELL_VALUE, top * w, (top - n) * w)
|
cells64.fill(EMPTY_CELL_VALUE, top * w, (top - n) * w)
|
||||||
noSel.fill(0, top * w, (top - n) * w)
|
noSel.fill(0, top * w, (top - n) * w)
|
||||||
|
written.fill(0, top * w, (top - n) * w)
|
||||||
sw.fill(0, top, top - n)
|
sw.fill(0, top, top - n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,6 @@ const screenWithText = () => {
|
||||||
return { screen, styles }
|
return { screen, styles }
|
||||||
}
|
}
|
||||||
|
|
||||||
const styledSpace = (styles: StylePool) => styles.intern([{ code: '\x1b[4m', endCode: '\x1b[24m', type: 'ansi' }])
|
|
||||||
|
|
||||||
describe('selection whitespace handling', () => {
|
describe('selection whitespace handling', () => {
|
||||||
it('does not copy whitespace-only selections', () => {
|
it('does not copy whitespace-only selections', () => {
|
||||||
const { screen } = screenWithText()
|
const { screen } = screenWithText()
|
||||||
|
|
@ -45,11 +43,10 @@ describe('selection whitespace handling', () => {
|
||||||
it('preserves selected indentation when spaces are rendered content', () => {
|
it('preserves selected indentation when spaces are rendered content', () => {
|
||||||
const styles = new StylePool()
|
const styles = new StylePool()
|
||||||
const screen = createScreen(10, 1, styles, new CharPool(), new HyperlinkPool())
|
const screen = createScreen(10, 1, styles, new CharPool(), new HyperlinkPool())
|
||||||
const indentStyle = styledSpace(styles)
|
|
||||||
const selection = createSelectionState()
|
const selection = createSelectionState()
|
||||||
|
|
||||||
setCellAt(screen, 0, 0, { char: ' ', hyperlink: undefined, styleId: indentStyle, width: CellWidth.Narrow })
|
setCellAt(screen, 0, 0, { char: ' ', hyperlink: undefined, styleId: screen.emptyStyleId, width: CellWidth.Narrow })
|
||||||
setCellAt(screen, 1, 0, { char: ' ', hyperlink: undefined, styleId: indentStyle, width: CellWidth.Narrow })
|
setCellAt(screen, 1, 0, { char: ' ', hyperlink: undefined, styleId: screen.emptyStyleId, width: CellWidth.Narrow })
|
||||||
setCellAt(screen, 2, 0, { char: 'x', hyperlink: undefined, styleId: screen.emptyStyleId, width: CellWidth.Narrow })
|
setCellAt(screen, 2, 0, { char: 'x', hyperlink: undefined, styleId: screen.emptyStyleId, width: CellWidth.Narrow })
|
||||||
|
|
||||||
startSelection(selection, 0, 0)
|
startSelection(selection, 0, 0)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
import { clamp } from './layout/geometry.js'
|
import { clamp } from './layout/geometry.js'
|
||||||
import type { Screen, StylePool } from './screen.js'
|
import type { Screen, StylePool } from './screen.js'
|
||||||
import { cellAt, cellAtIndex, CellWidth, isEmptyCellAt, setCellStyleId } from './screen.js'
|
import { cellAt, cellAtIndex, CellWidth, isWrittenCellAt, setCellStyleId } from './screen.js'
|
||||||
|
|
||||||
type Point = { col: number; row: number }
|
type Point = { col: number; row: number }
|
||||||
|
|
||||||
|
|
@ -847,7 +847,7 @@ function selectableCell(screen: Screen, row: number, col: number): boolean {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
screen.noSelect[row * screen.width + col] !== 1 &&
|
screen.noSelect[row * screen.width + col] !== 1 &&
|
||||||
!isEmptyCellAt(screen, col, row) &&
|
isWrittenCellAt(screen, col, row) &&
|
||||||
!!cell &&
|
!!cell &&
|
||||||
cell.width !== CellWidth.SpacerTail &&
|
cell.width !== CellWidth.SpacerTail &&
|
||||||
cell.width !== CellWidth.SpacerHead
|
cell.width !== CellWidth.SpacerHead
|
||||||
|
|
|
||||||
|
|
@ -114,25 +114,6 @@ const renderTable = (k: number, rows: string[][], t: Theme) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const codeText = (text: string, color: string | undefined, key: number | string, t: Theme) => (
|
|
||||||
<Text color={color ?? (text.trim() ? undefined : t.color.dim)} key={key}>
|
|
||||||
{text}
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
|
|
||||||
const plainCodeLine = (text: string, t: Theme) => {
|
|
||||||
const indent = text.match(/^\s+/)?.[0] ?? ''
|
|
||||||
|
|
||||||
return indent ? (
|
|
||||||
<>
|
|
||||||
{codeText(indent, undefined, 'indent', t)}
|
|
||||||
{text.slice(indent.length)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
text
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function MdInline({ t, text }: { t: Theme; text: string }) {
|
function MdInline({ t, text }: { t: Theme; text: string }) {
|
||||||
const parts: ReactNode[] = []
|
const parts: ReactNode[] = []
|
||||||
|
|
||||||
|
|
@ -335,7 +316,19 @@ function MdImpl({ compact, t, text }: MdProps) {
|
||||||
|
|
||||||
{block.map((l, j) => {
|
{block.map((l, j) => {
|
||||||
if (highlighted) {
|
if (highlighted) {
|
||||||
return <Text key={j}>{highlightLine(l, lang, t).map(([color, text], kk) => codeText(text, color, kk, t))}</Text>
|
return (
|
||||||
|
<Text key={j}>
|
||||||
|
{highlightLine(l, lang, t).map(([color, text], kk) =>
|
||||||
|
color ? (
|
||||||
|
<Text color={color} key={kk}>
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<Text key={kk}>{text}</Text>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const add = isDiff && l.startsWith('+')
|
const add = isDiff && l.startsWith('+')
|
||||||
|
|
@ -349,7 +342,7 @@ function MdImpl({ compact, t, text }: MdProps) {
|
||||||
dimColor={isDiff && !add && !del && !hunk && l.startsWith(' ')}
|
dimColor={isDiff && !add && !del && !hunk && l.startsWith(' ')}
|
||||||
key={j}
|
key={j}
|
||||||
>
|
>
|
||||||
{plainCodeLine(l, t)}
|
{l}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue