Merge remote-tracking branch 'origin/master' into feat/web-terminal-card

# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
This commit is contained in:
Chinesezjc
2026-07-28 21:03:26 +08:00
407 changed files with 6332 additions and 3102 deletions

View File

@@ -8,6 +8,7 @@ import { describe, expect, it } from 'vitest'
import { parseAnsiLines } from '../src/ansi.ts'
const ESC = '\u001b'
const BS = '\u0008'
/** Paint `text` with the SGR `codes`, then reset. */
function sgr(codes: string, text: string): string {
@@ -170,6 +171,30 @@ describe('parseAnsiLines: carriage returns', () => {
})
})
describe('parseAnsiLines: backspaces', () => {
it('applies a backspace as the overwrite a terminal draws', () => {
// `abc` then two backspaces then `XY` shows as `aXY`, not `abcXY`.
expect(onlySpan(`abc${BS}${BS}XY`)).toEqual({ text: 'aXY', style: undefined })
})
it('stops at the line start instead of eating the newline before it', () => {
expect(parseAnsiLines(`ab\n${BS}${BS}${BS}cd`)).toEqual([
[{ text: 'ab', style: undefined }],
[{ text: 'cd', style: undefined }],
])
})
it('applies the overwrite after a carriage-return redraw, not before', () => {
// The redraw wins first; the backspace then erases inside what survived.
expect(onlySpan(`old\rnew${BS}`)).toEqual({ text: 'ne', style: undefined })
})
it('keeps the run\'s style while erasing its own characters', () => {
expect(onlySpan(sgr('31', `bad${BS}${BS}${BS}ok`)))
.toEqual({ text: 'ok', style: { color: 'var(--dsw-alias-state-error-primary)' } })
})
})
describe('parseAnsiLines: runs spanning lines', () => {
it('carries one run\'s style onto every line it covers', () => {
expect(parseAnsiLines(sgr('32', 'first\nsecond'))).toEqual([

View File

@@ -217,6 +217,18 @@ describe('TerminalBlock run-state dot', () => {
expect(promptRows(view.container)).toEqual(['$echo one', '$echo two'])
})
// A heredoc or an editor-authored command commonly ends in a newline; that
// terminator is not a further, empty command to draw a row for.
it('drops a trailing newline instead of drawing an empty final row', () => {
const view = render(<TerminalBlock command={'echo one\necho two\n'} output="a" exitCode={0} />)
expect(promptRows(view.container)).toEqual(['$echo one', '$echo two'])
})
it('keeps a genuinely blank command line when the command ends with two newlines', () => {
const view = render(<TerminalBlock command={'echo one\n\n'} output="a" exitCode={0} />)
expect(promptRows(view.container)).toEqual(['$echo one', '$'])
})
// The exit status the view carries is the whole call's — bash reports no
// per-command status — so exactly one dot and one label are correct however
// many lines the command spans. A dot per row would assert, of a line that