codeep 1.1.20 → 1.1.21
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/dist/renderer/App.d.ts +6 -2
- package/dist/renderer/App.js +70 -51
- package/dist/renderer/Screen.js +18 -3
- package/dist/renderer/ansi.d.ts +9 -0
- package/dist/renderer/ansi.js +84 -8
- package/dist/renderer/index.d.ts +1 -1
- package/dist/renderer/main.js +7 -0
- package/package.json +1 -1
package/dist/renderer/App.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ export interface Message {
|
|
|
8
8
|
role: 'user' | 'assistant' | 'system';
|
|
9
9
|
content: string;
|
|
10
10
|
}
|
|
11
|
-
export type AppScreen = 'chat' | 'status';
|
|
12
11
|
export interface ConfirmOptions {
|
|
13
12
|
title: string;
|
|
14
13
|
message: string[];
|
|
@@ -34,7 +33,6 @@ export declare class App {
|
|
|
34
33
|
private streamingContent;
|
|
35
34
|
private isStreaming;
|
|
36
35
|
private isLoading;
|
|
37
|
-
private currentScreen;
|
|
38
36
|
private options;
|
|
39
37
|
private scrollOffset;
|
|
40
38
|
private notification;
|
|
@@ -49,6 +47,7 @@ export declare class App {
|
|
|
49
47
|
private pasteInfoOpen;
|
|
50
48
|
private helpOpen;
|
|
51
49
|
private helpScrollIndex;
|
|
50
|
+
private statusOpen;
|
|
52
51
|
private settingsState;
|
|
53
52
|
private showAutocomplete;
|
|
54
53
|
private autocompleteIndex;
|
|
@@ -278,6 +277,10 @@ export declare class App {
|
|
|
278
277
|
* Update autocomplete suggestions
|
|
279
278
|
*/
|
|
280
279
|
private updateAutocomplete;
|
|
280
|
+
/**
|
|
281
|
+
* Handle inline status keys
|
|
282
|
+
*/
|
|
283
|
+
private handleInlineStatusKey;
|
|
281
284
|
/**
|
|
282
285
|
* Handle help screen keys
|
|
283
286
|
*/
|
|
@@ -347,6 +350,7 @@ export declare class App {
|
|
|
347
350
|
/**
|
|
348
351
|
* Render inline help below status bar
|
|
349
352
|
*/
|
|
353
|
+
private renderInlineStatus;
|
|
350
354
|
private renderInlineHelp;
|
|
351
355
|
/**
|
|
352
356
|
* Render inline autocomplete below status bar
|
package/dist/renderer/App.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Screen } from './Screen.js';
|
|
6
6
|
import { Input, LineEditor } from './Input.js';
|
|
7
|
-
import { fg, style } from './ansi.js';
|
|
7
|
+
import { fg, style, stringWidth } from './ansi.js';
|
|
8
8
|
import clipboardy from 'clipboardy';
|
|
9
9
|
// Primary color: #f02a30 (Codeep red)
|
|
10
10
|
const PRIMARY_COLOR = fg.rgb(240, 42, 48);
|
|
@@ -202,7 +202,6 @@ const COMMAND_DESCRIPTIONS = {
|
|
|
202
202
|
'learn': 'Learn code preferences',
|
|
203
203
|
};
|
|
204
204
|
import { helpCategories, keyboardShortcuts } from './components/Help.js';
|
|
205
|
-
import { renderStatusScreen } from './components/Status.js';
|
|
206
205
|
import { handleSettingsKey, SETTINGS } from './components/Settings.js';
|
|
207
206
|
export class App {
|
|
208
207
|
screen;
|
|
@@ -212,7 +211,6 @@ export class App {
|
|
|
212
211
|
streamingContent = '';
|
|
213
212
|
isStreaming = false;
|
|
214
213
|
isLoading = false;
|
|
215
|
-
currentScreen = 'chat';
|
|
216
214
|
options;
|
|
217
215
|
scrollOffset = 0;
|
|
218
216
|
notification = '';
|
|
@@ -231,6 +229,8 @@ export class App {
|
|
|
231
229
|
// Inline help state
|
|
232
230
|
helpOpen = false;
|
|
233
231
|
helpScrollIndex = 0;
|
|
232
|
+
// Inline status state
|
|
233
|
+
statusOpen = false;
|
|
234
234
|
// Settings screen state
|
|
235
235
|
settingsState = {
|
|
236
236
|
selectedIndex: 0,
|
|
@@ -784,19 +784,7 @@ export class App {
|
|
|
784
784
|
this.options.onExit();
|
|
785
785
|
return;
|
|
786
786
|
}
|
|
787
|
-
|
|
788
|
-
switch (this.currentScreen) {
|
|
789
|
-
case 'status':
|
|
790
|
-
if (event.key === 'escape' || event.key === 'q') {
|
|
791
|
-
this.currentScreen = 'chat';
|
|
792
|
-
this.render();
|
|
793
|
-
}
|
|
794
|
-
break;
|
|
795
|
-
case 'chat':
|
|
796
|
-
default:
|
|
797
|
-
this.handleChatKey(event);
|
|
798
|
-
break;
|
|
799
|
-
}
|
|
787
|
+
this.handleChatKey(event);
|
|
800
788
|
}
|
|
801
789
|
/**
|
|
802
790
|
* Handle chat screen keys
|
|
@@ -822,6 +810,11 @@ export class App {
|
|
|
822
810
|
this.handleInlineConfirmKey(event);
|
|
823
811
|
return;
|
|
824
812
|
}
|
|
813
|
+
// If status is open, handle status keys first
|
|
814
|
+
if (this.statusOpen) {
|
|
815
|
+
this.handleInlineStatusKey(event);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
825
818
|
// If help is open, handle help keys first
|
|
826
819
|
if (this.helpOpen) {
|
|
827
820
|
this.handleInlineHelpKey(event);
|
|
@@ -1036,6 +1029,15 @@ export class App {
|
|
|
1036
1029
|
this.autocompleteItems = [];
|
|
1037
1030
|
}
|
|
1038
1031
|
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Handle inline status keys
|
|
1034
|
+
*/
|
|
1035
|
+
handleInlineStatusKey(event) {
|
|
1036
|
+
if (event.key === 'escape' || event.key === 'q') {
|
|
1037
|
+
this.statusOpen = false;
|
|
1038
|
+
this.render();
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1039
1041
|
/**
|
|
1040
1042
|
* Handle help screen keys
|
|
1041
1043
|
*/
|
|
@@ -1515,7 +1517,7 @@ export class App {
|
|
|
1515
1517
|
this.render();
|
|
1516
1518
|
break;
|
|
1517
1519
|
case 'status':
|
|
1518
|
-
this.
|
|
1520
|
+
this.statusOpen = true;
|
|
1519
1521
|
this.render();
|
|
1520
1522
|
break;
|
|
1521
1523
|
case 'clear':
|
|
@@ -1542,15 +1544,7 @@ export class App {
|
|
|
1542
1544
|
this.renderIntro();
|
|
1543
1545
|
return;
|
|
1544
1546
|
}
|
|
1545
|
-
|
|
1546
|
-
case 'status':
|
|
1547
|
-
renderStatusScreen(this.screen, this.options.getStatus());
|
|
1548
|
-
break;
|
|
1549
|
-
case 'chat':
|
|
1550
|
-
default:
|
|
1551
|
-
this.renderChat();
|
|
1552
|
-
break;
|
|
1553
|
-
}
|
|
1547
|
+
this.renderChat();
|
|
1554
1548
|
}
|
|
1555
1549
|
/**
|
|
1556
1550
|
* Render chat screen
|
|
@@ -1603,31 +1597,12 @@ export class App {
|
|
|
1603
1597
|
bottomPanelHeight = Math.min(this.autocompleteItems.length + 3, 12);
|
|
1604
1598
|
}
|
|
1605
1599
|
const mainHeight = height - bottomPanelHeight;
|
|
1606
|
-
// Determine if we have enough space for logo (need at least 20 lines)
|
|
1607
|
-
const showLogo = height >= 24;
|
|
1608
|
-
const logoSpace = showLogo ? LOGO_HEIGHT + 1 : 1; // +1 for tagline or simple header
|
|
1609
1600
|
// Layout - main UI takes top portion
|
|
1610
|
-
const
|
|
1611
|
-
const messagesStart = logoSpace;
|
|
1601
|
+
const messagesStart = 0;
|
|
1612
1602
|
const messagesEnd = mainHeight - 4;
|
|
1613
1603
|
const separatorLine = mainHeight - 3;
|
|
1614
1604
|
const inputLine = mainHeight - 2;
|
|
1615
1605
|
const statusLine = mainHeight - 1;
|
|
1616
|
-
// Header - show logo if space permits, otherwise simple text
|
|
1617
|
-
if (showLogo) {
|
|
1618
|
-
// Center the logo
|
|
1619
|
-
const logoWidth = LOGO_LINES[0].length;
|
|
1620
|
-
const logoX = Math.max(0, Math.floor((width - logoWidth) / 2));
|
|
1621
|
-
for (let i = 0; i < LOGO_LINES.length; i++) {
|
|
1622
|
-
this.screen.write(logoX, headerLine + i, LOGO_LINES[i], PRIMARY_COLOR);
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
else {
|
|
1626
|
-
// Simple header for small terminals
|
|
1627
|
-
const header = ' Codeep ';
|
|
1628
|
-
const headerPadding = '─'.repeat(Math.max(0, (width - header.length) / 2));
|
|
1629
|
-
this.screen.writeLine(headerLine, headerPadding + header + headerPadding, PRIMARY_COLOR);
|
|
1630
|
-
}
|
|
1631
1606
|
// Messages
|
|
1632
1607
|
const messagesHeight = messagesEnd - messagesStart + 1;
|
|
1633
1608
|
const messagesToRender = this.getVisibleMessages(messagesHeight, width - 2);
|
|
@@ -1662,6 +1637,10 @@ export class App {
|
|
|
1662
1637
|
if (this.helpOpen) {
|
|
1663
1638
|
this.renderInlineHelp(statusLine + 1, width, height - statusLine - 1);
|
|
1664
1639
|
}
|
|
1640
|
+
// Inline status renders BELOW status bar
|
|
1641
|
+
if (this.statusOpen) {
|
|
1642
|
+
this.renderInlineStatus(statusLine + 1, width);
|
|
1643
|
+
}
|
|
1665
1644
|
// Inline search renders BELOW status bar
|
|
1666
1645
|
if (this.searchOpen) {
|
|
1667
1646
|
this.renderInlineSearch(statusLine + 1, width, height - statusLine - 1);
|
|
@@ -1946,6 +1925,35 @@ export class App {
|
|
|
1946
1925
|
/**
|
|
1947
1926
|
* Render inline help below status bar
|
|
1948
1927
|
*/
|
|
1928
|
+
renderInlineStatus(startY, width) {
|
|
1929
|
+
const status = this.options.getStatus();
|
|
1930
|
+
let y = startY;
|
|
1931
|
+
// Separator line
|
|
1932
|
+
this.screen.horizontalLine(y++, '─', PRIMARY_COLOR);
|
|
1933
|
+
// Title
|
|
1934
|
+
this.screen.writeLine(y++, 'Status', PRIMARY_COLOR + style.bold);
|
|
1935
|
+
const items = [
|
|
1936
|
+
{ label: 'Version', value: 'v' + status.version, color: fg.white },
|
|
1937
|
+
{ label: 'Provider', value: status.provider, color: fg.white },
|
|
1938
|
+
{ label: 'Model', value: status.model, color: fg.white },
|
|
1939
|
+
{ label: 'Agent Mode', value: status.agentMode.toUpperCase(), color: status.agentMode === 'on' ? fg.green : status.agentMode === 'manual' ? fg.yellow : fg.gray },
|
|
1940
|
+
{ label: 'Project', value: status.projectPath, color: fg.white },
|
|
1941
|
+
{ label: 'Write Access', value: status.hasWriteAccess ? 'Yes' : 'No', color: status.hasWriteAccess ? fg.green : fg.red },
|
|
1942
|
+
{ label: 'Session', value: status.sessionId || 'New', color: fg.white },
|
|
1943
|
+
{ label: 'Messages', value: status.messageCount.toString(), color: fg.white },
|
|
1944
|
+
{ label: 'Platform', value: process.platform, color: fg.white },
|
|
1945
|
+
{ label: 'Node', value: process.version, color: fg.white },
|
|
1946
|
+
{ label: 'Terminal', value: width + 'x' + this.screen.getSize().height, color: fg.white },
|
|
1947
|
+
];
|
|
1948
|
+
const labelWidth = Math.max(...items.map(i => i.label.length)) + 2;
|
|
1949
|
+
for (const item of items) {
|
|
1950
|
+
this.screen.write(2, y, item.label + ':', fg.gray);
|
|
1951
|
+
this.screen.write(2 + labelWidth, y, item.value, item.color);
|
|
1952
|
+
y++;
|
|
1953
|
+
}
|
|
1954
|
+
y++;
|
|
1955
|
+
this.screen.writeLine(y, 'Esc close', fg.gray);
|
|
1956
|
+
}
|
|
1949
1957
|
renderInlineHelp(startY, width, availableHeight) {
|
|
1950
1958
|
// Build all help items
|
|
1951
1959
|
const allItems = [];
|
|
@@ -2326,6 +2334,20 @@ export class App {
|
|
|
2326
2334
|
*/
|
|
2327
2335
|
getVisibleMessages(height, width) {
|
|
2328
2336
|
const allLines = [];
|
|
2337
|
+
// Logo at the top, scrolls with content
|
|
2338
|
+
if (height >= 20) {
|
|
2339
|
+
const logoWidth = LOGO_LINES[0].length;
|
|
2340
|
+
const logoX = Math.max(0, Math.floor((width - logoWidth) / 2));
|
|
2341
|
+
const pad = ' '.repeat(logoX);
|
|
2342
|
+
for (const line of LOGO_LINES) {
|
|
2343
|
+
allLines.push({ text: pad + line, style: PRIMARY_COLOR, raw: false });
|
|
2344
|
+
}
|
|
2345
|
+
allLines.push({ text: '', style: '' });
|
|
2346
|
+
}
|
|
2347
|
+
else {
|
|
2348
|
+
allLines.push({ text: ' Codeep', style: PRIMARY_COLOR, raw: false });
|
|
2349
|
+
allLines.push({ text: '', style: '' });
|
|
2350
|
+
}
|
|
2329
2351
|
for (const msg of this.messages) {
|
|
2330
2352
|
const msgLines = this.formatMessage(msg.role, msg.content, width);
|
|
2331
2353
|
allLines.push(...msgLines);
|
|
@@ -2335,10 +2357,7 @@ export class App {
|
|
|
2335
2357
|
allLines.push(...streamLines);
|
|
2336
2358
|
}
|
|
2337
2359
|
// Calculate visible window based on scroll offset
|
|
2338
|
-
// scrollOffset=0 means show the most recent (bottom) lines
|
|
2339
|
-
// scrollOffset>0 means scroll up to see older messages
|
|
2340
2360
|
const totalLines = allLines.length;
|
|
2341
|
-
// Clamp scrollOffset to valid range
|
|
2342
2361
|
const maxScroll = Math.max(0, totalLines - height);
|
|
2343
2362
|
if (this.scrollOffset > maxScroll) {
|
|
2344
2363
|
this.scrollOffset = maxScroll;
|
|
@@ -2508,7 +2527,7 @@ export class App {
|
|
|
2508
2527
|
}
|
|
2509
2528
|
else {
|
|
2510
2529
|
// Plain text - word wrap as before
|
|
2511
|
-
if (line
|
|
2530
|
+
if (stringWidth(line) > maxWidth - prefix.length) {
|
|
2512
2531
|
const wrapped = this.wordWrap(line, maxWidth - prefix.length);
|
|
2513
2532
|
for (let j = 0; j < wrapped.length; j++) {
|
|
2514
2533
|
lines.push({
|
|
@@ -2790,7 +2809,7 @@ export class App {
|
|
|
2790
2809
|
const lines = [];
|
|
2791
2810
|
let currentLine = '';
|
|
2792
2811
|
for (const word of words) {
|
|
2793
|
-
if (currentLine
|
|
2812
|
+
if (stringWidth(currentLine) + stringWidth(word) + 1 > maxWidth && currentLine) {
|
|
2794
2813
|
lines.push(currentLine);
|
|
2795
2814
|
currentLine = word;
|
|
2796
2815
|
}
|
package/dist/renderer/Screen.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Screen buffer with diff-based rendering
|
|
3
3
|
* Only writes changes to terminal - minimizes flickering
|
|
4
4
|
*/
|
|
5
|
-
import { cursor, screen, style, visibleLength } from './ansi.js';
|
|
5
|
+
import { cursor, screen, style, visibleLength, charWidth } from './ansi.js';
|
|
6
6
|
export class Screen {
|
|
7
7
|
width;
|
|
8
8
|
height;
|
|
@@ -73,10 +73,15 @@ export class Screen {
|
|
|
73
73
|
break;
|
|
74
74
|
}
|
|
75
75
|
else {
|
|
76
|
+
const w = charWidth(char);
|
|
76
77
|
if (col >= 0 && col < this.width) {
|
|
77
78
|
this.buffer[y][col] = { char, style: currentStyle };
|
|
79
|
+
// Wide char: fill next cell with empty placeholder
|
|
80
|
+
if (w === 2 && col + 1 < this.width) {
|
|
81
|
+
this.buffer[y][col + 1] = { char: '', style: currentStyle };
|
|
82
|
+
}
|
|
78
83
|
}
|
|
79
|
-
col
|
|
84
|
+
col += w;
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
87
|
}
|
|
@@ -132,10 +137,15 @@ export class Screen {
|
|
|
132
137
|
}
|
|
133
138
|
else {
|
|
134
139
|
// Regular character
|
|
140
|
+
const w = charWidth(text[i]);
|
|
135
141
|
if (col < this.width) {
|
|
136
142
|
this.buffer[y][col] = { char: text[i], style: currentStyle };
|
|
137
|
-
|
|
143
|
+
// Wide char: fill next cell with empty placeholder
|
|
144
|
+
if (w === 2 && col + 1 < this.width) {
|
|
145
|
+
this.buffer[y][col + 1] = { char: '', style: currentStyle };
|
|
146
|
+
}
|
|
138
147
|
}
|
|
148
|
+
col += w;
|
|
139
149
|
i++;
|
|
140
150
|
}
|
|
141
151
|
}
|
|
@@ -216,6 +226,11 @@ export class Screen {
|
|
|
216
226
|
if (cell.char === renderedCell.char && cell.style === renderedCell.style) {
|
|
217
227
|
continue;
|
|
218
228
|
}
|
|
229
|
+
// Skip wide-char placeholder cells
|
|
230
|
+
if (cell.char === '') {
|
|
231
|
+
this.rendered[y][x] = { ...cell };
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
219
234
|
// Move cursor and write
|
|
220
235
|
output += cursor.to(y + 1, x + 1);
|
|
221
236
|
if (cell.style !== lastStyle) {
|
package/dist/renderer/ansi.d.ts
CHANGED
|
@@ -81,6 +81,15 @@ export declare const style: {
|
|
|
81
81
|
* Helper to create styled text
|
|
82
82
|
*/
|
|
83
83
|
export declare function styled(text: string, ...styles: string[]): string;
|
|
84
|
+
/**
|
|
85
|
+
* Get terminal display width of a single character
|
|
86
|
+
* CJK, fullwidth, and emoji characters take 2 columns
|
|
87
|
+
*/
|
|
88
|
+
export declare function charWidth(char: string): number;
|
|
89
|
+
/**
|
|
90
|
+
* Get terminal display width of a string (excluding ANSI codes)
|
|
91
|
+
*/
|
|
92
|
+
export declare function stringWidth(str: string): number;
|
|
84
93
|
/**
|
|
85
94
|
* Strip ANSI codes from string (for length calculation)
|
|
86
95
|
*/
|
package/dist/renderer/ansi.js
CHANGED
|
@@ -101,6 +101,84 @@ export function styled(text, ...styles) {
|
|
|
101
101
|
return text;
|
|
102
102
|
return styles.join('') + text + style.reset;
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Get terminal display width of a single character
|
|
106
|
+
* CJK, fullwidth, and emoji characters take 2 columns
|
|
107
|
+
*/
|
|
108
|
+
export function charWidth(char) {
|
|
109
|
+
const code = char.codePointAt(0);
|
|
110
|
+
if (code === undefined)
|
|
111
|
+
return 0;
|
|
112
|
+
// Control characters
|
|
113
|
+
if (code < 32 || (code >= 0x7f && code < 0xa0))
|
|
114
|
+
return 0;
|
|
115
|
+
// CJK Unified Ideographs
|
|
116
|
+
if (code >= 0x4e00 && code <= 0x9fff)
|
|
117
|
+
return 2;
|
|
118
|
+
// CJK Unified Ideographs Extension A
|
|
119
|
+
if (code >= 0x3400 && code <= 0x4dbf)
|
|
120
|
+
return 2;
|
|
121
|
+
// CJK Unified Ideographs Extension B
|
|
122
|
+
if (code >= 0x20000 && code <= 0x2a6df)
|
|
123
|
+
return 2;
|
|
124
|
+
// CJK Compatibility Ideographs
|
|
125
|
+
if (code >= 0xf900 && code <= 0xfaff)
|
|
126
|
+
return 2;
|
|
127
|
+
// CJK Radicals / Kangxi Radicals
|
|
128
|
+
if (code >= 0x2e80 && code <= 0x2fdf)
|
|
129
|
+
return 2;
|
|
130
|
+
// CJK Strokes / Enclosed CJK
|
|
131
|
+
if (code >= 0x31c0 && code <= 0x33ff)
|
|
132
|
+
return 2;
|
|
133
|
+
// CJK Symbols and Punctuation
|
|
134
|
+
if (code >= 0x3000 && code <= 0x303f)
|
|
135
|
+
return 2;
|
|
136
|
+
// Hiragana, Katakana
|
|
137
|
+
if (code >= 0x3040 && code <= 0x30ff)
|
|
138
|
+
return 2;
|
|
139
|
+
// Katakana Phonetic Extensions
|
|
140
|
+
if (code >= 0x31f0 && code <= 0x31ff)
|
|
141
|
+
return 2;
|
|
142
|
+
// Hangul Jamo
|
|
143
|
+
if (code >= 0x1100 && code <= 0x11ff)
|
|
144
|
+
return 2;
|
|
145
|
+
// Hangul Syllables
|
|
146
|
+
if (code >= 0xac00 && code <= 0xd7af)
|
|
147
|
+
return 2;
|
|
148
|
+
// Hangul Jamo Extended-A/B
|
|
149
|
+
if (code >= 0xa960 && code <= 0xa97f)
|
|
150
|
+
return 2;
|
|
151
|
+
if (code >= 0xd7b0 && code <= 0xd7ff)
|
|
152
|
+
return 2;
|
|
153
|
+
// Fullwidth Forms
|
|
154
|
+
if (code >= 0xff01 && code <= 0xff60)
|
|
155
|
+
return 2;
|
|
156
|
+
if (code >= 0xffe0 && code <= 0xffe6)
|
|
157
|
+
return 2;
|
|
158
|
+
// Bopomofo
|
|
159
|
+
if (code >= 0x3100 && code <= 0x312f)
|
|
160
|
+
return 2;
|
|
161
|
+
// Emoji ranges (common)
|
|
162
|
+
if (code >= 0x1f300 && code <= 0x1f9ff)
|
|
163
|
+
return 2;
|
|
164
|
+
if (code >= 0x1fa00 && code <= 0x1fa6f)
|
|
165
|
+
return 2;
|
|
166
|
+
if (code >= 0x1fa70 && code <= 0x1faff)
|
|
167
|
+
return 2;
|
|
168
|
+
if (code >= 0x2600 && code <= 0x27bf)
|
|
169
|
+
return 2;
|
|
170
|
+
return 1;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get terminal display width of a string (excluding ANSI codes)
|
|
174
|
+
*/
|
|
175
|
+
export function stringWidth(str) {
|
|
176
|
+
let width = 0;
|
|
177
|
+
for (const char of str) {
|
|
178
|
+
width += charWidth(char);
|
|
179
|
+
}
|
|
180
|
+
return width;
|
|
181
|
+
}
|
|
104
182
|
/**
|
|
105
183
|
* Strip ANSI codes from string (for length calculation)
|
|
106
184
|
*/
|
|
@@ -112,14 +190,14 @@ export function stripAnsi(str) {
|
|
|
112
190
|
* Get visible length of string (excluding ANSI codes)
|
|
113
191
|
*/
|
|
114
192
|
export function visibleLength(str) {
|
|
115
|
-
return stripAnsi(str)
|
|
193
|
+
return stringWidth(stripAnsi(str));
|
|
116
194
|
}
|
|
117
195
|
/**
|
|
118
196
|
* Truncate string to visible length, preserving ANSI codes
|
|
119
197
|
*/
|
|
120
198
|
export function truncate(str, maxLength, suffix = '...') {
|
|
121
199
|
const visible = stripAnsi(str);
|
|
122
|
-
if (visible
|
|
200
|
+
if (stringWidth(visible) <= maxLength)
|
|
123
201
|
return str;
|
|
124
202
|
// Simple truncation - may cut ANSI codes
|
|
125
203
|
// For proper handling, we'd need to parse ANSI sequences
|
|
@@ -138,13 +216,11 @@ export function truncate(str, maxLength, suffix = '...') {
|
|
|
138
216
|
}
|
|
139
217
|
}
|
|
140
218
|
else {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
visibleCount++;
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
219
|
+
const w = charWidth(char);
|
|
220
|
+
if (visibleCount + w > maxLength - suffix.length)
|
|
146
221
|
break;
|
|
147
|
-
|
|
222
|
+
result += char;
|
|
223
|
+
visibleCount += w;
|
|
148
224
|
}
|
|
149
225
|
}
|
|
150
226
|
return result + style.reset + suffix;
|
package/dist/renderer/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export { cursor, screen, fg, bg, style, styled, stripAnsi, visibleLength, trunca
|
|
|
9
9
|
export { Screen, Cell } from './Screen';
|
|
10
10
|
export { Input, LineEditor, KeyEvent, KeyHandler } from './Input';
|
|
11
11
|
export { ChatUI, ChatMessage, ChatUIOptions } from './ChatUI';
|
|
12
|
-
export { App,
|
|
12
|
+
export { App, AppOptions, Message } from './App';
|
|
13
13
|
export { createBox, centerBox, BoxStyle, BoxOptions } from './components/Box';
|
|
14
14
|
export { renderModal, renderHelpModal, renderListModal, ModalOptions } from './components/Modal';
|
|
15
15
|
export { renderHelpScreen, helpCategories, keyboardShortcuts } from './components/Help';
|
package/dist/renderer/main.js
CHANGED
|
@@ -620,6 +620,13 @@ function handleCommand(command, args) {
|
|
|
620
620
|
return;
|
|
621
621
|
}
|
|
622
622
|
const newName = args.join('-');
|
|
623
|
+
// Save current session first so there's a file to rename
|
|
624
|
+
const messages = app.getMessages();
|
|
625
|
+
if (messages.length === 0) {
|
|
626
|
+
app.notify('No messages to save. Start a conversation first.');
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
saveSession(sessionId, messages, projectPath);
|
|
623
630
|
if (renameSession(sessionId, newName, projectPath)) {
|
|
624
631
|
sessionId = newName;
|
|
625
632
|
app.notify(`Session renamed to: ${newName}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.21",
|
|
4
4
|
"description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|