@yivan-lab/pretty-please 1.0.0 → 1.2.0
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/README.md +381 -28
- package/bin/pls.tsx +1138 -109
- package/dist/bin/pls.d.ts +1 -1
- package/dist/bin/pls.js +994 -91
- package/dist/package.json +80 -0
- package/dist/src/ai.d.ts +1 -41
- package/dist/src/ai.js +9 -190
- package/dist/src/alias.d.ts +41 -0
- package/dist/src/alias.js +240 -0
- package/dist/src/builtin-detector.d.ts +14 -8
- package/dist/src/builtin-detector.js +36 -16
- package/dist/src/chat-history.d.ts +16 -11
- package/dist/src/chat-history.js +35 -4
- package/dist/src/components/Chat.js +5 -4
- package/dist/src/components/CodeColorizer.js +26 -20
- package/dist/src/components/CommandBox.js +3 -17
- package/dist/src/components/ConfirmationPrompt.d.ts +2 -1
- package/dist/src/components/ConfirmationPrompt.js +9 -4
- package/dist/src/components/Duration.js +2 -1
- package/dist/src/components/InlineRenderer.js +2 -1
- package/dist/src/components/MarkdownDisplay.js +2 -1
- package/dist/src/components/MultiStepCommandGenerator.d.ts +5 -1
- package/dist/src/components/MultiStepCommandGenerator.js +127 -14
- package/dist/src/components/TableRenderer.js +2 -1
- package/dist/src/config.d.ts +59 -9
- package/dist/src/config.js +147 -48
- package/dist/src/history.d.ts +19 -5
- package/dist/src/history.js +26 -11
- package/dist/src/mastra-agent.d.ts +0 -1
- package/dist/src/mastra-agent.js +3 -4
- package/dist/src/mastra-chat.d.ts +28 -0
- package/dist/src/mastra-chat.js +93 -0
- package/dist/src/multi-step.d.ts +23 -7
- package/dist/src/multi-step.js +29 -6
- package/dist/src/prompts.d.ts +11 -0
- package/dist/src/prompts.js +140 -0
- package/dist/src/remote-history.d.ts +63 -0
- package/dist/src/remote-history.js +315 -0
- package/dist/src/remote.d.ts +113 -0
- package/dist/src/remote.js +634 -0
- package/dist/src/shell-hook.d.ts +87 -12
- package/dist/src/shell-hook.js +315 -17
- package/dist/src/sysinfo.d.ts +9 -5
- package/dist/src/sysinfo.js +2 -2
- package/dist/src/ui/theme.d.ts +27 -24
- package/dist/src/ui/theme.js +71 -21
- package/dist/src/upgrade.d.ts +41 -0
- package/dist/src/upgrade.js +348 -0
- package/dist/src/utils/console.d.ts +11 -11
- package/dist/src/utils/console.js +26 -17
- package/package.json +11 -9
- package/src/alias.ts +301 -0
- package/src/builtin-detector.ts +126 -0
- package/src/chat-history.ts +140 -0
- package/src/components/Chat.tsx +6 -5
- package/src/components/CodeColorizer.tsx +27 -19
- package/src/components/CommandBox.tsx +3 -17
- package/src/components/ConfirmationPrompt.tsx +11 -3
- package/src/components/Duration.tsx +2 -1
- package/src/components/InlineRenderer.tsx +2 -1
- package/src/components/MarkdownDisplay.tsx +2 -1
- package/src/components/MultiStepCommandGenerator.tsx +167 -16
- package/src/components/TableRenderer.tsx +2 -1
- package/src/config.ts +394 -0
- package/src/history.ts +160 -0
- package/src/mastra-agent.ts +3 -4
- package/src/mastra-chat.ts +124 -0
- package/src/multi-step.ts +45 -8
- package/src/prompts.ts +154 -0
- package/src/remote-history.ts +390 -0
- package/src/remote.ts +800 -0
- package/src/shell-hook.ts +754 -0
- package/src/{sysinfo.js → sysinfo.ts} +28 -16
- package/src/ui/theme.ts +101 -24
- package/src/upgrade.ts +397 -0
- package/src/utils/{console.js → console.ts} +36 -27
- package/bin/pls.js +0 -681
- package/src/ai.js +0 -324
- package/src/builtin-detector.js +0 -98
- package/src/chat-history.js +0 -94
- package/src/components/ChatStatus.tsx +0 -53
- package/src/components/CommandGenerator.tsx +0 -184
- package/src/components/ConfigDisplay.tsx +0 -64
- package/src/components/ConfigWizard.tsx +0 -101
- package/src/components/HistoryDisplay.tsx +0 -69
- package/src/components/HookManager.tsx +0 -150
- package/src/config.js +0 -221
- package/src/history.js +0 -131
- package/src/shell-hook.js +0 -393
|
@@ -7,26 +7,50 @@
|
|
|
7
7
|
// Shell 内置命令列表
|
|
8
8
|
const SHELL_BUILTINS = [
|
|
9
9
|
// 目录相关
|
|
10
|
-
'cd',
|
|
10
|
+
'cd',
|
|
11
|
+
'pushd',
|
|
12
|
+
'popd',
|
|
13
|
+
'dirs',
|
|
11
14
|
// 历史相关
|
|
12
15
|
'history',
|
|
13
16
|
// 别名和函数
|
|
14
|
-
'alias',
|
|
17
|
+
'alias',
|
|
18
|
+
'unalias',
|
|
15
19
|
// 环境变量
|
|
16
|
-
'export',
|
|
20
|
+
'export',
|
|
21
|
+
'set',
|
|
22
|
+
'unset',
|
|
23
|
+
'declare',
|
|
24
|
+
'local',
|
|
25
|
+
'readonly',
|
|
17
26
|
// 脚本执行
|
|
18
|
-
'source',
|
|
27
|
+
'source',
|
|
28
|
+
'.',
|
|
19
29
|
// 任务控制
|
|
20
|
-
'jobs',
|
|
30
|
+
'jobs',
|
|
31
|
+
'fg',
|
|
32
|
+
'bg',
|
|
33
|
+
'disown',
|
|
21
34
|
// 其他
|
|
22
|
-
'ulimit',
|
|
23
|
-
'
|
|
24
|
-
'
|
|
35
|
+
'ulimit',
|
|
36
|
+
'umask',
|
|
37
|
+
'builtin',
|
|
38
|
+
'command',
|
|
39
|
+
'type',
|
|
40
|
+
'enable',
|
|
41
|
+
'hash',
|
|
42
|
+
'help',
|
|
43
|
+
'let',
|
|
44
|
+
'read',
|
|
45
|
+
'wait',
|
|
46
|
+
'eval',
|
|
47
|
+
'exec',
|
|
48
|
+
'trap',
|
|
49
|
+
'times',
|
|
50
|
+
'shopt',
|
|
25
51
|
];
|
|
26
52
|
/**
|
|
27
53
|
* 提取命令中的所有命令名
|
|
28
|
-
* @param {string} command - 完整命令字符串
|
|
29
|
-
* @returns {string[]} 命令名数组
|
|
30
54
|
*/
|
|
31
55
|
function extractCommandNames(command) {
|
|
32
56
|
// 按分隔符拆分(&&, ||, ;, |, &, 换行)
|
|
@@ -39,7 +63,7 @@ function extractCommandNames(command) {
|
|
|
39
63
|
continue;
|
|
40
64
|
// 提取第一个单词(命令名)
|
|
41
65
|
// 处理 sudo、env 等前缀
|
|
42
|
-
|
|
66
|
+
const words = trimmed.split(/\s+/);
|
|
43
67
|
// 跳过 sudo、env 等
|
|
44
68
|
let i = 0;
|
|
45
69
|
while (i < words.length && ['sudo', 'env', 'nohup', 'nice'].includes(words[i])) {
|
|
@@ -53,8 +77,6 @@ function extractCommandNames(command) {
|
|
|
53
77
|
}
|
|
54
78
|
/**
|
|
55
79
|
* 检测命令中是否包含 builtin
|
|
56
|
-
* @param {string} command - 要检测的命令
|
|
57
|
-
* @returns {{ hasBuiltin: boolean, builtins: string[] }} 检测结果
|
|
58
80
|
*/
|
|
59
81
|
export function detectBuiltin(command) {
|
|
60
82
|
const commandNames = extractCommandNames(command);
|
|
@@ -66,13 +88,11 @@ export function detectBuiltin(command) {
|
|
|
66
88
|
}
|
|
67
89
|
return {
|
|
68
90
|
hasBuiltin: foundBuiltins.length > 0,
|
|
69
|
-
builtins: [...new Set(foundBuiltins)] // 去重
|
|
91
|
+
builtins: [...new Set(foundBuiltins)], // 去重
|
|
70
92
|
};
|
|
71
93
|
}
|
|
72
94
|
/**
|
|
73
95
|
* 格式化 builtin 列表为易读的字符串
|
|
74
|
-
* @param {string[]} builtins - builtin 命令数组
|
|
75
|
-
* @returns {string} 格式化后的字符串
|
|
76
96
|
*/
|
|
77
97
|
export function formatBuiltins(builtins) {
|
|
78
98
|
if (builtins.length === 0)
|
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @returns {Array<{role: string, content: string}>} messages 数组
|
|
2
|
+
* 聊天消息
|
|
4
3
|
*/
|
|
5
|
-
export
|
|
6
|
-
role:
|
|
4
|
+
export interface ChatMessage {
|
|
5
|
+
role: 'user' | 'assistant';
|
|
7
6
|
content: string;
|
|
8
|
-
}
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 读取对话历史
|
|
10
|
+
*/
|
|
11
|
+
export declare function getChatHistory(): ChatMessage[];
|
|
9
12
|
/**
|
|
10
13
|
* 添加一轮对话(用户问题 + AI 回答)
|
|
11
|
-
* @param {string} userMessage - 用户消息
|
|
12
|
-
* @param {string} assistantMessage - AI 回复
|
|
13
14
|
*/
|
|
14
|
-
export function addChatMessage(userMessage: string, assistantMessage: string): void;
|
|
15
|
+
export declare function addChatMessage(userMessage: string, assistantMessage: string): void;
|
|
15
16
|
/**
|
|
16
17
|
* 清空对话历史
|
|
17
18
|
*/
|
|
18
|
-
export function clearChatHistory(): void;
|
|
19
|
+
export declare function clearChatHistory(): void;
|
|
19
20
|
/**
|
|
20
21
|
* 获取对话历史文件路径
|
|
21
22
|
*/
|
|
22
|
-
export function getChatHistoryFilePath(): string;
|
|
23
|
+
export declare function getChatHistoryFilePath(): string;
|
|
23
24
|
/**
|
|
24
25
|
* 获取当前对话轮数
|
|
25
26
|
*/
|
|
26
|
-
export function getChatRoundCount(): number;
|
|
27
|
+
export declare function getChatRoundCount(): number;
|
|
28
|
+
/**
|
|
29
|
+
* 显示对话历史(只显示用户的 prompt)
|
|
30
|
+
*/
|
|
31
|
+
export declare function displayChatHistory(): void;
|
package/dist/src/chat-history.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import os from 'os';
|
|
4
|
+
import chalk from 'chalk';
|
|
4
5
|
import { getConfig } from './config.js';
|
|
6
|
+
import { getCurrentTheme } from './ui/theme.js';
|
|
7
|
+
// 获取主题颜色
|
|
8
|
+
function getColors() {
|
|
9
|
+
const theme = getCurrentTheme();
|
|
10
|
+
return {
|
|
11
|
+
primary: theme.primary,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
5
14
|
const CONFIG_DIR = path.join(os.homedir(), '.please');
|
|
6
15
|
const CHAT_HISTORY_FILE = path.join(CONFIG_DIR, 'chat_history.json');
|
|
7
16
|
/**
|
|
@@ -14,7 +23,6 @@ function ensureConfigDir() {
|
|
|
14
23
|
}
|
|
15
24
|
/**
|
|
16
25
|
* 读取对话历史
|
|
17
|
-
* @returns {Array<{role: string, content: string}>} messages 数组
|
|
18
26
|
*/
|
|
19
27
|
export function getChatHistory() {
|
|
20
28
|
ensureConfigDir();
|
|
@@ -31,7 +39,6 @@ export function getChatHistory() {
|
|
|
31
39
|
}
|
|
32
40
|
/**
|
|
33
41
|
* 保存对话历史
|
|
34
|
-
* @param {Array<{role: string, content: string}>} history
|
|
35
42
|
*/
|
|
36
43
|
function saveChatHistory(history) {
|
|
37
44
|
ensureConfigDir();
|
|
@@ -39,8 +46,6 @@ function saveChatHistory(history) {
|
|
|
39
46
|
}
|
|
40
47
|
/**
|
|
41
48
|
* 添加一轮对话(用户问题 + AI 回答)
|
|
42
|
-
* @param {string} userMessage - 用户消息
|
|
43
|
-
* @param {string} assistantMessage - AI 回复
|
|
44
49
|
*/
|
|
45
50
|
export function addChatMessage(userMessage, assistantMessage) {
|
|
46
51
|
const config = getConfig();
|
|
@@ -79,3 +84,29 @@ export function getChatRoundCount() {
|
|
|
79
84
|
const history = getChatHistory();
|
|
80
85
|
return Math.floor(history.length / 2);
|
|
81
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* 显示对话历史(只显示用户的 prompt)
|
|
89
|
+
*/
|
|
90
|
+
export function displayChatHistory() {
|
|
91
|
+
const history = getChatHistory();
|
|
92
|
+
const config = getConfig();
|
|
93
|
+
const colors = getColors();
|
|
94
|
+
if (history.length === 0) {
|
|
95
|
+
console.log('\n' + chalk.gray('暂无对话历史'));
|
|
96
|
+
console.log('');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// 只提取用户消息
|
|
100
|
+
const userMessages = history.filter((msg) => msg.role === 'user');
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log(chalk.bold(`对话历史(最近 ${userMessages.length} 轮):`));
|
|
103
|
+
console.log(chalk.gray('━'.repeat(50)));
|
|
104
|
+
userMessages.forEach((msg, index) => {
|
|
105
|
+
const num = index + 1;
|
|
106
|
+
console.log(` ${chalk.hex(colors.primary)(num.toString().padStart(2, ' '))}. ${msg.content}`);
|
|
107
|
+
});
|
|
108
|
+
console.log(chalk.gray('━'.repeat(50)));
|
|
109
|
+
console.log(chalk.gray(`配置: 保留最近 ${config.chatHistoryLimit} 轮对话`));
|
|
110
|
+
console.log(chalk.gray(`文件: ${CHAT_HISTORY_FILE}`));
|
|
111
|
+
console.log('');
|
|
112
|
+
}
|
|
@@ -2,14 +2,15 @@ import React, { useState, useEffect } from 'react';
|
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import Spinner from 'ink-spinner';
|
|
4
4
|
import { MarkdownDisplay } from './MarkdownDisplay.js';
|
|
5
|
-
import {
|
|
5
|
+
import { chatWithMastra } from '../mastra-chat.js';
|
|
6
6
|
import { getChatRoundCount } from '../chat-history.js';
|
|
7
|
-
import {
|
|
7
|
+
import { getCurrentTheme } from '../ui/theme.js';
|
|
8
8
|
/**
|
|
9
9
|
* Chat 组件 - AI 对话模式
|
|
10
10
|
* 使用正常渲染,完成后保持最后一帧在终端
|
|
11
11
|
*/
|
|
12
12
|
export function Chat({ prompt, debug, showRoundCount, onComplete }) {
|
|
13
|
+
const theme = getCurrentTheme();
|
|
13
14
|
const [status, setStatus] = useState('thinking');
|
|
14
15
|
const [content, setContent] = useState('');
|
|
15
16
|
const [duration, setDuration] = useState(0);
|
|
@@ -23,12 +24,12 @@ export function Chat({ prompt, debug, showRoundCount, onComplete }) {
|
|
|
23
24
|
setContent((prev) => prev + chunk);
|
|
24
25
|
};
|
|
25
26
|
// 调用 AI
|
|
26
|
-
|
|
27
|
+
chatWithMastra(prompt, { debug: debug || false, onChunk })
|
|
27
28
|
.then((result) => {
|
|
28
29
|
const endTime = Date.now();
|
|
29
30
|
setDuration(endTime - startTime);
|
|
30
31
|
setStatus('done');
|
|
31
|
-
if (debug && typeof result === 'object' && 'debug' in result) {
|
|
32
|
+
if (debug && typeof result === 'object' && 'debug' in result && result.debug) {
|
|
32
33
|
setDebugInfo(result.debug);
|
|
33
34
|
}
|
|
34
35
|
setTimeout(onComplete, 100);
|
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Text, Box } from 'ink';
|
|
3
3
|
import { common, createLowlight } from 'lowlight';
|
|
4
|
-
import {
|
|
4
|
+
import { getCurrentTheme } from '../ui/theme.js';
|
|
5
5
|
// 创建 lowlight 实例
|
|
6
6
|
const lowlight = createLowlight(common);
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
/**
|
|
8
|
+
* 获取语法高亮颜色映射
|
|
9
|
+
*/
|
|
10
|
+
function getSyntaxColors(theme) {
|
|
11
|
+
return {
|
|
12
|
+
'hljs-keyword': theme.code.keyword,
|
|
13
|
+
'hljs-string': theme.code.string,
|
|
14
|
+
'hljs-function': theme.code.function,
|
|
15
|
+
'hljs-comment': theme.code.comment,
|
|
16
|
+
'hljs-number': theme.primary,
|
|
17
|
+
'hljs-built_in': theme.secondary,
|
|
18
|
+
'hljs-title': theme.accent,
|
|
19
|
+
'hljs-variable': theme.text.primary,
|
|
20
|
+
'hljs-type': theme.info,
|
|
21
|
+
'hljs-operator': theme.text.secondary,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
20
24
|
/**
|
|
21
25
|
* 渲染 HAST 语法树节点
|
|
22
26
|
*/
|
|
23
|
-
function renderHastNode(node, inheritedColor) {
|
|
27
|
+
function renderHastNode(node, inheritedColor, syntaxColors, theme) {
|
|
24
28
|
if (node.type === 'text') {
|
|
25
29
|
const color = inheritedColor || theme.code.text;
|
|
26
30
|
return React.createElement(Text, { color: color }, node.value);
|
|
@@ -38,26 +42,26 @@ function renderHastNode(node, inheritedColor) {
|
|
|
38
42
|
}
|
|
39
43
|
const colorToPassDown = elementColor || inheritedColor;
|
|
40
44
|
// 递归渲染子节点
|
|
41
|
-
const children = node.children?.map((child, index) => (React.createElement(React.Fragment, { key: index }, renderHastNode(child, colorToPassDown))));
|
|
45
|
+
const children = node.children?.map((child, index) => (React.createElement(React.Fragment, { key: index }, renderHastNode(child, colorToPassDown, syntaxColors, theme))));
|
|
42
46
|
return React.createElement(React.Fragment, null, children);
|
|
43
47
|
}
|
|
44
48
|
if (node.type === 'root') {
|
|
45
49
|
if (!node.children || node.children.length === 0) {
|
|
46
50
|
return null;
|
|
47
51
|
}
|
|
48
|
-
return node.children?.map((child, index) => (React.createElement(React.Fragment, { key: index }, renderHastNode(child, inheritedColor))));
|
|
52
|
+
return node.children?.map((child, index) => (React.createElement(React.Fragment, { key: index }, renderHastNode(child, inheritedColor, syntaxColors, theme))));
|
|
49
53
|
}
|
|
50
54
|
return null;
|
|
51
55
|
}
|
|
52
56
|
/**
|
|
53
57
|
* 高亮并渲染一行代码
|
|
54
58
|
*/
|
|
55
|
-
function highlightLine(line, language) {
|
|
59
|
+
function highlightLine(line, language, syntaxColors, theme) {
|
|
56
60
|
try {
|
|
57
61
|
const highlighted = !language || !lowlight.registered(language)
|
|
58
62
|
? lowlight.highlightAuto(line)
|
|
59
63
|
: lowlight.highlight(language, line);
|
|
60
|
-
const rendered = renderHastNode(highlighted, undefined);
|
|
64
|
+
const rendered = renderHastNode(highlighted, undefined, syntaxColors, theme);
|
|
61
65
|
return rendered !== null ? rendered : line;
|
|
62
66
|
}
|
|
63
67
|
catch {
|
|
@@ -68,11 +72,13 @@ function highlightLine(line, language) {
|
|
|
68
72
|
* 代码高亮组件
|
|
69
73
|
*/
|
|
70
74
|
function ColorizeCodeInternal({ code, language = null, showLineNumbers = false }) {
|
|
75
|
+
const theme = getCurrentTheme();
|
|
76
|
+
const syntaxColors = getSyntaxColors(theme);
|
|
71
77
|
const codeToHighlight = code.replace(/\n$/, '');
|
|
72
78
|
const lines = codeToHighlight.split('\n');
|
|
73
79
|
const padWidth = String(lines.length).length;
|
|
74
80
|
const renderedLines = lines.map((line, index) => {
|
|
75
|
-
const contentToRender = highlightLine(line, language);
|
|
81
|
+
const contentToRender = highlightLine(line, language, syntaxColors, theme);
|
|
76
82
|
return (React.createElement(Box, { key: index, minHeight: 1 },
|
|
77
83
|
showLineNumbers && (React.createElement(Text, { color: theme.text.dim }, `${String(index + 1).padStart(padWidth, ' ')} `)),
|
|
78
84
|
React.createElement(Text, { color: theme.code.text }, contentToRender)));
|
|
@@ -1,26 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
* 计算字符串的显示宽度(中文占2个宽度)
|
|
6
|
-
*/
|
|
7
|
-
function getDisplayWidth(str) {
|
|
8
|
-
let width = 0;
|
|
9
|
-
for (const char of str) {
|
|
10
|
-
// 中文、日文、韩文等宽字符占 2 个宽度
|
|
11
|
-
if (char.match(/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef\u3000-\u303f]/)) {
|
|
12
|
-
width += 2;
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
width += 1;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return width;
|
|
19
|
-
}
|
|
3
|
+
import { getCurrentTheme } from '../ui/theme.js';
|
|
4
|
+
import { getDisplayWidth } from '../utils/console.js';
|
|
20
5
|
/**
|
|
21
6
|
* CommandBox 组件 - 显示带边框和标题的命令框
|
|
22
7
|
*/
|
|
23
8
|
export const CommandBox = ({ command, title = '生成命令' }) => {
|
|
9
|
+
const theme = getCurrentTheme();
|
|
24
10
|
const lines = command.split('\n');
|
|
25
11
|
const titleWidth = getDisplayWidth(title);
|
|
26
12
|
const maxContentWidth = Math.max(...lines.map(l => getDisplayWidth(l)));
|
|
@@ -3,10 +3,11 @@ interface ConfirmationPromptProps {
|
|
|
3
3
|
prompt: string;
|
|
4
4
|
onConfirm: () => void;
|
|
5
5
|
onCancel: () => void;
|
|
6
|
+
onEdit?: () => void;
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* ConfirmationPrompt 组件 - 单键确认提示
|
|
9
|
-
* 回车 = 确认,Esc = 取消,Ctrl+C = 退出
|
|
10
|
+
* 回车 = 确认,E = 编辑,Esc = 取消,Ctrl+C = 退出
|
|
10
11
|
*/
|
|
11
12
|
export declare const ConfirmationPrompt: React.FC<ConfirmationPromptProps>;
|
|
12
13
|
export {};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Text, useInput } from 'ink';
|
|
3
|
-
import {
|
|
3
|
+
import { getCurrentTheme } from '../ui/theme.js';
|
|
4
4
|
/**
|
|
5
5
|
* ConfirmationPrompt 组件 - 单键确认提示
|
|
6
|
-
* 回车 = 确认,Esc = 取消,Ctrl+C = 退出
|
|
6
|
+
* 回车 = 确认,E = 编辑,Esc = 取消,Ctrl+C = 退出
|
|
7
7
|
*/
|
|
8
|
-
export const ConfirmationPrompt = ({ prompt, onConfirm, onCancel, }) => {
|
|
8
|
+
export const ConfirmationPrompt = ({ prompt, onConfirm, onCancel, onEdit, }) => {
|
|
9
|
+
const theme = getCurrentTheme();
|
|
9
10
|
useInput((input, key) => {
|
|
10
11
|
if (key.return) {
|
|
11
12
|
// 回车键
|
|
@@ -15,6 +16,10 @@ export const ConfirmationPrompt = ({ prompt, onConfirm, onCancel, }) => {
|
|
|
15
16
|
// Esc 键
|
|
16
17
|
onCancel();
|
|
17
18
|
}
|
|
19
|
+
else if ((input === 'e' || input === 'E') && onEdit) {
|
|
20
|
+
// E 键进入编辑模式
|
|
21
|
+
onEdit();
|
|
22
|
+
}
|
|
18
23
|
else if (key.ctrl && input === 'c') {
|
|
19
24
|
// Ctrl+C
|
|
20
25
|
process.exit(0);
|
|
@@ -22,5 +27,5 @@ export const ConfirmationPrompt = ({ prompt, onConfirm, onCancel, }) => {
|
|
|
22
27
|
});
|
|
23
28
|
return (React.createElement(Text, null,
|
|
24
29
|
React.createElement(Text, { bold: true, color: theme.warning }, prompt),
|
|
25
|
-
React.createElement(Text, { color: theme.text.secondary },
|
|
30
|
+
React.createElement(Text, { color: theme.text.secondary }, onEdit ? ' [回车执行 / E 编辑 / Esc 取消] ' : ' [回车执行 / Esc 取消] ')));
|
|
26
31
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Text } from 'ink';
|
|
3
|
-
import {
|
|
3
|
+
import { getCurrentTheme } from '../ui/theme.js';
|
|
4
4
|
/**
|
|
5
5
|
* 格式化耗时
|
|
6
6
|
*/
|
|
@@ -14,6 +14,7 @@ function formatDuration(ms) {
|
|
|
14
14
|
* Duration 组件 - 显示耗时
|
|
15
15
|
*/
|
|
16
16
|
export const Duration = ({ ms }) => {
|
|
17
|
+
const theme = getCurrentTheme();
|
|
17
18
|
return React.createElement(Text, { color: theme.text.secondary },
|
|
18
19
|
"(",
|
|
19
20
|
formatDuration(ms),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Text } from 'ink';
|
|
3
|
-
import {
|
|
3
|
+
import { getCurrentTheme } from '../ui/theme.js';
|
|
4
4
|
/**
|
|
5
5
|
* 行内 Markdown 渲染器
|
|
6
6
|
* 处理 **粗体**、*斜体*、`代码`、~~删除线~~、<u>下划线</u>、链接
|
|
7
7
|
*/
|
|
8
8
|
function RenderInlineInternal({ text, defaultColor }) {
|
|
9
|
+
const theme = getCurrentTheme();
|
|
9
10
|
const baseColor = defaultColor || theme.text.primary;
|
|
10
11
|
// 快速路径:纯文本无 markdown
|
|
11
12
|
if (!/[*_~`<[https?:]/.test(text)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Text, Box } from 'ink';
|
|
3
|
-
import {
|
|
3
|
+
import { getCurrentTheme } from '../ui/theme.js';
|
|
4
4
|
import { ColorizeCode } from './CodeColorizer.js';
|
|
5
5
|
import { TableRenderer } from './TableRenderer.js';
|
|
6
6
|
import { RenderInline } from './InlineRenderer.js';
|
|
@@ -12,6 +12,7 @@ import { RenderInline } from './InlineRenderer.js';
|
|
|
12
12
|
function MarkdownDisplayInternal({ text, terminalWidth = 80 }) {
|
|
13
13
|
if (!text)
|
|
14
14
|
return React.createElement(React.Fragment, null);
|
|
15
|
+
const theme = getCurrentTheme();
|
|
15
16
|
const lines = text.split(/\r?\n/);
|
|
16
17
|
// 正则表达式
|
|
17
18
|
const headerRegex = /^ *(#{1,4}) +(.*)/;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { type ExecutedStep } from '../multi-step.js';
|
|
2
|
+
import { type ExecutedStep, type RemoteContext } from '../multi-step.js';
|
|
3
3
|
interface MultiStepCommandGeneratorProps {
|
|
4
4
|
prompt: string;
|
|
5
5
|
debug?: boolean;
|
|
6
6
|
onStepComplete: (step: {
|
|
7
7
|
command: string;
|
|
8
|
+
aiGeneratedCommand?: string;
|
|
9
|
+
userModified?: boolean;
|
|
8
10
|
confirmed: boolean;
|
|
9
11
|
cancelled?: boolean;
|
|
10
12
|
hasBuiltin?: boolean;
|
|
@@ -16,6 +18,8 @@ interface MultiStepCommandGeneratorProps {
|
|
|
16
18
|
}) => void;
|
|
17
19
|
previousSteps?: ExecutedStep[];
|
|
18
20
|
currentStepNumber?: number;
|
|
21
|
+
remoteContext?: RemoteContext;
|
|
22
|
+
isRemote?: boolean;
|
|
19
23
|
}
|
|
20
24
|
/**
|
|
21
25
|
* MultiStepCommandGenerator 组件 - 多步骤命令生成
|