UNPKG

shellquest

Version:

Terminal-based procedurally generated dungeon crawler

1,682 lines (1,660 loc) 2.32 MB
#!/usr/bin/env bun // @bun var __create = Object.create; var __getProtoOf = Object.getPrototypeOf; var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __toESM = (mod, isNodeMode, target) => { target = mod != null ? __create(__getProtoOf(mod)) : {}; const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target; for (let key of __getOwnPropNames(mod)) if (!__hasOwnProp.call(to, key)) __defProp(to, key, { get: () => mod[key], enumerable: true }); return to; }; var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true, configurable: true, set: (newValue) => all[name] = () => newValue }); }; var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res); var __promiseAll = (args) => Promise.all(args); var __require = import.meta.require; // node_modules/@opentui/core/assets/javascript/highlights.scm var highlights_default = "./highlights-ghv9g403.scm"; var init_highlights = () => {}; // node_modules/@opentui/core/assets/javascript/tree-sitter-javascript.wasm var tree_sitter_javascript_default = "./tree-sitter-javascript-nd0q4pe9.wasm"; var init_tree_sitter_javascript = () => {}; // node_modules/@opentui/core/assets/typescript/highlights.scm var highlights_default2 = "./highlights-eq9cgrbb.scm"; var init_highlights2 = () => {}; // node_modules/@opentui/core/assets/typescript/tree-sitter-typescript.wasm var tree_sitter_typescript_default = "./tree-sitter-typescript-zxjzwt75.wasm"; var init_tree_sitter_typescript = () => {}; // node_modules/@opentui/core/assets/markdown/highlights.scm var highlights_default3 = "./highlights-r812a2qc.scm"; var init_highlights3 = () => {}; // node_modules/@opentui/core/assets/markdown/tree-sitter-markdown.wasm var tree_sitter_markdown_default = "./tree-sitter-markdown-411r6y9b.wasm"; var init_tree_sitter_markdown = () => {}; // node_modules/@opentui/core/assets/markdown/injections.scm var injections_default = "./injections-73j83es3.scm"; var init_injections = () => {}; // node_modules/@opentui/core/assets/markdown_inline/highlights.scm var highlights_default4 = "./highlights-x6tmsnaa.scm"; var init_highlights4 = () => {}; // node_modules/@opentui/core/assets/markdown_inline/tree-sitter-markdown_inline.wasm var tree_sitter_markdown_inline_default = "./tree-sitter-markdown_inline-j5349f42.wasm"; var init_tree_sitter_markdown_inline = () => {}; // node_modules/@opentui/core/assets/zig/highlights.scm var highlights_default5 = "./highlights-hk7bwhj4.scm"; var init_highlights5 = () => {}; // node_modules/@opentui/core/assets/zig/tree-sitter-zig.wasm var tree_sitter_zig_default = "./tree-sitter-zig-e78zbjpm.wasm"; var init_tree_sitter_zig = () => {}; // node_modules/@opentui/core/index-916mvx7m.js import { Buffer as Buffer2 } from "buffer"; import { EventEmitter } from "events"; import { EventEmitter as EventEmitter2 } from "events"; import { EventEmitter as EventEmitter3 } from "events"; import { resolve, dirname } from "path"; import { fileURLToPath } from "url"; import { resolve as resolve2, isAbsolute, parse } from "path"; import { existsSync } from "fs"; import { basename, join } from "path"; import os from "os"; import path from "path"; import { EventEmitter as EventEmitter4 } from "events"; import { dlopen, toArrayBuffer as toArrayBuffer4, JSCallback, ptr as ptr3 } from "bun:ffi"; import { existsSync as existsSync2 } from "fs"; import { EventEmitter as EventEmitter5 } from "events"; import { toArrayBuffer } from "bun:ffi"; import { ptr, toArrayBuffer as toArrayBuffer2 } from "bun:ffi"; import { ptr as ptr2, toArrayBuffer as toArrayBuffer3 } from "bun:ffi"; import { EventEmitter as EventEmitter6 } from "events"; import util from "util"; import { EventEmitter as EventEmitter8 } from "events"; import { Console } from "console"; import fs from "fs"; import path4 from "path"; import util2 from "util"; import { Writable } from "stream"; import { EventEmitter as EventEmitter7 } from "events"; import { EventEmitter as EventEmitter9 } from "events"; function wrapAssembly(lib) { function patch(prototype, name, fn) { const original = prototype[name]; prototype[name] = function() { for (var _len = arguments.length, args = new Array(_len), _key = 0;_key < _len; _key++) { args[_key] = arguments[_key]; } return fn.call(this, original, ...args); }; } for (const fnName of ["setPosition", "setMargin", "setFlexBasis", "setWidth", "setHeight", "setMinWidth", "setMinHeight", "setMaxWidth", "setMaxHeight", "setPadding", "setGap"]) { const methods = { [Unit.Point]: lib.Node.prototype[fnName], [Unit.Percent]: lib.Node.prototype[`${fnName}Percent`], [Unit.Auto]: lib.Node.prototype[`${fnName}Auto`] }; patch(lib.Node.prototype, fnName, function(original) { for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1;_key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } const value = args.pop(); let unit, asNumber; if (value === "auto") { unit = Unit.Auto; asNumber = undefined; } else if (typeof value === "object") { unit = value.unit; asNumber = value.valueOf(); } else { unit = typeof value === "string" && value.endsWith("%") ? Unit.Percent : Unit.Point; asNumber = parseFloat(value); if (value !== undefined && !Number.isNaN(value) && Number.isNaN(asNumber)) { throw new Error(`Invalid value ${value} for ${fnName}`); } } if (!methods[unit]) throw new Error(`Failed to execute "${fnName}": Unsupported unit '${value}'`); if (asNumber !== undefined) { return methods[unit].call(this, ...args, asNumber); } else { return methods[unit].call(this, ...args); } }); } function wrapMeasureFunction(measureFunction) { return lib.MeasureCallback.implement({ measure: function() { const { width, height } = measureFunction(...arguments); return { width: width ?? NaN, height: height ?? NaN }; } }); } patch(lib.Node.prototype, "setMeasureFunc", function(original, measureFunc) { if (measureFunc) { return original.call(this, wrapMeasureFunction(measureFunc)); } else { return this.unsetMeasureFunc(); } }); function wrapDirtiedFunc(dirtiedFunction) { return lib.DirtiedCallback.implement({ dirtied: dirtiedFunction }); } patch(lib.Node.prototype, "setDirtiedFunc", function(original, dirtiedFunc) { original.call(this, wrapDirtiedFunc(dirtiedFunc)); }); patch(lib.Config.prototype, "free", function() { lib.Config.destroy(this); }); patch(lib.Node, "create", (_, config) => { return config ? lib.Node.createWithConfig(config) : lib.Node.createDefault(); }); patch(lib.Node.prototype, "free", function() { lib.Node.destroy(this); }); patch(lib.Node.prototype, "freeRecursive", function() { for (let t = 0, T = this.getChildCount();t < T; ++t) { this.getChild(0).freeRecursive(); } this.free(); }); patch(lib.Node.prototype, "calculateLayout", function(original) { let width = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : NaN; let height = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : NaN; let direction = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Direction.LTR; return original.call(this, width, height, direction); }); return { Config: lib.Config, Node: lib.Node, ...YGEnums_default }; } function getBorderSides(border) { return border === true ? { top: true, right: true, bottom: true, left: true } : Array.isArray(border) ? { top: border.includes("top"), right: border.includes("right"), bottom: border.includes("bottom"), left: border.includes("left") } : { top: false, right: false, bottom: false, left: false }; } function borderCharsToArray(chars) { const array = new Uint32Array(11); array[0] = chars.topLeft.codePointAt(0); array[1] = chars.topRight.codePointAt(0); array[2] = chars.bottomLeft.codePointAt(0); array[3] = chars.bottomRight.codePointAt(0); array[4] = chars.horizontal.codePointAt(0); array[5] = chars.vertical.codePointAt(0); array[6] = chars.topT.codePointAt(0); array[7] = chars.bottomT.codePointAt(0); array[8] = chars.leftT.codePointAt(0); array[9] = chars.rightT.codePointAt(0); array[10] = chars.cross.codePointAt(0); return array; } function fromKittyMods(mod) { return { shift: !!(mod & 1), alt: !!(mod & 2), ctrl: !!(mod & 4), super: !!(mod & 8), hyper: !!(mod & 16), meta: !!(mod & 32), capsLock: !!(mod & 64), numLock: !!(mod & 128) }; } function parseKittySpecialKey(sequence) { const specialKeyRe = /^\x1b\[(\d+);(\d+):(\d+)([A-Z~])$/; const match = specialKeyRe.exec(sequence); if (!match) return null; const keyNumOrOne = match[1]; const modifierStr = match[2]; const eventTypeStr = match[3]; const terminator = match[4]; let keyName; if (terminator === "~") { keyName = tildeKeyMap[keyNumOrOne]; } else { if (keyNumOrOne !== "1") return null; keyName = functionalKeyMap[terminator]; } if (!keyName) return null; const key = { name: keyName, ctrl: false, meta: false, shift: false, option: false, number: false, sequence, raw: sequence, eventType: "press", source: "kitty", super: false, hyper: false, capsLock: false, numLock: false }; if (modifierStr) { const modifierMask = parseInt(modifierStr, 10); if (!isNaN(modifierMask) && modifierMask > 1) { const mods = fromKittyMods(modifierMask - 1); key.shift = mods.shift; key.ctrl = mods.ctrl; key.meta = mods.alt || mods.meta; key.option = mods.alt; key.super = mods.super; key.hyper = mods.hyper; key.capsLock = mods.capsLock; key.numLock = mods.numLock; } } if (eventTypeStr === "1" || !eventTypeStr) { key.eventType = "press"; } else if (eventTypeStr === "2") { key.eventType = "press"; key.repeated = true; } else if (eventTypeStr === "3") { key.eventType = "release"; } return key; } function parseKittyKeyboard(sequence) { const specialResult = parseKittySpecialKey(sequence); if (specialResult) return specialResult; const kittyRe = /^\x1b\[([^\x1b]+)u$/; const match = kittyRe.exec(sequence); if (!match) return null; const params = match[1]; const fields = params.split(";"); if (fields.length < 1) return null; const key = { name: "", ctrl: false, meta: false, shift: false, option: false, number: false, sequence, raw: sequence, eventType: "press", source: "kitty", super: false, hyper: false, capsLock: false, numLock: false }; let text = ""; const field1 = fields[0]?.split(":") || []; const codepointStr = field1[0]; if (!codepointStr) return null; const codepoint = parseInt(codepointStr, 10); if (isNaN(codepoint)) return null; let shiftedCodepoint; let baseCodepoint; if (field1[1]) { const shifted = parseInt(field1[1], 10); if (!isNaN(shifted) && shifted > 0 && shifted <= 1114111) { shiftedCodepoint = shifted; } } if (field1[2]) { const base = parseInt(field1[2], 10); if (!isNaN(base) && base > 0 && base <= 1114111) { baseCodepoint = base; } } const knownKey = kittyKeyMap[codepoint]; if (knownKey) { key.name = knownKey; key.code = `[${codepoint}u`; } else { if (codepoint > 0 && codepoint <= 1114111) { const char = String.fromCodePoint(codepoint); key.name = char; if (baseCodepoint) { key.baseCode = baseCodepoint; } } else { return null; } } if (fields[1]) { const field2 = fields[1].split(":"); const modifierStr = field2[0]; const eventTypeStr = field2[1]; if (modifierStr) { const modifierMask = parseInt(modifierStr, 10); if (!isNaN(modifierMask) && modifierMask > 1) { const mods = fromKittyMods(modifierMask - 1); key.shift = mods.shift; key.ctrl = mods.ctrl; key.meta = mods.alt || mods.meta; key.option = mods.alt; key.super = mods.super; key.hyper = mods.hyper; key.capsLock = mods.capsLock; key.numLock = mods.numLock; } } if (eventTypeStr === "1" || !eventTypeStr) { key.eventType = "press"; } else if (eventTypeStr === "2") { key.eventType = "press"; key.repeated = true; } else if (eventTypeStr === "3") { key.eventType = "release"; } else { key.eventType = "press"; } } if (fields[2]) { const codepoints = fields[2].split(":"); for (const cpStr of codepoints) { const cp = parseInt(cpStr, 10); if (!isNaN(cp) && cp > 0 && cp <= 1114111) { text += String.fromCodePoint(cp); } } } if (text === "") { const isPrintable = key.name.length > 0 && !kittyKeyMap[codepoint]; if (isPrintable) { if (key.shift && shiftedCodepoint) { text = String.fromCodePoint(shiftedCodepoint); } else { text = key.name; } } } if (key.name === " " && key.shift && !key.ctrl && !key.meta) { text = " "; } if (text) { key.sequence = text; } return key; } class KeyEvent { name; ctrl; meta; shift; option; sequence; number; raw; eventType; source; code; super; hyper; capsLock; numLock; baseCode; repeated; _defaultPrevented = false; constructor(key) { this.name = key.name; this.ctrl = key.ctrl; this.meta = key.meta; this.shift = key.shift; this.option = key.option; this.sequence = key.sequence; this.number = key.number; this.raw = key.raw; this.eventType = key.eventType; this.source = key.source; this.code = key.code; this.super = key.super; this.hyper = key.hyper; this.capsLock = key.capsLock; this.numLock = key.numLock; this.baseCode = key.baseCode; this.repeated = key.repeated; } get defaultPrevented() { return this._defaultPrevented; } preventDefault() { this._defaultPrevented = true; } } class PasteEvent { text; _defaultPrevented = false; constructor(text) { this.text = text; } get defaultPrevented() { return this._defaultPrevented; } preventDefault() { this._defaultPrevented = true; } } class RGBA { buffer; constructor(buffer) { this.buffer = buffer; } static fromArray(array) { return new RGBA(array); } static fromValues(r, g, b, a = 1) { return new RGBA(new Float32Array([r, g, b, a])); } static fromInts(r, g, b, a = 255) { return new RGBA(new Float32Array([r / 255, g / 255, b / 255, a / 255])); } static fromHex(hex) { return hexToRgb(hex); } toInts() { return [Math.round(this.r * 255), Math.round(this.g * 255), Math.round(this.b * 255), Math.round(this.a * 255)]; } get r() { return this.buffer[0]; } set r(value) { this.buffer[0] = value; } get g() { return this.buffer[1]; } set g(value) { this.buffer[1] = value; } get b() { return this.buffer[2]; } set b(value) { this.buffer[2] = value; } get a() { return this.buffer[3]; } set a(value) { this.buffer[3] = value; } map(fn) { return [fn(this.r), fn(this.g), fn(this.b), fn(this.a)]; } toString() { return `rgba(${this.r.toFixed(2)}, ${this.g.toFixed(2)}, ${this.b.toFixed(2)}, ${this.a.toFixed(2)})`; } } function hexToRgb(hex) { hex = hex.replace(/^#/, ""); if (hex.length === 3) { hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } else if (hex.length === 4) { hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3]; } if (!/^[0-9A-Fa-f]{6}$/.test(hex) && !/^[0-9A-Fa-f]{8}$/.test(hex)) { console.warn(`Invalid hex color: ${hex}, defaulting to magenta`); return RGBA.fromValues(1, 0, 1, 1); } const r = parseInt(hex.substring(0, 2), 16) / 255; const g = parseInt(hex.substring(2, 4), 16) / 255; const b = parseInt(hex.substring(4, 6), 16) / 255; const a = hex.length === 8 ? parseInt(hex.substring(6, 8), 16) / 255 : 1; return RGBA.fromValues(r, g, b, a); } function parseColor(color) { if (typeof color === "string") { const lowerColor = color.toLowerCase(); if (lowerColor === "transparent") { return RGBA.fromValues(0, 0, 0, 0); } if (CSS_COLOR_NAMES[lowerColor]) { return hexToRgb(CSS_COLOR_NAMES[lowerColor]); } return hexToRgb(color); } return color; } function parseColorTags(text) { const segments = []; let currentIndex = 0; const colorTagRegex = /<c(\d+)>(.*?)<\/c\d+>/g; let lastIndex = 0; let match; while ((match = colorTagRegex.exec(text)) !== null) { if (match.index > lastIndex) { const plainText = text.slice(lastIndex, match.index); if (plainText) { segments.push({ text: plainText, colorIndex: 0 }); } } const colorIndex = parseInt(match[1]) - 1; const taggedText = match[2]; segments.push({ text: taggedText, colorIndex: Math.max(0, colorIndex) }); lastIndex = match.index + match[0].length; } if (lastIndex < text.length) { const remainingText = text.slice(lastIndex); if (remainingText) { segments.push({ text: remainingText, colorIndex: 0 }); } } return segments; } function getParsedFont(fontKey) { if (!parsedFonts[fontKey]) { const fontDef = fonts[fontKey]; const parsedChars = {}; for (const [char, lines] of Object.entries(fontDef.chars)) { parsedChars[char] = lines.map((line) => parseColorTags(line)); } parsedFonts[fontKey] = { ...fontDef, colors: fontDef.colors || 1, chars: parsedChars }; } return parsedFonts[fontKey]; } function measureText({ text, font = "tiny" }) { const fontDef = getParsedFont(font); if (!fontDef) { console.warn(`Font '${font}' not found`); return { width: 0, height: 0 }; } let currentX = 0; for (let i = 0;i < text.length; i++) { const char = text[i].toUpperCase(); const charDef = fontDef.chars[char]; if (!charDef) { const spaceChar = fontDef.chars[" "]; if (spaceChar && spaceChar[0]) { let spaceWidth = 0; for (const segment of spaceChar[0]) { spaceWidth += segment.text.length; } currentX += spaceWidth; } else { currentX += 1; } continue; } let charWidth = 0; if (charDef[0]) { for (const segment of charDef[0]) { charWidth += segment.text.length; } } currentX += charWidth; if (i < text.length - 1) { currentX += fontDef.letterspace_size; } } return { width: currentX, height: fontDef.lines }; } function getCharacterPositions(text, font = "tiny") { const fontDef = getParsedFont(font); if (!fontDef) { return [0]; } const positions = [0]; let currentX = 0; for (let i = 0;i < text.length; i++) { const char = text[i].toUpperCase(); const charDef = fontDef.chars[char]; let charWidth = 0; if (!charDef) { const spaceChar = fontDef.chars[" "]; if (spaceChar && spaceChar[0]) { for (const segment of spaceChar[0]) { charWidth += segment.text.length; } } else { charWidth = 1; } } else if (charDef[0]) { for (const segment of charDef[0]) { charWidth += segment.text.length; } } currentX += charWidth; if (i < text.length - 1) { currentX += fontDef.letterspace_size; } positions.push(currentX); } return positions; } function coordinateToCharacterIndex(x, text, font = "tiny") { const positions = getCharacterPositions(text, font); if (x < 0) { return 0; } for (let i = 0;i < positions.length - 1; i++) { const currentPos = positions[i]; const nextPos = positions[i + 1]; if (x >= currentPos && x < nextPos) { const charMidpoint = currentPos + (nextPos - currentPos) / 2; return x < charMidpoint ? i : i + 1; } } if (positions.length > 0 && x >= positions[positions.length - 1]) { return text.length; } return 0; } function renderFontToFrameBuffer(buffer, { text, x = 0, y = 0, color = [RGBA.fromInts(255, 255, 255, 255)], backgroundColor = RGBA.fromInts(0, 0, 0, 255), font = "tiny" }) { const width = buffer.width; const height = buffer.height; const fontDef = getParsedFont(font); if (!fontDef) { console.warn(`Font '${font}' not found`); return { width: 0, height: 0 }; } const colors = Array.isArray(color) ? color : [color]; if (y < 0 || y + fontDef.lines > height) { return { width: 0, height: fontDef.lines }; } let currentX = x; const startX = x; for (let i = 0;i < text.length; i++) { const char = text[i].toUpperCase(); const charDef = fontDef.chars[char]; if (!charDef) { const spaceChar = fontDef.chars[" "]; if (spaceChar && spaceChar[0]) { let spaceWidth = 0; for (const segment of spaceChar[0]) { spaceWidth += segment.text.length; } currentX += spaceWidth; } else { currentX += 1; } continue; } let charWidth = 0; if (charDef[0]) { for (const segment of charDef[0]) { charWidth += segment.text.length; } } if (currentX >= width) break; if (currentX + charWidth < 0) { currentX += charWidth + fontDef.letterspace_size; continue; } for (let lineIdx = 0;lineIdx < fontDef.lines && lineIdx < charDef.length; lineIdx++) { const segments = charDef[lineIdx]; const renderY = y + lineIdx; if (renderY >= 0 && renderY < height) { let segmentX = currentX; for (const segment of segments) { const segmentColor = colors[segment.colorIndex] || colors[0]; for (let charIdx = 0;charIdx < segment.text.length; charIdx++) { const renderX = segmentX + charIdx; if (renderX >= 0 && renderX < width) { const fontChar = segment.text[charIdx]; if (fontChar !== " ") { buffer.setCellWithAlphaBlending(renderX, renderY, fontChar, parseColor(segmentColor), parseColor(backgroundColor)); } } } segmentX += segment.text.length; } } } currentX += charWidth; if (i < text.length - 1) { currentX += fontDef.letterspace_size; } } return { width: currentX - startX, height: fontDef.lines }; } function createTextAttributes({ bold = false, italic = false, underline = false, dim = false, blink = false, inverse = false, hidden = false, strikethrough = false } = {}) { let attributes = TextAttributes.NONE; if (bold) attributes |= TextAttributes.BOLD; if (italic) attributes |= TextAttributes.ITALIC; if (underline) attributes |= TextAttributes.UNDERLINE; if (dim) attributes |= TextAttributes.DIM; if (blink) attributes |= TextAttributes.BLINK; if (inverse) attributes |= TextAttributes.INVERSE; if (hidden) attributes |= TextAttributes.HIDDEN; if (strikethrough) attributes |= TextAttributes.STRIKETHROUGH; return attributes; } function isStyledText(obj) { return obj && obj[BrandedStyledText]; } function stringToStyledText(content) { const chunk = { __isChunk: true, text: content }; return new StyledText([chunk]); } function applyStyle(input, style) { if (typeof input === "object" && "__isChunk" in input) { const existingChunk = input; const fg = style.fg ? parseColor(style.fg) : existingChunk.fg; const bg = style.bg ? parseColor(style.bg) : existingChunk.bg; const newAttrs = createTextAttributes(style); const mergedAttrs = existingChunk.attributes ? existingChunk.attributes | newAttrs : newAttrs; return { __isChunk: true, text: existingChunk.text, fg, bg, attributes: mergedAttrs }; } else { const plainTextStr = String(input); const fg = style.fg ? parseColor(style.fg) : undefined; const bg = style.bg ? parseColor(style.bg) : undefined; const attributes = createTextAttributes(style); return { __isChunk: true, text: plainTextStr, fg, bg, attributes }; } } class LinearScrollAccel { tick(_now) { return 1; } reset() {} } function isCompleteSequence(data) { if (!data.startsWith(ESC)) { return "not-escape"; } if (data.length === 1) { return "incomplete"; } const afterEsc = data.slice(1); if (afterEsc.startsWith("[")) { if (afterEsc.startsWith("[M")) { return data.length >= 6 ? "complete" : "incomplete"; } return isCompleteCsiSequence(data); } if (afterEsc.startsWith("]")) { return isCompleteOscSequence(data); } if (afterEsc.startsWith("P")) { return isCompleteDcsSequence(data); } if (afterEsc.startsWith("_")) { return isCompleteApcSequence(data); } if (afterEsc.startsWith("O")) { return afterEsc.length >= 2 ? "complete" : "incomplete"; } if (afterEsc.length === 1) { return "complete"; } return "complete"; } function isCompleteCsiSequence(data) { if (!data.startsWith(ESC + "[")) { return "complete"; } if (data.length < 3) { return "incomplete"; } const payload = data.slice(2); const lastChar = payload[payload.length - 1]; const lastCharCode = lastChar.charCodeAt(0); if (lastCharCode >= 64 && lastCharCode <= 126) { if (payload.startsWith("<")) { const mouseMatch = /^<\d+;\d+;\d+[Mm]$/.test(payload); if (mouseMatch) { return "complete"; } if (lastChar === "M" || lastChar === "m") { const parts = payload.slice(1, -1).split(";"); if (parts.length === 3 && parts.every((p) => /^\d+$/.test(p))) { return "complete"; } } return "incomplete"; } return "complete"; } return "incomplete"; } function isCompleteOscSequence(data) { if (!data.startsWith(ESC + "]")) { return "complete"; } if (data.endsWith(ESC + "\\") || data.endsWith("\x07")) { return "complete"; } return "incomplete"; } function isCompleteDcsSequence(data) { if (!data.startsWith(ESC + "P")) { return "complete"; } if (data.endsWith(ESC + "\\")) { return "complete"; } return "incomplete"; } function isCompleteApcSequence(data) { if (!data.startsWith(ESC + "_")) { return "complete"; } if (data.endsWith(ESC + "\\")) { return "complete"; } return "incomplete"; } function extractCompleteSequences(buffer) { const sequences = []; let pos = 0; while (pos < buffer.length) { const remaining = buffer.slice(pos); if (remaining.startsWith(ESC)) { let seqEnd = 1; while (seqEnd <= remaining.length) { const candidate = remaining.slice(0, seqEnd); const status = isCompleteSequence(candidate); if (status === "complete") { sequences.push(candidate); pos += seqEnd; break; } else if (status === "incomplete") { seqEnd++; } else { sequences.push(candidate); pos += seqEnd; break; } } if (seqEnd > remaining.length) { return { sequences, remainder: remaining }; } } else { sequences.push(remaining[0]); pos++; } } return { sequences, remainder: "" }; } function parseAlign(value) { if (value == null) { return Align.Auto; } switch (value.toLowerCase()) { case "auto": return Align.Auto; case "flex-start": return Align.FlexStart; case "center": return Align.Center; case "flex-end": return Align.FlexEnd; case "stretch": return Align.Stretch; case "baseline": return Align.Baseline; case "space-between": return Align.SpaceBetween; case "space-around": return Align.SpaceAround; case "space-evenly": return Align.SpaceEvenly; default: return Align.Auto; } } function parseFlexDirection(value) { if (value == null) { return FlexDirection.Column; } switch (value.toLowerCase()) { case "column": return FlexDirection.Column; case "column-reverse": return FlexDirection.ColumnReverse; case "row": return FlexDirection.Row; case "row-reverse": return FlexDirection.RowReverse; default: return FlexDirection.Column; } } function parseJustify(value) { if (value == null) { return Justify.FlexStart; } switch (value.toLowerCase()) { case "flex-start": return Justify.FlexStart; case "center": return Justify.Center; case "flex-end": return Justify.FlexEnd; case "space-between": return Justify.SpaceBetween; case "space-around": return Justify.SpaceAround; case "space-evenly": return Justify.SpaceEvenly; default: return Justify.FlexStart; } } function parseOverflow(value) { if (value == null) { return Overflow.Visible; } switch (value.toLowerCase()) { case "visible": return Overflow.Visible; case "hidden": return Overflow.Hidden; case "scroll": return Overflow.Scroll; default: return Overflow.Visible; } } function parsePositionType(value) { if (value == null) { return PositionType.Relative; } switch (value.toLowerCase()) { case "static": return PositionType.Static; case "relative": return PositionType.Relative; case "absolute": return PositionType.Absolute; default: return PositionType.Static; } } function parseWrap(value) { if (value == null) { return Wrap.NoWrap; } switch (value.toLowerCase()) { case "no-wrap": return Wrap.NoWrap; case "wrap": return Wrap.Wrap; case "wrap-reverse": return Wrap.WrapReverse; default: return Wrap.NoWrap; } } class SelectionAnchor { renderable; relativeX; relativeY; constructor(renderable, absoluteX, absoluteY) { this.renderable = renderable; this.relativeX = absoluteX - this.renderable.x; this.relativeY = absoluteY - this.renderable.y; } get x() { return this.renderable.x + this.relativeX; } get y() { return this.renderable.y + this.relativeY; } } class Selection { _anchor; _originalFocus; _normalizedAnchor; _normalizedFocus; _selectedRenderables = []; _touchedRenderables = []; _isActive = true; _isSelecting = true; constructor(anchorRenderable, anchor, focus) { this._anchor = new SelectionAnchor(anchorRenderable, anchor.x, anchor.y); this._originalFocus = { ...focus }; this._updateNormalizedSelection(); } get anchor() { return { ...this._normalizedAnchor }; } get focus() { return { ...this._normalizedFocus }; } set focus(value) { this._originalFocus = { ...value }; this._updateNormalizedSelection(); } _updateNormalizedSelection() { const anchorBeforeFocus = this._anchor.y < this._originalFocus.y || this._anchor.y === this._originalFocus.y && this._anchor.x <= this._originalFocus.x; if (anchorBeforeFocus) { this._normalizedAnchor = { x: this._anchor.x, y: this._anchor.y }; this._normalizedFocus = { ...this._originalFocus }; } else { this._normalizedAnchor = { ...this._originalFocus }; this._normalizedFocus = { x: this._anchor.x + 1, y: this._anchor.y }; } } get isActive() { return this._isActive; } set isActive(value) { this._isActive = value; } get isSelecting() { return this._isSelecting; } set isSelecting(value) { this._isSelecting = value; } get bounds() { return { x: Math.min(this._normalizedAnchor.x, this._normalizedFocus.x), y: Math.min(this._normalizedAnchor.y, this._normalizedFocus.y), width: Math.max(this._normalizedAnchor.x, this._normalizedFocus.x) - Math.min(this._normalizedAnchor.x, this._normalizedFocus.x), height: Math.max(this._normalizedAnchor.y, this._normalizedFocus.y) - Math.min(this._normalizedAnchor.y, this._normalizedFocus.y) }; } updateSelectedRenderables(selectedRenderables) { this._selectedRenderables = selectedRenderables; } get selectedRenderables() { return this._selectedRenderables; } updateTouchedRenderables(touchedRenderables) { this._touchedRenderables = touchedRenderables; } get touchedRenderables() { return this._touchedRenderables; } getSelectedText() { const selectedTexts = this._selectedRenderables.sort((a, b) => { const aY = a.y; const bY = b.y; if (aY !== bY) { return aY - bY; } return a.x - b.x; }).filter((renderable) => !renderable.isDestroyed).map((renderable) => renderable.getSelectedText()).filter((text) => text); return selectedTexts.join(` `); } } function convertGlobalToLocalSelection(globalSelection, localX, localY) { if (!globalSelection?.isActive) { return null; } return { anchorX: globalSelection.anchor.x - localX, anchorY: globalSelection.anchor.y - localY, focusX: globalSelection.focus.x - localX, focusY: globalSelection.focus.y - localY, isActive: true }; } class ASCIIFontSelectionHelper { getText; getFont; localSelection = null; constructor(getText, getFont) { this.getText = getText; this.getFont = getFont; } hasSelection() { return this.localSelection !== null; } getSelection() { return this.localSelection; } shouldStartSelection(localX, localY, width, height) { if (localX < 0 || localX >= width || localY < 0 || localY >= height) { return false; } const text = this.getText(); const font = this.getFont(); const charIndex = coordinateToCharacterIndex(localX, text, font); return charIndex >= 0 && charIndex <= text.length; } onLocalSelectionChanged(localSelection, width, height) { const previousSelection = this.localSelection; if (!localSelection?.isActive) { this.localSelection = null; return previousSelection !== null; } const text = this.getText(); const font = this.getFont(); const selStart = { x: localSelection.anchorX, y: localSelection.anchorY }; const selEnd = { x: localSelection.focusX, y: localSelection.focusY }; if (height - 1 < selStart.y || 0 > selEnd.y) { this.localSelection = null; return previousSelection !== null; } let startCharIndex = 0; let endCharIndex = text.length; if (selStart.y > height - 1) { this.localSelection = null; return previousSelection !== null; } else if (selStart.y >= 0 && selStart.y <= height - 1) { if (selStart.x > 0) { startCharIndex = coordinateToCharacterIndex(selStart.x, text, font); } } if (selEnd.y < 0) { this.localSelection = null; return previousSelection !== null; } else if (selEnd.y >= 0 && selEnd.y <= height - 1) { if (selEnd.x >= 0) { endCharIndex = coordinateToCharacterIndex(selEnd.x, text, font); } else { endCharIndex = 0; } } if (startCharIndex < endCharIndex && startCharIndex >= 0 && endCharIndex <= text.length) { this.localSelection = { start: startCharIndex, end: endCharIndex }; } else { this.localSelection = null; } return previousSelection?.start !== this.localSelection?.start || previousSelection?.end !== this.localSelection?.end; } } function singleton(key, factory) { const bag = globalThis[singletonCacheSymbol] ??= {}; if (!(key in bag)) { bag[key] = factory(); } return bag[key]; } function destroySingleton(key) { const bag = globalThis[singletonCacheSymbol]; if (bag && key in bag) { delete bag[key]; } } function hasSingleton(key) { const bag = globalThis[singletonCacheSymbol]; return bag && key in bag; } function registerEnvVar(config) { const existing = envRegistry[config.name]; if (existing) { if (existing.description !== config.description || existing.type !== config.type || existing.default !== config.default) { throw new Error(`Environment variable "${config.name}" is already registered with different configuration. ` + `Existing: ${JSON.stringify(existing)}, New: ${JSON.stringify(config)}`); } return; } envRegistry[config.name] = config; } function normalizeBoolean(value) { const lowerValue = value.toLowerCase(); return ["true", "1", "on", "yes"].includes(lowerValue); } function parseEnvValue(config) { const envValue = process.env[config.name]; if (envValue === undefined && config.default !== undefined) { return config.default; } if (envValue === undefined) { throw new Error(`Required environment variable ${config.name} is not set. ${config.description}`); } switch (config.type) { case "boolean": return typeof envValue === "boolean" ? envValue : normalizeBoolean(envValue); case "number": const numValue = Number(envValue); if (isNaN(numValue)) { throw new Error(`Environment variable ${config.name} must be a valid number, got: ${envValue}`); } return numValue; case "string": default: return envValue; } } class EnvStore { parsedValues = new Map; get(key) { if (this.parsedValues.has(key)) { return this.parsedValues.get(key); } if (!(key in envRegistry)) { throw new Error(`Environment variable ${key} is not registered.`); } try { const value = parseEnvValue(envRegistry[key]); this.parsedValues.set(key, value); return value; } catch (error) { throw new Error(`Failed to parse env var ${key}: ${error instanceof Error ? error.message : String(error)}`); } } has(key) { return key in envRegistry; } clearCache() { this.parsedValues.clear(); } } function getSpecificity(group) { return group.split(".").length; } function shouldSuppressInInjection(group, meta) { if (meta?.isInjection) { return false; } return group === "markup.raw.block"; } function treeSitterToTextChunks(content, highlights, syntaxStyle, options) { const chunks = []; const defaultStyle = syntaxStyle.getStyle("default"); const concealEnabled = options?.enabled ?? true; const injectionContainerRanges = []; const boundaries = []; for (let i = 0;i < highlights.length; i++) { const [start, end, , meta] = highlights[i]; if (start === end) continue; if (meta?.containsInjection) { injectionContainerRanges.push({ start, end }); } boundaries.push({ offset: start, type: "start", highlightIndex: i }); boundaries.push({ offset: end, type: "end", highlightIndex: i }); } boundaries.sort((a, b) => { if (a.offset !== b.offset) return a.offset - b.offset; if (a.type === "end" && b.type === "start") return -1; if (a.type === "start" && b.type === "end") return 1; return 0; }); const activeHighlights = new Set; let currentOffset = 0; for (let i = 0;i < boundaries.length; i++) { const boundary = boundaries[i]; if (currentOffset < boundary.offset && activeHighlights.size > 0) { const segmentText = content.slice(currentOffset, boundary.offset); const activeGroups = []; for (const idx of activeHighlights) { const [, , group, meta] = highlights[idx]; activeGroups.push({ group, meta, index: idx }); } const concealHighlight = concealEnabled ? activeGroups.find((h) => h.meta?.conceal !== undefined || h.group === "conceal" || h.group.startsWith("conceal.")) : undefined; if (concealHighlight) { let replacementText = ""; if (concealHighlight.meta?.conceal !== undefined) { replacementText = concealHighlight.meta.conceal; } else if (concealHighlight.group === "conceal.with.space") { replacementText = " "; } if (replacementText) { chunks.push({ __isChunk: true, text: replacementText, fg: defaultStyle?.fg, bg: defaultStyle?.bg, attributes: defaultStyle ? createTextAttributes({ bold: defaultStyle.bold, italic: defaultStyle.italic, underline: defaultStyle.underline, dim: defaultStyle.dim }) : 0 }); } } else { const insideInjectionContainer = injectionContainerRanges.some((range) => currentOffset >= range.start && currentOffset < range.end); const validGroups = activeGroups.filter((h) => { if (insideInjectionContainer && shouldSuppressInInjection(h.group, h.meta)) { return false; } return true; }); const sortedGroups = validGroups.sort((a, b) => { const aSpec = getSpecificity(a.group); const bSpec = getSpecificity(b.group); if (aSpec !== bSpec) return aSpec - bSpec; return a.index - b.index; }); const mergedStyle = {}; for (const { group } of sortedGroups) { let styleForGroup = syntaxStyle.getStyle(group); if (!styleForGroup && group.includes(".")) { const baseName = group.split(".")[0]; styleForGroup = syntaxStyle.getStyle(baseName); } if (styleForGroup) { if (styleForGroup.fg !== undefined) mergedStyle.fg = styleForGroup.fg; if (styleForGroup.bg !== undefined) mergedStyle.bg = styleForGroup.bg; if (styleForGroup.bold !== undefined) mergedStyle.bold = styleForGroup.bold; if (styleForGroup.italic !== undefined) mergedStyle.italic = styleForGroup.italic; if (styleForGroup.underline !== undefined) mergedStyle.underline = styleForGroup.underline; if (styleForGroup.dim !== undefined) mergedStyle.dim = styleForGroup.dim; } else { if (group.includes(".")) { const baseName = group.split(".")[0]; if (env.OTUI_TS_STYLE_WARN) { console.warn(`Syntax style not found for group "${group}" or base scope "${baseName}", using default style`); } } else { if (env.OTUI_TS_STYLE_WARN) { console.warn(`Syntax style not found for group "${group}", using default style`); } } } } const finalStyle = Object.keys(mergedStyle).length > 0 ? mergedStyle : defaultStyle; chunks.push({ __isChunk: true, text: segmentText, fg: finalStyle?.fg, bg: finalStyle?.bg, attributes: finalStyle ? createTextAttributes({ bold: finalStyle.bold, italic: finalStyle.italic, underline: finalStyle.underline, dim: finalStyle.dim }) : 0 }); } } else if (currentOffset < boundary.offset) { const text = content.slice(currentOffset, boundary.offset); chunks.push({ __isChunk: true, text, fg: defaultStyle?.fg, bg: defaultStyle?.bg, attributes: defaultStyle ? createTextAttributes({ bold: defaultStyle.bold, italic: defaultStyle.italic, underline: defaultStyle.underline, dim: defaultStyle.dim }) : 0 }); } if (boundary.type === "start") { activeHighlights.add(boundary.highlightIndex); } else { activeHighlights.delete(boundary.highlightIndex); if (concealEnabled) { const [, , group, meta] = highlights[boundary.highlightIndex]; if (meta?.concealLines !== undefined) { if (boundary.offset < content.length && content[boundary.offset] === ` `) { currentOffset = boundary.offset + 1; continue; } } if (meta?.conceal !== undefined) { if (meta.conceal === " ") { if (boundary.offset < content.length && content[boundary.offset] === " ") { currentOffset = boundary.offset + 1; continue; } } else if (meta.conceal === "" && group === "conceal" && !meta.isInjection) { if (boundary.offset < content.length && content[boundary.offset] === " ") { currentOffset = boundary.offset + 1; continue; } } } } } currentOffset = boundary.offset; } if (currentOffset < content.length) { const text = content.slice(currentOffset); chunks.push({ __isChunk: true, text, fg: defaultStyle?.fg, bg: defaultStyle?.bg, attributes: defaultStyle ? createTextAttributes({ bold: defaultStyle.bold, italic: defaultStyle.italic, underline: defaultStyle.underline, dim: defaultStyle.dim }) : 0 }); } return chunks; } class DebounceController { scopeId; constructor(scopeId) { this.scopeId = scopeId; if (!TIMERS_MAP.has(this.scopeId)) { TIMERS_MAP.set(this.scopeId, new Map); } } debounce(id, ms, fn) { const scopeMap = TIMERS_MAP.get(this.scopeId); return new Promise((resolve3, reject) => { if (scopeMap.has(id)) { clearTimeout(scopeMap.get(id)); } const timerId = setTimeout(() => { try { resolve3(fn()); } catch (error) { reject(error); } scopeMap.delete(id); }, ms); scopeMap.set(id, timerId); }); } clearDebounce(id) { const scopeMap = TIMERS_MAP.get(this.scopeId); if (scopeMap && scopeMap.has(id)) { clearTimeout(scopeMap.get(id)); scopeMap.delete(id); } } clear() { const scopeMap = TIMERS_MAP.get(this.scopeId); if (scopeMap) { scopeMap.forEach((timerId) => clearTimeout(timerId)); scopeMap.clear(); } } } function createDebounce(scopeId) { return new DebounceController(scopeId); } function clearDebounceScope(scopeId) { const scopeMap = TIMERS_MAP.get(scopeId); if (scopeMap) { scopeMap.forEach((timerId) => clearTimeout(timerId)); scopeMap.clear(); } } class ProcessQueue { processor; queue = []; processing = false; autoProcess = true; constructor(processor, autoProcess = true) { this.processor = processor; this.autoProcess = autoProcess; } enqueue(item) { this.queue.push(item); if (!this.processing && this.autoProcess) { this.processQueue(); } } processQueue() { if (this.queue.length === 0) { return; } this.processing = true; queueMicrotask(async () => { if (this.queue.length === 0) { this.processing = false; return; } const item = this.queue.shift(); try { await this.processor(item); } catch (error) { console.error("Error processing queue item:", error); } if (this.queue.length > 0) { this.processQueue(); } else { this.processing = false; } }); } clear() { this.queue = []; } isProcessing() { return this.processing; } size() { return this.queue.length; } } function getParsers() { if (!_cachedParsers) { _cachedParsers = [ { filetype: "javascript", queries: { highlights: [resolve(dirname(fileURLToPath(import.meta.url)), highlights_default)] }, wasm: resolve(dirname(fileURLToPath(import.meta.url)), tree_sitter_javascript_default) }, { filetype: "typescript", queries: { highlights: [resolve(dirname(fileURLToPath(import.meta.url)), highlights_default2)] }, wasm: resolve(dirname(fileURLToPath(import.meta.url)), tree_sitter_typescript_default) }, { filetype: "markdown", queries: { highlights: [resolve(dirname(fileURLToPath(import.meta.url)), highlights_default3)], injections: [resolve(dirname(fileURLToPath(import.meta.url)), injections_default)] }, wasm: resolve(dirname(fileURLToPath(import.meta.url)), tree_sitter_markdown_default), injectionMapping: { nodeTypes: { inline: "markdown_inline", pipe_table_cell: "markdown_inline" }, infoStringMap: { javascript: "javascript", js: "javascript", typescript: "typescript", ts: "typescript", markdown: "markdown", md: "markdown" } } }, { filetype: "markdown_inline", queries: { highlights: [resolve(dirname(fileURLToPath(import.meta.url)), highlights_default4)] }, wasm: resolve(dirname(fileURLToPath(import.meta.url)), tree_sitter_markdown_inline_default) }, { filetype: "zig", queries: { highlights: [resolve(dirname(fileURLToPath(import.meta.url)), highlights_default5)] }, wasm: resolve(dirname(fileURLToPath(import.meta.url)), tree_sitter_zig_default) } ]; } return _cachedParsers; } function isBunfsPath(path2) { return path2.includes("$bunfs") || /^B:[\\/]~BUN/i.test(path2); } function getBunfsRootPath() { return process.platform === "win32" ? "B:\\~BUN\\root" : "/$bunfs/root"; } function normalizeBunf