diff --git a/apps/desktop/src/components/editor-area/floating-button.tsx b/apps/desktop/src/components/editor-area/floating-button.tsx index c6cde31416..0c3141ff7e 100644 --- a/apps/desktop/src/components/editor-area/floating-button.tsx +++ b/apps/desktop/src/components/editor-area/floating-button.tsx @@ -1,3 +1,4 @@ +import { useQueryClient } from "@tanstack/react-query"; import { PlusIcon, RefreshCwIcon, TypeOutlineIcon, XIcon, ZapIcon } from "lucide-react"; import { useEffect, useRef, useState } from "react"; @@ -70,6 +71,7 @@ export function FloatingButton({ const [showRefreshIcon, setShowRefreshIcon] = useState(true); const [showTemplatePopover, setShowTemplatePopover] = useState(false); const hideTimeoutRef = useRef(null); + const queryClient = useQueryClient(); // Clear timeout on cleanup useEffect(() => { @@ -111,6 +113,7 @@ export function FloatingButton({ hideTimeoutRef.current = null; } if (!showRaw && !isEnhancePending && showRefreshIcon) { + queryClient.invalidateQueries({ queryKey: ["templates"] }); setShowTemplatePopover(true); } }; @@ -121,11 +124,9 @@ export function FloatingButton({ }, 100); }; - // Simple template selection - just call parent function const handleTemplateSelect = (templateId: string) => { setShowTemplatePopover(false); - // Send analytics event for custom template usage (not for "auto") if (templateId !== "auto") { analyticsCommands.event({ event: "custom_template_enhancement_started", @@ -136,45 +137,21 @@ export function FloatingButton({ handleEnhanceWithTemplate(templateId); }; - // Helper function to extract emoji and clean name - const extractEmojiAndName = (title: string) => { - const emojiMatch = title.match(/^(\p{Emoji})\s*/u); - if (emojiMatch) { - return { - emoji: emojiMatch[1], - name: title.replace(/^(\p{Emoji})\s*/u, "").trim(), - }; - } - - // Fallback emoji based on keywords if no emoji in title - const lowercaseTitle = title.toLowerCase(); - let fallbackEmoji = "📄"; - if (lowercaseTitle.includes("meeting")) { - fallbackEmoji = "💼"; - } - if (lowercaseTitle.includes("interview")) { - fallbackEmoji = "👔"; - } - if (lowercaseTitle.includes("standup")) { - fallbackEmoji = "☀️"; - } - if (lowercaseTitle.includes("review")) { - fallbackEmoji = "📝"; - } - - return { - emoji: fallbackEmoji, - name: title, - }; - }; - const handleAddTemplate = async () => { setShowTemplatePopover(false); try { - // Open settings window + queryClient.invalidateQueries({ queryKey: ["templates"] }); + await windowsCommands.windowShow({ type: "settings" }); - // Navigate to templates tab await windowsCommands.windowNavigate({ type: "settings" }, "/app/settings?tab=templates"); + + const handleWindowFocus = () => { + queryClient.invalidateQueries({ queryKey: ["templates"] }); + queryClient.invalidateQueries({ queryKey: ["llm-connection"] }); + window.removeEventListener("focus", handleWindowFocus); + }; + + window.addEventListener("focus", handleWindowFocus); } catch (error) { console.error("Failed to open settings/templates:", error); } @@ -356,3 +333,35 @@ function RunOrRerun({ showRefresh }: { showRefresh: boolean }) { ); } + +// Helper function to extract emoji and clean name +const extractEmojiAndName = (title: string) => { + const emojiMatch = title.match(/^(\p{Emoji})\s*/u); + if (emojiMatch) { + return { + emoji: emojiMatch[1], + name: title.replace(/^(\p{Emoji})\s*/u, "").trim(), + }; + } + + // Fallback emoji based on keywords if no emoji in title + const lowercaseTitle = title.toLowerCase(); + let fallbackEmoji = "📄"; + if (lowercaseTitle.includes("meeting")) { + fallbackEmoji = "💼"; + } + if (lowercaseTitle.includes("interview")) { + fallbackEmoji = "👔"; + } + if (lowercaseTitle.includes("standup")) { + fallbackEmoji = "☀️"; + } + if (lowercaseTitle.includes("review")) { + fallbackEmoji = "📝"; + } + + return { + emoji: fallbackEmoji, + name: title, + }; +}; diff --git a/apps/desktop/src/components/editor-area/index.tsx b/apps/desktop/src/components/editor-area/index.tsx index 2105cb462e..afc2b3eb6d 100644 --- a/apps/desktop/src/components/editor-area/index.tsx +++ b/apps/desktop/src/components/editor-area/index.tsx @@ -292,6 +292,8 @@ export function useEnhanceMutation({ }) { const { userId, onboardingSessionId } = useHypr(); const [progress, setProgress] = useState(0); + const [actualIsLocalLlm, setActualIsLocalLlm] = useState(isLocalLlm); + const queryClient = useQueryClient(); const preMeetingText = extractTextFromHtml(preMeetingNote); const rawText = extractTextFromHtml(rawContent); @@ -316,7 +318,15 @@ export function useEnhanceMutation({ const enhance = useMutation({ mutationKey: ["enhance", sessionId], mutationFn: async () => { - if (isLocalLlm) { + await queryClient.invalidateQueries({ queryKey: ["llm-connection"] }); + await new Promise(resolve => setTimeout(resolve, 100)); + + const { type } = await connectorCommands.getLlmConnection(); + const freshIsLocalLlm = type === "HyprLocal"; + + setActualIsLocalLlm(freshIsLocalLlm); + + if (freshIsLocalLlm) { setProgress(0); } @@ -334,12 +344,9 @@ export function useEnhanceMutation({ dismissible: true, duration: 5000, }); - return; } - const { type } = await connectorCommands.getLlmConnection(); - const config = await dbCommands.getConfig(); let templateInfo = ""; @@ -399,8 +406,6 @@ Sections:`; : provider.languageModel("defaultModel"); if (sessionId !== onboardingSessionId) { - const { type } = await connectorCommands.getLlmConnection(); - analyticsCommands.event({ event: "normal_enhance_start", distinct_id: userId, @@ -412,7 +417,8 @@ Sections:`; const { text, fullStream } = streamText({ abortSignal, model, - ...(isLocalLlm && { + // Use fresh value for tools + ...(freshIsLocalLlm && { tools: { update_progress: tool({ parameters: z.any() }), }, @@ -425,7 +431,8 @@ Sections:`; markdownTransform(), smoothStream({ delayInMs: 80, chunking: "line" }), ], - ...(isLocalLlm && { + // Use fresh value for provider options + ...(freshIsLocalLlm && { providerOptions: { [localProviderName]: { metadata: customGrammar @@ -446,7 +453,8 @@ Sections:`; if (chunk.type === "text-delta") { acc += chunk.textDelta; } - if (chunk.type === "tool-call" && isLocalLlm) { + // Use fresh value for progress updates + if (chunk.type === "tool-call" && freshIsLocalLlm) { const chunkProgress = chunk.args?.progress ?? 0; setProgress(chunkProgress); } @@ -469,12 +477,13 @@ Sections:`; }); persistSession(); - if (isLocalLlm) { + + if (actualIsLocalLlm) { setProgress(0); } }, onError: (error) => { - if (isLocalLlm) { + if (actualIsLocalLlm) { setProgress(0); } console.error(error); @@ -485,7 +494,7 @@ Sections:`; }, }); - return { enhance, progress: isLocalLlm ? progress : undefined }; + return { enhance, progress: actualIsLocalLlm ? progress : undefined }; } function useGenerateTitleMutation({ sessionId }: { sessionId: string }) {