mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-17 07:03:25 +00:00
Add minimal TUI rewrite with differential rendering
- New TUI implementation with 3-strategy differential rendering - Synchronized output (CSI 2026) for flicker-free updates - New components: Editor, Markdown, Loader, SelectList, Spacer - Editor: file autocomplete, slash commands, large paste markers - Markdown: RGB background colors, caching - Terminal: cursor movement, visibility, clear operations - Chat demo with color-coded messages
This commit is contained in:
parent
904fc909c9
commit
97c730c874
9 changed files with 1933 additions and 0 deletions
145
packages/tui/test/chat-simple.ts
Normal file
145
packages/tui/test/chat-simple.ts
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* Simple chat interface demo using tui-new.ts
|
||||
*/
|
||||
|
||||
import { CombinedAutocompleteProvider } from "../src/autocomplete.js";
|
||||
import { Editor } from "../src/components-new/editor.js";
|
||||
import { Loader } from "../src/components-new/loader.js";
|
||||
import { Markdown } from "../src/components-new/markdown.js";
|
||||
import { Spacer } from "../src/components-new/spacer.js";
|
||||
import { ProcessTerminal } from "../src/terminal.js";
|
||||
import { Text, TUI } from "../src/tui-new.js";
|
||||
|
||||
// Create terminal
|
||||
const terminal = new ProcessTerminal();
|
||||
|
||||
// Create TUI
|
||||
const tui = new TUI(terminal);
|
||||
|
||||
// Create chat container with some initial messages
|
||||
tui.addChild(new Text("Welcome to Simple Chat!"));
|
||||
tui.addChild(new Text("Type your messages below. Type '/' for commands. Press Ctrl+C to exit.\n"));
|
||||
|
||||
// Create editor with autocomplete
|
||||
const editor = new Editor();
|
||||
|
||||
// Set up autocomplete provider with slash commands and file completion
|
||||
const autocompleteProvider = new CombinedAutocompleteProvider(
|
||||
[
|
||||
{ name: "delete", description: "Delete the last message" },
|
||||
{ name: "clear", description: "Clear all messages" },
|
||||
],
|
||||
process.cwd(),
|
||||
);
|
||||
editor.setAutocompleteProvider(autocompleteProvider);
|
||||
|
||||
tui.addChild(editor);
|
||||
|
||||
// Focus the editor
|
||||
tui.setFocus(editor);
|
||||
|
||||
// Track if we're waiting for bot response
|
||||
let isResponding = false;
|
||||
|
||||
// Handle message submission
|
||||
editor.onSubmit = (value: string) => {
|
||||
// Prevent submission if already responding
|
||||
if (isResponding) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trimmed = value.trim();
|
||||
|
||||
// Handle slash commands
|
||||
if (trimmed === "/delete") {
|
||||
const children = tui.children;
|
||||
// Remove component before editor (if there are any besides the initial text)
|
||||
if (children.length > 3) {
|
||||
// children[0] = "Welcome to Simple Chat!"
|
||||
// children[1] = "Type your messages below..."
|
||||
// children[2...n-1] = messages
|
||||
// children[n] = editor
|
||||
children.splice(children.length - 2, 1);
|
||||
}
|
||||
tui.requestRender();
|
||||
return;
|
||||
}
|
||||
|
||||
if (trimmed === "/clear") {
|
||||
const children = tui.children;
|
||||
// Remove all messages but keep the welcome text and editor
|
||||
children.splice(2, children.length - 3);
|
||||
tui.requestRender();
|
||||
return;
|
||||
}
|
||||
|
||||
if (trimmed) {
|
||||
// Mark as responding and disable submit
|
||||
isResponding = true;
|
||||
editor.disableSubmit = true;
|
||||
|
||||
// Add user message with custom gray background (similar to Claude.ai)
|
||||
const userMessage = new Markdown(value, undefined, undefined, { r: 52, g: 53, b: 65 });
|
||||
|
||||
// Insert before the editor (which is last)
|
||||
const children = tui.children;
|
||||
children.splice(children.length - 1, 0, userMessage);
|
||||
|
||||
// Add spacer after user message
|
||||
children.splice(children.length - 1, 0, new Spacer());
|
||||
|
||||
// Add loader
|
||||
const loader = new Loader(tui, "Thinking...");
|
||||
children.splice(children.length - 1, 0, loader);
|
||||
|
||||
// Add spacer after loader
|
||||
const loaderSpacer = new Spacer();
|
||||
children.splice(children.length - 1, 0, loaderSpacer);
|
||||
|
||||
tui.requestRender();
|
||||
|
||||
// Simulate a 1 second delay
|
||||
setTimeout(() => {
|
||||
// Remove loader and its spacer
|
||||
const loaderIndex = children.indexOf(loader);
|
||||
if (loaderIndex !== -1) {
|
||||
children.splice(loaderIndex, 1);
|
||||
loader.stop();
|
||||
}
|
||||
const loaderSpacerIndex = children.indexOf(loaderSpacer);
|
||||
if (loaderSpacerIndex !== -1) {
|
||||
children.splice(loaderSpacerIndex, 1);
|
||||
}
|
||||
|
||||
// Simulate a response
|
||||
const responses = [
|
||||
"That's interesting! Tell me more.",
|
||||
"I see what you mean.",
|
||||
"Fascinating perspective!",
|
||||
"Could you elaborate on that?",
|
||||
"That makes sense to me.",
|
||||
"I hadn't thought of it that way.",
|
||||
"Great point!",
|
||||
"Thanks for sharing that.",
|
||||
];
|
||||
const randomResponse = responses[Math.floor(Math.random() * responses.length)];
|
||||
|
||||
// Add assistant message with no background (transparent)
|
||||
const botMessage = new Markdown(randomResponse);
|
||||
children.splice(children.length - 1, 0, botMessage);
|
||||
|
||||
// Add spacer after assistant message
|
||||
children.splice(children.length - 1, 0, new Spacer());
|
||||
|
||||
// Re-enable submit
|
||||
isResponding = false;
|
||||
editor.disableSubmit = false;
|
||||
|
||||
// Request render
|
||||
tui.requestRender();
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
// Start the TUI
|
||||
tui.start();
|
||||
Loading…
Add table
Add a link
Reference in a new issue