@@ -2,6 +2,8 @@ import { useEffect, useRef, useState } from 'react'
22import { useUiStore } from '../stores/uiStore'
33import { usePaneStore } from '../stores/paneStore'
44
5+ type Mode = 'new' | 'existing'
6+
57export function CreateDialog ( ) : React . JSX . Element | null {
68 const createDialog = useUiStore ( ( s ) => s . createDialog )
79 const setCreateDialog = useUiStore ( ( s ) => s . setCreateDialog )
@@ -13,8 +15,11 @@ export function CreateDialog(): React.JSX.Element | null {
1315 const paneDetail = useUiStore ( ( s ) => s . paneDetail )
1416 const setPanes = usePaneStore ( ( s ) => s . setPanes )
1517
18+ const [ mode , setMode ] = useState < Mode > ( 'new' )
19+ const [ sessionName , setSessionName ] = useState ( '' )
1620 const [ highlightIndex , setHighlightIndex ] = useState ( 0 )
1721 const listRef = useRef < HTMLUListElement > ( null )
22+ const inputRef = useRef < HTMLInputElement > ( null )
1823
1924 // Sync highlightIndex when dialog opens or sessions change
2025 useEffect ( ( ) => {
@@ -29,22 +34,47 @@ export function CreateDialog(): React.JSX.Element | null {
2934 item ?. scrollIntoView ( { block : 'nearest' } )
3035 } , [ highlightIndex ] )
3136
37+ // Focus input when mode switches to 'new'
38+ useEffect ( ( ) => {
39+ if ( mode === 'new' ) {
40+ requestAnimationFrame ( ( ) => inputRef . current ?. focus ( ) )
41+ }
42+ } , [ mode ] )
43+
3244 const closeDialog = ( ) : void => {
3345 setCreateDialog ( false )
46+ setSessionName ( '' )
47+ setMode ( 'new' )
48+ requestAnimationFrame ( ( ) => {
49+ document . querySelector < HTMLTextAreaElement > ( '.textarea' ) ?. focus ( )
50+ } )
51+ }
52+
53+ const handleCreateNew = async ( ) : Promise < void > => {
54+ const name = sessionName . trim ( )
55+ if ( ! name ) return
56+ const r = await window . api . createNewSession ( name , newSessionCommand , paneDetail ?. cwd )
57+ if ( r . success ) {
58+ setCreateDialog ( false )
59+ setSessionName ( '' )
60+ useUiStore . getState ( ) . flashStatus ( `Created session "${ name } " with ${ newSessionCommand } ` , true )
61+ const result = await window . api . listSessions ( )
62+ setPanes ( result )
63+ } else {
64+ useUiStore . getState ( ) . flashStatus ( r . error ?? 'Failed' , false )
65+ }
3466 requestAnimationFrame ( ( ) => {
3567 document . querySelector < HTMLTextAreaElement > ( '.textarea' ) ?. focus ( )
3668 } )
3769 }
3870
39- const handleCreate = async ( ) : Promise < void > => {
71+ const handleAddToExisting = async ( ) : Promise < void > => {
4072 const target = tmuxSessions [ highlightIndex ] ?? newSessionTarget
4173 if ( ! target ) return
4274 const r = await window . api . createSession ( target , newSessionCommand , paneDetail ?. cwd )
4375 if ( r . success ) {
4476 setCreateDialog ( false )
45- useUiStore
46- . getState ( )
47- . flashStatus ( `Created ${ newSessionCommand } in ${ target } ` , true )
77+ useUiStore . getState ( ) . flashStatus ( `Added ${ newSessionCommand } to ${ target } ` , true )
4878 const result = await window . api . listSessions ( )
4979 setPanes ( result )
5080 } else {
@@ -58,24 +88,30 @@ export function CreateDialog(): React.JSX.Element | null {
5888 if ( ! createDialog ) return null
5989
6090 const handleKeyDown = ( e : React . KeyboardEvent ) : void => {
91+ if ( ( e . target as HTMLElement ) . tagName === 'INPUT' ) return
92+
6193 switch ( e . key ) {
6294 case 'j' :
6395 case 'ArrowDown' :
6496 e . preventDefault ( )
65- setHighlightIndex ( ( i ) => {
66- const next = Math . min ( i + 1 , tmuxSessions . length - 1 )
67- setNewSessionTarget ( tmuxSessions [ next ] )
68- return next
69- } )
97+ if ( mode === 'existing' ) {
98+ setHighlightIndex ( ( i ) => {
99+ const next = Math . min ( i + 1 , tmuxSessions . length - 1 )
100+ setNewSessionTarget ( tmuxSessions [ next ] )
101+ return next
102+ } )
103+ }
70104 break
71105 case 'k' :
72106 case 'ArrowUp' :
73107 e . preventDefault ( )
74- setHighlightIndex ( ( i ) => {
75- const next = Math . max ( i - 1 , 0 )
76- setNewSessionTarget ( tmuxSessions [ next ] )
77- return next
78- } )
108+ if ( mode === 'existing' ) {
109+ setHighlightIndex ( ( i ) => {
110+ const next = Math . max ( i - 1 , 0 )
111+ setNewSessionTarget ( tmuxSessions [ next ] )
112+ return next
113+ } )
114+ }
79115 break
80116 case 'h' :
81117 e . preventDefault ( )
@@ -85,9 +121,14 @@ export function CreateDialog(): React.JSX.Element | null {
85121 e . preventDefault ( )
86122 setNewSessionCommand ( 'codex' )
87123 break
124+ case 'Tab' :
125+ e . preventDefault ( )
126+ setMode ( ( m ) => ( m === 'new' ? 'existing' : 'new' ) )
127+ break
88128 case 'Enter' :
89129 e . preventDefault ( )
90- handleCreate ( )
130+ if ( mode === 'new' ) handleCreateNew ( )
131+ else handleAddToExisting ( )
91132 break
92133 case 'Escape' :
93134 e . preventDefault ( )
@@ -112,29 +153,68 @@ export function CreateDialog(): React.JSX.Element | null {
112153 < div className = "pane-popup detail-popup" onClick = { ( e ) => e . stopPropagation ( ) } >
113154 < div className = "pane-popup-header" >
114155 < span className = "pane-popup-title" > New Session</ span >
115- < span className = "create-dialog-hint" > j/k: select · h/l: cmd · Enter: create</ span >
156+ < span className = "create-dialog-hint" > Tab: switch · h/l: cmd · Enter: create</ span >
116157 < button className = "pane-popup-close" onClick = { closeDialog } >
117158 Esc
118159 </ button >
119160 </ div >
120161 < div className = "create-session-form" >
121- < div className = "setting-row" >
122- < span className = "setting-label" > Session</ span >
123- < ul className = "create-session-list" ref = { listRef } >
124- { tmuxSessions . map ( ( s , i ) => (
125- < li
126- key = { s }
127- className = { `create-session-item ${ i === highlightIndex ? 'create-session-item-active' : '' } ` }
128- onClick = { ( ) => {
129- setHighlightIndex ( i )
130- setNewSessionTarget ( s )
131- } }
132- >
133- { s }
134- </ li >
135- ) ) }
136- </ ul >
162+ { /* Mode tabs */ }
163+ < div className = "create-mode-tabs" >
164+ < button
165+ className = { `create-mode-tab ${ mode === 'new' ? 'create-mode-tab-active' : '' } ` }
166+ onClick = { ( ) => setMode ( 'new' ) }
167+ >
168+ New Session
169+ </ button >
170+ < button
171+ className = { `create-mode-tab ${ mode === 'existing' ? 'create-mode-tab-active' : '' } ` }
172+ onClick = { ( ) => setMode ( 'existing' ) }
173+ >
174+ Add to Existing
175+ </ button >
137176 </ div >
177+
178+ { mode === 'new' ? (
179+ < div className = "setting-row" >
180+ < span className = "setting-label" > Name</ span >
181+ < input
182+ ref = { inputRef }
183+ className = "git-commit-input"
184+ placeholder = "Session name..."
185+ value = { sessionName }
186+ onChange = { ( e ) => setSessionName ( e . target . value ) }
187+ onKeyDown = { ( e ) => {
188+ e . stopPropagation ( )
189+ if ( e . key === 'Enter' && sessionName . trim ( ) ) {
190+ handleCreateNew ( )
191+ }
192+ if ( e . key === 'Escape' ) {
193+ closeDialog ( )
194+ }
195+ } }
196+ />
197+ </ div >
198+ ) : (
199+ < div className = "setting-row" >
200+ < span className = "setting-label" > Session</ span >
201+ < ul className = "create-session-list" ref = { listRef } >
202+ { tmuxSessions . map ( ( s , i ) => (
203+ < li
204+ key = { s }
205+ className = { `create-session-item ${ i === highlightIndex ? 'create-session-item-active' : '' } ` }
206+ onClick = { ( ) => {
207+ setHighlightIndex ( i )
208+ setNewSessionTarget ( s )
209+ } }
210+ >
211+ { s }
212+ </ li >
213+ ) ) }
214+ </ ul >
215+ </ div >
216+ ) }
217+
138218 < div className = "setting-row" >
139219 < span className = "setting-label" > Command</ span >
140220 < div className = "theme-segment" >
@@ -154,10 +234,10 @@ export function CreateDialog(): React.JSX.Element | null {
154234 </ div >
155235 < button
156236 className = "git-btn create-session-btn"
157- disabled = { ! tmuxSessions [ highlightIndex ] }
158- onClick = { handleCreate }
237+ disabled = { mode === 'new' ? ! sessionName . trim ( ) : ! tmuxSessions [ highlightIndex ] }
238+ onClick = { mode === 'new' ? handleCreateNew : handleAddToExisting }
159239 >
160- Create
240+ { mode === 'new' ? ' Create' : 'Add' }
161241 </ button >
162242 </ div >
163243 </ div >
0 commit comments