Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ export function FloatingButton({

const handleWindowFocus = () => {
queryClient.invalidateQueries({ queryKey: ["templates"] });
queryClient.invalidateQueries({ queryKey: ["llm-connection"] });
window.removeEventListener("focus", handleWindowFocus);
};

Expand Down
121 changes: 37 additions & 84 deletions apps/desktop/src/components/editor-area/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,37 +63,12 @@ export default function EditorArea({
[sessionId, showRaw],
);

const [needsRestoration, setNeedsRestoration] = useState(false);
const [originalTemplateId, setOriginalTemplateId] = useState<string | null>(null);
const queryClient = useQueryClient();

const configQuery = useQuery({
queryKey: ["config", "general"],
queryFn: async () => {
const result = await dbCommands.getConfig();
return result;
},
});

const setConfigMutation = useMutation({
mutationFn: async (configData: any) => {
await dbCommands.setConfig(configData);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["config", "general"] });
},
onError: (error) => {
console.error("Failed to set template config:", error);
},
});

const templatesQuery = useQuery({
queryKey: ["templates"],
queryFn: () => dbCommands.listTemplates(),
refetchOnWindowFocus: true,
});

const generateTitle = useGenerateTitleMutation({ sessionId });
const preMeetingNote = useSession(sessionId, (s) => s.session.pre_meeting_memo_html) ?? "";
const hasTranscriptWords = useSession(sessionId, (s) => s.session.words.length > 0);

Expand All @@ -108,27 +83,14 @@ export default function EditorArea({
rawContent,
isLocalLlm: llmConnectionQuery.data?.type === "HyprLocal",
onSuccess: (content) => {
generateTitle.mutate({ enhancedContent: content });

if (needsRestoration && configQuery.data) {
const restoreConfig = {
...configQuery.data,
general: {
...configQuery.data.general,
selected_template_id: originalTemplateId,
},
};
setConfigMutation.mutate(restoreConfig);
setNeedsRestoration(false);
setOriginalTemplateId(null);
}

if (hasTranscriptWords) {
generateTitle.mutate({ enhancedContent: content });
}
},
});

const generateTitle = useGenerateTitleMutation({ sessionId });

useAutoEnhance({
sessionId,
enhanceStatus: enhance.status,
Expand All @@ -151,46 +113,13 @@ export default function EditorArea({
[showRaw, enhancedContent, rawContent],
);

const handleEnhanceWithTemplate = useCallback(async (templateId: string) => {
if (configQuery.data) {
const currentTemplateId = configQuery.data.general?.selected_template_id || null;
setOriginalTemplateId(currentTemplateId);
setNeedsRestoration(true);

const targetTemplateId = templateId === "auto" ? null : templateId;

const updatedConfig = {
...configQuery.data,
general: {
...configQuery.data.general,
selected_template_id: targetTemplateId,
},
};

try {
await setConfigMutation.mutateAsync(updatedConfig);

await new Promise(resolve => setTimeout(resolve, 200));

const verifyConfig = await dbCommands.getConfig();

if (verifyConfig.general?.selected_template_id !== targetTemplateId) {
setOriginalTemplateId(null);
setNeedsRestoration(false);
return;
}
} catch (error) {
setOriginalTemplateId(null);
setNeedsRestoration(false);
return;
}
}

enhance.mutate();
}, [enhance, configQuery.data, setConfigMutation]);
const handleEnhanceWithTemplate = useCallback((templateId: string) => {
const targetTemplateId = templateId === "auto" ? null : templateId;
enhance.mutate({ templateId: targetTemplateId, triggerType: "template" });
}, [enhance]);

const handleClickEnhance = useCallback(() => {
enhance.mutate();
enhance.mutate({ triggerType: "manual" });
}, [enhance]);

const safelyFocusEditor = useCallback(() => {
Expand Down Expand Up @@ -317,7 +246,13 @@ export function useEnhanceMutation({

const enhance = useMutation({
mutationKey: ["enhance", sessionId],
mutationFn: async () => {
mutationFn: async ({
triggerType,
templateId,
}: {
triggerType: "manual" | "template" | "auto";
templateId?: string | null;
} = { triggerType: "manual" }) => {
await queryClient.invalidateQueries({ queryKey: ["llm-connection"] });
await new Promise(resolve => setTimeout(resolve, 100));

Expand Down Expand Up @@ -347,15 +282,20 @@ export function useEnhanceMutation({
return;
}

// Get current config for default template
const config = await dbCommands.getConfig();

// Use provided templateId or fall back to config
const effectiveTemplateId = templateId !== undefined
? templateId
: config.general?.selected_template_id;

let templateInfo = "";
let customGrammar: string | null = null;
const selectedTemplateId = config.general.selected_template_id;

if (selectedTemplateId) {
if (effectiveTemplateId) {
const templates = await dbCommands.listTemplates();
const selectedTemplate = templates.find(t => t.id === selectedTemplateId);
const selectedTemplate = templates.find(t => t.id === effectiveTemplateId);

if (selectedTemplate) {
if (selectedTemplate.sections && selectedTemplate.sections.length > 0) {
Expand Down Expand Up @@ -560,9 +500,11 @@ function useAutoEnhance({
}: {
sessionId: string;
enhanceStatus: string;
enhanceMutate: () => void;
enhanceMutate: (params: { triggerType: "auto"; templateId?: string | null }) => void;
}) {
const ongoingSessionStatus = useOngoingSession((s) => s.status);
const autoEnhanceTemplate = useOngoingSession((s) => s.autoEnhanceTemplate);
const setAutoEnhanceTemplate = useOngoingSession((s) => s.setAutoEnhanceTemplate);
const prevOngoingSessionStatus = usePreviousValue(ongoingSessionStatus);
const setShowRaw = useSession(sessionId, (s) => s.setShowRaw);

Expand All @@ -573,14 +515,25 @@ function useAutoEnhance({
&& enhanceStatus !== "pending"
) {
setShowRaw(false);
enhanceMutate();

// Use the selected template and then clear it
enhanceMutate({
triggerType: "auto",
templateId: autoEnhanceTemplate,
});

// Clear the template after using it (one-time use)
setAutoEnhanceTemplate(null);
}
}, [
ongoingSessionStatus,
enhanceStatus,
sessionId,
enhanceMutate,
setShowRaw,
autoEnhanceTemplate,
setAutoEnhanceTemplate,
prevOngoingSessionStatus,
]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { useEffect, useState } from "react";
import SoundIndicator from "@/components/sound-indicator";
import { useHypr } from "@/contexts";
import { useEnhancePendingState } from "@/hooks/enhance-pending";
import { commands as dbCommands } from "@hypr/plugin-db";
import { commands as listenerCommands } from "@hypr/plugin-listener";
import { commands as localSttCommands } from "@hypr/plugin-local-stt";
import { Button } from "@hypr/ui/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@hypr/ui/components/ui/popover";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@hypr/ui/components/ui/select";
import { Spinner } from "@hypr/ui/components/ui/spinner";
import { sonnerToast, toast } from "@hypr/ui/components/ui/toast";
import { Tooltip, TooltipContent, TooltipTrigger } from "@hypr/ui/components/ui/tooltip";
Expand Down Expand Up @@ -222,6 +224,7 @@ function WhenActive() {
const ongoingSessionStore = useOngoingSession((s) => ({
pause: s.pause,
stop: s.stop,
setAutoEnhanceTemplate: s.setAutoEnhanceTemplate,
}));
const sessionWords = useSession(ongoingSessionId!, (s) => s.session.words);
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
Expand All @@ -231,7 +234,11 @@ function WhenActive() {
setIsPopoverOpen(false);
};

const handleStopSession = () => {
const handleStopSession = (templateId?: string | null) => {
if (templateId !== undefined) {
ongoingSessionStore.setAutoEnhanceTemplate(templateId);
}

ongoingSessionStore.stop();
setIsPopoverOpen(false);

Expand All @@ -241,7 +248,7 @@ function WhenActive() {
};

return (
<Popover>
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
<PopoverTrigger asChild>
<button
className={cn([
Expand All @@ -268,12 +275,13 @@ function RecordingControls({
onStop,
}: {
onPause: () => void;
onStop: () => void;
onStop: (templateId?: string | null) => void;
}) {
const ongoingSessionMuted = useOngoingSession((s) => ({
micMuted: s.micMuted,
speakerMuted: s.speakerMuted,
}));
const [selectedTemplate, setSelectedTemplate] = useState<string>("auto");

const toggleMicMuted = useMutation({
mutationFn: () => listenerCommands.setMicMuted(!ongoingSessionMuted.micMuted),
Expand All @@ -283,9 +291,34 @@ function RecordingControls({
mutationFn: () => listenerCommands.setSpeakerMuted(!ongoingSessionMuted.speakerMuted),
});

const configQuery = useQuery({
queryKey: ["config"],
queryFn: () => dbCommands.getConfig(),
refetchOnWindowFocus: true,
});

const templatesQuery = useQuery({
queryKey: ["templates"],
queryFn: () => dbCommands.listTemplates(),
refetchOnWindowFocus: true,
});

useEffect(() => {
if (configQuery.data?.general?.selected_template_id) {
setSelectedTemplate(configQuery.data.general.selected_template_id);
} else {
setSelectedTemplate("auto");
}
}, [configQuery.data]);

const handleStopWithTemplate = () => {
const actualTemplateId = selectedTemplate === "auto" ? null : selectedTemplate;
onStop(actualTemplateId);
};

return (
<>
<div className="flex w-full justify-between mb-4">
<div className="flex w-full justify-between mb-3">
<AudioControlButton
isMuted={ongoingSessionMuted.micMuted}
onClick={() => toggleMicMuted.mutate()}
Expand All @@ -298,6 +331,24 @@ function RecordingControls({
/>
</div>

<div className="mb-3">
<Select value={selectedTemplate} onValueChange={setSelectedTemplate}>
<SelectTrigger className="w-full text-sm">
<SelectValue placeholder="Select template..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="auto">
<Trans>Auto (Default)</Trans>
</SelectItem>
{templatesQuery.data?.map((template) => (
<SelectItem key={template.id} value={template.id}>
{template.title || "Untitled"}
</SelectItem>
))}
</SelectContent>
</Select>
</div>

<div className="flex gap-2">
<Button
variant="outline"
Expand All @@ -309,7 +360,7 @@ function RecordingControls({
</Button>
<Button
variant="destructive"
onClick={onStop}
onClick={handleStopWithTemplate}
className="w-full"
>
<StopCircleIcon size={16} />
Expand Down
10 changes: 10 additions & 0 deletions packages/utils/src/stores/ongoing-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ type State = {
enhanceController: AbortController | null;
micMuted: boolean;
speakerMuted: boolean;
autoEnhanceTemplate: string | null;
};

type Actions = {
get: () => State & Actions;
cancelEnhance: () => void;
setEnhanceController: (controller: AbortController | null) => void;
setAutoEnhanceTemplate: (templateId: string | null) => void;
start: (sessionId: string) => void;
stop: () => void;
pause: () => void;
Expand All @@ -33,6 +35,7 @@ const initialState: State = {
enhanceController: null,
micMuted: false,
speakerMuted: false,
autoEnhanceTemplate: null,
};

export type OngoingSessionStore = ReturnType<typeof createOngoingSessionStore>;
Expand All @@ -54,6 +57,13 @@ export const createOngoingSessionStore = (sessionsStore: ReturnType<typeof creat
})
);
},
setAutoEnhanceTemplate: (templateId: string | null) => {
set((state) =>
mutate(state, (draft) => {
draft.autoEnhanceTemplate = templateId;
})
);
},
start: (sessionId: string) => {
console.log("start", sessionId);
set((state) =>
Expand Down
Loading