diff --git a/static/js/terminal-manager.js b/static/js/terminal-manager.js index d4584a4..9753233 100644 --- a/static/js/terminal-manager.js +++ b/static/js/terminal-manager.js @@ -878,7 +878,7 @@ const TerminalManager = { if (!raw) { return ''; } - const stripped = this.stripAnsiSequences(raw); + const stripped = this.stripAnsiSequences(raw.replace(/\r\n/g, '\n')); return this.normalizeControlChars(stripped); }, diff --git a/tests/js/terminal-transcript.test.js b/tests/js/terminal-transcript.test.js new file mode 100644 index 0000000..e122b88 --- /dev/null +++ b/tests/js/terminal-transcript.test.js @@ -0,0 +1,32 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); + +global.window = {addEventListener() {}, visualViewport: null}; +global.document = {}; +global.navigator = {}; + +require('../../static/js/terminal-manager.js'); +const TerminalManager = global.window.TerminalManager; + +test('transcript export preserves text before CRLF line endings', () => { + TerminalManager.transcripts = { + session: ['user@host:~$ echo hello\r', '\n\x1b[32mhello\x1b[0m\r', '\nuser@host:~$ '], + }; + + assert.equal( + TerminalManager.getCleanTranscript('session'), + 'user@host:~$ echo hello\nhello\nuser@host:~$ ', + ); +}); + +test('transcript export still handles a lone carriage return as an overwrite', () => { + TerminalManager.transcripts = {session: ['Downloading 10%\rDownloading 90%\n']}; + + assert.equal(TerminalManager.getCleanTranscript('session'), 'Downloading 90%\n'); +}); + +test('ANSI erase between CR and LF does not turn a cleared line into a saved line', () => { + TerminalManager.transcripts = {session: ['text\r', '\x1b[2K\n']}; + + assert.equal(TerminalManager.getCleanTranscript('session'), '\n'); +});