codeep 1.1.12 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/codeep.js +1 -1
- package/dist/config/index.js +10 -10
- package/dist/renderer/App.d.ts +430 -0
- package/dist/renderer/App.js +2712 -0
- package/dist/renderer/ChatUI.d.ts +71 -0
- package/dist/renderer/ChatUI.js +286 -0
- package/dist/renderer/Input.d.ts +72 -0
- package/dist/renderer/Input.js +371 -0
- package/dist/renderer/Screen.d.ts +79 -0
- package/dist/renderer/Screen.js +278 -0
- package/dist/renderer/ansi.d.ts +99 -0
- package/dist/renderer/ansi.js +176 -0
- package/dist/renderer/components/Box.d.ts +64 -0
- package/dist/renderer/components/Box.js +90 -0
- package/dist/renderer/components/Help.d.ts +30 -0
- package/dist/renderer/components/Help.js +195 -0
- package/dist/renderer/components/Intro.d.ts +12 -0
- package/dist/renderer/components/Intro.js +128 -0
- package/dist/renderer/components/Login.d.ts +42 -0
- package/dist/renderer/components/Login.js +178 -0
- package/dist/renderer/components/Modal.d.ts +43 -0
- package/dist/renderer/components/Modal.js +207 -0
- package/dist/renderer/components/Permission.d.ts +20 -0
- package/dist/renderer/components/Permission.js +113 -0
- package/dist/renderer/components/SelectScreen.d.ts +26 -0
- package/dist/renderer/components/SelectScreen.js +101 -0
- package/dist/renderer/components/Settings.d.ts +37 -0
- package/dist/renderer/components/Settings.js +333 -0
- package/dist/renderer/components/Status.d.ts +18 -0
- package/dist/renderer/components/Status.js +78 -0
- package/dist/renderer/demo-app.d.ts +6 -0
- package/dist/renderer/demo-app.js +85 -0
- package/dist/renderer/demo.d.ts +6 -0
- package/dist/renderer/demo.js +52 -0
- package/dist/renderer/index.d.ts +16 -0
- package/dist/renderer/index.js +17 -0
- package/dist/renderer/main.d.ts +6 -0
- package/dist/renderer/main.js +1634 -0
- package/dist/utils/agent.d.ts +21 -0
- package/dist/utils/agent.js +29 -0
- package/dist/utils/clipboard.d.ts +15 -0
- package/dist/utils/clipboard.js +95 -0
- package/package.json +7 -11
- package/dist/utils/console.d.ts +0 -55
- package/dist/utils/console.js +0 -188
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Chat UI - Proof of Concept
|
|
3
|
+
* Demonstrates custom renderer without Ink
|
|
4
|
+
*/
|
|
5
|
+
export interface ChatMessage {
|
|
6
|
+
role: 'user' | 'assistant' | 'system';
|
|
7
|
+
content: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ChatUIOptions {
|
|
10
|
+
onSubmit: (message: string) => void;
|
|
11
|
+
onExit: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare class ChatUI {
|
|
14
|
+
private screen;
|
|
15
|
+
private input;
|
|
16
|
+
private editor;
|
|
17
|
+
private messages;
|
|
18
|
+
private streamingContent;
|
|
19
|
+
private isStreaming;
|
|
20
|
+
private options;
|
|
21
|
+
private scrollOffset;
|
|
22
|
+
constructor(options: ChatUIOptions);
|
|
23
|
+
/**
|
|
24
|
+
* Start the UI
|
|
25
|
+
*/
|
|
26
|
+
start(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Stop the UI
|
|
29
|
+
*/
|
|
30
|
+
stop(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Add a message to chat
|
|
33
|
+
*/
|
|
34
|
+
addMessage(message: ChatMessage): void;
|
|
35
|
+
/**
|
|
36
|
+
* Start streaming response
|
|
37
|
+
*/
|
|
38
|
+
startStreaming(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Add chunk to streaming response
|
|
41
|
+
*/
|
|
42
|
+
addStreamChunk(chunk: string): void;
|
|
43
|
+
/**
|
|
44
|
+
* End streaming and add as message
|
|
45
|
+
*/
|
|
46
|
+
endStreaming(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Handle keyboard input
|
|
49
|
+
*/
|
|
50
|
+
private handleKey;
|
|
51
|
+
/**
|
|
52
|
+
* Render the entire UI
|
|
53
|
+
*/
|
|
54
|
+
render(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Full render (alias for render, used on start)
|
|
57
|
+
*/
|
|
58
|
+
private fullRender;
|
|
59
|
+
/**
|
|
60
|
+
* Format a message into lines
|
|
61
|
+
*/
|
|
62
|
+
private formatMessage;
|
|
63
|
+
/**
|
|
64
|
+
* Get messages formatted for visible area (including streaming)
|
|
65
|
+
*/
|
|
66
|
+
private getVisibleMessages;
|
|
67
|
+
/**
|
|
68
|
+
* Simple word wrap
|
|
69
|
+
*/
|
|
70
|
+
private wordWrap;
|
|
71
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Chat UI - Proof of Concept
|
|
3
|
+
* Demonstrates custom renderer without Ink
|
|
4
|
+
*/
|
|
5
|
+
import { Screen } from './Screen.js';
|
|
6
|
+
import { Input, LineEditor } from './Input.js';
|
|
7
|
+
import { fg } from './ansi.js';
|
|
8
|
+
export class ChatUI {
|
|
9
|
+
screen;
|
|
10
|
+
input;
|
|
11
|
+
editor;
|
|
12
|
+
messages = [];
|
|
13
|
+
streamingContent = '';
|
|
14
|
+
isStreaming = false;
|
|
15
|
+
options;
|
|
16
|
+
scrollOffset = 0;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.screen = new Screen();
|
|
19
|
+
this.input = new Input();
|
|
20
|
+
this.editor = new LineEditor();
|
|
21
|
+
this.options = options;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Start the UI
|
|
25
|
+
*/
|
|
26
|
+
start() {
|
|
27
|
+
this.screen.init();
|
|
28
|
+
this.input.start();
|
|
29
|
+
// Handle keyboard input
|
|
30
|
+
this.input.onKey((event) => this.handleKey(event));
|
|
31
|
+
// Initial render - use full render first time
|
|
32
|
+
this.fullRender();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Stop the UI
|
|
36
|
+
*/
|
|
37
|
+
stop() {
|
|
38
|
+
this.input.stop();
|
|
39
|
+
this.screen.cleanup();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Add a message to chat
|
|
43
|
+
*/
|
|
44
|
+
addMessage(message) {
|
|
45
|
+
this.messages.push(message);
|
|
46
|
+
this.scrollOffset = 0; // Reset scroll to bottom
|
|
47
|
+
this.render();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Start streaming response
|
|
51
|
+
*/
|
|
52
|
+
startStreaming() {
|
|
53
|
+
this.isStreaming = true;
|
|
54
|
+
this.streamingContent = '';
|
|
55
|
+
this.render();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Add chunk to streaming response
|
|
59
|
+
*/
|
|
60
|
+
addStreamChunk(chunk) {
|
|
61
|
+
this.streamingContent += chunk;
|
|
62
|
+
this.render();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* End streaming and add as message
|
|
66
|
+
*/
|
|
67
|
+
endStreaming() {
|
|
68
|
+
if (this.streamingContent) {
|
|
69
|
+
this.messages.push({
|
|
70
|
+
role: 'assistant',
|
|
71
|
+
content: this.streamingContent,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
this.streamingContent = '';
|
|
75
|
+
this.isStreaming = false;
|
|
76
|
+
this.render();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Handle keyboard input
|
|
80
|
+
*/
|
|
81
|
+
handleKey(event) {
|
|
82
|
+
// Ctrl+C or Ctrl+D to exit
|
|
83
|
+
if (event.ctrl && (event.key === 'c' || event.key === 'd')) {
|
|
84
|
+
this.stop();
|
|
85
|
+
this.options.onExit();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Escape to cancel streaming
|
|
89
|
+
if (event.key === 'escape' && this.isStreaming) {
|
|
90
|
+
this.endStreaming();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Ctrl+L to clear
|
|
94
|
+
if (event.ctrl && event.key === 'l') {
|
|
95
|
+
this.messages = [];
|
|
96
|
+
this.render();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Page up/down for scrolling
|
|
100
|
+
if (event.key === 'pageup') {
|
|
101
|
+
this.scrollOffset = Math.min(this.scrollOffset + 5, this.messages.length - 1);
|
|
102
|
+
this.render();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (event.key === 'pagedown') {
|
|
106
|
+
this.scrollOffset = Math.max(this.scrollOffset - 5, 0);
|
|
107
|
+
this.render();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Enter to submit
|
|
111
|
+
if (event.key === 'enter') {
|
|
112
|
+
const value = this.editor.getValue().trim();
|
|
113
|
+
if (value) {
|
|
114
|
+
this.editor.addToHistory(value);
|
|
115
|
+
this.editor.clear();
|
|
116
|
+
// Add user message
|
|
117
|
+
this.addMessage({ role: 'user', content: value });
|
|
118
|
+
// Callback
|
|
119
|
+
this.options.onSubmit(value);
|
|
120
|
+
}
|
|
121
|
+
this.render();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Handle editor keys
|
|
125
|
+
if (this.editor.handleKey(event)) {
|
|
126
|
+
this.render();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Render the entire UI
|
|
131
|
+
*/
|
|
132
|
+
render() {
|
|
133
|
+
const { width, height } = this.screen.getSize();
|
|
134
|
+
this.screen.clear();
|
|
135
|
+
// Layout:
|
|
136
|
+
// - Line 0: Header
|
|
137
|
+
// - Lines 1 to height-4: Messages
|
|
138
|
+
// - Line height-3: Separator
|
|
139
|
+
// - Line height-2: Input
|
|
140
|
+
// - Line height-1: Status bar
|
|
141
|
+
const headerLine = 0;
|
|
142
|
+
const messagesStart = 1;
|
|
143
|
+
const messagesEnd = height - 4;
|
|
144
|
+
const separatorLine = height - 3;
|
|
145
|
+
const inputLine = height - 2;
|
|
146
|
+
const statusLine = height - 1;
|
|
147
|
+
// Header
|
|
148
|
+
const header = ' Codeep Chat ';
|
|
149
|
+
const headerPadding = '─'.repeat(Math.max(0, (width - header.length) / 2));
|
|
150
|
+
this.screen.writeLine(headerLine, headerPadding + header + headerPadding, fg.cyan);
|
|
151
|
+
// Messages area (including streaming content)
|
|
152
|
+
const messagesHeight = messagesEnd - messagesStart + 1;
|
|
153
|
+
const messagesToRender = this.getVisibleMessages(messagesHeight, width - 2);
|
|
154
|
+
let y = messagesStart;
|
|
155
|
+
for (const line of messagesToRender) {
|
|
156
|
+
if (y > messagesEnd)
|
|
157
|
+
break;
|
|
158
|
+
this.screen.writeLine(y, line.text, line.style);
|
|
159
|
+
y++;
|
|
160
|
+
}
|
|
161
|
+
// Separator
|
|
162
|
+
this.screen.horizontalLine(separatorLine, '─', fg.gray);
|
|
163
|
+
// Input line
|
|
164
|
+
const prompt = '> ';
|
|
165
|
+
const inputValue = this.editor.getValue();
|
|
166
|
+
const cursorPos = this.editor.getCursorPos();
|
|
167
|
+
const maxInputWidth = width - prompt.length - 1;
|
|
168
|
+
// Calculate what part of input to show and where cursor should be
|
|
169
|
+
let displayValue;
|
|
170
|
+
let cursorX;
|
|
171
|
+
if (inputValue.length <= maxInputWidth) {
|
|
172
|
+
// Input fits - show all, cursor at actual position
|
|
173
|
+
displayValue = inputValue;
|
|
174
|
+
cursorX = prompt.length + cursorPos;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// Input too long - scroll to keep cursor visible
|
|
178
|
+
// Keep cursor roughly in the middle-right of visible area
|
|
179
|
+
const visibleStart = Math.max(0, cursorPos - Math.floor(maxInputWidth * 0.7));
|
|
180
|
+
const visibleEnd = visibleStart + maxInputWidth;
|
|
181
|
+
if (visibleStart > 0) {
|
|
182
|
+
displayValue = '…' + inputValue.slice(visibleStart + 1, visibleEnd);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
displayValue = inputValue.slice(0, maxInputWidth);
|
|
186
|
+
}
|
|
187
|
+
// Cursor position relative to visible portion
|
|
188
|
+
cursorX = prompt.length + (cursorPos - visibleStart);
|
|
189
|
+
if (visibleStart > 0) {
|
|
190
|
+
cursorX = prompt.length + (cursorPos - visibleStart);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
this.screen.writeLine(inputLine, prompt + displayValue, fg.green);
|
|
194
|
+
// Position cursor
|
|
195
|
+
this.screen.setCursor(cursorX, inputLine);
|
|
196
|
+
this.screen.showCursor(true);
|
|
197
|
+
// Status bar
|
|
198
|
+
const statusLeft = ` ${this.messages.length} messages`;
|
|
199
|
+
const statusRight = this.isStreaming ? 'Streaming... (Esc to cancel)' : 'Enter to send | Ctrl+C to exit';
|
|
200
|
+
const statusPadding = ' '.repeat(Math.max(0, width - statusLeft.length - statusRight.length));
|
|
201
|
+
this.screen.writeLine(statusLine, statusLeft + statusPadding + statusRight, fg.gray);
|
|
202
|
+
// Render to terminal (use fullRender for now - more reliable)
|
|
203
|
+
this.screen.fullRender();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Full render (alias for render, used on start)
|
|
207
|
+
*/
|
|
208
|
+
fullRender() {
|
|
209
|
+
this.render();
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Format a message into lines
|
|
213
|
+
*/
|
|
214
|
+
formatMessage(role, content, maxWidth) {
|
|
215
|
+
const lines = [];
|
|
216
|
+
// Role indicator
|
|
217
|
+
const roleStyle = role === 'user' ? fg.green : role === 'assistant' ? fg.cyan : fg.yellow;
|
|
218
|
+
const roleLabel = role === 'user' ? '> ' : role === 'assistant' ? ' ' : '# ';
|
|
219
|
+
// Split content into lines
|
|
220
|
+
const contentLines = content.split('\n');
|
|
221
|
+
for (let i = 0; i < contentLines.length; i++) {
|
|
222
|
+
const line = contentLines[i];
|
|
223
|
+
const prefix = i === 0 ? roleLabel : ' ';
|
|
224
|
+
const prefixStyle = i === 0 ? roleStyle : '';
|
|
225
|
+
// Word wrap long lines
|
|
226
|
+
if (line.length > maxWidth - prefix.length) {
|
|
227
|
+
const wrapped = this.wordWrap(line, maxWidth - prefix.length);
|
|
228
|
+
for (let j = 0; j < wrapped.length; j++) {
|
|
229
|
+
lines.push({
|
|
230
|
+
text: (j === 0 ? prefix : ' ') + wrapped[j],
|
|
231
|
+
style: j === 0 ? prefixStyle : '',
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
lines.push({
|
|
237
|
+
text: prefix + line,
|
|
238
|
+
style: prefixStyle,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// Add empty line after message
|
|
243
|
+
lines.push({ text: '', style: '' });
|
|
244
|
+
return lines;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get messages formatted for visible area (including streaming)
|
|
248
|
+
*/
|
|
249
|
+
getVisibleMessages(height, width) {
|
|
250
|
+
const allLines = [];
|
|
251
|
+
for (const msg of this.messages) {
|
|
252
|
+
const msgLines = this.formatMessage(msg.role, msg.content, width);
|
|
253
|
+
allLines.push(...msgLines);
|
|
254
|
+
}
|
|
255
|
+
// Add streaming content if active
|
|
256
|
+
if (this.isStreaming && this.streamingContent) {
|
|
257
|
+
const streamLines = this.formatMessage('assistant', this.streamingContent + '▊', width);
|
|
258
|
+
allLines.push(...streamLines);
|
|
259
|
+
}
|
|
260
|
+
// Apply scroll offset and return last 'height' lines
|
|
261
|
+
const startIndex = Math.max(0, allLines.length - height - this.scrollOffset);
|
|
262
|
+
const endIndex = allLines.length - this.scrollOffset;
|
|
263
|
+
return allLines.slice(startIndex, endIndex);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Simple word wrap
|
|
267
|
+
*/
|
|
268
|
+
wordWrap(text, maxWidth) {
|
|
269
|
+
const words = text.split(' ');
|
|
270
|
+
const lines = [];
|
|
271
|
+
let currentLine = '';
|
|
272
|
+
for (const word of words) {
|
|
273
|
+
if (currentLine.length + word.length + 1 > maxWidth && currentLine) {
|
|
274
|
+
lines.push(currentLine);
|
|
275
|
+
currentLine = word;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
currentLine += (currentLine ? ' ' : '') + word;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (currentLine) {
|
|
282
|
+
lines.push(currentLine);
|
|
283
|
+
}
|
|
284
|
+
return lines.length > 0 ? lines : [''];
|
|
285
|
+
}
|
|
286
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw input handling for terminal
|
|
3
|
+
* Handles keypresses, special keys, and line editing
|
|
4
|
+
*/
|
|
5
|
+
export interface KeyEvent {
|
|
6
|
+
key: string;
|
|
7
|
+
ctrl: boolean;
|
|
8
|
+
alt: boolean;
|
|
9
|
+
shift: boolean;
|
|
10
|
+
raw: string;
|
|
11
|
+
isPaste?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export type KeyHandler = (event: KeyEvent) => void;
|
|
14
|
+
export declare class Input {
|
|
15
|
+
private handlers;
|
|
16
|
+
private rl;
|
|
17
|
+
/**
|
|
18
|
+
* Start listening for input
|
|
19
|
+
*/
|
|
20
|
+
start(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Stop listening
|
|
23
|
+
*/
|
|
24
|
+
stop(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Add key handler
|
|
27
|
+
*/
|
|
28
|
+
onKey(handler: KeyHandler): () => void;
|
|
29
|
+
/**
|
|
30
|
+
* Emit key event to all handlers
|
|
31
|
+
*/
|
|
32
|
+
private emit;
|
|
33
|
+
/**
|
|
34
|
+
* Parse raw input into KeyEvent
|
|
35
|
+
*/
|
|
36
|
+
private parseKey;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Simple line editor with cursor support
|
|
40
|
+
*/
|
|
41
|
+
export declare class LineEditor {
|
|
42
|
+
private value;
|
|
43
|
+
private cursorPos;
|
|
44
|
+
private history;
|
|
45
|
+
private historyIndex;
|
|
46
|
+
private tempValue;
|
|
47
|
+
getValue(): string;
|
|
48
|
+
getCursorPos(): number;
|
|
49
|
+
setValue(value: string): void;
|
|
50
|
+
clear(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Insert text at cursor position
|
|
53
|
+
*/
|
|
54
|
+
insert(text: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Set cursor position
|
|
57
|
+
*/
|
|
58
|
+
setCursorPos(pos: number): void;
|
|
59
|
+
/**
|
|
60
|
+
* Delete word backward (Ctrl+W)
|
|
61
|
+
*/
|
|
62
|
+
deleteWordBackward(): void;
|
|
63
|
+
/**
|
|
64
|
+
* Delete to end of line (Ctrl+K)
|
|
65
|
+
*/
|
|
66
|
+
deleteToEnd(): void;
|
|
67
|
+
addToHistory(value: string): void;
|
|
68
|
+
/**
|
|
69
|
+
* Handle key event, returns true if value changed
|
|
70
|
+
*/
|
|
71
|
+
handleKey(event: KeyEvent): boolean;
|
|
72
|
+
}
|