A self-hosted orchestration system crafted for power users. composia.xyz
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161import type { CellData, TerminalCore } from "@wterm/dom";
const DEFAULT_COLOR = 256;const REVERSE_FLAG = 0x20;const CELL_BYTES = 16;
const BLANK_CELL: CellData = { char: 32, fg: DEFAULT_COLOR, bg: DEFAULT_COLOR, flags: 0,};
export interface TerminalThemeColors { background: number; foreground: number;}
interface GhosttyWasmExports { memory: WebAssembly.Memory; get_scrollback_line( termPtr: number, offset: number, bufPtr: number, maxCols: number, ): number; alloc_buffer(length: number): number; free_buffer(ptr: number, length: number): void;}
interface GhosttyCoreInternals { wasm?: { exports: GhosttyWasmExports }; termPtr?: number;}
interface WasmCellData { codepoint: number; fgR: number; fgG: number; fgB: number; bgR: number; bgG: number; bgB: number; flags: number; colorFlags: number;}
function packRgb(r: number, g: number, b: number) { return (r << 16) | (g << 8) | b;}
function parseCell(view: DataView, byteOffset: number): WasmCellData { return { codepoint: view.getUint32(byteOffset, true), fgR: view.getUint8(byteOffset + 4), fgG: view.getUint8(byteOffset + 5), fgB: view.getUint8(byteOffset + 6), bgR: view.getUint8(byteOffset + 7), bgG: view.getUint8(byteOffset + 8), bgB: view.getUint8(byteOffset + 9), flags: view.getUint8(byteOffset + 10), colorFlags: view.getUint8(byteOffset + 12), };}
function readScrollbackCell( core: TerminalCore, offset: number, col: number,): CellData | null { const internals = core as unknown as GhosttyCoreInternals; const wasm = internals.wasm; const termPtr = internals.termPtr; if (!wasm || !termPtr) { return null; }
const maxCols = core.getCols(); const lineSize = maxCols * CELL_BYTES; const bufPtr = wasm.exports.alloc_buffer(lineSize); if (bufPtr === 0) { return BLANK_CELL; }
try { const length = wasm.exports.get_scrollback_line( termPtr, offset, bufPtr, maxCols, ); if (length === 0 || col >= length) { return BLANK_CELL; }
const view = new DataView(wasm.exports.memory.buffer, bufPtr, lineSize); const cell = parseCell(view, col * CELL_BYTES); if (cell.codepoint === 0 && cell.flags === 0 && cell.colorFlags === 0) { return BLANK_CELL; }
const result: CellData = { char: cell.codepoint || 32, fg: DEFAULT_COLOR, bg: DEFAULT_COLOR, flags: cell.flags, }; if (cell.colorFlags & 1) { result.fgRgb = packRgb(cell.fgR, cell.fgG, cell.fgB); } if (cell.colorFlags & 2) { result.bgRgb = packRgb(cell.bgR, cell.bgG, cell.bgB); } return result; } finally { wasm.exports.free_buffer(bufPtr, lineSize); }}
function hasDefaultColors(cell: CellData) { return ( cell.fg === DEFAULT_COLOR && cell.bg === DEFAULT_COLOR && cell.fgRgb === undefined && cell.bgRgb === undefined );}
function normalizeReverseDefaultCell( cell: CellData, colors: TerminalThemeColors,): CellData { if (!(cell.flags & REVERSE_FLAG) || !hasDefaultColors(cell)) { return cell; }
return { ...cell, flags: cell.flags & ~REVERSE_FLAG, fgRgb: colors.background, bgRgb: colors.foreground, };}
export function patchGhosttyCore( core: TerminalCore, getThemeColors: () => TerminalThemeColors,) { const originalGetCell = core.getCell.bind(core); const originalGetScrollbackCell = core.getScrollbackCell.bind(core);
core.getCell = (row, col) => normalizeReverseDefaultCell(originalGetCell(row, col), getThemeColors()); core.getScrollbackCell = (offset, col) => { const cell = readScrollbackCell(core, offset, col) ?? originalGetScrollbackCell(offset, col); return normalizeReverseDefaultCell(cell, getThemeColors()); };}