refactor(cli): move session rendering out of the adapter
Keep display helpers with their screens and shared presentation code.
199 unmodified linesExpand all200201202203204204205206207208125 unmodified linesExpand all199 unmodified linesExpand all } if asJSON { return writeJSON(cmd.OutOrStdout(), result.Sessions, true) } _, err = fmt.Fprintln(cmd.OutOrStdout(), daemon.SessionListing(result.Sessions, time.Now())) _, err = fmt.Fprintln(cmd.OutOrStdout(), sessionListing(result.Sessions, time.Now())) return err }} command.Flags().BoolVar(&asJSON, "json", false, "print sessions as JSON") command.AddCommand(newSessionRead(deps), newSend(deps))125 unmodified linesExpand all1234567891011121314151617181920212223242526272829303132333435363738package cli
import ( "albedo/cli/internal/daemon" "albedo/cli/internal/presentation" "fmt" "strings" "text/tabwriter" "time")
func sessionListing(sessions []daemon.Session, now time.Time) string { if len(sessions) == 0 { return "no sessions" }
var listing strings.Builder writer := tabwriter.NewWriter(&listing, 0, 4, 2, ' ', 0) fmt.Fprintln(writer, "ID\tTITLE\tWORKSPACE\tLAST ASSISTANT") for _, session := range sessions { title := presentation.SessionText(session.Title) title = strings.TrimSpace(title) if title == "" { title = "session name unavailable" } shortID := session.ID if len(shortID) > 8 { shortID = shortID[:8] } workspace := presentation.SessionText(strings.TrimSpace(session.Workspace)) if workspace == "" { workspace = "—" } fmt.Fprintf(writer, "%s\t%s\t%s\t%s\n", shortID, title, workspace, presentation.AssistantAge(session.LastAssistantAt, now)) } _ = writer.Flush() return strings.TrimRight(listing.String(), "\n")}12345678910111213141534562 unmodified lines9101112262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970package daemon
import ( "fmt" "regexp" "strings" "text/tabwriter" "time")
var sanitizerRegex = regexp.MustCompile(`[\p{Cc}\x{202a}-\x{202e}\x{2066}-\x{2069}]`)
// SessionText replaces control characters and direction overrides in session metadata.func SessionText(s string) string { return sanitizerRegex.ReplaceAllString(s, " ") }
type Session struct { ID string `json:"id"` Title string `json:"title,omitempty"` LastAssistantAt *int64 `json:"last_assistant_at,omitempty"`2 unmodified lines Effort string `json:"effort,omitempty"` Protocol string `json:"protocol"` Provider string `json:"provider"`}
func AssistantAge(timestamp *int64, now time.Time) string { if timestamp == nil { return "time unknown" } sec := max(0, now.Unix()-*timestamp) if sec < 60 { return "just now" } if sec < 3600 { return fmt.Sprintf("%dm ago", sec/60) } if sec < 86400 { return fmt.Sprintf("%dh ago", sec/3600) } return fmt.Sprintf("%dd ago", sec/86400)}
func SessionListing(sessions []Session, now time.Time) string { if len(sessions) == 0 { return "no sessions" }
var listing strings.Builder writer := tabwriter.NewWriter(&listing, 0, 4, 2, ' ', 0) fmt.Fprintln(writer, "ID\tTITLE\tWORKSPACE\tLAST ASSISTANT") for _, s := range sessions { title := SessionText(s.Title) title = strings.TrimSpace(title) if title == "" { title = "session name unavailable" } shortID := s.ID if len(shortID) > 8 { shortID = shortID[:8] } workspace := SessionText(strings.TrimSpace(s.Workspace)) if workspace == "" { workspace = "—" } fmt.Fprintf(writer, "%s\t%s\t%s\t%s\n", shortID, title, workspace, AssistantAge(s.LastAssistantAt, now)) } _ = writer.Flush() return strings.TrimRight(listing.String(), "\n")}1234567891011121314151617181920212223242526272829package presentation
import ( "fmt" "regexp" "time")
var sanitizerRegex = regexp.MustCompile(`[\p{Cc}\x{202a}-\x{202e}\x{2066}-\x{2069}]`)
// SessionText replaces control characters and direction overrides in session metadata.func SessionText(text string) string { return sanitizerRegex.ReplaceAllString(text, " ") }
func AssistantAge(timestamp *int64, now time.Time) string { if timestamp == nil { return "time unknown" } elapsedSeconds := max(0, now.Unix()-*timestamp) if elapsedSeconds < 60 { return "just now" } if elapsedSeconds < 3600 { return fmt.Sprintf("%dm ago", elapsedSeconds/60) } if elapsedSeconds < 86400 { return fmt.Sprintf("%dh ago", elapsedSeconds/3600) } return fmt.Sprintf("%dd ago", elapsedSeconds/86400)}1234567891 unmodified line11121314141516171819532 unmodified linesExpand allpackage tui
import ( "albedo/cli/internal/daemon" "albedo/cli/internal/presentation" "cmp" "slices" "strings" "time"1 unmodified line tea "charm.land/bubbletea/v2" "github.com/charmbracelet/x/ansi")
func sessionText(s string) string { return daemon.SessionText(ansi.Strip(s)) }func sessionText(s string) string { return presentation.SessionText(ansi.Strip(s)) }
// Sections of the session list, in navigation order. Pinned and most used// sessions lead the dated groups.const (532 unmodified linesExpand all123456789465 unmodified linesExpand all475476477478478479480481482483128 unmodified linesExpand allpackage tui
import ( "albedo/cli/internal/daemon" "albedo/cli/internal/presentation" "cmp" "fmt" "path/filepath" "slices"465 unmodified linesExpand all } if s.Workspace != "" { meta = append(meta, DefaultStyles.Muted.Render(sessionText(homePath(s.Workspace)))) } meta = append(meta, DefaultStyles.Faint.Render(daemon.AssistantAge(s.LastAssistantAt, now))) meta = append(meta, DefaultStyles.Faint.Render(presentation.AssistantAge(s.LastAssistantAt, now))) if c := m.previews[s.ID]; c != nil && !c.loading && !c.err { meta = append(meta, DefaultStyles.Faint.Render(fmt.Sprintf("%d messages", c.Total))) } top = append(top, strings.Join(meta, DefaultStyles.Decor.Render(" · ")), DefaultStyles.Decor.Render(strings.Repeat("─", inner)))128 unmodified linesExpand all