{
diff --git a/openless-all/app/src-tauri/src/lib.rs b/openless-all/app/src-tauri/src/lib.rs
index 01b33e2b..4e93e444 100644
--- a/openless-all/app/src-tauri/src/lib.rs
+++ b/openless-all/app/src-tauri/src/lib.rs
@@ -152,6 +152,7 @@ pub fn run() {
commands::start_dictation,
commands::stop_dictation,
commands::cancel_dictation,
+ commands::handle_window_hotkey_event,
#[cfg(debug_assertions)]
commands::inject_hotkey_click_for_dev,
commands::repolish,
diff --git a/openless-all/app/src/App.tsx b/openless-all/app/src/App.tsx
index f0a22e59..a03507cb 100644
--- a/openless-all/app/src/App.tsx
+++ b/openless-all/app/src/App.tsx
@@ -3,7 +3,12 @@ import { Capsule } from './components/Capsule';
import { FloatingShell } from './components/FloatingShell';
import { Onboarding } from './components/Onboarding';
import { detectOS } from './components/WindowChrome';
-import { checkAccessibilityPermission, checkMicrophonePermission, isTauri } from './lib/ipc';
+import {
+ checkAccessibilityPermission,
+ checkMicrophonePermission,
+ handleWindowHotkeyEvent,
+ isTauri,
+} from './lib/ipc';
import { HotkeySettingsProvider } from './state/HotkeySettingsContext';
interface AppProps {
@@ -53,6 +58,28 @@ export function App({ isCapsule }: AppProps) {
};
}, [os]);
+ useEffect(() => {
+ if (!isTauri || os !== 'win') return;
+ const forwardKey = (event: KeyboardEvent) => {
+ if (!isWindowHotkeyCandidate(event)) return;
+ console.debug(
+ `[ui-key] type=${event.type} key=${event.key} code=${event.code} repeat=${event.repeat}`,
+ );
+ void handleWindowHotkeyEvent(
+ event.type as 'keydown' | 'keyup',
+ event.key,
+ event.code,
+ event.repeat,
+ ).catch(error => console.warn('[window-hotkey] forward failed', error));
+ };
+ window.addEventListener('keydown', forwardKey, true);
+ window.addEventListener('keyup', forwardKey, true);
+ return () => {
+ window.removeEventListener('keydown', forwardKey, true);
+ window.removeEventListener('keyup', forwardKey, true);
+ };
+ }, [os]);
+
if (gate === 'checking') {
return ;
}
@@ -63,6 +90,18 @@ export function App({ isCapsule }: AppProps) {
);
}
+function isWindowHotkeyCandidate(event: KeyboardEvent): boolean {
+ return (
+ event.key === 'Escape' ||
+ event.code === 'ControlRight' ||
+ event.code === 'ControlLeft' ||
+ event.code === 'AltRight' ||
+ event.code === 'AltLeft' ||
+ event.code === 'MetaRight' ||
+ event.code === 'Fn'
+ );
+}
+
function StartupShell() {
return (
{
return invokeOrMock('cancel_dictation', undefined, () => undefined);
}
+export function handleWindowHotkeyEvent(
+ eventType: 'keydown' | 'keyup',
+ key: string,
+ code: string,
+ repeat: boolean,
+): Promise {
+ return invokeOrMock('handle_window_hotkey_event', { event_type: eventType, key, code, repeat }, () => undefined);
+}
+
// ── Polish ─────────────────────────────────────────────────────────────
export function repolish(rawText: string, mode: PolishMode): Promise {
return invokeOrMock('repolish', { rawText, mode }, () => rawText);