feat(market): square grid cells (2x1 min), preserve grid on edit, sparkline interval

- Grid cells are now square (cellH = cellW); column count is responsive with a
  2-column minimum, and a group's minimum size is 2x1 cells. New groups default
  to 2x1; drag/resize/auto-place all enforce the 2-wide minimum.
- Fix: editing a group no longer resets its grid placement/size. The editor's
  Save now starts from the existing entry and only overwrites the form fields,
  so grid_col/row/w/h (and anything else off-form) are preserved.
- Add a per-group sparkline interval (Minute/Hour/Day/Week/Month) next to the
  Sparkline toggle. The price history (~1 sample/min) is resampled by averaging
  each interval's worth of minute-samples into one point.

PortfolioEntry gains sparklineInterval and defaults gridW=2. Persisted in
settings.json. Full-node + Lite build clean; ctest 1/1; hygiene clean.

Note: the app only accumulates in-session minute samples, so Hour/Day/Month
show points only once enough data has been collected; true long-range history
(instant day/month charts) would need a CoinGecko market_chart fetch, which can
be added separately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-03 04:15:38 -05:00
parent 77219c5ce7
commit 76ed09fad0
4 changed files with 66 additions and 25 deletions

View File

@@ -332,6 +332,8 @@ bool Settings::load(const std::string& path)
entry.show24h = e["show_24h"].get<bool>();
if (e.contains("show_sparkline") && e["show_sparkline"].is_boolean())
entry.showSparkline = e["show_sparkline"].get<bool>();
if (e.contains("sparkline_interval") && e["sparkline_interval"].is_number_integer())
entry.sparklineInterval = e["sparkline_interval"].get<int>();
if (e.contains("grid_col") && e["grid_col"].is_number_integer())
entry.gridCol = e["grid_col"].get<int>();
if (e.contains("grid_row") && e["grid_row"].is_number_integer())
@@ -498,6 +500,7 @@ bool Settings::save(const std::string& path)
entry["show_value"] = e.showValue;
entry["show_24h"] = e.show24h;
entry["show_sparkline"] = e.showSparkline;
entry["sparkline_interval"] = e.sparklineInterval;
entry["grid_col"] = e.gridCol;
entry["grid_row"] = e.gridRow;
entry["grid_w"] = e.gridW;