feat: stabilize list output with fixed-height preview (5 lines, 300 chars)
Each session always occupies the same number of lines, preventing visual jitter between watch refreshes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -92,10 +92,12 @@ func DisplaySessionInfos(w io.Writer, sessions []SessionInfo, noColor bool, now
|
|||||||
|
|
||||||
fmt.Fprintf(w, "[%s] %s%s%s%s%s\n", stateStr, s.Cwd, branch, workspace, label, waiting)
|
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")
|
lines := strings.Split(s.Preview, "\n")
|
||||||
for _, line := range lines {
|
for i := 0; i < previewLines; i++ {
|
||||||
fmt.Fprintf(w, " %s\n", line)
|
if i < len(lines) && lines[i] != "" {
|
||||||
|
fmt.Fprintf(w, " %s\n", lines[i])
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
state.go
15
state.go
@@ -64,7 +64,7 @@ func DetectState(messages []JSONLMessage, now time.Time) SessionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExtractPreview finds the last assistant text content and returns the first
|
// ExtractPreview finds the last assistant text content and returns the first
|
||||||
// 3 lines, truncated to 200 characters.
|
// 5 lines, truncated to 300 characters.
|
||||||
func ExtractPreview(messages []JSONLMessage) string {
|
func ExtractPreview(messages []JSONLMessage) string {
|
||||||
for i := len(messages) - 1; i >= 0; i-- {
|
for i := len(messages) - 1; i >= 0; i-- {
|
||||||
msg := messages[i]
|
msg := messages[i]
|
||||||
@@ -80,15 +80,18 @@ func ExtractPreview(messages []JSONLMessage) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const previewLines = 5
|
||||||
|
const previewMaxChars = 300
|
||||||
|
|
||||||
func truncatePreview(text string) string {
|
func truncatePreview(text string) string {
|
||||||
lines := strings.SplitN(text, "\n", 4)
|
lines := strings.SplitN(text, "\n", previewLines+1)
|
||||||
if len(lines) > 3 {
|
if len(lines) > previewLines {
|
||||||
lines = lines[:3]
|
lines = lines[:previewLines]
|
||||||
}
|
}
|
||||||
result := strings.Join(lines, "\n")
|
result := strings.Join(lines, "\n")
|
||||||
|
|
||||||
if len(result) > 200 {
|
if len(result) > previewMaxChars {
|
||||||
result = result[:200] + "..."
|
result = result[:previewMaxChars] + "..."
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,13 +151,13 @@ func TestExtractPreview(t *testing.T) {
|
|||||||
Type: "assistant",
|
Type: "assistant",
|
||||||
Message: &MessagePayload{
|
Message: &MessagePayload{
|
||||||
Role: "assistant",
|
Role: "assistant",
|
||||||
Content: []ContentBlock{{Type: "text", Text: "Voici le resultat\nLigne 2\nLigne 3\nLigne 4\nLigne 5"}},
|
Content: []ContentBlock{{Type: "text", Text: "Voici le resultat\nLigne 2\nLigne 3\nLigne 4\nLigne 5\nLigne 6\nLigne 7"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
preview := ExtractPreview(msgs)
|
preview := ExtractPreview(msgs)
|
||||||
want := "Voici le resultat\nLigne 2\nLigne 3"
|
want := "Voici le resultat\nLigne 2\nLigne 3\nLigne 4\nLigne 5"
|
||||||
if preview != want {
|
if preview != want {
|
||||||
t.Errorf("ExtractPreview = %q, want %q", preview, want)
|
t.Errorf("ExtractPreview = %q, want %q", preview, want)
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ func TestExtractPreview_NoAssistant(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractPreview_LongText(t *testing.T) {
|
func TestExtractPreview_LongText(t *testing.T) {
|
||||||
// Text longer than 200 chars should be truncated
|
// Text longer than 300 chars should be truncated
|
||||||
longText := ""
|
longText := ""
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
longText += "abcde "
|
longText += "abcde "
|
||||||
@@ -194,7 +194,7 @@ func TestExtractPreview_LongText(t *testing.T) {
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
preview := ExtractPreview(msgs)
|
preview := ExtractPreview(msgs)
|
||||||
if len(preview) > 203 { // 200 + "..."
|
if len(preview) > 303 { // 300 + "..."
|
||||||
t.Errorf("preview too long: %d chars", len(preview))
|
t.Errorf("preview too long: %d chars", len(preview))
|
||||||
}
|
}
|
||||||
if preview[len(preview)-3:] != "..." {
|
if preview[len(preview)-3:] != "..." {
|
||||||
|
|||||||
Reference in New Issue
Block a user