Skip to content

Commit 5f629ea

Browse files
committed
merge remote changes
1 parent e53691a commit 5f629ea

4 files changed

Lines changed: 189 additions & 1 deletion

File tree

src/main/shortcuts.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ const callbacks: Record<string, () => void> = {
112112
}
113113
},
114114

115+
// Stop current AI solution stream
116+
stopSolutionStream: () => {
117+
abortCurrentStream('user')
118+
},
119+
115120
ignoreOrEnableMouse: () => {
116121
const mainWindow = global.mainWindow
117122
if (!mainWindow || mainWindow.isDestroyed() || !state.inCoderPage) return

src/renderer/src/lib/store/shortcuts.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ const defaultShortcuts: Record<string, Omit<Shortcut, 'defaultKey'>> = {
3838
category: 'Window Management'
3939
},
4040
takeScreenshot: { action: 'takeScreenshot', key: 'Alt+Enter', category: 'Screenshot & AI' },
41+
stopSolutionStream: {
42+
action: 'stopSolutionStream',
43+
key: 'Alt+.',
44+
category: 'Screenshot & AI'
45+
},
4146
pageUp: { action: 'pageUp', key: 'CommandOrControl+J', category: 'Navigation' },
4247
pageDown: { action: 'pageDown', key: 'CommandOrControl+K', category: 'Navigation' },
4348
moveMainWindowUp: {
@@ -95,7 +100,24 @@ export const useShortcutsStore = create<ShortcutsStore>()(
95100
}),
96101
{
97102
name: 'interview-coder-shortcuts',
98-
version: 1
103+
version: 2,
104+
migrate: (state: any) => {
105+
if (!state?.shortcuts) return state
106+
// Merge in any new default shortcuts that are missing
107+
const defaults = Object.fromEntries(
108+
Object.entries(defaultShortcuts).map(([action, shortcut]) => [
109+
action,
110+
{ ...shortcut, defaultKey: shortcut.key }
111+
])
112+
)
113+
return {
114+
...state,
115+
shortcuts: {
116+
...defaults,
117+
...state.shortcuts
118+
}
119+
}
120+
}
99121
}
100122
)
101123
)

src/renderer/src/settings/CustomShortcuts.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ export function CustomShortcuts() {
8787
<Shortcut label="向左移动窗口" shortcut="moveMainWindowLeft" />
8888
<Shortcut label="向右移动窗口" shortcut="moveMainWindowRight" />
8989
</div>
90+
{/* AI 控制 */}
91+
<div className="space-y-2">
92+
<h3 className="text-sm text-gray-500">AI 控制</h3>
93+
<Shortcut label="停止生成" shortcut="stopSolutionStream" />
94+
</div>
9095
</div>
9196
</ShortcutsContext.Provider>
9297
)

tmp.txt

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import { useState, useEffect, useCallback, createContext, useContext } from 'react'
2+
import { toast } from 'sonner'
3+
import { Button } from '@/components/ui/button'
4+
import ShortcutRenderer from '@/components/ShortcutRenderer'
5+
import { isModifierKey, getShortcutAccelerator } from '@/lib/utils/keyboard'
6+
import { useShortcutsStore } from '@/lib/store/shortcuts'
7+
8+
const ShortcutsContext = createContext<{
9+
recordingAction: string | null
10+
setRecordingAction: (action: string | null) => void
11+
}>({
12+
recordingAction: null,
13+
setRecordingAction: () => {}
14+
})
15+
16+
export function CustomShortcuts() {
17+
const { shortcuts, updateShortcut } = useShortcutsStore()
18+
const [recordingAction, setRecordingAction] = useState<string | null>(null)
19+
20+
const onShortcutChange = useCallback(
21+
(action: string, key: string) => {
22+
const newShortcut = { ...shortcuts[action], key }
23+
updateShortcut(action, newShortcut)
24+
window.api.updateShortcuts([newShortcut])
25+
},
26+
[shortcuts, updateShortcut]
27+
)
28+
29+
const handleKeyDown = useCallback(
30+
(e: KeyboardEvent) => {
31+
if (!recordingAction) return
32+
33+
e.preventDefault()
34+
35+
if (isModifierKey(e.code)) return
36+
const accelerator = getShortcutAccelerator(e)
37+
// User press escape to cancel recording.
38+
if (e.code === 'Escape' && !accelerator) {
39+
setRecordingAction(null)
40+
}
41+
if (!accelerator) return
42+
onShortcutChange(recordingAction, accelerator)
43+
setRecordingAction(null)
44+
},
45+
[recordingAction, onShortcutChange]
46+
)
47+
48+
useEffect(() => {
49+
window.addEventListener('keydown', handleKeyDown)
50+
return () => {
51+
window.removeEventListener('keydown', handleKeyDown)
52+
}
53+
}, [handleKeyDown])
54+
55+
return (
56+
<ShortcutsContext.Provider value={{ recordingAction, setRecordingAction }}>
57+
<div className="space-y-4">
58+
{/* Window Management */}
59+
<div className="space-y-2">
60+
<h3 className="text-sm text-gray-500">窗口管理</h3>
61+
<Shortcut label="隐藏/显示窗口" shortcut="hideOrShowMainWindow" />
62+
<Shortcut
63+
label="鼠标穿透"
64+
description="启用后窗口对鼠标穿透,可以点击窗口背后的内容"
65+
shortcut="ignoreOrEnableMouse"
66+
/>
67+
</div>
68+
69+
{/* Screenshot & AI */}
70+
<div className="space-y-2">
71+
<h3 className="text-sm text-gray-500">截图与AI</h3>
72+
<Shortcut label="截图" shortcut="takeScreenshot" />
73+
</div>
74+
75+
{/* Navigation */}
76+
<div className="space-y-2">
77+
<h3 className="text-sm text-gray-500">页面导航</h3>
78+
<Shortcut label="向上翻页" shortcut="pageUp" />
79+
<Shortcut label="向下翻页" shortcut="pageDown" />
80+
</div>
81+
82+
{/* Window Movement */}
83+
<div className="space-y-2">
84+
<h3 className="text-sm text-gray-500">窗口移动</h3>
85+
<Shortcut label="向上移动窗口" shortcut="moveMainWindowUp" />
86+
<Shortcut label="向下移动窗口" shortcut="moveMainWindowDown" />
87+
<Shortcut label="向左移动窗口" shortcut="moveMainWindowLeft" />
88+
<Shortcut label="向右移动窗口" shortcut="moveMainWindowRight" />
89+
</div>
90+
</div>
91+
</ShortcutsContext.Provider>
92+
)
93+
}
94+
95+
function Shortcut({
96+
label,
97+
description,
98+
shortcut: shortcutAction
99+
}: {
100+
label: string
101+
description?: string
102+
shortcut: string
103+
}) {
104+
const { shortcuts } = useShortcutsStore()
105+
const { recordingAction, setRecordingAction } = useContext(ShortcutsContext)
106+
const shortcut = shortcuts[shortcutAction]
107+
const isRecording = recordingAction === shortcutAction
108+
109+
return shortcut ? (
110+
<div className="flex items-center justify-between">
111+
<div className="flex gap-2 items-center">
112+
<label className="text-sm font-medium">{label}</label>
113+
{description && <p className="text-xs font-light">{description}</p>}
114+
</div>
115+
<span
116+
className="cursor-pointer"
117+
onClick={() => setRecordingAction(isRecording ? null : shortcutAction)}
118+
>
119+
{!isRecording ? (
120+
<ShortcutRenderer shortcut={shortcut.key} />
121+
) : (
122+
<span className="font-mono text-sm align-middle rounded-md pl-2 pr-1 py-1 transition-colors bg-gray-200 animate-pulse">
123+
请按下自定义快捷键...
124+
</span>
125+
)}
126+
</span>
127+
</div>
128+
) : null
129+
}
130+
131+
export function ResetDefaultShortcuts() {
132+
const { shortcuts, resetShortcuts } = useShortcutsStore()
133+
return (
134+
<Button
135+
variant="outline"
136+
size="sm"
137+
className="ml-auto"
138+
onClick={async () => {
139+
await window.api.updateShortcuts(
140+
Object.values(shortcuts)
141+
.filter(({ key, defaultKey }) => key !== defaultKey)
142+
.map((shortcut) => ({
143+
...shortcut,
144+
key: shortcut.defaultKey
145+
}))
146+
)
147+
resetShortcuts()
148+
toast.success('重置默认快捷键成功')
149+
}}
150+
>
151+
重置默认快捷键
152+
</Button>
153+
)
154+
}
155+
156+

0 commit comments

Comments
 (0)