feat(nexus): mount phase 14 providers + drain voice queue in assistant
main.tsx adds VoiceProvider and CommandPaletteProvider to the provider stack, placed above BrowserRouter so both the route tree and the document-level Cmd+K listener see a stable context. PersonalAssistant gains a single-shot effect that, on mount, drains VoiceContext.queue via drainQueue() and feeds each transcript through the existing handleSend pipeline. This implements spec section 5.5 (voice captured on non-Assistant routes streams through on arrival). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2a950dedd0
commit
9b772aa1bd
2 changed files with 45 additions and 23 deletions
|
|
@ -14,6 +14,8 @@ import { SidebarProvider } from "./context/SidebarContext";
|
||||||
import { DialogProvider } from "./context/DialogContext";
|
import { DialogProvider } from "./context/DialogContext";
|
||||||
import { ToastProvider } from "./context/ToastContext";
|
import { ToastProvider } from "./context/ToastContext";
|
||||||
import { ThemeProvider } from "./context/ThemeContext";
|
import { ThemeProvider } from "./context/ThemeContext";
|
||||||
|
import { VoiceProvider } from "./context/VoiceContext";
|
||||||
|
import { CommandPaletteProvider } from "./context/CommandPaletteContext";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
import { initPluginBridge } from "./plugins/bridge-init";
|
import { initPluginBridge } from "./plugins/bridge-init";
|
||||||
import { PluginLauncherProvider } from "./plugins/launchers";
|
import { PluginLauncherProvider } from "./plugins/launchers";
|
||||||
|
|
@ -41,29 +43,33 @@ createRoot(document.getElementById("root")!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<BrowserRouter>
|
<VoiceProvider>
|
||||||
<CompanyProvider>
|
<CommandPaletteProvider>
|
||||||
<ToastProvider>
|
<BrowserRouter>
|
||||||
<LiveUpdatesProvider>
|
<CompanyProvider>
|
||||||
<TooltipProvider>
|
<ToastProvider>
|
||||||
<BreadcrumbProvider>
|
<LiveUpdatesProvider>
|
||||||
<SidebarProvider>
|
<TooltipProvider>
|
||||||
<PanelProvider>
|
<BreadcrumbProvider>
|
||||||
<ChatPanelProvider>
|
<SidebarProvider>
|
||||||
<PluginLauncherProvider>
|
<PanelProvider>
|
||||||
<DialogProvider>
|
<ChatPanelProvider>
|
||||||
<App />
|
<PluginLauncherProvider>
|
||||||
</DialogProvider>
|
<DialogProvider>
|
||||||
</PluginLauncherProvider>
|
<App />
|
||||||
</ChatPanelProvider>
|
</DialogProvider>
|
||||||
</PanelProvider>
|
</PluginLauncherProvider>
|
||||||
</SidebarProvider>
|
</ChatPanelProvider>
|
||||||
</BreadcrumbProvider>
|
</PanelProvider>
|
||||||
</TooltipProvider>
|
</SidebarProvider>
|
||||||
</LiveUpdatesProvider>
|
</BreadcrumbProvider>
|
||||||
</ToastProvider>
|
</TooltipProvider>
|
||||||
</CompanyProvider>
|
</LiveUpdatesProvider>
|
||||||
</BrowserRouter>
|
</ToastProvider>
|
||||||
|
</CompanyProvider>
|
||||||
|
</BrowserRouter>
|
||||||
|
</CommandPaletteProvider>
|
||||||
|
</VoiceProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ export function PersonalAssistant() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { pushToast } = useToast();
|
const { pushToast } = useToast();
|
||||||
const { activeConversationId, setActiveConversationId } = useChatPanel();
|
const { activeConversationId, setActiveConversationId } = useChatPanel();
|
||||||
|
const voice = useVoice();
|
||||||
|
|
||||||
const companyId = selectedCompany?.id ?? null;
|
const companyId = selectedCompany?.id ?? null;
|
||||||
const companyPrefix = useMemo(
|
const companyPrefix = useMemo(
|
||||||
|
|
@ -195,6 +196,21 @@ export function PersonalAssistant() {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [promote.state.kind]);
|
}, [promote.state.kind]);
|
||||||
|
|
||||||
|
// Phase 14 — drain any voice transcripts captured from non-Assistant
|
||||||
|
// routes into the chat pipeline. Runs once per mount; if the queue is
|
||||||
|
// empty the effect is a no-op.
|
||||||
|
const voiceDrainedRef = useRef(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (voiceDrainedRef.current) return;
|
||||||
|
if (!companyId) return;
|
||||||
|
if (voice.queue.length === 0) return;
|
||||||
|
voiceDrainedRef.current = true;
|
||||||
|
const drained = voice.drainQueue();
|
||||||
|
for (const text of drained) {
|
||||||
|
handleSend(text);
|
||||||
|
}
|
||||||
|
}, [companyId, voice, handleSend]);
|
||||||
|
|
||||||
const handlePromote = useCallback(() => {
|
const handlePromote = useCallback(() => {
|
||||||
if (!selectedConvId || !canPromote) return;
|
if (!selectedConvId || !canPromote) return;
|
||||||
promote.startPrompting();
|
promote.startPrompting();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue