Skip to content

Release v0.2.0 - #21

Merged
j4v3l merged 3 commits into
mainfrom
dev
Apr 29, 2026
Merged

j4v3l merged 3 commits into
mainfrom
dev

Conversation

@j4v3l

@j4v3l j4v3l commented Apr 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Release ADS-B TUI v0.2.0.
  • Includes radar viewport pan/zoom, dynamic feed URL templates, 2D radar selection, selected-target visibility, quit confirmation modal, and related docs/config updates.
  • Refreshes audited transitive TLS dependencies so the release branch passes cargo audit.

Local validation

  • cargo fmt -- --check passed
  • cargo clippy --all-targets -- -D warnings passed
  • cargo test --all-targets passed: 57 unit tests and 4 integration tests
  • cargo audit passed with only allowed advisory warnings
  • git diff --check and git diff --cached --check passed

GitHub validation

Summary by CodeRabbit

  • New Features

    • Interactive radar pan and zoom controls with dynamic feed URL updates
    • Directional nearest-aircraft selection using arrow keys
    • Quit confirmation modal with explicit confirm/cancel
    • Enhanced selected-aircraft rendering with ASCII fallback support
    • Dynamic feed URL templating with coordinate placeholders
  • Bug Fixes

    • Faster feed updates following radar pan/zoom operations
    • Improved selection panel differentiation for out-of-range and missing-position aircraft
  • Documentation

    • Added configuration guide for dynamic feed URL templating
    • Updated controls reference with new radar and selection keybindings

@coderabbitai

coderabbitai Bot commented Apr 29, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Version bump to 0.2.0 introducing dynamic feed URL templating with coordinate/range placeholders, radar zoom/pan/directional navigation with runtime feed updates, quit confirmation modal, improved selected-aircraft rendering, refactored config/fetcher wiring for runtime URL updates, and UI enhancements including scrollable help and theme adjustments.

Changes

Cohort / File(s) Summary
Version & Metadata
.github/ISSUE_TEMPLATE/bug-report.yml, .github/ISSUE_TEMPLATE/security.yml, Cargo.toml
Version placeholder updates from 0.1.0 to 0.2.0 across templates and package manifest.
Changelog & Documentation
CHANGELOG.md, README.md, docs/CONFIGURATION.md, docs/DATA_SOURCES.md
Release notes and docs for 0.2.0 features: dynamic URL templating with {lat}, {lon}, {range_nm} placeholders, radar controls (zoom/pan/direction select), quit confirmation, improved aircraft rendering, environment variable renames from ADSBTUI_* to ADSB_*.
Configuration System
src/config.rs
New fields url_template and url_templates with precedence logic; new exported functions active_url_templates, initial_fetch_urls, render_url_templates, render_url_template for template substitution; CLI option --url-template; security validation now evaluates template-rendered URLs; normalized URL handling.
Application Core
src/app.rs
Quit confirmation state added to InputMode; new RadarCenter and RadarDirection types; added radar_center, feed_templates, help_scroll to App; radar methods zoom_radar, pan_radar, refresh_feed_urls; directional selection via select_radar_direction; geospatial helpers (distance, bearing, offset, wrapping/clamping); comprehensive unit tests.
Main & Fetcher
src/main.rs, src/net.rs
Dynamic feed URL wiring: main.rs derives feed_templates and initial_feed_urls, creates feed_update_rx channel; spawn_fetcher now accepts update_rx parameter, drains source updates during fetch loop, resets backoff/index on update, replaces unconditional sleep with event-driven waiting.
Radar & Display
src/radar.rs
Added selected flag to radar points; selection highlighted separately (Dot renderer uses theme.highlight); ASCII fallback uses ascii_point_marker with priority ('X' for selected); rounded borders; target panel includes status lines for out-of-range/missing-position; extended unit tests for selection/bounds behavior.
Runtime Input
src/runtime.rs
New InputMode::QuitConfirm flow (Enter/Y quits, Esc/N closes); q opens quit in Normal/Columns/Help/Legend/Watchlist/Config modes; SHIFT+Arrow pans radar with feed updates; Arrow selects by direction in radar layout; +/- zoom radar with feed updates; feed_updates: Option<Sender<Vec<String>>> parameter added; new helpers send_feed_update and toggle_favorite.
UI & Styling
src/ui.rs
New render_quit_confirm modal overlay; refactored status rendering (color changed to theme.accent, uses short_error_code and parse_http_status); removed pulse animations; rounded borders; reworked scrollable help layout with app.help_scroll; column-aware text alignment (new TextAlign, align_text, column_align); updated theme palette and footer help.

Sequence Diagram

sequenceDiagram
    participant User
    participant Runtime
    participant App
    participant Config
    participant FeedUpdateCh as Feed Update<br/>Channel
    participant Fetcher
    participant Sources

    User->>Runtime: Press SHIFT+Arrow (Pan)<br/>or +/- (Zoom)
    Runtime->>App: pan_radar() or zoom_radar()
    App->>App: Update radar_center
    App->>Config: render_url_templates(...,<br/>new_lat, new_lon, new_range)
    Config-->>App: Vec<String> (rendered URLs)
    App->>App: refresh_feed_urls()
    App->>FeedUpdateCh: Send new URL list
    Fetcher->>FeedUpdateCh: Receive update
    Fetcher->>Sources: build_sources(new URLs)
    Sources-->>Fetcher: Updated source list
    Fetcher->>Fetcher: Reset backoff, fetch immediately
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Dev #12: Changes allow_http default and validates security against updated URL sources, directly impacting the new validate_security(initial_fetch_urls) logic.
  • Dev #13: Modifies fetcher spawn signature and feed/URL configuration wiring, overlapping with the new spawn_fetcher(update_rx) and dynamic URL template flow.
  • Dev #9: Simplifies radar selection and index handling, complementing the new selected-point state and directional selection mechanics.

Poem

🐰 A rabbit springs through radar bounds,
Pan and zoom with feed-URL rounds—
Templates bloom with {lat}, {lon}, {range},
Quit with care (confirm's not strange!),
Selection glows bright, help scrolls free! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete against the template requirements. While a summary and validation details are provided, the required sections—Type of Change, Related Issues, Testing checklist, and pre-merge Checklist—are absent or not properly filled. Complete all required template sections: select a Type of Change, add Related Issues links, provide explicit Testing details with checkboxes, and complete the Checklist section with all relevant items marked.
Docstring Coverage ⚠️ Warning Docstring coverage is 21.60% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Release v0.2.0' is concise, clear, and directly summarizes the main purpose of the PR—releasing version 0.2.0 of the ADS-B TUI project.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@README.md`:
- Around line 184-186: The zoom keybinding table row ("Zoom radar/feed range")
omits the `=` key; update that table entry to list both `+` and `=` (e.g., "`+`
/ `=` / `-`" or similar) so it matches CHANGELOG.md's advertised `+`/`=` zoom-in
binding and covers layouts where `+` requires Shift. Ensure the table cell text
for the Zoom row includes the `=` symbol alongside `+` to make the unshifted
binding discoverable.

In `@src/app.rs`:
- Around line 1431-1451: The aircraft_centroid_for function computes longitude
by simple averaging which breaks at the antimeridian; replace the raw lon sum
with a circular mean: accumulate sum_sin += sin(lon_rad) and sum_cos +=
cos(lon_rad) for each ac.lon, compute mean_lon_rad = atan2(sum_sin / count,
sum_cos / count), convert back to degrees, and normalize into the expected range
before returning RadarCenter; keep the latitude as the linear average and ensure
downstream callers like ensure_radar_center() and refresh_feed_urls() receive
the corrected RadarCenter.

In `@src/config.rs`:
- Around line 389-390: The new array setting url_templates (and the duplicate at
lines 454-455) can't round-trip because src/app.rs treats non-scalar TOML as
ConfigKind::Str and re-serializes as a quoted string; either add array-aware
handling or exclude the key from the editor: modify the ConfigKind enum (e.g.,
add ConfigKind::Array or ConfigKind::List) and update the loader/serializer in
src/app.rs to detect toml::Value::Array when reading url_templates (and
serialize it back as a TOML array), or alternatively add url_templates (and
url_template if needed) to the editor blacklist so they are not loaded/rewritten
by the editor; adjust code paths that construct/serialize ConfigKind values
accordingly so array values round-trip unchanged.

In `@src/radar.rs`:
- Around line 170-177: The code currently derives a single center
(center_lat/center_lon) from app.radar_center() or app.site() and uses it for
both plotting and metric readouts; change this to compute two distinct centers:
a plot_center (e.g., plot_center_lat/plot_center_lon) used for the radar
projection as currently chosen by app.radar_center() / pan logic, and a
metric_center (e.g., metric_center_lat/metric_center_lon) that is always
anchored to app.site() (fallback to the averaged sum_lat/sum_lon only if
app.site() is None). Keep the existing selection of plot center logic but ensure
metric_center is derived from app.site(), then update the call site of
selected_aircraft() to accept both centers so position stays radar-relative
while RNG/BRG calculations use metric_center.

In `@src/ui.rs`:
- Around line 1073-1077: Update the confirmation dialog text to use formal
language by replacing the colloquial "Are you sure you wanna quit?" in the lines
vector (the Line::from(...) entry) with "Are you sure you want to quit?" so the
dialog displays "Are you sure you want to quit?" while leaving the other
Line::from entries unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 654e6161-a8e7-4f61-8be2-51eee498413f

📥 Commits

Reviewing files that changed from the base of the PR and between 678a739 and 7e884bb.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • .github/ISSUE_TEMPLATE/bug-report.yml
  • .github/ISSUE_TEMPLATE/security.yml
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • docs/CONFIGURATION.md
  • docs/DATA_SOURCES.md
  • src/app.rs
  • src/config.rs
  • src/main.rs
  • src/net.rs
  • src/radar.rs
  • src/runtime.rs
  • src/ui.rs

Comment thread README.md
Comment on lines +184 to +186
| `+` / `-` | Zoom radar/feed range |
| `Shift` + `↑/↓/←/→` | Pan radar/feed center |
| `↑/↓/←/→` in radar view | Select nearest aircraft in that direction |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

List = as a zoom-in binding here too.

CHANGELOG.md Line 13 advertises +/= for zoom-in, but this table only mentions +. On many layouts + requires Shift, so omitting = makes the unshifted binding easy to miss.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 184 - 186, The zoom keybinding table row ("Zoom
radar/feed range") omits the `=` key; update that table entry to list both `+`
and `=` (e.g., "`+` / `=` / `-`" or similar) so it matches CHANGELOG.md's
advertised `+`/`=` zoom-in binding and covers layouts where `+` requires Shift.
Ensure the table cell text for the Zoom row includes the `=` symbol alongside
`+` to make the unshifted binding discoverable.

Comment thread src/app.rs
Comment on lines +1431 to +1451
fn aircraft_centroid_for(&self, indices: &[usize]) -> Option<RadarCenter> {
let mut sum_lat = 0.0;
let mut sum_lon = 0.0;
let mut count = 0usize;
for idx in indices {
let Some(ac) = self.data.aircraft.get(*idx) else {
continue;
};
if let (Some(lat), Some(lon)) = (ac.lat, ac.lon) {
sum_lat += lat;
sum_lon += lon;
count += 1;
}
}
if count == 0 {
None
} else {
Some(RadarCenter {
lat: sum_lat / count as f64,
lon: sum_lon / count as f64,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Antimeridian centroiding is incorrect.

This averages raw longitudes, so traffic clustered around 179°E and 179°W produces a center near 0°. Once ensure_radar_center() falls back to this centroid, radar navigation and refresh_feed_urls() can jump the viewport/feed request to the wrong side of the world. Use a circular mean for longitude (atan2(mean_sin, mean_cos)) instead of sum_lon / count.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app.rs` around lines 1431 - 1451, The aircraft_centroid_for function
computes longitude by simple averaging which breaks at the antimeridian; replace
the raw lon sum with a circular mean: accumulate sum_sin += sin(lon_rad) and
sum_cos += cos(lon_rad) for each ac.lon, compute mean_lon_rad = atan2(sum_sin /
count, sum_cos / count), convert back to degrees, and normalize into the
expected range before returning RadarCenter; keep the latitude as the linear
average and ensure downstream callers like ensure_radar_center() and
refresh_feed_urls() receive the corrected RadarCenter.

Comment thread src/config.rs
Comment on lines +389 to +390
pub url_template: Option<String>,
pub url_templates: Vec<String>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

url_templates won't round-trip through the config editor.

This adds an array-valued setting, but src/app.rs still loads non-scalar TOML values as ConfigKind::Str and saves Str back as a quoted string. A config containing url_templates = ["..."] will therefore be corrupted after an in-app save. Please add array-aware config kinds/serialization or exclude this key from the editor until that exists.

Also applies to: 454-455

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.rs` around lines 389 - 390, The new array setting url_templates
(and the duplicate at lines 454-455) can't round-trip because src/app.rs treats
non-scalar TOML as ConfigKind::Str and re-serializes as a quoted string; either
add array-aware handling or exclude the key from the editor: modify the
ConfigKind enum (e.g., add ConfigKind::Array or ConfigKind::List) and update the
loader/serializer in src/app.rs to detect toml::Value::Array when reading
url_templates (and serialize it back as a TOML array), or alternatively add
url_templates (and url_template if needed) to the editor blacklist so they are
not loaded/rewritten by the editor; adjust code paths that construct/serialize
ConfigKind values accordingly so array values round-trip unchanged.

Comment thread src/config.rs
Comment on lines +1391 to +1425
pub fn active_url_templates(config: &Config) -> Vec<String> {
let templates = if config.url_templates.is_empty() {
config
.url_template
.as_ref()
.map(|template| vec![template.clone()])
.unwrap_or_default()
} else {
config.url_templates.clone()
};
templates
.into_iter()
.map(|template| template.trim().to_string())
.filter(|template| !template.is_empty())
.collect()
}

pub fn initial_fetch_urls(config: &Config) -> Result<Vec<String>> {
let templates = active_url_templates(config);
if !templates.is_empty() {
let (Some(lat), Some(lon)) = (config.site_lat, config.site_lon) else {
return Err(anyhow!(
"url_template/url_templates require site_lat and site_lon"
));
};
return Ok(render_url_templates(
&templates,
lat,
lon,
config.radar_range_nm.max(1.0),
));
}

Ok(static_urls(config))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Template mode currently masks explicit static URL overrides.

initial_fetch_urls() always prefers url_template/url_templates whenever either is set. If a template comes from the config file, later ADSB_URL, ADSB_URLS, or --url values never take effect, so users can't temporarily switch back to a fixed feed without editing the file. This needs an explicit override path or a way for env/CLI inputs to clear template mode.

Comment thread src/radar.rs
Comment on lines +170 to +177
let (center_lat, center_lon) = app
.radar_center()
.map(|center| (center.lat, center.lon))
.or_else(|| app.site().map(|site| (site.lat, site.lon)))
.unwrap_or((
sum_lat / current_points as f64,
sum_lon / current_points as f64,
),
};
));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep target-panel range/bearing anchored to the configured site.

This center now prefers app.radar_center(), and those coordinates are later reused by selected_aircraft() for the RNG/BRG lines. After a pan, the selected-target panel will report range/bearing from the viewport center instead of the receiver/home site, which contradicts CHANGELOG.md Line 20. Split plot-center from metric-center here so panning only affects projection, not site-relative readouts.

Possible direction for the fix
-    let (center_lat, center_lon) = app
+    let (plot_center_lat, plot_center_lon) = app
         .radar_center()
         .map(|center| (center.lat, center.lon))
         .or_else(|| app.site().map(|site| (site.lat, site.lon)))
         .unwrap_or((
             sum_lat / current_points as f64,
             sum_lon / current_points as f64,
         ));
+
+    let (metric_center_lat, metric_center_lon) = app
+        .site()
+        .map(|site| (site.lat, site.lon))
+        .unwrap_or((plot_center_lat, plot_center_lon));

Then use plot_center_* for radar projection and pass both centers into selected_aircraft() so:

  • position stays radar-relative
  • RNG/BRG stays site-relative
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/radar.rs` around lines 170 - 177, The code currently derives a single
center (center_lat/center_lon) from app.radar_center() or app.site() and uses it
for both plotting and metric readouts; change this to compute two distinct
centers: a plot_center (e.g., plot_center_lat/plot_center_lon) used for the
radar projection as currently chosen by app.radar_center() / pan logic, and a
metric_center (e.g., metric_center_lat/metric_center_lon) that is always
anchored to app.site() (fallback to the averaged sum_lat/sum_lon only if
app.site() is None). Keep the existing selection of plot center logic but ensure
metric_center is derived from app.site(), then update the call site of
selected_aircraft() to accept both centers so position stays radar-relative
while RNG/BRG calculations use metric_center.
```

</details>

<!-- fingerprinting:phantom:medusa:grasshopper:327f2e9c-e4c6-420d-b02f-b487855268b7 -->

<!-- 4e71b3a2 -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment thread src/ui.rs
Comment on lines +1073 to +1077
let lines = vec![
Line::from("Are you sure you wanna quit?"),
Line::from(""),
Line::from("Enter/y quit Esc/n cancel"),
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Consider using formal language in the confirmation dialog.

"wanna" is colloquial. Consider "want to" for a more professional tone in the UI.

Suggested fix
     let lines = vec![
-        Line::from("Are you sure you wanna quit?"),
+        Line::from("Are you sure you want to quit?"),
         Line::from(""),
         Line::from("Enter/y quit    Esc/n cancel"),
     ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let lines = vec![
Line::from("Are you sure you wanna quit?"),
Line::from(""),
Line::from("Enter/y quit Esc/n cancel"),
];
let lines = vec![
Line::from("Are you sure you want to quit?"),
Line::from(""),
Line::from("Enter/y quit Esc/n cancel"),
];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui.rs` around lines 1073 - 1077, Update the confirmation dialog text to
use formal language by replacing the colloquial "Are you sure you wanna quit?"
in the lines vector (the Line::from(...) entry) with "Are you sure you want to
quit?" so the dialog displays "Are you sure you want to quit?" while leaving the
other Line::from entries unchanged.

@j4v3l
j4v3l merged commit 07574ba into main Apr 29, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant