22
33import type React from "react" ;
44
5- import { useState , useRef , useEffect } from "react" ;
65import { Button } from "@/components/ui/button" ;
76import { Card } from "@/components/ui/card" ;
87import { Input } from "@/components/ui/input" ;
@@ -13,12 +12,11 @@ import {
1312 SelectTrigger ,
1413 SelectValue ,
1514} from "@/components/ui/select" ;
16- import { MessageSquare , Send , Settings , User , Bot } from "lucide-react" ;
17- import { OpenRouterCore } from "@openrouter/sdk/core" ;
18- import { chatSend , SendAcceptEnum } from "@openrouter/sdk/funcs/chatSend" ;
19- import { OPENROUTER_KEY_LOCALSTORAGE_KEY } from "@/lib/config" ;
15+ import { useApiKey } from "@/lib/hooks/use-api-key" ;
16+ import { useOpenRouter } from "@/lib/hooks/use-openrouter-client" ;
2017import { Message as OpenRouterMessageRequest } from "@openrouter/sdk/models" ;
21- import useLocalStorage from "@/lib/hooks/use-local-storage" ;
18+ import { Bot , MessageSquare , Send , Settings , User } from "lucide-react" ;
19+ import { useEffect , useRef , useState } from "react" ;
2220import { NotConnectedDialog } from "./_dialogs/not-connected" ;
2321
2422type Message = OpenRouterMessageRequest & {
@@ -27,9 +25,8 @@ type Message = OpenRouterMessageRequest & {
2725} ;
2826
2927export default function Page ( ) {
30- const { value : apiKey , isPending : isApiKeyPending } = useLocalStorage <
31- string | null
32- > ( OPENROUTER_KEY_LOCALSTORAGE_KEY , null ) ;
28+ const { client : openRouter } = useOpenRouter ( ) ;
29+ const [ apiKey ] = useApiKey ( ) ;
3330
3431 const [ messages , setMessages ] = useState < Message [ ] > ( [
3532 {
@@ -39,11 +36,12 @@ export default function Page() {
3936 "Hello! I'm your AI assistant powered by OpenRouter. I can help you with a wide variety of tasks. What would you like to know or discuss today?" ,
4037 } ,
4138 ] ) ;
42- const [ input , setInput ] = useState ( "" ) ;
4339 const [ selectedModel , setSelectedModel ] = useState ( "gpt-4" ) ;
4440 const [ isLoading , setIsLoading ] = useState ( false ) ;
4541 const messagesEndRef = useRef < HTMLDivElement > ( null ) ;
4642
43+ const inputRef = useRef < HTMLInputElement > ( null ) ;
44+
4745 const scrollToBottom = ( ) => {
4846 messagesEndRef . current ?. scrollIntoView ( { behavior : "smooth" } ) ;
4947 } ;
@@ -53,6 +51,9 @@ export default function Page() {
5351 } , [ messages ] ) ;
5452
5553 const handleSend = async ( ) => {
54+ const input = inputRef . current ?. value ;
55+ if ( ! input ) return ;
56+
5657 setIsLoading ( true ) ;
5758
5859 if ( ! input . trim ( ) || isLoading ) return ;
@@ -72,7 +73,7 @@ export default function Page() {
7273 ] ;
7374
7475 setMessages ( updatedMessages ) ;
75- setInput ( "" ) ;
76+ inputRef . current ! . value = "" ;
7677
7778 // Add an empty assistant message to stream into
7879 const assistantMessage : Message = {
@@ -88,27 +89,16 @@ export default function Page() {
8889 throw new Error ( "API key is required but not present." ) ;
8990 }
9091
91- const openRouter = new OpenRouterCore ( { apiKey } ) ;
92-
93- const result = await chatSend (
94- openRouter ,
95- {
96- model : "openai/gpt-4o" ,
97- maxTokens : 1000 ,
98- messages : updatedMessages ,
99- stream : true ,
100- } ,
101- { acceptHeaderOverride : SendAcceptEnum . textEventStream } ,
102- ) ;
103-
104- if ( ! result . ok ) {
105- alert ( "Error: " + result . error . message ) ;
106- return setIsLoading ( false ) ;
107- }
92+ const result = await openRouter . chat . send ( {
93+ model : "openai/gpt-4o" ,
94+ maxTokens : 1000 ,
95+ messages : updatedMessages ,
96+ stream : true ,
97+ } ) ;
10898
10999 // Stream chunks into the latest message
110100 const chunks : string [ ] = [ ] ;
111- for await ( const chunk of result . value ) {
101+ for await ( const chunk of result ) {
112102 chunks . push ( chunk . data . choices [ 0 ] . delta . content || "" ) ;
113103 setMessages ( ( prev ) => {
114104 const newMessages = [ ...prev ] ;
@@ -134,7 +124,7 @@ export default function Page() {
134124
135125 return (
136126 < >
137- < NotConnectedDialog open = { ! isApiKeyPending && apiKey === null } />
127+ < NotConnectedDialog open = { ! apiKey } />
138128 < div className = "flex flex-col h-screen bg-background" >
139129 { /* Header */ }
140130 < div className = "border-b border-border bg-card" >
@@ -221,18 +211,13 @@ export default function Page() {
221211 < div className = "border-border bg-card p-4 max-w-4xl mx-auto w-full" >
222212 < div className = "flex gap-2" >
223213 < Input
224- value = { input }
225- onChange = { ( e ) => setInput ( e . target . value ) }
214+ ref = { inputRef }
226215 onKeyPress = { handleKeyPress }
227216 placeholder = "Type your message..."
228217 className = "flex-1"
229218 disabled = { isLoading }
230219 />
231- < Button
232- onClick = { handleSend }
233- disabled = { ! input . trim ( ) || isLoading }
234- size = "icon"
235- >
220+ < Button onClick = { handleSend } disabled = { isLoading } size = "icon" >
236221 < Send className = "h-4 w-4" />
237222 </ Button >
238223 </ div >
0 commit comments