Files
toolLooper/cli-ink.js

61 lines
1.9 KiB
JavaScript
Executable File

#!/usr/bin/env -S node --import tsx
import 'dotenv/config';
import React from 'react';
import { render } from 'ink';
import InkApp from './src/ui/InkApp.jsx';
import terminalService from './src/terminalService.js';
// Start the PTY backend independent from UI lifecycle
terminalService.start();
const { unmount } = render(React.createElement(InkApp));
// ESC to exit (only bare ESC, not escape sequences like arrows)
if (process.stdin.isTTY) {
try { process.stdin.setRawMode(true); } catch { }
let escPending = false;
let escTimer = null;
const exitCleanly = () => {
unmount();
try { terminalService.dispose(); } catch { }
try { process.stdin.setRawMode(false); } catch { }
process.exit(0);
};
const onData = (data) => {
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(String(data));
for (const byte of buffer) {
// Ctrl-C (ETX)
if (byte === 0x03) {
return exitCleanly();
}
if (!escPending) {
if (byte === 0x1b) { // ESC
escPending = true;
escTimer = setTimeout(() => {
// No additional byte followed: treat as bare ESC
escPending = false;
escTimer = null;
exitCleanly();
}, 120);
}
// else: ignore other bytes
} else {
// Some byte followed ESC quickly: it's an escape sequence → cancel exit
if (escTimer) { clearTimeout(escTimer); escTimer = null; }
escPending = false;
// Do not process further for exit
}
}
};
process.stdin.on('data', onData);
// Also handle SIGINT in case raw mode changes or comes from elsewhere
const onSigint = () => exitCleanly();
process.on('SIGINT', onSigint);
}