mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-09 19:27:45 +02:00
[eric] voice: context extracted to its own module, provider/overlay runtime import cycle broken
This commit is contained in:
@@ -15,7 +15,7 @@ import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined';
|
||||
import { useAppDispatch, useAppSelector } from '@/shared/hooks';
|
||||
import { fetchSkills } from '@/shared/state/skillsSlice';
|
||||
import { openSettingsModal } from '@/shared/state/settingsSlice';
|
||||
import { useVoice } from '@/shared/voice/VoiceDictationContext';
|
||||
import { useVoice } from '@/shared/voice/voiceContext';
|
||||
import { useElementSelection } from '@/app/components/editor/ElementSelectionContext';
|
||||
import { ClaudeTokens } from '@/shared/styles/claudeTokens';
|
||||
import { ComposerPlusMenu, ActiveTogglePills, PlusMenuItem } from './ComposerPlusMenu';
|
||||
|
||||
@@ -6,7 +6,7 @@ import AddRounded from '@mui/icons-material/AddRounded';
|
||||
import MicNoneOutlinedIcon from '@mui/icons-material/MicNoneOutlined';
|
||||
import MicIcon from '@mui/icons-material/Mic';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import { useVoice } from '@/shared/voice/VoiceDictationContext';
|
||||
import { useVoice } from '@/shared/voice/voiceContext';
|
||||
import GridViewRoundedIcon from '@mui/icons-material/GridViewRounded';
|
||||
import StickyNote2OutlinedIcon from '@mui/icons-material/StickyNote2Outlined';
|
||||
import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded';
|
||||
|
||||
@@ -5,7 +5,7 @@ import Tooltip from '@mui/material/Tooltip';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import MicNoneOutlinedIcon from '@mui/icons-material/MicNoneOutlined';
|
||||
import MicIcon from '@mui/icons-material/Mic';
|
||||
import { useVoice } from '@/shared/voice/VoiceDictationContext';
|
||||
import { useVoice } from '@/shared/voice/voiceContext';
|
||||
import HelpPanel from './HelpPanel';
|
||||
|
||||
/** Top-right desktop pill: Help opens the help panel (ask, report a bug, docs); the mic dictates (local whisper) into the focused field. */
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
import React, { createContext, useCallback, useContext, useEffect, useRef } from 'react';
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import { useAppSelector } from '@/shared/hooks';
|
||||
import { useVoiceDictation, VoiceState, VoiceFeedback } from './useVoiceDictation';
|
||||
import { useVoiceDictation } from './useVoiceDictation';
|
||||
import { VoiceContext } from './voiceContext';
|
||||
import VoiceOverlay from './VoiceOverlay';
|
||||
|
||||
// One recorder for the whole app. Both mics (the Help pill and the spawn composer) plus the global
|
||||
// hotkey drive the SAME dictation session, so two mics can't fight over the microphone or show
|
||||
// out-of-sync state. Mounted once near the app root.
|
||||
interface VoiceContextValue {
|
||||
state: VoiceState;
|
||||
lastText: string;
|
||||
error: string | null;
|
||||
pct: number;
|
||||
feedback: VoiceFeedback | null;
|
||||
toggle: () => void;
|
||||
// Mic-button press semantics that respect the hold/toggle setting: press starts (or toggles),
|
||||
// release stops only in hold mode. Buttons wire onPointerDown/Up to these and stay mode-agnostic.
|
||||
pressStart: () => void;
|
||||
pressEnd: () => void;
|
||||
holdMode: boolean;
|
||||
volumeRef: React.MutableRefObject<number>;
|
||||
}
|
||||
|
||||
const NOOP_REF = { current: 0 };
|
||||
const NOOP: VoiceContextValue = { state: 'idle', lastText: '', error: null, pct: 0, feedback: null, toggle: () => {}, pressStart: () => {}, pressEnd: () => {}, holdMode: true, volumeRef: NOOP_REF };
|
||||
const VoiceContext = createContext<VoiceContextValue>(NOOP);
|
||||
|
||||
export function VoiceDictationProvider({ children }: { children: React.ReactNode }): React.ReactElement {
|
||||
const { state, lastText, error, pct, feedback, toggle, start, stop, volumeRef } = useVoiceDictation();
|
||||
@@ -67,9 +50,3 @@ export function VoiceDictationProvider({ children }: { children: React.ReactNode
|
||||
</VoiceContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
// A component rendered outside the provider (or a web build with no Electron bridge) gets the no-op,
|
||||
// so mics still render and just do nothing rather than crashing.
|
||||
export function useVoice(): VoiceContextValue {
|
||||
return useContext(VoiceContext);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
||||
import ContentPasteRoundedIcon from '@mui/icons-material/ContentPasteRounded';
|
||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||
import { useThemeAccent } from '@/shared/styles/ThemeContext';
|
||||
import { useVoice } from './VoiceDictationContext';
|
||||
import { useVoice } from './voiceContext';
|
||||
|
||||
// WhisperFlow-style presence: while the mic is hot, an accent-tinted aurora breathes up from the
|
||||
// bottom edge, its height and glow riding the live mic level. Imperative rAF writes only (opacity +
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { VoiceState, VoiceFeedback } from './useVoiceDictation';
|
||||
|
||||
// The context lives below both the provider and the overlay so neither imports the other
|
||||
// (VoiceDictationContext renders VoiceOverlay; both reach down here instead of sideways).
|
||||
export interface VoiceContextValue {
|
||||
state: VoiceState;
|
||||
lastText: string;
|
||||
error: string | null;
|
||||
pct: number;
|
||||
feedback: VoiceFeedback | null;
|
||||
toggle: () => void;
|
||||
// Mic-button press semantics that respect the hold/toggle setting: press starts (or toggles),
|
||||
// release stops only in hold mode. Buttons wire onPointerDown/Up to these and stay mode-agnostic.
|
||||
pressStart: () => void;
|
||||
pressEnd: () => void;
|
||||
holdMode: boolean;
|
||||
volumeRef: React.MutableRefObject<number>;
|
||||
}
|
||||
|
||||
const NOOP_REF = { current: 0 };
|
||||
const NOOP: VoiceContextValue = { state: 'idle', lastText: '', error: null, pct: 0, feedback: null, toggle: () => {}, pressStart: () => {}, pressEnd: () => {}, holdMode: true, volumeRef: NOOP_REF };
|
||||
|
||||
export const VoiceContext = createContext<VoiceContextValue>(NOOP);
|
||||
|
||||
// A component rendered outside the provider (or a web build with no Electron bridge) gets the no-op,
|
||||
// so mics still render and just do nothing rather than crashing.
|
||||
export function useVoice(): VoiceContextValue {
|
||||
return useContext(VoiceContext);
|
||||
}
|
||||
Reference in New Issue
Block a user