2026-01-11 18:27:51 +08:00
|
|
|
// main.js
|
2026-01-11 17:50:27 +08:00
|
|
|
import { createPanel } from './scripts/panel.js';
|
|
|
|
|
import { handleCommand, COMMANDS } from './scripts/commands.js';
|
|
|
|
|
import { initVoice } from './scripts/voice.js';
|
|
|
|
|
import { translateToCommand } from './scripts/ai.js';
|
2026-01-02 21:06:54 +08:00
|
|
|
|
2026-01-11 12:22:49 +08:00
|
|
|
const init = async () => {
|
|
|
|
|
const ui = createPanel();
|
2026-01-11 18:27:51 +08:00
|
|
|
let spaceTimer = null;
|
|
|
|
|
let isRecording = false;
|
2026-01-02 21:06:54 +08:00
|
|
|
|
2026-01-15 22:49:48 +08:00
|
|
|
// 统一定义 UI 更新引用,方便 handleCommand 调用
|
|
|
|
|
const uiRefs = {
|
|
|
|
|
updateStatus: (text) => {
|
|
|
|
|
const statusText = document.getElementById("statusText");
|
|
|
|
|
if (statusText) {
|
|
|
|
|
statusText.innerText = text;
|
|
|
|
|
statusText.style.color = "#409eff"; // 使用蓝色区分对话与就绪状态
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-17 20:24:08 +08:00
|
|
|
/**
|
|
|
|
|
* 处理 AI 流程的主函数
|
|
|
|
|
*/
|
2026-01-11 12:22:49 +08:00
|
|
|
async function startProcess(text) {
|
2026-01-11 18:27:51 +08:00
|
|
|
if (!text) return;
|
2026-01-11 12:22:49 +08:00
|
|
|
ui.setLoading(true);
|
|
|
|
|
try {
|
2026-01-17 20:24:08 +08:00
|
|
|
// 获取 AI 响应结果(包含内容和语音开关状态)
|
|
|
|
|
const aiResponse = await translateToCommand(text);
|
|
|
|
|
|
|
|
|
|
if (aiResponse && aiResponse.content) {
|
|
|
|
|
// 将内容、UI 引用以及语音开关状态传给指令处理器
|
|
|
|
|
handleCommand(aiResponse.content, uiRefs, aiResponse.voiceEnabled);
|
|
|
|
|
} else {
|
|
|
|
|
uiRefs.updateStatus("未识别到有效指令");
|
2026-01-11 12:22:49 +08:00
|
|
|
}
|
2026-01-15 22:49:48 +08:00
|
|
|
} catch (err) {
|
|
|
|
|
console.error("处理流程错误:", err);
|
|
|
|
|
uiRefs.updateStatus("处理指令时出错");
|
2026-01-11 12:22:49 +08:00
|
|
|
} finally {
|
|
|
|
|
ui.setLoading(false);
|
2026-01-02 21:06:54 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 20:24:08 +08:00
|
|
|
// 初始化语音识别模块
|
|
|
|
|
const voiceCtrl = initVoice(ui, (text) => {
|
|
|
|
|
ui.setRecording(false);
|
|
|
|
|
isRecording = false;
|
2026-01-11 18:27:51 +08:00
|
|
|
ui.input.value = text;
|
|
|
|
|
startProcess(text);
|
|
|
|
|
});
|
|
|
|
|
|
2026-01-17 20:24:08 +08:00
|
|
|
// 输入框回车触发
|
|
|
|
|
ui.input.addEventListener("keydown", (e) => {
|
|
|
|
|
if (e.key === "Enter") {
|
|
|
|
|
const text = ui.input.value.trim();
|
|
|
|
|
ui.input.value = "";
|
|
|
|
|
startProcess(text);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 按钮点击触发录音
|
2026-01-11 18:27:51 +08:00
|
|
|
ui.btn.onclick = () => {
|
|
|
|
|
if (!isRecording) {
|
|
|
|
|
voiceCtrl.start();
|
|
|
|
|
ui.setRecording(true);
|
|
|
|
|
isRecording = true;
|
2026-01-17 20:24:08 +08:00
|
|
|
// 4秒自动停止录音保护
|
2026-01-11 18:27:51 +08:00
|
|
|
setTimeout(() => {
|
|
|
|
|
if (isRecording) {
|
|
|
|
|
voiceCtrl.stop();
|
|
|
|
|
ui.setRecording(false);
|
|
|
|
|
isRecording = false;
|
|
|
|
|
}
|
2026-01-17 20:24:08 +08:00
|
|
|
}, 4000);
|
|
|
|
|
} else {
|
|
|
|
|
voiceCtrl.stop();
|
|
|
|
|
ui.setRecording(false);
|
|
|
|
|
isRecording = false;
|
2026-01-11 18:27:51 +08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-17 20:24:08 +08:00
|
|
|
// 空格长按触发录音逻辑
|
2026-01-11 18:27:51 +08:00
|
|
|
window.addEventListener("keydown", (e) => {
|
|
|
|
|
if (e.code === "Space" && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
|
|
|
|
|
if (spaceTimer || isRecording) return;
|
|
|
|
|
|
|
|
|
|
spaceTimer = setTimeout(() => {
|
|
|
|
|
if (voiceCtrl.supportSpeech) {
|
|
|
|
|
voiceCtrl.start();
|
|
|
|
|
ui.setRecording(true);
|
|
|
|
|
isRecording = true;
|
|
|
|
|
}
|
2026-01-17 20:24:08 +08:00
|
|
|
}, 500); // 判定为长按
|
2026-01-11 18:27:51 +08:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
window.addEventListener("keyup", (e) => {
|
|
|
|
|
if (e.code === "Space") {
|
|
|
|
|
if (spaceTimer) {
|
|
|
|
|
clearTimeout(spaceTimer);
|
|
|
|
|
spaceTimer = null;
|
|
|
|
|
}
|
|
|
|
|
if (isRecording) {
|
2026-01-17 20:24:08 +08:00
|
|
|
// 延迟停止以捕捉最后一段语音
|
2026-01-11 18:27:51 +08:00
|
|
|
setTimeout(() => {
|
|
|
|
|
voiceCtrl.stop();
|
|
|
|
|
ui.setRecording(false);
|
|
|
|
|
isRecording = false;
|
2026-01-17 20:24:08 +08:00
|
|
|
}, 300);
|
2026-01-11 18:27:51 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-01-11 12:22:49 +08:00
|
|
|
};
|
|
|
|
|
|
2026-01-17 20:24:08 +08:00
|
|
|
// 启动初始化
|
|
|
|
|
if (document.readyState === "complete" || document.readyState === "interactive") {
|
|
|
|
|
init();
|
|
|
|
|
} else {
|
|
|
|
|
window.addEventListener("DOMContentLoaded", init);
|
|
|
|
|
}
|