@whyuds/ask-user
Version:
A cross-platform command-line tool and MCP server to show dialog windows and get user input for AI code editors
253 lines (231 loc) • 6.52 kB
JavaScript
import { app, BrowserWindow, ipcMain } from 'electron';
import path from 'path';
let mainWindow = null;
let userInputPromise = null;
// 从命令行参数获取标题和消息
const args = process.argv.slice(2);
let title = '';
let message = '';
for (let i = 0; i < args.length; i++) {
if (args[i] === '--title' && i + 1 < args.length) {
title = args[i + 1];
i++;
} else if (args[i] === '--message' && i + 1 < args.length) {
message = args[i + 1];
i++;
}
}
// 确保在无头模式下也能运行 Electron
if (process.platform === 'linux') {
app.disableHardwareAcceleration();
}
// 防止应用在所有窗口关闭时退出
app.on('window-all-closed', () => {
app.quit();
});
// 防止多个实例
app.on('second-instance', () => {
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
}
});
function createWindow() {
mainWindow = new BrowserWindow({
width: 500,
height: 400,
resizable: true,
alwaysOnTop: true,
center: true,
frame: false,
show: false, // 先不显示,等内容加载完再显示
title: title || 'Ask User',
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
const htmlContent = generateHTML(title, message);
mainWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`);
// 内容加载完成后立即显示并聚焦窗口
mainWindow.once('ready-to-show', () => {
mainWindow.show();
mainWindow.focus();
mainWindow.setAlwaysOnTop(true, 'screen-saver');
});
// 添加超时机制,确保窗口一定会显示
setTimeout(() => {
if (mainWindow && !mainWindow.isVisible()) {
mainWindow.show();
mainWindow.focus();
mainWindow.setAlwaysOnTop(true, 'screen-saver');
}
}, 1000); // 1秒后强制显示
mainWindow.on('closed', () => {
mainWindow = null;
console.log(''); // 输出空字符串表示取消
app.quit();
});
}
function generateHTML(title, message) {
return `
<html>
<head>
<meta charset="UTF-8">
<title>${title || 'Ask User'}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 0;
background: #f5f5f5;
display: flex;
flex-direction: column;
height: 100vh;
}
.title-bar {
background: #007acc;
color: white;
padding: 8px 15px;
display: flex;
justify-content: space-between;
align-items: center;
-webkit-app-region: drag;
user-select: none;
}
.title-bar-text {
font-size: 14px;
font-weight: 500;
}
.close-btn {
background: none;
border: none;
color: white;
font-size: 16px;
cursor: pointer;
padding: 4px 8px;
border-radius: 3px;
-webkit-app-region: no-drag;
}
.close-btn:hover {
background: rgba(255,255,255,0.2);
}
.container {
background: white;
padding: 20px;
flex: 1;
display: flex;
flex-direction: column;
margin: 0;
}
.message {
font-size: 14px;
margin-bottom: 20px;
color: #666;
flex: 1;
}
.input-area {
display: flex;
flex-direction: column;
gap: 10px;
}
textarea {
padding: 10px;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 14px;
outline: none;
transition: border-color 0.2s;
resize: vertical;
min-height: 80px;
font-family: inherit;
}
textarea:focus {
border-color: #007acc;
}
.buttons {
display: flex;
gap: 10px;
justify-content: flex-end;
}
button {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
}
.btn-primary {
background: #007acc;
color: white;
}
.btn-primary:hover {
background: #005a9e;
}
.btn-secondary {
background: #ddd;
color: #333;
}
.btn-secondary:hover {
background: #ccc;
}
</style>
</head>
<body>
<div class="title-bar">
<div class="title-bar-text">${title || 'Ask User'}</div>
<button class="close-btn" onclick="cancel()">×</button>
</div>
<div class="container">
<div class="message">${message || 'Enter your response below:'}</div>
<div class="input-area">
<textarea id="userInput" placeholder="Type your answer here..." autofocus></textarea>
<div class="buttons">
<button class="btn-secondary" onclick="cancel()">Cancel</button>
<button class="btn-primary" onclick="submit()">Submit</button>
</div>
</div>
</div>
<script>
const { ipcRenderer } = window.require('electron');
function submit() {
const input = document.getElementById('userInput').value;
ipcRenderer.send('user-input', input);
}
function cancel() {
ipcRenderer.send('user-input', '');
}
// 监听 Ctrl+Enter 提交
document.getElementById('userInput').addEventListener('keydown', function(e) {
if (e.key === 'Enter' && e.ctrlKey) {
submit();
}
});
// 监听 ESC 键
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
cancel();
}
});
</script>
</body>
</html>
`;
}
// 监听用户输入
ipcMain.on('user-input', (event, input) => {
console.log(input);
app.quit();
});
// 当应用准备就绪时创建窗口
app.whenReady().then(() => {
// 确保应用能够获得焦点
if (process.platform === 'darwin') {
app.dock.show();
}
createWindow();
// 确保窗口能够显示在最前面
app.focus();
});