Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/hive/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func (s *Service) CreateSession(ctx context.Context, opts CreateOptions) (*sessi

sess = recyclable
sess.Name = opts.Name
sess.Prompt = opts.Prompt
sess.State = session.StateActive
sess.UpdatedAt = time.Now()
} else {
Expand Down
48 changes: 32 additions & 16 deletions internal/tui/delegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import (
"github.com/hay-kot/hive/pkg/kv"
)

// maxPromptDisplayLen is the maximum number of runes to display for a session prompt.
const maxPromptDisplayLen = 60

// SessionItem wraps a session for the list component.
type SessionItem struct {
Session session.Session
Expand Down Expand Up @@ -54,7 +57,7 @@ func NewSessionDelegate() SessionDelegate {

// Height returns the height of each item.
func (d SessionDelegate) Height() int {
return 4
return 5
}

// Spacing returns the spacing between items.
Expand All @@ -77,10 +80,7 @@ func (d SessionDelegate) Render(w io.Writer, m list.Model, index int, item list.
s := sessionItem.Session
isSelected := index == m.Index()

// Build the title line: ID Name
title := fmt.Sprintf("%-8s %s", s.ID, s.Name)

// Build the description line: State | Path
// Build the title line: Name [state]
var stateStyle lipgloss.Style
switch s.State {
case session.StateActive:
Expand All @@ -91,26 +91,42 @@ func (d SessionDelegate) Render(w io.Writer, m list.Model, index int, item list.
stateStyle = d.Styles.Normal
}

state := stateStyle.Render(string(s.State))
desc := fmt.Sprintf("%s %s", state, s.Path)
stateTag := stateStyle.Render(fmt.Sprintf("[%s]", s.State))
title := fmt.Sprintf("%s %s", s.Name, stateTag)

// Build the description line: Path
path := pathStyle.Render(s.Path)

// Build prompt line (truncated with ellipsis)
prompt := s.Prompt
if prompt == "" {
prompt = "(no prompt)"
}
promptRunes := []rune(prompt)
if len(promptRunes) > maxPromptDisplayLen {
prompt = string(promptRunes[:maxPromptDisplayLen-3]) + "..."
}
promptLine := promptStyle.Render(prompt)

// Build git status line
gitLine := d.renderGitStatus(s.Path)

// Apply selection styling
// Apply selection styling with left border
var titleStyle lipgloss.Style
var border string
if isSelected {
titleStyle = d.Styles.Selected
title = "> " + title
border = selectedBorderStyle.Render("┃") + " "
} else {
titleStyle = d.Styles.Normal
title = " " + title
border = " "
}

// Write to output
_, _ = fmt.Fprintf(w, "%s\n", titleStyle.Render(title))
_, _ = fmt.Fprintf(w, " %s\n", desc)
_, _ = fmt.Fprintf(w, " %s", gitLine)
// Write to output with left border
_, _ = fmt.Fprintf(w, "%s%s\n", border, titleStyle.Render(title))
_, _ = fmt.Fprintf(w, "%s%s\n", border, promptLine)
_, _ = fmt.Fprintf(w, "%s%s\n", border, path)
_, _ = fmt.Fprintf(w, "%s%s", border, gitLine)
}

// renderGitStatus returns the formatted git status line for a session path.
Expand All @@ -128,8 +144,8 @@ func (d SessionDelegate) renderGitStatus(path string) string {
return gitLoadingStyle.Render("Git: unavailable")
}

// Format: branch +N -N • clean/uncommitted changes
branch := gitBranchStyle.Render(status.Branch)
// Format: branch +N -N • clean/uncommitted changes
branch := gitBranchStyle.Render("\ue725 " + status.Branch)

// Diff stats with colored additions (green) and deletions (red)
additions := gitAdditionsStyle.Render(fmt.Sprintf(" +%d", status.Additions))
Expand Down
26 changes: 18 additions & 8 deletions internal/tui/styles.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ var (
colorGreen = lipgloss.Color("#9ece6a") // green
colorYellow = lipgloss.Color("#e0af68") // yellow
colorBlue = lipgloss.Color("#7aa2f7") // blue
/* colorPurple = lipgloss.Color("#bb9af7") // purple */
/* colorCyan = lipgloss.Color("#7dcfff") // cyan */
colorGray = lipgloss.Color("#565f89") // comment
colorWhite = lipgloss.Color("#c0caf5") // foreground
colorGray = lipgloss.Color("#565f89") // comment
colorWhite = lipgloss.Color("#c0caf5") // foreground
)

// Styles used for rendering the TUI (lipgloss v1 for bubbles compatibility).
Expand All @@ -32,14 +30,26 @@ var (
recycledStyle = lipgloss.NewStyle().
Foreground(colorYellow)

// Selected item style.
// Selected item style (matches border color).
selectedStyle = lipgloss.NewStyle().
Foreground(colorWhite).
Foreground(colorBlue).
Bold(true)

// Normal item style.
normalStyle = lipgloss.NewStyle().
// Normal item style (no color, uses terminal default).
normalStyle = lipgloss.NewStyle()

// Path style for subtle directory text.
pathStyle = lipgloss.NewStyle().
Foreground(colorGray)

// Prompt style for session prompt text.
promptStyle = lipgloss.NewStyle().
Foreground(colorGray).
Italic(true)

// Selected border style for left accent bar.
selectedBorderStyle = lipgloss.NewStyle().
Foreground(colorBlue)
)

// Banner ASCII art for the header.
Expand Down
Loading