aiexecode 1.0.66 → 1.0.68
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.
Potentially problematic release.
This version of aiexecode might be problematic. Click here for more details.
- package/config_template/settings.json +1 -3
- package/index.js +46 -71
- package/package.json +1 -12
- package/payload_viewer/out/404/index.html +1 -1
- package/payload_viewer/out/404.html +1 -1
- package/payload_viewer/out/index.html +1 -1
- package/payload_viewer/out/index.txt +1 -1
- package/payload_viewer/web_server.js +0 -163
- package/src/ai_based/completion_judge.js +96 -5
- package/src/ai_based/orchestrator.js +71 -3
- package/src/ai_based/pip_package_installer.js +14 -12
- package/src/ai_based/pip_package_lookup.js +13 -10
- package/src/commands/apikey.js +8 -34
- package/src/commands/help.js +3 -4
- package/src/commands/model.js +17 -74
- package/src/commands/reasoning_effort.js +1 -1
- package/src/config/feature_flags.js +0 -12
- package/src/{ui → frontend}/App.js +23 -25
- package/src/frontend/README.md +81 -0
- package/src/{ui/components/SuggestionsDisplay.js → frontend/components/AutocompleteMenu.js} +3 -3
- package/src/{ui/components/HistoryItemDisplay.js → frontend/components/ConversationItem.js} +37 -89
- package/src/{ui → frontend}/components/CurrentModelView.js +3 -5
- package/src/{ui → frontend}/components/Footer.js +4 -6
- package/src/{ui → frontend}/components/Header.js +2 -5
- package/src/{ui/components/InputPrompt.js → frontend/components/Input.js} +16 -54
- package/src/frontend/components/ModelListView.js +106 -0
- package/src/{ui → frontend}/components/ModelUpdatedView.js +3 -5
- package/src/{ui → frontend}/components/SessionSpinner.js +3 -3
- package/src/{ui → frontend}/components/SetupWizard.js +8 -101
- package/src/{ui → frontend}/components/ToolApprovalPrompt.js +16 -14
- package/src/frontend/design/themeColors.js +42 -0
- package/src/{ui → frontend}/index.js +7 -7
- package/src/frontend/utils/inputBuffer.js +441 -0
- package/src/{ui/utils/markdownRenderer.js → frontend/utils/markdownParser.js} +3 -3
- package/src/{ui/utils/ConsolePatcher.js → frontend/utils/outputRedirector.js} +9 -9
- package/src/{ui/utils/codeColorizer.js → frontend/utils/syntaxHighlighter.js} +2 -3
- package/src/system/ai_request.js +145 -595
- package/src/system/code_executer.js +111 -16
- package/src/system/file_integrity.js +5 -7
- package/src/system/log.js +3 -3
- package/src/system/mcp_integration.js +15 -13
- package/src/system/output_helper.js +0 -20
- package/src/system/session.js +97 -23
- package/src/system/session_memory.js +2 -82
- package/src/system/system_info.js +1 -1
- package/src/system/ui_events.js +0 -43
- package/src/tools/code_editor.js +17 -2
- package/src/tools/file_reader.js +17 -2
- package/src/tools/glob.js +9 -1
- package/src/tools/response_message.js +0 -2
- package/src/tools/ripgrep.js +9 -1
- package/src/tools/web_downloader.js +9 -1
- package/src/util/config.js +3 -8
- package/src/util/debug_log.js +4 -11
- package/src/util/mcp_config_manager.js +3 -5
- package/src/util/output_formatter.js +0 -47
- package/src/util/prompt_loader.js +3 -4
- package/src/util/safe_fs.js +60 -0
- package/src/util/setup_wizard.js +1 -3
- package/src/util/text_formatter.js +0 -86
- package/src/config/claude_models.js +0 -195
- package/src/ui/README.md +0 -208
- package/src/ui/api.js +0 -167
- package/src/ui/components/AgenticProgressDisplay.js +0 -126
- package/src/ui/components/Composer.js +0 -55
- package/src/ui/components/LoadingIndicator.js +0 -54
- package/src/ui/components/ModelListView.js +0 -214
- package/src/ui/components/Notifications.js +0 -55
- package/src/ui/components/StreamingIndicator.js +0 -36
- package/src/ui/contexts/AppContext.js +0 -25
- package/src/ui/contexts/StreamingContext.js +0 -20
- package/src/ui/contexts/UIStateContext.js +0 -117
- package/src/ui/example-usage.js +0 -180
- package/src/ui/hooks/useTerminalResize.js +0 -39
- package/src/ui/themes/semantic-tokens.js +0 -73
- package/src/ui/utils/text-buffer.js +0 -975
- /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → Z3AZSKhutj-kS4L8VpcOl}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → Z3AZSKhutj-kS4L8VpcOl}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → Z3AZSKhutj-kS4L8VpcOl}/_ssgManifest.js +0 -0
- /package/src/{ui → frontend}/components/BlankLine.js +0 -0
- /package/src/{ui → frontend}/components/FileDiffViewer.js +0 -0
- /package/src/{ui → frontend}/components/HelpView.js +0 -0
- /package/src/{ui → frontend}/hooks/useCompletion.js +0 -0
- /package/src/{ui → frontend}/hooks/useKeypress.js +0 -0
- /package/src/{ui → frontend}/utils/diffUtils.js +0 -0
- /package/src/{ui → frontend}/utils/renderInkComponent.js +0 -0
package/src/ui/README.md
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
# UI System Documentation
|
|
2
|
-
|
|
3
|
-
이 디렉토리는 Ink 기반의 터미널 UI 시스템을 포함합니다.
|
|
4
|
-
|
|
5
|
-
## 구조
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
ui/
|
|
9
|
-
├── App.js # 메인 앱 진입점 (Context 제공)
|
|
10
|
-
├── index.js # UI 렌더링 진입점
|
|
11
|
-
├── api.js # UI 상태 업데이트 API
|
|
12
|
-
├── example-usage.js # API 사용 예제
|
|
13
|
-
├── contexts/ # React Context 시스템
|
|
14
|
-
│ ├── AppContext.js # 전역 앱 상태
|
|
15
|
-
│ ├── UIStateContext.js # UI 상태 관리
|
|
16
|
-
│ └── StreamingContext.js # 스트리밍 상태
|
|
17
|
-
├── components/ # UI 컴포넌트
|
|
18
|
-
│ ├── Header.js # 헤더 (로고)
|
|
19
|
-
│ ├── Footer.js # 푸터 (상태 정보)
|
|
20
|
-
│ ├── HistoryItemDisplay.js # 개별 히스토리 아이템
|
|
21
|
-
│ ├── InputPrompt.js # 입력 프롬프트
|
|
22
|
-
│ ├── Composer.js # 입력 영역 + 로딩 표시
|
|
23
|
-
│ ├── Notifications.js # 알림/경고 표시
|
|
24
|
-
│ ├── LoadingIndicator.js # 로딩 인디케이터
|
|
25
|
-
│ ├── StreamingIndicator.js # 스트리밍 애니메이션
|
|
26
|
-
│ ├── AgenticProgressDisplay.js # 에이전틱 작업 진행 표시
|
|
27
|
-
│ └── SuggestionsDisplay.js # 자동완성 제안
|
|
28
|
-
├── hooks/ # Custom hooks
|
|
29
|
-
│ ├── useKeypress.js # 키보드 이벤트
|
|
30
|
-
│ └── useCompletion.js # 자동완성
|
|
31
|
-
├── themes/ # 테마 시스템
|
|
32
|
-
│ └── semantic-tokens.js # 색상 토큰
|
|
33
|
-
└── utils/ # 유틸리티
|
|
34
|
-
└── text-buffer.js # 텍스트 버퍼 (멀티라인)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## 아키텍처
|
|
38
|
-
|
|
39
|
-
### Context 시스템
|
|
40
|
-
|
|
41
|
-
**AppContext**: 전역 애플리케이션 상태
|
|
42
|
-
- version, model, buffer, commands, callbacks
|
|
43
|
-
|
|
44
|
-
**UIStateContext**: UI 상태 관리
|
|
45
|
-
- history, pendingHistoryItems
|
|
46
|
-
- streamingState, operations
|
|
47
|
-
- layout dimensions (mainAreaWidth, terminalHeight)
|
|
48
|
-
|
|
49
|
-
**StreamingContext**: 실시간 스트리밍 상태 전달
|
|
50
|
-
|
|
51
|
-
### 레이아웃 구조
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
┌──────────────────────────────────┐
|
|
55
|
-
│ App (Main Layout) │
|
|
56
|
-
│ ├─ Header (Static) │
|
|
57
|
-
│ ├─ History (Static) │
|
|
58
|
-
│ ├─ Pending Items (Dynamic) │
|
|
59
|
-
│ ├─ Notifications │
|
|
60
|
-
│ ├─ Composer │
|
|
61
|
-
│ │ ├─ LoadingIndicator │
|
|
62
|
-
│ │ ├─ AgenticProgressDisplay │
|
|
63
|
-
│ │ └─ InputPrompt │
|
|
64
|
-
│ └─ Footer │
|
|
65
|
-
└──────────────────────────────────┘
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### StreamingState
|
|
69
|
-
|
|
70
|
-
```javascript
|
|
71
|
-
StreamingState = {
|
|
72
|
-
Idle: 'idle', // 대기 중
|
|
73
|
-
Responding: 'responding', // AI 응답 중
|
|
74
|
-
WaitingForConfirmation: 'waiting_for_confirmation',
|
|
75
|
-
Executing: 'executing', // 작업 실행 중
|
|
76
|
-
Completed: 'completed', // 완료
|
|
77
|
-
Error: 'error' // 에러
|
|
78
|
-
}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## UI API 사용법
|
|
82
|
-
|
|
83
|
-
### 기본 사용
|
|
84
|
-
|
|
85
|
-
```javascript
|
|
86
|
-
import {
|
|
87
|
-
StreamingState,
|
|
88
|
-
setStreamingState,
|
|
89
|
-
setThought,
|
|
90
|
-
addHistoryMessage
|
|
91
|
-
} from './ui/api.js';
|
|
92
|
-
|
|
93
|
-
// AI가 생각 중일 때
|
|
94
|
-
setStreamingState(StreamingState.Responding);
|
|
95
|
-
setThought('코드 구조를 분석하고 있습니다...');
|
|
96
|
-
|
|
97
|
-
// 완료 후 메시지 추가
|
|
98
|
-
setStreamingState(StreamingState.Completed);
|
|
99
|
-
addHistoryMessage({
|
|
100
|
-
type: 'assistant',
|
|
101
|
-
text: '분석이 완료되었습니다!'
|
|
102
|
-
});
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### 작업 진행 표시
|
|
106
|
-
|
|
107
|
-
```javascript
|
|
108
|
-
import { startOperation, updateOperation, completeOperation } from './ui/api.js';
|
|
109
|
-
|
|
110
|
-
// 작업 시작
|
|
111
|
-
const opId = startOperation({
|
|
112
|
-
type: 'reading', // thinking, analyzing, reading, writing, editing, executing, etc.
|
|
113
|
-
name: '파일 읽기',
|
|
114
|
-
description: 'src/ 디렉토리 분석 중'
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// 진행 상황 업데이트
|
|
118
|
-
updateOperation(opId, {
|
|
119
|
-
progress: 50,
|
|
120
|
-
detail: 'components/ 처리 중...'
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// 작업 완료
|
|
124
|
-
completeOperation(opId);
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### 자동 작업 관리
|
|
128
|
-
|
|
129
|
-
```javascript
|
|
130
|
-
import { runOperation } from './ui/api.js';
|
|
131
|
-
|
|
132
|
-
await runOperation({
|
|
133
|
-
type: 'building',
|
|
134
|
-
name: '프로젝트 빌드',
|
|
135
|
-
description: 'npm run build'
|
|
136
|
-
}, async (update) => {
|
|
137
|
-
update({ progress: 25, detail: 'TypeScript 컴파일 중...' });
|
|
138
|
-
await buildStep1();
|
|
139
|
-
|
|
140
|
-
update({ progress: 75, detail: '번들링 중...' });
|
|
141
|
-
await buildStep2();
|
|
142
|
-
|
|
143
|
-
update({ progress: 100, detail: '완료!' });
|
|
144
|
-
});
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### 배치 업데이트
|
|
148
|
-
|
|
149
|
-
```javascript
|
|
150
|
-
import { batchUpdate } from './ui/api.js';
|
|
151
|
-
|
|
152
|
-
batchUpdate({
|
|
153
|
-
streamingState: StreamingState.Executing,
|
|
154
|
-
thought: '최선의 접근 방법을 고민 중...',
|
|
155
|
-
progressMessage: '요구사항 분석 중',
|
|
156
|
-
addHistory: {
|
|
157
|
-
type: 'system',
|
|
158
|
-
text: '분석 시작...'
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## 작업 타입 (operation types)
|
|
164
|
-
|
|
165
|
-
| Type | Icon | Description |
|
|
166
|
-
|------|------|-------------|
|
|
167
|
-
| `thinking` | 🤔 | 생각/추론 중 |
|
|
168
|
-
| `analyzing` | 🔍 | 분석 중 |
|
|
169
|
-
| `reading` | 📖 | 파일 읽기 |
|
|
170
|
-
| `writing` | ✏️ | 파일 쓰기 |
|
|
171
|
-
| `editing` | 📝 | 파일 편집 |
|
|
172
|
-
| `executing` | ⚙️ | 명령 실행 |
|
|
173
|
-
| `building` | 🔨 | 빌드 |
|
|
174
|
-
| `testing` | 🧪 | 테스트 |
|
|
175
|
-
| `debugging` | 🐛 | 디버깅 |
|
|
176
|
-
| `searching` | 🔎 | 검색 |
|
|
177
|
-
| `planning` | 📋 | 계획 수립 |
|
|
178
|
-
|
|
179
|
-
## 메시지 타입
|
|
180
|
-
|
|
181
|
-
| Type | Icon | Color | Usage |
|
|
182
|
-
|------|------|-------|-------|
|
|
183
|
-
| `user` | `>` | Accent | 사용자 입력 |
|
|
184
|
-
| `assistant` | `◆` | Info | AI 응답 |
|
|
185
|
-
| `system` | `ℹ` | Secondary | 시스템 메시지 |
|
|
186
|
-
| `error` | `✗` | Error | 에러 메시지 |
|
|
187
|
-
| `tool` | `⚙` | Success | 도구 실행 결과 |
|
|
188
|
-
| `thinking` | `💭` | Link | AI 사고 과정 |
|
|
189
|
-
|
|
190
|
-
## 애니메이션
|
|
191
|
-
|
|
192
|
-
### Spinner
|
|
193
|
-
- 속도: 80ms per frame
|
|
194
|
-
- 프레임: `['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']`
|
|
195
|
-
|
|
196
|
-
### Dots
|
|
197
|
-
- 속도: 400ms per frame
|
|
198
|
-
- 프레임: `['. ', '.. ', '...', ' ']`
|
|
199
|
-
|
|
200
|
-
## 성능 최적화
|
|
201
|
-
|
|
202
|
-
1. **Static 컴포넌트**: 불변 히스토리는 Static으로 렌더링하여 리렌더링 방지
|
|
203
|
-
2. **Dynamic 영역**: 진행 중인 작업만 동적으로 업데이트
|
|
204
|
-
3. **조건부 렌더링**: 필요한 컴포넌트만 표시
|
|
205
|
-
|
|
206
|
-
## 예제
|
|
207
|
-
|
|
208
|
-
자세한 사용 예제는 `example-usage.js` 파일을 참고하세요.
|
package/src/ui/api.js
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UI API - Helper functions to update UI state from external code
|
|
3
|
-
*
|
|
4
|
-
* This module provides a simple API for the AI agent to update the UI
|
|
5
|
-
* with progress information, operations, and streaming states.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
let uiStateRef = null;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Initialize the UI API with the UI state reference
|
|
12
|
-
*/
|
|
13
|
-
export function initializeUIAPI(uiState) {
|
|
14
|
-
uiStateRef = uiState;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Add a message to the history
|
|
19
|
-
*/
|
|
20
|
-
export function addHistoryMessage(message) {
|
|
21
|
-
if (!uiStateRef) return;
|
|
22
|
-
|
|
23
|
-
uiStateRef.setHistory(prev => [...prev, {
|
|
24
|
-
id: Date.now(),
|
|
25
|
-
...message
|
|
26
|
-
}]);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Add a pending history item (for streaming content)
|
|
31
|
-
*/
|
|
32
|
-
export function addPendingMessage(message) {
|
|
33
|
-
if (!uiStateRef) return;
|
|
34
|
-
|
|
35
|
-
uiStateRef.setPendingHistoryItems(prev => [...prev, message]);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Clear pending messages
|
|
40
|
-
*/
|
|
41
|
-
export function clearPendingMessages() {
|
|
42
|
-
if (!uiStateRef) return;
|
|
43
|
-
|
|
44
|
-
uiStateRef.setPendingHistoryItems([]);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Update streaming state
|
|
49
|
-
*/
|
|
50
|
-
export function setStreamingState(state) {
|
|
51
|
-
if (!uiStateRef) return;
|
|
52
|
-
|
|
53
|
-
uiStateRef.setStreamingState(state);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Set the current thought/reasoning message
|
|
58
|
-
*/
|
|
59
|
-
export function setThought(thought) {
|
|
60
|
-
if (!uiStateRef) return;
|
|
61
|
-
|
|
62
|
-
uiStateRef.setThought(thought);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Set progress message
|
|
67
|
-
*/
|
|
68
|
-
export function setProgressMessage(message) {
|
|
69
|
-
if (!uiStateRef) return;
|
|
70
|
-
|
|
71
|
-
uiStateRef.setProgressMessage(message);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Start a new operation
|
|
76
|
-
* @param {Object} operation - { type, name, description }
|
|
77
|
-
* @returns {number} operation ID
|
|
78
|
-
*/
|
|
79
|
-
export function startOperation(operation) {
|
|
80
|
-
if (!uiStateRef) return null;
|
|
81
|
-
|
|
82
|
-
const id = Date.now();
|
|
83
|
-
uiStateRef.addOperation({
|
|
84
|
-
...operation,
|
|
85
|
-
id,
|
|
86
|
-
startTime: Date.now(),
|
|
87
|
-
status: 'running'
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
return id;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Update an existing operation
|
|
95
|
-
* @param {number} id - Operation ID
|
|
96
|
-
* @param {Object} updates - { status, progress, detail }
|
|
97
|
-
*/
|
|
98
|
-
export function updateOperation(id, updates) {
|
|
99
|
-
if (!uiStateRef) return;
|
|
100
|
-
|
|
101
|
-
uiStateRef.updateOperation(id, updates);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Complete an operation
|
|
106
|
-
* @param {number} id - Operation ID
|
|
107
|
-
*/
|
|
108
|
-
export function completeOperation(id) {
|
|
109
|
-
if (!uiStateRef) return;
|
|
110
|
-
|
|
111
|
-
uiStateRef.completeOperation(id);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Add an operation with automatic completion
|
|
116
|
-
* Useful for simple sequential operations
|
|
117
|
-
*/
|
|
118
|
-
export async function runOperation(operation, asyncFn) {
|
|
119
|
-
const opId = startOperation(operation);
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
const result = await asyncFn((updates) => {
|
|
123
|
-
updateOperation(opId, updates);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
completeOperation(opId);
|
|
127
|
-
return result;
|
|
128
|
-
} catch (error) {
|
|
129
|
-
updateOperation(opId, {
|
|
130
|
-
status: 'error',
|
|
131
|
-
detail: error.message
|
|
132
|
-
});
|
|
133
|
-
throw error;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Clear all operations
|
|
139
|
-
*/
|
|
140
|
-
export function clearOperations() {
|
|
141
|
-
if (!uiStateRef) return;
|
|
142
|
-
|
|
143
|
-
uiStateRef.setOperations([]);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Batch update multiple UI elements at once
|
|
148
|
-
*/
|
|
149
|
-
export function batchUpdate(updates) {
|
|
150
|
-
if (!uiStateRef) return;
|
|
151
|
-
|
|
152
|
-
if (updates.streamingState !== undefined) {
|
|
153
|
-
setStreamingState(updates.streamingState);
|
|
154
|
-
}
|
|
155
|
-
if (updates.thought !== undefined) {
|
|
156
|
-
setThought(updates.thought);
|
|
157
|
-
}
|
|
158
|
-
if (updates.progressMessage !== undefined) {
|
|
159
|
-
setProgressMessage(updates.progressMessage);
|
|
160
|
-
}
|
|
161
|
-
if (updates.addHistory) {
|
|
162
|
-
addHistoryMessage(updates.addHistory);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Export StreamingState enum for convenience
|
|
167
|
-
export { StreamingState } from './contexts/UIStateContext.js';
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgenticProgressDisplay - Shows agentic coding operations with detailed progress
|
|
3
|
-
* Isolated animation to prevent parent re-renders
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { useState, useEffect, memo } from 'react';
|
|
7
|
-
import { Box, Text } from 'ink';
|
|
8
|
-
import { theme } from '../themes/semantic-tokens.js';
|
|
9
|
-
|
|
10
|
-
const OPERATION_ICONS = {
|
|
11
|
-
thinking: '🤔',
|
|
12
|
-
analyzing: '🔍',
|
|
13
|
-
reading: '📖',
|
|
14
|
-
writing: '✏️',
|
|
15
|
-
editing: '📝',
|
|
16
|
-
executing: '⚙️',
|
|
17
|
-
building: '🔨',
|
|
18
|
-
testing: '🧪',
|
|
19
|
-
debugging: '🐛',
|
|
20
|
-
searching: '🔎',
|
|
21
|
-
planning: '📋',
|
|
22
|
-
completed: '✅'
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
26
|
-
|
|
27
|
-
// operations 렌더링 활성화 여부
|
|
28
|
-
const RENDER_OPERATIONS = false;
|
|
29
|
-
|
|
30
|
-
// Isolated spinner component
|
|
31
|
-
const SpinnerFrame = memo(function SpinnerFrame() {
|
|
32
|
-
const [frameIndex, setFrameIndex] = useState(0);
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
const interval = setInterval(() => {
|
|
36
|
-
setFrameIndex(prev => (prev + 1) % SPINNER_FRAMES.length);
|
|
37
|
-
}, 80);
|
|
38
|
-
|
|
39
|
-
return () => clearInterval(interval);
|
|
40
|
-
}, []);
|
|
41
|
-
|
|
42
|
-
return React.createElement(Text, { color: theme.status.info },
|
|
43
|
-
SPINNER_FRAMES[frameIndex] + ' '
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// Main component
|
|
48
|
-
export const AgenticProgressDisplay = memo(function AgenticProgressDisplay({ operations = [] }) {
|
|
49
|
-
if (!RENDER_OPERATIONS || operations.length === 0) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return React.createElement(Box, {
|
|
54
|
-
flexDirection: "column",
|
|
55
|
-
borderStyle: "round",
|
|
56
|
-
borderColor: theme.border.default,
|
|
57
|
-
paddingX: 1,
|
|
58
|
-
marginBottom: 1
|
|
59
|
-
},
|
|
60
|
-
React.createElement(Box, { marginBottom: 1 },
|
|
61
|
-
React.createElement(Text, { color: theme.text.accent, bold: true },
|
|
62
|
-
'🤖 Agentic Operations')
|
|
63
|
-
),
|
|
64
|
-
operations.map((op, index) =>
|
|
65
|
-
React.createElement(OperationItem, {
|
|
66
|
-
key: op.id || index,
|
|
67
|
-
operation: op
|
|
68
|
-
})
|
|
69
|
-
)
|
|
70
|
-
);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const OperationItem = memo(function OperationItem({ operation }) {
|
|
74
|
-
const { type, name, status, progress, detail, startTime } = operation;
|
|
75
|
-
|
|
76
|
-
const icon = OPERATION_ICONS[type] || '•';
|
|
77
|
-
const statusColor = {
|
|
78
|
-
running: theme.status.info,
|
|
79
|
-
completed: theme.status.success,
|
|
80
|
-
error: theme.status.error,
|
|
81
|
-
waiting: theme.text.secondary
|
|
82
|
-
}[status] || theme.text.secondary;
|
|
83
|
-
|
|
84
|
-
const elapsed = startTime ? Math.floor((Date.now() - startTime) / 1000) : 0;
|
|
85
|
-
|
|
86
|
-
return React.createElement(Box, {
|
|
87
|
-
flexDirection: "column",
|
|
88
|
-
marginBottom: 0
|
|
89
|
-
},
|
|
90
|
-
React.createElement(Box, { flexDirection: "row" },
|
|
91
|
-
status === 'running' && React.createElement(SpinnerFrame),
|
|
92
|
-
React.createElement(Text, null, icon + ' '),
|
|
93
|
-
React.createElement(Text, { color: statusColor, bold: status === 'running' }, name),
|
|
94
|
-
status === 'running' && elapsed > 0 && React.createElement(Text, {
|
|
95
|
-
color: theme.text.secondary,
|
|
96
|
-
dimColor: true
|
|
97
|
-
}, ` (${elapsed}s)`)
|
|
98
|
-
),
|
|
99
|
-
detail && React.createElement(Box, { marginLeft: 4 },
|
|
100
|
-
React.createElement(Text, {
|
|
101
|
-
color: theme.text.secondary,
|
|
102
|
-
dimColor: true
|
|
103
|
-
}, detail)
|
|
104
|
-
),
|
|
105
|
-
progress && React.createElement(Box, { marginLeft: 4 },
|
|
106
|
-
React.createElement(ProgressBar, { progress })
|
|
107
|
-
)
|
|
108
|
-
);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const ProgressBar = memo(function ProgressBar({ progress }) {
|
|
112
|
-
const percentage = Math.min(100, Math.max(0, progress));
|
|
113
|
-
const width = 40;
|
|
114
|
-
const filled = Math.floor((percentage / 100) * width);
|
|
115
|
-
const empty = width - filled;
|
|
116
|
-
|
|
117
|
-
const bar = '█'.repeat(filled) + '░'.repeat(empty);
|
|
118
|
-
|
|
119
|
-
return React.createElement(Box, { flexDirection: "row" },
|
|
120
|
-
React.createElement(Text, { color: theme.status.info }, bar),
|
|
121
|
-
React.createElement(Text, {
|
|
122
|
-
color: theme.text.secondary,
|
|
123
|
-
dimColor: true
|
|
124
|
-
}, ` ${percentage.toFixed(0)}%`)
|
|
125
|
-
);
|
|
126
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Composer - Input area with loading indicators and context display
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import React from 'react';
|
|
6
|
-
import { Box } from 'ink';
|
|
7
|
-
import { InputPrompt } from './InputPrompt.js';
|
|
8
|
-
import { LoadingIndicator } from './LoadingIndicator.js';
|
|
9
|
-
import { AgenticProgressDisplay } from './AgenticProgressDisplay.js';
|
|
10
|
-
import { SessionSpinner } from './SessionSpinner.js';
|
|
11
|
-
import { useUIState, StreamingState } from '../contexts/UIStateContext.js';
|
|
12
|
-
import { createDebugLogger } from '../../util/debug_log.js';
|
|
13
|
-
|
|
14
|
-
const debugLog = createDebugLogger('ui_components.log', 'Composer');
|
|
15
|
-
|
|
16
|
-
// 로딩/진행상황 렌더링 활성화 여부
|
|
17
|
-
const RENDER_LOADING_PROGRESS = false;
|
|
18
|
-
|
|
19
|
-
export function Composer({ onSubmit, onClearScreen, commands = [], buffer }) {
|
|
20
|
-
debugLog('Composer rendering');
|
|
21
|
-
const uiState = useUIState();
|
|
22
|
-
|
|
23
|
-
const showLoading = uiState.streamingState === StreamingState.Responding ||
|
|
24
|
-
uiState.streamingState === StreamingState.Executing;
|
|
25
|
-
|
|
26
|
-
debugLog(`Composer - streamingState: ${uiState.streamingState}, showLoading: ${showLoading}, operations: ${uiState.operations.length}`);
|
|
27
|
-
|
|
28
|
-
return React.createElement(Box, {
|
|
29
|
-
flexDirection: "column",
|
|
30
|
-
flexShrink: 0
|
|
31
|
-
},
|
|
32
|
-
RENDER_LOADING_PROGRESS && showLoading && React.createElement(LoadingIndicator, {
|
|
33
|
-
thought: uiState.thought,
|
|
34
|
-
currentLoadingPhrase: uiState.progressMessage,
|
|
35
|
-
elapsedTime: uiState.elapsedTime
|
|
36
|
-
}),
|
|
37
|
-
|
|
38
|
-
RENDER_LOADING_PROGRESS && uiState.operations.length > 0 && React.createElement(AgenticProgressDisplay, {
|
|
39
|
-
operations: uiState.operations
|
|
40
|
-
}),
|
|
41
|
-
|
|
42
|
-
React.createElement(SessionSpinner, {
|
|
43
|
-
isRunning: uiState.isSessionRunning,
|
|
44
|
-
message: uiState.sessionMessage
|
|
45
|
-
}),
|
|
46
|
-
|
|
47
|
-
React.createElement(InputPrompt, {
|
|
48
|
-
buffer,
|
|
49
|
-
onSubmit,
|
|
50
|
-
onClearScreen,
|
|
51
|
-
commands,
|
|
52
|
-
focus: true
|
|
53
|
-
})
|
|
54
|
-
);
|
|
55
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LoadingIndicator - Shows loading state with thought/progress message
|
|
3
|
-
* Isolated animation to prevent parent re-renders
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { useState, useEffect, memo } from 'react';
|
|
7
|
-
import { Box, Text } from 'ink';
|
|
8
|
-
import { theme } from '../themes/semantic-tokens.js';
|
|
9
|
-
|
|
10
|
-
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
11
|
-
|
|
12
|
-
// Isolated spinner animation component
|
|
13
|
-
const SpinnerFrame = memo(function SpinnerFrame() {
|
|
14
|
-
const [frameIndex, setFrameIndex] = useState(0);
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
const interval = setInterval(() => {
|
|
18
|
-
setFrameIndex(prev => (prev + 1) % SPINNER_FRAMES.length);
|
|
19
|
-
}, 80);
|
|
20
|
-
|
|
21
|
-
return () => clearInterval(interval);
|
|
22
|
-
}, []);
|
|
23
|
-
|
|
24
|
-
return React.createElement(Text, { color: theme.status.info },
|
|
25
|
-
SPINNER_FRAMES[frameIndex] + ' '
|
|
26
|
-
);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// Main component
|
|
30
|
-
export const LoadingIndicator = memo(function LoadingIndicator({ thought, currentLoadingPhrase, elapsedTime }) {
|
|
31
|
-
if (!thought && !currentLoadingPhrase && !elapsedTime) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const displayTime = elapsedTime ? `${Math.floor(elapsedTime / 1000)}s` : '';
|
|
36
|
-
|
|
37
|
-
return React.createElement(Box, {
|
|
38
|
-
flexDirection: "column",
|
|
39
|
-
marginBottom: 0
|
|
40
|
-
},
|
|
41
|
-
thought && React.createElement(Box, null,
|
|
42
|
-
React.createElement(SpinnerFrame),
|
|
43
|
-
React.createElement(Text, { color: theme.text.link, italic: true }, '💭 ' + thought)
|
|
44
|
-
),
|
|
45
|
-
currentLoadingPhrase && React.createElement(Box, null,
|
|
46
|
-
React.createElement(SpinnerFrame),
|
|
47
|
-
React.createElement(Text, { color: theme.text.secondary }, currentLoadingPhrase),
|
|
48
|
-
displayTime && React.createElement(Text, {
|
|
49
|
-
color: theme.text.secondary,
|
|
50
|
-
dimColor: true
|
|
51
|
-
}, ` (${displayTime})`)
|
|
52
|
-
)
|
|
53
|
-
);
|
|
54
|
-
});
|