11import { usePrevious } from "@uidotdev/usehooks" ;
2+ import { SparklesIcon } from "lucide-react" ;
23import { forwardRef , useEffect , useRef , useState } from "react" ;
34
45import { cn } from "@hypr/utils" ;
@@ -12,8 +13,9 @@ export const TitleInput = forwardRef<
1213 {
1314 tab : Extract < Tab , { type : "sessions" } > ;
1415 onNavigateToEditor ?: ( ) => void ;
16+ onGenerateTitle ?: ( ) => void ;
1517 }
16- > ( ( { tab, onNavigateToEditor } , ref ) => {
18+ > ( ( { tab, onNavigateToEditor, onGenerateTitle } , ref ) => {
1719 const {
1820 id : sessionId ,
1921 state : { view } ,
@@ -23,15 +25,19 @@ export const TitleInput = forwardRef<
2325 const wasGenerating = usePrevious ( isGenerating ) ;
2426 const [ showRevealAnimation , setShowRevealAnimation ] = useState ( false ) ;
2527
28+ const editorId = view ? "active" : "inactive" ;
29+ const internalRef = useRef < HTMLInputElement > ( null ) ;
30+ const inputRef = ( ref as React . RefObject < HTMLInputElement > ) || internalRef ;
31+
2632 useEffect ( ( ) => {
27- if ( wasGenerating && ! isGenerating && title ) {
33+ if ( wasGenerating && ! isGenerating ) {
2834 setShowRevealAnimation ( true ) ;
2935 const timer = setTimeout ( ( ) => {
3036 setShowRevealAnimation ( false ) ;
3137 } , 1000 ) ;
3238 return ( ) => clearTimeout ( timer ) ;
3339 }
34- } , [ wasGenerating , isGenerating , title ] ) ;
40+ } , [ wasGenerating , isGenerating ] ) ;
3541
3642 const handleEditTitle = main . UI . useSetPartialRowCallback (
3743 "sessions" ,
@@ -41,10 +47,6 @@ export const TitleInput = forwardRef<
4147 main . STORE_ID ,
4248 ) ;
4349
44- const editorId = view ? "active" : "inactive" ;
45- const internalRef = useRef < HTMLInputElement > ( null ) ;
46- const inputRef = ( ref as React . RefObject < HTMLInputElement > ) || internalRef ;
47-
4850 useEffect ( ( ) => {
4951 const handleMoveToTitlePosition = ( e : Event ) => {
5052 const customEvent = e as CustomEvent < { pixelWidth : number } > ;
@@ -172,20 +174,42 @@ export const TitleInput = forwardRef<
172174 ) ;
173175 }
174176
177+ const hasTitle = Boolean ( title ?. trim ( ) ) ;
178+ const showButton = hasTitle && onGenerateTitle ;
179+
175180 return (
176- < input
177- ref = { inputRef }
178- id = { `title-input-${ sessionId } -${ editorId } ` }
179- placeholder = "Untitled"
180- type = "text"
181- onChange = { ( e ) => handleEditTitle ( e . target . value ) }
182- onKeyDown = { handleKeyDown }
183- value = { title ?? "" }
184- className = { cn ( [
185- "w-full transition-opacity duration-200" ,
186- "border-none bg-transparent focus:outline-none" ,
187- "text-xl font-semibold placeholder:text-muted-foreground" ,
188- ] ) }
189- />
181+ < div className = "flex items-center gap-2 w-full" >
182+ < input
183+ ref = { inputRef }
184+ id = { `title-input-${ sessionId } -${ editorId } ` }
185+ placeholder = "Untitled"
186+ type = "text"
187+ onChange = { ( e ) => handleEditTitle ( e . target . value ) }
188+ onKeyDown = { handleKeyDown }
189+ value = { title ?? "" }
190+ className = { cn ( [
191+ "flex-1 min-w-0 transition-opacity duration-200" ,
192+ "border-none bg-transparent focus:outline-none" ,
193+ "text-xl font-semibold placeholder:text-muted-foreground" ,
194+ ] ) }
195+ />
196+ { showButton && (
197+ < button
198+ onClick = { ( e ) => {
199+ e . preventDefault ( ) ;
200+ e . stopPropagation ( ) ;
201+ onGenerateTitle ?.( ) ;
202+ } }
203+ onMouseDown = { ( e ) => e . preventDefault ( ) }
204+ className = { cn ( [
205+ "shrink-0 p-1" ,
206+ "text-muted-foreground hover:text-foreground" ,
207+ "opacity-50 hover:opacity-100 transition-opacity" ,
208+ ] ) }
209+ >
210+ < SparklesIcon className = "w-4 h-4" />
211+ </ button >
212+ ) }
213+ </ div >
190214 ) ;
191215} ) ;
0 commit comments