AT Protocol Terminal Interface Explorer

add global footer

+32 -25
+30 -12
ui/app.go
··· 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 "github.com/charmbracelet/bubbles/spinner" 11 tea "github.com/charmbracelet/bubbletea" 12 "github.com/treethought/attie/at" 13 ) 14 ··· 92 return a.active.Init() 93 } 94 95 func (a *App) resizeChildren() tea.Cmd { 96 cmds := []tea.Cmd{} 97 - a.search.SetSize(a.w, a.h) 98 - a.repoView.SetSize(a.w, a.h) 99 - a.rlist.SetSize(a.w, a.h) 100 - a.recordView.SetSize(a.w, a.h) 101 - a.jetstream.SetSize(a.w, a.h) 102 - a.jetEventView.SetSize(a.w, a.h) 103 return tea.Batch(cmds...) 104 } 105 ··· 225 a.actx.collection = "" 226 a.actx.record = nil 227 cmd := a.repoView.SetRepo(msg.repo) 228 - a.repoView.SetSize(a.w, a.h) // Set size before switching view 229 a.active = a.repoView 230 a.search.loading = false 231 return a, cmd ··· 241 a.actx.collection = msg.records.Collection() 242 a.actx.record = nil 243 cmd := a.rlist.SetRecords(msg.records.Records) 244 - a.rlist.SetSize(a.w, a.h) // Set size before switching view 245 a.active = a.rlist 246 a.search.loading = false 247 return a, cmd ··· 252 a.actx.collection = msg.record.Record.Collection() 253 a.actx.record = msg.record.Record 254 a.recordView.SetRecord(msg.record.Record) 255 - a.recordView.SetSize(a.w, a.h) // Set size before switching view 256 a.active = a.recordView 257 return a, nil 258 259 case jetEventSelectedMsg: 260 a.jetEventView.SetEvent(msg.evt) 261 - a.jetEventView.SetSize(a.w, a.h) 262 a.active = a.jetEventView 263 a.jetSreamActive = false 264 return a, nil ··· 326 } 327 } 328 329 func (a *App) View() string { 330 if a.loading { 331 return "Loading... " + a.spinner.View() 332 } 333 if a.jetSreamActive { 334 - return a.jetstream.View() 335 } 336 - return a.active.View() 337 } 338 339 // Message types
··· 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 "github.com/charmbracelet/bubbles/spinner" 11 tea "github.com/charmbracelet/bubbletea" 12 + "github.com/charmbracelet/lipgloss" 13 "github.com/treethought/attie/at" 14 ) 15 ··· 93 return a.active.Init() 94 } 95 96 + const footerHeight = 1 97 + 98 func (a *App) resizeChildren() tea.Cmd { 99 cmds := []tea.Cmd{} 100 + h := a.h - footerHeight 101 + a.search.SetSize(a.w, h) 102 + a.repoView.SetSize(a.w, h) 103 + a.rlist.SetSize(a.w, h) 104 + a.recordView.SetSize(a.w, h) 105 + a.jetstream.SetSize(a.w, h) 106 + a.jetEventView.SetSize(a.w, h) 107 return tea.Batch(cmds...) 108 } 109 ··· 229 a.actx.collection = "" 230 a.actx.record = nil 231 cmd := a.repoView.SetRepo(msg.repo) 232 + a.repoView.SetSize(a.w, a.h-footerHeight) // Set size before switching view 233 a.active = a.repoView 234 a.search.loading = false 235 return a, cmd ··· 245 a.actx.collection = msg.records.Collection() 246 a.actx.record = nil 247 cmd := a.rlist.SetRecords(msg.records.Records) 248 + a.rlist.SetSize(a.w, a.h-footerHeight) // Set size before switching view 249 a.active = a.rlist 250 a.search.loading = false 251 return a, cmd ··· 256 a.actx.collection = msg.record.Record.Collection() 257 a.actx.record = msg.record.Record 258 a.recordView.SetRecord(msg.record.Record) 259 + a.recordView.SetSize(a.w, a.h-footerHeight) // Set size before switching view 260 a.active = a.recordView 261 return a, nil 262 263 case jetEventSelectedMsg: 264 a.jetEventView.SetEvent(msg.evt) 265 + a.jetEventView.SetSize(a.w, a.h-footerHeight) 266 a.active = a.jetEventView 267 a.jetSreamActive = false 268 return a, nil ··· 330 } 331 } 332 333 + func (a *App) footer() string { 334 + key := func(k string) string { 335 + return lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("205")).Render(k) 336 + } 337 + sep := dimStyle.Render(" · ") 338 + content := key("esc") + dimStyle.Render(" back") + 339 + sep + key("ctrl+k") + dimStyle.Render(" search") + 340 + sep + key("ctrl+j") + dimStyle.Render(" jetstream") 341 + return lipgloss.NewStyle().Width(a.w).Align(lipgloss.Right).Render(content) 342 + } 343 + 344 func (a *App) View() string { 345 if a.loading { 346 return "Loading... " + a.spinner.View() 347 } 348 + var body string 349 if a.jetSreamActive { 350 + body = a.jetstream.View() 351 + } else { 352 + body = a.active.View() 353 } 354 + return lipgloss.JoinVertical(lipgloss.Left, body, a.footer()) 355 } 356 357 // Message types
+2 -13
ui/repo.go
··· 173 return 174 } 175 headerHeight := lipgloss.Height(r.header) 176 - footerHeight := 2 // "\n" + help text line 177 178 // List gets all remaining space 179 - listHeight := r.height - headerHeight - footerHeight 180 if listHeight < 5 { 181 listHeight = 5 182 } ··· 188 if r.repo == nil { 189 return "No repository loaded" 190 } 191 - 192 - // Footer help text 193 - footer := dimStyle.Render("Press Esc to go back • ↑/↓ or j/k to navigate • Ctrl+C to quit") 194 - 195 - // Join header (fixed), list (scrollable), and footer 196 - return lipgloss.JoinVertical( 197 - lipgloss.Left, 198 - r.header, 199 - r.clist.View(), 200 - "\n"+footer, 201 - ) 202 }
··· 173 return 174 } 175 headerHeight := lipgloss.Height(r.header) 176 177 // List gets all remaining space 178 + listHeight := r.height - headerHeight 179 if listHeight < 5 { 180 listHeight = 5 181 } ··· 187 if r.repo == nil { 188 return "No repository loaded" 189 } 190 + return lipgloss.JoinVertical(lipgloss.Left, r.header, r.clist.View()) 191 }