Skip to content

Commit 6afd415

Browse files
committed
feat(inquirerer): add AICodeUI with diff-based viewport rendering
- Add ViewportRenderer class with ansi-diff for minimal flicker rendering - Add AICodeUI class for claude-code style terminal interface - Implement two-channel output model (committed scrollback + live viewport) - Add line editor with cursor movement and character input - Add streaming text support for AI-style responses - Add demo-ai-code.ts showcasing the new interface - Add pnpm dev:ai-code script This implements the terminal UI spec for building interactive coding tools with scrollback preservation and diff-based updates.
1 parent 0a99a25 commit 6afd415

6 files changed

Lines changed: 3570 additions & 5114 deletions

File tree

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Demo: AI Code Assistant Interface
4+
*
5+
* This demo showcases the AICodeUI component which provides a claude-code style
6+
* terminal interface with:
7+
* - Diff-based rendering (minimal flicker)
8+
* - Scrollback preservation (committed messages stay in terminal history)
9+
* - Streaming text display for AI responses
10+
* - Line editor for user input
11+
* - In-app scrolling for chat history
12+
*
13+
* Run with: pnpm dev:ai-code
14+
*/
15+
16+
import { createAICodeUI } from '../src/ui';
17+
import { white, dim, cyan, green } from 'yanse';
18+
19+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
20+
21+
// Simulated AI responses based on user input
22+
const AI_RESPONSES: Record<string, string> = {
23+
'hello': "Hello! I'm your AI coding assistant. I can help you with:\n\n- Writing and explaining code\n- Debugging issues\n- Refactoring suggestions\n- Git workflows\n- And much more!\n\nWhat would you like help with today?",
24+
25+
'help': "Here are some things you can ask me:\n\n1. 'explain [code]' - I'll explain how code works\n2. 'write [description]' - I'll write code for you\n3. 'debug [error]' - I'll help debug issues\n4. 'refactor [code]' - I'll suggest improvements\n\nJust type naturally and I'll do my best to help!",
26+
27+
'typescript': "TypeScript is a strongly typed programming language that builds on JavaScript. Here's a quick example:\n\n```typescript\ninterface User {\n name: string;\n age: number;\n}\n\nfunction greet(user: User): string {\n return `Hello, ${user.name}!`;\n}\n```\n\nKey benefits:\n- Type safety catches errors at compile time\n- Better IDE support with autocomplete\n- Self-documenting code through types",
28+
29+
'git': "Here are some useful git commands:\n\n```bash\n# Create a new branch\ngit checkout -b feature/my-feature\n\n# Stage and commit changes\ngit add .\ngit commit -m \"feat: add new feature\"\n\n# Push to remote\ngit push origin feature/my-feature\n\n# Create a PR (using gh cli)\ngh pr create --title \"My Feature\" --body \"Description\"\n```\n\nWant me to explain any of these in more detail?",
30+
31+
'default': "I understand you're asking about that. Let me think...\n\nBased on my analysis, here's what I can tell you:\n\n1. First, consider the context of your question\n2. Then, break down the problem into smaller parts\n3. Finally, apply the appropriate solution\n\nWould you like me to elaborate on any specific aspect?",
32+
};
33+
34+
/**
35+
* Get a response based on user input
36+
*/
37+
function getResponse(input: string): string {
38+
const lowerInput = input.toLowerCase();
39+
40+
for (const [key, response] of Object.entries(AI_RESPONSES)) {
41+
if (key !== 'default' && lowerInput.includes(key)) {
42+
return response;
43+
}
44+
}
45+
46+
return AI_RESPONSES['default'];
47+
}
48+
49+
/**
50+
* Simulate streaming text character by character
51+
*/
52+
async function streamResponse(ui: ReturnType<typeof createAICodeUI>, text: string): Promise<void> {
53+
ui.startStream('assistant');
54+
55+
const words = text.split(' ');
56+
57+
for (let i = 0; i < words.length; i++) {
58+
const word = words[i];
59+
60+
// Stream word character by character for realistic effect
61+
for (const char of word) {
62+
ui.appendStream(char);
63+
await sleep(15 + Math.random() * 25); // Variable typing speed
64+
}
65+
66+
// Add space after word (except last)
67+
if (i < words.length - 1) {
68+
ui.appendStream(' ');
69+
await sleep(10);
70+
}
71+
}
72+
73+
// Small delay before ending stream
74+
await sleep(100);
75+
ui.endStream();
76+
}
77+
78+
/**
79+
* Main demo function
80+
*/
81+
async function main() {
82+
// Print welcome banner (this goes to scrollback)
83+
console.log('\n' + white('═'.repeat(60)));
84+
console.log(white(' AI Code Assistant Demo'));
85+
console.log(white(' Powered by inquirerer ViewportRenderer'));
86+
console.log(white('═'.repeat(60)));
87+
console.log(dim(' This demo showcases diff-based terminal rendering'));
88+
console.log(dim(' with scrollback preservation.'));
89+
console.log('');
90+
console.log(cyan(' Commands:'));
91+
console.log(dim(' - Type a message and press Enter to chat'));
92+
console.log(dim(' - Use UP/DOWN arrows to scroll through history'));
93+
console.log(dim(' - Press Ctrl+C to exit'));
94+
console.log('');
95+
console.log(green(' Try: "hello", "help", "typescript", or "git"'));
96+
console.log(white('═'.repeat(60)) + '\n');
97+
98+
// Create the AI Code UI
99+
const ui = createAICodeUI({
100+
title: 'AI Code Assistant',
101+
promptPrefix: '> ',
102+
viewportHeight: 14,
103+
104+
onSubmit: async (input) => {
105+
// Simulate thinking delay
106+
await sleep(300 + Math.random() * 500);
107+
108+
// Get and stream the response
109+
const response = getResponse(input);
110+
await streamResponse(ui, response);
111+
},
112+
113+
onExit: () => {
114+
console.log('\n' + dim('Goodbye! Thanks for trying the AI Code Assistant demo.') + '\n');
115+
process.exit(0);
116+
},
117+
});
118+
119+
// Start the UI
120+
ui.start();
121+
122+
// Add initial system message
123+
ui.addMessage('system', 'Welcome! Type a message below to start chatting.');
124+
}
125+
126+
// Run the demo
127+
main().catch(err => {
128+
console.error('Error:', err);
129+
process.exit(1);
130+
});

packages/inquirerer/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@
2929
"dev:chat": "ts-node dev/demo-chat",
3030
"dev:upgrade": "ts-node dev/demo-upgrade",
3131
"dev:prompts": "ts-node dev/demo-prompts-engine",
32+
"dev:ai-code": "ts-node dev/demo-ai-code",
3233
"test": "jest",
3334
"test:watch": "jest --watch"
3435
},
3536
"dependencies": {
37+
"ansi-diff": "^1.2.0",
3638
"deepmerge": "^4.3.1",
3739
"find-and-require-package-json": "workspace:*",
3840
"js-yaml": "^4.1.0",

0 commit comments

Comments
 (0)