@@ -15,12 +15,28 @@ import (
1515 "github.com/GoCodeAlone/ratchet-cli/internal/tui/theme"
1616)
1717
18+ type appPage int
19+
20+ const (
21+ pageSplash appPage = iota
22+ pageOnboarding
23+ pageChat
24+ )
25+
26+ // ProvidersCheckedMsg carries the result of the async provider list check.
27+ type ProvidersCheckedMsg struct {
28+ Providers []* pb.Provider
29+ }
30+
1831// App is the root Bubbletea v2 model.
1932type App struct {
2033 client * client.Client
2134 sessionID string
35+ session * pb.Session
2236 chat pages.ChatModel
2337 team pages.TeamModel
38+ splash pages.SplashModel
39+ onboarding pages.OnboardingModel
2440 sidebar components.SidebarModel
2541 theme theme.Theme
2642 dark bool
@@ -29,110 +45,191 @@ type App struct {
2945 showSidebar bool
3046 showTeam bool
3147 ready bool
48+ page appPage
49+
50+ // Coordination between splash animation and provider check.
51+ splashDone bool
52+ providersReady bool
53+ providers []* pb.Provider
3254}
3355
3456// NewApp creates the root TUI application model.
3557func NewApp (c * client.Client , session * pb.Session , t theme.Theme , dark bool ) App {
36- chat := pages .NewChat (c , session .GetId (), t , dark )
37- team := pages .NewTeam ()
58+ splash := pages .NewSplash ()
3859 sidebar := components .NewSidebar ([]* pb.Session {session }, session .GetId ())
3960 return App {
4061 client : c ,
4162 sessionID : session .GetId (),
42- chat : chat ,
43- team : team ,
63+ session : session ,
64+ splash : splash ,
4465 sidebar : sidebar ,
4566 theme : t ,
4667 dark : dark ,
68+ page : pageSplash ,
4769 }
4870}
4971
5072func (a App ) Init () tea.Cmd {
5173 return tea .Batch (
52- a .chat .Init (),
74+ a .splash .Init (),
75+ a .checkProviders (),
5376 )
5477}
5578
79+ func (a App ) checkProviders () tea.Cmd {
80+ return func () tea.Msg {
81+ resp , err := a .client .ListProviders (context .Background ())
82+ if err != nil {
83+ return ProvidersCheckedMsg {Providers : nil }
84+ }
85+ return ProvidersCheckedMsg {Providers : resp .Providers }
86+ }
87+ }
88+
5689func (a App ) Update (msg tea.Msg ) (tea.Model , tea.Cmd ) {
5790 var cmds []tea.Cmd
5891
5992 switch msg := msg .(type ) {
60- case tea.KeyPressMsg :
61- switch msg .String () {
62- case "ctrl+c" :
63- return a , tea .Quit
64- case "ctrl+d" :
65- // Detach: quit TUI, leave session running
66- return a , tea .Quit
67- case "ctrl+s" :
68- a .showSidebar = ! a .showSidebar
69- if a .showSidebar {
70- a .showTeam = false
71- }
72- case "ctrl+t" :
73- a .showTeam = ! a .showTeam
74- if a .showTeam {
75- a .showSidebar = false
76- }
77- }
7893 case tea.WindowSizeMsg :
7994 a .width = msg .Width
8095 a .height = msg .Height
8196 a .ready = true
97+
98+ case tea.KeyPressMsg :
99+ if msg .String () == "ctrl+c" {
100+ return a , tea .Quit
101+ }
102+ // Chat-only shortcuts
103+ if a .page == pageChat {
104+ switch msg .String () {
105+ case "ctrl+d" :
106+ return a , tea .Quit
107+ case "ctrl+s" :
108+ a .showSidebar = ! a .showSidebar
109+ if a .showSidebar {
110+ a .showTeam = false
111+ }
112+ case "ctrl+t" :
113+ a .showTeam = ! a .showTeam
114+ if a .showTeam {
115+ a .showSidebar = false
116+ }
117+ }
118+ }
119+
120+ case pages.SplashDoneMsg :
121+ a .splashDone = true
122+ if a .providersReady {
123+ return a .transitionFromSplash ()
124+ }
125+ return a , nil
126+
127+ case ProvidersCheckedMsg :
128+ a .providersReady = true
129+ a .providers = msg .Providers
130+ if a .splashDone {
131+ return a .transitionFromSplash ()
132+ }
133+ return a , nil
134+
135+ case pages.OnboardingDoneMsg :
136+ return a .transitionToChat ()
137+
82138 case components.SessionSelectedMsg :
83139 a .sessionID = msg .SessionID
84140 a .showSidebar = false
141+
85142 case components.SessionKillMsg :
86143 go func () {
87144 a .client .KillSession (context .Background (), msg .SessionID )
88145 }()
89146 }
90147
91- // Route key events to active panel
92- if a .showSidebar {
93- var sidebarCmd tea.Cmd
94- a .sidebar , sidebarCmd = a .sidebar .Update (msg )
95- cmds = append (cmds , sidebarCmd )
96- } else if a .showTeam {
97- var teamCmd tea.Cmd
98- a .team , teamCmd = a .team .Update (msg )
99- cmds = append (cmds , teamCmd )
100- } else {
101- var chatCmd tea.Cmd
102- a .chat , chatCmd = a .chat .Update (msg )
103- cmds = append (cmds , chatCmd )
148+ // Route updates to active page
149+ switch a .page {
150+ case pageSplash :
151+ var splashCmd tea.Cmd
152+ a .splash , splashCmd = a .splash .Update (msg )
153+ cmds = append (cmds , splashCmd )
154+
155+ case pageOnboarding :
156+ var obCmd tea.Cmd
157+ a .onboarding , obCmd = a .onboarding .Update (msg )
158+ cmds = append (cmds , obCmd )
159+
160+ case pageChat :
161+ if a .showSidebar {
162+ var sidebarCmd tea.Cmd
163+ a .sidebar , sidebarCmd = a .sidebar .Update (msg )
164+ cmds = append (cmds , sidebarCmd )
165+ } else if a .showTeam {
166+ var teamCmd tea.Cmd
167+ a .team , teamCmd = a .team .Update (msg )
168+ cmds = append (cmds , teamCmd )
169+ } else {
170+ var chatCmd tea.Cmd
171+ a .chat , chatCmd = a .chat .Update (msg )
172+ cmds = append (cmds , chatCmd )
173+ }
104174 }
105175
106176 return a , tea .Batch (cmds ... )
107177}
108178
179+ func (a App ) transitionFromSplash () (tea.Model , tea.Cmd ) {
180+ if len (a .providers ) == 0 {
181+ a .onboarding = pages .NewOnboarding (a .client , a .theme )
182+ a .page = pageOnboarding
183+ return a , a .onboarding .Init ()
184+ }
185+ return a .transitionToChat ()
186+ }
187+
188+ func (a App ) transitionToChat () (tea.Model , tea.Cmd ) {
189+ chat := pages .NewChat (a .client , a .sessionID , a .theme , a .dark )
190+ team := pages .NewTeam ()
191+ a .chat = chat
192+ a .team = team
193+ a .page = pageChat
194+ return a , a .chat .Init ()
195+ }
196+
109197func (a App ) View () tea.View {
110198 if ! a .ready {
111199 v := tea .NewView ("Connecting to ratchet daemon..." )
112200 return v
113201 }
114202
115- header := a .renderHeader ()
116- var body string
203+ var content string
204+
205+ switch a .page {
206+ case pageSplash :
207+ content = a .splash .View (a .theme , a .width , a .height )
208+
209+ case pageOnboarding :
210+ content = a .onboarding .View (a .theme , a .width , a .height )
117211
118- switch {
119- case a .showSidebar :
120- sidebarWidth := 30
121- if a .width > 0 && sidebarWidth > a .width / 3 {
122- sidebarWidth = a .width / 3
212+ case pageChat :
213+ header := a .renderHeader ()
214+ var body string
215+ switch {
216+ case a .showSidebar :
217+ sidebarWidth := 30
218+ if a .width > 0 && sidebarWidth > a .width / 3 {
219+ sidebarWidth = a .width / 3
220+ }
221+ sidebarView := a .sidebar .SetSize (sidebarWidth , a .height - 3 ).View (a .theme )
222+ chatView := a .chat .View (a .theme )
223+ body = joinColumns (sidebarView , chatView , sidebarWidth , a .width )
224+ case a .showTeam :
225+ teamView := a .team .SetSize (a .width , a .height - 3 ).View (a .theme )
226+ body = teamView
227+ default :
228+ body = a .chat .View (a .theme )
123229 }
124- sidebarView := a .sidebar .SetSize (sidebarWidth , a .height - 3 ).View (a .theme )
125- chatView := a .chat .View (a .theme )
126- body = joinColumns (sidebarView , chatView , sidebarWidth , a .width )
127- case a .showTeam :
128- teamView := a .team .SetSize (a .width , a .height - 3 ).View (a .theme )
129- body = teamView
130- default :
131- body = a .chat .View (a .theme )
230+ content = header + "\n " + body
132231 }
133232
134- content := header + "\n " + body
135-
136233 view := tea .NewView (content )
137234 view .AltScreen = true
138235 return view
0 commit comments