diff --git a/.vscode/settings.json b/.vscode/settings.json index 5d5d68686..2300c75e4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,5 +21,8 @@ "rust-analyzer.cargo.targetDir": "target/analyzer", "rust-analyzer.cargo.extraEnv": { "MACOSX_DEPLOYMENT_TARGET": "14.2" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "vscode.typescript-language-features" } } diff --git a/apps/desktop/src/components/editor-area/index.tsx b/apps/desktop/src/components/editor-area/index.tsx index 0dab6bc90..80accf5d9 100644 --- a/apps/desktop/src/components/editor-area/index.tsx +++ b/apps/desktop/src/components/editor-area/index.tsx @@ -22,12 +22,30 @@ import { toast } from "@hypr/ui/components/ui/toast"; import { cn } from "@hypr/ui/lib/utils"; import { generateText, localProviderName, modelProvider, smoothStream, streamText, tool } from "@hypr/utils/ai"; import { useOngoingSession, useSession, useSessions } from "@hypr/utils/contexts"; +import { load } from "@tauri-apps/plugin-store"; import { enhanceFailedToast } from "../toast/shared"; import { AnnotationBox } from "./annotation-box"; import { FloatingButton } from "./floating-button"; import { NoteHeader } from "./note-header"; import { TextSelectionPopover } from "./text-selection-popover"; +const getUserContext = async (): Promise => { + try { + const store = await load("store.json", { autoSave: false }); + const val = await store.get("user_context"); + if (val && typeof val === "object" && "value" in val && typeof (val as any).value === "string") { + return (val as { value: string }).value; + } + return null; + } catch (error) { + console.error("Failed to fetch user context", error); + return null; + } +} + + + + async function generateTitleDirect( enhancedContent: string, targetSessionId: string, @@ -448,11 +466,14 @@ export function useEnhanceMutation({ let customInstruction = selectedTemplate?.description; + const userContext = (await getUserContext()) ?? ""; + const systemMessage = await templateCommands.render( "enhance.system", { config, type, + userContext, // Pass userHeaders when using H1 headers, templateInfo otherwise ...(shouldUseH1Headers ? { userHeaders: h1Headers } diff --git a/apps/desktop/src/components/settings/components/tab-icon.tsx b/apps/desktop/src/components/settings/components/tab-icon.tsx index 6ea708d10..845af49c6 100644 --- a/apps/desktop/src/components/settings/components/tab-icon.tsx +++ b/apps/desktop/src/components/settings/components/tab-icon.tsx @@ -9,6 +9,7 @@ import { MessageSquareIcon, SettingsIcon, SparklesIcon, + UserIcon, } from "lucide-react"; import { type Tab } from "./types"; @@ -56,6 +57,8 @@ export function TabIcon({ tab }: { tab: Tab }) { return ; case "mcp": return ; + case "user-context": + return ; default: return null; } diff --git a/apps/desktop/src/components/settings/components/types.ts b/apps/desktop/src/components/settings/components/types.ts index ac5d0848e..960441941 100644 --- a/apps/desktop/src/components/settings/components/types.ts +++ b/apps/desktop/src/components/settings/components/types.ts @@ -9,6 +9,7 @@ import { NetworkIcon, Settings, Sparkles, + User, Volume2, } from "lucide-react"; @@ -23,7 +24,8 @@ export type Tab = | "feedback" | "integrations" | "mcp" - | "billing"; + | "billing" + | "user-context"; export const TABS: { name: Tab; icon: LucideIcon }[] = [ { name: "general", icon: Settings }, @@ -37,4 +39,5 @@ export const TABS: { name: Tab; icon: LucideIcon }[] = [ { name: "mcp", icon: NetworkIcon }, { name: "billing", icon: CreditCard }, { name: "feedback", icon: BlocksIcon }, + { name: "user-context", icon: User }, ]; diff --git a/apps/desktop/src/components/settings/views/user-context.tsx b/apps/desktop/src/components/settings/views/user-context.tsx new file mode 100644 index 000000000..e69cb9e75 --- /dev/null +++ b/apps/desktop/src/components/settings/views/user-context.tsx @@ -0,0 +1,93 @@ +import { Button } from "@hypr/ui/components/ui/button"; +import { Textarea } from "@hypr/ui/components/ui/textarea"; +import { toast } from "@hypr/ui/components/ui/toast"; +import { load } from "@tauri-apps/plugin-store"; +import React, { useEffect, useRef, useState } from "react"; + +export default function UserContext() { + const [isLoading, setIsLoading] = useState(false); + const textAreaRef = useRef(null); + const [userContext, setUserContext] = useState<{ value: string } | null>(null); + + const showUserContextToast = (content: string) => { + toast({ + id: "user-context", + title: "User Context", + content: content, + dismissible: true, + }); + }; + + const getStore = async () => { + return await load("store.json", { autoSave: false }); + }; + + const getUserContext = async (): Promise<{ value: string } | null> => { + let store = await getStore(); + let userContext = await store.get("user_context"); + + return userContext as { value: string } | null; + }; + + const handleSave = async () => { + try { + setIsLoading(true); + let store = await getStore(); + + const value = textAreaRef.current?.value?.trim(); + + if (!store) { + showUserContextToast("Failed to retrieve user store"); + setIsLoading(false); + return; + } + + if (!value) { + showUserContextToast("Please enter some content before saving."); + setIsLoading(false); + return; + } + + store.set("user_context", { value }); + await store.save(); + showUserContextToast("User context saved successfully"); + setIsLoading(false); + } catch (error) { + setIsLoading(false); + console.log("Failed to save user context with error ", error); + } + }; + + useEffect(() => { + getUserContext().then((val) => { + setUserContext(val); + }).catch((e) => { + console.log(e); + }); + }, []); + + return ( +
+
+

User Context

+
+ + +
+ +
+
+ ); +} diff --git a/apps/desktop/src/routes/app.settings.tsx b/apps/desktop/src/routes/app.settings.tsx index b4001dbf7..8e0f0a809 100644 --- a/apps/desktop/src/routes/app.settings.tsx +++ b/apps/desktop/src/routes/app.settings.tsx @@ -19,6 +19,7 @@ import { Sound, TemplatesView, } from "@/components/settings/views"; +import UserContext from "@/components/settings/views/user-context"; import { cn } from "@hypr/ui/lib/utils"; const schema = z.object({ @@ -71,6 +72,8 @@ function Component() { return t`License`; case "mcp": return t`MCP`; + case "user-context": + return t`User Context`; default: return tab; } @@ -140,6 +143,7 @@ function Component() { {search.tab === "integrations" && } {search.tab === "mcp" && } {search.tab === "billing" && } + {search.tab === "user-context" && } diff --git a/crates/template/assets/enhance.system.jinja b/crates/template/assets/enhance.system.jinja index add87dab0..b02178d11 100644 --- a/crates/template/assets/enhance.system.jinja +++ b/crates/template/assets/enhance.system.jinja @@ -56,6 +56,15 @@ You will be given multiple inputs from the user. Below are useful information th - Meeting Information (txt) - Raw Note (txt) - Meeting Transcript (txt) +- User Context (txt) + +-The User Context contains important background details about the user, such as their role, company, ongoing projects, style/tone preferences, and domain terminology. +-You must always take this into account when generating enhanced notes to make them more relevant and personalized. + +{% if userContext %} +Here is the User Context: +{{ userContext | trim }} +{% endif %} # About Raw Notes