@@ -71,7 +71,6 @@ import {
7171function AgentConnectPrompt ( ) {
7272 const { status, error, connect } = useGateway ( )
7373 const isMobileDevice = typeof window !== 'undefined' && window . innerWidth <= 768
74- const [ showManual , setShowManual ] = useState ( isMobileDevice ) // auto-expand on mobile
7574 const [ url , setUrl ] = useState ( isMobileDevice ? '' : 'ws://localhost:18789' )
7675 const [ password , setPassword ] = useState ( '' )
7776
@@ -83,121 +82,135 @@ function AgentConnectPrompt() {
8382 }
8483
8584 return (
86- < div className = "flex flex-col items-center justify-center text-center py-8 px-4" >
87- < div className = "relative mb-5" >
88- < 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" >
85+ < div className = "flex flex-1 flex-col items-center justify-center text-center px-6" >
86+ { /* Animated connection icon */ }
87+ < div className = "relative mb-6" >
88+ < div
89+ className = { `w-16 h-16 rounded-[20px] flex items-center justify-center transition-all duration-500 ${
90+ isConnecting
91+ ? 'bg-[color-mix(in_srgb,var(--warning,#eab308)_12%,transparent)] border border-[color-mix(in_srgb,var(--warning,#eab308)_30%,transparent)]'
92+ : 'bg-[color-mix(in_srgb,var(--brand)_10%,transparent)] border border-[color-mix(in_srgb,var(--brand)_20%,transparent)]'
93+ } `}
94+ >
8995 < Icon
90- icon = { isConnecting ? 'lucide:loader-2 ' : 'lucide:cpu ' }
96+ icon = { isConnecting ? 'lucide:radio ' : 'lucide:radio ' }
9197 width = { 28 }
9298 height = { 28 }
93- className = { `text-[var(--brand)] ${ isConnecting ? 'animate-spin' : '' } ` }
99+ className = { `transition-colors duration-500 ${
100+ isConnecting ? 'text-[var(--warning,#eab308)] animate-pulse' : 'text-[var(--brand)]'
101+ } `}
94102 />
95103 </ div >
96- < span
97- className = { `absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 rounded-full border-2 border-[var(--bg)] ${
98- isConnecting ? 'bg-[var(--warning,#eab308)] animate-pulse' : 'bg-[var(--text-disabled)]'
99- } `}
100- />
101104 </ div >
102105
103106 { isConnecting ? (
104107 < >
105- < h3 className = "text-[16px ] font-semibold text-[var(--text-primary)] mb-1.5 " >
108+ < h3 className = "text-[17px ] font-semibold text-[var(--text-primary)] mb-1" >
106109 Connecting…
107110 </ h3 >
108- < p className = "text-[13px] text-[var(--text-tertiary)]" > Looking for OpenClaw gateway</ p >
111+ < p className = "text-[13px] text-[var(--text-tertiary)]" > Looking for your gateway</ p >
109112 </ >
110113 ) : (
111114 < >
112- < h3 className = "text-[16px ] font-semibold text-[var(--text-primary)] mb-1.5 " >
113- Gateway not found
115+ < h3 className = "text-[17px ] font-semibold text-[var(--text-primary)] mb-1" >
116+ Connect to Gateway
114117 </ h3 >
115- < p className = "text-[13px] text-[var(--text-tertiary)] leading-relaxed mb-4 max-w-[320px ]" >
118+ < p className = "text-[13px] text-[var(--text-tertiary)] leading-relaxed mb-6 max-w-[280px ]" >
116119 { isMobileDevice
117- ? 'Enter your gateway URL and password to connect .'
120+ ? 'Enter your gateway address to start chatting .'
118121 : 'Make sure OpenClaw is running on this machine.' }
119122 </ p >
120123
121- < div className = "space-y-2.5 w-full max-w-[320px]" >
122- { ! isMobileDevice && (
123- < button
124- onClick = { ( ) => connect ( 'ws://localhost:18789' , '' ) }
125- className = "w-full flex items-center justify-center gap-2 py-2.5 rounded-lg text-[13px] font-medium transition-all cursor-pointer"
126- style = { { backgroundColor : 'var(--brand)' , color : 'var(--brand-contrast, #fff)' } }
127- >
128- < Icon icon = "lucide:refresh-cw" width = { 14 } height = { 14 } />
129- Retry connection
130- </ button >
131- ) }
124+ < div className = "w-full max-w-[340px] space-y-3" >
125+ { /* URL input */ }
126+ < div className = "relative" >
127+ < Icon
128+ icon = "lucide:globe"
129+ width = { 15 }
130+ height = { 15 }
131+ className = "absolute left-3.5 top-1/2 -translate-y-1/2 text-[var(--text-disabled)]"
132+ />
133+ < input
134+ type = "text"
135+ value = { url }
136+ onChange = { ( e ) => setUrl ( e . target . value ) }
137+ onKeyDown = { ( e ) => {
138+ if ( e . key === 'Enter' ) handleConnect ( )
139+ } }
140+ placeholder = { isMobileDevice ? 'wss://your-gateway.ts.net' : 'ws://localhost:18789' }
141+ className = "w-full pl-10 pr-3 py-3.5 rounded-xl bg-[var(--bg)] border border-[var(--border)] text-[14px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--brand)] focus:ring-1 focus:ring-[color-mix(in_srgb,var(--brand)_30%,transparent)] transition-all"
142+ autoCapitalize = "off"
143+ autoCorrect = "off"
144+ spellCheck = { false }
145+ />
146+ </ div >
132147
148+ { /* Password input */ }
149+ < div className = "relative" >
150+ < Icon
151+ icon = "lucide:lock"
152+ width = { 15 }
153+ height = { 15 }
154+ className = "absolute left-3.5 top-1/2 -translate-y-1/2 text-[var(--text-disabled)]"
155+ />
156+ < input
157+ type = "password"
158+ value = { password }
159+ onChange = { ( e ) => setPassword ( e . target . value ) }
160+ onKeyDown = { ( e ) => {
161+ if ( e . key === 'Enter' ) handleConnect ( )
162+ } }
163+ placeholder = "Password (optional)"
164+ className = "w-full pl-10 pr-3 py-3.5 rounded-xl bg-[var(--bg)] border border-[var(--border)] text-[14px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--brand)] focus:ring-1 focus:ring-[color-mix(in_srgb,var(--brand)_30%,transparent)] transition-all"
165+ />
166+ </ div >
167+
168+ { /* Connect button */ }
133169 < button
134- onClick = { ( ) => setShowManual ( ( v ) => ! v ) }
135- 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"
170+ onClick = { handleConnect }
171+ disabled = { ! url . trim ( ) }
172+ className = "w-full py-3.5 rounded-xl text-[14px] font-semibold transition-all cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed"
173+ style = { {
174+ backgroundColor : url . trim ( ) ? 'var(--brand)' : 'var(--bg-subtle)' ,
175+ color : url . trim ( ) ? 'var(--brand-contrast, #fff)' : 'var(--text-disabled)' ,
176+ } }
136177 >
137- < Icon icon = "lucide:settings-2" width = { 12 } height = { 12 } />
138- { showManual ? 'Hide' : 'Manual connection' }
178+ Connect
139179 </ button >
140180
141- { showManual && (
142- < div className = "space-y-2 pt-1" >
143- < input
144- type = "text"
145- value = { url }
146- onChange = { ( e ) => setUrl ( e . target . value ) }
147- onKeyDown = { ( e ) => {
148- if ( e . key === 'Enter' ) handleConnect ( )
149- } }
150- placeholder = "wss://your-gateway.ts.net"
151- className = "w-full px-3 py-3 sm:py-2 rounded-lg bg-[var(--bg)] border border-[var(--border)] text-[14px] sm:text-[12px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--border-focus)]"
152- autoCapitalize = "off"
153- autoCorrect = "off"
154- spellCheck = { false }
155- />
156- < input
157- type = "password"
158- value = { password }
159- onChange = { ( e ) => setPassword ( e . target . value ) }
160- onKeyDown = { ( e ) => {
161- if ( e . key === 'Enter' ) handleConnect ( )
162- } }
163- placeholder = "Password (if set)"
164- className = "w-full px-3 py-3 sm:py-2 rounded-lg bg-[var(--bg)] border border-[var(--border)] text-[14px] sm:text-[12px] font-mono text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] outline-none focus:border-[var(--border-focus)]"
165- />
166- < button
167- onClick = { handleConnect }
168- disabled = { ! url . trim ( ) }
169- className = "w-full py-3 sm:py-2 rounded-lg text-[14px] sm:text-[12px] font-medium bg-[var(--bg-subtle)] text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] transition-colors cursor-pointer disabled:opacity-50"
170- >
171- Connect
172- </ button >
173- </ div >
181+ { /* Desktop retry */ }
182+ { ! isMobileDevice && (
183+ < button
184+ onClick = { ( ) => connect ( 'ws://localhost:18789' , '' ) }
185+ className = "w-full flex items-center justify-center gap-2 py-2 text-[12px] text-[var(--text-tertiary)] hover:text-[var(--text-secondary)] transition-colors cursor-pointer"
186+ >
187+ < Icon icon = "lucide:refresh-cw" width = { 12 } height = { 12 } />
188+ Retry localhost
189+ </ button >
174190 ) }
175191 </ div >
176192
193+ { /* Error message */ }
177194 { error && (
178- < div className = "flex items-start gap-2 mt-3 text-[11px ] text-[var(--color-deletions)] max-w-[280px ] text-left" >
179- < Icon icon = "lucide:alert-circle" width = { 12 } height = { 12 } className = "shrink-0 mt-0.5" />
195+ < div className = "flex items-start gap-2 mt-4 py-2.5 px-3.5 rounded-lg bg-[color-mix(in_srgb,var(--color-deletions)_8%,transparent)] border border-[color-mix(in_srgb,var(--color-deletions)_15%,transparent)] text-[12px ] text-[var(--color-deletions)] max-w-[340px ] text-left" >
196+ < Icon icon = "lucide:alert-circle" width = { 14 } height = { 14 } className = "shrink-0 mt-0.5" />
180197 < span > { error } </ span >
181198 </ div >
182199 ) }
183200
184- { isMobileDevice ? (
185- < div className = "mt-5 text-[11px] text-[var(--text-disabled)] text-left max-w-[320px] space-y-1" >
186- < p className = "font-medium text-[var(--text-tertiary)]" > Connection tips:</ p >
187- < p > • Use your Tailscale Funnel or local network URL</ p >
201+ { /* Tips */ }
202+ { isMobileDevice && (
203+ < div className = "mt-5 text-[11px] text-[var(--text-disabled)] max-w-[340px] space-y-0.5" >
188204 < p >
189- • Format:{ ' ' }
190- < code className = "px-1 py-0.5 bg-[var(--bg-secondary)] rounded text-[var(--brand)]" >
191- wss://host:port
192- </ code > { ' ' }
193- or{ ' ' }
205+ Use your Tailscale Funnel URL or{ ' ' }
194206 < code className = "px-1 py-0.5 bg-[var(--bg-secondary)] rounded text-[var(--brand)]" >
195207 ws://ip:18789
196208 </ code >
197209 </ p >
198210 </ div >
199- ) : (
200- < p className = "mt-5 text-[11px] text-[var(--text-disabled)]" >
211+ ) }
212+ { ! isMobileDevice && (
213+ < p className = "mt-4 text-[11px] text-[var(--text-disabled)]" >
201214 Run{ ' ' }
202215 < code className = "px-1 py-0.5 bg-[var(--bg-secondary)] rounded text-[var(--brand)]" >
203216 openclaw gateway start
@@ -2146,8 +2159,8 @@ export function AgentPanel() {
21462159 />
21472160 ) }
21482161
2149- { /* Input section — hidden when ChatHome is showing */ }
2150- { ( messages . length > 0 || ! isConnected ) && (
2162+ { /* Input section — hidden when disconnected on mobile, hidden when ChatHome is showing on desktop */ }
2163+ { ( messages . length > 0 || ( ! isConnected && ! ( typeof window !== 'undefined' && window . innerWidth <= 768 ) ) ) && (
21512164 < ChatInputBar
21522165 input = { input }
21532166 setInput = { setInput }
@@ -2181,13 +2194,13 @@ export function AgentPanel() {
21812194 />
21822195 ) }
21832196
2184- { /* Branded footer */ }
2185- < div className = "shrink-0 flex items-center justify-center gap-1.5 px-3 py-0.5 border-t border-[var(--border)] bg-[var(--bg-elevated)]" >
2197+ { /* Branded footer — hidden on mobile to save space */ }
2198+ < div className = "shrink-0 hidden sm: flex items-center justify-center gap-1.5 px-3 py-0.5 border-t border-[var(--border)] bg-[var(--bg-elevated)]" >
21862199 < KnotLogo size = { 9 } className = "opacity-40" />
21872200 < span className = "text-[8px] text-[var(--text-disabled)] font-medium tracking-wide" >
21882201 KnotCode
21892202 </ span >
2190- < span className = "text-[7px] text-[var(--text-disabled)] opacity-50" > v1.0 .0</ span >
2203+ < span className = "text-[7px] text-[var(--text-disabled)] opacity-50" > v1.4 .0</ span >
21912204 </ div >
21922205 </ div >
21932206 )
0 commit comments