UNPKG

openai-cli-unofficial

Version:

A powerful OpenAI CLI Coding Agent built with TypeScript

388 lines 14.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HistoryEditor = void 0; const chalk_1 = __importDefault(require("chalk")); class HistoryEditor { constructor(messages, historyMessages) { this.currentIndex = 0; this.hasChanges = false; this.isRunning = false; this.isFirstRender = true; this.messages = messages; this.originalMessages = [...historyMessages]; this.workingMessages = [...historyMessages]; } /** * 启动历史记录编辑器 */ async start() { if (this.workingMessages.length === 0) { console.log(chalk_1.default.yellow(this.messages.main.historyManagement.editor.noHistoryToEdit)); return { saved: false, messages: this.originalMessages, deletedCount: 0 }; } // 只保留 user 类型的消息作为可选择的项目 this.filterUserMessages(); if (this.workingMessages.length === 0) { console.log(chalk_1.default.yellow(this.messages.main.historyManagement.editor.noHistoryToEdit)); return { saved: false, messages: this.originalMessages, deletedCount: 0 }; } this.isRunning = true; this.showHeader(); this.renderMenu(); return new Promise((resolve) => { this.setupKeyListener(resolve); }); } /** * 筛选出用户消息,并记录它们在原始数组中的位置 */ filterUserMessages() { const userMessages = []; for (let i = 0; i < this.workingMessages.length; i++) { if (this.workingMessages[i].type === 'user') { userMessages.push({ ...this.workingMessages[i], originalIndex: i // 记录在原始数组中的位置 }); } } this.workingMessages = userMessages; } /** * 显示编辑器头部信息 */ showHeader() { console.clear(); console.log(chalk_1.default.bold.cyan('=== ' + this.messages.main.historyManagement.editor.title + ' ===')); console.log(chalk_1.default.gray(this.messages.main.historyManagement.editor.instructions)); console.log(chalk_1.default.gray('─'.repeat(80))); console.log(); this.showKeyHelp(); console.log(); } /** * 显示按键帮助 */ showKeyHelp() { const help = this.messages.main.historyManagement.editor.keyHelp; console.log(chalk_1.default.cyan(help.navigation)); console.log(chalk_1.default.cyan(help.delete)); console.log(chalk_1.default.cyan(help.save)); console.log(chalk_1.default.cyan(help.exit)); } /** * 渲染菜单项列表 */ renderMenuItems() { const menuLines = []; for (let i = 0; i < this.workingMessages.length; i++) { const message = this.workingMessages[i]; const isSelected = i === this.currentIndex; const timeStr = message.timestamp.toLocaleTimeString(this.messages.main.messages.format.timeLocale, { hour: '2-digit', minute: '2-digit', second: '2-digit' }); // 选择指示器 const indicator = isSelected ? chalk_1.default.cyan('●') : chalk_1.default.gray('○'); // 消息类型标签 const typeLabel = chalk_1.default.bgBlue.white.bold(` ${this.messages.main.historyManagement.editor.userMessage} `); // 时间戳 const timestamp = chalk_1.default.blue(` ${timeStr} `); // 消息内容(截断长内容) const content = message.content.length > 60 ? message.content.substring(0, 60) + '...' : message.content; const contentDisplay = isSelected ? chalk_1.default.white.bold(content) : chalk_1.default.gray(content); menuLines.push(` ${indicator} ${typeLabel}${timestamp}${contentDisplay}`); } return menuLines; } /** * 渲染完整菜单 */ renderMenu() { if (!this.isFirstRender) { // 隐藏光标,减少闪烁 process.stdout.write('\x1B[?25l'); // 计算需要移动的行数(菜单项数量 + 可能的状态行) const linesToMove = this.workingMessages.length + (this.hasChanges ? 2 : 1); // 移动到菜单开始位置 process.stdout.write(`\x1B[${linesToMove}A`); // 清除从当前位置到屏幕末尾的内容 process.stdout.write('\x1B[0J'); } // 批量渲染所有菜单项 const menuLines = this.renderMenuItems(); process.stdout.write(menuLines.join('\n') + '\n'); // 添加空行 console.log(); // 显示状态信息 if (this.hasChanges) { const unsavedText = this.messages.main.historyManagement.editor.keyHelp.save.includes('保存') ? '有未保存的修改' : 'Unsaved changes'; console.log(chalk_1.default.yellow('* ' + unsavedText)); } if (!this.isFirstRender) { // 显示光标 process.stdout.write('\x1B[?25h'); } this.isFirstRender = false; } /** * 设置键盘监听器 */ setupKeyListener(resolve) { // 隐藏光标以减少闪烁 process.stdout.write('\x1B[?25l'); if (process.stdin.isTTY) { process.stdin.setRawMode(true); } process.stdin.resume(); process.stdin.setEncoding('utf8'); const cleanup = () => { process.stdin.removeAllListeners('data'); process.stdin.removeAllListeners('error'); process.stdin.removeAllListeners('end'); if (process.stdin.isTTY) { try { process.stdin.setRawMode(false); } catch (error) { // 忽略错误 } } process.stdin.pause(); // 恢复光标显示 process.stdout.write('\x1B[?25h'); }; const onKeyPress = async (key) => { if (!this.isRunning) return; const keyCode = key.charCodeAt(0); // 处理特殊按键 if (key === '\x1B[A') { // 上箭头 this.moveUp(); } else if (key === '\x1B[B') { // 下箭头 this.moveDown(); } else if (key === '\x7f' || key.toLowerCase() === 'd') { // Delete 或 d await this.deleteCurrentMessage(resolve); } else if (key.toLowerCase() === 's') { // s - 保存 await this.saveChanges(resolve); } else if (key.toLowerCase() === 'q' || key === '\x1b') { // q 或 Esc - 退出 await this.exitEditor(resolve); } else if (keyCode === 3) { // Ctrl+C await this.exitEditor(resolve); } }; process.stdin.on('data', onKeyPress); process.stdin.on('error', cleanup); process.stdin.on('end', cleanup); } /** * 向上移动光标 */ moveUp() { if (this.currentIndex > 0) { this.currentIndex--; } else { this.currentIndex = this.workingMessages.length - 1; // 循环到末尾 } this.renderMenu(); } /** * 向下移动光标 */ moveDown() { if (this.currentIndex < this.workingMessages.length - 1) { this.currentIndex++; } else { this.currentIndex = 0; // 循环到开头 } this.renderMenu(); } /** * 删除当前选中的消息 */ async deleteCurrentMessage(resolve) { if (this.workingMessages.length === 0) return; const confirmed = await this.confirmDelete(); if (!confirmed) { this.fullRerender(); return; } // 删除选中的用户消息 this.workingMessages.splice(this.currentIndex, 1); this.hasChanges = true; // 调整光标位置 if (this.currentIndex >= this.workingMessages.length && this.workingMessages.length > 0) { this.currentIndex = this.workingMessages.length - 1; } // 如果没有消息了,自动保存并退出 if (this.workingMessages.length === 0) { await this.saveChanges(resolve); return; } this.fullRerender(); } /** * 完全重新渲染 */ fullRerender() { this.showHeader(); this.isFirstRender = true; this.renderMenu(); } /** * 确认删除操作 */ async confirmDelete() { return new Promise((resolve) => { console.log(); console.log(chalk_1.default.yellow(this.messages.main.historyManagement.editor.deleteConfirm)); console.log(chalk_1.default.gray(this.messages.main.historyManagement.editor.deleteConfirmOptions)); if (process.stdin.isTTY) { process.stdin.setRawMode(true); } const onConfirmKey = (key) => { const keyLower = key.toLowerCase(); if (keyLower === 'y' || key === '是') { process.stdin.removeListener('data', onConfirmKey); resolve(true); } else if (keyLower === 'n' || key === '否') { process.stdin.removeListener('data', onConfirmKey); resolve(false); } // 忽略其他按键 }; process.stdin.on('data', onConfirmKey); }); } /** * 保存修改 */ async saveChanges(resolve) { this.isRunning = false; // 构建最终的消息列表,包括删除用户消息后相应的AI消息 const finalMessages = this.buildFinalMessages(); const deletedCount = this.originalMessages.length - finalMessages.length; this.cleanup(); console.clear(); console.log(chalk_1.default.green(this.messages.main.historyManagement.editor.saveSuccess)); if (deletedCount > 0) { const deletedMsg = this.messages.main.historyManagement.editor.deletedCount .replace('{count}', deletedCount.toString()); console.log(chalk_1.default.gray(deletedMsg)); } resolve({ saved: true, messages: finalMessages, deletedCount }); } /** * 构建最终的消息列表 */ buildFinalMessages() { const finalMessages = []; const userMessages = this.workingMessages; // 重新构建完整的消息列表,包括相应的AI回复 for (const userMsg of userMessages) { const originalIndex = userMsg.originalIndex; // 添加用户消息 finalMessages.push({ type: userMsg.type, content: userMsg.content, timestamp: userMsg.timestamp }); // 查找并添加紧随其后的AI消息 if (originalIndex + 1 < this.originalMessages.length) { const nextMsg = this.originalMessages[originalIndex + 1]; if (nextMsg.type === 'ai') { finalMessages.push(nextMsg); } } } return finalMessages; } /** * 退出编辑器 */ async exitEditor(resolve) { if (this.hasChanges) { const shouldSave = await this.confirmSave(); if (shouldSave === 'save') { await this.saveChanges(resolve); return; } else if (shouldSave === 'cancel') { this.fullRerender(); return; } // 否则继续退出,不保存 } this.isRunning = false; this.cleanup(); console.clear(); console.log(chalk_1.default.gray(this.messages.main.historyManagement.editor.saveCancel)); resolve({ saved: false, messages: this.originalMessages, deletedCount: 0 }); } /** * 确认是否保存修改 */ async confirmSave() { return new Promise((resolve) => { console.log(); console.log(chalk_1.default.yellow(this.messages.main.historyManagement.editor.exitWithoutSave)); console.log(chalk_1.default.gray(this.messages.main.historyManagement.editor.exitWithoutSaveOptions)); if (process.stdin.isTTY) { process.stdin.setRawMode(true); } const onConfirmKey = (key) => { const keyLower = key.toLowerCase(); if (keyLower === 'y' || key === '是') { process.stdin.removeListener('data', onConfirmKey); resolve('exit'); } else if (keyLower === 'n' || key === '否') { process.stdin.removeListener('data', onConfirmKey); resolve('cancel'); } // 忽略其他按键 }; process.stdin.on('data', onConfirmKey); }); } /** * 清理资源 */ cleanup() { process.stdin.removeAllListeners('data'); process.stdin.removeAllListeners('error'); process.stdin.removeAllListeners('end'); if (process.stdin.isTTY) { try { process.stdin.setRawMode(false); } catch (error) { // 忽略错误 } } process.stdin.pause(); // 恢复光标显示 process.stdout.write('\x1B[?25h'); } } exports.HistoryEditor = HistoryEditor; //# sourceMappingURL=history-editor.js.map