Skip to content

Commit a356496

Browse files
fix config path for windows/macos, make --tiny independent of bubble tea
1 parent b6dcb2f commit a356496

17 files changed

Lines changed: 425 additions & 111 deletions

File tree

CHANGELOG.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,26 @@
1-
# Changelog
1+
## [0.1.1] - 2026-07-05
2+
3+
### Added
4+
5+
- Graphs-only "mini" mode (`--mini`) showing just download/upload panels and waveforms, omitting global title, today's summary, active interface, and key help hints.
6+
- Key binding `m` to interactively cycle through view modes (`hero` -> `compact` -> `mini` -> `tiny` -> `hero`).
7+
- Responsive vertical layout resizing, automatically scaling down to mini mode when the screen height is too small for compact/hero dashboards.
8+
- Premium dev-tool theme styling inspired by Stripe, Spotify, and Apple aesthetics, featuring high-contrast vertical gradients.
9+
- Sleek, modern rounded borders for a clean and unified desktop-TUI look.
10+
- Minimalist, high-end unicode today statistics using colored down/upload arrows (`` / ``) and clean accent-colored values.
11+
- Refined dot-separated (`·`) status and navigation footer containing real-time active/paused interface dot status (``) and highlighted key binds.
12+
- Live peak pulsing white-flash animations when a new session throughput record is reached.
13+
- Clean modal help overlay with modern rounded border styling and highlighted keys.
14+
- `--tiny` mode is now fully independent of Bubble Tea, works reliably in tmux `#(...)`, cron, pipes, and redirected stdout
15+
- Platform-specific config paths: Linux (`~/.config/flow/config.toml`), macOS (`~/Library/Application Support/flow/config.toml`), Windows (`%APPDATA%/flow/config.toml`)
16+
17+
### Fixed
18+
19+
- Daily traffic totals failing to reset when the calendar month/year changes (now compares full date: year, month, and day).
20+
- TUI and one-shot modes hanging indefinitely on network counter read errors (now propagates errors through sampler and exits gracefully with a message).
21+
- Config file not being created on macOS and Windows due to non-standard path resolution
22+
- `--tiny` no longer initializes Bubble Tea, Lip Gloss, termenv, or terminal queries — zero TTY dependency
23+
- `--tiny --no-color` emits clean plain text with no ANSI sequences
224

325
## [0.1.0] - 2026-07-04
426

README.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,24 +122,25 @@ The interface is built for restraint rather than density: large typography, cont
122122

123123
## Modes
124124

125-
flow adjusts its display according to terminal width.
125+
flow adjusts its display according to terminal width and height.
126126

127-
| hero | compact | tiny |
128-
|:---:|:---:|:---:|
129-
| <img src="./docs/normal_mode.png" alt="hero mode"> | <img src="./docs/compact_mode.png" alt="compact mode"> | <img src="./docs/tiny_mode.png" alt="tiny mode"> |
130-
| Full dashboard with sparklines, peaks, and daily totals | Numeric values only, for narrow terminals | Single-line output, intended for status bars |
127+
| hero | compact | mini | tiny |
128+
|:---:|:---:|:---:|:---:|
129+
| <img src="./docs/normal_mode.png" alt="hero mode"> | <img src="./docs/compact_mode.png" alt="compact mode"> | | <img src="./docs/tiny_mode.png" alt="tiny mode"> |
130+
| Full dashboard with logo branding, waveforms, peaks, and daily totals | Cleaner layout with title row, waveforms, peaks, and daily totals | Graphs-only layout, hiding header logo, today's summary, and help footers | Single-line output, intended for status bars |
131131

132132
## Features
133133

134134
- Real-time download and upload throughput
135135
- Interpolated display values using spring-based animation
136136
- Braille-grid waveform rendering at 30 frames per second
137-
- Border color reflects current transfer speed
138-
- Visual indication when a new session peak is recorded
137+
- Border color reflects current transfer speed with sleek, modern rounded outlines
138+
- Live peak pulsing white-flash animations when a new session peak is reached
139+
- Minimalist, high-end unicode today statistics and navigation footer
139140
- Directional indicators for traffic trend
140141
- Automatic unit scaling from B/s to GB/s
141142
- Session peak tracking and daily traffic totals
142-
- Three display modes with automatic switching on resize
143+
- Four display modes with automatic responsive switching on both width and height resize
143144
- No required configuration; optional TOML configuration file
144145
- Non-interactive output modes for use in scripts
145146
- Supported on Linux, macOS, and Windows
@@ -151,7 +152,8 @@ flow adjusts its display according to terminal width.
151152
```sh
152153
flow # hero view, auto interface
153154
flow --tiny # single-line mode for status bars
154-
flow --compact # numeric values only
155+
flow --mini # graphs-only mode, no headers/footers
156+
flow --compact # compact layout with title row and waveforms
155157
flow --json # single JSON output, then exit
156158
flow --once # single plain-text output, then exit
157159
flow --interface wlan0 # specify network interface
@@ -168,6 +170,7 @@ flow --help
168170
| Key | Action |
169171
|-------------|-----------------------------|
170172
| `q` / `^C` | Quit |
173+
| `m` | Cycle display/view modes |
171174
| `r` | Reset session peaks |
172175
| `i` | Cycle network interfaces |
173176
| `c` | Cycle display units |
@@ -197,7 +200,15 @@ set -g status-interval 1
197200

198201
## Configuration
199202

200-
A configuration file is created automatically at `~/.config/flow/config.toml` on first run. The `XDG_CONFIG_HOME` environment variable is respected if set.
203+
A configuration file is created automatically on first run:
204+
205+
| Platform | Path |
206+
|----------|------|
207+
| Linux | `~/.config/flow/config.toml` |
208+
| macOS | `~/Library/Application Support/flow/config.toml` |
209+
| Windows | `%APPDATA%\flow\config.toml` |
210+
211+
The `XDG_CONFIG_HOME` environment variable is respected on Linux if set.
201212

202213
```toml
203214
refresh = "100ms" # sampling interval

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0
1+
0.1.1

cmd/flow/main.go

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ var version = "dev"
2424

2525
func main() {
2626
flagTiny := flag.Bool("tiny", false, "single-line mode for tmux/status bars")
27+
flagMini := flag.Bool("mini", false, "graphs-only mini mode")
2728
flagCompact := flag.Bool("compact", false, "numbers-only compact mode")
2829
flagJSON := flag.Bool("json", false, "one-shot JSON snapshot, then exit")
2930
flagOnce := flag.Bool("once", false, "one-shot plain-text snapshot, then exit")
@@ -71,6 +72,11 @@ func main() {
7172
return
7273
}
7374

75+
if *flagTiny {
76+
runTiny(col, smp, refresh, cfg.NoColor)
77+
return
78+
}
79+
7480
ifaces, err := collector.Interfaces()
7581
if err != nil {
7682
ifaces = []string{cfg.Interface}
@@ -81,8 +87,8 @@ func main() {
8187

8288
var forced ui.ViewMode
8389
switch {
84-
case *flagTiny:
85-
forced = ui.ViewTiny
90+
case *flagMini:
91+
forced = ui.ViewMini
8692
case *flagCompact:
8793
forced = ui.ViewCompact
8894
default:
@@ -98,15 +104,51 @@ func main() {
98104
model := ui.New(cfg, smp, ifaces, initialIface, cancel, forced)
99105

100106
opts := []tea.ProgramOption{tea.WithAltScreen()}
101-
if *flagTiny || *flagCompact {
107+
if *flagCompact || *flagMini {
102108
opts = []tea.ProgramOption{}
103109
}
104110

105111
p := tea.NewProgram(model, opts...)
106-
if _, err := p.Run(); err != nil {
112+
m, err := p.Run()
113+
if err != nil {
107114
fmt.Fprintf(os.Stderr, "flow: %v\n", err)
108115
os.Exit(1)
109116
}
117+
if finalModel, ok := m.(ui.Model); ok && finalModel.Err() != nil {
118+
fmt.Fprintf(os.Stderr, "flow: %v\n", finalModel.Err())
119+
os.Exit(1)
120+
}
121+
}
122+
123+
// runTiny collects a single sample and prints a compact one-line summary.
124+
// Completely independent of Bubble Tea — works in tmux, cron, pipes.
125+
func runTiny(col *collector.Collector, smp *sampler.Sampler, refresh time.Duration, noColor bool) {
126+
ctx, cancel := context.WithCancel(context.Background())
127+
defer cancel()
128+
129+
go smp.Run(ctx)
130+
131+
// Wait for two samples so the first diff is valid.
132+
s1 := <-smp.Out
133+
if s1.Err != nil {
134+
fmt.Fprintf(os.Stderr, "flow: %v\n", s1.Err)
135+
os.Exit(1)
136+
}
137+
s := <-smp.Out
138+
if s.Err != nil {
139+
fmt.Fprintf(os.Stderr, "flow: %v\n", s.Err)
140+
os.Exit(1)
141+
}
142+
cancel()
143+
144+
down := ui.FormatBps(s.DownBps, ui.UnitAuto)
145+
up := ui.FormatBps(s.UpBps, ui.UnitAuto)
146+
147+
if noColor {
148+
fmt.Printf("↓ %s · ↑ %s\n", down, up)
149+
} else {
150+
fmt.Printf("↓ %s · ↑ %s\n", down, up)
151+
}
110152
}
111153

112154
// runOnce takes exactly one sample and either prints JSON or plain text, then
@@ -118,8 +160,16 @@ func runOnce(col *collector.Collector, smp *sampler.Sampler, refresh time.Durati
118160
go smp.Run(ctx)
119161

120162
// Wait for two samples so the first diff is valid.
121-
<-smp.Out
163+
s1 := <-smp.Out
164+
if s1.Err != nil {
165+
fmt.Fprintf(os.Stderr, "flow: %v\n", s1.Err)
166+
os.Exit(1)
167+
}
122168
s := <-smp.Out
169+
if s.Err != nil {
170+
fmt.Fprintf(os.Stderr, "flow: %v\n", s.Err)
171+
os.Exit(1)
172+
}
123173
cancel()
124174

125175
if asJSON {

docs/compact_mode.png

-11.5 KB
Loading

docs/demo.gif

1.88 MB
Loading

docs/keybinds.png

4.23 KB
Loading

docs/normal_mode.png

-1.78 KB
Loading

docs/tiny_mode.png

-5.89 KB
Loading

internal/config/config.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,19 @@ func Load() (Config, error) {
5656
return cfg, nil
5757
}
5858

59-
// configPath resolves the config file location (respects XDG_CONFIG_HOME).
59+
// configPath resolves the config file location using platform-specific paths.
6060
func configPath() (string, error) {
61-
base := os.Getenv("XDG_CONFIG_HOME")
62-
if base == "" {
63-
home, err := os.UserHomeDir()
64-
if err != nil {
65-
return "", err
61+
base, err := os.UserConfigDir()
62+
if err != nil {
63+
// Fallback to XDG-style path if os.UserConfigDir fails.
64+
base = os.Getenv("XDG_CONFIG_HOME")
65+
if base == "" {
66+
home, err2 := os.UserHomeDir()
67+
if err2 != nil {
68+
return "", err2
69+
}
70+
base = filepath.Join(home, ".config")
6671
}
67-
base = filepath.Join(home, ".config")
6872
}
6973
return filepath.Join(base, "flow", "config.toml"), nil
7074
}

0 commit comments

Comments
 (0)