///|
// Global state management
extern "js" fn init_readline() -> Unit =
#|function() {
#| if (!globalThis.readlineState) {
#| // Detect environment: browser vs Node.js
#| const isBrowser = typeof window !== 'undefined' && typeof process === 'undefined';
#|
#| if (isBrowser) {
#| // Browser environment - use prompt()
#| globalThis.readlineState = {
#| isBrowser: true,
#| questionCb: null
#| };
#| } else {
#| // Node.js environment - use stdin/stdout with raw mode
#| const { stdin: input, stdout: output } = process;
#| input.setEncoding('utf8');
#| input.setRawMode(true);
#|
#| globalThis.readlineState = {
#| isBrowser: false,
#| questionCb: null,
#| currentLine: '',
#| cursorPos: 0,
#| history: [],
#| historyIndex: -1,
#| input,
#| output,
#| prompt: ''
#| };
#|
#| // Handle key input
#| input.on('data', chunk => {
#| const state = globalThis.readlineState;
#| if (!state.questionCb) return;
#|
#| const data = chunk.toString();
#|
#| // Handle special keys
#| if (data === '\u0003') { // Ctrl+C
#| process.exit(0);
#| } else if (data === '\r' || data === '\n') { // Enter
#| state.output.write('\n');
#| const line = state.currentLine;
#| if (line.trim()) {
#| state.history.push(line);
#| }
#| state.currentLine = '';
#| state.cursorPos = 0;
#| state.historyIndex = -1;
#| const cb = state.questionCb;
#| state.questionCb = null;
#| cb(line);
#| } else if (data === '\u007f' || data === '\b') { // Backspace
#| if (state.cursorPos > 0) {
#| state.currentLine = state.currentLine.slice(0, state.cursorPos - 1) +
#| state.currentLine.slice(state.cursorPos);
#| state.cursorPos--;
#| state.output.write('\b \b');
#| // Redraw line from cursor position
#| const remaining = state.currentLine.slice(state.cursorPos);
#| if (remaining) {
#| state.output.write(remaining + ' ');
#| state.output.write('\b'.repeat(remaining.length + 1));
#| }
#| }
#| } else if (data === '\u001b[A') { // Up arrow
#| if (state.history.length > 0) {
#| if (state.historyIndex === -1) {
#| state.historyIndex = state.history.length - 1;
#| } else if (state.historyIndex > 0) {
#| state.historyIndex--;
#| }
#| const historyLine = state.history[state.historyIndex];
#| // Clear current line
#| state.output.write('\r' + state.prompt + ' '.repeat(state.currentLine.length) + '\r' + state.prompt);
#| state.currentLine = historyLine;
#| state.cursorPos = historyLine.length;
#| state.output.write(historyLine);
#| }
#| } else if (data === '\u001b[B') { // Down arrow
#| if (state.historyIndex !== -1) {
#| if (state.historyIndex < state.history.length - 1) {
#| state.historyIndex++;
#| const historyLine = state.history[state.historyIndex];
#| // Clear current line
#| state.output.write('\r' + state.prompt + ' '.repeat(state.currentLine.length) + '\r' + state.prompt);
#| state.currentLine = historyLine;
#| state.cursorPos = historyLine.length;
#| state.output.write(historyLine);
#| } else {
#| state.historyIndex = -1;
#| // Clear current line
#| state.output.write('\r' + state.prompt + ' '.repeat(state.currentLine.length) + '\r' + state.prompt);
#| state.currentLine = '';
#| state.cursorPos = 0;
#| }
#| }
#| } else if (data === '\u001b[C') { // Right arrow
#| if (state.cursorPos < state.currentLine.length) {
#| state.cursorPos++;
#| state.output.write('\u001b[C');
#| }
#| } else if (data === '\u001b[D') { // Left arrow
#| if (state.cursorPos > 0) {
#| state.cursorPos--;
#| state.output.write('\u001b[D');
#| }
#| } else if (data.charCodeAt(0) >= 32) { // Printable characters
#| state.currentLine = state.currentLine.slice(0, state.cursorPos) +
#| data +
#| state.currentLine.slice(state.cursorPos);
#| state.cursorPos += data.length;
#| state.output.write(data);
#| // Redraw remaining characters
#| const remaining = state.currentLine.slice(state.cursorPos);
#| if (remaining) {
#| state.output.write(remaining);
#| state.output.write('\b'.repeat(remaining.length));
#| }
#| }
#| });
#| }
#| }
#|}
///|
extern "js" fn question_ffi(
question : String,
callback : (String) -> Unit,
) -> Unit =
#|function(question, callback) {
#| if (!globalThis.readlineState) {
#| throw new Error('Readline not initialized');
#| }
#| const state = globalThis.readlineState;
#|
#| if (state.isBrowser) {
#| // Browser environment - use prompt()
#| const result = prompt(question);
#| callback(result || '');
#| } else {
#| // Node.js environment - use stdin/stdout with line editing
#| state.prompt = question;
#| state.output.write(question);
#| state.questionCb = callback;
#| state.currentLine = '';
#| state.cursorPos = 0;
#| state.historyIndex = -1;
#| }
#|}
///|
extern "js" fn close_ffi() -> Unit =
#|function() {
#| if (globalThis.readlineState) {
#| const state = globalThis.readlineState;
#|
#| if (state.isBrowser) {
#| // Browser environment - simple cleanup
#| state.questionCb = null;
#| } else {
#| // Node.js environment - full cleanup
#| state.questionCb = null;
#| state.currentLine = '';
#| state.cursorPos = 0;
#| state.input.setRawMode(false);
#| state.input.removeAllListeners('data');
#| state.input.pause();
#| }
#|
#| // Clean up global state
#| delete globalThis.readlineState;
#| }
#|}
///|
fn wait_for_completion() -> Unit {
// No-op for JS version as it's already async
}