Activity: streamd <key> [RANGE] prints the chart and the Days table #280

Closed
opened 2026-10-17 08:23:25 +02:00 by agentic-developer · 1 comment
Collaborator

Part of #277. Needs #279.

Goal

streamd <key> [RANGE] of an activity command prints a chart chosen by the length of the range, then the totals and the Days table. streamd <key> shards [RANGE] lists the matching shards.

Command line (commands/activity/cli.rs)

streamd sport                        # this month
streamd sport 2026
streamd sport 2026-10-03..2026-10-17
streamd sport last 30 days           # the words of RANGE are joined
streamd sport all --exclude-mixed    # e.g. the days in the home office only
streamd sport shards 2026-10         # one line per shard, newest first
streamd sport -i                     # the view (#281)
  • RANGE is the syntax of #279 (num_args(1..), joined by spaces), defaulting to this month. A wrong range is a usage error naming the forms.
  • --exclude-mixed / --include-mixed override exclude_mixed (they conflict with each other).
  • shards and the range positional do not clash (args_conflicts_with_subcommands, as the state commands do).
  • -i combines with the range and the mixed flags and opens the view on them.

Output (commands/activity/print.rs)

The glyph is the level (░▒▓█ = 1, 2, 3, 4+; always █ with intensity = "fixed"), coloured in the series colour (truecolor through crossterm, as commands/state/style.rs prints). A mixed day drawn split is ◩ with the first series as foreground and the second as background (with three or more series it's the same with the first two). With first / most it's the winner's glyph. · is a day without a match, ┄ (dim) a future day, and a blank is an untracked day. Today is bold/underlined. Without colour (NO_COLOR, not a terminal) the glyphs stay, and mixed days are ◩.

Calendar (2–42 days; the weeks of the range, Monday first, days outside the range dim):

$ streamd sport
SPORT · Oct 2026 · 17 tracked days

 Mo  Tu  We  Th  Fr  Sa  Su
            ░ 1 · 2 ▓ 3 ◩ 4
· 5 ░ 6 · 7 ░ 8 ▒ 9 ░10 ▒11
·12 ░13 ·14 ·15 ◩16 ░17 ┄18
┄19 ┄20 ┄21 ┄22 ┄23 ┄24 ┄25
┄26 ┄27 ┄28 ┄29 ┄30 ┄31
░▒▓█ Run  ░▒▓█ Climbing  ◩ Mixed  · None

15 shards · 11 of 17 days active · 6.2 per week

DAYS        days    %  shards
Run            7  41%       7  incl. 2 mixed
Climbing       6  35%       8  incl. 2 mixed
Mixed          2  12%          also counted above
None           6  35%          no series matched

Grid (more than 42 days, and all): one 53-column strip per calendar year, newest year first, one column per week, the rows Monday to Sunday (labels Mo, We, Fr, Su), the months above (Jan carrying no year: the year heads the strip). Weeks outside the range are blank.

$ streamd sport 2026
2026  Jan    Feb    Mar    Apr   …   Sep    Oct
Mo    ·░·▒░·▓·░·▒░·░·▒·░·▒░·░·▒ … ·░▒·░·▒░┄┄┄┄┄┄┄┄┄┄
      …
Su    ░·▒·░◩·░·▒░·░·▒·░·▒░·░·▒· … ░·▒◩·┄┄┄┄┄┄┄┄┄┄┄┄

Day (one day): the shards of the day instead of a chart.

$ streamd sport 2026-10-16
SPORT · Fri 16 Oct 2026

18:30  ● Fingerboard session · 120 min   @Climbing   journal/20261016-183000.md:1
06:45  ● Long run · 17 km                @Run        journal/20261016-064500.md:1

2 shards · Run 1 at 06:45 · Climbing 1 at 18:30

Exclusive (--exclude-mixed, or exclude_mixed = true): the Days table reads Run only "no other series that day", Climbing only, Mixed "several series that day", None "no series matched". Wording is generic: the only domain words are the series names.

shards [RANGE]: one line per shard, newest first: 2026-10-16 Fri 18:30 Fingerboard session · 120 min @Climbing journal/…:1 (paths as in R21 Paths). A shard matching two series prints a warning after the output, naming it by file:line, as streamd <key> does too.

Tests

The clap adapter (default range, joined words, conflicts, shards), the printing of each shape on the sample of #279 without colour (golden strings), an integration test in crates/streamd-cli/tests/cli.rs.

Docs

R36 (the command line, the output), README (usage with the example), AGENTS.md.

Part of #277. Needs #279. ## Goal `streamd <key> [RANGE]` of an activity command prints a chart chosen by the length of the range, then the totals and the Days table. `streamd <key> shards [RANGE]` lists the matching shards. ## Command line (`commands/activity/cli.rs`) ```sh streamd sport # this month streamd sport 2026 streamd sport 2026-10-03..2026-10-17 streamd sport last 30 days # the words of RANGE are joined streamd sport all --exclude-mixed # e.g. the days in the home office only streamd sport shards 2026-10 # one line per shard, newest first streamd sport -i # the view (#281) ``` - `RANGE` is the syntax of #279 (`num_args(1..)`, joined by spaces), defaulting to `this month`. A wrong range is a usage error naming the forms. - `--exclude-mixed` / `--include-mixed` override `exclude_mixed` (they conflict with each other). - `shards` and the range positional do not clash (`args_conflicts_with_subcommands`, as the state commands do). - `-i` combines with the range and the mixed flags and opens the view on them. ## Output (`commands/activity/print.rs`) The glyph is the level (`░▒▓█` = 1, 2, 3, 4+; always `█` with `intensity = "fixed"`), coloured in the series colour (truecolor through crossterm, as `commands/state/style.rs` prints). A mixed day drawn split is `◩` with the first series as foreground and the second as background (with three or more series it's the same with the first two). With `first` / `most` it's the winner's glyph. `·` is a day without a match, `┄` (dim) a future day, and a blank is an untracked day. Today is bold/underlined. Without colour (`NO_COLOR`, not a terminal) the glyphs stay, and mixed days are `◩`. **Calendar** (2–42 days; the weeks of the range, Monday first, days outside the range dim): ``` $ streamd sport SPORT · Oct 2026 · 17 tracked days Mo Tu We Th Fr Sa Su ░ 1 · 2 ▓ 3 ◩ 4 · 5 ░ 6 · 7 ░ 8 ▒ 9 ░10 ▒11 ·12 ░13 ·14 ·15 ◩16 ░17 ┄18 ┄19 ┄20 ┄21 ┄22 ┄23 ┄24 ┄25 ┄26 ┄27 ┄28 ┄29 ┄30 ┄31 ░▒▓█ Run ░▒▓█ Climbing ◩ Mixed · None 15 shards · 11 of 17 days active · 6.2 per week DAYS days % shards Run 7 41% 7 incl. 2 mixed Climbing 6 35% 8 incl. 2 mixed Mixed 2 12% also counted above None 6 35% no series matched ``` **Grid** (more than 42 days, and `all`): one 53-column strip per calendar year, newest year first, one column per week, the rows Monday to Sunday (labels `Mo`, `We`, `Fr`, `Su`), the months above (`Jan` carrying no year: the year heads the strip). Weeks outside the range are blank. ``` $ streamd sport 2026 2026 Jan Feb Mar Apr … Sep Oct Mo ·░·▒░·▓·░·▒░·░·▒·░·▒░·░·▒ … ·░▒·░·▒░┄┄┄┄┄┄┄┄┄┄ … Su ░·▒·░◩·░·▒░·░·▒·░·▒░·░·▒· … ░·▒◩·┄┄┄┄┄┄┄┄┄┄┄┄ ``` **Day** (one day): the shards of the day instead of a chart. ``` $ streamd sport 2026-10-16 SPORT · Fri 16 Oct 2026 18:30 ● Fingerboard session · 120 min @Climbing journal/20261016-183000.md:1 06:45 ● Long run · 17 km @Run journal/20261016-064500.md:1 2 shards · Run 1 at 06:45 · Climbing 1 at 18:30 ``` **Exclusive** (`--exclude-mixed`, or `exclude_mixed = true`): the Days table reads `Run only` "no other series that day", `Climbing only`, `Mixed` "several series that day", `None` "no series matched". **Wording is generic:** the only domain words are the series names. `shards [RANGE]`: one line per shard, newest first: `2026-10-16 Fri 18:30 Fingerboard session · 120 min @Climbing journal/…:1` (paths as in R21 *Paths*). A shard matching two series prints a warning after the output, naming it by `file:line`, as `streamd <key>` does too. ## Tests The clap adapter (default range, joined words, conflicts, `shards`), the printing of each shape on the sample of #279 without colour (golden strings), an integration test in `crates/streamd-cli/tests/cli.rs`. ## Docs R36 (the command line, the output), README (usage with the example), AGENTS.md.
Author
Collaborator

Implemented in PR #306 (branch 280_print-activity-chart, 6 commits).

Time: about 1 hour (13:08–14:07 on 2026-10-20), including nix flake check.
Tokens: about 62k of the session budget. This is approximate: it comes from the session's remaining-token counter, not an exact per-request count.

Findings during the implementation

  • The plan was in the issue description, not in a comment, so /implement would have refused it. It was implemented from the description at the user's request.
  • The range parser of #279 has no this month. The default is RangeSpec::Month of today's month, built in run.rs.
  • args_conflicts_with_subcommands makes clap read streamd sport -i shards as a range word. Without special handling the error would be "shards is no range". That case now gets its own message: `shards` takes no options; write `streamd <key> shards [RANGE]`.
  • The exclusive Days table needed the shards of each series on its days alone, which Stats did not have. I added Stats.series_only_shards to the core. The bindings don't carry Stats yet (#283), so they are unaffected.
  • The plan's example output does not match the #279 sample exactly (e.g. 2 Oct is a Climbing day, and Run has 8 shards). The golden tests use what the core actually computes for the sample.
  • Calendar: the plan says "days outside the range dim", but its example shows them blank. They are printed blank, which reads better for ranges like 3..17 Oct.
  • Day view: a series with several shards that day is worded Climbing 2 from 10:00 to 19:00, and a day without shards says No shards ( · the day is to come / · the day is not tracked).
  • styled_output (terminal and no NO_COLOR) moved from the state command's run.rs to commands/state/style.rs so the activity output can share it.
Implemented in PR #306 (branch `280_print-activity-chart`, 6 commits). **Time:** about 1 hour (13:08–14:07 on 2026-10-20), including `nix flake check`. **Tokens:** about 62k of the session budget. This is approximate: it comes from the session's remaining-token counter, not an exact per-request count. **Findings during the implementation** - The plan was in the issue description, not in a comment, so `/implement` would have refused it. It was implemented from the description at the user's request. - The range parser of #279 has no `this month`. The default is `RangeSpec::Month` of today's month, built in `run.rs`. - `args_conflicts_with_subcommands` makes clap read `streamd sport -i shards` as a range word. Without special handling the error would be "`shards` is no range". That case now gets its own message: `` `shards` takes no options; write `streamd <key> shards [RANGE]` ``. - The exclusive Days table needed the shards of each series on its days alone, which `Stats` did not have. I added `Stats.series_only_shards` to the core. The bindings don't carry `Stats` yet (#283), so they are unaffected. - The plan's example output does not match the #279 sample exactly (e.g. 2 Oct is a Climbing day, and Run has 8 shards). The golden tests use what the core actually computes for the sample. - Calendar: the plan says "days outside the range dim", but its example shows them blank. They are printed blank, which reads better for ranges like `3..17 Oct`. - Day view: a series with several shards that day is worded `Climbing 2 from 10:00 to 19:00`, and a day without shards says `No shards` (` · the day is to come` / ` · the day is not tracked`). - `styled_output` (terminal and no `NO_COLOR`) moved from the state command's `run.rs` to `commands/state/style.rs` so the activity output can share it.
Sign in to join this conversation.
No labels
android
cli
epic
planned
tui
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
kfickel/streamd#280
No description provided.