mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[eric] voice: one-time Globe-key conflict tip when macOS still binds fn to the emoji picker
This commit is contained in:
@@ -116,6 +116,12 @@ contextBridge.exposeInMainWorld('openswarm', {
|
||||
ipcRenderer.on('voice:hold-up', up);
|
||||
return () => { ipcRenderer.removeListener('voice:hold-down', down); ipcRenderer.removeListener('voice:hold-up', up); };
|
||||
},
|
||||
// Fires once at fn-watcher arm when macOS's own Globe-key action is still active (emoji picker on tap).
|
||||
onVoiceGlobeConflict: (cb) => {
|
||||
const h = () => cb();
|
||||
ipcRenderer.on('voice:globe-conflict', h);
|
||||
return () => ipcRenderer.removeListener('voice:globe-conflict', h);
|
||||
},
|
||||
// Hands a vetted social platform's partition cookies to its session-backed MCP shim (allowlisted domains only, gated again in the main process).
|
||||
getPartitionCookies: (domain) => ipcRenderer.invoke('get-partition-cookies', domain),
|
||||
// Silently reads the user's own chatgpt.com/claude.ai history offscreen (no card) for onboarding personalization; main owns the injected script + gates the provider.
|
||||
|
||||
@@ -169,6 +169,16 @@ function installVoiceHotkey(getMainWindow) {
|
||||
});
|
||||
app.on('will-quit', () => { try { fnProc && fnProc.kill('SIGKILL'); } catch (_) {} });
|
||||
console.log('[voice] fn watcher armed (awaiting first event to prove Input Monitoring)');
|
||||
// macOS's own Globe-key action (emoji picker by default) fires on a quick fn tap alongside us;
|
||||
// tell the renderer once so it can point the user at "Press Globe key to: Do Nothing".
|
||||
require('child_process').exec('defaults read com.apple.HIToolbox AppleFnUsageType', (err, out) => {
|
||||
const usage = err ? '2' : String(out).trim();
|
||||
if (usage !== '0') {
|
||||
const win = getMainWindow();
|
||||
if (win && !win.isDestroyed()) win.webContents.send('voice:globe-conflict');
|
||||
console.log(`[voice] Globe key system action is active (AppleFnUsageType=${usage}); quick fn taps also trigger it`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let tapKeycode;
|
||||
|
||||
@@ -10,7 +10,19 @@ import VoiceOverlay from './VoiceOverlay';
|
||||
// out-of-sync state. Mounted once near the app root.
|
||||
|
||||
export function VoiceDictationProvider({ children }: { children: React.ReactNode }): React.ReactElement {
|
||||
const { state, lastText, error, pct, feedback, partial, toggle, start, stop, cancel, volumeRef } = useVoiceDictation();
|
||||
const { state, lastText, error, pct, feedback, partial, toggle, start, stop, cancel, notify, volumeRef } = useVoiceDictation();
|
||||
|
||||
// fn is the dictation key, but macOS may still have its own Globe action bound (emoji picker on a quick tap); say so once.
|
||||
const globeWarnedRef = useRef(false);
|
||||
useEffect(() => {
|
||||
const bridge = window as unknown as { openswarm?: { onVoiceGlobeConflict?: (cb: () => void) => () => void } };
|
||||
const off = bridge.openswarm?.onVoiceGlobeConflict?.(() => {
|
||||
if (globeWarnedRef.current) return;
|
||||
globeWarnedRef.current = true;
|
||||
notify('Tip: set System Settings > Keyboard > "Press Globe key to" to "Do Nothing" so fn only dictates.');
|
||||
});
|
||||
return () => { off?.(); };
|
||||
}, [notify]);
|
||||
const holdMode = useAppSelector((s) => s.settings.data.voice_hold_to_talk ?? true);
|
||||
const dictationShortcut = useAppSelector((s) => s.settings.data.dictation_shortcut ?? null);
|
||||
const dictationModel = useAppSelector((s) => s.settings.data.dictation_model ?? null);
|
||||
|
||||
@@ -294,5 +294,10 @@ export function useVoiceDictation() {
|
||||
// A dangling recorder (unmount mid-capture) must release the mic.
|
||||
useEffect(() => () => { teardown(); }, [teardown]);
|
||||
|
||||
return { state, lastText, error, pct, feedback, partial, toggle, start, stop, cancel, volumeRef };
|
||||
// One-line notices from outside the session flow (Globe-key conflict, permission hints) ride the same bubble.
|
||||
const notify = useCallback((text: string): void => {
|
||||
setFeedback({ tone: 'warn', icon: 'info', text, at: Date.now() });
|
||||
}, []);
|
||||
|
||||
return { state, lastText, error, pct, feedback, partial, toggle, start, stop, cancel, notify, volumeRef };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user