From 8cc789d5aa1889145de21857841c418cd9687925 Mon Sep 17 00:00:00 2001 From: AntonCSalvador Date: Mon, 31 Mar 2025 15:48:29 -0400 Subject: [PATCH 1/4] added more toast error handling --- components/chat/Chat.tsx | 56 +++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/components/chat/Chat.tsx b/components/chat/Chat.tsx index ba4e7e3..393462e 100644 --- a/components/chat/Chat.tsx +++ b/components/chat/Chat.tsx @@ -25,29 +25,55 @@ export default function Chat() { })) const db = useDB() + + //ensure each notification only shows up once + const notifInit = useRef(false); // Fetch messages and API key from database. useEffect(() => { // TODO: HANDLE DATABASE ERRORS. + if (notifInit.current) return; + notifInit.current = true; + const fetchMessages = async () => { - const msgs = await db.history.readAll() - setMessages(msgs.map(msg => ({ - role : msg.role, - content : msg.content - } as Message))) - setIsTyping(false) + try{ + const msgs = await db.history.readAll() + setMessages(msgs.map(msg => ({ + role : msg.role, + content : msg.content + } as Message))) + } + catch{ + toast('Error: Failed to Load Chat Message', { + description: 'The database failed to load chat message' + }) + } + finally{ + setIsTyping(false) + } + } const fetchKey = async () => { - const keys = await db.keys.readAll() - if (keys.length === 0) { - // TODO: SHOW DIALOG - return + try{ + const keys = await db.keys.readAll() + if (keys.length === 0) { + toast('No API Key Found', { + description: 'Please go to settings and add your OpenRouter API Key' + }) + return + } + setClient(new OpenAI({ + baseURL: "https://openrouter.ai/api/v1", + dangerouslyAllowBrowser: true, + apiKey: keys[0].key_hash, + })) } - setClient(new OpenAI({ - baseURL: "https://openrouter.ai/api/v1", - dangerouslyAllowBrowser: true, - apiKey: keys[0].key_hash, - })) + catch(error){ + toast('Error: Failed to load API Key', { + description: 'Ensure your API Key is correct' + }) + } + } fetchMessages() fetchKey() From fef9063f245458a830208847f36f7cf662059ee3 Mon Sep 17 00:00:00 2001 From: AntonCSalvador Date: Mon, 31 Mar 2025 20:01:00 -0400 Subject: [PATCH 2/4] fixed linter errors --- components/chat/Chat.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/chat/Chat.tsx b/components/chat/Chat.tsx index 393462e..769b762 100644 --- a/components/chat/Chat.tsx +++ b/components/chat/Chat.tsx @@ -25,15 +25,15 @@ export default function Chat() { })) const db = useDB() - + //ensure each notification only shows up once - const notifInit = useRef(false); + const notifInit = useRef(false) // Fetch messages and API key from database. useEffect(() => { // TODO: HANDLE DATABASE ERRORS. - if (notifInit.current) return; - notifInit.current = true; + if (notifInit.current) return + notifInit.current = true const fetchMessages = async () => { try{ @@ -68,7 +68,7 @@ export default function Chat() { apiKey: keys[0].key_hash, })) } - catch(error){ + catch{ toast('Error: Failed to load API Key', { description: 'Ensure your API Key is correct' }) From 6e66e7d33eb2fb0729c9b4b12db25a5e535526b9 Mon Sep 17 00:00:00 2001 From: zeim839 Date: Wed, 9 Apr 2025 14:37:58 -0400 Subject: [PATCH 3/4] refactor: set Settings state by prop --- components/chat/Settings.tsx | 27 ++++++++------- components/navigation/RightNavigation.tsx | 42 ++++++++++++++--------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/components/chat/Settings.tsx b/components/chat/Settings.tsx index e6cf306..31560fd 100644 --- a/components/chat/Settings.tsx +++ b/components/chat/Settings.tsx @@ -1,24 +1,26 @@ 'use client' -import { useState, useEffect } from "react" +import { useState, useEffect, Dispatch, SetStateAction } from "react" import { Button } from "@/components/ui/button" -import { SettingsIcon } from "lucide-react" import { useDB } from "@/components/DatabaseProvider" import { toast } from "sonner" import { Keys } from "@/lib/controller/KeyController" import { Dialog, - DialogTrigger, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog" +type SettingsProps = { + onOpenChange: Dispatch> + open: boolean, +} + // Settings displays a modal asking the user to configure their // OpenRouter API key. -const Settings = () => { - const [open, setOpen] = useState(false) +const Settings = ({open, onOpenChange} : SettingsProps) => { const [apiKey, setApiKey] = useState("") const db = useDB() @@ -35,7 +37,7 @@ const Settings = () => { setApiKey("") return } - setApiKey(keys[0].id!.toString()) + setApiKey(keys[0].key_hash) } fetchKey() }, []) @@ -45,6 +47,10 @@ const Settings = () => { const handleSubmit = async () => { try { await db.keys.deleteAll() + if (apiKey === "") { + onOpenChange(false) + return + } await db.keys.create({ key_hash : apiKey, created_at : Math.floor(Date.now() / 1000) @@ -54,16 +60,11 @@ const Settings = () => { description: 'A database error prevented the key from being saved' }) } - setOpen(false) + onOpenChange(false) } return ( - - - - + Enter your OpenRouter API Key diff --git a/components/navigation/RightNavigation.tsx b/components/navigation/RightNavigation.tsx index 8f4178a..a5c3805 100644 --- a/components/navigation/RightNavigation.tsx +++ b/components/navigation/RightNavigation.tsx @@ -1,27 +1,35 @@ import { Button } from "@/components/ui/button" import { NavigationState } from "./NavigationState" -import { MessageSquareIcon, PlusIcon} from "lucide-react" +import { MessageSquareIcon, PlusIcon, SettingsIcon} from "lucide-react" +import { useState } from "react" + import Chat from "@/components/chat/Chat" import Settings from "../chat/Settings" // RightNavigation consists of the create note, LLM chat, and // settings buttons. -const RightNavigation = ({ state } : { state: NavigationState }) => ( -
- { /* Redirecting to the note page creates a new note */ } - - { /* Toggle the right sidebar */ } - - -
-) +const RightNavigation = ({ state } : { state: NavigationState }) => { + const [settingsOpen, setSettingsOpen] = useState(false) + return ( +
+ { /* Redirecting to the note page creates a new note */ } + + { /* Toggle the right sidebar */ } + + + +
+ ) +} // RightSidebar shows the LLM chat sidebar. const RightSidebar = () => ( From 30a646fc159c3a9200c63bb5ec87c1fae1962f14 Mon Sep 17 00:00:00 2001 From: zeim839 Date: Wed, 9 Apr 2025 14:38:24 -0400 Subject: [PATCH 4/4] feat: create SetUpKeyCard component --- components/chat/Chat.tsx | 65 ++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/components/chat/Chat.tsx b/components/chat/Chat.tsx index f87bb2c..26828b5 100644 --- a/components/chat/Chat.tsx +++ b/components/chat/Chat.tsx @@ -6,7 +6,11 @@ import { Stream, Message } from "@/lib/OpenRouter" import { MessageView } from "./Messages" import { useDB } from "@/components/DatabaseProvider" import { toast } from "sonner" +import { Button } from "@/components/ui/button" +import { openUrl } from '@tauri-apps/plugin-opener' + import SourceDropdown from "./SourceDropdown" +import Settings from "./Settings" import OpenAI from "openai" import { @@ -14,7 +18,8 @@ import { SendHorizontalIcon, GlobeIcon, WrenchIcon, - CheckIcon + CheckIcon, + KeyRoundIcon, } from "lucide-react" import { @@ -26,6 +31,33 @@ import { type FormEvent = React.KeyboardEvent +// SetUpKeyCard is a card that asks the user to set up their OpenRouter +// API key whenever it's not available. +export const SetUpKeyCard = () => { + const [settingsOpen, setSettingsOpen] = useState(false) + const openrouter = () => { + openUrl("https://openrouter.ai/") + } + return ( +
+ +

+ Configure API Key +

+

+ Configure your OpenRouter API key to begin sending messages. +

+
+ + +
+ +
+ ) +} + export default function Chat() { const [messages, setMessages] = useState<(Message|ToolCall)[]>([]) const [source, setSource] = useState('google/gemini-2.5-pro-exp-03-25:free') @@ -34,6 +66,8 @@ export default function Chat() { const [isStreaming, setIsStreaming] = useState(false) const [webSearchEnabled, setWebSearchEnabled] = useState(false) const [toolCallingEnabled, setToolCallingEnabled] = useState(true) + const [hasKey, setHasKey] = useState(false) + const [isLoading, setIsLoading] = useState(true) const [client, setClient] = useState(new OpenAI({ baseURL: "https://openrouter.ai/api/v1", dangerouslyAllowBrowser: true, @@ -42,15 +76,8 @@ export default function Chat() { const db = useDB() - //ensure each notification only shows up once - const notifInit = useRef(false) - // Fetch messages and API key from database. useEffect(() => { - // TODO: HANDLE DATABASE ERRORS. - if (notifInit.current) return - notifInit.current = true - const fetchMessages = async () => { try { const msgs = await db.history.readAll() @@ -76,12 +103,10 @@ export default function Chat() { finally {setIsTyping(false)} } const fetchKey = async () => { - try{ + try { const keys = await db.keys.readAll() if (keys.length === 0) { - toast('No API Key Found', { - description: 'Please go to settings and add your OpenRouter API Key' - }) + setHasKey(false) return } setClient(new OpenAI({ @@ -89,14 +114,15 @@ export default function Chat() { dangerouslyAllowBrowser: true, apiKey: keys[0].key_hash, })) + setHasKey(true) } - catch{ - toast('Error: Failed to load API Key', { + catch { + toast('Error: Failed to fetch API Key', { description: 'Ensure your API Key is correct' }) } - } + setIsLoading(false) fetchMessages() fetchKey() }, []) @@ -190,6 +216,15 @@ export default function Chat() { } } + if (isLoading) { + setIsLoading(false) + } + + // Show a "set up API key" card when OpenRouter key is not configured. + if (!hasKey) { + return + } + return (