diff --git a/src/ui.rs b/src/ui.rs
index af13d85..9c4a6eb 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -528,10 +528,11 @@ fn render_lines(f: &mut Frame, area: Rect, lines: &[Line<'static>]) {
}
}
-/// One stacked plot: (label, live text, peak text, ring, scale min/max,
-/// line color, optional marker row, axis display divisor — clock shows
-/// GHz on the axis but stores kHz).
-type PlotSpec<'a> = (&'a str, String, String, &'a Ring, f64, f64, Color, Option<u16>, f64);
+/// One stacked plot: (label, live text, peak text, ring, axis min/max
+/// [RAW, for labels], trace min/max [PADDED, for render], line color,
+/// optional marker row, axis display divisor — clock shows GHz on the
+/// axis but stores kHz).
+type PlotSpec<'a> = (&'a str, String, String, &'a Ring, f64, f64, f64, f64, Color, Option<u16>, f64);
pub fn draw(f: &mut Frame, app: &App) {
let area = f.area();
@@ -694,6 +695,9 @@ pub fn draw(f: &mut Frame, app: &App) {
// and one-row headroom at each edge keeps the line ~2 cells clear
// of the box borders.
let ring_min = |ring: &Ring| ring.min_window(ring_ticks);
+ // Raw bounds: fixed = conf min/max, auto = window min/max +
+ // SCALE_MARGIN. Used for the axis labels so they show the user's
+ // conf values (not the padded trace bounds).
let (vc_lo, vc_hi, ck_lo, ck_hi, pw_lo, pw_hi, tp_lo, tp_hi) =
if app.cfg.graph_scale_fixed {
(
@@ -709,42 +713,37 @@ pub fn draw(f: &mut Frame, app: &App) {
let tp = (ring_min(&app.temp_graph), ring_max(&app.temp_graph, 0.0));
(vc.0, vc.1, ck.0, ck.1, pw.0, pw.1, tp.0, tp.1)
};
- // Headroom so the trace fills the interior without kissing the
- // borders: pad the window span by ~1/4 interior row top and bottom
- // (plus a small per-metric floor so flat lines don't go degenerate).
+ // Padded bounds for the trace mapping: ~1/4 interior row top and
+ // bottom (plus a small per-metric floor) so the trace stays just
+ // clear of the box borders in both fixed and auto mode. Applied
+ // to a COPY of the raw bounds; the raw bounds stay for axis labels.
let pad = |lo: f64, hi: f64, floor: f64| -> (f64, f64) {
let row_w = ((hi - lo) / (graph_rows.max(1) as f64) * 0.25).max(floor);
((lo - row_w).max(0.0), hi + row_w)
};
- let (vc_lo, vc_hi, ck_lo, ck_hi, pw_lo, pw_hi, tp_lo, tp_hi) =
- if app.cfg.graph_scale_fixed {
- (vc_lo, vc_hi, ck_lo, ck_hi, pw_lo, pw_hi, tp_lo, tp_hi)
- } else {
- let (a, b) = pad(vc_lo, vc_hi, 0.02);
- let (c, d) = pad(ck_lo, ck_hi, 50_000.0);
- let (e, f_) = pad(pw_lo, pw_hi, 1.0);
- let (g, h) = pad(tp_lo, tp_hi, 1.0);
- (a, b, c, d, e, f_, g, h)
- };
+ let (p_vc_lo, p_vc_hi) = pad(vc_lo, vc_hi, 0.02);
+ let (p_ck_lo, p_ck_hi) = pad(ck_lo, ck_hi, 50_000.0);
+ let (p_pw_lo, p_pw_hi) = pad(pw_lo, pw_hi, 1.0);
+ let (p_tp_lo, p_tp_hi) = pad(tp_lo, tp_hi, 1.0);
// 4 boxed plots stacked top→bottom, sharing one time axis.
// Cascade: vCore → Peak clock → Pkg power → Pkg temp (user order).
let plots: [PlotSpec; 4] = [
- ("vCore", hero_text.clone(), hero_peak, &app.vcore_graph, vc_lo, vc_hi, vc_color,
- Some(((1.0 - ((app.cfg.vcore_limit - vc_lo) / (vc_hi - vc_lo)).clamp(0.0, 1.0))
+ ("vCore", hero_text.clone(), hero_peak, &app.vcore_graph, vc_lo, vc_hi, p_vc_lo, p_vc_hi, vc_color,
+ Some(((1.0 - ((app.cfg.vcore_limit - p_vc_lo) / (p_vc_hi - p_vc_lo)).clamp(0.0, 1.0))
* (graph_rows - 1) as f64).round() as u16), 1.0),
- ("Clock", clock_text, clock_peak, &app.clock_graph, ck_lo, ck_hi, Color::Green, None, 1_000_000.0),
- ("Power", fmt_opt_watts(app.package_watts), watts_peak, &app.watts_graph, pw_lo, pw_hi, Color::Blue, None, 1.0),
- ("Temp", fmt_opt_temp(app.package_temp), temp_peak, &app.temp_graph, tp_lo, tp_hi,
+ ("Clock", clock_text, clock_peak, &app.clock_graph, ck_lo, ck_hi, p_ck_lo, p_ck_hi, Color::Green, None, 1_000_000.0),
+ ("Power", fmt_opt_watts(app.package_watts), watts_peak, &app.watts_graph, pw_lo, pw_hi, p_pw_lo, p_pw_hi, Color::Blue, None, 1.0),
+ ("Temp", fmt_opt_temp(app.package_temp), temp_peak, &app.temp_graph, tp_lo, tp_hi, p_tp_lo, p_tp_hi,
app.package_temp.map_or(Color::DarkGray, |t| temp_color(t, app.cfg.temp_warn, app.cfg.temp_crit)), None, 1.0),
];
let mut y = bottom_y;
- for (label, text, peak, ring, min, max, color, marker, axis_div) in plots {
- let axis_max = plot::fmt_axis(max / axis_div);
+ for (label, text, peak, ring, axis_min, axis_max, min, max, color, marker, axis_div) in plots {
+ let axis_max = plot::fmt_axis(axis_max / axis_div);
let axis_min = if axis_div > 1.0 {
// GHz axis: always show decimals so a near-zero min isn't "0".
- format!("{:.1}", min / axis_div)
+ format!("{:.1}", axis_min / axis_div)
} else {
- plot::fmt_axis(min / axis_div)
+ plot::fmt_axis(axis_min / axis_div)
};
// Box spans the full width — no left gutter. Axis labels
// overlay the top/bottom BORDER rows, just inside the ┌/└
Graphs: 1/4-row trace pad in both fixed+auto; axis labels stay raw
- pad() now applies in BOTH fixed and auto mode (was auto-only). Trace stays ~1/4 row clear of the box borders in fixed mode too, instead of sitting flush against them when a value hits the conf bound - PlotSpec extended with (axis_min, axis_max, trace_min, trace_max): axis labels show the RAW conf/window bounds (e.g. 1.5/0.5, 280/0), render maps over the PADDED bounds. User sees their conf values on the axis, not 1.536/0.464 - 15 tests, clippy clean; pty-verified 50x100
bbc5b462ab18885f83b7ffbdd28b78b1b2190d40
josie <administrator@josie-c.com> · 2026-09-01T23:21 ·
browse files at this commit
parents:
4197028