Files
vmux/display.go
Pierre Martin 170790fcda feat(02-03): main.go subcommands + display enrichi avec workspace, label, duree
- main.go dispatch list/switch/label/stop/daemon via client socket
- DisplaySessionInfos affiche [ws:N], label entre guillemets, "depuis X min"
- formatDuration pour duree relative humaine (< 1 min, 3 min, 1 h 5 min)
- Daemon mode integre i3 + X11 avec degradation gracieuse
- Tests complets pour tous les champs optionnels du display
2026-03-23 17:54:18 +01:00

156 lines
3.0 KiB
Go

package main
import (
"fmt"
"io"
"strings"
"time"
)
const (
colorReset = "\033[0m"
colorGreen = "\033[32m"
colorYellow = "\033[33m"
colorRed = "\033[31m"
colorGray = "\033[90m"
colorBold = "\033[1m"
)
// DisplaySessions writes a formatted list of sessions to w.
func DisplaySessions(w io.Writer, sessions []Session, noColor bool) {
if len(sessions) == 0 {
fmt.Fprintln(w, "No active Claude Code sessions found.")
return
}
for i, s := range sessions {
if i > 0 {
fmt.Fprintln(w)
}
stateLabel := stateColor(s.State, noColor) + s.State.String() + resetIfColor(noColor)
branch := ""
if s.GitBranch != "" {
branch = " (" + s.GitBranch + ")"
}
worktreeInfo := ""
if s.Worktree != "" && s.Worktree != s.CwdPath {
worktreeInfo = " [worktree: " + s.Worktree + "]"
}
fmt.Fprintf(w, "[%s] %s%s%s\n", stateLabel, s.CwdPath, branch, worktreeInfo)
if s.Preview != "" {
lines := strings.Split(s.Preview, "\n")
for _, line := range lines {
fmt.Fprintf(w, " %s\n", line)
}
}
}
}
// DisplaySessionInfos writes a formatted list of SessionInfo (from daemon) to w.
// Includes workspace, label, and waiting duration.
func DisplaySessionInfos(w io.Writer, sessions []SessionInfo, noColor bool, now time.Time) {
if len(sessions) == 0 {
fmt.Fprintln(w, "No active Claude Code sessions found.")
return
}
for i, s := range sessions {
if i > 0 {
fmt.Fprintln(w)
}
stateStr := stateColorStr(s.State, noColor) + s.State + resetIfColor(noColor)
branch := ""
if s.GitBranch != "" {
branch = " (" + s.GitBranch + ")"
}
workspace := ""
if s.Workspace != "" {
workspace = " [ws:" + s.Workspace + "]"
}
label := ""
if s.Label != "" {
label = fmt.Sprintf(" %q", s.Label)
}
waiting := ""
if s.WaitingSince != nil {
waiting = " (depuis " + formatDuration(now.Sub(*s.WaitingSince)) + ")"
}
fmt.Fprintf(w, "[%s] %s%s%s%s%s\n", stateStr, s.Cwd, branch, workspace, label, waiting)
if s.Preview != "" {
lines := strings.Split(s.Preview, "\n")
for _, line := range lines {
fmt.Fprintf(w, " %s\n", line)
}
}
}
}
// formatDuration returns a human-readable relative duration.
func formatDuration(d time.Duration) string {
if d < time.Minute {
return "< 1 min"
}
hours := int(d.Hours())
minutes := int(d.Minutes()) % 60
if hours > 0 {
if minutes > 0 {
return fmt.Sprintf("%d h %d min", hours, minutes)
}
return fmt.Sprintf("%d h", hours)
}
return fmt.Sprintf("%d min", minutes)
}
func stateColorStr(state string, noColor bool) string {
if noColor {
return ""
}
switch state {
case "Working":
return colorGreen
case "Needs Input":
return colorYellow
case "Idle":
return colorGray
default:
return colorGray
}
}
func stateColor(state SessionState, noColor bool) string {
if noColor {
return ""
}
switch state {
case Working:
return colorGreen
case NeedsInput:
return colorYellow
case Idle:
return colorGray
default:
return colorGray
}
}
func resetIfColor(noColor bool) string {
if noColor {
return ""
}
return colorReset
}