mirror of
https://github.com/OliveTin/OliveTin
synced 2026-09-23 02:05:42 +00:00
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>
113 lines
2.6 KiB
JavaScript
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
|
|
}
|
|
}
|