Files
jamesreadandCursor 91f7764de2 perf: optimize frontend build and initial loading
Remove redundant component scanning, lazy-load terminal CSS and locale
messages, and clean obsolete frontend package metadata. Generate one JSON
file per locale so initial loads only fetch English and the selected locale.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 17:47:33 +01:00

113 lines
2.6 KiB
JavaScript

import '@xterm/xterm/css/xterm.css'
import { Terminal } from '@xterm/xterm'
import { FitAddon } from '@xterm/addon-fit'
import { WebLinksAddon } from '@xterm/addon-web-links'
import { Mutex } from './Mutex.js'
/**
* xterm.js based terminal output for the execution dialog.
*
* the xterm.js methods for write(), reset() and clear() appear to be async,
* but they do not return a Promise and instead use a callback. When calling
* these methods in quick succession, the output can get garbled due to race
* conditions.
*
* To avoid this, this class uses Mutex around those methods to ensure that
* only one write OR reset is executed at a time, is completed, and the calls
* occour in sequential order.
*/
export class OutputTerminal {
constructor (executionTrackingId) {
this.executionTrackingId = executionTrackingId
this.writeMutex = new Mutex()
const linkHandler = {
activate (event, text, _range) {
event.preventDefault()
window.open(text, '_blank')
}
}
this.terminal = new Terminal({
convertEol: true,
linkHandler,
scrollback: 10000
})
const fitAddon = new FitAddon()
this.terminal.loadAddon(fitAddon)
this.terminal.fit = fitAddon
this.terminal.loadAddon(new WebLinksAddon((event, uri) => linkHandler.activate(event, uri)))
this.linkHandlerConfigured = true
}
async write (out, then) {
const unlock = await this.writeMutex.lock()
try {
await new Promise(resolve => {
this.terminal.write(out, () => {
resolve()
})
})
} finally {
unlock()
if (then != null && then !== undefined) {
then()
}
}
}
async reset () {
const unlock = await this.writeMutex.lock()
try {
await new Promise(resolve => {
this.terminal.clear()
this.terminal.reset()
resolve()
})
} finally {
unlock()
}
}
fit () {
this.terminal.fit.fit()
}
open (el) {
this.terminal.open(el)
}
close () {
this.terminal.dispose()
}
resize (cols, rows) {
this.terminal.resize(cols, rows)
}
/**
* Get the terminal buffer content as a string.
* This method is intended for use in integration tests to verify output.
* @returns {string} The terminal buffer content as a string
*/
getBufferAsString () {
if (!this.terminal) {
return ''
}
const buffer = this.terminal.buffer.active
let text = ''
for (let i = 0; i < buffer.length; i++) {
const line = buffer.getLine(i)
if (line) {
text += line.translateToString(true) + '\n'
}
}
return text
}
}