sugarrush 2026.8.3

A terminal UI for viewing Nightscout CGM (blood glucose sensor) data
// Time in range, one stacked bar per local day, oldest on the left.
//
// The profile next door folds every day onto one clock and answers "what do my
// nights look like". This answers the other question people actually track:
// whether it is getting better. Neither can be read off the other.
//
// Canvas rather than a Repeater of Rectangles: a fortnight is 14 bars of up to
// five bands each, and 70 items with their own bindings is a lot of scene
// graph for something that changes once a day.

import QtQuick

Canvas {
  id: root

  // `[{date, readings, tir: {very_low, low, in_range, high, very_high}}]`,
  // oldest first.
  property var days: []
  property var themeColors: null
  property color foreground: "white"

  // Below this a day is drawn faded: a bar computed from a handful of readings
  // is a rumour, and drawing it like a full day would let one thin morning
  // read as the best day of the fortnight.
  readonly property int thinReadings: 60

  function themed(role, fallback) {
    return themeColors && themeColors[role] ? themeColors[role] : fallback
  }

  onDaysChanged: requestPaint()
  onThemeColorsChanged: requestPaint()
  onForegroundChanged: requestPaint()
  onWidthChanged: requestPaint()
  onHeightChanged: requestPaint()

  onPaint: {
    var ctx = getContext("2d")
    ctx.reset()
    var list = days || []
    if (list.length === 0) return

    var gap = list.length > 20 ? 1 : 2
    var slot = width / list.length
    var barW = Math.max(1, slot - gap)

    // Same order as the time-in-range bar above, so a column here and the bar
    // there are read the same way round.
    var bands = [
      { key: "very_low", color: themed("urgent", "#cc241d") },
      { key: "low", color: themed("low", "#d79921") },
      { key: "in_range", color: themed("in_range", "#98971a") },
      { key: "high", color: themed("high", "#d79921") },
      { key: "very_high", color: themed("urgent", "#cc241d") }
    ]

    for (var d = 0; d < list.length; d++) {
      var day = list[d]
      var tir = day.tir
      if (!tir) continue
      var x = d * slot
      var thin = day.readings < root.thinReadings
      var y = 0

      for (var b = 0; b < bands.length; b++) {
        var share = tir[bands[b].key] || 0
        if (share <= 0) continue
        var h = height * share / 100
        ctx.globalAlpha = thin ? 0.3 : 1
        ctx.fillStyle = bands[b].color
        ctx.fillRect(x, y, barW, h)
        y += h
      }

      // A day with nothing in it still gets a place, so the fortnight does not
      // silently close up around a sensor that was off.
      if (y === 0) {
        ctx.globalAlpha = 0.18
        ctx.fillStyle = root.foreground
        ctx.fillRect(x, 0, barW, height)
      }
    }
    ctx.globalAlpha = 1
  }
}