/**
* KHAELOR
* File: prototypes/tui-spike/driver/debug-ink-input.tsx
* Description: Diagnostic harness — traces stdin arrival vs Ink useInput delivery vs echo-frame
* write time, to attribute candidate B's measured input latency (skew vs real stall).
*
* Author: Simon-Pierre Boucher
* Contact: contact@spboucher.ai
*/
import fs from "node:fs";
import React, { useEffect, useReducer, useState } from "react";
import { Box, Static, Text, render, useApp, useInput } from "ink";
import { DemoModel } from "../shared/demo";
const log: string[] = [];
const t0 = process.hrtime.bigint();
const ms = () => Number(process.hrtime.bigint() - t0) / 1e6;
const model = new DemoModel();
process.stdin.on("data", (b: Buffer) => {
log.push(`${ms().toFixed(1)} DATA ${JSON.stringify(b.toString("utf8"))}`);
});
const origWrite = process.stdout.write.bind(process.stdout);
let lastComposer = "";
(process.stdout as unknown as { write: unknown }).write = (chunk: unknown, ...rest: unknown[]) => {
const s = typeof chunk === "string" ? chunk : Buffer.from(chunk as Uint8Array).toString("utf8");
if (lastComposer.length > 0 && s.includes("❯ " + lastComposer)) {
log.push(`${ms().toFixed(1)} ECHO ${JSON.stringify(lastComposer.slice(-8))}`);
lastComposer = "";
}
return (origWrite as (c: unknown, ...r: unknown[]) => boolean)(chunk, ...rest);
};
function App(): React.JSX.Element {
const { exit } = useApp();
const [settled, setSettled] = useState<{ id: number; text: string }[]>([]);
const [, force] = useReducer((x: number) => x + 1, 0);
useEffect(() => {
model.on("settled", (lines: string[]) =>
setSettled((s) => [...s, { id: s.length, text: lines.join("\n") }]),
);
model.on("dirty", () => force());
const ticker = setInterval(() => {
if (model.status !== null) force();
}, 100);
model.start();
return () => clearInterval(ticker);
}, []);
useInput((input, key) => {
log.push(
`${ms().toFixed(1)} USEINPUT ${JSON.stringify(input)} esc=${key.escape} ret=${key.return}`,
);
if (key.escape) {
exit();
return;
}
if (key.return) {
model.handleKey({ type: "enter" });
log.push(`${ms().toFixed(1)} ENTER composer_now=${JSON.stringify(model.composer)}`);
return;
}
for (const ch of input) model.handleKey({ type: "char", ch });
lastComposer = model.composer;
log.push(`${ms().toFixed(1)} COMPOSER ${JSON.stringify(model.composer.slice(-12))}`);
});
const snap = model.snapshot();
const tail = snap.tail.length > 0 ? snap.tail.split("\n").slice(-12) : [];
return (
<>
{(i) => {i.text}}
{tail.length > 0 && {tail.join("\n")}}
{"❯ " + snap.composer}
>
);
}
const inst = render(, { exitOnCtrlC: true, patchConsole: false });
setTimeout(() => inst.unmount(), Number(process.env.DURATION_MS ?? 6000)).unref();
await inst.waitUntilExit();
model.stop();
fs.writeFileSync(process.env.DEBUG_OUT ?? "out/debug-ink.log", log.join("\n") + "\n");
process.exit(0);