1- import { TextField } from "@opencode-ai/ui/text-field"
2- import { Logo } from "@opencode-ai/ui/logo"
1+ import { AsciiLogo } from "@opencode-ai/ui/logo"
32import { Button } from "@opencode-ai/ui/button"
4- import { Component , createMemo , Show } from "solid-js"
3+ import { TextField } from "@opencode-ai/ui/text-field"
4+ import { Component , createMemo , createSignal , Show } from "solid-js"
55import { usePlatform } from "@/context/platform"
66import { Icon } from "@opencode-ai/ui/icon"
7- import { getStoredServerUrl , clearStoredServerUrl } from "@/lib/server-url"
7+ import {
8+ addToServerUrlHistory ,
9+ clearStoredServerUrl ,
10+ getStoredServerUrl ,
11+ hasMixedContentRisk ,
12+ isValidServerUrl ,
13+ setStoredServerUrl ,
14+ } from "@/lib/server-url"
815
916export type InitError = {
1017 name : string
@@ -133,16 +140,39 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
133140
134141 const hasServerOverride = createMemo ( ( ) => ! ! getStoredServerUrl ( ) )
135142 const showResetButton = createMemo ( ( ) => isConnectionError ( props . error ) && hasServerOverride ( ) )
143+ const showServerConfig = createMemo ( ( ) => isConnectionError ( props . error ) )
144+ const [ serverUrl , setServerUrl ] = createSignal ( getStoredServerUrl ( ) ?? "" )
145+ const inputValid = createMemo ( ( ) => {
146+ const url = serverUrl ( ) . trim ( )
147+ return url . length > 0 && isValidServerUrl ( url )
148+ } )
149+ const showInvalidUrl = createMemo ( ( ) => {
150+ const url = serverUrl ( ) . trim ( )
151+ return url . length > 0 && ! isValidServerUrl ( url )
152+ } )
153+ const showMixedContentWarning = createMemo ( ( ) => {
154+ const url = serverUrl ( ) . trim ( )
155+ if ( ! url ) return false
156+ return hasMixedContentRisk ( url )
157+ } )
136158
137159 function resetServerUrl ( ) {
138160 clearStoredServerUrl ( )
139161 platform . restart ?.( ) ?? window . location . reload ( )
140162 }
141163
164+ function applyServerUrl ( ) {
165+ const url = serverUrl ( ) . trim ( )
166+ if ( ! isValidServerUrl ( url ) ) return
167+ setStoredServerUrl ( url )
168+ addToServerUrlHistory ( url )
169+ platform . restart ?.( ) ?? window . location . reload ( )
170+ }
171+
142172 return (
143173 < div class = "relative flex-1 h-screen w-screen min-h-0 flex flex-col items-center justify-center bg-background-base font-sans" >
144174 < div class = "w-2/3 max-w-3xl flex flex-col items-center justify-center gap-8" >
145- < Logo class = "w-58.5 opacity-12 shrink-0" />
175+ < AsciiLogo scale = { 1.1 } class = " opacity-25 shrink-0" />
146176 < div class = "flex flex-col items-center gap-2 text-center" >
147177 < h1 class = "text-lg font-medium text-text-strong" > Something went wrong</ h1 >
148178 < p class = "text-sm text-text-weak" > An error occurred while loading the application.</ p >
@@ -156,6 +186,39 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
156186 label = "Error Details"
157187 hideLabel
158188 />
189+ < Show when = { showServerConfig ( ) } >
190+ < div class = "flex flex-col gap-3 w-full" >
191+ < div class = "text-xs text-text-weak uppercase tracking-wide text-left" > Server</ div >
192+ < div class = "flex w-full gap-2" >
193+ < TextField
194+ value = { serverUrl ( ) }
195+ onInput = { ( e : InputEvent & { currentTarget : HTMLInputElement } ) => setServerUrl ( e . currentTarget . value ) }
196+ placeholder = "http://localhost:4096"
197+ class = "flex-1 font-mono"
198+ onKeyDown = { ( e : KeyboardEvent ) => {
199+ if ( e . key === "Enter" && inputValid ( ) ) {
200+ applyServerUrl ( )
201+ }
202+ } }
203+ />
204+ < Button size = "large" onClick = { applyServerUrl } disabled = { ! inputValid ( ) } >
205+ Set & Restart
206+ </ Button >
207+ </ div >
208+ < Show when = { showInvalidUrl ( ) } >
209+ < p class = "text-xs text-text-weak text-left" > Enter a valid HTTP or HTTPS URL.</ p >
210+ </ Show >
211+ < Show when = { showMixedContentWarning ( ) } >
212+ < div class = "flex items-start gap-2 p-2 rounded-md bg-yellow-500/10 text-yellow-600 dark:text-yellow-400 text-xs" >
213+ < Icon name = "circle-ban-sign" size = "small" class = "shrink-0 mt-0.5" />
214+ < span >
215+ This HTTP URL will be blocked by your browser (mixed content). Use{ " " }
216+ < code class = "font-mono bg-surface-base px-1 rounded" > localhost</ code > or an HTTPS URL instead.
217+ </ span >
218+ </ div >
219+ </ Show >
220+ </ div >
221+ </ Show >
159222 < div class = "flex flex-col items-center gap-3" >
160223 < Button size = "large" onClick = { platform . restart } >
161224 Restart
@@ -171,11 +234,11 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
171234 </ div >
172235 < div class = "flex flex-col items-center gap-2" >
173236 < div class = "flex items-center justify-center gap-1" >
174- Please report this error to the OpenCode team
237+ Please report this error to the shuvcode team
175238 < button
176239 type = "button"
177240 class = "flex items-center text-text-interactive-base gap-1"
178- onClick = { ( ) => platform . openLink ( "https://opencode .ai/desktop-feedback " ) }
241+ onClick = { ( ) => platform . openLink ( "https://shuv .ai" ) }
179242 >
180243 < div > on Discord</ div >
181244 < Icon name = "discord" class = "text-text-interactive-base" />
0 commit comments