feat: initial commit of dsh-plugin-token-heatmap

This commit is contained in:
katR 2026-10-09 08:05:13 +05:30
commit 33dda8f123
14 changed files with 2223 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
node_modules/\n.DS_Store

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 oiupoyt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

78
README.md Normal file
View file

@ -0,0 +1,78 @@
# dsh-plugin-token-heatmap
> GitHub-style token usage contribution heatmap and analytics for [DeepSeek Harness](https://github.com/deepseek-ai/dsh).
Visualizes your daily token consumption, prompt intensity, cache hit efficiency, and streaks across all sessions in a 52-week contribution calendar.
---
## Features
- **52-Week GitHub Contribution Calendar:** 7 rows × 52 columns tracking daily token activity with 4-level color intensity.
- **Detailed Summary Cards:**
- **Total Tokens:** Complete volume of input, output, and cached tokens.
- **Cache Hit Ratio:** Percentage of tokens served from KV cache reads.
- **Daily Average & Active Days:** Real workload metrics.
- **Streak Tracker:** Current and longest active day streaks.
- **Peak Day Callout:** Date and token count for your heaviest day.
- **Dual Color Palette:** Switch between **GitHub Emerald** and **DeepSeek Indigo**.
- **Interactive Tooltips:** Hover over any day to see token breakdown (billed input/output vs. cache reads and step counts).
- **Session Token Meter Integration:** Integrates into session stats pills with a *"View 12-Month Activity Heatmap"* button that opens the interactive modal dialog anywhere.
- **High-Performance Parser:** Aggregates zstandard (`.jsonl.zstd`) session logs with timestamp and mtime caching for near-instant rendering.
- **DeepSeek Native Theme:** Automatically adapts to DSH dark and light themes using `--dsw-alias-*` design tokens.
---
## Installation
### Method 1: Using pnpm in your DSH Web profile
Inside your DSH profile directory:
```bash
cd ~/.dsh/profiles/web
pnpm add dsh-plugin-token-heatmap
```
Then add `"dsh-plugin-token-heatmap"` to `~/.dsh/profiles/web/package.json` under `dsh.profile.bundles`:
```json
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-plugin-token-heatmap"
]
}
}
}
```
### Method 2: Local Development Link
Clone the repository locally:
```bash
git clone https://github.com/oiupoyt/dsh-plugin-token-heatmap.git ~/projects/dsh-plugin-token-heatmap
cd ~/projects/dsh-plugin-token-heatmap
npm run build
cd ~/.dsh/profiles/web
pnpm add --link ~/projects/dsh-plugin-token-heatmap
```
---
## Usage
1. Open the DeepSeek Harness Web GUI (`http://127.0.0.1:3080`).
2. Go to **Settings** (gear icon at the bottom left) → **Token Heatmap**.
3. Or during any session, click on the token stats pill at the bottom of the composer to view token details and tap **"View 12-Month Activity Heatmap →"**.
---
## License
MIT © [oiupoyt](https://github.com/oiupoyt)

4
cordis.patch.yml Normal file
View file

@ -0,0 +1,4 @@
# dsh-plugin-token-heatmap bundle patch
- insert:
- id: token-heatmap
name: 'dsh-plugin-token-heatmap'

230
lib/aggregator.js Normal file
View file

@ -0,0 +1,230 @@
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import { spawn } from "node:child_process";
const fileCache = /* @__PURE__ */ new Map();
function findSessionFiles(dir) {
const results = [];
try {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
results.push(...findSessionFiles(fullPath));
} else if (entry.name.endsWith(".jsonl.zstd") || entry.name.endsWith(".jsonl")) {
results.push(fullPath);
}
}
} catch {
}
return results;
}
async function parseSessionFile(filePath) {
try {
const stat = fs.statSync(filePath);
const cached = fileCache.get(filePath);
if (cached && cached.mtimeMs === stat.mtimeMs) {
return cached.days;
}
const isZstd = filePath.endsWith(".zstd");
const days = {};
await new Promise((resolve) => {
let buffer = "";
const handleLine = (line) => {
if (!line.includes('"usage"')) return;
try {
const obj = JSON.parse(line);
const time = obj.time || obj.data && obj.data.time;
const usage = obj.usage || obj.data && obj.data.usage;
if (time && usage) {
const d = new Date(time);
const dateKey = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
const input = Number(usage.inputTokens) || 0;
const output = Number(usage.outputTokens) || 0;
const cacheRead = Number(usage.cacheReadTokens) || 0;
const cacheWrite = Number(usage.cacheWriteTokens) || 0;
const total = input + output + cacheRead;
if (!days[dateKey]) {
days[dateKey] = {
totalTokens: 0,
inputTokens: 0,
outputTokens: 0,
cacheReadTokens: 0,
cacheWriteTokens: 0,
turns: 0
};
}
days[dateKey].totalTokens += total;
days[dateKey].inputTokens += input;
days[dateKey].outputTokens += output;
days[dateKey].cacheReadTokens += cacheRead;
days[dateKey].cacheWriteTokens += cacheWrite;
days[dateKey].turns += 1;
}
} catch {
}
};
if (isZstd) {
const proc = spawn("zstd", ["-dc", filePath], { stdio: ["ignore", "pipe", "ignore"] });
proc.stdout.on("data", (chunk) => {
buffer += chunk.toString("utf8");
let idx;
while ((idx = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, idx);
buffer = buffer.slice(idx + 1);
handleLine(line);
}
});
proc.on("close", () => {
if (buffer.trim().length > 0) handleLine(buffer);
resolve();
});
proc.on("error", () => resolve());
} else {
const stream = fs.createReadStream(filePath, { encoding: "utf8" });
stream.on("data", (chunk) => {
buffer += chunk;
let idx;
while ((idx = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, idx);
buffer = buffer.slice(idx + 1);
handleLine(line);
}
});
stream.on("end", () => {
if (buffer.trim().length > 0) handleLine(buffer);
resolve();
});
stream.on("error", () => resolve());
}
});
fileCache.set(filePath, { mtimeMs: stat.mtimeMs, days });
return days;
} catch {
return {};
}
}
async function aggregateTokens(customSessionsRoot) {
const sessionsRoot = customSessionsRoot || path.join(os.homedir(), ".dsh", "sessions");
const files = findSessionFiles(sessionsRoot);
const parsedResults = await Promise.all(files.map(parseSessionFile));
const days = {};
for (const fileDays of parsedResults) {
for (const [date, stats] of Object.entries(fileDays)) {
if (!days[date]) {
days[date] = {
totalTokens: 0,
inputTokens: 0,
outputTokens: 0,
cacheReadTokens: 0,
cacheWriteTokens: 0,
turns: 0
};
}
days[date].totalTokens += stats.totalTokens;
days[date].inputTokens += stats.inputTokens;
days[date].outputTokens += stats.outputTokens;
days[date].cacheReadTokens += stats.cacheReadTokens;
days[date].cacheWriteTokens += stats.cacheWriteTokens;
days[date].turns += stats.turns;
}
}
const positiveValues = Object.values(days).map((d) => d.totalTokens).filter((t) => t > 0).sort((a, b) => a - b);
let q1 = 5e6;
let q2 = 25e6;
let q3 = 75e6;
let q4 = 15e7;
if (positiveValues.length >= 4) {
q1 = positiveValues[Math.floor(positiveValues.length * 0.25)];
q2 = positiveValues[Math.floor(positiveValues.length * 0.5)];
q3 = positiveValues[Math.floor(positiveValues.length * 0.75)];
q4 = positiveValues[Math.floor(positiveValues.length * 0.9)];
}
for (const day of Object.values(days)) {
if (day.totalTokens === 0) day.level = 0;
else if (day.totalTokens <= q1) day.level = 1;
else if (day.totalTokens <= q2) day.level = 2;
else if (day.totalTokens <= q3) day.level = 3;
else day.level = 4;
}
let totalTokens = 0;
let totalInputTokens = 0;
let totalOutputTokens = 0;
let totalCacheReadTokens = 0;
let totalTurns = 0;
let peakDay = null;
for (const [date, stats] of Object.entries(days)) {
totalTokens += stats.totalTokens;
totalInputTokens += stats.inputTokens;
totalOutputTokens += stats.outputTokens;
totalCacheReadTokens += stats.cacheReadTokens;
totalTurns += stats.turns;
if (!peakDay || stats.totalTokens > peakDay.tokens) {
peakDay = { date, tokens: stats.totalTokens, turns: stats.turns };
}
}
const activeDays = Object.values(days).filter((d) => d.totalTokens > 0).length;
const dailyAverage = activeDays > 0 ? Math.round(totalTokens / activeDays) : 0;
const now = /* @__PURE__ */ new Date();
const formatDate = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
let currentStreak = 0;
let cursor = new Date(now);
const todayKey = formatDate(cursor);
cursor.setDate(cursor.getDate() - 1);
const yesterdayKey = formatDate(cursor);
let streakDate = (days[todayKey]?.totalTokens ?? 0) > 0 ? new Date(now) : (days[yesterdayKey]?.totalTokens ?? 0) > 0 ? new Date(cursor) : null;
if (streakDate) {
while (true) {
const key = formatDate(streakDate);
if ((days[key]?.totalTokens ?? 0) > 0) {
currentStreak++;
streakDate.setDate(streakDate.getDate() - 1);
} else {
break;
}
}
}
const sortedDates = Object.keys(days).filter((k) => days[k].totalTokens > 0).sort();
let longestStreak = 0;
let tempStreak = 0;
let prevDate = null;
for (const dateStr of sortedDates) {
const cur = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
if (!prevDate) {
tempStreak = 1;
} else {
const diffDays = Math.round((cur.getTime() - prevDate.getTime()) / (1e3 * 60 * 60 * 24));
if (diffDays === 1) {
tempStreak++;
} else {
tempStreak = 1;
}
}
if (tempStreak > longestStreak) longestStreak = tempStreak;
prevDate = cur;
}
const endDate = formatDate(now);
const start = new Date(now);
start.setDate(start.getDate() - 364);
const startDate = formatDate(start);
return {
summary: {
totalTokens,
totalInputTokens,
totalOutputTokens,
totalCacheReadTokens,
totalTurns,
activeDays,
currentStreak,
longestStreak: Math.max(longestStreak, currentStreak),
peakDay,
dailyAverage
},
days,
startDate,
endDate
};
}
export {
aggregateTokens
};

643
lib/client.js Normal file
View file

@ -0,0 +1,643 @@
// src/client/index.tsx
import React3, { useState as useState2, useEffect as useEffect2 } from "react";
import { createRoot } from "react-dom/client";
// src/client/HeatmapView.tsx
import React, { useEffect, useState, useMemo } from "react";
function formatNumber(num) {
if (num >= 1e9) return (num / 1e9).toFixed(2) + "B";
if (num >= 1e6) return (num / 1e6).toFixed(1) + "M";
if (num >= 1e3) return (num / 1e3).toFixed(1) + "k";
return num.toLocaleString();
}
function formatExact(num) {
return num.toLocaleString();
}
var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var DAY_LABELS = ["", "Mon", "", "Wed", "", "Fri", ""];
var COLOR_THEMES = {
green: {
name: "GitHub Emerald",
levels: [
"var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.08))",
"#0e4429",
"#006d32",
"#26a641",
"#39d353"
]
},
deepseek: {
name: "DeepSeek Indigo",
levels: [
"var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.08))",
"#1e1e4a",
"#2e3188",
"#434fc7",
"#6366f1"
]
}
};
function HeatmapView() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [themeKey, setThemeKey] = useState("green");
const [hoveredDay, setHoveredDay] = useState(null);
const [tooltipPos, setTooltipPos] = useState(null);
const fetchStats = () => {
setLoading(true);
fetch("/api/dsh-token-heatmap/stats").then((r) => {
if (!r.ok) throw new Error(`HTTP error ${r.status}`);
return r.json();
}).then((res) => {
setData(res);
setLoading(false);
}).catch((err) => {
setError(err.message);
setLoading(false);
});
};
useEffect(() => {
fetchStats();
}, []);
const calendarGrid = useMemo(() => {
if (!data) return null;
const weeks = [];
const now = /* @__PURE__ */ new Date();
const end = new Date(now);
const start = new Date(now);
start.setDate(start.getDate() - 364);
start.setDate(start.getDate() - start.getDay());
let current = new Date(start);
let currentWeek = [];
while (current <= end || currentWeek.length > 0) {
const dateKey = `${current.getFullYear()}-${String(current.getMonth() + 1).padStart(2, "0")}-${String(current.getDate()).padStart(2, "0")}`;
const dayStats = data.days[dateKey] || {
totalTokens: 0,
inputTokens: 0,
outputTokens: 0,
cacheReadTokens: 0,
cacheWriteTokens: 0,
turns: 0,
level: 0
};
if (current <= end) {
currentWeek.push({ date: dateKey, stats: dayStats });
} else {
currentWeek.push(null);
}
if (currentWeek.length === 7) {
weeks.push(currentWeek);
currentWeek = [];
}
current.setDate(current.getDate() + 1);
if (current > end && currentWeek.length === 0) break;
}
return weeks;
}, [data]);
const monthHeaders = useMemo(() => {
if (!calendarGrid) return [];
const headers = [];
let lastMonth = -1;
calendarGrid.forEach((week, colIdx) => {
const firstValidDay = week.find((d) => d !== null);
if (firstValidDay) {
const d = /* @__PURE__ */ new Date(firstValidDay.date + "T00:00:00");
const month = d.getMonth();
if (month !== lastMonth) {
headers.push({ label: MONTH_NAMES[month], col: colIdx });
lastMonth = month;
}
}
});
return headers;
}, [calendarGrid]);
const activeTheme = COLOR_THEMES[themeKey];
if (loading && !data) {
return /* @__PURE__ */ React.createElement("div", { style: { padding: "40px 20px", textAlign: "center", color: "var(--dsw-alias-label-secondary)" } }, "Loading token usage heatmap...");
}
if (error) {
return /* @__PURE__ */ React.createElement("div", { style: { padding: "20px", color: "var(--dsw-alias-state-error-primary, #ef4444)" } }, "Failed to load heatmap: ", error, /* @__PURE__ */ React.createElement(
"button",
{
onClick: fetchStats,
style: {
marginLeft: 12,
padding: "4px 10px",
background: "var(--dsw-alias-bg-layer-2)",
border: "1px solid var(--dsw-alias-border-l2)",
color: "var(--dsw-alias-label-primary)",
borderRadius: 6,
cursor: "pointer"
}
},
"Retry"
));
}
if (!data) return null;
const { summary } = data;
const cacheHitPercent = summary.totalTokens > 0 ? Math.round(summary.totalCacheReadTokens / summary.totalTokens * 100) : 0;
return /* @__PURE__ */ React.createElement(
"div",
{
style: {
boxSizing: "border-box",
width: "100%",
maxWidth: "960px",
margin: "0 auto",
padding: "16px 0 40px",
fontFamily: "inherit",
color: "var(--dsw-alias-label-primary, #eeeeee)"
}
},
/* @__PURE__ */ React.createElement(
"div",
{
style: {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
flexWrap: "wrap",
gap: 12,
marginBottom: 20
}
},
/* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
"h2",
{
style: {
margin: 0,
fontSize: 22,
fontWeight: 650,
letterSpacing: "-0.02em",
display: "flex",
alignItems: "center",
gap: 10
}
},
"Token Usage Activity",
/* @__PURE__ */ React.createElement(
"span",
{
style: {
fontSize: 12,
fontWeight: 500,
padding: "2px 8px",
borderRadius: 12,
background: "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.15))",
color: "var(--dsw-alias-label-secondary)"
}
},
"Last 12 Months"
)
), /* @__PURE__ */ React.createElement(
"p",
{
style: {
margin: "6px 0 0",
fontSize: 13,
color: "var(--dsw-alias-label-tertiary, #a0a0a0)"
}
},
"Daily token volume and request step intensity across all sessions"
)),
/* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React.createElement(
"div",
{
style: {
display: "inline-flex",
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 8,
padding: 2
}
},
/* @__PURE__ */ React.createElement(
"button",
{
onClick: () => setThemeKey("green"),
style: {
border: 0,
background: themeKey === "green" ? "var(--dsw-alias-bg-layer-1, #1e1e1e)" : "transparent",
color: themeKey === "green" ? "var(--dsw-alias-label-primary)" : "var(--dsw-alias-label-tertiary)",
fontSize: 12,
padding: "4px 10px",
borderRadius: 6,
cursor: "pointer",
fontWeight: themeKey === "green" ? 600 : 400
}
},
"Emerald"
),
/* @__PURE__ */ React.createElement(
"button",
{
onClick: () => setThemeKey("deepseek"),
style: {
border: 0,
background: themeKey === "deepseek" ? "var(--dsw-alias-bg-layer-1, #1e1e1e)" : "transparent",
color: themeKey === "deepseek" ? "var(--dsw-alias-label-primary)" : "var(--dsw-alias-label-tertiary)",
fontSize: 12,
padding: "4px 10px",
borderRadius: 6,
cursor: "pointer",
fontWeight: themeKey === "deepseek" ? 600 : 400
}
},
"Indigo"
)
), /* @__PURE__ */ React.createElement(
"button",
{
onClick: fetchStats,
title: "Refresh statistics",
style: {
display: "inline-flex",
alignItems: "center",
gap: 6,
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
color: "var(--dsw-alias-label-primary)",
borderRadius: 8,
padding: "6px 12px",
fontSize: 12,
fontWeight: 500,
cursor: "pointer"
}
},
"\u21BB Refresh"
))
),
/* @__PURE__ */ React.createElement(
"div",
{
style: {
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
gap: 12,
marginBottom: 20
}
},
/* @__PURE__ */ React.createElement(
"div",
{
style: {
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 10,
padding: "14px 16px"
}
},
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 } }, "Total Tokens"),
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "var(--dsw-alias-label-primary, #ffffff)" } }, formatNumber(summary.totalTokens)),
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" } }, formatExact(summary.totalTokens), " total")
),
/* @__PURE__ */ React.createElement(
"div",
{
style: {
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 10,
padding: "14px 16px"
}
},
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 } }, "Cache Hit Ratio"),
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "#10b981" } }, cacheHitPercent, "%"),
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" } }, formatNumber(summary.totalCacheReadTokens), " cached reads")
),
/* @__PURE__ */ React.createElement(
"div",
{
style: {
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 10,
padding: "14px 16px"
}
},
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 } }, "Daily Average"),
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "var(--dsw-alias-label-primary, #ffffff)" } }, formatNumber(summary.dailyAverage)),
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" } }, "Across ", summary.activeDays, " active days")
),
/* @__PURE__ */ React.createElement(
"div",
{
style: {
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 10,
padding: "14px 16px"
}
},
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 } }, "Active Streak"),
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "var(--dsw-alias-label-primary, #ffffff)" } }, summary.currentStreak, " ", summary.currentStreak === 1 ? "day" : "days"),
/* @__PURE__ */ React.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" } }, "Longest streak: ", summary.longestStreak, " days")
)
),
/* @__PURE__ */ React.createElement(
"div",
{
style: {
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 12,
padding: "20px 22px",
position: "relative",
overflowX: "auto"
}
},
/* @__PURE__ */ React.createElement(
"div",
{
style: {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 14,
fontSize: 13,
fontWeight: 600
}
},
/* @__PURE__ */ React.createElement("span", null, "Contribution Calendar"),
summary.peakDay && /* @__PURE__ */ React.createElement("span", { style: { fontSize: 12, fontWeight: 400, color: "var(--dsw-alias-label-secondary)" } }, "Peak day: ", /* @__PURE__ */ React.createElement("strong", { style: { color: "var(--dsw-alias-label-primary)" } }, summary.peakDay.date), " (", formatNumber(summary.peakDay.tokens), " tokens)")
),
calendarGrid && /* @__PURE__ */ React.createElement("div", { style: { display: "inline-block", minWidth: 780 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", marginLeft: 32, marginBottom: 4, height: 16 } }, monthHeaders.map((m, i) => /* @__PURE__ */ React.createElement(
"div",
{
key: i,
style: {
position: "relative",
left: m.col * 14,
width: 0,
whiteSpace: "nowrap",
fontSize: 10,
color: "var(--dsw-alias-label-tertiary, #a0a0a0)"
}
},
m.label
))), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 6 } }, /* @__PURE__ */ React.createElement(
"div",
{
style: {
display: "flex",
flexDirection: "column",
gap: 3,
width: 26,
fontSize: 9,
color: "var(--dsw-alias-label-tertiary, #a0a0a0)",
textAlign: "right",
paddingTop: 1
}
},
DAY_LABELS.map((lbl, idx) => /* @__PURE__ */ React.createElement("div", { key: idx, style: { height: 11, lineHeight: "11px" } }, lbl))
), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 3 } }, calendarGrid.map((week, colIdx) => /* @__PURE__ */ React.createElement("div", { key: colIdx, style: { display: "flex", flexDirection: "column", gap: 3 } }, week.map((day, rowIdx) => {
if (!day) {
return /* @__PURE__ */ React.createElement("div", { key: rowIdx, style: { width: 11, height: 11 } });
}
const lvl = day.stats.level || 0;
const bg = activeTheme.levels[lvl];
return /* @__PURE__ */ React.createElement(
"div",
{
key: rowIdx,
onMouseEnter: (e) => {
setHoveredDay({ date: day.date, stats: day.stats });
const rect = e.currentTarget.getBoundingClientRect();
setTooltipPos({ x: rect.left + rect.width / 2, y: rect.top - 8 });
},
onMouseLeave: () => {
setHoveredDay(null);
setTooltipPos(null);
},
style: {
width: 11,
height: 11,
borderRadius: 2,
backgroundColor: bg,
border: lvl === 0 ? "1px solid var(--dsw-alias-border-l1, rgba(255,255,255,0.06))" : "1px solid transparent",
cursor: "pointer",
transition: "transform 0.1s ease, filter 0.1s ease"
}
}
);
}))))), /* @__PURE__ */ React.createElement(
"div",
{
style: {
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
gap: 6,
marginTop: 14,
fontSize: 11,
color: "var(--dsw-alias-label-tertiary, #a0a0a0)"
}
},
/* @__PURE__ */ React.createElement("span", null, "Less"),
activeTheme.levels.map((c, i) => /* @__PURE__ */ React.createElement(
"div",
{
key: i,
style: {
width: 11,
height: 11,
borderRadius: 2,
backgroundColor: c,
border: i === 0 ? "1px solid var(--dsw-alias-border-l1)" : "none"
}
}
)),
/* @__PURE__ */ React.createElement("span", null, "More")
))
),
hoveredDay && tooltipPos && /* @__PURE__ */ React.createElement(
"div",
{
style: {
position: "fixed",
left: tooltipPos.x,
top: tooltipPos.y,
transform: "translate(-50%, -100%)",
background: "var(--dsw-alias-bg-layer-1, #1e1e1e)",
border: "1px solid var(--dsw-alias-border-l3, #555555)",
borderRadius: 8,
padding: "8px 12px",
fontSize: 12,
lineHeight: 1.4,
boxShadow: "0 6px 18px rgba(0,0,0,0.35)",
pointerEvents: "none",
zIndex: 9999,
whiteSpace: "nowrap"
}
},
/* @__PURE__ */ React.createElement("div", { style: { fontWeight: 650, color: "var(--dsw-alias-label-primary, #ffffff)", marginBottom: 4 } }, (/* @__PURE__ */ new Date(hoveredDay.date + "T00:00:00")).toLocaleDateString(void 0, {
weekday: "short",
year: "numeric",
month: "short",
day: "numeric"
})),
hoveredDay.stats.totalTokens === 0 ? /* @__PURE__ */ React.createElement("div", { style: { color: "var(--dsw-alias-label-tertiary)" } }, "No activity recorded") : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { style: { color: "#34d399", fontWeight: 600 } }, formatExact(hoveredDay.stats.totalTokens), " tokens"), /* @__PURE__ */ React.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-secondary)", marginTop: 2 } }, hoveredDay.stats.turns, " requests / steps"), /* @__PURE__ */ React.createElement("div", { style: { fontSize: 10, color: "var(--dsw-alias-label-tertiary)", marginTop: 4 } }, "Billed: ", formatNumber(hoveredDay.stats.inputTokens + hoveredDay.stats.outputTokens), " \xB7 Cache: ", formatNumber(hoveredDay.stats.cacheReadTokens)))
)
);
}
// src/client/HeatmapModal.tsx
import React2 from "react";
function HeatmapModal({ open, onClose }) {
if (!open) return null;
return /* @__PURE__ */ React2.createElement(
"div",
{
role: "dialog",
"aria-modal": "true",
"aria-label": "Token Usage Heatmap",
onClick: onClose,
style: {
position: "fixed",
inset: 0,
zIndex: 1e4,
backgroundColor: "rgba(0, 0, 0, 0.65)",
backdropFilter: "blur(4px)",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "20px",
overflowY: "auto"
}
},
/* @__PURE__ */ React2.createElement(
"div",
{
onClick: (e) => e.stopPropagation(),
style: {
position: "relative",
width: "100%",
maxWidth: "960px",
maxHeight: "90vh",
overflowY: "auto",
background: "var(--dsw-alias-bg-layer-1, #1e1e1e)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: "14px",
boxShadow: "0 16px 40px rgba(0, 0, 0, 0.5)",
padding: "24px 28px"
}
},
/* @__PURE__ */ React2.createElement(
"button",
{
onClick: onClose,
"aria-label": "Close dialog",
style: {
position: "absolute",
top: "16px",
right: "18px",
background: "transparent",
border: "none",
color: "var(--dsw-alias-label-tertiary, #a0a0a0)",
fontSize: "20px",
cursor: "pointer",
width: "32px",
height: "32px",
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "6px"
}
},
"\u2715"
),
/* @__PURE__ */ React2.createElement(HeatmapView, null)
)
);
}
// src/client/index.tsx
var name = "dsh-plugin-token-heatmap-client";
var inject = ["slots"];
function apply(ctx) {
if (ctx.slots && typeof ctx.slots.inject === "function") {
ctx.slots.inject("settings.section", () => {
return ctx.slots.register(
{
name: "settings.section",
id: "token-heatmap",
order: 14,
label: () => "Token Heatmap",
icon: "chart"
},
HeatmapView
);
});
}
if (typeof document !== "undefined") {
let ModalController = function() {
const [isOpen, setIsOpen] = useState2(false);
useEffect2(() => {
const handleOpen = () => setIsOpen(true);
const handleClose = () => setIsOpen(false);
window.addEventListener("dsh:open-token-heatmap", handleOpen);
window.addEventListener("keydown", (e) => {
if (e.key === "Escape") handleClose();
});
return () => {
window.removeEventListener("dsh:open-token-heatmap", handleOpen);
};
}, []);
return /* @__PURE__ */ React3.createElement(HeatmapModal, { open: isOpen, onClose: () => setIsOpen(false) });
};
const modalContainer = document.createElement("div");
modalContainer.id = "dsh-token-heatmap-modal-root";
document.body.appendChild(modalContainer);
const root = createRoot(modalContainer);
root.render(/* @__PURE__ */ React3.createElement(ModalController, null));
const observer = new MutationObserver(() => {
const statDialogs = document.querySelectorAll(
'div[role="dialog"][aria-label*="usage" i], div[role="dialog"][aria-label*="Token" i], [data-session-stats-usage]'
);
statDialogs.forEach((dialog) => {
if (dialog.querySelector(".dsh-heatmap-trigger-btn")) return;
const button = document.createElement("button");
button.className = "dsh-heatmap-trigger-btn";
button.type = "button";
button.innerHTML = "<span>\u{1F4CA} View 12-Month Activity Heatmap \u2192</span>";
button.style.cssText = `
display: flex;
align-items: center;
justify-content: center;
width: 100%;
margin-top: 12px;
padding: 8px 12px;
background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.12));
border: 1px solid var(--dsw-alias-border-l2, #494949);
border-radius: 8px;
color: var(--dsw-alias-brand-primary, #6366f1);
font-size: 12px;
font-weight: 550;
cursor: pointer;
transition: background 0.15s ease, border-color 0.15s ease;
`;
button.onmouseenter = () => {
button.style.background = "var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.2))";
button.style.borderColor = "var(--dsw-alias-brand-primary, #6366f1)";
};
button.onmouseleave = () => {
button.style.background = "var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.12))";
button.style.borderColor = "var(--dsw-alias-border-l2, #494949)";
};
button.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
window.dispatchEvent(new CustomEvent("dsh:open-token-heatmap"));
};
dialog.appendChild(button);
});
});
observer.observe(document.body, { childList: true, subtree: true });
}
}
export {
apply,
inject,
name
};

44
lib/index.js Normal file
View file

@ -0,0 +1,44 @@
import { aggregateTokens } from "./aggregator.js";
const name = "dsh-plugin-token-heatmap";
const inject = ["webServer"];
function apply(ctx) {
ctx.effect(
() => ctx.webServer.register({
kind: "prefix",
path: "/api/dsh-token-heatmap",
handler: async (req, res) => {
const url = new URL(req.url, "http://localhost");
const pathname = url.pathname.replace(/\/+$/, "");
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
if (req.method === "OPTIONS") {
res.writeHead(204);
res.end();
return;
}
if (req.method === "GET" && (pathname === "/api/dsh-token-heatmap" || pathname === "/api/dsh-token-heatmap/stats")) {
try {
const data = await aggregateTokens();
res.writeHead(200, {
"Content-Type": "application/json; charset=utf-8",
"Cache-Control": "no-cache"
});
res.end(JSON.stringify(data));
} catch (err) {
res.writeHead(500, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: err?.message || "Failed to aggregate token stats" }));
}
return;
}
res.writeHead(404, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Not found" }));
}
})
);
}
export {
apply,
inject,
name
};

47
package.json Normal file
View file

@ -0,0 +1,47 @@
{
"name": "dsh-plugin-token-heatmap",
"version": "0.1.0",
"description": "GitHub-style token usage contribution heatmap and analytics for DeepSeek Harness",
"type": "module",
"main": "./lib/index.js",
"exports": {
".": "./lib/index.js",
"./client": "./lib/client.js",
"./package.json": "./package.json"
},
"files": [
"lib",
"cordis.patch.yml",
"README.md",
"LICENSE"
],
"keywords": [
"deepseek-harness",
"dsh",
"dsh-plugin",
"cordis",
"heatmap",
"token-usage",
"analytics"
],
"dsh": {
"bundle": {
"patch": "./cordis.patch.yml"
},
"client": {
"inject": [
"@deepseek-ai/dsh-client-ui-slots"
],
"platform": "web"
}
},
"scripts": {
"build": "node scripts/build.mjs"
},
"peerDependencies": {
"@deepseek-ai/cordis": ">=4.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"license": "MIT"
}

9
scripts/build.mjs Normal file
View file

@ -0,0 +1,9 @@
import { execSync } from "node:child_process";
console.log("Building server backend...");
execSync("npx esbuild src/server/index.ts src/server/aggregator.ts --outdir=lib --platform=node --format=esm --target=node20 --sourcemap", { stdio: "inherit" });
console.log("Building client frontend bundle...");
execSync("npx esbuild src/client/index.tsx --outfile=lib/client.js --platform=browser --format=esm --target=es2022 --bundle --external:react --external:react-dom --external:react-dom/client --sourcemap", { stdio: "inherit" });
console.log("Build complete!");

View file

@ -0,0 +1,74 @@
import React from "react";
import { HeatmapView } from "./HeatmapView.js";
interface HeatmapModalProps {
open: boolean;
onClose: () => void;
}
export function HeatmapModal({ open, onClose }: HeatmapModalProps) {
if (!open) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-label="Token Usage Heatmap"
onClick={onClose}
style={{
position: "fixed",
inset: 0,
zIndex: 10000,
backgroundColor: "rgba(0, 0, 0, 0.65)",
backdropFilter: "blur(4px)",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "20px",
overflowY: "auto",
}}
>
<div
onClick={(e) => e.stopPropagation()}
style={{
position: "relative",
width: "100%",
maxWidth: "960px",
maxHeight: "90vh",
overflowY: "auto",
background: "var(--dsw-alias-bg-layer-1, #1e1e1e)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: "14px",
boxShadow: "0 16px 40px rgba(0, 0, 0, 0.5)",
padding: "24px 28px",
}}
>
{/* Close button */}
<button
onClick={onClose}
aria-label="Close dialog"
style={{
position: "absolute",
top: "16px",
right: "18px",
background: "transparent",
border: "none",
color: "var(--dsw-alias-label-tertiary, #a0a0a0)",
fontSize: "20px",
cursor: "pointer",
width: "32px",
height: "32px",
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "6px",
}}
>
✕
</button>
<HeatmapView />
</div>
</div>
);
}

617
src/client/HeatmapView.tsx Normal file
View file

@ -0,0 +1,617 @@
import React, { useEffect, useState, useMemo } from "react";
export interface DayStats {
totalTokens: number;
inputTokens: number;
outputTokens: number;
cacheReadTokens: number;
cacheWriteTokens: number;
turns: number;
level?: number;
}
export interface SummaryStats {
totalTokens: number;
totalInputTokens: number;
totalOutputTokens: number;
totalCacheReadTokens: number;
totalTurns: number;
activeDays: number;
currentStreak: number;
longestStreak: number;
peakDay: { date: string; tokens: number; turns: number } | null;
dailyAverage: number;
}
export interface HeatmapResponse {
summary: SummaryStats;
days: Record<string, DayStats>;
startDate: string;
endDate: string;
}
function formatNumber(num: number): string {
if (num >= 1_000_000_000) return (num / 1_000_000_000).toFixed(2) + "B";
if (num >= 1_000_000) return (num / 1_000_000).toFixed(1) + "M";
if (num >= 1_000) return (num / 1_000).toFixed(1) + "k";
return num.toLocaleString();
}
function formatExact(num: number): string {
return num.toLocaleString();
}
const MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const DAY_LABELS = ["", "Mon", "", "Wed", "", "Fri", ""];
// Themes: Green (GitHub classic) and DeepSeek Blue/Indigo
const COLOR_THEMES = {
green: {
name: "GitHub Emerald",
levels: [
"var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.08))",
"#0e4429",
"#006d32",
"#26a641",
"#39d353",
],
},
deepseek: {
name: "DeepSeek Indigo",
levels: [
"var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.08))",
"#1e1e4a",
"#2e3188",
"#434fc7",
"#6366f1",
],
},
};
export function HeatmapView() {
const [data, setData] = useState<HeatmapResponse | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [themeKey, setThemeKey] = useState<"green" | "deepseek">("green");
const [hoveredDay, setHoveredDay] = useState<{ date: string; stats: DayStats } | null>(null);
const [tooltipPos, setTooltipPos] = useState<{ x: number; y: number } | null>(null);
const fetchStats = () => {
setLoading(true);
fetch("/api/dsh-token-heatmap/stats")
.then((r) => {
if (!r.ok) throw new Error(`HTTP error ${r.status}`);
return r.json();
})
.then((res) => {
setData(res);
setLoading(false);
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
};
useEffect(() => {
fetchStats();
}, []);
// Compute 52-week calendar grid: 7 rows x 53 columns
const calendarGrid = useMemo(() => {
if (!data) return null;
const weeks: Array<Array<{ date: string; stats: DayStats } | null>> = [];
const now = new Date();
// End on current week Saturday
const end = new Date(now);
// Start 52 weeks back on Sunday
const start = new Date(now);
start.setDate(start.getDate() - 364);
// Align start to nearest previous Sunday
start.setDate(start.getDate() - start.getDay());
let current = new Date(start);
let currentWeek: Array<{ date: string; stats: DayStats } | null> = [];
while (current <= end || currentWeek.length > 0) {
const dateKey = `${current.getFullYear()}-${String(current.getMonth() + 1).padStart(2, "0")}-${String(current.getDate()).padStart(2, "0")}`;
const dayStats = data.days[dateKey] || {
totalTokens: 0,
inputTokens: 0,
outputTokens: 0,
cacheReadTokens: 0,
cacheWriteTokens: 0,
turns: 0,
level: 0,
};
if (current <= end) {
currentWeek.push({ date: dateKey, stats: dayStats });
} else {
currentWeek.push(null);
}
if (currentWeek.length === 7) {
weeks.push(currentWeek);
currentWeek = [];
}
current.setDate(current.getDate() + 1);
if (current > end && currentWeek.length === 0) break;
}
return weeks;
}, [data]);
// Compute month headers
const monthHeaders = useMemo(() => {
if (!calendarGrid) return [];
const headers: Array<{ label: string; col: number }> = [];
let lastMonth = -1;
calendarGrid.forEach((week, colIdx) => {
const firstValidDay = week.find((d) => d !== null);
if (firstValidDay) {
const d = new Date(firstValidDay.date + "T00:00:00");
const month = d.getMonth();
if (month !== lastMonth) {
headers.push({ label: MONTH_NAMES[month], col: colIdx });
lastMonth = month;
}
}
});
return headers;
}, [calendarGrid]);
const activeTheme = COLOR_THEMES[themeKey];
if (loading && !data) {
return (
<div style={{ padding: "40px 20px", textAlign: "center", color: "var(--dsw-alias-label-secondary)" }}>
Loading token usage heatmap...
</div>
);
}
if (error) {
return (
<div style={{ padding: "20px", color: "var(--dsw-alias-state-error-primary, #ef4444)" }}>
Failed to load heatmap: {error}
<button
onClick={fetchStats}
style={{
marginLeft: 12,
padding: "4px 10px",
background: "var(--dsw-alias-bg-layer-2)",
border: "1px solid var(--dsw-alias-border-l2)",
color: "var(--dsw-alias-label-primary)",
borderRadius: 6,
cursor: "pointer",
}}
>
Retry
</button>
</div>
);
}
if (!data) return null;
const { summary } = data;
const cacheHitPercent =
summary.totalTokens > 0
? Math.round((summary.totalCacheReadTokens / summary.totalTokens) * 100)
: 0;
return (
<div
style={{
boxSizing: "border-box",
width: "100%",
maxWidth: "960px",
margin: "0 auto",
padding: "16px 0 40px",
fontFamily: "inherit",
color: "var(--dsw-alias-label-primary, #eeeeee)",
}}
>
{/* Title Bar */}
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
flexWrap: "wrap",
gap: 12,
marginBottom: 20,
}}
>
<div>
<h2
style={{
margin: 0,
fontSize: 22,
fontWeight: 650,
letterSpacing: "-0.02em",
display: "flex",
alignItems: "center",
gap: 10,
}}
>
Token Usage Activity
<span
style={{
fontSize: 12,
fontWeight: 500,
padding: "2px 8px",
borderRadius: 12,
background: "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.15))",
color: "var(--dsw-alias-label-secondary)",
}}
>
Last 12 Months
</span>
</h2>
<p
style={{
margin: "6px 0 0",
fontSize: 13,
color: "var(--dsw-alias-label-tertiary, #a0a0a0)",
}}
>
Daily token volume and request step intensity across all sessions
</p>
</div>
{/* Action Controls */}
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
{/* Theme Switcher */}
<div
style={{
display: "inline-flex",
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 8,
padding: 2,
}}
>
<button
onClick={() => setThemeKey("green")}
style={{
border: 0,
background: themeKey === "green" ? "var(--dsw-alias-bg-layer-1, #1e1e1e)" : "transparent",
color: themeKey === "green" ? "var(--dsw-alias-label-primary)" : "var(--dsw-alias-label-tertiary)",
fontSize: 12,
padding: "4px 10px",
borderRadius: 6,
cursor: "pointer",
fontWeight: themeKey === "green" ? 600 : 400,
}}
>
Emerald
</button>
<button
onClick={() => setThemeKey("deepseek")}
style={{
border: 0,
background: themeKey === "deepseek" ? "var(--dsw-alias-bg-layer-1, #1e1e1e)" : "transparent",
color: themeKey === "deepseek" ? "var(--dsw-alias-label-primary)" : "var(--dsw-alias-label-tertiary)",
fontSize: 12,
padding: "4px 10px",
borderRadius: 6,
cursor: "pointer",
fontWeight: themeKey === "deepseek" ? 600 : 400,
}}
>
Indigo
</button>
</div>
<button
onClick={fetchStats}
title="Refresh statistics"
style={{
display: "inline-flex",
alignItems: "center",
gap: 6,
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
color: "var(--dsw-alias-label-primary)",
borderRadius: 8,
padding: "6px 12px",
fontSize: 12,
fontWeight: 500,
cursor: "pointer",
}}
>
↻ Refresh
</button>
</div>
</div>
{/* Summary Cards */}
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
gap: 12,
marginBottom: 20,
}}
>
{/* Card 1: Total Tokens */}
<div
style={{
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 10,
padding: "14px 16px",
}}
>
<div style={{ fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 }}>
Total Tokens
</div>
<div style={{ fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "var(--dsw-alias-label-primary, #ffffff)" }}>
{formatNumber(summary.totalTokens)}
</div>
<div style={{ fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" }}>
{formatExact(summary.totalTokens)} total
</div>
</div>
{/* Card 2: Cache Efficiency */}
<div
style={{
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 10,
padding: "14px 16px",
}}
>
<div style={{ fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 }}>
Cache Hit Ratio
</div>
<div style={{ fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "#10b981" }}>
{cacheHitPercent}%
</div>
<div style={{ fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" }}>
{formatNumber(summary.totalCacheReadTokens)} cached reads
</div>
</div>
{/* Card 3: Daily Average */}
<div
style={{
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 10,
padding: "14px 16px",
}}
>
<div style={{ fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 }}>
Daily Average
</div>
<div style={{ fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "var(--dsw-alias-label-primary, #ffffff)" }}>
{formatNumber(summary.dailyAverage)}
</div>
<div style={{ fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" }}>
Across {summary.activeDays} active days
</div>
</div>
{/* Card 4: Streaks */}
<div
style={{
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 10,
padding: "14px 16px",
}}
>
<div style={{ fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 }}>
Active Streak
</div>
<div style={{ fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "var(--dsw-alias-label-primary, #ffffff)" }}>
{summary.currentStreak} {summary.currentStreak === 1 ? "day" : "days"}
</div>
<div style={{ fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" }}>
Longest streak: {summary.longestStreak} days
</div>
</div>
</div>
{/* Heatmap Card */}
<div
style={{
background: "var(--dsw-alias-bg-layer-2, #2c2c2c)",
border: "1px solid var(--dsw-alias-border-l2, #494949)",
borderRadius: 12,
padding: "20px 22px",
position: "relative",
overflowX: "auto",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 14,
fontSize: 13,
fontWeight: 600,
}}
>
<span>Contribution Calendar</span>
{summary.peakDay && (
<span style={{ fontSize: 12, fontWeight: 400, color: "var(--dsw-alias-label-secondary)" }}>
Peak day: <strong style={{ color: "var(--dsw-alias-label-primary)" }}>{summary.peakDay.date}</strong> ({formatNumber(summary.peakDay.tokens)} tokens)
</span>
)}
</div>
{/* SVG Heatmap */}
{calendarGrid && (
<div style={{ display: "inline-block", minWidth: 780 }}>
{/* Month labels */}
<div style={{ display: "flex", marginLeft: 32, marginBottom: 4, height: 16 }}>
{monthHeaders.map((m, i) => (
<div
key={i}
style={{
position: "relative",
left: m.col * 14,
width: 0,
whiteSpace: "nowrap",
fontSize: 10,
color: "var(--dsw-alias-label-tertiary, #a0a0a0)",
}}
>
{m.label}
</div>
))}
</div>
{/* Grid with day labels */}
<div style={{ display: "flex", gap: 6 }}>
{/* Day of week labels */}
<div
style={{
display: "flex",
flexDirection: "column",
gap: 3,
width: 26,
fontSize: 9,
color: "var(--dsw-alias-label-tertiary, #a0a0a0)",
textAlign: "right",
paddingTop: 1,
}}
>
{DAY_LABELS.map((lbl, idx) => (
<div key={idx} style={{ height: 11, lineHeight: "11px" }}>
{lbl}
</div>
))}
</div>
{/* Columns of weeks */}
<div style={{ display: "flex", gap: 3 }}>
{calendarGrid.map((week, colIdx) => (
<div key={colIdx} style={{ display: "flex", flexDirection: "column", gap: 3 }}>
{week.map((day, rowIdx) => {
if (!day) {
return <div key={rowIdx} style={{ width: 11, height: 11 }} />;
}
const lvl = day.stats.level || 0;
const bg = activeTheme.levels[lvl];
return (
<div
key={rowIdx}
onMouseEnter={(e) => {
setHoveredDay({ date: day.date, stats: day.stats });
const rect = e.currentTarget.getBoundingClientRect();
setTooltipPos({ x: rect.left + rect.width / 2, y: rect.top - 8 });
}}
onMouseLeave={() => {
setHoveredDay(null);
setTooltipPos(null);
}}
style={{
width: 11,
height: 11,
borderRadius: 2,
backgroundColor: bg,
border:
lvl === 0
? "1px solid var(--dsw-alias-border-l1, rgba(255,255,255,0.06))"
: "1px solid transparent",
cursor: "pointer",
transition: "transform 0.1s ease, filter 0.1s ease",
}}
/>
);
})}
</div>
))}
</div>
</div>
{/* Legend & Footnote */}
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
gap: 6,
marginTop: 14,
fontSize: 11,
color: "var(--dsw-alias-label-tertiary, #a0a0a0)",
}}
>
<span>Less</span>
{activeTheme.levels.map((c, i) => (
<div
key={i}
style={{
width: 11,
height: 11,
borderRadius: 2,
backgroundColor: c,
border: i === 0 ? "1px solid var(--dsw-alias-border-l1)" : "none",
}}
/>
))}
<span>More</span>
</div>
</div>
)}
</div>
{/* Floating Hover Tooltip */}
{hoveredDay && tooltipPos && (
<div
style={{
position: "fixed",
left: tooltipPos.x,
top: tooltipPos.y,
transform: "translate(-50%, -100%)",
background: "var(--dsw-alias-bg-layer-1, #1e1e1e)",
border: "1px solid var(--dsw-alias-border-l3, #555555)",
borderRadius: 8,
padding: "8px 12px",
fontSize: 12,
lineHeight: 1.4,
boxShadow: "0 6px 18px rgba(0,0,0,0.35)",
pointerEvents: "none",
zIndex: 9999,
whiteSpace: "nowrap",
}}
>
<div style={{ fontWeight: 650, color: "var(--dsw-alias-label-primary, #ffffff)", marginBottom: 4 }}>
{new Date(hoveredDay.date + "T00:00:00").toLocaleDateString(undefined, {
weekday: "short",
year: "numeric",
month: "short",
day: "numeric",
})}
</div>
{hoveredDay.stats.totalTokens === 0 ? (
<div style={{ color: "var(--dsw-alias-label-tertiary)" }}>No activity recorded</div>
) : (
<div>
<div style={{ color: "#34d399", fontWeight: 600 }}>
{formatExact(hoveredDay.stats.totalTokens)} tokens
</div>
<div style={{ fontSize: 11, color: "var(--dsw-alias-label-secondary)", marginTop: 2 }}>
{hoveredDay.stats.turns} requests / steps
</div>
<div style={{ fontSize: 10, color: "var(--dsw-alias-label-tertiary)", marginTop: 4 }}>
Billed: {formatNumber(hoveredDay.stats.inputTokens + hoveredDay.stats.outputTokens)} · Cache: {formatNumber(hoveredDay.stats.cacheReadTokens)}
</div>
</div>
)}
</div>
)}
</div>
);
}

106
src/client/index.tsx Normal file
View file

@ -0,0 +1,106 @@
import React, { useState, useEffect } from "react";
import { createRoot } from "react-dom/client";
import { HeatmapView } from "./HeatmapView.js";
import { HeatmapModal } from "./HeatmapModal.js";
export const name = "dsh-plugin-token-heatmap-client";
export const inject = ["slots"];
export function apply(ctx: any) {
// 1. Register under Settings -> "Token Heatmap"
if (ctx.slots && typeof ctx.slots.inject === "function") {
ctx.slots.inject("settings.section", () => {
return ctx.slots.register(
{
name: "settings.section",
id: "token-heatmap",
order: 14,
label: () => "Token Heatmap",
icon: "chart",
},
HeatmapView
);
});
}
// 2. Global Modal Host & In-Session Token Meter Integration
if (typeof document !== "undefined") {
const modalContainer = document.createElement("div");
modalContainer.id = "dsh-token-heatmap-modal-root";
document.body.appendChild(modalContainer);
const root = createRoot(modalContainer);
function ModalController() {
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
const handleOpen = () => setIsOpen(true);
const handleClose = () => setIsOpen(false);
window.addEventListener("dsh:open-token-heatmap", handleOpen);
window.addEventListener("keydown", (e) => {
if (e.key === "Escape") handleClose();
});
return () => {
window.removeEventListener("dsh:open-token-heatmap", handleOpen);
};
}, []);
return <HeatmapModal open={isOpen} onClose={() => setIsOpen(false)} />;
}
root.render(<ModalController />);
// 3. Observe DOM for in-session token usage dialog to attach "View Heatmap" button
const observer = new MutationObserver(() => {
// Find the open session stats dialog
const statDialogs = document.querySelectorAll(
'div[role="dialog"][aria-label*="usage" i], div[role="dialog"][aria-label*="Token" i], [data-session-stats-usage]'
);
statDialogs.forEach((dialog) => {
if (dialog.querySelector(".dsh-heatmap-trigger-btn")) return;
const button = document.createElement("button");
button.className = "dsh-heatmap-trigger-btn";
button.type = "button";
button.innerHTML = "<span>📊 View 12-Month Activity Heatmap →</span>";
button.style.cssText = `
display: flex;
align-items: center;
justify-content: center;
width: 100%;
margin-top: 12px;
padding: 8px 12px;
background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.12));
border: 1px solid var(--dsw-alias-border-l2, #494949);
border-radius: 8px;
color: var(--dsw-alias-brand-primary, #6366f1);
font-size: 12px;
font-weight: 550;
cursor: pointer;
transition: background 0.15s ease, border-color 0.15s ease;
`;
button.onmouseenter = () => {
button.style.background = "var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.2))";
button.style.borderColor = "var(--dsw-alias-brand-primary, #6366f1)";
};
button.onmouseleave = () => {
button.style.background = "var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.12))";
button.style.borderColor = "var(--dsw-alias-border-l2, #494949)";
};
button.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
window.dispatchEvent(new CustomEvent("dsh:open-token-heatmap"));
};
dialog.appendChild(button);
});
});
observer.observe(document.body, { childList: true, subtree: true });
}
}

303
src/server/aggregator.ts Normal file
View file

@ -0,0 +1,303 @@
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import { spawn } from "node:child_process";
export interface DayStats {
totalTokens: number;
inputTokens: number;
outputTokens: number;
cacheReadTokens: number;
cacheWriteTokens: number;
turns: number;
level?: number; // 0..4
}
export interface SummaryStats {
totalTokens: number;
totalInputTokens: number;
totalOutputTokens: number;
totalCacheReadTokens: number;
totalTurns: number;
activeDays: number;
currentStreak: number;
longestStreak: number;
peakDay: { date: string; tokens: number; turns: number } | null;
dailyAverage: number;
}
export interface HeatmapResponse {
summary: SummaryStats;
days: Record<string, DayStats>;
startDate: string;
endDate: string;
}
interface CacheEntry {
mtimeMs: number;
days: Record<string, DayStats>;
}
const fileCache = new Map<string, CacheEntry>();
function findSessionFiles(dir: string): string[] {
const results: string[] = [];
try {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
results.push(...findSessionFiles(fullPath));
} else if (entry.name.endsWith(".jsonl.zstd") || entry.name.endsWith(".jsonl")) {
results.push(fullPath);
}
}
} catch {
// Ignore unreadable dirs
}
return results;
}
async function parseSessionFile(filePath: string): Promise<Record<string, DayStats>> {
try {
const stat = fs.statSync(filePath);
const cached = fileCache.get(filePath);
if (cached && cached.mtimeMs === stat.mtimeMs) {
return cached.days;
}
const isZstd = filePath.endsWith(".zstd");
const days: Record<string, DayStats> = {};
await new Promise<void>((resolve) => {
let buffer = "";
const handleLine = (line: string) => {
if (!line.includes('"usage"')) return;
try {
const obj = JSON.parse(line);
const time = obj.time || (obj.data && obj.data.time);
const usage = obj.usage || (obj.data && obj.data.usage);
if (time && usage) {
const d = new Date(time);
const dateKey = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
const input = Number(usage.inputTokens) || 0;
const output = Number(usage.outputTokens) || 0;
const cacheRead = Number(usage.cacheReadTokens) || 0;
const cacheWrite = Number(usage.cacheWriteTokens) || 0;
const total = input + output + cacheRead;
if (!days[dateKey]) {
days[dateKey] = {
totalTokens: 0,
inputTokens: 0,
outputTokens: 0,
cacheReadTokens: 0,
cacheWriteTokens: 0,
turns: 0,
};
}
days[dateKey].totalTokens += total;
days[dateKey].inputTokens += input;
days[dateKey].outputTokens += output;
days[dateKey].cacheReadTokens += cacheRead;
days[dateKey].cacheWriteTokens += cacheWrite;
days[dateKey].turns += 1;
}
} catch {
// Ignore malformed lines
}
};
if (isZstd) {
const proc = spawn("zstd", ["-dc", filePath], { stdio: ["ignore", "pipe", "ignore"] });
proc.stdout.on("data", (chunk: Buffer) => {
buffer += chunk.toString("utf8");
let idx: number;
while ((idx = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, idx);
buffer = buffer.slice(idx + 1);
handleLine(line);
}
});
proc.on("close", () => {
if (buffer.trim().length > 0) handleLine(buffer);
resolve();
});
proc.on("error", () => resolve());
} else {
const stream = fs.createReadStream(filePath, { encoding: "utf8" });
stream.on("data", (chunk: string) => {
buffer += chunk;
let idx: number;
while ((idx = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, idx);
buffer = buffer.slice(idx + 1);
handleLine(line);
}
});
stream.on("end", () => {
if (buffer.trim().length > 0) handleLine(buffer);
resolve();
});
stream.on("error", () => resolve());
}
});
fileCache.set(filePath, { mtimeMs: stat.mtimeMs, days });
return days;
} catch {
return {};
}
}
export async function aggregateTokens(customSessionsRoot?: string): Promise<HeatmapResponse> {
const sessionsRoot = customSessionsRoot || path.join(os.homedir(), ".dsh", "sessions");
const files = findSessionFiles(sessionsRoot);
const parsedResults = await Promise.all(files.map(parseSessionFile));
const days: Record<string, DayStats> = {};
for (const fileDays of parsedResults) {
for (const [date, stats] of Object.entries(fileDays)) {
if (!days[date]) {
days[date] = {
totalTokens: 0,
inputTokens: 0,
outputTokens: 0,
cacheReadTokens: 0,
cacheWriteTokens: 0,
turns: 0,
};
}
days[date].totalTokens += stats.totalTokens;
days[date].inputTokens += stats.inputTokens;
days[date].outputTokens += stats.outputTokens;
days[date].cacheReadTokens += stats.cacheReadTokens;
days[date].cacheWriteTokens += stats.cacheWriteTokens;
days[date].turns += stats.turns;
}
}
// Calculate Levels (GitHub 0..4 intensity scale based on positive token days)
const positiveValues = Object.values(days)
.map((d) => d.totalTokens)
.filter((t) => t > 0)
.sort((a, b) => a - b);
let q1 = 5_000_000;
let q2 = 25_000_000;
let q3 = 75_000_000;
let q4 = 150_000_000;
if (positiveValues.length >= 4) {
q1 = positiveValues[Math.floor(positiveValues.length * 0.25)];
q2 = positiveValues[Math.floor(positiveValues.length * 0.50)];
q3 = positiveValues[Math.floor(positiveValues.length * 0.75)];
q4 = positiveValues[Math.floor(positiveValues.length * 0.90)];
}
for (const day of Object.values(days)) {
if (day.totalTokens === 0) day.level = 0;
else if (day.totalTokens <= q1) day.level = 1;
else if (day.totalTokens <= q2) day.level = 2;
else if (day.totalTokens <= q3) day.level = 3;
else day.level = 4;
}
// Calculate Summary & Streaks
let totalTokens = 0;
let totalInputTokens = 0;
let totalOutputTokens = 0;
let totalCacheReadTokens = 0;
let totalTurns = 0;
let peakDay: { date: string; tokens: number; turns: number } | null = null;
for (const [date, stats] of Object.entries(days)) {
totalTokens += stats.totalTokens;
totalInputTokens += stats.inputTokens;
totalOutputTokens += stats.outputTokens;
totalCacheReadTokens += stats.cacheReadTokens;
totalTurns += stats.turns;
if (!peakDay || stats.totalTokens > peakDay.tokens) {
peakDay = { date, tokens: stats.totalTokens, turns: stats.turns };
}
}
const activeDays = Object.values(days).filter((d) => d.totalTokens > 0).length;
const dailyAverage = activeDays > 0 ? Math.round(totalTokens / activeDays) : 0;
// Streak calculation (iterating backwards from today)
const now = new Date();
const formatDate = (d: Date) =>
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
let currentStreak = 0;
let cursor = new Date(now);
const todayKey = formatDate(cursor);
cursor.setDate(cursor.getDate() - 1);
const yesterdayKey = formatDate(cursor);
// If active today, start from today; else if active yesterday, start from yesterday
let streakDate = (days[todayKey]?.totalTokens ?? 0) > 0 ? new Date(now) : (days[yesterdayKey]?.totalTokens ?? 0) > 0 ? new Date(cursor) : null;
if (streakDate) {
while (true) {
const key = formatDate(streakDate);
if ((days[key]?.totalTokens ?? 0) > 0) {
currentStreak++;
streakDate.setDate(streakDate.getDate() - 1);
} else {
break;
}
}
}
// Longest streak
const sortedDates = Object.keys(days)
.filter((k) => days[k].totalTokens > 0)
.sort();
let longestStreak = 0;
let tempStreak = 0;
let prevDate: Date | null = null;
for (const dateStr of sortedDates) {
const cur = new Date(dateStr + "T00:00:00");
if (!prevDate) {
tempStreak = 1;
} else {
const diffDays = Math.round((cur.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24));
if (diffDays === 1) {
tempStreak++;
} else {
tempStreak = 1;
}
}
if (tempStreak > longestStreak) longestStreak = tempStreak;
prevDate = cur;
}
const endDate = formatDate(now);
const start = new Date(now);
start.setDate(start.getDate() - 364); // 52 weeks = 364 days
const startDate = formatDate(start);
return {
summary: {
totalTokens,
totalInputTokens,
totalOutputTokens,
totalCacheReadTokens,
totalTurns,
activeDays,
currentStreak,
longestStreak: Math.max(longestStreak, currentStreak),
peakDay,
dailyAverage,
},
days,
startDate,
endDate,
};
}

46
src/server/index.ts Normal file
View file

@ -0,0 +1,46 @@
import { aggregateTokens } from "./aggregator.js";
export const name = "dsh-plugin-token-heatmap";
export const inject = ["webServer"];
export function apply(ctx: any) {
ctx.effect(() =>
ctx.webServer.register({
kind: "prefix",
path: "/api/dsh-token-heatmap",
handler: async (req: any, res: any) => {
const url = new URL(req.url, "http://localhost");
const pathname = url.pathname.replace(/\/+$/, "");
// Set common headers
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
if (req.method === "OPTIONS") {
res.writeHead(204);
res.end();
return;
}
if (req.method === "GET" && (pathname === "/api/dsh-token-heatmap" || pathname === "/api/dsh-token-heatmap/stats")) {
try {
const data = await aggregateTokens();
res.writeHead(200, {
"Content-Type": "application/json; charset=utf-8",
"Cache-Control": "no-cache",
});
res.end(JSON.stringify(data));
} catch (err: any) {
res.writeHead(500, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: err?.message || "Failed to aggregate token stats" }));
}
return;
}
res.writeHead(404, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Not found" }));
},
})
);
}