Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -306,17 +306,24 @@ export default function App() {
if (optimizedResult.success && optimizedResult.enhanced_by_ai && optimizedResult.text) {
// 显示AI优化后的文本
setProcessedText(optimizedResult.text);

// 自动粘贴AI优化后的文本
console.log("📋 准备粘贴AI优化后的文本:", optimizedResult.text);
await safePaste(optimizedResult.text);
console.log("✅ AI优化文本粘贴完成");

toast.success("🤖 AI文本优化完成并已自动粘贴!");
console.log('AI优化文本已设置:', optimizedResult.text);
} else if (optimizedResult.ai_no_change) {
// AI调用成功,但认为文本无需修改
console.log("📋 AI认为文本无需修改,粘贴原始文本:", originalText);
if (originalText) {
await safePaste(originalText);
}
toast.info("✅ 识别文本已是最优,无需修改");
} else {
console.warn('AI优化结果无效,使用原始文本:', optimizedResult);
// 如果AI优化失败,则粘贴原始文本
// AI调用失败,粘贴原始文本
if (originalText) {
console.log("📋 AI优化失败,粘贴原始文本:", originalText);
await safePaste(originalText);
Expand Down Expand Up @@ -456,8 +463,13 @@ export default function App() {

const initializeHotkey = async () => {
try {
// 注册默认热键 CommandOrControl+Shift+Space
const success = await registerHotkey('CommandOrControl+Shift+Space');
// 从持久化设置读取热键,默认为 CommandOrControl+Shift+Space
let savedHotkey = 'CommandOrControl+Shift+Space';
if (window.electronAPI) {
const stored = await window.electronAPI.getSetting('hotkey', 'CommandOrControl+Shift+Space');
if (stored) savedHotkey = stored;
}
const success = await registerHotkey(savedHotkey);
if (success) {
console.log('主窗口热键注册成功');
} else {
Expand Down Expand Up @@ -750,7 +762,10 @@ export default function App() {

{/* 设置面板 */}
{showSettings && (
<SettingsPanel onClose={() => setShowSettings(false)} />
<SettingsPanel
onClose={() => setShowSettings(false)}
onHotkeyChange={(newHotkey) => registerHotkey(newHotkey)}
/>
)}

</div>
Expand Down
169 changes: 166 additions & 3 deletions src/components/SettingsPanel.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useState } from "react";
import { Mic, Shield, Settings } from "lucide-react";
import React, { useState, useEffect, useRef } from "react";
import { Mic, Shield, Settings, Keyboard } from "lucide-react";
import { usePermissions } from "../hooks/usePermissions";
import PermissionCard from "./ui/permission-card";
import { toast } from "sonner";

const SettingsPanel = ({ onClose }) => {
const SettingsPanel = ({ onClose, onHotkeyChange }) => {
const showAlert = (alert) => {
toast(alert.title, {
description: alert.description,
Expand All @@ -19,6 +19,99 @@ const SettingsPanel = ({ onClose }) => {
testAccessibilityPermission,
} = usePermissions(showAlert);

// 快捷键设置状态
const [currentHotkey, setCurrentHotkey] = useState('CommandOrControl+Shift+Space');
const [isRecordingHotkey, setIsRecordingHotkey] = useState(false);
const [pendingHotkey, setPendingHotkey] = useState('');
const recordingRef = useRef(false);

// 加载当前热键
useEffect(() => {
const loadHotkey = async () => {
if (window.electronAPI) {
const saved = await window.electronAPI.getSetting('hotkey', 'CommandOrControl+Shift+Space');
if (saved) setCurrentHotkey(saved);
}
};
loadHotkey();
}, []);

// 格式化热键显示
const formatHotkey = (raw) => {
if (!raw) return '';
return raw
.replace('CommandOrControl', navigator.platform.includes('Mac') ? '⌘' : 'Ctrl')
.replace('Shift', '⇧')
.replace('Alt', '⌥')
.replace('Space', '空格')
.replace(/\+/g, ' + ');
};

// 将 KeyboardEvent 转为 Electron 热键字符串
const eventToHotkeyString = (e) => {
const parts = [];
if (e.ctrlKey || e.metaKey) parts.push('CommandOrControl');
if (e.shiftKey) parts.push('Shift');
if (e.altKey) parts.push('Alt');
const key = e.key;
if (!['Control', 'Meta', 'Shift', 'Alt'].includes(key)) {
parts.push(key === ' ' ? 'Space' : key.length === 1 ? key.toUpperCase() : key);
}
return parts.length >= 2 ? parts.join('+') : '';
};

const startRecordingHotkey = () => {
setIsRecordingHotkey(true);
setPendingHotkey('');
recordingRef.current = true;
};

const cancelRecordingHotkey = () => {
setIsRecordingHotkey(false);
setPendingHotkey('');
recordingRef.current = false;
};

const handleKeyDown = (e) => {
if (!recordingRef.current) return;
e.preventDefault();
e.stopPropagation();
const combo = eventToHotkeyString(e);
if (combo) setPendingHotkey(combo);
};

const handleKeyUp = (e) => {
if (!recordingRef.current) return;
e.preventDefault();
e.stopPropagation();
};

const saveHotkey = async () => {
if (!pendingHotkey) return;
try {
// 注销旧热键
await window.electronAPI.unregisterHotkey(currentHotkey);
// 注册新热键
const result = await window.electronAPI.registerHotkey(pendingHotkey);
if (result && result.success) {
await window.electronAPI.setSetting('hotkey', pendingHotkey);
setCurrentHotkey(pendingHotkey);
if (onHotkeyChange) onHotkeyChange(pendingHotkey);
toast.success(`快捷键已更新为 ${formatHotkey(pendingHotkey)}`);
} else {
// 注册失败则恢复旧热键
await window.electronAPI.registerHotkey(currentHotkey);
toast.error('快捷键注册失败,可能与其他应用冲突');
}
} catch (err) {
await window.electronAPI.registerHotkey(currentHotkey);
toast.error('快捷键保存失败');
}
setIsRecordingHotkey(false);
setPendingHotkey('');
recordingRef.current = false;
};


return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
Expand All @@ -39,6 +132,76 @@ const SettingsPanel = ({ onClose }) => {

{/* 内容区域 */}
<div className="p-6 space-y-8">
{/* 快捷键部分 */}
<div>
<h3 className="text-lg font-semibold text-gray-900 mb-4 chinese-title flex items-center gap-2">
<Keyboard className="w-5 h-5 text-blue-600" />
快捷键设置
</h3>
<p className="text-sm text-gray-600 mb-4">
设置触发录音的全局快捷键。
</p>
<div className="bg-gray-50 rounded-lg p-4">
<div className="flex items-center justify-between mb-3">
<span className="text-sm text-gray-700">当前快捷键</span>
<span className="text-sm font-mono bg-white border border-gray-200 rounded px-3 py-1 text-blue-700">
{formatHotkey(currentHotkey)}
</span>
</div>

{!isRecordingHotkey ? (
<button
onClick={startRecordingHotkey}
className="w-full py-2 px-4 bg-blue-600 text-white rounded-lg text-sm hover:bg-blue-700 transition-colors"
>
录制新快捷键
</button>
) : (
<div
className="border-2 border-dashed border-blue-400 rounded-lg p-4 text-center outline-none"
tabIndex={0}
autoFocus
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
>
{pendingHotkey ? (
<div>
<p className="text-sm text-gray-500 mb-2">检测到快捷键:</p>
<p className="text-lg font-mono font-semibold text-blue-700 mb-3">
{formatHotkey(pendingHotkey)}
</p>
<div className="flex gap-2 justify-center">
<button
onClick={saveHotkey}
className="py-1.5 px-4 bg-green-600 text-white rounded-lg text-sm hover:bg-green-700 transition-colors"
>
保存
</button>
<button
onClick={cancelRecordingHotkey}
className="py-1.5 px-4 bg-gray-200 text-gray-700 rounded-lg text-sm hover:bg-gray-300 transition-colors"
>
取消
</button>
</div>
</div>
) : (
<div>
<p className="text-sm text-blue-600 animate-pulse">请按下快捷键组合...</p>
<p className="text-xs text-gray-400 mt-1">需要包含修饰键(⌘ / Ctrl / ⇧ / ⌥)</p>
<button
onClick={cancelRecordingHotkey}
className="mt-3 py-1 px-3 bg-gray-200 text-gray-600 rounded text-xs hover:bg-gray-300 transition-colors"
>
取消
</button>
</div>
)}
</div>
)}
</div>
</div>

{/* 权限部分 */}
<div>
<h3 className="text-lg font-semibold text-gray-900 mb-4 chinese-title">
Expand Down
Loading