codeep 1.2.11 → 1.2.12
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/api/index.js +7 -7
- package/dist/config/index.js +3 -3
- package/dist/config/providers.d.ts +6 -0
- package/dist/config/providers.js +41 -2
- package/dist/config/providers.test.js +31 -2
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/useAgent.js +3 -3
- package/dist/renderer/App.js +8 -8
- package/dist/renderer/ChatUI.js +3 -3
- package/dist/renderer/Screen.js +1 -1
- package/dist/renderer/components/Export.js +1 -1
- package/dist/renderer/components/Help.js +1 -1
- package/dist/renderer/components/Intro.js +1 -1
- package/dist/renderer/components/Login.js +3 -3
- package/dist/renderer/components/Logout.js +1 -1
- package/dist/renderer/components/Modal.js +2 -2
- package/dist/renderer/components/Permission.js +2 -2
- package/dist/renderer/components/Search.js +1 -1
- package/dist/renderer/components/SelectScreen.js +1 -1
- package/dist/renderer/components/Settings.js +3 -3
- package/dist/renderer/components/Status.js +1 -1
- package/dist/renderer/demo-app.js +1 -1
- package/dist/renderer/demo.js +1 -1
- package/dist/renderer/index.js +9 -9
- package/dist/renderer/main.js +31 -31
- package/dist/utils/agent.js +10 -10
- package/dist/utils/agent.test.js +1 -1
- package/dist/utils/codeReview.js +1 -1
- package/dist/utils/context.js +1 -1
- package/dist/utils/git.test.js +1 -1
- package/dist/utils/gitignore.test.js +1 -1
- package/dist/utils/keychain.js +1 -1
- package/dist/utils/project.test.js +1 -1
- package/dist/utils/ratelimit.js +1 -1
- package/dist/utils/ratelimit.test.js +1 -1
- package/dist/utils/retry.test.js +1 -1
- package/dist/utils/smartContext.js +1 -1
- package/dist/utils/smartContext.test.js +1 -1
- package/dist/utils/taskPlanner.js +2 -2
- package/dist/utils/tools.d.ts +64 -4
- package/dist/utils/tools.js +212 -7
- package/dist/utils/tools.test.js +1 -1
- package/dist/utils/validation.test.js +1 -1
- package/dist/utils/verify.js +1 -1
- package/package.json +1 -1
- package/bin/codeep.js +0 -2
- package/dist/app.d.ts +0 -2
- package/dist/app.js +0 -1501
- package/dist/components/AgentActions.d.ts +0 -18
- package/dist/components/AgentActions.js +0 -122
- package/dist/components/AgentProgress.d.ts +0 -59
- package/dist/components/AgentProgress.js +0 -368
- package/dist/components/Export.d.ts +0 -8
- package/dist/components/Export.js +0 -27
- package/dist/components/Help.d.ts +0 -6
- package/dist/components/Help.js +0 -7
- package/dist/components/Input.d.ts +0 -9
- package/dist/components/Input.js +0 -334
- package/dist/components/Loading.d.ts +0 -17
- package/dist/components/Loading.js +0 -52
- package/dist/components/Login.d.ts +0 -7
- package/dist/components/Login.js +0 -77
- package/dist/components/Logo.d.ts +0 -8
- package/dist/components/Logo.js +0 -89
- package/dist/components/LogoutPicker.d.ts +0 -8
- package/dist/components/LogoutPicker.js +0 -61
- package/dist/components/Message.d.ts +0 -10
- package/dist/components/Message.js +0 -242
- package/dist/components/MessageList.d.ts +0 -10
- package/dist/components/MessageList.js +0 -42
- package/dist/components/ProjectPermission.d.ts +0 -7
- package/dist/components/ProjectPermission.js +0 -65
- package/dist/components/Search.d.ts +0 -10
- package/dist/components/Search.js +0 -30
- package/dist/components/SessionPicker.d.ts +0 -9
- package/dist/components/SessionPicker.js +0 -88
- package/dist/components/Sessions.d.ts +0 -12
- package/dist/components/Sessions.js +0 -119
- package/dist/components/Settings.d.ts +0 -9
- package/dist/components/Settings.js +0 -198
- package/dist/components/Spinner.d.ts +0 -34
- package/dist/components/Spinner.js +0 -38
- package/dist/components/Status.d.ts +0 -2
- package/dist/components/Status.js +0 -13
- package/dist/components/StreamingMessage.d.ts +0 -14
- package/dist/components/StreamingMessage.js +0 -19
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -42
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { Text, Box, useInput } from 'ink';
|
|
4
|
-
import { getConfiguredProviders, clearApiKey, getCurrentProvider } from '../config/index.js';
|
|
5
|
-
export const LogoutPicker = ({ onLogout, onLogoutAll, onCancel }) => {
|
|
6
|
-
const providers = getConfiguredProviders();
|
|
7
|
-
const currentProvider = getCurrentProvider();
|
|
8
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
9
|
-
// Options: individual providers + "All" + "Cancel"
|
|
10
|
-
const options = [
|
|
11
|
-
...providers.map(p => ({
|
|
12
|
-
type: 'provider',
|
|
13
|
-
id: p.id,
|
|
14
|
-
label: p.name,
|
|
15
|
-
isCurrent: p.id === currentProvider.id
|
|
16
|
-
})),
|
|
17
|
-
{ type: 'all', id: 'all', label: 'Logout from all providers', isCurrent: false },
|
|
18
|
-
{ type: 'cancel', id: 'cancel', label: 'Cancel', isCurrent: false },
|
|
19
|
-
];
|
|
20
|
-
useInput((input, key) => {
|
|
21
|
-
if (key.escape) {
|
|
22
|
-
onCancel();
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (key.upArrow) {
|
|
26
|
-
setSelectedIndex(i => Math.max(0, i - 1));
|
|
27
|
-
}
|
|
28
|
-
if (key.downArrow) {
|
|
29
|
-
setSelectedIndex(i => Math.min(options.length - 1, i + 1));
|
|
30
|
-
}
|
|
31
|
-
if (key.return) {
|
|
32
|
-
const selected = options[selectedIndex];
|
|
33
|
-
if (selected.type === 'provider') {
|
|
34
|
-
clearApiKey(selected.id);
|
|
35
|
-
onLogout(selected.id);
|
|
36
|
-
}
|
|
37
|
-
else if (selected.type === 'all') {
|
|
38
|
-
for (const p of providers) {
|
|
39
|
-
clearApiKey(p.id);
|
|
40
|
-
}
|
|
41
|
-
onLogoutAll();
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
onCancel();
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
if (providers.length === 0) {
|
|
49
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { color: "yellow", children: "No providers configured." }), _jsx(Text, { color: "cyan", children: "Press Escape to go back." })] }));
|
|
50
|
-
}
|
|
51
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Select provider to logout:" }) }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: options.map((option, index) => {
|
|
52
|
-
const isSelected = index === selectedIndex;
|
|
53
|
-
const prefix = isSelected ? '→ ' : ' ';
|
|
54
|
-
let color = isSelected ? 'green' : 'white';
|
|
55
|
-
if (option.type === 'all')
|
|
56
|
-
color = isSelected ? 'red' : 'yellow';
|
|
57
|
-
if (option.type === 'cancel')
|
|
58
|
-
color = isSelected ? 'blue' : 'gray';
|
|
59
|
-
return (_jsxs(Box, { children: [_jsxs(Text, { color: color, bold: isSelected, children: [prefix, option.label] }), option.isCurrent && (_jsx(Text, { color: "cyan", children: " (current)" }))] }, option.id));
|
|
60
|
-
}) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "cyan", children: "\u2191\u2193 Navigate Enter Select Esc Cancel" }) })] }));
|
|
61
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export declare function getCodeBlocks(): string[];
|
|
3
|
-
export declare function clearCodeBlocks(): void;
|
|
4
|
-
export declare function getCodeBlock(index: number): string | null;
|
|
5
|
-
interface MessageProps {
|
|
6
|
-
role: 'user' | 'assistant' | 'system';
|
|
7
|
-
content: string;
|
|
8
|
-
}
|
|
9
|
-
export declare const MessageView: React.FC<MessageProps>;
|
|
10
|
-
export {};
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { memo } from 'react';
|
|
3
|
-
import { Text, Box } from 'ink';
|
|
4
|
-
// Global code block storage for copy functionality
|
|
5
|
-
let codeBlocks = [];
|
|
6
|
-
export function getCodeBlocks() {
|
|
7
|
-
return codeBlocks;
|
|
8
|
-
}
|
|
9
|
-
export function clearCodeBlocks() {
|
|
10
|
-
codeBlocks = [];
|
|
11
|
-
}
|
|
12
|
-
export function getCodeBlock(index) {
|
|
13
|
-
if (index < 0) {
|
|
14
|
-
// Negative index = from end (-1 = last block)
|
|
15
|
-
const actualIndex = codeBlocks.length + index;
|
|
16
|
-
return codeBlocks[actualIndex] || null;
|
|
17
|
-
}
|
|
18
|
-
return codeBlocks[index] || null;
|
|
19
|
-
}
|
|
20
|
-
export const MessageView = memo(({ role, content }) => {
|
|
21
|
-
if (role === 'user') {
|
|
22
|
-
// For long user messages, truncate display but keep full content for processing
|
|
23
|
-
const maxDisplayLength = 500;
|
|
24
|
-
const isLong = content.length > maxDisplayLength;
|
|
25
|
-
const displayContent = isLong
|
|
26
|
-
? content.substring(0, maxDisplayLength) + '...'
|
|
27
|
-
: content;
|
|
28
|
-
// Replace multiple newlines with single space for cleaner display
|
|
29
|
-
const cleanContent = displayContent.replace(/\n+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
30
|
-
return (_jsxs(Box, { marginY: 1, flexDirection: "column", children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: "#f02a30", bold: true, children: '> ' }), _jsx(Text, { children: cleanContent })] }), isLong && (_jsxs(Text, { color: "cyan", dimColor: true, children: [" (", content.length, " characters total)"] }))] }));
|
|
31
|
-
}
|
|
32
|
-
if (role === 'system') {
|
|
33
|
-
return (_jsx(Box, { marginY: 1, justifyContent: "center", children: _jsx(Text, { italic: true, children: content }) }));
|
|
34
|
-
}
|
|
35
|
-
// Assistant message - parse code blocks and markdown
|
|
36
|
-
// Clear and rebuild code blocks on each render
|
|
37
|
-
codeBlocks = [];
|
|
38
|
-
return (_jsx(Box, { flexDirection: "column", marginY: 1, paddingX: 1, children: _jsx(FormattedResponse, { content: content }) }));
|
|
39
|
-
});
|
|
40
|
-
const FormattedResponse = memo(({ content }) => {
|
|
41
|
-
const segments = parseContent(content);
|
|
42
|
-
let codeBlockIndex = 0;
|
|
43
|
-
return (_jsx(_Fragment, { children: segments.map((segment, i) => {
|
|
44
|
-
if (segment.type === 'code') {
|
|
45
|
-
const idx = codeBlockIndex++;
|
|
46
|
-
// Store code block for copy functionality
|
|
47
|
-
codeBlocks.push(segment.content);
|
|
48
|
-
return _jsx(CodeBlock, { code: segment.content, language: segment.language || 'code', index: idx }, i);
|
|
49
|
-
}
|
|
50
|
-
// Render text with inline markdown
|
|
51
|
-
return _jsx(MarkdownText, { text: segment.content }, i);
|
|
52
|
-
}) }));
|
|
53
|
-
});
|
|
54
|
-
// Render inline markdown: **bold**, *italic*, `code`, __underline__
|
|
55
|
-
const MarkdownText = memo(({ text }) => {
|
|
56
|
-
const parts = [];
|
|
57
|
-
let remaining = text;
|
|
58
|
-
let key = 0;
|
|
59
|
-
while (remaining.length > 0) {
|
|
60
|
-
// Bold: **text** or __text__
|
|
61
|
-
const boldMatch = remaining.match(/^(\*\*|__)(.+?)\1/);
|
|
62
|
-
if (boldMatch) {
|
|
63
|
-
parts.push(_jsx(Text, { bold: true, children: boldMatch[2] }, key++));
|
|
64
|
-
remaining = remaining.slice(boldMatch[0].length);
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
// Italic: *text* or _text_
|
|
68
|
-
const italicMatch = remaining.match(/^(\*|_)([^*_]+)\1/);
|
|
69
|
-
if (italicMatch) {
|
|
70
|
-
parts.push(_jsx(Text, { italic: true, children: italicMatch[2] }, key++));
|
|
71
|
-
remaining = remaining.slice(italicMatch[0].length);
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
// Inline code: `code`
|
|
75
|
-
const codeMatch = remaining.match(/^`([^`]+)`/);
|
|
76
|
-
if (codeMatch) {
|
|
77
|
-
parts.push(_jsx(Text, { color: "cyan", backgroundColor: "gray", children: codeMatch[1] }, key++));
|
|
78
|
-
remaining = remaining.slice(codeMatch[0].length);
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
// Headers: # ## ### at start of line
|
|
82
|
-
const headerMatch = remaining.match(/^(#{1,3})\s+(.+?)(?:\n|$)/);
|
|
83
|
-
if (headerMatch) {
|
|
84
|
-
const level = headerMatch[1].length;
|
|
85
|
-
const color = level === 1 ? '#f02a30' : level === 2 ? 'cyan' : 'green';
|
|
86
|
-
parts.push(_jsx(Text, { color: color, bold: true, children: headerMatch[2] }, key++));
|
|
87
|
-
parts.push(_jsx(Text, { children: '\n' }, key++));
|
|
88
|
-
remaining = remaining.slice(headerMatch[0].length);
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
// List items: - item or * item or number. item
|
|
92
|
-
const listMatch = remaining.match(/^(\s*)([-*]|\d+\.)\s+(.+?)(?:\n|$)/);
|
|
93
|
-
if (listMatch) {
|
|
94
|
-
const indent = listMatch[1];
|
|
95
|
-
const bullet = listMatch[2].match(/^\d/) ? listMatch[2] : '•';
|
|
96
|
-
parts.push(_jsxs(Text, { children: [indent, _jsx(Text, { color: "#f02a30", children: bullet }), " ", listMatch[3], '\n'] }, key++));
|
|
97
|
-
remaining = remaining.slice(listMatch[0].length);
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
// Links: [text](url)
|
|
101
|
-
const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)]+)\)/);
|
|
102
|
-
if (linkMatch) {
|
|
103
|
-
parts.push(_jsx(Text, { color: "blue", underline: true, children: linkMatch[1] }, key++));
|
|
104
|
-
remaining = remaining.slice(linkMatch[0].length);
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
// Default: consume until next special character or end
|
|
108
|
-
const nextSpecial = remaining.search(/[\*_`#\[\n-]/);
|
|
109
|
-
if (nextSpecial === -1) {
|
|
110
|
-
parts.push(_jsx(Text, { children: remaining }, key++));
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
else if (nextSpecial === 0) {
|
|
114
|
-
// Not a valid markdown char, consume single char
|
|
115
|
-
parts.push(_jsx(Text, { children: remaining[0] }, key++));
|
|
116
|
-
remaining = remaining.slice(1);
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
parts.push(_jsx(Text, { children: remaining.slice(0, nextSpecial) }, key++));
|
|
120
|
-
remaining = remaining.slice(nextSpecial);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return _jsx(Text, { children: parts });
|
|
124
|
-
});
|
|
125
|
-
const CodeBlock = memo(({ code, language, index }) => {
|
|
126
|
-
const lines = code.split('\n');
|
|
127
|
-
return (_jsxs(Box, { flexDirection: "column", marginY: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: "cyan", bold: true, children: language }), _jsxs(Text, { children: ["[", index, "]"] })] }), _jsx(Text, { children: " " }), lines.map((line, i) => (_jsx(SyntaxLine, { line: line, language: language }, i)))] }));
|
|
128
|
-
});
|
|
129
|
-
// Keywords by language - defined outside component to avoid recreation
|
|
130
|
-
const SYNTAX_KEYWORDS = {
|
|
131
|
-
python: ['def', 'class', 'return', 'if', 'else', 'elif', 'for', 'while', 'import', 'from', 'as', 'try', 'except', 'raise', 'with', 'True', 'False', 'None', 'and', 'or', 'not', 'in', 'is', 'lambda', 'pass', 'break', 'continue', 'global', 'async', 'await'],
|
|
132
|
-
javascript: ['const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'class', 'import', 'export', 'from', 'async', 'await', 'try', 'catch', 'throw', 'new', 'this', 'true', 'false', 'null', 'undefined', 'typeof', 'instanceof'],
|
|
133
|
-
typescript: ['const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'class', 'import', 'export', 'from', 'async', 'await', 'try', 'catch', 'throw', 'new', 'this', 'true', 'false', 'null', 'undefined', 'interface', 'type', 'enum', 'private', 'public', 'protected', 'readonly'],
|
|
134
|
-
java: ['public', 'private', 'protected', 'class', 'interface', 'extends', 'implements', 'return', 'if', 'else', 'for', 'while', 'new', 'this', 'static', 'final', 'void', 'int', 'String', 'boolean', 'true', 'false', 'null', 'try', 'catch', 'throw', 'throws'],
|
|
135
|
-
go: ['func', 'var', 'const', 'if', 'else', 'for', 'range', 'switch', 'case', 'return', 'struct', 'interface', 'package', 'import', 'type', 'map', 'chan', 'go', 'defer', 'true', 'false', 'nil'],
|
|
136
|
-
rust: ['fn', 'let', 'mut', 'const', 'if', 'else', 'for', 'while', 'loop', 'match', 'struct', 'enum', 'impl', 'trait', 'pub', 'use', 'mod', 'self', 'super', 'true', 'false', 'return', 'async', 'await'],
|
|
137
|
-
bash: ['if', 'then', 'else', 'fi', 'for', 'do', 'done', 'while', 'case', 'esac', 'function', 'return', 'echo', 'exit', 'export', 'local'],
|
|
138
|
-
sh: ['if', 'then', 'else', 'fi', 'for', 'do', 'done', 'while', 'case', 'esac', 'function', 'return', 'echo', 'exit', 'export', 'local'],
|
|
139
|
-
php: ['function', 'class', 'public', 'private', 'protected', 'return', 'if', 'else', 'elseif', 'for', 'foreach', 'while', 'echo', 'print', 'new', 'this', 'true', 'false', 'null', 'use', 'namespace', 'extends', 'implements'],
|
|
140
|
-
html: ['html', 'head', 'body', 'div', 'span', 'p', 'a', 'img', 'script', 'style', 'link', 'meta', 'title', 'h1', 'h2', 'h3', 'ul', 'li', 'table', 'tr', 'td', 'form', 'input', 'button'],
|
|
141
|
-
css: ['color', 'background', 'margin', 'padding', 'border', 'width', 'height', 'display', 'flex', 'grid', 'position', 'top', 'left', 'right', 'bottom', 'font', 'text'],
|
|
142
|
-
sql: ['SELECT', 'FROM', 'WHERE', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'TABLE', 'INDEX', 'JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER', 'ON', 'AND', 'OR', 'NOT', 'NULL', 'ORDER', 'BY', 'GROUP', 'HAVING', 'LIMIT'],
|
|
143
|
-
};
|
|
144
|
-
// Syntax highlighting component - memoized to prevent re-renders
|
|
145
|
-
const SyntaxLine = memo(({ line, language }) => {
|
|
146
|
-
const lang = language.toLowerCase();
|
|
147
|
-
const langKeywords = SYNTAX_KEYWORDS[lang] || [];
|
|
148
|
-
const parts = [];
|
|
149
|
-
let remaining = line;
|
|
150
|
-
let key = 0;
|
|
151
|
-
while (remaining.length > 0) {
|
|
152
|
-
// Comments
|
|
153
|
-
if (remaining.startsWith('//') || remaining.startsWith('#')) {
|
|
154
|
-
parts.push(_jsx(Text, { color: "cyan", children: remaining }, key++));
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
// Multi-line string/docstring
|
|
158
|
-
if (remaining.startsWith('"""') || remaining.startsWith("'''")) {
|
|
159
|
-
const quote = remaining.slice(0, 3);
|
|
160
|
-
parts.push(_jsx(Text, { color: "green", children: remaining }, key++));
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
// Strings
|
|
164
|
-
const stringMatch = remaining.match(/^(["'`])(?:[^\\]|\\.)*?\1/);
|
|
165
|
-
if (stringMatch) {
|
|
166
|
-
parts.push(_jsx(Text, { color: "green", children: stringMatch[0] }, key++));
|
|
167
|
-
remaining = remaining.slice(stringMatch[0].length);
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
// Keywords
|
|
171
|
-
const wordMatch = remaining.match(/^[a-zA-Z_]\w*/);
|
|
172
|
-
if (wordMatch) {
|
|
173
|
-
const word = wordMatch[0];
|
|
174
|
-
if (langKeywords.includes(word)) {
|
|
175
|
-
parts.push(_jsx(Text, { color: "magenta", bold: true, children: word }, key++));
|
|
176
|
-
}
|
|
177
|
-
else if (word.match(/^[A-Z]/)) {
|
|
178
|
-
// Class names / constants
|
|
179
|
-
parts.push(_jsx(Text, { color: "cyan", children: word }, key++));
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
parts.push(_jsx(Text, { children: word }, key++));
|
|
183
|
-
}
|
|
184
|
-
remaining = remaining.slice(word.length);
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
// Numbers
|
|
188
|
-
const numMatch = remaining.match(/^\d+\.?\d*/);
|
|
189
|
-
if (numMatch) {
|
|
190
|
-
parts.push(_jsx(Text, { color: "#f02a30", children: numMatch[0] }, key++));
|
|
191
|
-
remaining = remaining.slice(numMatch[0].length);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
// Operators and brackets
|
|
195
|
-
const opMatch = remaining.match(/^[+\-*/%=<>!&|^~?:;,.()\[\]{}]+/);
|
|
196
|
-
if (opMatch) {
|
|
197
|
-
parts.push(_jsx(Text, { color: "white", children: opMatch[0] }, key++));
|
|
198
|
-
remaining = remaining.slice(opMatch[0].length);
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
// Default
|
|
202
|
-
parts.push(_jsx(Text, { children: remaining[0] }, key++));
|
|
203
|
-
remaining = remaining.slice(1);
|
|
204
|
-
}
|
|
205
|
-
return _jsx(Text, { children: parts });
|
|
206
|
-
});
|
|
207
|
-
function parseContent(text) {
|
|
208
|
-
const segments = [];
|
|
209
|
-
// Match code blocks: ```lang or ``` followed by code and closing ```
|
|
210
|
-
const codeBlockRegex = /```(\w*)\n?([\s\S]*?)```/g;
|
|
211
|
-
let lastIndex = 0;
|
|
212
|
-
let match;
|
|
213
|
-
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
214
|
-
// Text before code block
|
|
215
|
-
if (match.index > lastIndex) {
|
|
216
|
-
const textBefore = text.slice(lastIndex, match.index).trim();
|
|
217
|
-
if (textBefore) {
|
|
218
|
-
segments.push({ type: 'text', content: textBefore });
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
// Code block
|
|
222
|
-
const lang = match[1] || 'code';
|
|
223
|
-
const code = match[2] || '';
|
|
224
|
-
segments.push({
|
|
225
|
-
type: 'code',
|
|
226
|
-
content: code.trim(),
|
|
227
|
-
language: lang,
|
|
228
|
-
});
|
|
229
|
-
lastIndex = match.index + match[0].length;
|
|
230
|
-
}
|
|
231
|
-
// Remaining text
|
|
232
|
-
if (lastIndex < text.length) {
|
|
233
|
-
const remaining = text.slice(lastIndex).trim();
|
|
234
|
-
if (remaining) {
|
|
235
|
-
segments.push({ type: 'text', content: remaining });
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
if (segments.length === 0) {
|
|
239
|
-
segments.push({ type: 'text', content: text });
|
|
240
|
-
}
|
|
241
|
-
return segments;
|
|
242
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Message } from '../config/index';
|
|
3
|
-
interface MessageListProps {
|
|
4
|
-
messages: Message[];
|
|
5
|
-
streamingContent?: string;
|
|
6
|
-
scrollOffset?: number;
|
|
7
|
-
terminalHeight?: number;
|
|
8
|
-
}
|
|
9
|
-
export declare const MessageList: React.FC<MessageListProps>;
|
|
10
|
-
export {};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { memo, useMemo } from 'react';
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
4
|
-
import { MessageView } from './Message.js';
|
|
5
|
-
import { StreamingMessage } from './StreamingMessage.js';
|
|
6
|
-
/**
|
|
7
|
-
* Memoized individual message component
|
|
8
|
-
* Only re-renders when its specific content changes
|
|
9
|
-
*/
|
|
10
|
-
const MemoizedMessage = memo(({ msg }) => _jsx(MessageView, { role: msg.role, content: msg.content }), (prev, next) => prev.msg.content === next.msg.content && prev.msg.role === next.msg.role);
|
|
11
|
-
MemoizedMessage.displayName = 'MemoizedMessage';
|
|
12
|
-
/**
|
|
13
|
-
* Message list WITHOUT Static component
|
|
14
|
-
*
|
|
15
|
-
* We removed the Static component because:
|
|
16
|
-
* - Static preserves content in terminal scroll history even after unmount
|
|
17
|
-
* - This causes ghost/duplicate content when switching screens
|
|
18
|
-
* - The trade-off is that messages will re-render on each update
|
|
19
|
-
* - We mitigate this with memoization at the individual message level
|
|
20
|
-
*
|
|
21
|
-
* NOTE: This is a temporary solution until we implement a custom renderer
|
|
22
|
-
* like Claude CLI uses (DEC Mode 2026 / synchronized output).
|
|
23
|
-
*/
|
|
24
|
-
// Maximum number of messages to render at once
|
|
25
|
-
// This prevents performance issues and flickering with large chat histories
|
|
26
|
-
const MAX_VISIBLE_MESSAGES = 20;
|
|
27
|
-
export const MessageList = memo(({ messages, streamingContent, }) => {
|
|
28
|
-
// Virtualization: only render the last N messages to prevent flickering
|
|
29
|
-
// Older messages are still in history but not rendered
|
|
30
|
-
const visibleMessages = useMemo(() => {
|
|
31
|
-
if (messages.length <= MAX_VISIBLE_MESSAGES) {
|
|
32
|
-
return messages;
|
|
33
|
-
}
|
|
34
|
-
return messages.slice(-MAX_VISIBLE_MESSAGES);
|
|
35
|
-
}, [messages]);
|
|
36
|
-
// Calculate how many messages are hidden
|
|
37
|
-
const hiddenCount = messages.length - visibleMessages.length;
|
|
38
|
-
// Memoize the messages array rendering
|
|
39
|
-
const renderedMessages = useMemo(() => (visibleMessages.map((msg, index) => (_jsx(MemoizedMessage, { msg: msg, index: hiddenCount + index }, `msg-${hiddenCount + index}-${msg.role}`)))), [visibleMessages, hiddenCount]);
|
|
40
|
-
return (_jsxs(Box, { flexDirection: "column", children: [hiddenCount > 0 && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: "gray", children: ["... ", hiddenCount, " earlier message(s) hidden ..."] }) })), renderedMessages, streamingContent && (_jsx(StreamingMessage, { content: streamingContent }))] }));
|
|
41
|
-
});
|
|
42
|
-
MessageList.displayName = 'MessageList';
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { Box, Text, useInput } from 'ink';
|
|
4
|
-
import { setProjectPermission, hasStandardProjectMarkers, initializeAsProject } from '../config/index.js';
|
|
5
|
-
import { getProjectSummary } from '../utils/project.js';
|
|
6
|
-
export const ProjectPermission = ({ projectPath, onComplete }) => {
|
|
7
|
-
// If folder doesn't have standard project markers, ask if user wants to initialize it
|
|
8
|
-
const isStandardProject = hasStandardProjectMarkers(projectPath);
|
|
9
|
-
const [step, setStep] = useState(isStandardProject ? 'read' : 'init');
|
|
10
|
-
const [readGranted, setReadGranted] = useState(false);
|
|
11
|
-
const summary = getProjectSummary(projectPath);
|
|
12
|
-
const projectName = summary?.name || projectPath.split('/').pop() || 'Unknown';
|
|
13
|
-
useInput((input, key) => {
|
|
14
|
-
const char = input.toLowerCase();
|
|
15
|
-
if (step === 'init') {
|
|
16
|
-
if (char === 'y') {
|
|
17
|
-
// Initialize as project and continue to permissions
|
|
18
|
-
initializeAsProject(projectPath);
|
|
19
|
-
setStep('read');
|
|
20
|
-
}
|
|
21
|
-
else if (char === 'n' || key.escape) {
|
|
22
|
-
// Don't initialize - use global config, skip to read permission
|
|
23
|
-
setStep('read');
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
else if (step === 'read') {
|
|
27
|
-
if (char === 'y') {
|
|
28
|
-
// Grant for this session only
|
|
29
|
-
setReadGranted(true);
|
|
30
|
-
setStep('write');
|
|
31
|
-
}
|
|
32
|
-
else if (char === 'a') {
|
|
33
|
-
// Grant permanently (always)
|
|
34
|
-
setProjectPermission(projectPath, true, false);
|
|
35
|
-
setReadGranted(true);
|
|
36
|
-
setStep('write');
|
|
37
|
-
}
|
|
38
|
-
else if (char === 'n' || key.escape) {
|
|
39
|
-
// Deny
|
|
40
|
-
onComplete(false, false);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
else if (step === 'write') {
|
|
44
|
-
if (char === 'y') {
|
|
45
|
-
// Grant write for session
|
|
46
|
-
onComplete(true, false, true);
|
|
47
|
-
}
|
|
48
|
-
else if (char === 'a') {
|
|
49
|
-
// Grant write permanently
|
|
50
|
-
setProjectPermission(projectPath, true, true);
|
|
51
|
-
onComplete(true, true, true);
|
|
52
|
-
}
|
|
53
|
-
else if (char === 'n' || key.escape) {
|
|
54
|
-
// Read only
|
|
55
|
-
if (readGranted) {
|
|
56
|
-
onComplete(true, false, false);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
onComplete(false, false, false);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "#f02a30", padding: 1, children: [_jsx(Text, { color: "#f02a30", bold: true, children: "Project Access Request" }), _jsx(Text, { children: " " }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { children: [_jsx(Text, { children: "Project: " }), _jsx(Text, { color: "cyan", bold: true, children: projectName })] }), _jsxs(Text, { children: [_jsx(Text, { children: "Path: " }), _jsx(Text, { children: projectPath })] }), summary && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { children: "Type: " }), _jsx(Text, { children: summary.type })] }), _jsxs(Text, { children: [_jsx(Text, { children: "Files: " }), _jsxs(Text, { children: [summary.fileCount, " code files"] })] })] }))] }), _jsx(Text, { children: " " }), step === 'init' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "yellow", children: "This folder is not recognized as a project." }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Initialize as a Codeep project?" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "gray", children: "This will create a .codeep/ folder to store:" }), _jsx(Text, { color: "gray", children: " \u2022 Session history" }), _jsx(Text, { color: "gray", children: " \u2022 Project-specific settings" }), _jsx(Text, { color: "gray", children: " \u2022 Permission preferences" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "gray", children: "If you choose No, settings will be stored globally." }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { color: "green", children: "[Y]" }), _jsx(Text, { children: " Yes, initialize " }), _jsx(Text, { color: "cyan", children: "[N]" }), _jsx(Text, { children: " No, use global config" })] })] })), step === 'read' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "#f02a30", children: "Allow Codeep to read project files?" }), _jsx(Text, { children: " " }), _jsx(Text, { children: "This enables:" }), _jsx(Text, { children: " \u2022 Auto-detect file paths in your messages" }), _jsx(Text, { children: " \u2022 Send file contents to AI for analysis" }), _jsx(Text, { children: " \u2022 Project structure awareness" }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { color: "green", children: "[Y]" }), _jsx(Text, { children: " Yes (this session) " }), _jsx(Text, { color: "cyan", children: "[A]" }), _jsx(Text, { children: " Always " }), _jsx(Text, { color: "#f02a30", children: "[N]" }), _jsx(Text, { children: " No" })] })] })), step === 'write' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "green", children: "\u2713 Read permission granted" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "#f02a30", children: "Allow Codeep to suggest file changes?" }), _jsx(Text, { children: " " }), _jsx(Text, { children: "This enables:" }), _jsx(Text, { children: " \u2022 AI can suggest code modifications" }), _jsx(Text, { children: " \u2022 You'll always see changes before applying" }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { color: "green", children: "[Y]" }), _jsx(Text, { children: " Yes (this session) " }), _jsx(Text, { color: "cyan", children: "[A]" }), _jsx(Text, { children: " Always " }), _jsx(Text, { color: "#f02a30", children: "[N]" }), _jsx(Text, { children: " Read-only" })] })] }))] }));
|
|
65
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { SearchResult } from '../utils/search';
|
|
3
|
-
interface SearchProps {
|
|
4
|
-
results: SearchResult[];
|
|
5
|
-
searchTerm: string;
|
|
6
|
-
onClose: () => void;
|
|
7
|
-
onSelectMessage: (index: number) => void;
|
|
8
|
-
}
|
|
9
|
-
export declare const Search: React.FC<SearchProps>;
|
|
10
|
-
export {};
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { Box, Text, useInput } from 'ink';
|
|
4
|
-
export const Search = ({ results, searchTerm, onClose, onSelectMessage }) => {
|
|
5
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
6
|
-
useInput((input, key) => {
|
|
7
|
-
if (key.escape) {
|
|
8
|
-
onClose();
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
if (key.upArrow) {
|
|
12
|
-
setSelectedIndex(i => Math.max(0, i - 1));
|
|
13
|
-
}
|
|
14
|
-
if (key.downArrow) {
|
|
15
|
-
setSelectedIndex(i => Math.min(results.length - 1, i + 1));
|
|
16
|
-
}
|
|
17
|
-
if (key.return && results.length > 0) {
|
|
18
|
-
onSelectMessage(results[selectedIndex].messageIndex);
|
|
19
|
-
onClose();
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
if (results.length === 0) {
|
|
23
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "#f02a30", padding: 1, children: [_jsx(Text, { color: "#f02a30", bold: true, children: "Search Results" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [_jsx(Text, { children: "Query: " }), _jsxs(Text, { color: "cyan", children: ["\"", searchTerm, "\""] })] }), _jsx(Text, { children: " " }), _jsx(Text, { children: "No results found." }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Press Esc to close" })] }));
|
|
24
|
-
}
|
|
25
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "#f02a30", padding: 1, children: [_jsx(Text, { color: "#f02a30", bold: true, children: "Search Results" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [_jsx(Text, { children: "Query: " }), _jsxs(Text, { color: "cyan", children: ["\"", searchTerm, "\""] }), _jsxs(Text, { children: [" (", results.length, " ", results.length === 1 ? 'result' : 'results', ")"] })] }), _jsx(Text, { children: " " }), results.map((result, i) => {
|
|
26
|
-
const isSelected = i === selectedIndex;
|
|
27
|
-
const roleColor = result.role === 'user' ? 'green' : 'blue';
|
|
28
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: i < results.length - 1 ? 1 : 0, children: [_jsxs(Text, { children: [isSelected ? _jsx(Text, { color: "#f02a30", children: "\u25B8 " }) : ' ', _jsxs(Text, { color: roleColor, bold: true, children: ["[", result.role.toUpperCase(), "]"] }), _jsxs(Text, { children: [" Message #", result.messageIndex + 1] })] }), _jsxs(Text, { children: [' ', _jsx(Text, { children: result.matchedText })] })] }, i));
|
|
29
|
-
}), _jsx(Text, { children: " " }), _jsx(Text, { children: "\u2191/\u2193 Navigate, Enter = Jump to message, Esc = Close" })] }));
|
|
30
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Message } from '../config/index';
|
|
3
|
-
interface SessionPickerProps {
|
|
4
|
-
onSelect: (messages: Message[], sessionName: string) => void;
|
|
5
|
-
onNewSession: () => void;
|
|
6
|
-
projectPath?: string;
|
|
7
|
-
}
|
|
8
|
-
export declare const SessionPicker: React.FC<SessionPickerProps>;
|
|
9
|
-
export {};
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useMemo } from 'react';
|
|
3
|
-
import { Text, Box, useInput } from 'ink';
|
|
4
|
-
import { listSessionsWithInfo, loadSession, startNewSession } from '../config/index.js';
|
|
5
|
-
/**
|
|
6
|
-
* Format relative time (e.g., "today", "yesterday", "3 days ago")
|
|
7
|
-
*/
|
|
8
|
-
function formatRelativeTime(dateStr) {
|
|
9
|
-
const date = new Date(dateStr);
|
|
10
|
-
const now = new Date();
|
|
11
|
-
const diffMs = now.getTime() - date.getTime();
|
|
12
|
-
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
13
|
-
if (diffDays === 0)
|
|
14
|
-
return 'today';
|
|
15
|
-
if (diffDays === 1)
|
|
16
|
-
return 'yesterday';
|
|
17
|
-
if (diffDays < 7)
|
|
18
|
-
return `${diffDays}d ago`;
|
|
19
|
-
if (diffDays < 30)
|
|
20
|
-
return `${Math.floor(diffDays / 7)}w ago`;
|
|
21
|
-
return `${Math.floor(diffDays / 30)}mo ago`;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Format file size (e.g., "1.2 KB")
|
|
25
|
-
*/
|
|
26
|
-
function formatFileSize(bytes) {
|
|
27
|
-
if (bytes < 1024)
|
|
28
|
-
return `${bytes} B`;
|
|
29
|
-
if (bytes < 1024 * 1024)
|
|
30
|
-
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
31
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Truncate session name for display
|
|
35
|
-
*/
|
|
36
|
-
function truncateName(name, maxLength = 25) {
|
|
37
|
-
if (name.length <= maxLength)
|
|
38
|
-
return name;
|
|
39
|
-
return name.substring(0, maxLength - 3) + '...';
|
|
40
|
-
}
|
|
41
|
-
export const SessionPicker = ({ onSelect, onNewSession, projectPath }) => {
|
|
42
|
-
const sessions = useMemo(() => listSessionsWithInfo(projectPath), [projectPath]);
|
|
43
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
44
|
-
useInput((input, key) => {
|
|
45
|
-
// N = New session
|
|
46
|
-
if (input === 'n' || input === 'N') {
|
|
47
|
-
startNewSession();
|
|
48
|
-
onNewSession();
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
// Arrow navigation
|
|
52
|
-
if (key.upArrow) {
|
|
53
|
-
setSelectedIndex(i => Math.max(0, i - 1));
|
|
54
|
-
}
|
|
55
|
-
if (key.downArrow) {
|
|
56
|
-
setSelectedIndex(i => Math.min(sessions.length - 1, i + 1));
|
|
57
|
-
}
|
|
58
|
-
// Enter = Load selected session
|
|
59
|
-
if (key.return && sessions.length > 0) {
|
|
60
|
-
const selected = sessions[selectedIndex];
|
|
61
|
-
const messages = loadSession(selected.name, projectPath);
|
|
62
|
-
if (messages) {
|
|
63
|
-
onSelect(messages, selected.name);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// Escape = Start new session (same as N)
|
|
67
|
-
if (key.escape) {
|
|
68
|
-
startNewSession();
|
|
69
|
-
onNewSession();
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
// If no sessions exist, auto-start new session
|
|
73
|
-
if (sessions.length === 0) {
|
|
74
|
-
// Will trigger on next render cycle
|
|
75
|
-
setTimeout(() => {
|
|
76
|
-
startNewSession();
|
|
77
|
-
onNewSession();
|
|
78
|
-
}, 0);
|
|
79
|
-
return (_jsx(Box, { flexDirection: "column", padding: 1, children: _jsx(Text, { color: "cyan", children: "Starting new session..." }) }));
|
|
80
|
-
}
|
|
81
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Select a session:" }) }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: sessions.map((session, index) => {
|
|
82
|
-
const isSelected = index === selectedIndex;
|
|
83
|
-
const prefix = isSelected ? '→ ' : ' ';
|
|
84
|
-
const name = truncateName(session.name);
|
|
85
|
-
const meta = `${session.messageCount} msg, ${formatRelativeTime(session.createdAt)}`;
|
|
86
|
-
return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? 'green' : 'white', bold: isSelected, children: [prefix, name] }), _jsxs(Text, { color: "cyan", children: [" (", meta, ")"] })] }, session.name));
|
|
87
|
-
}) }), _jsxs(Box, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { color: "yellow", children: "[N]" }), _jsx(Text, { children: " New session" })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "cyan", children: "\u2191\u2193 Navigate " }), _jsx(Text, { color: "cyan", children: "Enter Select " }), _jsx(Text, { color: "cyan", children: "N New " }), _jsx(Text, { color: "cyan", children: "Esc New" })] })] }));
|
|
88
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Message } from '../config/index';
|
|
3
|
-
interface SessionsProps {
|
|
4
|
-
history: Message[];
|
|
5
|
-
onLoad: (history: Message[], name: string) => void;
|
|
6
|
-
onClose: () => void;
|
|
7
|
-
onDelete?: (name: string) => void;
|
|
8
|
-
deleteMode?: boolean;
|
|
9
|
-
projectPath?: string;
|
|
10
|
-
}
|
|
11
|
-
export declare const Sessions: React.FC<SessionsProps>;
|
|
12
|
-
export {};
|