DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

lhf6623 /

lhf6623/dsh-usage-heatmap

Verified

This plugin has no description yet.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@320c0b04

dsh-usage-heatmap

A DeepSeek Harness (DSH) Web UI plugin: one left-sidebar entry that opens a full-panel calendar heatmap of daily session and message counts, computed from the session logs this machine already stores.

Usage heatmap preview

Features

  • A calendar, not a blank card. Every day in the range is a visible square; a stretch with no usage still reads as a heatmap. Ranges: last 30 / 90 / 365 days, laid out as Monday-to-Sunday week columns with month and weekday labels.
  • Grid first, data later. The calendar is painted from local dates immediately (a skeleton of pale squares), and the counts only repaint the cells when they arrive. A failed load keeps the grid and shows the error inline with a retry button.
  • Each day opens up. Selecting a cell expands a detail card below the calendar: the message split (user vs assistant), sessions (active vs newly created), tool calls plus the busiest tools, token usage (output and cache reads), the hour-by-hour activity shape with its peak, first/last activity and its span, the workspaces worked in, and the busiest sessions with their titles. The day's two headline numbers come from the calendar itself, so they paint instantly; the rest arrives from a dedicated per-day detail route.
  • Fast reopen. Per-session day counts are cached on disk, so opening the panel only re-reads sessions whose log file changed — normally just the conversation being written. A background warm-up runs a few seconds after startup.
  • Account-scoped cache. One cache file per DSH account, so usage never leaks across accounts.
  • Read-only and local. Nothing is uploaded and nothing is written outside the cache. Colors come from --dsw-alias-* theme tokens, so light and dark themes both work.

What it counts

Field Meaning
sessions logical sessions whose header createdAt falls on that day
messages user/message + assistant/message events whose own time falls on that day, across every session on the machine
detail for one selected day: user/assistant messages, active vs newly created sessions, tool calls, token usage, hourly activity, workspaces, busiest sessions

Events before a forked/subagent session's inheritedEventCount are skipped, so an inherited log prefix is not counted twice.

Install

This package is a DSH profile bundle (dsh.bundle.patch + dsh.client). From a local checkout:

  1. Quit DSH.

  2. In the profile's package.json (for example ~/.dsh/profiles/desktop/package.json) add the dependency and list the bundle:

    {
      "dependencies": {
        "dsh-usage-heatmap": "link:D:/dsh-usage-heatmap"
      },
      "dsh": {
        "profile": {
          "bundles": ["...existing bundles...", "dsh-usage-heatmap"]
        }
      }
    }
    

    A git checkout works the same way — point the dependency at the cloned directory.

  3. Start DSH. The left sidebar gets the 使用热力图 / Usage heatmap entry.

The checked-in client.js is already built, so a fresh checkout needs no package install and no build to run. Only plugin development needs pnpm (see Development).

If you previously installed an auto-generated local version of this plugin, disable or remove that entry first so the sidebar only shows one heatmap.

Cache and the HTTP routes

The Host half reads sessions through ctx.sessionQuery and serves two JSON routes:

GET /_dsh/dsh-usage-heatmap/heatmap?from=<ms>&to=<ms>&tz=<minutes east>&refresh=1
GET /_dsh/dsh-usage-heatmap/detail?date=<YYYY-MM-DD>&tz=<minutes east>

Cache files live under $DSH_HOME/dsh-usage-heatmap/:

  • cache-<account>.json stores, per session, its message counts by day plus the log file identity (mtime:size) they came from.
  • A request re-reads a session only when that identity changed; unchanged sessions are reused, a disappeared session drops out, and a failed re-read keeps the last good counts.
  • Delete the cache files to force a full rebuild; refresh=1 rebuilds once.

Day detail route

Selecting a day asks the second route for that day's breakdown. It reuses the same rolling cache to decide which sessions were active on that date, so a click re-reads only those logs — normally a handful — instead of replaying the whole corpus. One response is bounded to the 150 most active sessions of the day, plus the top tools, workspaces, sessions and models. Titles and workspaces come from the session log itself (the latest session/title event and the header cwd), and the client remembers each day's answer for the rest of the page session. A day detail request after the calendar has loaded does not rebuild the whole cache: it uses the same fresh dataset the calendar request just wrote.

Repository layout

.
├── index.js                  # Host entry: re-exports the plugin from src/host/
├── client.js                 # Client entry, GENERATED from src/client/ by the build
├── src/
│   ├── client/               # Web half sources (bundled into client.js)
│   │   ├── index.js          #   loader registration root (default export = plugin)
│   │   ├── plugin.js         #   Cordis plugin: locale, stylesheet, main + sidebar slots
│   │   ├── constants.js      #   ids, routes, ranges, cell sizing
│   │   ├── time.js           #   local-day keys, calendar dates, day formatting
│   │   ├── format.js         #   number, clock and duration formatting
│   │   ├── calendar.js       #   week-column layout, skeleton days, heat levels, cell geometry
│   │   ├── data.js           #   fetch + React hooks for the calendar and one day's detail
│   │   ├── styles.css        #   panel CSS (inlined into the bundle by the build)
│   │   ├── i18n/{zh,en}.js   #   UI copy dictionaries
│   │   └── components/       #   PanelIcon, StatTile, DayDetail, HeatmapBody, UsageHeatmapPage
│   └── host/                 # Host half sources (run directly by Node, no build)
│       ├── plugin.js         #   Cordis plugin: route registration, locking, cache warm-up
│       ├── routes.js         #   request parsing + JSON responses for both routes
│       ├── dataset.js        #   calendar payload aggregation
│       ├── detail.js         #   one-day breakdown aggregation
│       ├── sessions.js       #   session listing, log freshness probe, message counting
│       ├── cache.js          #   per-account cache file read/write
│       ├── time.js           #   day keys, day enumeration, local-day parsing
│       └── constants.js      #   routes, limits, cache paths
├── scripts/build-client.mjs  # esbuild bundle -> client.js (lazy-CJS loader wrapper)
├── test/                     # node:test suites for the Host routes and the client bundle
├── cordis.patch.yml          # Bundle patch that inserts the plugin row
├── package.json              # DSH package metadata (bundle + client halves)
├── locale/                   # Sidebar label / description (zh, en)
├── icon.svg                  # Sidebar glyph
└── docs/                     # README preview images (sample data)

The DSH Web client loads one lazy-CJS bundle per package (the ./client export), so src/client/ is bundled ahead of time into the committed client.js. The bundle only registers a factory with window.__ModuleLoader__, exactly like a compiled DSH client plugin. The Host half is plain Node ESM and runs straight from src/host/.

Development

Requirements: Node.js (20+) and pnpm.

pnpm install          # once: installs esbuild (dev-only)
pnpm run build        # bundle src/client/ -> client.js
pnpm test             # node:test suites (build first: they load client.js)
  • Client half: edit src/client/**, run pnpm run build, restart DSH. client.js is generated — never edit it by hand.
  • Host half: edit src/host/** and restart DSH; no build involved.
  • node scripts/build-client.mjs --sourcemap keeps function names readable and emits client.js.map for debugging (not committed).

The Host half writes its cache on the first request, so the first open after a cold cache rebuild may take a few seconds; later opens are incremental.

License

No license has been chosen yet. Add one before publishing if you want others to reuse this code.

—/ 5

No ratings yet

Verified DSH bundle

Commit 320c0b044191

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout