From 3abb4313a756779b5c18dc98757179a6423ea16c Mon Sep 17 00:00:00 2001 From: steven-mpawulo Date: Wed, 20 Aug 2025 11:36:22 +0300 Subject: [PATCH 1/3] works on adding user context --- .vscode/settings.json | 3 + .../src/components/editor-area/index.tsx | 21 +++++ .../settings/components/tab-icon.tsx | 3 + .../components/settings/components/types.ts | 5 +- .../settings/views/user-context.tsx | 90 +++++++++++++++++++ apps/desktop/src/routes/app.settings.tsx | 4 + crates/template/assets/enhance.system.jinja | 7 ++ 7 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 apps/desktop/src/components/settings/views/user-context.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 5d5d68686f..2300c75e45 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 0dab6bc90d..0f9bf3a26e 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<{ value: string } | null> => { + try { + const store = await load("store.json", { autoSave: false }) + const val = await store.get("user_context") + if (val && typeof val === "object" && "value" in val) { + return val as { value: string } + } + 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; + let 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 6ea708d103..845af49c6b 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 ac5d0848e6..960441941d 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 0000000000..9752e072b1 --- /dev/null +++ b/apps/desktop/src/components/settings/views/user-context.tsx @@ -0,0 +1,90 @@ +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(); + + if (!store) { + showUserContextToast("Failed to retrieve user store"); + setIsLoading(false); + return; + } + + if (!textAreaRef?.current?.value) { + showUserContextToast("Failed to save user context"); + setIsLoading(false); + return; + } + + store.set("user_context", { value: textAreaRef?.current?.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 b4001dbf7d..8e0f0a8094 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 add87dab0c..658341719b 100644 --- a/crates/template/assets/enhance.system.jinja +++ b/crates/template/assets/enhance.system.jinja @@ -63,6 +63,13 @@ You will be given multiple inputs from the user. Below are useful information th - The beginning of a raw note may include agenda items, discussion topics, and preliminary questions. - Primarily consist of key phrases or sentences the user wants to remember, though they may also contain random or extraneous words. - May sometimes be empty. +- 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. + +Here is the User Context: +{{ userContext}} # Enhanced Note Format From eec716743568d4b3462b9ef309b5be05d3d24703 Mon Sep 17 00:00:00 2001 From: steven-mpawulo Date: Wed, 20 Aug 2025 12:16:33 +0300 Subject: [PATCH 2/3] updates fetching the user context and also properly adds it to the system template --- .../src/components/editor-area/index.tsx | 18 +++++++++--------- crates/template/assets/enhance.system.jinja | 16 +++++++++------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/desktop/src/components/editor-area/index.tsx b/apps/desktop/src/components/editor-area/index.tsx index 0f9bf3a26e..80accf5d94 100644 --- a/apps/desktop/src/components/editor-area/index.tsx +++ b/apps/desktop/src/components/editor-area/index.tsx @@ -29,17 +29,17 @@ import { FloatingButton } from "./floating-button"; import { NoteHeader } from "./note-header"; import { TextSelectionPopover } from "./text-selection-popover"; -const getUserContext = async (): Promise<{ value: string } | null> => { +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) { - return val as { value: string } + 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 + return null; } catch (error) { - console.error("Failed to fetch user context", error) - return null + console.error("Failed to fetch user context", error); + return null; } } @@ -466,7 +466,7 @@ export function useEnhanceMutation({ let customInstruction = selectedTemplate?.description; - let userContext = await getUserContext() || ""; + const userContext = (await getUserContext()) ?? ""; const systemMessage = await templateCommands.render( "enhance.system", diff --git a/crates/template/assets/enhance.system.jinja b/crates/template/assets/enhance.system.jinja index 658341719b..b02178d11e 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 @@ -63,13 +72,6 @@ You will be given multiple inputs from the user. Below are useful information th - The beginning of a raw note may include agenda items, discussion topics, and preliminary questions. - Primarily consist of key phrases or sentences the user wants to remember, though they may also contain random or extraneous words. - May sometimes be empty. -- 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. - -Here is the User Context: -{{ userContext}} # Enhanced Note Format From b32e5b4544fcb31cdc0b8dc7938fee2b3ac4a70e Mon Sep 17 00:00:00 2001 From: steven-mpawulo Date: Wed, 20 Aug 2025 12:25:32 +0300 Subject: [PATCH 3/3] trims the user context value, updates the message displayed when the user context is empty and also adds a default value to the text area field --- .../settings/views/user-context.tsx | 145 +++++++++--------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/apps/desktop/src/components/settings/views/user-context.tsx b/apps/desktop/src/components/settings/views/user-context.tsx index 9752e072b1..e69cb9e757 100644 --- a/apps/desktop/src/components/settings/views/user-context.tsx +++ b/apps/desktop/src/components/settings/views/user-context.tsx @@ -5,86 +5,89 @@ 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 [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 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 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"); + const getUserContext = async (): Promise<{ value: string } | null> => { + let store = await getStore(); + let userContext = await store.get("user_context"); - return userContext as { value: string } | null; - }; + return userContext as { value: string } | null; + }; - const handleSave = async () => { - try { - setIsLoading(true); - let store = await getStore(); + const handleSave = async () => { + try { + setIsLoading(true); + let store = await getStore(); - if (!store) { - showUserContextToast("Failed to retrieve user store"); - setIsLoading(false); - return; - } + const value = textAreaRef.current?.value?.trim(); - if (!textAreaRef?.current?.value) { - showUserContextToast("Failed to save user context"); - setIsLoading(false); - return; - } + if (!store) { + showUserContextToast("Failed to retrieve user store"); + setIsLoading(false); + return; + } - store.set("user_context", { value: textAreaRef?.current?.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); - } - }; + if (!value) { + showUserContextToast("Please enter some content before saving."); + setIsLoading(false); + return; + } - useEffect(() => { - getUserContext().then((val) => { - setUserContext(val); - }).catch((e) => { - console.log(e); - }); - }, []); + 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); + } + }; - return ( -
-
-

User Context

-
- + useEffect(() => { + getUserContext().then((val) => { + setUserContext(val); + }).catch((e) => { + console.log(e); + }); + }, []); -
- -
-
- ); + return ( +
+
+

User Context

+
+ + +
+ +
+
+ ); }