@@ -40,21 +40,13 @@ interface ChatMessage {
4040}
4141
4242function AgentConnectPrompt ( ) {
43- const { status, error, connect, gatewayUrl } = useGateway ( )
44- const [ url , setUrl ] = useState ( '' )
43+ const { status, error, connect } = useGateway ( )
44+ const [ showManual , setShowManual ] = useState ( false )
45+ const [ url , setUrl ] = useState ( 'ws://localhost:18789' )
4546 const [ password , setPassword ] = useState ( '' )
46- const [ showPassword , setShowPassword ] = useState ( false )
4747
4848 const isConnecting = status === 'connecting' || status === 'authenticating'
4949
50- useEffect ( ( ) => {
51- try {
52- const savedUrl = localStorage . getItem ( 'code-flow:gateway-url' )
53- if ( savedUrl && ! url ) setUrl ( savedUrl )
54- } catch { }
55- if ( gatewayUrl && ! url ) setUrl ( gatewayUrl )
56- } , [ gatewayUrl ] )
57-
5850 const handleConnect = ( ) => {
5951 if ( ! url . trim ( ) ) return
6052 connect ( url . trim ( ) , password )
@@ -64,81 +56,84 @@ function AgentConnectPrompt() {
6456 < div className = "flex flex-col items-center justify-center text-center py-8 px-4" >
6557 < div className = "relative mb-5" >
6658 < div className = "w-14 h-14 rounded-2xl bg-gradient-to-br from-[color-mix(in_srgb,var(--brand)_20%,transparent)] to-[color-mix(in_srgb,var(--brand)_6%,transparent)] border border-[color-mix(in_srgb,var(--brand)_25%,transparent)] flex items-center justify-center shadow-lg" >
67- < Icon icon = " lucide:cpu" width = { 28 } height = { 28 } className = " text-[var(--brand)]" />
59+ < Icon icon = { isConnecting ? ' lucide:loader-2' : 'lucide: cpu' } width = { 28 } height = { 28 } className = { ` text-[var(--brand)] ${ isConnecting ? 'animate-spin' : '' } ` } />
6860 </ div >
69- < span className = { `absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 rounded-full border-2 border-[var(--sidebar- bg)] ${
61+ < span className = { `absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 rounded-full border-2 border-[var(--bg)] ${
7062 isConnecting ? 'bg-[var(--warning,#eab308)] animate-pulse' : 'bg-[var(--text-disabled)]'
7163 } `} />
7264 </ div >
7365
74- < h3 className = "text-[16px] font-semibold text-[var(--text-primary)] mb-1.5" > Connect to Gateway</ h3 >
75- < p className = "text-[13px] text-[var(--text-tertiary)] leading-relaxed mb-5 max-w-[280px]" >
76- Your OpenClaw gateway powers the AI agent. Connect to enable completions, chat, and slash commands.
77- </ p >
78-
79- < div className = "w-full max-w-[300px] space-y-2.5" >
80- < input
81- type = "text"
82- value = { url || 'ws://openclaw.local:18789' }
83- onChange = { e => setUrl ( e . target . value ) }
84- onKeyDown = { e => { if ( e . key === 'Enter' ) handleConnect ( ) } }
85- placeholder = "ws://openclaw.local:18789"
86- className = "w-full px-3 py-2.5 rounded-lg bg-[var(--bg)] border border-[var(--border)] text-[13px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--border-focus)] transition-colors"
87- disabled = { isConnecting }
88- />
89- < div className = "relative" >
90- < input
91- type = { showPassword ? 'text' : 'password' }
92- value = { password }
93- onChange = { e => setPassword ( e . target . value ) }
94- onKeyDown = { e => { if ( e . key === 'Enter' ) handleConnect ( ) } }
95- placeholder = "Password (optional)"
96- className = "w-full px-3 py-2.5 pr-9 rounded-lg bg-[var(--bg)] border border-[var(--border)] text-[13px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--border-focus)] transition-colors"
97- disabled = { isConnecting }
98- />
99- < button
100- onClick = { ( ) => setShowPassword ( v => ! v ) }
101- className = "absolute right-2.5 top-1/2 -translate-y-1/2 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] cursor-pointer p-0.5"
102- tabIndex = { - 1 }
103- >
104- < Icon icon = { showPassword ? 'lucide:eye-off' : 'lucide:eye' } width = { 14 } height = { 14 } />
105- </ button >
106- </ div >
107- < button
108- onClick = { handleConnect }
109- disabled = { ! url . trim ( ) || isConnecting }
110- className = "w-full flex items-center justify-center gap-2 py-2.5 rounded-lg text-[13px] font-medium transition-all cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
111- style = { {
112- backgroundColor : 'var(--brand)' ,
113- color : 'var(--brand-contrast, #fff)' ,
114- } }
115- >
116- { isConnecting ? (
117- < Icon icon = "lucide:loader-2" width = { 14 } height = { 14 } className = "animate-spin" />
118- ) : (
119- < Icon icon = "lucide:plug" width = { 14 } height = { 14 } />
66+ { isConnecting ? (
67+ < >
68+ < h3 className = "text-[16px] font-semibold text-[var(--text-primary)] mb-1.5" > Connecting…</ h3 >
69+ < p className = "text-[13px] text-[var(--text-tertiary)]" > Looking for OpenClaw gateway</ p >
70+ </ >
71+ ) : (
72+ < >
73+ < h3 className = "text-[16px] font-semibold text-[var(--text-primary)] mb-1.5" > Gateway not found</ h3 >
74+ < p className = "text-[13px] text-[var(--text-tertiary)] leading-relaxed mb-4 max-w-[280px]" >
75+ Make sure OpenClaw is running on this machine.
76+ </ p >
77+
78+ < div className = "space-y-2.5 w-full max-w-[280px]" >
79+ < button
80+ onClick = { ( ) => connect ( 'ws://localhost:18789' , '' ) }
81+ className = "w-full flex items-center justify-center gap-2 py-2.5 rounded-lg text-[13px] font-medium transition-all cursor-pointer"
82+ style = { { backgroundColor : 'var(--brand)' , color : 'var(--brand-contrast, #fff)' } }
83+ >
84+ < Icon icon = "lucide:refresh-cw" width = { 14 } height = { 14 } />
85+ Retry connection
86+ </ button >
87+
88+ < button
89+ onClick = { ( ) => setShowManual ( v => ! v ) }
90+ className = "w-full flex items-center justify-center gap-1.5 py-2 text-[12px] text-[var(--text-tertiary)] hover:text-[var(--text-secondary)] transition-colors cursor-pointer"
91+ >
92+ < Icon icon = "lucide:settings-2" width = { 12 } height = { 12 } />
93+ { showManual ? 'Hide' : 'Manual connection' }
94+ </ button >
95+
96+ { showManual && (
97+ < div className = "space-y-2 pt-1" >
98+ < input
99+ type = "text"
100+ value = { url }
101+ onChange = { e => setUrl ( e . target . value ) }
102+ onKeyDown = { e => { if ( e . key === 'Enter' ) handleConnect ( ) } }
103+ placeholder = "ws://localhost:18789"
104+ className = "w-full px-3 py-2 rounded-lg bg-[var(--bg)] border border-[var(--border)] text-[12px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--border-focus)]"
105+ />
106+ < input
107+ type = "password"
108+ value = { password }
109+ onChange = { e => setPassword ( e . target . value ) }
110+ onKeyDown = { e => { if ( e . key === 'Enter' ) handleConnect ( ) } }
111+ placeholder = "Password (if set)"
112+ className = "w-full px-3 py-2 rounded-lg bg-[var(--bg)] border border-[var(--border)] text-[12px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--border-focus)]"
113+ />
114+ < button
115+ onClick = { handleConnect }
116+ disabled = { ! url . trim ( ) }
117+ className = "w-full py-2 rounded-lg text-[12px] font-medium bg-[var(--bg-subtle)] text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] transition-colors cursor-pointer disabled:opacity-50"
118+ >
119+ Connect
120+ </ button >
121+ </ div >
122+ ) }
123+ </ div >
124+
125+ { error && (
126+ < div className = "flex items-start gap-2 mt-3 text-[11px] text-[var(--color-deletions)] max-w-[280px] text-left" >
127+ < Icon icon = "lucide:alert-circle" width = { 12 } height = { 12 } className = "shrink-0 mt-0.5" />
128+ < span > { error } </ span >
129+ </ div >
120130 ) }
121- { isConnecting ? 'Connecting…' : 'Connect' }
122- </ button >
123- </ div >
124131
125- { status === 'error' && error && (
126- < div className = "flex items-start gap-2 mt-4 text-[12px] text-[var(--color-deletions)] max-w-[300px] text-left" >
127- < Icon icon = "lucide:alert-circle" width = { 14 } height = { 14 } className = "shrink-0 mt-0.5" />
128- < span className = "leading-relaxed" > { error } </ span >
129- </ div >
132+ < p className = "mt-5 text-[11px] text-[var(--text-disabled)]" >
133+ Run < code className = "px-1 py-0.5 bg-[var(--bg-secondary)] rounded text-[var(--brand)]" > openclaw gateway start</ code > to start
134+ </ p >
135+ </ >
130136 ) }
131-
132- < div className = "mt-6 space-y-2 text-[12px] text-[var(--text-disabled)] max-w-[260px]" >
133- < div className = "flex items-center gap-2" >
134- < Icon icon = "lucide:shield" width = { 13 } height = { 13 } />
135- < span > Runs locally. Code never leaves your machine</ span >
136- </ div >
137- < div className = "flex items-center gap-2" >
138- < Icon icon = "lucide:zap" width = { 13 } height = { 13 } />
139- < span > Works with any LLM provider</ span >
140- </ div >
141- </ div >
142137 </ div >
143138 )
144139}
0 commit comments