-
Notifications
You must be signed in to change notification settings - Fork 4
feat(ui): enhance color styling with adaptive colors and improve term… #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,19 +7,23 @@ import ( | |||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| var ( | ||||||||||||||||||||||||
| // Colors | ||||||||||||||||||||||||
| ColorPrimary = lipgloss.Color("205") // Pink | ||||||||||||||||||||||||
| ColorSecondary = lipgloss.Color("241") // Gray | ||||||||||||||||||||||||
| ColorSuccess = lipgloss.Color("42") // Green | ||||||||||||||||||||||||
| ColorError = lipgloss.Color("160") // Red | ||||||||||||||||||||||||
| ColorWarning = lipgloss.Color("214") // Orange/Yellow | ||||||||||||||||||||||||
| ColorText = lipgloss.Color("252") // White/Gray | ||||||||||||||||||||||||
| ColorCyan = lipgloss.Color("87") // Cyan for strategy | ||||||||||||||||||||||||
| ColorBlue = lipgloss.Color("75") // Blue for answers | ||||||||||||||||||||||||
| ColorHighlight = lipgloss.Color("12") // Blue for titles/highlights | ||||||||||||||||||||||||
| ColorSelected = lipgloss.Color("10") // Green for selected items | ||||||||||||||||||||||||
| ColorDim = lipgloss.Color("240") // Dim gray for secondary text | ||||||||||||||||||||||||
| ColorYellow = lipgloss.Color("11") // Yellow for badges/accents | ||||||||||||||||||||||||
| // Colors — AdaptiveColor auto-selects Light/Dark based on terminal background | ||||||||||||||||||||||||
| ColorPrimary = lipgloss.AdaptiveColor{Light: "161", Dark: "205"} // Pink | ||||||||||||||||||||||||
| ColorSecondary = lipgloss.AdaptiveColor{Light: "244", Dark: "241"} // Gray | ||||||||||||||||||||||||
| ColorSuccess = lipgloss.AdaptiveColor{Light: "28", Dark: "42"} // Green | ||||||||||||||||||||||||
| ColorError = lipgloss.AdaptiveColor{Light: "160", Dark: "160"} // Red | ||||||||||||||||||||||||
| ColorWarning = lipgloss.AdaptiveColor{Light: "172", Dark: "214"} // Orange/Yellow | ||||||||||||||||||||||||
| ColorText = lipgloss.AdaptiveColor{Light: "235", Dark: "252"} // Text | ||||||||||||||||||||||||
| ColorCyan = lipgloss.AdaptiveColor{Light: "30", Dark: "87"} // Cyan for strategy | ||||||||||||||||||||||||
| ColorBlue = lipgloss.AdaptiveColor{Light: "27", Dark: "75"} // Blue for answers | ||||||||||||||||||||||||
| ColorHighlight = lipgloss.AdaptiveColor{Light: "4", Dark: "12"} // Blue for titles/highlights | ||||||||||||||||||||||||
| ColorSelected = lipgloss.AdaptiveColor{Light: "2", Dark: "10"} // Green for selected items | ||||||||||||||||||||||||
| ColorDim = lipgloss.AdaptiveColor{Light: "247", Dark: "240"} // Dim gray for secondary text | ||||||||||||||||||||||||
| ColorYellow = lipgloss.AdaptiveColor{Light: "136", Dark: "11"} // Yellow for badges/accents | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Shared constants used across multiple views | ||||||||||||||||||||||||
| ColorPurple = lipgloss.AdaptiveColor{Light: "97", Dark: "141"} // Purple for sections | ||||||||||||||||||||||||
| ColorBarEmpty = lipgloss.AdaptiveColor{Light: "250", Dark: "237"} // Empty bar segments | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Base Styles | ||||||||||||||||||||||||
| StyleTitle = lipgloss.NewStyle().Foreground(ColorText).Bold(true) | ||||||||||||||||||||||||
|
|
@@ -84,8 +88,6 @@ var ( | |||||||||||||||||||||||
| StyleSelectBadge = lipgloss.NewStyle().Foreground(ColorYellow).Bold(true) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Table Styles (alternating rows) | ||||||||||||||||||||||||
| ColorTableRowEven = lipgloss.Color("236") // Subtle dark background | ||||||||||||||||||||||||
| ColorTableRowOdd = lipgloss.Color("234") // Slightly darker | ||||||||||||||||||||||||
| StyleTableRowEven = lipgloss.NewStyle().Foreground(ColorText) | ||||||||||||||||||||||||
| StyleTableRowOdd = lipgloss.NewStyle().Foreground(ColorDim) | ||||||||||||||||||||||||
| StyleTableHeader = lipgloss.NewStyle().Bold(true).Foreground(ColorPrimary).Underline(true) | ||||||||||||||||||||||||
|
|
@@ -106,24 +108,24 @@ var ( | |||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // CategoryBadge returns a styled badge string for a knowledge node type. | ||||||||||||||||||||||||
| func CategoryBadge(nodeType string) string { | ||||||||||||||||||||||||
| colors := map[string]lipgloss.Color{ | ||||||||||||||||||||||||
| "decision": lipgloss.Color("205"), // Pink | ||||||||||||||||||||||||
| "feature": lipgloss.Color("75"), // Blue | ||||||||||||||||||||||||
| "constraint": lipgloss.Color("214"), // Orange | ||||||||||||||||||||||||
| "pattern": lipgloss.Color("141"), // Purple | ||||||||||||||||||||||||
| "plan": lipgloss.Color("42"), // Green | ||||||||||||||||||||||||
| "note": lipgloss.Color("252"), // White | ||||||||||||||||||||||||
| "metadata": lipgloss.Color("87"), // Cyan | ||||||||||||||||||||||||
| "documentation": lipgloss.Color("11"), // Yellow | ||||||||||||||||||||||||
| colors := map[string]lipgloss.AdaptiveColor{ | ||||||||||||||||||||||||
| "decision": ColorPrimary, | ||||||||||||||||||||||||
| "feature": ColorBlue, | ||||||||||||||||||||||||
| "constraint": ColorWarning, | ||||||||||||||||||||||||
| "pattern": ColorPurple, | ||||||||||||||||||||||||
| "plan": ColorSuccess, | ||||||||||||||||||||||||
| "note": ColorText, | ||||||||||||||||||||||||
| "metadata": ColorCyan, | ||||||||||||||||||||||||
| "documentation": ColorYellow, | ||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In the old code this was
Consider introducing a dedicated neutral adaptive color for "note" badges, e.g.: // in styles.go
ColorNeutral = lipgloss.AdaptiveColor{Light: "248", Dark: "252"} // Neutral gray for note badgesThen in
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: internal/ui/styles.go
Line: 119
Comment:
**`ColorText` misused as badge background for "note" type**
`ColorText` is defined as `{Light: "235", Dark: "252"}`. In light-mode terminals, `"235"` resolves to approximately `#262626` (near-black). Using a foreground-text color as a badge *background* causes a visual inversion in light mode: a "note" badge will render with a near-black background, which is jarring and semantically wrong when every other UI element in that theme is light.
In the old code this was `lipgloss.Color("252")` — a consistent light-gray in all modes. The new mapping produces:
- **Dark mode:** `"252"` (#d0d0d0, light gray) background — visually light and subtle, fine.
- **Light mode:** `"235"` (#262626, near-black) background — visually an inverted/dark badge in a light theme, inconsistent with all other badge backgrounds.
Consider introducing a dedicated neutral adaptive color for "note" badges, e.g.:
```go
// in styles.go
ColorNeutral = lipgloss.AdaptiveColor{Light: "248", Dark: "252"} // Neutral gray for note badges
```
Then in `CategoryBadge`:
```suggestion
"note": ColorNeutral,
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| color, ok := colors[nodeType] | ||||||||||||||||||||||||
| if !ok { | ||||||||||||||||||||||||
| color = lipgloss.Color("241") | ||||||||||||||||||||||||
| color = lipgloss.AdaptiveColor{Light: "244", Dark: "241"} | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| badge := lipgloss.NewStyle(). | ||||||||||||||||||||||||
| Foreground(lipgloss.Color("0")). | ||||||||||||||||||||||||
| Foreground(lipgloss.AdaptiveColor{Light: "0", Dark: "0"}). | ||||||||||||||||||||||||
| Background(color). | ||||||||||||||||||||||||
| Padding(0, 1). | ||||||||||||||||||||||||
| Bold(true) | ||||||||||||||||||||||||
|
Comment on lines
127
to
131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Poor contrast: white text on warm light-mode badge backgrounds In light mode, the badge foreground is set to
The dark-mode pairing (
Suggested change
If you need a lighter foreground on dark badge backgrounds in dark mode, consider using Prompt To Fix With AIThis is a comment left during a code review.
Path: internal/ui/styles.go
Line: 129-133
Comment:
**Poor contrast: white text on warm light-mode badge backgrounds**
In light mode, the badge foreground is set to `"231"` (effectively `#ffffff` white). For several badge background colors in light mode, this produces insufficient contrast:
- `ColorYellow.Light = "136"` → approximately `#af8700` (goldenrod). White text on goldenrod ≈ 2.8:1 contrast ratio — fails WCAG AA (requires 4.5:1 for normal text).
- `ColorWarning.Light = "172"` → approximately `#d78700` (amber/orange). White text on amber ≈ 3.1:1 — also fails.
- `ColorText.Light = "235"` → approximately `#262626` (near-black). White text on near-black is fine (high contrast), but this will be invisible to users.
The dark-mode pairing (`"0"` = black on vivid colors) is generally fine, but the light-mode foreground of `"231"` is the wrong direction. On a light terminal background the badge background colors are mid-toned; the readable foreground choice should be dark (e.g., `"0"` or `"232"`), not white.
```suggestion
badge := lipgloss.NewStyle().
Foreground(lipgloss.AdaptiveColor{Light: "0", Dark: "0"}).
Background(color).
Padding(0, 1).
Bold(true)
```
If you need a lighter foreground on dark badge backgrounds in dark mode, consider using `{Light: "0", Dark: "231"}` (dark text in light mode, white text in dark mode), which inverts the current (broken) mapping.
How can I resolve this? If you propose a fix, please make it concise.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed — badge foreground changed from Light:231 (white) to Light:0 (black) for proper contrast on light-mode backgrounds. |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,9 +1,11 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| package ui | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||
| "os" | ||||||||||||||||||||||||||||||||||||||||||||
| "strings" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| "github.com/charmbracelet/lipgloss" | ||||||||||||||||||||||||||||||||||||||||||||
| "golang.org/x/term" | ||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Table renders data in a compact markdown-style table format. | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -41,6 +43,61 @@ func (t *Table) ColumnWidths() []int { | |||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Auto-constrain to terminal width when MaxWidth is not set | ||||||||||||||||||||||||||||||||||||||||||||
| if t.MaxWidth == 0 { | ||||||||||||||||||||||||||||||||||||||||||||
| termWidth := GetTerminalWidth() | ||||||||||||||||||||||||||||||||||||||||||||
| // Account for leading space + column separators (2 chars between each column) | ||||||||||||||||||||||||||||||||||||||||||||
| overhead := 1 | ||||||||||||||||||||||||||||||||||||||||||||
| if len(widths) > 1 { | ||||||||||||||||||||||||||||||||||||||||||||
| overhead += (len(widths) - 1) * 2 | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| available := termWidth - overhead | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+46
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overhead formula underestimates when When
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: internal/ui/table.go
Line: 46-51
Comment:
**Overhead formula underestimates when `len(widths) == 0`**
When `t.Headers` is empty the guard in `Render()` returns early, so `ColumnWidths()` will never reach this block in the current code. However, `ColumnWidths()` is a public method and can be called directly on a zero-header `Table`. In that case:
```
overhead = 1 + (0 - 1) * 2 → 1 + (-2) → -1
available = termWidth - (-1) → termWidth + 1
```
`available > 0` is satisfied but the `total` loop over an empty slice produces `0 > available` → false, so no crash occurs. Still, `-1` overhead is semantically wrong and makes the guard appear broader than it is. A minimal defensive fix:
```suggestion
// Account for leading space + column separators (2 chars between each column)
overhead := 1
if len(widths) > 1 {
overhead += (len(widths) - 1) * 2
}
available := termWidth - overhead
```
How can I resolve this? If you propose a fix, please make it concise.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed — overhead formula now guards against len(widths) <= 1 to avoid negative overhead. |
||||||||||||||||||||||||||||||||||||||||||||
| if available > 0 { | ||||||||||||||||||||||||||||||||||||||||||||
| total := 0 | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+53
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mixed byte-count vs. terminal-column comparison in auto-constrain logic
The fix is to measure visual width using // Instead of len(h) / len(cell), use:
import "github.com/charmbracelet/lipgloss"
// In ColumnWidths():
for i, h := range t.Headers {
widths[i] = lipgloss.Width(h)
}
for _, row := range t.Rows {
for i, cell := range row {
if i < len(widths) && lipgloss.Width(cell) > widths[i] {
widths[i] = lipgloss.Width(cell)
}
}
}
Prompt To Fix With AIThis is a comment left during a code review.
Path: internal/ui/table.go
Line: 53-56
Comment:
**Mixed byte-count vs. terminal-column comparison in auto-constrain logic**
`widths[i]` values are populated using `len(h)` (header) and `len(cell)` (row cells), which return **byte lengths**. `GetTerminalWidth()` returns **terminal columns** (visual width). For pure ASCII these are equivalent, but multi-byte UTF-8 content (e.g., emoji in headers like `"🔍 Symbol"` used in `explain.go`, or CJK characters) will cause `total` (bytes) to exceed `available` (columns) even when the table would visually fit, triggering unnecessary proportional shrinking. In extreme cases with wide characters, columns get over-shrunk and the table renders narrower than the terminal.
The fix is to measure visual width using `lipgloss.Width(s)` (which accounts for Unicode and ANSI sequences) when calculating `widths`:
```go
// Instead of len(h) / len(cell), use:
import "github.com/charmbracelet/lipgloss"
// In ColumnWidths():
for i, h := range t.Headers {
widths[i] = lipgloss.Width(h)
}
for _, row := range t.Rows {
for i, cell := range row {
if i < len(widths) && lipgloss.Width(cell) > widths[i] {
widths[i] = lipgloss.Width(cell)
}
}
}
```
`padRight` has the same `len(s)` issue — it should also use `lipgloss.Width(s)` to avoid producing visually misaligned padding for Unicode content.
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||||||||||||||||||||||
| for _, w := range widths { | ||||||||||||||||||||||||||||||||||||||||||||
| total += w | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| if total > available { | ||||||||||||||||||||||||||||||||||||||||||||
| // Proportionally shrink columns, but keep a minimum of 4 chars | ||||||||||||||||||||||||||||||||||||||||||||
| ratio := float64(available) / float64(total) | ||||||||||||||||||||||||||||||||||||||||||||
| for i := range widths { | ||||||||||||||||||||||||||||||||||||||||||||
| newW := int(float64(widths[i]) * ratio) | ||||||||||||||||||||||||||||||||||||||||||||
| if newW < 4 { | ||||||||||||||||||||||||||||||||||||||||||||
| newW = 4 | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| widths[i] = newW | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| // Post-clamp: if min-floor caused overflow, trim widest columns | ||||||||||||||||||||||||||||||||||||||||||||
| for { | ||||||||||||||||||||||||||||||||||||||||||||
| postTotal := 0 | ||||||||||||||||||||||||||||||||||||||||||||
| for _, w := range widths { | ||||||||||||||||||||||||||||||||||||||||||||
| postTotal += w | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| excess := postTotal - available | ||||||||||||||||||||||||||||||||||||||||||||
| if excess <= 0 { | ||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| // Find widest column and shrink it | ||||||||||||||||||||||||||||||||||||||||||||
| maxIdx, maxW := 0, 0 | ||||||||||||||||||||||||||||||||||||||||||||
| for i, w := range widths { | ||||||||||||||||||||||||||||||||||||||||||||
| if w > maxW { | ||||||||||||||||||||||||||||||||||||||||||||
| maxIdx, maxW = i, w | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| // Don't shrink below minimum | ||||||||||||||||||||||||||||||||||||||||||||
| if maxW <= 4 { | ||||||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| shrink := excess | ||||||||||||||||||||||||||||||||||||||||||||
| if shrink > maxW-4 { | ||||||||||||||||||||||||||||||||||||||||||||
| shrink = maxW - 4 | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| widths[maxIdx] -= shrink | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+60
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minimum-clamp can still produce overflowing column totals. After applying the proportional Example: A follow-up validation is needed after the clamp loop to either re-trim the widest columns or document that overflow is accepted:
Suggested change
At minimum, add a comment documenting that the 4-char floor can cause overflow. Prompt To Fix With AIThis is a comment left during a code review.
Path: internal/ui/table.go
Line: 57-68
Comment:
Minimum-clamp can still produce overflowing column totals.
After applying the proportional `ratio` (line 59), each column is floored to a minimum of 4 characters (lines 62–64). With many columns, the total `4*n + overhead` can exceed `termWidth`, meaning the table will overflow even after the "constrain" pass. There is no post-clamp overflow check.
Example: `termWidth=80`, 15 columns, overhead=29, available=51, ratio≈0.34. Each `newW = int(10*0.34)=3`, clamped to `4`. Post-clamp total = `4*15+29 = 89 > 80`.
A follow-up validation is needed after the clamp loop to either re-trim the widest columns or document that overflow is accepted:
```suggestion
// After the minimum-clamp loop, check if post-clamp total still exceeds available
postClampTotal := 0
for _, w := range widths {
postClampTotal += w
}
if postClampTotal > available {
// Post-clamp overflow detected. Accept gracefully or trim.
// Consider logging a debug message or trimming extra from widest columns.
}
```
At minimum, add a comment documenting that the 4-char floor can cause overflow.
How can I resolve this? If you propose a fix, please make it concise.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed — added post-clamp overflow loop that trims widest columns until total fits within available width. |
||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| return widths | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -101,6 +158,20 @@ func padRight(s string, width int) string { | |||||||||||||||||||||||||||||||||||||||||||
| return s + strings.Repeat(" ", width-len(s)) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // GetTerminalWidthFor returns the terminal width for the given file descriptor, defaulting to 80. | ||||||||||||||||||||||||||||||||||||||||||||
| func GetTerminalWidthFor(f *os.File) int { | ||||||||||||||||||||||||||||||||||||||||||||
| w, _, err := term.GetSize(int(f.Fd())) | ||||||||||||||||||||||||||||||||||||||||||||
| if err != nil || w <= 0 { | ||||||||||||||||||||||||||||||||||||||||||||
| return 80 | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| return w | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // GetTerminalWidth returns the current stdout terminal width, defaulting to 80. | ||||||||||||||||||||||||||||||||||||||||||||
| func GetTerminalWidth() int { | ||||||||||||||||||||||||||||||||||||||||||||
| return GetTerminalWidthFor(os.Stdout) | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // TruncateID shortens an ID for display (first 6 chars). | ||||||||||||||||||||||||||||||||||||||||||||
| func TruncateID(id string) string { | ||||||||||||||||||||||||||||||||||||||||||||
| if len(id) > 6 { | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Highly specific benchmark claims lack reproducibility caveats
The figures
25,000 tokens,1,500 tokens,42 seconds, and3 minutesare labeled "Real session, real numbers" but will vary significantly by repository size, AI model, query complexity, network latency, and TaskWing knowledge-base completeness. Presenting single-datapoint figures as representative benchmarks without any caveat could mislead users whose results differ substantially.Consider adding a qualifier such as:
Or add a footnote explaining the test environment (repo LOC, model used, knowledge-base node count).
Prompt To Fix With AI