import test from 'brittle' import { readFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' import { attachBareOsTuiSdk } from '../../bare-os-booter/lib/services/bare-os-tui-sdk.js' const __dirname = dirname(fileURLToPath(import.meta.url)) const lib = (name) => join(__dirname, '../lib', name) async function loadEditTui() { const parts = await Promise.all([ readFile(lib('edit-ansi.js'), 'utf8'), readFile(lib('edit-highlight.js'), 'utf8'), readFile(lib('edit-buffer.js'), 'utf8'), readFile(lib('edit-tui.js'), 'utf8') ]) const hook = {} new Function( 'exports', parts.join('\n') + '\nexports.bareEditCreateTuiApp = bareEditCreateTuiApp;\nexports.bareEditTuiRunOpts = bareEditTuiRunOpts;\nexports.bareEditIsGotoChord = bareEditIsGotoChord;\n' )(hook) return hook } function makeStream(isTTY, writes) { return { isTTY: !!isTTY, raw: false, columns: 80, rows: 24, write(s) { writes.push(String(s)) }, setRawMode(v) { this.raw = !!v }, resume() {}, pause() {}, on() {}, removeListener() {} } } function makeCtx(extra) { const writes = [] const ctx = { env: {}, console: { error() {}, log() {} }, replStdin: makeStream(true, writes), replStdout: makeStream(true, writes), b4a: { from(s) { return Buffer.from(String(s)) }, toString(b) { return Buffer.from(b).toString('utf8') } }, vfs: { async writeFile() {} }, suspendReplForSubprocess() {}, resumeReplAfterSubprocess() {}, ...(extra || {}) } attachBareOsTuiSdk(ctx) return ctx } test('edit-tui source prefers ctx.tui.run with cell buffer', async (t) => { const src = await readFile(lib('edit-tui.js'), 'utf8') t.ok(src.includes('ctx.tui.run'), 'TEA path uses ctx.tui.run') t.ok(src.includes("buffer: 'cell'"), 'uses cell buffer') t.ok(src.includes('bareEditCreateTuiApp'), 'exports TEA factory') t.ok(src.includes('bareOsRunEditTuiLegacy'), 'keeps pre-SDK fallback') }) test('edit TEA app inserts, marks dirty, and quits on ctrl+x', async (t) => { const hook = await loadEditTui() const ctx = makeCtx() const app = hook.bareEditCreateTuiApp(ctx, { path: '/tmp/note.js', initialText: 'const x = 1\n', argv0: 'edit' }) t.is(app.buf.dirty, false) app.update(ctx.tui.decode('a')[0]) t.ok(app.buf.dirty) t.ok(app.buf.lines[0].startsWith('a')) const view = ctx.tui.style.stripAnsi(app.view()) t.ok(view.includes('edit'), 'status shows argv0') t.ok(view.includes('[Modified]')) app.update(ctx.tui.decode('\x18')[0]) t.is(app.mode, 'quit_confirm') const no = app.update(ctx.tui.decode('n')[0]) t.is(typeof no[1], 'function') t.is(no[1]().type, 'quit') const clean = hook.bareEditCreateTuiApp(ctx, { path: '/tmp/clean.txt', initialText: 'ok', argv0: 'edit' }) const q = clean.update(ctx.tui.decode('\x18')[0]) t.is(q[1]().type, 'quit') }) test('edit TEA app help overlay and search', async (t) => { const hook = await loadEditTui() const ctx = makeCtx() const app = hook.bareEditCreateTuiApp(ctx, { path: 'a.js', initialText: 'hello\nworld\n', argv0: 'edit' }) app.update(ctx.tui.decode('\x07')[0]) t.is(app.mode, 'help') const ovs = app.overlays({ width: 80, height: 24 }) t.ok(ovs.length >= 1) t.ok(ctx.tui.style.stripAnsi(ovs[0].text).includes('Bare OS edit')) app.update(ctx.tui.decode('x')[0]) t.is(app.mode, 'edit') app.update(ctx.tui.decode('\x17')[0]) t.is(app.mode, 'prompt_search') app.update(ctx.tui.decode('w')[0]) app.update(ctx.tui.decode('o')[0]) app.update(ctx.tui.decode('\r')[0]) t.is(app.mode, 'edit') t.is(app.buf.row, 1) t.is(app.buf.col, 0) }) test('edit TEA app save command writes vfs', async (t) => { const hook = await loadEditTui() let written = '' const ctx = makeCtx({ vfs: { async writeFile(_p, buf) { written = Buffer.from(buf).toString('utf8') } } }) const app = hook.bareEditCreateTuiApp(ctx, { path: '/tmp/a.txt', initialText: 'hi', argv0: 'edit' }) app.update(ctx.tui.decode('!')[0]) const pair = app.update(ctx.tui.decode('\x13')[0]) t.ok(typeof pair[1] === 'function') const msg = await pair[1]() app.update(msg) t.ok(msg.ok) t.ok(written.startsWith('!hi')) t.absent(app.buf.dirty) }) test('kernel edit bin includes ctx.tui.run after build', async (t) => { const bin = await readFile( join(__dirname, '../../../kernel/bin/edit'), 'utf8' ) t.ok(bin.includes('ctx.tui.run'), 'kernel edit uses ctx.tui.run') t.ok(bin.includes("buffer: 'cell'"), 'kernel edit uses cell buffer') })