DataHistoryGraph
components/data/HistoryGraph.vue
Pictogram-style history chart — one unit-block lane per series, side by side per time column (or continuous bars past a density threshold), with an inline swatch legend. Opt-in: `stacked` draws the series bottom-up in one column a day; `maxHeight` caps the chart's height by coarsening the unit (design c110).
Mon
Tue
Wed
Thu
Fri
Sat
Sun
ChatGPT
claude
grok
Scenarios
7-day · 3 series · weekday labels
Mon
Tue
Wed
Thu
Fri
Sat
Sun
ChatGPT
claude
grok
30-day · 2 series · day-number labels
13
15
17
19
21
23
25
27
29
31
2
4
6
8
10
Desktop app
CLI
60-day · 1 series · date labels (auto → bar mode)
Jul 14
Jul 18
Jul 22
Jul 26
Jul 30
Aug 3
Aug 7
Aug 11
Aug 15
Aug 19
Aug 23
Aug 27
Aug 31
Sep 4
Sep 8
Sessions
Single spike — unit-scaling ceiling
Sat
Sun
Mon
Tue
Wed
Thu
Fri
Desktop app
CLI
Sparse/empty week
Sat
Sun
Mon
Tue
Wed
Thu
Fri
ChatGPT
claude
grok
Not collected — Wed (values: null), Sat (missing); Fri a real day of zeros
Mon
Tue
Wed
Thu
Fri
Sat
Sun
ChatGPT
claude
grok
First run — no day collected yet
Mon
Tue
Wed
Thu
Fri
Sat
Sun
ChatGPT
claude
grok
Forced bar mode, legend below
13
15
17
19
21
23
25
27
29
31
2
4
6
8
10
Desktop app
CLI
Stacked — 30-day · 2 series in one column a day
13
15
17
19
21
23
25
27
29
31
2
4
6
8
10
Desktop app
CLI
Stacked + maxHeight 64 — bars, the spike day still inside the cap
13
15
17
19
21
23
25
27
29
31
2
4
6
8
10
Desktop app
CLI
maxHeight 48 — single spike, blocks
Sat
Sun
Mon
Tue
Wed
Thu
Fri
Desktop app
CLI
ChatGPT
claude
grok
components/data/HistoryGraph.vue
Usage
<DataHistoryGraph
:data = "required"
:labelFormat = "null (auto)"
:maxBlocksPerColumn = "12"
:mode = "auto"
:legendBelow = "false"
:blockSize = "6"
:blockGap = "2"
:dayGap = "8"
:labelSize = "11"
:stacked = "false"
:maxHeight = "maxHeight"
/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| data | Object | required | { series: [{ key, label, color? }], points: [{ date, values: { [seriesKey]: number } }] }. Dates are real Date/ISO values — the component derives the axis label, not the caller. |
| labelFormat | 'weekday' | 'day' | 'date' | null (auto) | X-axis label style. Auto-detected from range length when omitted: <=7 points -> weekday, <=31 -> day number, else -> date. |
| maxBlocksPerColumn | Number | 12 | Unit-scaling cap — the "nice" step (1/2/5 x10^n) per block is chosen so the tallest single-series lane never exceeds this many blocks. Series render side by side, not stacked, so this caps each lane independently. |
| mode | 'blocks' | 'bars' | 'auto' | 'auto' | Render as discrete unit blocks, continuous bars, or auto-switch to bars past ~40 columns where individual blocks would no longer read. |
| legendBelow | Boolean | false | Render the legend below the columns in a horizontal row instead of beside them. |
| blockSize | Number | 6 | Unit block width/height in px. |
| blockGap | Number | 2 | Gap between stacked blocks (vertical) and between series lanes within a day (horizontal), in px. |
| dayGap | Number | 8 | Horizontal gap between day columns in px. |
| labelSize | Number | 11 | X-axis label font size in px. Scales the label-driven minimum column width to match; the legend label font size follows at labelSize + 1. |
| stacked | Boolean | false | One column per day, one lane wide: the series stack bottom-up in series order, each part its own color, so a small share stays visible on top of a big one (compliant / non-compliant). The unit scale follows the day's total, not the tallest lane. Blocks and bars; the hover payload is unchanged (design c110). |
| maxHeight | Number | null | A px cap on the chart area. The block cap becomes what fits (maxHeight / (blockSize + blockGap), under maxBlocksPerColumn) and the nice unit steps up until the tallest column as drawn — rounded parts, stacked totals, a bar's unrounded height — fits; never clipped (design c110). |