Skip to content

Commit 6b8a123

Browse files
v0.2.1
- Fix panic when sampling interval is set to 300s (NaN guard in Clamp01, fiveStopGradient, SpeedRatio) - Add display filter: press 'd' to cycle download-only / upload-only / both - Update demo.tape with display filter sequence - Regenerate and compress demo.gif with gifsicle Closes #27 Closes #28
1 parent 7b518df commit 6b8a123

11 files changed

Lines changed: 84 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
## [0.2.1] - 2026-07-23
2+
3+
### Added
4+
- Display filter — Press `d` to cycle through download-only, upload-only, or both panels, allowing full focus on a single direction.
5+
- Footer indicator shows `[down only]` or `[up only]` when a filter is active.
6+
7+
### Fixed
8+
- Panic when sampling interval was set to 300s — `Clamp01` now guards against NaN values, preventing `fiveStopGradient` from computing an out-of-range index with `int(NaN)`.
9+
- Added NaN/Inf guards in `SpeedRatio` and `fiveStopGradient` for defense-in-depth against floating-point edge cases.
10+
11+
### Changed
12+
- VERSION bumped to 0.2.1.
13+
114
## [0.2.0] - 2026-07-15
215

316
### Added

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,14 @@ flow --help
132132
### Keybindings
133133

134134
| Key | Action | Key | Action |
135-
|---|---|---|---|
135+
|---|---|---|---|---|
136136
| `q` / `^C` | Quit | `c` | Cycle display units |
137137
| `m` | Cycle display modes | `b` | Toggle bits/bytes |
138-
| `n` | Network processes | `+` / `-` | Adjust refresh interval |
139-
| `t` | Choose theme | `p` | Pause / resume |
140-
| `i` | Cycle interfaces | `?` | Help |
141-
| `I` | Interface info | `esc` | Back / close overlay |
142-
| `r` | Reset peaks (press twice) | | |
138+
| `d` | Cycle display filter | `+` / `-` | Adjust refresh interval |
139+
| `n` | Network processes | `p` | Pause / resume |
140+
| `t` | Choose theme | `?` | Help |
141+
| `i` | Cycle interfaces | `esc` | Back / close overlay |
142+
| `I` | Interface info | `r` | Reset peaks (press twice) |
143143

144144
### JSON output
145145

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.2.0
1+
0.2.1

assets/demo.gif

-727 KB
Loading

cmd/flow/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"github.com/programmersd21/flow/internal/ui"
1717
)
1818

19-
var version = "dev"
19+
var version = "0.2.1"
2020

2121
func main() {
2222
flagTiny := flag.Bool("tiny", false, "single-line mode for tmux/status bars")

demo.tape

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ Sleep 600ms
123123
Type "m"
124124
Sleep 600ms
125125

126+
# Display filter — cycle through download-only → upload-only → both
127+
Type "d"
128+
Sleep 1s
129+
Type "d"
130+
Sleep 1s
131+
Type "d"
132+
Sleep 800ms
133+
126134
# Bits + units
127135
Type "b"
128136
Sleep 600ms

internal/animate/ease.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func ColorLerp(r1, g1, b1, r2, g2, b2 uint8, t float64) (uint8, uint8, uint8) {
2020
}
2121

2222
func Clamp01(t float64) float64 {
23-
if t < 0 {
23+
if t < 0 || math.IsNaN(t) {
2424
return 0
2525
}
2626
if t > 1 {

internal/theme/theme.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ func fiveStopGradient(stops [5][3]uint8, t float64) (uint8, uint8, uint8) {
464464
t = animate.Clamp01(t)
465465
segment := t * 4.0
466466
idx := int(segment)
467-
if idx >= 4 {
467+
if idx >= 4 || idx < 0 {
468468
idx = 3
469469
segment = 4.0
470470
}
@@ -475,7 +475,7 @@ func fiveStopGradient(stops [5][3]uint8, t float64) (uint8, uint8, uint8) {
475475
}
476476

477477
func SpeedRatio(current, rollingMax float64) float64 {
478-
if rollingMax <= 0 {
478+
if rollingMax <= 0 || math.IsNaN(rollingMax) || math.IsInf(rollingMax, 0) {
479479
return 0
480480
}
481481
return animate.Clamp01(current / rollingMax)

internal/ui/keys.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type KeyMap struct {
1414
Mode key.Binding
1515
Processes key.Binding
1616
Bits key.Binding
17+
Display key.Binding
1718
Faster key.Binding
1819
Slower key.Binding
1920
Themes key.Binding
@@ -66,6 +67,10 @@ func DefaultKeyMap() KeyMap {
6667
key.WithKeys("b"),
6768
key.WithHelp("b", "toggle bits/bytes"),
6869
),
70+
Display: key.NewBinding(
71+
key.WithKeys("d"),
72+
key.WithHelp("d", "cycle display filter"),
73+
),
6974
Faster: key.NewBinding(
7075
key.WithKeys("+", "="),
7176
key.WithHelp("+", "faster refresh"),

internal/ui/model.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ type ifaceDetailMsg struct {
3333
err error
3434
}
3535

36+
type DisplayFilter int
37+
38+
const (
39+
DisplayBoth DisplayFilter = iota
40+
DisplayDownOnly
41+
DisplayUpOnly
42+
)
43+
3644
const (
3745
slopeWindow = 6
3846
resetConfirmTimeout = 2 * time.Second
@@ -86,6 +94,7 @@ type Model struct {
8694
unitMode UnitMode
8795
viewMode ViewMode
8896
bitsMode bool
97+
displayFilter DisplayFilter
8998
procs []processes.Info
9099

91100
width, height int
@@ -362,6 +371,9 @@ func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
362371
case key.Matches(msg, m.keys.Bits):
363372
m.bitsMode = !m.bitsMode
364373

374+
case key.Matches(msg, m.keys.Display):
375+
m.displayFilter = (m.displayFilter + 1) % 3
376+
365377
case key.Matches(msg, m.keys.Faster):
366378
m.adjustRefreshInterval(true)
367379
return m, waitForSample(m.smp.Out)

0 commit comments

Comments
 (0)