@@ -3,87 +3,95 @@ import { MetaProvider } from "@solidjs/meta"
33import "@opencode-ai/app/index.css"
44import { Font } from "@opencode-ai/ui/font"
55import { Splash } from "@opencode-ai/ui/logo"
6+ import { Progress } from "@opencode-ai/ui/progress"
67import "./styles.css"
7- import { createSignal , Match , onCleanup , onMount } from "solid-js"
8+ import { createEffect , createMemo , createSignal , onCleanup } from "solid-js"
89import { commands , events , InitStep } from "./bindings"
910import { Channel } from "@tauri-apps/api/core"
10- import { Switch } from "solid-js"
1111
1212const root = document . getElementById ( "root" ) !
13+ const lines = [ "Just a moment..." , "Migrating your database" , "This may take a couple of minutes" ]
14+ const delays = [ 3000 , 9000 ]
1315
1416render ( ( ) => {
15- let splash ! : SVGSVGElement
16- const [ state , setState ] = createSignal < InitStep | null > ( null )
17+ const [ step , setStep ] = createSignal < InitStep | null > ( null )
18+ const [ line , setLine ] = createSignal ( 0 )
19+ const [ percent , setPercent ] = createSignal ( 0 )
20+
21+ const phase = createMemo ( ( ) => step ( ) ?. phase )
22+
23+ const value = createMemo ( ( ) => {
24+ if ( phase ( ) === "done" ) return 100
25+ return Math . max ( 25 , Math . min ( 100 , percent ( ) ) )
26+ } )
1727
1828 const channel = new Channel < InitStep > ( )
19- channel . onmessage = ( e ) => setState ( e )
20- commands . awaitInitialization ( channel as any ) . then ( ( ) => {
21- const currentOpacity = getComputedStyle ( splash ) . opacity
22-
23- splash . style . animation = "none"
24- splash . style . animationPlayState = "paused"
25- splash . style . opacity = currentOpacity
26-
27- requestAnimationFrame ( ( ) => {
28- splash . style . transition = "opacity 0.3s ease"
29- requestAnimationFrame ( ( ) => {
30- splash . style . opacity = "1"
29+ channel . onmessage = ( next ) => setStep ( next )
30+ commands . awaitInitialization ( channel as any ) . catch ( ( ) => undefined )
31+
32+ createEffect ( ( ) => {
33+ if ( phase ( ) !== "sqlite_waiting" ) return
34+
35+ setLine ( 0 )
36+ setPercent ( 0 )
37+
38+ const timers = delays . map ( ( ms , i ) => setTimeout ( ( ) => setLine ( i + 1 ) , ms ) )
39+
40+ let stop : ( ( ) => void ) | undefined
41+ let active = true
42+
43+ void events . sqliteMigrationProgress
44+ . listen ( ( e ) => {
45+ if ( e . payload . type === "InProgress" ) setPercent ( Math . max ( 0 , Math . min ( 100 , e . payload . value ) ) )
46+ if ( e . payload . type === "Done" ) setPercent ( 100 )
3147 } )
48+ . then ( ( unlisten ) => {
49+ if ( active ) {
50+ stop = unlisten
51+ return
52+ }
53+
54+ unlisten ( )
55+ } )
56+ . catch ( ( ) => undefined )
57+
58+ onCleanup ( ( ) => {
59+ active = false
60+ timers . forEach ( clearTimeout )
61+ stop ?.( )
3262 } )
3363 } )
3464
65+ createEffect ( ( ) => {
66+ if ( phase ( ) !== "done" ) return
67+
68+ const timer = setTimeout ( ( ) => events . loadingWindowComplete . emit ( null ) , 1000 )
69+ onCleanup ( ( ) => clearTimeout ( timer ) )
70+ } )
71+
72+ const status = createMemo ( ( ) => {
73+ if ( phase ( ) === "done" ) return "All done"
74+ if ( phase ( ) === "sqlite_waiting" ) return lines [ line ( ) ]
75+ return "Just a moment..."
76+ } )
77+
3578 return (
3679 < MetaProvider >
3780 < div class = "w-screen h-screen bg-background-base flex items-center justify-center" >
3881 < Font />
39- < div class = "flex flex-col items-center gap-10" >
40- < Splash ref = { splash } class = "h-25 animate-[pulse-splash_2s_ease-in-out_infinite]" />
41- < span class = "text-text-base" >
42- < Switch fallback = "Just a moment..." >
43- < Match when = { state ( ) ?. phase === "done" } >
44- { ( _ ) => {
45- onMount ( ( ) => {
46- setTimeout ( ( ) => events . loadingWindowComplete . emit ( null ) , 1000 )
47- } )
48-
49- return "All done"
50- } }
51- </ Match >
52- < Match when = { state ( ) ?. phase === "sqlite_waiting" } >
53- { ( _ ) => {
54- const textItems = [
55- "Just a moment..." ,
56- "Migrating your database" ,
57- "This could take a couple of minutes" ,
58- ]
59- const [ textIndex , setTextIndex ] = createSignal ( 0 )
60- const [ progress , setProgress ] = createSignal ( 0 )
61-
62- onMount ( async ( ) => {
63- const listener = events . sqliteMigrationProgress . listen ( ( e ) => {
64- if ( e . payload . type === "InProgress" ) setProgress ( e . payload . value )
65- } )
66- onCleanup ( ( ) => listener . then ( ( c ) => c ( ) ) )
67-
68- await new Promise ( ( res ) => setTimeout ( res , 3000 ) )
69- setTextIndex ( 1 )
70- await new Promise ( ( res ) => setTimeout ( res , 6000 ) )
71- setTextIndex ( 2 )
72- } )
73-
74- return (
75- < div class = "flex flex-col items-center gap-1" >
76- < span > { textItems [ textIndex ( ) ] } </ span >
77- < span > Progress: { progress ( ) } %</ span >
78- < div class = "h-2 w-48 rounded-full border border-white relative" >
79- < div class = "bg-[#fff] h-full absolute left-0 inset-y-0" style = { { width : `${ progress ( ) } %` } } />
80- </ div >
81- </ div >
82- )
83- } }
84- </ Match >
85- </ Switch >
86- </ span >
82+ < div class = "flex flex-col items-center gap-11" >
83+ < Splash class = "w-20 h-25 opacity-15" />
84+ < div class = "w-60 flex flex-col items-center gap-4" aria-live = "polite" >
85+ < span class = "w-full overflow-hidden text-center text-ellipsis whitespace-nowrap text-text-strong text-14-normal" >
86+ { status ( ) }
87+ </ span >
88+ < Progress
89+ value = { value ( ) }
90+ class = "w-20 [&_[data-slot='progress-track']]:h-1 [&_[data-slot='progress-track']]:border-0 [&_[data-slot='progress-track']]:rounded-none [&_[data-slot='progress-track']]:bg-surface-weak [&_[data-slot='progress-fill']]:rounded-none [&_[data-slot='progress-fill']]:bg-icon-warning-base"
91+ aria-label = "Database migration progress"
92+ getValueLabel = { ( { value } ) => `${ Math . round ( value ) } %` }
93+ />
94+ </ div >
8795 </ div >
8896 </ div >
8997 </ MetaProvider >
0 commit comments