automagik-cli
Version:
Automagik CLI - A powerful command-line interface for interacting with Automagik Hive multi-agent AI systems
262 lines (261 loc) • 11.7 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useCallback } from 'react';
import { Box, Text, useInput } from 'ink';
export const ProperMultilineInput = ({ onSubmit, disabled = false, placeholder = 'Type your message...', }) => {
const [input, setInput] = useState('');
const [cursorPosition, setCursorPosition] = useState(0);
// Convert cursor position to row/col
const lines = input.split('\n');
let currentRow = 0;
let currentCol = 0;
let charCount = 0;
for (let i = 0; i < lines.length; i++) {
if (charCount + lines[i].length >= cursorPosition) {
currentRow = i;
currentCol = cursorPosition - charCount;
break;
}
charCount += lines[i].length + 1; // +1 for newline
}
const isMultiline = lines.length > 1 || input.length > 80;
const handleSubmit = useCallback(() => {
if (input.trim() && !disabled) {
onSubmit(input.trim());
setInput('');
setCursorPosition(0);
}
}, [input, onSubmit, disabled]);
const insertText = useCallback((text) => {
const newInput = input.slice(0, cursorPosition) + text + input.slice(cursorPosition);
setInput(newInput);
setCursorPosition(cursorPosition + text.length);
}, [input, cursorPosition]);
const deleteText = useCallback((direction, wordMode = false) => {
if (direction === 'backward' && cursorPosition > 0) {
let deleteCount = 1;
if (wordMode) {
// Meta+Backspace: Delete word
let pos = cursorPosition - 1;
// Skip whitespace
while (pos >= 0 && /\s/.test(input[pos])) {
pos--;
}
// Delete word characters
while (pos >= 0 && !/\s/.test(input[pos])) {
pos--;
}
deleteCount = cursorPosition - pos - 1;
}
const newInput = input.slice(0, cursorPosition - deleteCount) + input.slice(cursorPosition);
setInput(newInput);
setCursorPosition(cursorPosition - deleteCount);
}
else if (direction === 'forward' && cursorPosition < input.length) {
let deleteCount = 1;
if (wordMode) {
// Meta+Delete: Delete word forward
let pos = cursorPosition;
// Skip whitespace
while (pos < input.length && /\s/.test(input[pos])) {
pos++;
}
// Delete word characters
while (pos < input.length && !/\s/.test(input[pos])) {
pos++;
}
deleteCount = pos - cursorPosition;
}
const newInput = input.slice(0, cursorPosition) + input.slice(cursorPosition + deleteCount);
setInput(newInput);
}
}, [input, cursorPosition]);
const moveCursor = useCallback((direction, wordMode = false) => {
switch (direction) {
case 'left':
if (wordMode) {
// Meta+Left: Move by word
let pos = cursorPosition - 1;
// Skip whitespace
while (pos >= 0 && /\s/.test(input[pos])) {
pos--;
}
// Move to start of word
while (pos >= 0 && !/\s/.test(input[pos])) {
pos--;
}
setCursorPosition(Math.max(0, pos + 1));
}
else {
setCursorPosition(Math.max(0, cursorPosition - 1));
}
break;
case 'right':
if (wordMode) {
// Meta+Right: Move by word
let pos = cursorPosition;
// Skip whitespace
while (pos < input.length && /\s/.test(input[pos])) {
pos++;
}
// Move to end of word
while (pos < input.length && !/\s/.test(input[pos])) {
pos++;
}
setCursorPosition(Math.min(input.length, pos));
}
else {
setCursorPosition(Math.min(input.length, cursorPosition + 1));
}
break;
case 'up':
if (currentRow > 0) {
const prevLineLength = lines[currentRow - 1].length;
const targetCol = Math.min(currentCol, prevLineLength);
// Calculate position in previous line
let newPos = 0;
for (let i = 0; i < currentRow - 1; i++) {
newPos += lines[i].length + 1;
}
newPos += targetCol;
setCursorPosition(newPos);
}
break;
case 'down':
if (currentRow < lines.length - 1) {
const nextLineLength = lines[currentRow + 1].length;
const targetCol = Math.min(currentCol, nextLineLength);
// Calculate position in next line
let newPos = 0;
for (let i = 0; i <= currentRow; i++) {
newPos += lines[i].length + 1;
}
newPos += targetCol;
setCursorPosition(newPos);
}
break;
case 'home':
// Move to start of current line
const lineStart = cursorPosition - currentCol;
setCursorPosition(lineStart);
break;
case 'end':
// Move to end of current line
const lineEnd = cursorPosition - currentCol + lines[currentRow].length;
setCursorPosition(lineEnd);
break;
}
}, [input, cursorPosition, currentRow, currentCol, lines]);
useInput((inputChar, key) => {
if (disabled)
return;
// Submit handling
if (key.return) {
if (key.ctrl || (!isMultiline && !key.shift)) {
// Ctrl+Enter or Enter in single-line mode = submit
handleSubmit();
}
else {
// Shift+Enter or Enter in multiline mode = new line
insertText('\n');
}
return;
}
// Navigation with word movement support
if (key.leftArrow) {
moveCursor('left', key.meta);
return;
}
if (key.rightArrow) {
moveCursor('right', key.meta);
return;
}
if (key.upArrow) {
moveCursor('up');
return;
}
if (key.downArrow) {
moveCursor('down');
return;
}
// Home/End keys with Ctrl
if (key.ctrl && key.leftArrow) {
moveCursor('home');
return;
}
if (key.ctrl && key.rightArrow) {
moveCursor('end');
return;
}
// Deletion with word support
if (key.backspace) {
deleteText('backward', key.meta);
return;
}
if (key.delete) {
deleteText('forward', key.meta);
return;
}
// Clear shortcuts
if (key.ctrl && inputChar === 'u') {
setInput('');
setCursorPosition(0);
return;
}
if (key.ctrl && inputChar === 'k') {
// Delete to end of line
const lineEnd = cursorPosition - currentCol + lines[currentRow].length;
const newInput = input.slice(0, cursorPosition) + input.slice(lineEnd);
setInput(newInput);
return;
}
// Select all
if (key.ctrl && inputChar === 'a') {
setCursorPosition(0);
// Note: We don't have selection support in this simple implementation
return;
}
// Regular character input
if (inputChar && !key.ctrl && !key.meta && inputChar.length === 1) {
const charCode = inputChar.charCodeAt(0);
if (charCode >= 32 || charCode === 9) { // Printable chars and tab
insertText(inputChar);
}
}
});
const renderInput = () => {
if (isMultiline) {
const maxLines = 6;
const visibleLines = lines.slice(0, maxLines);
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "cyan", children: ["\u250C\u2500 ", _jsx(Text, { color: "yellow", children: "multiline" }), " \u2500 ", lines.length, " lines, ", input.length, " chars \u2500\u2510"] }), visibleLines.map((line, lineIndex) => {
const isCurrentLine = lineIndex === currentRow;
if (isCurrentLine && !disabled) {
const beforeCursor = line.slice(0, currentCol);
const atCursor = line[currentCol] || ' ';
const afterCursor = line.slice(currentCol + 1);
return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "\u2502 " }), _jsx(Text, { children: beforeCursor }), _jsx(Text, { inverse: true, children: atCursor }), _jsx(Text, { children: afterCursor })] }, lineIndex));
}
else {
return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "\u2502 " }), _jsx(Text, { color: disabled ? 'gray' : 'white', children: line || (lineIndex === 0 && !input ? placeholder : '') })] }, lineIndex));
}
}), lines.length > maxLines && (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "\u2502 " }), _jsxs(Text, { color: "gray", children: ["... ", lines.length - maxLines, " more lines"] })] })), _jsxs(Text, { color: "cyan", children: ["\u2514", '─'.repeat(50), "\u2518"] })] }));
}
else {
// Single line mode
const displayText = input || placeholder;
const isPlaceholder = !input;
if (disabled) {
return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: '> ' }), _jsx(Text, { color: "gray", children: placeholder })] }));
}
if (isPlaceholder) {
return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: '> ' }), _jsx(Text, { color: "gray", children: placeholder })] }));
}
const beforeCursor = input.slice(0, cursorPosition);
const atCursor = input[cursorPosition] || ' ';
const afterCursor = input.slice(cursorPosition + 1);
return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: '> ' }), _jsx(Text, { children: beforeCursor }), _jsx(Text, { inverse: true, children: atCursor }), _jsx(Text, { children: afterCursor })] }));
}
};
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [renderInput(), !disabled && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: isMultiline
? 'Ctrl+Enter: Send • Enter: New line • Cmd+←/→: Word jump • Cmd+Backspace: Delete word'
: 'Enter: Send • Shift+Enter: New line • Cmd+←/→: Word jump' }) }))] }));
};