Skip to content

Enrich JSON, extend sampling intervals, show refresh hint#23

Merged
programmersd21 merged 1 commit into
mainfrom
feat/enrich-json-refresh-hint
Jul 8, 2026
Merged

Enrich JSON, extend sampling intervals, show refresh hint#23
programmersd21 merged 1 commit into
mainfrom
feat/enrich-json-refresh-hint

Conversation

@programmersd21

@programmersd21 programmersd21 commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Closes #19, #20, #21

  • Enrich --once --json snapshot output with status, timestamp, download_human, upload_human fields
  • Show current sampling rate in footer for 2s when +/- is pressed
  • Add 3s, 5s, 10s sampling intervals for long-term monitoring
  • Close Network speed units should include "bits", and not just "bytes" #22 as already implemented (bits mode exists since v0.1.3)

Summary by Sourcery

Enrich JSON snapshot output and improve sampling interval controls and feedback for both short-term and long-term monitoring use cases.

New Features:

  • Include status, timestamp, and human-readable download/upload rates in one-shot JSON output for easier script consumption.
  • Display a temporary footer hint showing the current sampling interval when the refresh rate is adjusted.
  • Extend available sampling intervals with longer options suitable for long-term monitoring (3s, 5s, 10s).

Summary by CodeRabbit

  • New Features
    • Added extended sampling intervals for longer monitoring: 3s, 5s, and 10s.
    • The status footer now briefly shows the current interval after changing refresh speed.
    • --once --json output now includes status, timestamp, and human-readable download/upload rates.

@sourcery-ai

sourcery-ai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR enriches one-shot JSON output for scripting, extends the available sampling intervals, and adds a transient UI hint that shows the active refresh rate when the user changes it.

Sequence diagram for refresh hint when adjusting sampling interval

sequenceDiagram
    actor User
    participant Model
    participant Sampler
    participant Runtime

    User->>Model: handleKey(Faster)
    activate Model
    Model->>Model: adjustRefreshInterval(true)
    Note over Model: updates m.refreshInterval
    Model->>Sampler: sampler.New(col, m.refreshInterval)
    Sampler-->>Model: smp (started via smp.Run)
    Model->>Model: set m.refreshHint
    Model-->>Runtime: tea.Tick(2s, func(t) refreshHintClearMsg)
    deactivate Model

    Runtime-->>Model: refreshHintClearMsg
    activate Model
    Model->>Model: Update(refreshHintClearMsg)
    Model->>Model: m.refreshHint = ""
    deactivate Model
Loading

File-Level Changes

Change Details Files
Add transient UI hint showing current sampling interval when + / - keys are pressed.
  • Introduce refreshHintClearMsg and refreshHint field on the UI model to track a temporary footer message.
  • Update key handling for Faster/Slower to return a tea.Batch combining sampling wait and hint-clear command.
  • Extend adjustRefreshInterval to return a tea.Cmd that sets refreshHint and schedules its clearing after 2 seconds.
  • Render refreshHint in the dashboard footer when present.
internal/ui/model.go
internal/ui/views.go
Extend sampling interval options to support longer-term monitoring.
  • Add 3s, 5s, and 10s entries to the refresh interval list used by adjustRefreshInterval.
  • Ensure sampler recreation uses the newly selected interval without changing existing behavior for shorter intervals.
internal/ui/model.go
Enrich --once --json output to include status, timestamp, and human-readable throughput fields.
  • Format human-readable download/upload rates using FormatBpsExt considering unit and bits mode.
  • Add status, timestamp (RFC3339), download_human, and upload_human fields to the JSON map while preserving existing numeric fields.
  • Continue to emit indented JSON via the existing encoder setup.
cmd/flow/main.go
Document the new behavior and bump the version for release.
  • Add a 0.1.5 section to the changelog describing JSON enrichment, refresh hint, and extended sampling range.
  • Update the VERSION file to the new release version.
CHANGELOG.md
VERSION

Assessment against linked issues

Issue Objective Addressed Explanation
#19 Add a status field to the flow --once --json snapshot output indicating success.
#19 Add a timestamp field to the flow --once --json snapshot output so each snapshot is self-contained.
#19 Add human-readable download_human and upload_human fields to the flow --once --json snapshot output while preserving existing numeric fields.
#22 Provide a mode or option to display network speed in bits (e.g., Mbps/Gbps) instead of only bytes (e.g., MB/s), so users can see bandwidth usage without converting bytes to bits. The diff does not add or modify any functionality related to bits-based units; it only enriches JSON output, extends sampling intervals, and adds a refresh hint. The PR body claims bits mode already exists since v0.1.3 and thus closes the issue, but this PR itself does not introduce or change that feature in code or documentation.

Possibly linked issues

  • #Feature request: enrich --once --json snapshot output with timestamp/status/human fields: PR directly adds status, timestamp, and human-readable fields to --once --json JSON output, matching the issue.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@programmersd21 programmersd21 merged commit 2c8b553 into main Jul 8, 2026
8 of 9 checks passed
@programmersd21 programmersd21 deleted the feat/enrich-json-refresh-hint branch July 8, 2026 10:49
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5cfa1010-bbc4-490c-a681-506f6ee6857b

📥 Commits

Reviewing files that changed from the base of the PR and between e10b981 and 5a49d30.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • VERSION
  • cmd/flow/main.go
  • internal/ui/model.go
  • internal/ui/views.go

📝 Walkthrough

Walkthrough

This PR enriches flow --once --json output with status, timestamp, and human-readable throughput fields, adds a temporary sampling-interval hint to the TUI footer when adjusting refresh rate, extends the refresh interval ladder with 3s/5s/10s options, and updates CHANGELOG/VERSION to 0.1.5.

Changes

JSON output and refresh hint updates

Layer / File(s) Summary
Enrich --once --json snapshot output
cmd/flow/main.go
One-shot JSON output now includes status, timestamp (RFC3339), download_human, and upload_human alongside existing numeric Bps and unit_display fields.
Refresh interval hint and extended sampling ladder
internal/ui/model.go, internal/ui/views.go
Adds refreshHintClearMsg and Model.refreshHint; adjustRefreshInterval now returns a tea.Cmd, sets a temporary "every " hint cleared via a 2-second tick, extends interval options with 3s/5s/10s, and the footer renders the hint when set.
Changelog and version bump
CHANGELOG.md, VERSION
Adds a 0.1.5 changelog entry describing the above changes and bumps VERSION from 0.1.4 to 0.1.5.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Model
  participant View as dashboardContentLines
  User->>Model: press + or - key
  Model->>Model: adjustRefreshInterval returns tea.Cmd
  Model->>Model: set refreshHint to "every <interval>"
  Model->>Model: schedule tea.Tick 2s
  View->>View: render refreshHint in footer
  Model->>Model: refreshHintClearMsg received after tick
  Model->>Model: clear refreshHint
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/enrich-json-refresh-hint

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="internal/ui/model.go" line_range="393" />
<code_context>
+	if m.ifaceName != "" {
</code_context>
<issue_to_address>
**issue (bug_risk):** Sampler restart on refresh interval changes may leak goroutines or duplicate sampling without cancelling the previous one.

Adjusting the refresh interval currently creates a new sampler with a new background context but never cancels the old one. If the interval is changed multiple times, you can end up with several samplers running in parallel and leaked goroutines. Please ensure the previous sampler/context is cleanly stopped before starting a new one, or update the existing sampler’s configuration instead of recreating it.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread internal/ui/model.go
m.smp = sampler.New(col, m.refreshInterval)
go m.smp.Run(ctx)

m.refreshHint = fmt.Sprintf("every %s", m.refreshInterval)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Sampler restart on refresh interval changes may leak goroutines or duplicate sampling without cancelling the previous one.

Adjusting the refresh interval currently creates a new sampler with a new background context but never cancels the old one. If the interval is changed multiple times, you can end up with several samplers running in parallel and leaked goroutines. Please ensure the previous sampler/context is cleanly stopped before starting a new one, or update the existing sampler’s configuration instead of recreating it.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the application to version 0.1.5, introducing an enriched JSON snapshot output, extended sampling intervals (3s, 5s, and 10s), and a temporary footer notification showing the current refresh rate when adjusted. Feedback on these changes highlights a critical goroutine leak when adjusting the refresh interval due to unclosed channels, a recommendation to use the sample's actual timestamp rather than the current system time in the JSON output, and a race condition in the refresh hint timer that can be resolved by tracking and verifying timestamps on clear messages.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread internal/ui/model.go
Comment on lines 321 to +327
case key.Matches(msg, m.keys.Faster):
m.adjustRefreshInterval(true)
return m, waitForSample(m.smp.Out)
cmd := m.adjustRefreshInterval(true)
return m, tea.Batch(waitForSample(m.smp.Out), cmd)

case key.Matches(msg, m.keys.Slower):
m.adjustRefreshInterval(false)
return m, waitForSample(m.smp.Out)
cmd := m.adjustRefreshInterval(false)
return m, tea.Batch(waitForSample(m.smp.Out), cmd)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Goroutine Leak on Refresh Interval Adjustment

Every time the refresh interval is adjusted (via Faster or Slower keys), a new Sampler is created with a new Out channel, and a new waitForSample goroutine is started to read from it via tea.Batch(waitForSample(m.smp.Out), cmd).

However, the previous waitForSample goroutine remains blocked indefinitely on the old Out channel because:

  1. The old channel is never closed.
  2. The old sampler's Run loop has exited (due to m.samplerCtx() cancellation) and will never write to it again.

This results in a goroutine leak every time the user adjusts the refresh rate (or cycles the interface).

Recommended Fix:

  1. Close the old channel in adjustRefreshInterval (and similarly in the interface cycling logic) to unblock the old reader:
    m.samplerCtx()
    close(m.smp.Out) // Unblock the old waitForSample goroutine
  2. Update waitForSample (around line 512) to handle channel closure gracefully:
    func waitForSample(ch <-chan sampler.Sample) tea.Cmd {
        return func() tea.Msg {
            s, ok := <-ch
            if !ok {
                return nil
            }
            return sampleMsg(s)
        }
    }

Comment thread cmd/flow/main.go
"interface": s.Interface,
"unit_display": autoUnitExt(s.DownBps, bits),
"status": "ok",
"timestamp": time.Now().Format(time.RFC3339),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the sample's actual timestamp (s.At) instead of time.Now() to ensure the JSON output reflects the exact time the network metrics were captured.

Suggested change
"timestamp": time.Now().Format(time.RFC3339),
"timestamp": s.At.Format(time.RFC3339),

Comment thread internal/ui/model.go

type pingMsg time.Duration

type refreshHintClearMsg struct{}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using an empty struct for refreshHintClearMsg can lead to a race/overlapping timer issue. If the user presses + or - multiple times quickly, multiple clear messages are scheduled. When the first one fires, it will prematurely clear the hint for the subsequent presses. Changing refreshHintClearMsg to carry a timestamp allows us to ensure we only clear the hint if it matches the most recent update.

Suggested change
type refreshHintClearMsg struct{}
type refreshHintClearMsg time.Time

Comment thread internal/ui/model.go
downPulse float64
upPulse float64
pingLatency time.Duration
refreshHint string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add a refreshHintTime field to track when the last refresh hint was set, allowing us to prevent older scheduled timers from clearing newer hints.

Suggested change
refreshHint string
refreshHint string
refreshHintTime time.Time

Comment thread internal/ui/model.go
Comment on lines +172 to +174
case refreshHintClearMsg:
m.refreshHint = ""
return m, nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Only clear the refresh hint if the message's timestamp matches the most recently set hint's timestamp. This prevents older scheduled clear messages from clearing newer hints.

Suggested change
case refreshHintClearMsg:
m.refreshHint = ""
return m, nil
case refreshHintClearMsg:
if time.Time(msg).Equal(m.refreshHintTime) {
m.refreshHint = ""
}
return m, nil

Comment thread internal/ui/model.go
Comment on lines +393 to +394
m.refreshHint = fmt.Sprintf("every %s", m.refreshInterval)
return tea.Tick(2*time.Second, func(t time.Time) tea.Msg { return refreshHintClearMsg{} })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Capture the current timestamp when setting the refresh hint, and pass it to the scheduled refreshHintClearMsg so we can uniquely identify this timer event.

		now := time.Now()
		m.refreshHint = fmt.Sprintf("every %s", m.refreshInterval)
		m.refreshHintTime = now
		return tea.Tick(2*time.Second, func(t time.Time) tea.Msg { return refreshHintClearMsg(now) })

programmersd21 added a commit that referenced this pull request Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant