|
1 | 1 | import { useEffect, useMemo, useState } from 'react' |
2 | 2 | import { useTranslation } from 'react-i18next' |
| 3 | +import { Events } from '@wailsio/runtime' |
3 | 4 | import { SettingsService, TriggerService } from '../shared/bindings' |
4 | 5 | import { comboKey, describeCombo, formatCombo, keyFromEventCode, modsFromEvent, reservedByMacOS } from '../shared/keybinding' |
5 | 6 | import { refreshKeybindings, useAppStore } from '../shared/store' |
@@ -29,6 +30,12 @@ interface ComboCaptureAdapter { |
29 | 30 | currentBinding: () => Promise<string | null> |
30 | 31 | assign: (mods: string[], key: string) => Promise<string> |
31 | 32 | unassign: () => Promise<void> |
| 33 | + // Which mill-data-changed entity carries this adapter's own combo -- |
| 34 | + // "hotkey" (a workflow trigger) or "keybinding" (a command override). |
| 35 | + // Lets useComboCapture refetch currentBinding when the SAME target |
| 36 | + // changes from elsewhere (another open tab/window's recorder), not |
| 37 | + // just after this hook instance's own assign/unassign call. |
| 38 | + entity: 'hotkey' | 'keybinding' |
32 | 39 | } |
33 | 40 |
|
34 | 41 | // Extracted from the now-retired RunbookView.tsx (docs/SPEC.md §2.2's |
@@ -58,6 +65,21 @@ function useComboCapture(enabled: boolean, adapter: ComboCaptureAdapter, onChang |
58 | 65 | // eslint-disable-next-line react-hooks/exhaustive-deps |
59 | 66 | }, [enabled]) |
60 | 67 |
|
| 68 | + // Live sync: this target's combo can change from a DIFFERENT open |
| 69 | + // recorder instance (another tab's NodeInspector, the Settings |
| 70 | + // Keyboard Shortcuts row, a second window) -- without this, only the |
| 71 | + // instance that made the change saw its own onChanged callback, and |
| 72 | + // every other mounted recorder for the same target stayed on its |
| 73 | + // mount-time snapshot until closed and reopened. |
| 74 | + useEffect(() => { |
| 75 | + if (!enabled) return |
| 76 | + return Events.On('mill-data-changed', (evt) => { |
| 77 | + const changed = (evt.data as { entity?: string })?.entity |
| 78 | + if (changed === adapter.entity) adapter.currentBinding().then(setBinding).catch(console.error) |
| 79 | + }) |
| 80 | + // eslint-disable-next-line react-hooks/exhaustive-deps |
| 81 | + }, [enabled]) |
| 82 | + |
61 | 83 | // Menu-accelerator suspension (SettingsService.SuspendMenuAccelerators) |
62 | 84 | // brackets the entire time this hook is "recording", not just the |
63 | 85 | // keydown listener below -- on macOS, NSMenu's own |
@@ -148,6 +170,7 @@ export function useHotkeyCapture(workflowId: string | null, onChanged?: () => vo |
148 | 170 | currentBinding: () => TriggerService.ListHotkeys().then((list) => (list ?? {})[workflowId ?? ''] ?? null), |
149 | 171 | assign: (mods, key) => TriggerService.AssignHotkey(workflowId ?? '', mods, key), |
150 | 172 | unassign: () => TriggerService.UnassignHotkey(workflowId ?? ''), |
| 173 | + entity: 'hotkey', |
151 | 174 | }), [workflowId]) |
152 | 175 | return useComboCapture(workflowId !== null, adapter, onChanged) |
153 | 176 | } |
@@ -199,6 +222,7 @@ export function useCommandKeybindingCapture(commandId: string | null, onChanged? |
199 | 222 | }) |
200 | 223 | }, |
201 | 224 | unassign: () => SettingsService.ClearKeybinding(commandId ?? '').then(() => { void refreshKeybindings() }), |
| 225 | + entity: 'keybinding', |
202 | 226 | }), [commandId, t]) |
203 | 227 | return useComboCapture(commandId !== null, adapter, onChanged) |
204 | 228 | } |
0 commit comments