From a151fcba93bb0c41f6f0382a7a5cadeb790be055 Mon Sep 17 00:00:00 2001 From: oiupoyt Date: Sat, 10 Oct 2026 20:05:22 +0530 Subject: [PATCH] feat: center total token usage, auto-resize settings panel to 1080px, clear blue color on cache, add model breakdown --- lib/aggregator.js | 36 ++- lib/aggregator.js.map | 4 +- lib/client.js | 380 ++++++++++++++---------- lib/client.js.map | 4 +- src/client/HeatmapView.tsx | 572 +++++++++++++++++++++++-------------- src/server/aggregator.ts | 52 ++++ 6 files changed, 683 insertions(+), 365 deletions(-) diff --git a/lib/aggregator.js b/lib/aggregator.js index 201db15..46087b7 100644 --- a/lib/aggregator.js +++ b/lib/aggregator.js @@ -28,6 +28,7 @@ async function parseSessionFile(filePath) { } const isZstd = filePath.endsWith(".zstd"); const days = {}; + const sessionModels = {}; let sessionId = path.basename(path.dirname(filePath)); let title = ""; let createdAt = stat.birthtimeMs || stat.mtimeMs; @@ -74,6 +75,7 @@ async function parseSessionFile(filePath) { const cacheRead = Number(usage.cacheReadTokens) || 0; const cacheWrite = Number(usage.cacheWriteTokens) || 0; const total = input + output + cacheRead; + const model = obj.data?.message?.source?.model || obj.data?.model || obj.model || "gemini-3.8-flash-tiered"; if (!days[dateKey]) { days[dateKey] = { totalTokens: 0, @@ -90,6 +92,14 @@ async function parseSessionFile(filePath) { days[dateKey].cacheReadTokens += cacheRead; days[dateKey].cacheWriteTokens += cacheWrite; days[dateKey].turns += 1; + if (!sessionModels[model]) { + sessionModels[model] = { totalTokens: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, turns: 0 }; + } + sessionModels[model].totalTokens += total; + sessionModels[model].inputTokens += input; + sessionModels[model].outputTokens += output; + sessionModels[model].cacheReadTokens += cacheRead; + sessionModels[model].turns += 1; totalTokens += total; totalInputTokens += input; totalOutputTokens += output; @@ -143,7 +153,8 @@ async function parseSessionFile(filePath) { outputTokens: totalOutputTokens, cacheReadTokens: totalCacheReadTokens, turns: totalTurns, - days + days, + models: sessionModels }; fileCache.set(filePath, { mtimeMs: stat.mtimeMs, session }); return session; @@ -157,6 +168,7 @@ async function aggregateTokens(customSessionsRoot) { const parsedSessions = (await Promise.all(files.map(parseSessionFile))).filter(Boolean); parsedSessions.sort((a, b) => b.createdAt - a.createdAt); const days = {}; + const allModelsMap = {}; for (const session of parsedSessions) { for (const [date, stats] of Object.entries(session.days)) { if (!days[date]) { @@ -187,6 +199,18 @@ async function aggregateTokens(customSessionsRoot) { turns: stats.turns }); } + if (session.models) { + for (const [modelName, mStats] of Object.entries(session.models)) { + if (!allModelsMap[modelName]) { + allModelsMap[modelName] = { totalTokens: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, turns: 0 }; + } + allModelsMap[modelName].totalTokens += mStats.totalTokens; + allModelsMap[modelName].inputTokens += mStats.inputTokens; + allModelsMap[modelName].outputTokens += mStats.outputTokens; + allModelsMap[modelName].cacheReadTokens += mStats.cacheReadTokens; + allModelsMap[modelName].turns += mStats.turns; + } + } } for (const day of Object.values(days)) { if (day.sessions) { @@ -225,6 +249,15 @@ async function aggregateTokens(customSessionsRoot) { peakDay = { date, tokens: stats.totalTokens, turns: stats.turns }; } } + const models = Object.entries(allModelsMap).map(([model, m]) => ({ + model, + totalTokens: m.totalTokens, + inputTokens: m.inputTokens, + outputTokens: m.outputTokens, + cacheReadTokens: m.cacheReadTokens, + turns: m.turns, + percentage: totalTokens > 0 ? Math.round(m.totalTokens / totalTokens * 1e3) / 10 : 0 + })).sort((a, b) => b.totalTokens - a.totalTokens); 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(); @@ -284,6 +317,7 @@ async function aggregateTokens(customSessionsRoot) { }, days, sessions: parsedSessions, + models, startDate, endDate }; diff --git a/lib/aggregator.js.map b/lib/aggregator.js.map index debbc92..4f2c732 100644 --- a/lib/aggregator.js.map +++ b/lib/aggregator.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../src/server/aggregator.ts"], - "sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { spawn } from \"node:child_process\";\n\nexport interface DaySessionDetail {\n sessionId: string;\n sessionTitle: string;\n createdAt: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n turns: number;\n}\n\nexport interface DayStats {\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n turns: number;\n level?: number; // 0..4\n sessions?: DaySessionDetail[];\n}\n\nexport interface SessionSummary {\n id: string;\n title: string;\n createdAt: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n turns: number;\n days: Record;\n}\n\nexport interface SummaryStats {\n totalTokens: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheReadTokens: number;\n totalTurns: number;\n activeDays: number;\n currentStreak: number;\n longestStreak: number;\n peakDay: { date: string; tokens: number; turns: number } | null;\n dailyAverage: number;\n}\n\nexport interface HeatmapResponse {\n summary: SummaryStats;\n days: Record;\n sessions: SessionSummary[];\n startDate: string;\n endDate: string;\n}\n\ninterface CacheEntry {\n mtimeMs: number;\n session: SessionSummary;\n}\n\nconst fileCache = new Map();\n\nfunction findSessionFiles(dir: string): string[] {\n const results: string[] = [];\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(...findSessionFiles(fullPath));\n } else if (entry.name.endsWith(\".jsonl.zstd\") || entry.name.endsWith(\".jsonl\")) {\n results.push(fullPath);\n }\n }\n } catch {\n // Ignore unreadable dirs\n }\n return results;\n}\n\nasync function parseSessionFile(filePath: string): Promise {\n try {\n const stat = fs.statSync(filePath);\n const cached = fileCache.get(filePath);\n if (cached && cached.mtimeMs === stat.mtimeMs) {\n return cached.session;\n }\n\n const isZstd = filePath.endsWith(\".zstd\");\n const days: Record = {};\n let sessionId = path.basename(path.dirname(filePath));\n let title = \"\";\n let createdAt = stat.birthtimeMs || stat.mtimeMs;\n let totalTokens = 0;\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n let totalCacheReadTokens = 0;\n let totalTurns = 0;\n\n await new Promise((resolve) => {\n let buffer = \"\";\n const handleLine = (line: string) => {\n if (line.includes('\"type\":\"session\"')) {\n try {\n const obj = JSON.parse(line);\n if (obj.id) sessionId = obj.id;\n if (obj.createdAt) createdAt = obj.createdAt;\n } catch {}\n }\n if (!title && line.includes('\"session/title\"')) {\n try {\n const obj = JSON.parse(line);\n if (obj.data?.title) title = obj.data.title;\n } catch {}\n }\n if (!title && line.includes('\"subagent/descriptor\"')) {\n try {\n const obj = JSON.parse(line);\n if (obj.data?.label) title = obj.data.label;\n } catch {}\n }\n if (line.includes('\"usage\"')) {\n try {\n const obj = JSON.parse(line);\n const time = obj.time || (obj.data && obj.data.time);\n const usage = obj.usage || (obj.data && obj.data.usage);\n if (time && usage) {\n const d = new Date(time);\n const dateKey = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n const input = Number(usage.inputTokens) || 0;\n const output = Number(usage.outputTokens) || 0;\n const cacheRead = Number(usage.cacheReadTokens) || 0;\n const cacheWrite = Number(usage.cacheWriteTokens) || 0;\n const total = input + output + cacheRead;\n\n if (!days[dateKey]) {\n days[dateKey] = {\n totalTokens: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n turns: 0,\n };\n }\n days[dateKey].totalTokens += total;\n days[dateKey].inputTokens += input;\n days[dateKey].outputTokens += output;\n days[dateKey].cacheReadTokens += cacheRead;\n days[dateKey].cacheWriteTokens += cacheWrite;\n days[dateKey].turns += 1;\n\n totalTokens += total;\n totalInputTokens += input;\n totalOutputTokens += output;\n totalCacheReadTokens += cacheRead;\n totalTurns += 1;\n }\n } catch {\n // Ignore malformed lines\n }\n }\n };\n\n if (isZstd) {\n const proc = spawn(\"zstd\", [\"-dc\", filePath], { stdio: [\"ignore\", \"pipe\", \"ignore\"] });\n proc.stdout.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString(\"utf8\");\n let idx: number;\n while ((idx = buffer.indexOf(\"\\n\")) !== -1) {\n const line = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 1);\n handleLine(line);\n }\n });\n proc.on(\"close\", () => {\n if (buffer.trim().length > 0) handleLine(buffer);\n resolve();\n });\n proc.on(\"error\", () => resolve());\n } else {\n const stream = fs.createReadStream(filePath, { encoding: \"utf8\" });\n stream.on(\"data\", (chunk: string) => {\n buffer += chunk;\n let idx: number;\n while ((idx = buffer.indexOf(\"\\n\")) !== -1) {\n const line = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 1);\n handleLine(line);\n }\n });\n stream.on(\"end\", () => {\n if (buffer.trim().length > 0) handleLine(buffer);\n resolve();\n });\n stream.on(\"error\", () => resolve());\n }\n });\n\n const session: SessionSummary = {\n id: sessionId,\n title: title || `Session ${sessionId.slice(0, 8)}`,\n createdAt,\n totalTokens,\n inputTokens: totalInputTokens,\n outputTokens: totalOutputTokens,\n cacheReadTokens: totalCacheReadTokens,\n turns: totalTurns,\n days,\n };\n\n fileCache.set(filePath, { mtimeMs: stat.mtimeMs, session });\n return session;\n } catch {\n return null;\n }\n}\n\nexport async function aggregateTokens(customSessionsRoot?: string): Promise {\n const sessionsRoot = customSessionsRoot || path.join(os.homedir(), \".dsh\", \"sessions\");\n const files = findSessionFiles(sessionsRoot);\n const parsedSessions = (await Promise.all(files.map(parseSessionFile))).filter(Boolean) as SessionSummary[];\n\n // Sort sessions newest first\n parsedSessions.sort((a, b) => b.createdAt - a.createdAt);\n\n const days: Record = {};\n\n for (const session of parsedSessions) {\n for (const [date, stats] of Object.entries(session.days)) {\n if (!days[date]) {\n days[date] = {\n totalTokens: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n turns: 0,\n sessions: [],\n };\n }\n days[date].totalTokens += stats.totalTokens;\n days[date].inputTokens += stats.inputTokens;\n days[date].outputTokens += stats.outputTokens;\n days[date].cacheReadTokens += stats.cacheReadTokens;\n days[date].cacheWriteTokens += stats.cacheWriteTokens;\n days[date].turns += stats.turns;\n\n days[date].sessions!.push({\n sessionId: session.id,\n sessionTitle: session.title,\n createdAt: session.createdAt,\n totalTokens: stats.totalTokens,\n inputTokens: stats.inputTokens,\n outputTokens: stats.outputTokens,\n cacheReadTokens: stats.cacheReadTokens,\n turns: stats.turns,\n });\n }\n }\n\n // Sort sessions within each day by totalTokens descending\n for (const day of Object.values(days)) {\n if (day.sessions) {\n day.sessions.sort((a, b) => b.totalTokens - a.totalTokens);\n }\n }\n\n // Calculate Levels (0..4 intensity scale based on positive token days)\n const positiveValues = Object.values(days)\n .map((d) => d.totalTokens)\n .filter((t) => t > 0)\n .sort((a, b) => a - b);\n\n let q1 = 5_000_000;\n let q2 = 25_000_000;\n let q3 = 75_000_000;\n\n if (positiveValues.length >= 4) {\n q1 = positiveValues[Math.floor(positiveValues.length * 0.25)];\n q2 = positiveValues[Math.floor(positiveValues.length * 0.50)];\n q3 = positiveValues[Math.floor(positiveValues.length * 0.75)];\n }\n\n for (const day of Object.values(days)) {\n if (day.totalTokens === 0) day.level = 0;\n else if (day.totalTokens <= q1) day.level = 1;\n else if (day.totalTokens <= q2) day.level = 2;\n else if (day.totalTokens <= q3) day.level = 3;\n else day.level = 4;\n }\n\n // Calculate Summary & Streaks\n let totalTokens = 0;\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n let totalCacheReadTokens = 0;\n let totalTurns = 0;\n let peakDay: { date: string; tokens: number; turns: number } | null = null;\n\n for (const [date, stats] of Object.entries(days)) {\n totalTokens += stats.totalTokens;\n totalInputTokens += stats.inputTokens;\n totalOutputTokens += stats.outputTokens;\n totalCacheReadTokens += stats.cacheReadTokens;\n totalTurns += stats.turns;\n\n if (!peakDay || stats.totalTokens > peakDay.tokens) {\n peakDay = { date, tokens: stats.totalTokens, turns: stats.turns };\n }\n }\n\n const activeDays = Object.values(days).filter((d) => d.totalTokens > 0).length;\n const dailyAverage = activeDays > 0 ? Math.round(totalTokens / activeDays) : 0;\n\n // Streak calculation (iterating backwards from today)\n const now = new Date();\n const formatDate = (d: Date) =>\n `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n\n let currentStreak = 0;\n let cursor = new Date(now);\n const todayKey = formatDate(cursor);\n cursor.setDate(cursor.getDate() - 1);\n const yesterdayKey = formatDate(cursor);\n\n let streakDate = (days[todayKey]?.totalTokens ?? 0) > 0 ? new Date(now) : (days[yesterdayKey]?.totalTokens ?? 0) > 0 ? new Date(cursor) : null;\n\n if (streakDate) {\n while (true) {\n const key = formatDate(streakDate);\n if ((days[key]?.totalTokens ?? 0) > 0) {\n currentStreak++;\n streakDate.setDate(streakDate.getDate() - 1);\n } else {\n break;\n }\n }\n }\n\n // Longest streak\n const sortedDates = Object.keys(days)\n .filter((k) => days[k].totalTokens > 0)\n .sort();\n\n let longestStreak = 0;\n let tempStreak = 0;\n let prevDate: Date | null = null;\n\n for (const dateStr of sortedDates) {\n const cur = new Date(dateStr + \"T00:00:00\");\n if (!prevDate) {\n tempStreak = 1;\n } else {\n const diffDays = Math.round((cur.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24));\n if (diffDays === 1) {\n tempStreak++;\n } else {\n tempStreak = 1;\n }\n }\n if (tempStreak > longestStreak) longestStreak = tempStreak;\n prevDate = cur;\n }\n\n const endDate = formatDate(now);\n const start = new Date(now);\n start.setDate(start.getDate() - 364);\n const startDate = formatDate(start);\n\n return {\n summary: {\n totalTokens,\n totalInputTokens,\n totalOutputTokens,\n totalCacheReadTokens,\n totalTurns,\n activeDays,\n currentStreak,\n longestStreak: Math.max(longestStreak, currentStreak),\n peakDay,\n dailyAverage,\n },\n days,\n sessions: parsedSessions,\n startDate,\n endDate,\n };\n}\n"], - "mappings": "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", + "sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { spawn } from \"node:child_process\";\n\nexport interface DaySessionDetail {\n sessionId: string;\n sessionTitle: string;\n createdAt: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n turns: number;\n}\n\nexport interface DayStats {\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n turns: number;\n level?: number; // 0..4\n sessions?: DaySessionDetail[];\n}\n\nexport interface ModelUsage {\n model: string;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n turns: number;\n percentage: number;\n}\n\nexport interface SessionSummary {\n id: string;\n title: string;\n createdAt: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n turns: number;\n days: Record;\n models?: Record;\n}\n\nexport interface SummaryStats {\n totalTokens: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheReadTokens: number;\n totalTurns: number;\n activeDays: number;\n currentStreak: number;\n longestStreak: number;\n peakDay: { date: string; tokens: number; turns: number } | null;\n dailyAverage: number;\n}\n\nexport interface HeatmapResponse {\n summary: SummaryStats;\n days: Record;\n sessions: SessionSummary[];\n models: ModelUsage[];\n startDate: string;\n endDate: string;\n}\n\ninterface CacheEntry {\n mtimeMs: number;\n session: SessionSummary;\n}\n\nconst fileCache = new Map();\n\nfunction findSessionFiles(dir: string): string[] {\n const results: string[] = [];\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(...findSessionFiles(fullPath));\n } else if (entry.name.endsWith(\".jsonl.zstd\") || entry.name.endsWith(\".jsonl\")) {\n results.push(fullPath);\n }\n }\n } catch {\n // Ignore unreadable dirs\n }\n return results;\n}\n\nasync function parseSessionFile(filePath: string): Promise {\n try {\n const stat = fs.statSync(filePath);\n const cached = fileCache.get(filePath);\n if (cached && cached.mtimeMs === stat.mtimeMs) {\n return cached.session;\n }\n\n const isZstd = filePath.endsWith(\".zstd\");\n const days: Record = {};\n const sessionModels: Record = {};\n let sessionId = path.basename(path.dirname(filePath));\n let title = \"\";\n let createdAt = stat.birthtimeMs || stat.mtimeMs;\n let totalTokens = 0;\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n let totalCacheReadTokens = 0;\n let totalTurns = 0;\n\n await new Promise((resolve) => {\n let buffer = \"\";\n const handleLine = (line: string) => {\n if (line.includes('\"type\":\"session\"')) {\n try {\n const obj = JSON.parse(line);\n if (obj.id) sessionId = obj.id;\n if (obj.createdAt) createdAt = obj.createdAt;\n } catch {}\n }\n if (!title && line.includes('\"session/title\"')) {\n try {\n const obj = JSON.parse(line);\n if (obj.data?.title) title = obj.data.title;\n } catch {}\n }\n if (!title && line.includes('\"subagent/descriptor\"')) {\n try {\n const obj = JSON.parse(line);\n if (obj.data?.label) title = obj.data.label;\n } catch {}\n }\n if (line.includes('\"usage\"')) {\n try {\n const obj = JSON.parse(line);\n const time = obj.time || (obj.data && obj.data.time);\n const usage = obj.usage || (obj.data && obj.data.usage);\n if (time && usage) {\n const d = new Date(time);\n const dateKey = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n const input = Number(usage.inputTokens) || 0;\n const output = Number(usage.outputTokens) || 0;\n const cacheRead = Number(usage.cacheReadTokens) || 0;\n const cacheWrite = Number(usage.cacheWriteTokens) || 0;\n const total = input + output + cacheRead;\n const model = obj.data?.message?.source?.model || obj.data?.model || obj.model || \"gemini-3.8-flash-tiered\";\n\n if (!days[dateKey]) {\n days[dateKey] = {\n totalTokens: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n turns: 0,\n };\n }\n days[dateKey].totalTokens += total;\n days[dateKey].inputTokens += input;\n days[dateKey].outputTokens += output;\n days[dateKey].cacheReadTokens += cacheRead;\n days[dateKey].cacheWriteTokens += cacheWrite;\n days[dateKey].turns += 1;\n\n if (!sessionModels[model]) {\n sessionModels[model] = { totalTokens: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, turns: 0 };\n }\n sessionModels[model].totalTokens += total;\n sessionModels[model].inputTokens += input;\n sessionModels[model].outputTokens += output;\n sessionModels[model].cacheReadTokens += cacheRead;\n sessionModels[model].turns += 1;\n\n totalTokens += total;\n totalInputTokens += input;\n totalOutputTokens += output;\n totalCacheReadTokens += cacheRead;\n totalTurns += 1;\n }\n } catch {\n // Ignore malformed lines\n }\n }\n };\n\n if (isZstd) {\n const proc = spawn(\"zstd\", [\"-dc\", filePath], { stdio: [\"ignore\", \"pipe\", \"ignore\"] });\n proc.stdout.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString(\"utf8\");\n let idx: number;\n while ((idx = buffer.indexOf(\"\\n\")) !== -1) {\n const line = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 1);\n handleLine(line);\n }\n });\n proc.on(\"close\", () => {\n if (buffer.trim().length > 0) handleLine(buffer);\n resolve();\n });\n proc.on(\"error\", () => resolve());\n } else {\n const stream = fs.createReadStream(filePath, { encoding: \"utf8\" });\n stream.on(\"data\", (chunk: string) => {\n buffer += chunk;\n let idx: number;\n while ((idx = buffer.indexOf(\"\\n\")) !== -1) {\n const line = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 1);\n handleLine(line);\n }\n });\n stream.on(\"end\", () => {\n if (buffer.trim().length > 0) handleLine(buffer);\n resolve();\n });\n stream.on(\"error\", () => resolve());\n }\n });\n\n const session: SessionSummary = {\n id: sessionId,\n title: title || `Session ${sessionId.slice(0, 8)}`,\n createdAt,\n totalTokens,\n inputTokens: totalInputTokens,\n outputTokens: totalOutputTokens,\n cacheReadTokens: totalCacheReadTokens,\n turns: totalTurns,\n days,\n models: sessionModels,\n };\n\n fileCache.set(filePath, { mtimeMs: stat.mtimeMs, session });\n return session;\n } catch {\n return null;\n }\n}\n\nexport async function aggregateTokens(customSessionsRoot?: string): Promise {\n const sessionsRoot = customSessionsRoot || path.join(os.homedir(), \".dsh\", \"sessions\");\n const files = findSessionFiles(sessionsRoot);\n const parsedSessions = (await Promise.all(files.map(parseSessionFile))).filter(Boolean) as SessionSummary[];\n\n // Sort sessions newest first\n parsedSessions.sort((a, b) => b.createdAt - a.createdAt);\n\n const days: Record = {};\n const allModelsMap: Record = {};\n\n for (const session of parsedSessions) {\n for (const [date, stats] of Object.entries(session.days)) {\n if (!days[date]) {\n days[date] = {\n totalTokens: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n turns: 0,\n sessions: [],\n };\n }\n days[date].totalTokens += stats.totalTokens;\n days[date].inputTokens += stats.inputTokens;\n days[date].outputTokens += stats.outputTokens;\n days[date].cacheReadTokens += stats.cacheReadTokens;\n days[date].cacheWriteTokens += stats.cacheWriteTokens;\n days[date].turns += stats.turns;\n\n days[date].sessions!.push({\n sessionId: session.id,\n sessionTitle: session.title,\n createdAt: session.createdAt,\n totalTokens: stats.totalTokens,\n inputTokens: stats.inputTokens,\n outputTokens: stats.outputTokens,\n cacheReadTokens: stats.cacheReadTokens,\n turns: stats.turns,\n });\n }\n\n if (session.models) {\n for (const [modelName, mStats] of Object.entries(session.models)) {\n if (!allModelsMap[modelName]) {\n allModelsMap[modelName] = { totalTokens: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, turns: 0 };\n }\n allModelsMap[modelName].totalTokens += mStats.totalTokens;\n allModelsMap[modelName].inputTokens += mStats.inputTokens;\n allModelsMap[modelName].outputTokens += mStats.outputTokens;\n allModelsMap[modelName].cacheReadTokens += mStats.cacheReadTokens;\n allModelsMap[modelName].turns += mStats.turns;\n }\n }\n }\n\n // Sort sessions within each day by totalTokens descending\n for (const day of Object.values(days)) {\n if (day.sessions) {\n day.sessions.sort((a, b) => b.totalTokens - a.totalTokens);\n }\n }\n\n // Calculate Levels (0..4 intensity scale based on positive token days)\n const positiveValues = Object.values(days)\n .map((d) => d.totalTokens)\n .filter((t) => t > 0)\n .sort((a, b) => a - b);\n\n let q1 = 5_000_000;\n let q2 = 25_000_000;\n let q3 = 75_000_000;\n\n if (positiveValues.length >= 4) {\n q1 = positiveValues[Math.floor(positiveValues.length * 0.25)];\n q2 = positiveValues[Math.floor(positiveValues.length * 0.50)];\n q3 = positiveValues[Math.floor(positiveValues.length * 0.75)];\n }\n\n for (const day of Object.values(days)) {\n if (day.totalTokens === 0) day.level = 0;\n else if (day.totalTokens <= q1) day.level = 1;\n else if (day.totalTokens <= q2) day.level = 2;\n else if (day.totalTokens <= q3) day.level = 3;\n else day.level = 4;\n }\n\n // Calculate Summary & Streaks\n let totalTokens = 0;\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n let totalCacheReadTokens = 0;\n let totalTurns = 0;\n let peakDay: { date: string; tokens: number; turns: number } | null = null;\n\n for (const [date, stats] of Object.entries(days)) {\n totalTokens += stats.totalTokens;\n totalInputTokens += stats.inputTokens;\n totalOutputTokens += stats.outputTokens;\n totalCacheReadTokens += stats.cacheReadTokens;\n totalTurns += stats.turns;\n\n if (!peakDay || stats.totalTokens > peakDay.tokens) {\n peakDay = { date, tokens: stats.totalTokens, turns: stats.turns };\n }\n }\n\n // Build sorted model breakdown array\n const models: ModelUsage[] = Object.entries(allModelsMap)\n .map(([model, m]) => ({\n model,\n totalTokens: m.totalTokens,\n inputTokens: m.inputTokens,\n outputTokens: m.outputTokens,\n cacheReadTokens: m.cacheReadTokens,\n turns: m.turns,\n percentage: totalTokens > 0 ? Math.round((m.totalTokens / totalTokens) * 1000) / 10 : 0,\n }))\n .sort((a, b) => b.totalTokens - a.totalTokens);\n\n const activeDays = Object.values(days).filter((d) => d.totalTokens > 0).length;\n const dailyAverage = activeDays > 0 ? Math.round(totalTokens / activeDays) : 0;\n\n // Streak calculation (iterating backwards from today)\n const now = new Date();\n const formatDate = (d: Date) =>\n `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n\n let currentStreak = 0;\n let cursor = new Date(now);\n const todayKey = formatDate(cursor);\n cursor.setDate(cursor.getDate() - 1);\n const yesterdayKey = formatDate(cursor);\n\n let streakDate = (days[todayKey]?.totalTokens ?? 0) > 0 ? new Date(now) : (days[yesterdayKey]?.totalTokens ?? 0) > 0 ? new Date(cursor) : null;\n\n if (streakDate) {\n while (true) {\n const key = formatDate(streakDate);\n if ((days[key]?.totalTokens ?? 0) > 0) {\n currentStreak++;\n streakDate.setDate(streakDate.getDate() - 1);\n } else {\n break;\n }\n }\n }\n\n // Longest streak\n const sortedDates = Object.keys(days)\n .filter((k) => days[k].totalTokens > 0)\n .sort();\n\n let longestStreak = 0;\n let tempStreak = 0;\n let prevDate: Date | null = null;\n\n for (const dateStr of sortedDates) {\n const cur = new Date(dateStr + \"T00:00:00\");\n if (!prevDate) {\n tempStreak = 1;\n } else {\n const diffDays = Math.round((cur.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24));\n if (diffDays === 1) {\n tempStreak++;\n } else {\n tempStreak = 1;\n }\n }\n if (tempStreak > longestStreak) longestStreak = tempStreak;\n prevDate = cur;\n }\n\n const endDate = formatDate(now);\n const start = new Date(now);\n start.setDate(start.getDate() - 364);\n const startDate = formatDate(start);\n\n return {\n summary: {\n totalTokens,\n totalInputTokens,\n totalOutputTokens,\n totalCacheReadTokens,\n totalTurns,\n activeDays,\n currentStreak,\n longestStreak: Math.max(longestStreak, currentStreak),\n peakDay,\n dailyAverage,\n },\n days,\n sessions: parsedSessions,\n models,\n startDate,\n endDate,\n };\n}\n"], + "mappings": "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", "names": [] } diff --git a/lib/client.js b/lib/client.js index 1c2799a..11440a0 100644 --- a/lib/client.js +++ b/lib/client.js @@ -72,7 +72,23 @@ function HeatmapInnerView() { const [hoveredDay, setHoveredDay] = (0, import_react.useState)(null); const [hoveredBar, setHoveredBar] = (0, import_react.useState)(null); const [tooltipPos, setTooltipPos] = (0, import_react.useState)(null); - const scrollContainerRef = (0, import_react.useRef)(null); + (0, import_react.useEffect)(() => { + if (typeof document === "undefined") return; + const panel = document.querySelector('[class*="_panel"]'); + if (panel) { + const prevWidth = panel.style.width; + const prevMaxWidth = panel.style.maxWidth; + const prevTransition = panel.style.transition; + panel.style.transition = "width 0.25s cubic-bezier(0.16, 1, 0.3, 1), max-width 0.25s ease"; + panel.style.width = "min(1080px, calc(100vw - 32px))"; + panel.style.maxWidth = "calc(100vw - 32px)"; + return () => { + panel.style.width = prevWidth; + panel.style.maxWidth = prevMaxWidth; + panel.style.transition = prevTransition; + }; + } + }, []); const fetchStats = () => { setLoading(true); fetch("/api/dsh-token-heatmap/stats").then((r) => { @@ -95,6 +111,7 @@ function HeatmapInnerView() { return { summary: data.summary, days: data.days, + models: data.models || [], sessionTitle: "All Sessions (Global)" }; } @@ -103,6 +120,7 @@ function HeatmapInnerView() { return { summary: data.summary, days: data.days, + models: data.models || [], sessionTitle: "All Sessions (Global)" }; } @@ -138,6 +156,15 @@ function HeatmapInnerView() { else lvl = 4; leveledDays[date] = { ...stats, level: lvl }; } + const sessionModels = session.models ? Object.entries(session.models).map(([model, m]) => ({ + model, + totalTokens: m.totalTokens, + inputTokens: m.inputTokens, + outputTokens: m.outputTokens, + cacheReadTokens: m.cacheReadTokens, + turns: m.turns, + percentage: totalTokens > 0 ? Math.round(m.totalTokens / totalTokens * 1e3) / 10 : 0 + })).sort((a, b) => b.totalTokens - a.totalTokens) : []; return { summary: { totalTokens, @@ -152,6 +179,7 @@ function HeatmapInnerView() { dailyAverage }, days: leveledDays, + models: sessionModels, sessionTitle: session.title }; }, [data, selectedSessionId]); @@ -207,11 +235,6 @@ function HeatmapInnerView() { }); return headers; }, [calendarGrid]); - (0, import_react.useEffect)(() => { - if (scrollContainerRef.current) { - scrollContainerRef.current.scrollLeft = scrollContainerRef.current.scrollWidth; - } - }, [calendarGrid]); const chartData = (0, import_react.useMemo)(() => { if (!activeView) return []; const now = /* @__PURE__ */ new Date(); @@ -270,12 +293,13 @@ function HeatmapInnerView() { return /* @__PURE__ */ import_react.default.createElement( "div", { + "data-dsh-heatmap": "true", style: { boxSizing: "border-box", width: "100%", - maxWidth: "960px", + maxWidth: "1020px", margin: "0 auto", - padding: "16px 0 40px", + padding: "8px 0 40px", fontFamily: "inherit", color: "var(--dsw-alias-label-primary, #eeeeee)" } @@ -289,7 +313,7 @@ function HeatmapInnerView() { justifyContent: "space-between", flexWrap: "wrap", gap: 14, - marginBottom: 20 + marginBottom: 16 } }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement( @@ -297,7 +321,7 @@ function HeatmapInnerView() { { style: { margin: 0, - fontSize: 22, + fontSize: 20, fontWeight: 650, letterSpacing: "-0.02em", display: "flex", @@ -325,12 +349,12 @@ function HeatmapInnerView() { "p", { style: { - margin: "5px 0 0", - fontSize: 13, + margin: "4px 0 0", + fontSize: 12, color: "var(--dsw-alias-label-tertiary, #a0a0a0)" } }, - "Daily token volume and request step intensity across sessions" + "Daily token throughput and model breakdown across sessions" )), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 6 } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { fontSize: 12, color: "var(--dsw-alias-label-tertiary)" } }, "Session:"), /* @__PURE__ */ import_react.default.createElement( "select", @@ -376,6 +400,149 @@ function HeatmapInnerView() { "\u21BB Refresh" )) ), + /* @__PURE__ */ import_react.default.createElement( + "div", + { + style: { + textAlign: "center", + margin: "12px 0 20px", + padding: "16px 20px", + background: "var(--dsw-alias-bg-layer-2, #2c2c2c)", + border: "1px solid var(--dsw-alias-border-l2, #494949)", + borderRadius: 12, + boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)" + } + }, + /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, textTransform: "uppercase", letterSpacing: "0.08em", color: "var(--dsw-alias-label-tertiary, #a0a0a0)", fontWeight: 600 } }, "Total Tokens Processed"), + /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 36, fontWeight: 800, margin: "6px 0 2px", color: "#4f85ff", letterSpacing: "-0.03em" } }, formatExact(summary.totalTokens)), + /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, color: "var(--dsw-alias-label-secondary, #b9b9b9)" } }, /* @__PURE__ */ import_react.default.createElement("strong", null, formatNumber(summary.totalTokens)), " tokens across ", summary.activeDays, " active days (", summary.totalTurns, " total requests)") + ), + /* @__PURE__ */ import_react.default.createElement( + "div", + { + style: { + background: "var(--dsw-alias-bg-layer-2, #2c2c2c)", + border: "1px solid var(--dsw-alias-border-l2, #494949)", + borderRadius: 12, + padding: "18px 20px", + position: "relative", + boxShadow: "0 4px 16px rgba(0, 0, 0, 0.2)" + } + }, + /* @__PURE__ */ import_react.default.createElement( + "div", + { + style: { + display: "flex", + alignItems: "center", + justifyContent: "space-between", + marginBottom: 12, + fontSize: 13, + fontWeight: 600 + } + }, + /* @__PURE__ */ import_react.default.createElement("span", null, "Contribution Calendar"), + summary.peakDay && /* @__PURE__ */ import_react.default.createElement("span", { style: { fontSize: 12, fontWeight: 400, color: "var(--dsw-alias-label-secondary)" } }, "Peak day: ", /* @__PURE__ */ import_react.default.createElement("strong", { style: { color: "var(--dsw-alias-label-primary)" } }, summary.peakDay.date), " (", formatNumber(summary.peakDay.tokens), " tokens)") + ), + calendarGrid && /* @__PURE__ */ import_react.default.createElement("div", { style: { width: "100%", display: "flex", flexDirection: "column", alignItems: "center" } }, /* @__PURE__ */ import_react.default.createElement( + "svg", + { + viewBox: "0 0 770 120", + style: { + width: "100%", + maxWidth: "770px", + height: "auto", + display: "block", + overflow: "visible" + } + }, + monthHeaders.map((m, i) => /* @__PURE__ */ import_react.default.createElement( + "text", + { + key: i, + x: 28 + m.col * 13.8, + y: 10, + fontSize: 10, + fill: "var(--dsw-alias-label-tertiary, #888888)", + fontFamily: "inherit" + }, + m.label + )), + /* @__PURE__ */ import_react.default.createElement("text", { x: 2, y: 39, fontSize: 9, fill: "var(--dsw-alias-label-tertiary, #888888)", fontFamily: "inherit" }, "Mon"), + /* @__PURE__ */ import_react.default.createElement("text", { x: 2, y: 67, fontSize: 9, fill: "var(--dsw-alias-label-tertiary, #888888)", fontFamily: "inherit" }, "Wed"), + /* @__PURE__ */ import_react.default.createElement("text", { x: 2, y: 95, fontSize: 9, fill: "var(--dsw-alias-label-tertiary, #888888)", fontFamily: "inherit" }, "Fri"), + calendarGrid.map((week, colIdx) => /* @__PURE__ */ import_react.default.createElement("g", { key: colIdx, transform: `translate(${28 + colIdx * 13.8}, 18)` }, week.map((day, rowIdx) => { + if (!day) return null; + const lvl = day.stats.level || 0; + const bg = DEEPSEEK_LEVELS[lvl]; + const isSelected = selectedDate === day.date; + const isHovered = hoveredDay?.date === day.date; + const strokeColor = isSelected ? "#ffffff" : isHovered ? "#5686fe" : lvl === 0 ? "rgba(255, 255, 255, 0.06)" : "transparent"; + const strokeWidth = isSelected ? 1.6 : isHovered ? 1.3 : 1; + return /* @__PURE__ */ import_react.default.createElement( + "rect", + { + key: rowIdx, + x: 0, + y: rowIdx * 14, + width: 11, + height: 11, + rx: 2, + ry: 2, + fill: bg, + stroke: strokeColor, + strokeWidth, + style: { + cursor: "pointer", + transition: "fill 0.12s ease, stroke 0.12s ease", + filter: isSelected ? "drop-shadow(0 0 6px rgba(86, 134, 254, 0.9))" : isHovered ? "drop-shadow(0 0 4px rgba(86, 134, 254, 0.7))" : lvl === 4 ? "drop-shadow(0 0 3px rgba(79, 133, 255, 0.5))" : "none" + }, + onClick: () => { + setSelectedDate(isSelected ? null : day.date); + }, + 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); + } + } + ); + }))) + ), /* @__PURE__ */ import_react.default.createElement( + "div", + { + style: { + width: "100%", + maxWidth: "770px", + display: "flex", + alignItems: "center", + justifyContent: "space-between", + marginTop: 12, + fontSize: 11, + color: "var(--dsw-alias-label-tertiary, #a0a0a0)" + } + }, + /* @__PURE__ */ import_react.default.createElement("span", { style: { fontSize: 11 } }, "Tip: Click any day to inspect sessions and token breakdown"), + /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 6 } }, /* @__PURE__ */ import_react.default.createElement("span", null, "Less"), DEEPSEEK_LEVELS.map((c, i) => /* @__PURE__ */ import_react.default.createElement( + "div", + { + key: i, + style: { + width: 11, + height: 11, + borderRadius: 2, + backgroundColor: c, + border: i === 0 ? "1px solid rgba(255, 255, 255, 0.06)" : "none", + filter: i === 4 ? "drop-shadow(0 0 3px rgba(79, 133, 255, 0.5))" : "none" + } + } + )), /* @__PURE__ */ import_react.default.createElement("span", null, "More")) + )) + ), /* @__PURE__ */ import_react.default.createElement( "div", { @@ -383,24 +550,10 @@ function HeatmapInnerView() { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 12, - marginBottom: 20 + marginTop: 18, + marginBottom: 18 } }, - /* @__PURE__ */ import_react.default.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", - boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)" - } - }, - /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 } }, "Total Tokens"), - /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "#4f85ff" } }, formatNumber(summary.totalTokens)), - /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" } }, formatExact(summary.totalTokens), " tokens") - ), /* @__PURE__ */ import_react.default.createElement( "div", { @@ -413,7 +566,7 @@ function HeatmapInnerView() { } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 } }, "Cache Hit Ratio"), - /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "#38bdf8" } }, cacheHitPercent, "%"), + /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "var(--dsw-alias-label-primary, #ffffff)" } }, cacheHitPercent, "%"), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" } }, formatNumber(summary.totalCacheReadTokens), " cached reads") ), /* @__PURE__ */ import_react.default.createElement( @@ -445,18 +598,33 @@ function HeatmapInnerView() { /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 } }, selectedSessionId === "all" ? "Active Streak" : "Active Days"), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 24, fontWeight: 700, margin: "6px 0 2px", color: "var(--dsw-alias-label-primary, #ffffff)" } }, selectedSessionId === "all" ? `${summary.currentStreak} days` : `${summary.activeDays} days`), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" } }, selectedSessionId === "all" ? `Longest streak: ${summary.longestStreak} days` : `Session: ${activeView.sessionTitle.slice(0, 24)}`) + ), + /* @__PURE__ */ import_react.default.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", + boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)" + } + }, + /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 } }, "Peak Volume Day"), + /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 20, fontWeight: 700, margin: "6px 0 2px", color: "var(--dsw-alias-label-primary, #ffffff)" } }, summary.peakDay ? formatNumber(summary.peakDay.tokens) : "None"), + /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-secondary, #b9b9b9)" } }, summary.peakDay ? summary.peakDay.date : "No activity") ) ), - /* @__PURE__ */ import_react.default.createElement( + activeView.models && activeView.models.length > 0 && /* @__PURE__ */ import_react.default.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", - boxShadow: "0 4px 16px rgba(0, 0, 0, 0.2)" + padding: "18px 20px", + marginBottom: 18, + boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)" } }, /* @__PURE__ */ import_react.default.createElement( @@ -466,133 +634,57 @@ function HeatmapInnerView() { display: "flex", alignItems: "center", justifyContent: "space-between", - marginBottom: 14, - fontSize: 13, - fontWeight: 600 + marginBottom: 12 } }, - /* @__PURE__ */ import_react.default.createElement("span", null, "Contribution Calendar"), - summary.peakDay && /* @__PURE__ */ import_react.default.createElement("span", { style: { fontSize: 12, fontWeight: 400, color: "var(--dsw-alias-label-secondary)" } }, "Peak day: ", /* @__PURE__ */ import_react.default.createElement("strong", { style: { color: "var(--dsw-alias-label-primary)" } }, summary.peakDay.date), " (", formatNumber(summary.peakDay.tokens), " tokens)") + /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, fontWeight: 650, color: "var(--dsw-alias-label-primary, #ffffff)" } }, "Model Usage Breakdown"), + /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)" } }, activeView.models.length, " model", activeView.models.length === 1 ? "" : "s", " tracked") ), - calendarGrid && /* @__PURE__ */ import_react.default.createElement( + /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 10 } }, activeView.models.map((m) => /* @__PURE__ */ import_react.default.createElement("div", { key: m.model }, /* @__PURE__ */ import_react.default.createElement( "div", { - ref: scrollContainerRef, style: { - width: "100%", - overflowX: "auto", - WebkitOverflowScrolling: "touch", - paddingBottom: "10px" + display: "flex", + justifyContent: "space-between", + fontSize: 12, + marginBottom: 5 + } + }, + /* @__PURE__ */ import_react.default.createElement("span", { style: { fontWeight: 600, color: "var(--dsw-alias-label-primary)" } }, m.model), + /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "var(--dsw-alias-label-secondary)" } }, /* @__PURE__ */ import_react.default.createElement("strong", { style: { color: "#4f85ff" } }, formatNumber(m.totalTokens)), " (", m.percentage, "%) \xB7 ", m.turns, " requests") + ), /* @__PURE__ */ import_react.default.createElement( + "div", + { + style: { + width: "100%", + height: 6, + background: "rgba(255, 255, 255, 0.06)", + borderRadius: 3, + overflow: "hidden" } }, - /* @__PURE__ */ import_react.default.createElement("div", { style: { minWidth: "920px", width: "100%", display: "inline-block" } }, /* @__PURE__ */ import_react.default.createElement( - "svg", - { - viewBox: "0 0 920 152", - style: { - width: "100%", - minWidth: "920px", - height: "152px", - display: "block", - overflow: "visible" - } - }, - monthHeaders.map((m, i) => /* @__PURE__ */ import_react.default.createElement( - "text", - { - key: i, - x: 36 + m.col * 16.5, - y: 12, - fontSize: 11, - fill: "var(--dsw-alias-label-tertiary, #888888)", - fontFamily: "inherit" - }, - m.label - )), - /* @__PURE__ */ import_react.default.createElement("text", { x: 4, y: 51, fontSize: 10, fill: "var(--dsw-alias-label-tertiary, #888888)", fontFamily: "inherit" }, "Mon"), - /* @__PURE__ */ import_react.default.createElement("text", { x: 4, y: 85, fontSize: 10, fill: "var(--dsw-alias-label-tertiary, #888888)", fontFamily: "inherit" }, "Wed"), - /* @__PURE__ */ import_react.default.createElement("text", { x: 4, y: 119, fontSize: 10, fill: "var(--dsw-alias-label-tertiary, #888888)", fontFamily: "inherit" }, "Fri"), - calendarGrid.map((week, colIdx) => /* @__PURE__ */ import_react.default.createElement("g", { key: colIdx, transform: `translate(${36 + colIdx * 16.5}, 22)` }, week.map((day, rowIdx) => { - if (!day) return null; - const lvl = day.stats.level || 0; - const bg = DEEPSEEK_LEVELS[lvl]; - const isSelected = selectedDate === day.date; - const isHovered = hoveredDay?.date === day.date; - const strokeColor = isSelected ? "#ffffff" : isHovered ? "#5686fe" : lvl === 0 ? "rgba(255, 255, 255, 0.06)" : "transparent"; - const strokeWidth = isSelected ? 2 : isHovered ? 1.5 : 1; - return /* @__PURE__ */ import_react.default.createElement( - "rect", - { - key: rowIdx, - x: 0, - y: rowIdx * 17, - width: 13, - height: 13, - rx: 2.5, - ry: 2.5, - fill: bg, - stroke: strokeColor, - strokeWidth, - style: { - cursor: "pointer", - transition: "fill 0.12s ease, stroke 0.12s ease", - filter: isSelected ? "drop-shadow(0 0 6px rgba(86, 134, 254, 0.9))" : isHovered ? "drop-shadow(0 0 4px rgba(86, 134, 254, 0.7))" : lvl === 4 ? "drop-shadow(0 0 4px rgba(79, 133, 255, 0.6))" : "none" - }, - onClick: () => { - setSelectedDate(isSelected ? null : day.date); - }, - 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); - } - } - ); - }))) - )), /* @__PURE__ */ import_react.default.createElement( "div", { style: { - display: "flex", - alignItems: "center", - justifyContent: "space-between", - marginTop: 14, - fontSize: 11, - color: "var(--dsw-alias-label-tertiary, #a0a0a0)" + width: `${Math.max(1, m.percentage)}%`, + height: "100%", + background: "#336dfa", + borderRadius: 3, + transition: "width 0.3s ease" } - }, - /* @__PURE__ */ import_react.default.createElement("span", { style: { fontSize: 11 } }, "Tip: Click any day to inspect sessions and token breakdown"), - /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 6 } }, /* @__PURE__ */ import_react.default.createElement("span", null, "Less"), DEEPSEEK_LEVELS.map((c, i) => /* @__PURE__ */ import_react.default.createElement( - "div", - { - key: i, - style: { - width: 12.5, - height: 12.5, - borderRadius: 2.5, - backgroundColor: c, - border: i === 0 ? "1px solid rgba(255, 255, 255, 0.06)" : "none", - filter: i === 4 ? "drop-shadow(0 0 3px rgba(79, 133, 255, 0.5))" : "none" - } - } - )), /* @__PURE__ */ import_react.default.createElement("span", null, "More")) + } ) - ) + )))) ), /* @__PURE__ */ import_react.default.createElement( "div", { style: { - marginTop: 18, background: "var(--dsw-alias-bg-layer-2, #2c2c2c)", border: "1px solid var(--dsw-alias-border-l2, #494949)", borderRadius: 12, - padding: "20px 22px", + padding: "18px 20px", position: "relative", boxShadow: "0 4px 16px rgba(0, 0, 0, 0.2)" } @@ -609,8 +701,8 @@ function HeatmapInnerView() { marginBottom: 16 } }, - /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 14, fontWeight: 650, color: "var(--dsw-alias-label-primary, #ffffff)" } }, "Token Usage Trends"), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 12, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", marginTop: 2 } }, "Daily billed vs cached token volume over time")), - /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 12, fontSize: 11, color: "var(--dsw-alias-label-secondary)" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 5 } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { width: 9, height: 9, borderRadius: 2, background: "#4f85ff" } }), /* @__PURE__ */ import_react.default.createElement("span", null, "Billed Tokens")), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 5 } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { width: 9, height: 9, borderRadius: 2, background: "#253b82" } }), /* @__PURE__ */ import_react.default.createElement("span", null, "Cache Reads"))), /* @__PURE__ */ import_react.default.createElement( + /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, fontWeight: 650, color: "var(--dsw-alias-label-primary, #ffffff)" } }, "Token Usage Trends"), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #a0a0a0)", marginTop: 2 } }, "Daily billed vs cached token volume over time")), + /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 12, fontSize: 11, color: "var(--dsw-alias-label-secondary)" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 5 } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { width: 8, height: 8, borderRadius: 2, background: "#4f85ff" } }), /* @__PURE__ */ import_react.default.createElement("span", null, "Billed Tokens")), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 5 } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { width: 8, height: 8, borderRadius: 2, background: "#253b82" } }), /* @__PURE__ */ import_react.default.createElement("span", null, "Cache Reads"))), /* @__PURE__ */ import_react.default.createElement( "div", { style: { @@ -642,7 +734,7 @@ function HeatmapInnerView() { )) )) ), - /* @__PURE__ */ import_react.default.createElement("div", { style: { width: "100%", overflowX: "auto", WebkitOverflowScrolling: "touch" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { minWidth: "720px", width: "100%" } }, /* @__PURE__ */ import_react.default.createElement( + /* @__PURE__ */ import_react.default.createElement("div", { style: { width: "100%", overflowX: "auto", WebkitOverflowScrolling: "touch" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { minWidth: "680px", width: "100%" } }, /* @__PURE__ */ import_react.default.createElement( "svg", { viewBox: "0 0 880 180", @@ -822,9 +914,9 @@ function HeatmapInnerView() { }, /* @__PURE__ */ import_react.default.createElement("div", { style: { padding: "6px 12px", background: "var(--dsw-alias-bg-layer-1, #1e1e1e)", borderRadius: 6, border: "1px solid var(--dsw-alias-border-l1)" } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "var(--dsw-alias-label-tertiary)" } }, "Billed Input: "), /* @__PURE__ */ import_react.default.createElement("strong", null, formatNumber(inspectedDayStats.inputTokens))), /* @__PURE__ */ import_react.default.createElement("div", { style: { padding: "6px 12px", background: "var(--dsw-alias-bg-layer-1, #1e1e1e)", borderRadius: 6, border: "1px solid var(--dsw-alias-border-l1)" } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "var(--dsw-alias-label-tertiary)" } }, "Output Generated: "), /* @__PURE__ */ import_react.default.createElement("strong", null, formatNumber(inspectedDayStats.outputTokens))), - /* @__PURE__ */ import_react.default.createElement("div", { style: { padding: "6px 12px", background: "var(--dsw-alias-bg-layer-1, #1e1e1e)", borderRadius: 6, border: "1px solid var(--dsw-alias-border-l1)" } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "var(--dsw-alias-label-tertiary)" } }, "Cache Reads: "), /* @__PURE__ */ import_react.default.createElement("strong", { style: { color: "#38bdf8" } }, formatNumber(inspectedDayStats.cacheReadTokens)), " (", Math.round(inspectedDayStats.cacheReadTokens / inspectedDayStats.totalTokens * 100), "%)") + /* @__PURE__ */ import_react.default.createElement("div", { style: { padding: "6px 12px", background: "var(--dsw-alias-bg-layer-1, #1e1e1e)", borderRadius: 6, border: "1px solid var(--dsw-alias-border-l1)" } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { color: "var(--dsw-alias-label-tertiary)" } }, "Cache Reads: "), /* @__PURE__ */ import_react.default.createElement("strong", null, formatNumber(inspectedDayStats.cacheReadTokens)), " (", Math.round(inspectedDayStats.cacheReadTokens / inspectedDayStats.totalTokens * 100), "%)") ), - /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, fontWeight: 600, color: "var(--dsw-alias-label-primary)", marginBottom: 10 } }, "Sessions Active on this Day (", inspectedDayStats?.sessions?.length || 0, ")"), inspectedDayStats?.sessions && inspectedDayStats.sessions.length > 0 ? /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 8, maxHeight: 320, overflowY: "auto" } }, inspectedDayStats.sessions.map((sess, idx) => /* @__PURE__ */ import_react.default.createElement( + /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, fontWeight: 600, color: "var(--dsw-alias-label-primary)", marginBottom: 10 } }, "Sessions Active on this Day (", (inspectedDayStats?.sessions || []).length, ")"), inspectedDayStats?.sessions && inspectedDayStats.sessions.length > 0 ? /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 8, maxHeight: 320, overflowY: "auto" } }, inspectedDayStats.sessions.map((sess, idx) => /* @__PURE__ */ import_react.default.createElement( "div", { key: idx, diff --git a/lib/client.js.map b/lib/client.js.map index dd4fb6f..180c09f 100644 --- a/lib/client.js.map +++ b/lib/client.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../src/client/index.tsx", "../src/client/HeatmapView.tsx", "../src/client/HeatmapModal.tsx"], - "sourcesContent": ["import React, { useState, useEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { HeatmapView } from \"./HeatmapView.js\";\nimport { HeatmapModal } from \"./HeatmapModal.js\";\n\nexport const name = \"dsh-plugin-token-heatmap-client\";\nexport const inject = [\"slots\"];\n\nexport function apply(ctx: any) {\n // 1. Register under Settings -> \"Token Heatmap\"\n if (ctx.slots && typeof ctx.slots.inject === \"function\") {\n ctx.slots.inject(\"settings.section\", () => {\n return ctx.slots.register(\n {\n name: \"settings.section\",\n id: \"token-heatmap\",\n order: 14,\n label: () => \"Token Heatmap\",\n icon: \"chart\",\n },\n HeatmapView\n );\n });\n }\n\n // 2. Global Modal Host & In-Session Token Meter Integration\n if (typeof document !== \"undefined\") {\n const modalContainer = document.createElement(\"div\");\n modalContainer.id = \"dsh-token-heatmap-modal-root\";\n document.body.appendChild(modalContainer);\n\n const root = createRoot(modalContainer);\n\n function ModalController() {\n const [isOpen, setIsOpen] = useState(false);\n\n useEffect(() => {\n const handleOpen = () => setIsOpen(true);\n const handleClose = () => setIsOpen(false);\n\n window.addEventListener(\"dsh:open-token-heatmap\", handleOpen);\n window.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\") handleClose();\n });\n\n return () => {\n window.removeEventListener(\"dsh:open-token-heatmap\", handleOpen);\n };\n }, []);\n\n return setIsOpen(false)} />;\n }\n\n root.render();\n\n // 3. Observe DOM for in-session token usage dialog: strictly single button\n const observer = new MutationObserver(() => {\n // Find the dl with data-session-stats-usage\n const usageList = document.querySelector('[data-session-stats-usage]');\n if (!usageList) return;\n\n const dialogContainer = usageList.closest('div[role=\"dialog\"]') || usageList.parentElement;\n if (!dialogContainer) return;\n\n // Strictly ensure only one button is ever attached\n if (dialogContainer.querySelector(\".dsh-heatmap-trigger-btn\") || document.querySelector(\".dsh-heatmap-trigger-btn\")) {\n return;\n }\n\n const button = document.createElement(\"button\");\n button.className = \"dsh-heatmap-trigger-btn\";\n button.type = \"button\";\n button.innerHTML = \"View Token Usage Heatmap \u2192\";\n button.style.cssText = `\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-top: 12px;\n padding: 8px 12px;\n background: rgba(51, 109, 250, 0.12);\n border: 1px solid rgba(51, 109, 250, 0.35);\n border-radius: 8px;\n color: #4f85ff;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);\n transition: all 0.15s ease;\n `;\n button.onmouseenter = () => {\n button.style.background = \"rgba(51, 109, 250, 0.22)\";\n button.style.borderColor = \"#336dfa\";\n button.style.boxShadow = \"0 2px 8px rgba(51, 109, 250, 0.25)\";\n };\n button.onmouseleave = () => {\n button.style.background = \"rgba(51, 109, 250, 0.12)\";\n button.style.borderColor = \"rgba(51, 109, 250, 0.35)\";\n button.style.boxShadow = \"0 1px 4px rgba(0, 0, 0, 0.15)\";\n };\n button.onclick = (e) => {\n e.preventDefault();\n e.stopPropagation();\n\n // Try navigating to Settings -> Token Heatmap\n const settingsTrigger = document.querySelector(\n 'button[aria-label*=\"Setting\" i], [data-sidebar-settings] button, button.settings'\n );\n\n if (settingsTrigger) {\n (settingsTrigger as HTMLElement).click();\n setTimeout(() => {\n const heatmapTab = Array.from(document.querySelectorAll(\"*\")).find(\n (el) =>\n el.textContent?.trim() === \"Token Heatmap\" &&\n (el.tagName === \"BUTTON\" || el.tagName === \"DIV\" || el.getAttribute(\"role\") === \"tab\" || el.classList.contains(\"ant-list-item\"))\n );\n if (heatmapTab) {\n (heatmapTab as HTMLElement).click();\n } else {\n window.dispatchEvent(new CustomEvent(\"dsh:open-token-heatmap\"));\n }\n }, 120);\n } else {\n // Open direct interactive modal\n window.dispatchEvent(new CustomEvent(\"dsh:open-token-heatmap\"));\n }\n };\n\n dialogContainer.appendChild(button);\n });\n\n observer.observe(document.body, { childList: true, subtree: true });\n }\n}\n", "import React, { useEffect, useState, useMemo, useRef } from \"react\";\n\nexport interface DaySessionDetail {\n sessionId: string;\n sessionTitle: string;\n createdAt: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n turns: number;\n}\n\nexport interface DayStats {\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n turns: number;\n level?: number;\n sessions?: DaySessionDetail[];\n}\n\nexport interface SessionSummary {\n id: string;\n title: string;\n createdAt: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n turns: number;\n days: Record;\n}\n\nexport interface SummaryStats {\n totalTokens: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheReadTokens: number;\n totalTurns: number;\n activeDays: number;\n currentStreak: number;\n longestStreak: number;\n peakDay: { date: string; tokens: number; turns: number } | null;\n dailyAverage: number;\n}\n\nexport interface HeatmapResponse {\n summary: SummaryStats;\n days: Record;\n sessions: SessionSummary[];\n startDate: string;\n endDate: string;\n}\n\nfunction formatNumber(num: number): string {\n if (num >= 1_000_000_000) return (num / 1_000_000_000).toFixed(2) + \"B\";\n if (num >= 1_000_000) return (num / 1_000_000).toFixed(1) + \"M\";\n if (num >= 1_000) return (num / 1_000).toFixed(1) + \"k\";\n return num.toLocaleString();\n}\n\nfunction formatExact(num: number): string {\n return num.toLocaleString();\n}\n\nconst MONTH_NAMES = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\nconst DAY_LABELS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n\n// Simple monochromatic gradient: Black -> Vibrant DeepSeek Blue\nconst DEEPSEEK_LEVELS = [\n \"#0f1117\", // Level 0: Black / dark void tile\n \"#152c6f\", // Level 1: Dark deep blue\n \"#1f4cdb\", // Level 2: Medium vibrant blue\n \"#336dfa\", // Level 3: Vibrant DeepSeek brand blue\n \"#4f85ff\", // Level 4: Bright electric DeepSeek blue\n];\n\nfunction HeatmapInnerView() {\n const [data, setData] = useState(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState(null);\n const [selectedSessionId, setSelectedSessionId] = useState(\"all\");\n const [selectedDate, setSelectedDate] = useState(null);\n const [chartRange, setChartRange] = useState<14 | 30 | 90>(30);\n const [hoveredDay, setHoveredDay] = useState<{ date: string; stats: DayStats } | null>(null);\n const [hoveredBar, setHoveredBar] = useState<{\n date: string;\n label: string;\n totalTokens: number;\n billedTokens: number;\n cachedTokens: number;\n turns: number;\n } | null>(null);\n const [tooltipPos, setTooltipPos] = useState<{ x: number; y: number } | null>(null);\n const scrollContainerRef = useRef(null);\n\n const fetchStats = () => {\n setLoading(true);\n fetch(\"/api/dsh-token-heatmap/stats\")\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP error ${r.status}`);\n return r.json();\n })\n .then((res) => {\n setData(res);\n setLoading(false);\n })\n .catch((err) => {\n setError(err.message);\n setLoading(false);\n });\n };\n\n useEffect(() => {\n fetchStats();\n }, []);\n\n // Compute active days & stats based on session filter\n const activeView = useMemo(() => {\n if (!data) return null;\n\n if (selectedSessionId === \"all\") {\n return {\n summary: data.summary,\n days: data.days,\n sessionTitle: \"All Sessions (Global)\",\n };\n }\n\n const session = (data.sessions || []).find((s) => s.id === selectedSessionId);\n if (!session) {\n return {\n summary: data.summary,\n days: data.days,\n sessionTitle: \"All Sessions (Global)\",\n };\n }\n\n let totalTokens = session.totalTokens;\n let totalInputTokens = session.inputTokens;\n let totalOutputTokens = session.outputTokens;\n let totalCacheReadTokens = session.cacheReadTokens;\n let totalTurns = session.turns;\n let activeDays = Object.values(session.days).filter((d) => d.totalTokens > 0).length;\n let dailyAverage = activeDays > 0 ? Math.round(totalTokens / activeDays) : 0;\n let peakDay: { date: string; tokens: number; turns: number } | null = null;\n\n for (const [date, stats] of Object.entries(session.days)) {\n if (!peakDay || stats.totalTokens > peakDay.tokens) {\n peakDay = { date, tokens: stats.totalTokens, turns: stats.turns };\n }\n }\n\n const positiveValues = Object.values(session.days)\n .map((d) => d.totalTokens)\n .filter((t) => t > 0)\n .sort((a, b) => a - b);\n\n let q1 = 1_000_000;\n let q2 = 5_000_000;\n let q3 = 15_000_000;\n\n if (positiveValues.length >= 4) {\n q1 = positiveValues[Math.floor(positiveValues.length * 0.25)];\n q2 = positiveValues[Math.floor(positiveValues.length * 0.50)];\n q3 = positiveValues[Math.floor(positiveValues.length * 0.75)];\n }\n\n const leveledDays: Record = {};\n for (const [date, stats] of Object.entries(session.days)) {\n let lvl = 0;\n if (stats.totalTokens === 0) lvl = 0;\n else if (stats.totalTokens <= q1) lvl = 1;\n else if (stats.totalTokens <= q2) lvl = 2;\n else if (stats.totalTokens <= q3) lvl = 3;\n else lvl = 4;\n leveledDays[date] = { ...stats, level: lvl };\n }\n\n return {\n summary: {\n totalTokens,\n totalInputTokens,\n totalOutputTokens,\n totalCacheReadTokens,\n totalTurns,\n activeDays,\n currentStreak: 0,\n longestStreak: 0,\n peakDay,\n dailyAverage,\n },\n days: leveledDays,\n sessionTitle: session.title,\n };\n }, [data, selectedSessionId]);\n\n // Compute 52-week calendar grid: 7 rows x 53 columns\n const calendarGrid = useMemo(() => {\n if (!activeView) return null;\n\n const weeks: Array> = [];\n const now = new Date();\n const end = new Date(now);\n const start = new Date(now);\n start.setDate(start.getDate() - 364);\n start.setDate(start.getDate() - start.getDay());\n\n let current = new Date(start);\n let currentWeek: Array<{ date: string; stats: DayStats } | null> = [];\n\n while (current <= end || currentWeek.length > 0) {\n const dateKey = `${current.getFullYear()}-${String(current.getMonth() + 1).padStart(2, \"0\")}-${String(current.getDate()).padStart(2, \"0\")}`;\n const dayStats = activeView.days[dateKey] || {\n totalTokens: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n turns: 0,\n level: 0,\n };\n\n if (current <= end) {\n currentWeek.push({ date: dateKey, stats: dayStats });\n } else {\n currentWeek.push(null);\n }\n\n if (currentWeek.length === 7) {\n weeks.push(currentWeek);\n currentWeek = [];\n }\n current.setDate(current.getDate() + 1);\n if (current > end && currentWeek.length === 0) break;\n }\n\n return weeks;\n }, [activeView]);\n\n // Compute month headers\n const monthHeaders = useMemo(() => {\n if (!calendarGrid) return [];\n const headers: Array<{ label: string; col: number }> = [];\n let lastMonth = -1;\n\n calendarGrid.forEach((week, colIdx) => {\n const firstValidDay = week.find((d) => d !== null);\n if (firstValidDay) {\n const d = new Date(firstValidDay.date + \"T00:00:00\");\n const month = d.getMonth();\n if (month !== lastMonth) {\n headers.push({ label: MONTH_NAMES[month], col: colIdx });\n lastMonth = month;\n }\n }\n });\n\n return headers;\n }, [calendarGrid]);\n\n useEffect(() => {\n if (scrollContainerRef.current) {\n scrollContainerRef.current.scrollLeft = scrollContainerRef.current.scrollWidth;\n }\n }, [calendarGrid]);\n\n // Daily token timeline chart data for the selected range (14, 30, or 90 days)\n const chartData = useMemo(() => {\n if (!activeView) return [];\n const now = new Date();\n const items: Array<{\n date: string;\n label: string;\n totalTokens: number;\n billedTokens: number;\n cachedTokens: number;\n turns: number;\n }> = [];\n\n for (let i = chartRange - 1; i >= 0; i--) {\n const d = new Date(now);\n d.setDate(d.getDate() - i);\n const dateKey = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n const stats = activeView.days[dateKey] || {\n totalTokens: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n turns: 0,\n };\n items.push({\n date: dateKey,\n label: `${MONTH_NAMES[d.getMonth()]} ${d.getDate()}`,\n totalTokens: stats.totalTokens,\n billedTokens: stats.inputTokens + stats.outputTokens,\n cachedTokens: stats.cacheReadTokens,\n turns: stats.turns,\n });\n }\n return items;\n }, [activeView, chartRange]);\n\n const maxChartTokens = useMemo(() => {\n return Math.max(...chartData.map((d) => d.totalTokens), 1_000_000);\n }, [chartData]);\n\n if (loading && !data) {\n return (\n
\n Loading token usage statistics...\n
\n );\n }\n\n if (error) {\n return (\n
\n Failed to load heatmap: {error}\n \n Retry\n \n
\n );\n }\n\n if (!data || !activeView) return null;\n\n const { summary } = activeView;\n const cacheHitPercent =\n summary.totalTokens > 0\n ? Math.round((summary.totalCacheReadTokens / summary.totalTokens) * 100)\n : 0;\n\n const inspectedDayStats = selectedDate ? activeView.days[selectedDate] : null;\n\n return (\n \n {/* Top Header & Filter Controls (OpenCode / Codex style) */}\n \n
\n \n Token Usage Activity\n \n 52 Weeks\n \n \n \n Daily token volume and request step intensity across sessions\n

\n
\n\n {/* Right side controls: Session selector & Refresh */}\n
\n {/* Session Filter Dropdown */}\n
\n Session:\n {\n setSelectedSessionId(e.target.value);\n setSelectedDate(null);\n }}\n style={{\n background: \"var(--dsw-alias-bg-layer-2, #2c2c2c)\",\n border: \"1px solid var(--dsw-alias-border-l2, #494949)\",\n color: \"var(--dsw-alias-label-primary, #eeeeee)\",\n borderRadius: 8,\n padding: \"6px 12px\",\n fontSize: 12,\n outline: \"none\",\n cursor: \"pointer\",\n maxWidth: 240,\n }}\n >\n \n {(data.sessions || []).map((s) => (\n \n ))}\n \n
\n\n \n \u21BB Refresh\n \n
\n \n\n {/* Summary Cards Row (Codex / OpenCode style) */}\n \n {/* Card 1: Total Tokens */}\n \n
\n Total Tokens\n
\n
\n {formatNumber(summary.totalTokens)}\n
\n
\n {formatExact(summary.totalTokens)} tokens\n
\n \n\n {/* Card 2: Cache Hit */}\n \n
\n Cache Hit Ratio\n
\n
\n {cacheHitPercent}%\n
\n
\n {formatNumber(summary.totalCacheReadTokens)} cached reads\n
\n \n\n {/* Card 3: Daily Average / Turns */}\n \n
\n {selectedSessionId === \"all\" ? \"Daily Average\" : \"Total Steps\"}\n
\n
\n {selectedSessionId === \"all\" ? formatNumber(summary.dailyAverage) : summary.totalTurns}\n
\n
\n {selectedSessionId === \"all\" ? `Across ${summary.activeDays} active days` : `${formatNumber(summary.totalInputTokens + summary.totalOutputTokens)} billed`}\n
\n \n\n {/* Card 4: Streak / Activity */}\n \n
\n {selectedSessionId === \"all\" ? \"Active Streak\" : \"Active Days\"}\n
\n
\n {selectedSessionId === \"all\" ? `${summary.currentStreak} days` : `${summary.activeDays} days`}\n
\n
\n {selectedSessionId === \"all\" ? `Longest streak: ${summary.longestStreak} days` : `Session: ${activeView.sessionTitle.slice(0, 24)}`}\n
\n \n \n\n {/* Heatmap Card (Auto-resizes responsive SVG panel, GitHub style) */}\n \n \n Contribution Calendar\n {summary.peakDay && (\n \n Peak day: {summary.peakDay.date} ({formatNumber(summary.peakDay.tokens)} tokens)\n \n )}\n \n\n {/* Generously sized GitHub-Style Calendar with smooth horizontal scroll */}\n {calendarGrid && (\n \n
\n \n {/* Month labels */}\n {monthHeaders.map((m, i) => (\n \n {m.label}\n \n ))}\n\n {/* Day of week labels */}\n \n Mon\n \n \n Wed\n \n \n Fri\n \n\n {/* Day Tiles: Generously sized 13px tiles with 16.5px step */}\n {calendarGrid.map((week, colIdx) => (\n \n {week.map((day, rowIdx) => {\n if (!day) return null;\n\n const lvl = day.stats.level || 0;\n const bg = DEEPSEEK_LEVELS[lvl];\n const isSelected = selectedDate === day.date;\n const isHovered = hoveredDay?.date === day.date;\n\n // GitHub-style glow & outline\n const strokeColor = isSelected\n ? \"#ffffff\"\n : isHovered\n ? \"#5686fe\"\n : lvl === 0\n ? \"rgba(255, 255, 255, 0.06)\"\n : \"transparent\";\n\n const strokeWidth = isSelected ? 2 : isHovered ? 1.5 : 1;\n\n return (\n {\n setSelectedDate(isSelected ? null : day.date);\n }}\n onMouseEnter={(e) => {\n setHoveredDay({ date: day.date, stats: day.stats });\n const rect = e.currentTarget.getBoundingClientRect();\n setTooltipPos({ x: rect.left + rect.width / 2, y: rect.top - 8 });\n }}\n onMouseLeave={() => {\n setHoveredDay(null);\n setTooltipPos(null);\n }}\n />\n );\n })}\n \n ))}\n \n
\n\n {/* Bottom Legend */}\n \n \n Tip: Click any day to inspect sessions and token breakdown\n \n\n
\n Less\n {DEEPSEEK_LEVELS.map((c, i) => (\n \n ))}\n More\n
\n \n \n )}\n \n\n {/* Token Usage Trends Graph Card */}\n \n \n
\n
\n Token Usage Trends\n
\n
\n Daily billed vs cached token volume over time\n
\n
\n\n
\n {/* Legend: Billed vs Cached */}\n
\n
\n \n Billed Tokens\n
\n
\n \n Cache Reads\n
\n
\n\n {/* Range Toggle: 14D | 30D | 90D */}\n \n {[14, 30, 90].map((days) => (\n setChartRange(days as any)}\n style={{\n border: 0,\n background: chartRange === days ? \"var(--dsw-alias-bg-layer-2, #2c2c2c)\" : \"transparent\",\n color: chartRange === days ? \"#4f85ff\" : \"var(--dsw-alias-label-tertiary)\",\n fontSize: 11,\n padding: \"3px 8px\",\n borderRadius: 6,\n cursor: \"pointer\",\n fontWeight: chartRange === days ? 600 : 400,\n }}\n >\n {days}D\n \n ))}\n
\n \n \n\n {/* The SVG Timeline Bar Chart */}\n
\n
\n \n {/* Horizontal grid lines & Y labels */}\n {[1, 0.66, 0.33, 0].map((ratio, i) => {\n const y = 20 + (1 - ratio) * 125;\n const tokenVal = Math.round(maxChartTokens * ratio);\n return (\n \n \n \n {formatNumber(tokenVal)}\n \n \n );\n })}\n\n {/* Bars for each day in chartData */}\n {chartData.map((d, idx) => {\n const totalBarWidth = 825 / chartData.length;\n const barWidth = Math.max(5, Math.min(22, totalBarWidth - 5));\n const x = 50 + idx * totalBarWidth + (totalBarWidth - barWidth) / 2;\n\n const totalH = (d.totalTokens / maxChartTokens) * 125;\n const cachedH = d.totalTokens > 0 ? (d.cachedTokens / d.totalTokens) * totalH : 0;\n const billedH = totalH - cachedH;\n const yBase = 145;\n\n const isHovered = hoveredBar?.date === d.date;\n\n return (\n {\n setHoveredBar(d);\n const rect = e.currentTarget.getBoundingClientRect();\n setTooltipPos({ x: rect.left + rect.width / 2, y: rect.top - 8 });\n }}\n onMouseLeave={() => {\n setHoveredBar(null);\n setTooltipPos(null);\n }}\n onClick={() => setSelectedDate(selectedDate === d.date ? null : d.date)}\n >\n {/* Hover hit area */}\n \n\n {/* Empty/Zero line marker if 0 tokens */}\n {d.totalTokens === 0 ? (\n \n ) : (\n \n {/* Cached segment (bottom) */}\n {cachedH > 0 && (\n \n )}\n {/* Billed segment (top) */}\n {billedH > 0 && (\n \n )}\n \n )}\n\n {/* X-axis date labels: show every Nth label depending on range */}\n {(chartRange === 14 || idx % (chartRange === 30 ? 3 : 7) === 0) && (\n \n {d.label}\n \n )}\n \n );\n })}\n \n
\n
\n \n\n {/* Day Inspection Detail Drawer (Opens when any day is clicked) */}\n {selectedDate && (\n \n {/* Header of the Day Detail */}\n \n
\n
\n {new Date(selectedDate + \"T00:00:00\").toLocaleDateString(undefined, {\n weekday: \"long\",\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n })}\n
\n
\n {inspectedDayStats && inspectedDayStats.totalTokens > 0 ? (\n \n {formatExact(inspectedDayStats.totalTokens)} total tokens across{\" \"}\n {inspectedDayStats.turns} turns\n \n ) : (\n \"No activity recorded\"\n )}\n
\n
\n\n setSelectedDate(null)}\n style={{\n background: \"transparent\",\n border: \"none\",\n color: \"var(--dsw-alias-label-tertiary)\",\n cursor: \"pointer\",\n fontSize: 16,\n padding: \"4px 8px\",\n borderRadius: 4,\n }}\n title=\"Close day view\"\n >\n \u2715\n \n \n\n {/* Breakdown Pills for the day */}\n {inspectedDayStats && inspectedDayStats.totalTokens > 0 && (\n \n
\n Billed Input: \n {formatNumber(inspectedDayStats.inputTokens)}\n
\n
\n Output Generated: \n {formatNumber(inspectedDayStats.outputTokens)}\n
\n
\n Cache Reads: \n {formatNumber(inspectedDayStats.cacheReadTokens)} (\n {Math.round((inspectedDayStats.cacheReadTokens / inspectedDayStats.totalTokens) * 100)}%)\n
\n \n )}\n\n {/* Sessions List for that Day */}\n
\n
\n Sessions Active on this Day ({inspectedDayStats?.sessions?.length || 0})\n
\n\n {inspectedDayStats?.sessions && inspectedDayStats.sessions.length > 0 ? (\n
\n {inspectedDayStats.sessions.map((sess, idx) => (\n \n
\n
\n {sess.sessionTitle}\n
\n
\n Started {new Date(sess.createdAt).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" })} \u00B7 ID: {sess.sessionId.slice(0, 10)}\n
\n
\n\n
\n
\n {formatNumber(sess.totalTokens)} tokens\n
\n
\n {sess.turns} turns \u00B7 {formatNumber(sess.inputTokens + sess.outputTokens)} billed\n
\n
\n
\n ))}\n
\n ) : (\n
\n No session records found for this date.\n
\n )}\n \n \n )}\n\n {/* Floating Hover Tooltip */}\n {hoveredDay && tooltipPos && !selectedDate && (\n \n
\n {new Date(hoveredDay.date + \"T00:00:00\").toLocaleDateString(undefined, {\n weekday: \"short\",\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n })}\n
\n {hoveredDay.stats.totalTokens === 0 ? (\n
No activity recorded
\n ) : (\n
\n
\n {formatExact(hoveredDay.stats.totalTokens)} tokens\n
\n
\n {hoveredDay.stats.turns} requests / steps\n
\n
\n Click to inspect sessions on this day\n
\n
\n )}\n \n )}\n\n {/* Floating Hover Tooltip for Chart Bar */}\n {hoveredBar && tooltipPos && (\n \n
\n {new Date(hoveredBar.date + \"T00:00:00\").toLocaleDateString(undefined, {\n weekday: \"short\",\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n })}\n
\n {hoveredBar.totalTokens === 0 ? (\n
No activity recorded
\n ) : (\n
\n
\n {formatExact(hoveredBar.totalTokens)} total tokens\n
\n
\n Billed: {formatNumber(hoveredBar.billedTokens)} \u00B7 Cached: {formatNumber(hoveredBar.cachedTokens)}\n
\n
\n {hoveredBar.turns} turns \u00B7 Click to inspect day\n
\n
\n )}\n \n )}\n \n );\n}\n\nclass HeatmapErrorBoundary extends React.Component<\n { children: React.ReactNode },\n { hasError: boolean; error: any }\n> {\n constructor(props: any) {\n super(props);\n this.state = { hasError: false, error: null };\n }\n static getDerivedStateFromError(error: any) {\n return { hasError: true, error };\n }\n componentDidCatch(error: any, info: any) {\n console.error(\"Heatmap rendering error:\", error, info);\n }\n render() {\n if (this.state.hasError) {\n return (\n
\n

Token Heatmap Encountered an Error

\n \n {String(this.state.error?.message || this.state.error)}\n \n this.setState({ hasError: false, error: null })}\n style={{\n marginTop: 12,\n padding: \"6px 14px\",\n background: \"var(--dsw-alias-brand-primary, #3964fe)\",\n color: \"#fff\",\n border: \"none\",\n borderRadius: 6,\n cursor: \"pointer\",\n }}\n >\n Retry\n \n
\n );\n }\n return this.props.children;\n }\n}\n\nexport function HeatmapView() {\n return (\n \n \n \n );\n}\n", "import React from \"react\";\nimport { HeatmapView } from \"./HeatmapView.js\";\n\ninterface HeatmapModalProps {\n open: boolean;\n onClose: () => void;\n}\n\nexport function HeatmapModal({ open, onClose }: HeatmapModalProps) {\n if (!open) return null;\n\n return (\n \n e.stopPropagation()}\n style={{\n position: \"relative\",\n width: \"100%\",\n maxWidth: \"1040px\",\n maxHeight: \"92vh\",\n overflowY: \"auto\",\n background: \"var(--dsw-alias-bg-layer-1, #1e1e1e)\",\n border: \"1px solid var(--dsw-alias-border-l2, #494949)\",\n borderRadius: \"14px\",\n boxShadow: \"0 16px 40px rgba(0, 0, 0, 0.5)\",\n padding: \"24px 28px\",\n }}\n >\n {/* Close button */}\n \n \u2715\n \n\n \n \n \n );\n}\n"], - "mappings": "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", + "sourcesContent": ["import React, { useState, useEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { HeatmapView } from \"./HeatmapView.js\";\nimport { HeatmapModal } from \"./HeatmapModal.js\";\n\nexport const name = \"dsh-plugin-token-heatmap-client\";\nexport const inject = [\"slots\"];\n\nexport function apply(ctx: any) {\n // 1. Register under Settings -> \"Token Heatmap\"\n if (ctx.slots && typeof ctx.slots.inject === \"function\") {\n ctx.slots.inject(\"settings.section\", () => {\n return ctx.slots.register(\n {\n name: \"settings.section\",\n id: \"token-heatmap\",\n order: 14,\n label: () => \"Token Heatmap\",\n icon: \"chart\",\n },\n HeatmapView\n );\n });\n }\n\n // 2. Global Modal Host & In-Session Token Meter Integration\n if (typeof document !== \"undefined\") {\n const modalContainer = document.createElement(\"div\");\n modalContainer.id = \"dsh-token-heatmap-modal-root\";\n document.body.appendChild(modalContainer);\n\n const root = createRoot(modalContainer);\n\n function ModalController() {\n const [isOpen, setIsOpen] = useState(false);\n\n useEffect(() => {\n const handleOpen = () => setIsOpen(true);\n const handleClose = () => setIsOpen(false);\n\n window.addEventListener(\"dsh:open-token-heatmap\", handleOpen);\n window.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\") handleClose();\n });\n\n return () => {\n window.removeEventListener(\"dsh:open-token-heatmap\", handleOpen);\n };\n }, []);\n\n return setIsOpen(false)} />;\n }\n\n root.render();\n\n // 3. Observe DOM for in-session token usage dialog: strictly single button\n const observer = new MutationObserver(() => {\n // Find the dl with data-session-stats-usage\n const usageList = document.querySelector('[data-session-stats-usage]');\n if (!usageList) return;\n\n const dialogContainer = usageList.closest('div[role=\"dialog\"]') || usageList.parentElement;\n if (!dialogContainer) return;\n\n // Strictly ensure only one button is ever attached\n if (dialogContainer.querySelector(\".dsh-heatmap-trigger-btn\") || document.querySelector(\".dsh-heatmap-trigger-btn\")) {\n return;\n }\n\n const button = document.createElement(\"button\");\n button.className = \"dsh-heatmap-trigger-btn\";\n button.type = \"button\";\n button.innerHTML = \"View Token Usage Heatmap \u2192\";\n button.style.cssText = `\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n margin-top: 12px;\n padding: 8px 12px;\n background: rgba(51, 109, 250, 0.12);\n border: 1px solid rgba(51, 109, 250, 0.35);\n border-radius: 8px;\n color: #4f85ff;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);\n transition: all 0.15s ease;\n `;\n button.onmouseenter = () => {\n button.style.background = \"rgba(51, 109, 250, 0.22)\";\n button.style.borderColor = \"#336dfa\";\n button.style.boxShadow = \"0 2px 8px rgba(51, 109, 250, 0.25)\";\n };\n button.onmouseleave = () => {\n button.style.background = \"rgba(51, 109, 250, 0.12)\";\n button.style.borderColor = \"rgba(51, 109, 250, 0.35)\";\n button.style.boxShadow = \"0 1px 4px rgba(0, 0, 0, 0.15)\";\n };\n button.onclick = (e) => {\n e.preventDefault();\n e.stopPropagation();\n\n // Try navigating to Settings -> Token Heatmap\n const settingsTrigger = document.querySelector(\n 'button[aria-label*=\"Setting\" i], [data-sidebar-settings] button, button.settings'\n );\n\n if (settingsTrigger) {\n (settingsTrigger as HTMLElement).click();\n setTimeout(() => {\n const heatmapTab = Array.from(document.querySelectorAll(\"*\")).find(\n (el) =>\n el.textContent?.trim() === \"Token Heatmap\" &&\n (el.tagName === \"BUTTON\" || el.tagName === \"DIV\" || el.getAttribute(\"role\") === \"tab\" || el.classList.contains(\"ant-list-item\"))\n );\n if (heatmapTab) {\n (heatmapTab as HTMLElement).click();\n } else {\n window.dispatchEvent(new CustomEvent(\"dsh:open-token-heatmap\"));\n }\n }, 120);\n } else {\n // Open direct interactive modal\n window.dispatchEvent(new CustomEvent(\"dsh:open-token-heatmap\"));\n }\n };\n\n dialogContainer.appendChild(button);\n });\n\n observer.observe(document.body, { childList: true, subtree: true });\n }\n}\n", "import React, { useEffect, useState, useMemo, useRef } from \"react\";\n\nexport interface DaySessionDetail {\n sessionId: string;\n sessionTitle: string;\n createdAt: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n turns: number;\n}\n\nexport interface DayStats {\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n turns: number;\n level?: number;\n sessions?: DaySessionDetail[];\n}\n\nexport interface ModelUsage {\n model: string;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n turns: number;\n percentage: number;\n}\n\nexport interface SessionSummary {\n id: string;\n title: string;\n createdAt: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n turns: number;\n days: Record;\n models?: Record;\n}\n\nexport interface SummaryStats {\n totalTokens: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheReadTokens: number;\n totalTurns: number;\n activeDays: number;\n currentStreak: number;\n longestStreak: number;\n peakDay: { date: string; tokens: number; turns: number } | null;\n dailyAverage: number;\n}\n\nexport interface HeatmapResponse {\n summary: SummaryStats;\n days: Record;\n sessions: SessionSummary[];\n models?: ModelUsage[];\n startDate: string;\n endDate: string;\n}\n\nfunction formatNumber(num: number): string {\n if (num >= 1_000_000_000) return (num / 1_000_000_000).toFixed(2) + \"B\";\n if (num >= 1_000_000) return (num / 1_000_000).toFixed(1) + \"M\";\n if (num >= 1_000) return (num / 1_000).toFixed(1) + \"k\";\n return num.toLocaleString();\n}\n\nfunction formatExact(num: number): string {\n return num.toLocaleString();\n}\n\nconst MONTH_NAMES = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\nconst DAY_LABELS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n\n// Simple monochromatic gradient: Black -> Vibrant DeepSeek Blue\nconst DEEPSEEK_LEVELS = [\n \"#0f1117\", // Level 0: Black / dark void tile\n \"#152c6f\", // Level 1: Dark deep blue\n \"#1f4cdb\", // Level 2: Medium vibrant blue\n \"#336dfa\", // Level 3: Vibrant DeepSeek brand blue\n \"#4f85ff\", // Level 4: Bright electric DeepSeek blue\n];\n\n// Clean depth for tiles (subtle glow on peak tiles only)\nconst TILE_SHADOWS = [\n \"none\",\n \"none\",\n \"none\",\n \"0 0 3px rgba(51, 109, 250, 0.35)\",\n \"0 0 6px rgba(79, 133, 255, 0.65)\",\n];\n\nfunction HeatmapInnerView() {\n const [data, setData] = useState(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState(null);\n const [selectedSessionId, setSelectedSessionId] = useState(\"all\");\n const [selectedDate, setSelectedDate] = useState(null);\n const [chartRange, setChartRange] = useState<14 | 30 | 90>(30);\n const [hoveredDay, setHoveredDay] = useState<{ date: string; stats: DayStats } | null>(null);\n const [hoveredBar, setHoveredBar] = useState<{\n date: string;\n label: string;\n totalTokens: number;\n billedTokens: number;\n cachedTokens: number;\n turns: number;\n } | null>(null);\n const [tooltipPos, setTooltipPos] = useState<{ x: number; y: number } | null>(null);\n\n // Auto-resize the settings dialog panel smoothly on mount so it comfortably fits the content\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const panel = document.querySelector('[class*=\"_panel\"]') as HTMLElement | null;\n if (panel) {\n const prevWidth = panel.style.width;\n const prevMaxWidth = panel.style.maxWidth;\n const prevTransition = panel.style.transition;\n\n panel.style.transition = \"width 0.25s cubic-bezier(0.16, 1, 0.3, 1), max-width 0.25s ease\";\n panel.style.width = \"min(1080px, calc(100vw - 32px))\";\n panel.style.maxWidth = \"calc(100vw - 32px)\";\n\n return () => {\n panel.style.width = prevWidth;\n panel.style.maxWidth = prevMaxWidth;\n panel.style.transition = prevTransition;\n };\n }\n }, []);\n\n const fetchStats = () => {\n setLoading(true);\n fetch(\"/api/dsh-token-heatmap/stats\")\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP error ${r.status}`);\n return r.json();\n })\n .then((res) => {\n setData(res);\n setLoading(false);\n })\n .catch((err) => {\n setError(err.message);\n setLoading(false);\n });\n };\n\n useEffect(() => {\n fetchStats();\n }, []);\n\n // Compute active days & stats based on session filter\n const activeView = useMemo(() => {\n if (!data) return null;\n\n if (selectedSessionId === \"all\") {\n return {\n summary: data.summary,\n days: data.days,\n models: data.models || [],\n sessionTitle: \"All Sessions (Global)\",\n };\n }\n\n const session = (data.sessions || []).find((s) => s.id === selectedSessionId);\n if (!session) {\n return {\n summary: data.summary,\n days: data.days,\n models: data.models || [],\n sessionTitle: \"All Sessions (Global)\",\n };\n }\n\n let totalTokens = session.totalTokens;\n let totalInputTokens = session.inputTokens;\n let totalOutputTokens = session.outputTokens;\n let totalCacheReadTokens = session.cacheReadTokens;\n let totalTurns = session.turns;\n let activeDays = Object.values(session.days).filter((d) => d.totalTokens > 0).length;\n let dailyAverage = activeDays > 0 ? Math.round(totalTokens / activeDays) : 0;\n let peakDay: { date: string; tokens: number; turns: number } | null = null;\n\n for (const [date, stats] of Object.entries(session.days)) {\n if (!peakDay || stats.totalTokens > peakDay.tokens) {\n peakDay = { date, tokens: stats.totalTokens, turns: stats.turns };\n }\n }\n\n const positiveValues = Object.values(session.days)\n .map((d) => d.totalTokens)\n .filter((t) => t > 0)\n .sort((a, b) => a - b);\n\n let q1 = 1_000_000;\n let q2 = 5_000_000;\n let q3 = 15_000_000;\n\n if (positiveValues.length >= 4) {\n q1 = positiveValues[Math.floor(positiveValues.length * 0.25)];\n q2 = positiveValues[Math.floor(positiveValues.length * 0.50)];\n q3 = positiveValues[Math.floor(positiveValues.length * 0.75)];\n }\n\n const leveledDays: Record = {};\n for (const [date, stats] of Object.entries(session.days)) {\n let lvl = 0;\n if (stats.totalTokens === 0) lvl = 0;\n else if (stats.totalTokens <= q1) lvl = 1;\n else if (stats.totalTokens <= q2) lvl = 2;\n else if (stats.totalTokens <= q3) lvl = 3;\n else lvl = 4;\n leveledDays[date] = { ...stats, level: lvl };\n }\n\n // Models for this session\n const sessionModels: ModelUsage[] = session.models\n ? Object.entries(session.models)\n .map(([model, m]) => ({\n model,\n totalTokens: m.totalTokens,\n inputTokens: m.inputTokens,\n outputTokens: m.outputTokens,\n cacheReadTokens: m.cacheReadTokens,\n turns: m.turns,\n percentage: totalTokens > 0 ? Math.round((m.totalTokens / totalTokens) * 1000) / 10 : 0,\n }))\n .sort((a, b) => b.totalTokens - a.totalTokens)\n : [];\n\n return {\n summary: {\n totalTokens,\n totalInputTokens,\n totalOutputTokens,\n totalCacheReadTokens,\n totalTurns,\n activeDays,\n currentStreak: 0,\n longestStreak: 0,\n peakDay,\n dailyAverage,\n },\n days: leveledDays,\n models: sessionModels,\n sessionTitle: session.title,\n };\n }, [data, selectedSessionId]);\n\n // Compute 52-week calendar grid: 7 rows x 53 columns\n const calendarGrid = useMemo(() => {\n if (!activeView) return null;\n\n const weeks: Array> = [];\n const now = new Date();\n const end = new Date(now);\n const start = new Date(now);\n start.setDate(start.getDate() - 364);\n start.setDate(start.getDate() - start.getDay());\n\n let current = new Date(start);\n let currentWeek: Array<{ date: string; stats: DayStats } | null> = [];\n\n while (current <= end || currentWeek.length > 0) {\n const dateKey = `${current.getFullYear()}-${String(current.getMonth() + 1).padStart(2, \"0\")}-${String(current.getDate()).padStart(2, \"0\")}`;\n const dayStats = activeView.days[dateKey] || {\n totalTokens: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n turns: 0,\n level: 0,\n };\n\n if (current <= end) {\n currentWeek.push({ date: dateKey, stats: dayStats });\n } else {\n currentWeek.push(null);\n }\n\n if (currentWeek.length === 7) {\n weeks.push(currentWeek);\n currentWeek = [];\n }\n current.setDate(current.getDate() + 1);\n if (current > end && currentWeek.length === 0) break;\n }\n\n return weeks;\n }, [activeView]);\n\n // Compute month headers\n const monthHeaders = useMemo(() => {\n if (!calendarGrid) return [];\n const headers: Array<{ label: string; col: number }> = [];\n let lastMonth = -1;\n\n calendarGrid.forEach((week, colIdx) => {\n const firstValidDay = week.find((d) => d !== null);\n if (firstValidDay) {\n const d = new Date(firstValidDay.date + \"T00:00:00\");\n const month = d.getMonth();\n if (month !== lastMonth) {\n headers.push({ label: MONTH_NAMES[month], col: colIdx });\n lastMonth = month;\n }\n }\n });\n\n return headers;\n }, [calendarGrid]);\n\n // Daily token timeline chart data for the selected range (14, 30, or 90 days)\n const chartData = useMemo(() => {\n if (!activeView) return [];\n const now = new Date();\n const items: Array<{\n date: string;\n label: string;\n totalTokens: number;\n billedTokens: number;\n cachedTokens: number;\n turns: number;\n }> = [];\n\n for (let i = chartRange - 1; i >= 0; i--) {\n const d = new Date(now);\n d.setDate(d.getDate() - i);\n const dateKey = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n const stats = activeView.days[dateKey] || {\n totalTokens: 0,\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n turns: 0,\n };\n items.push({\n date: dateKey,\n label: `${MONTH_NAMES[d.getMonth()]} ${d.getDate()}`,\n totalTokens: stats.totalTokens,\n billedTokens: stats.inputTokens + stats.outputTokens,\n cachedTokens: stats.cacheReadTokens,\n turns: stats.turns,\n });\n }\n return items;\n }, [activeView, chartRange]);\n\n const maxChartTokens = useMemo(() => {\n return Math.max(...chartData.map((d) => d.totalTokens), 1_000_000);\n }, [chartData]);\n\n if (loading && !data) {\n return (\n
\n Loading token usage statistics...\n
\n );\n }\n\n if (error) {\n return (\n
\n Failed to load heatmap: {error}\n \n Retry\n \n
\n );\n }\n\n if (!data || !activeView) return null;\n\n const { summary } = activeView;\n const cacheHitPercent =\n summary.totalTokens > 0\n ? Math.round((summary.totalCacheReadTokens / summary.totalTokens) * 100)\n : 0;\n\n const inspectedDayStats = selectedDate ? activeView.days[selectedDate] : null;\n\n return (\n \n {/* Top Header & Filter Controls (OpenCode / Codex style) */}\n \n
\n \n Token Usage Activity\n \n 52 Weeks\n \n \n \n Daily token throughput and model breakdown across sessions\n

\n
\n\n {/* Right side controls: Session selector & Refresh */}\n
\n {/* Session Filter Dropdown */}\n
\n Session:\n {\n setSelectedSessionId(e.target.value);\n setSelectedDate(null);\n }}\n style={{\n background: \"var(--dsw-alias-bg-layer-2, #2c2c2c)\",\n border: \"1px solid var(--dsw-alias-border-l2, #494949)\",\n color: \"var(--dsw-alias-label-primary, #eeeeee)\",\n borderRadius: 8,\n padding: \"6px 12px\",\n fontSize: 12,\n outline: \"none\",\n cursor: \"pointer\",\n maxWidth: 240,\n }}\n >\n \n {(data.sessions || []).map((s) => (\n \n ))}\n \n
\n\n \n \u21BB Refresh\n \n
\n \n\n {/* Prominent Centered Total Token Usage Hero (Centered above the chart) */}\n \n
\n Total Tokens Processed\n
\n
\n {formatExact(summary.totalTokens)}\n
\n
\n {formatNumber(summary.totalTokens)} tokens across {summary.activeDays} active days ({summary.totalTurns} total requests)\n
\n \n\n {/* Contribution Calendar Card (Centered and scaled to fit without horizontal scrollbars) */}\n \n \n Contribution Calendar\n {summary.peakDay && (\n \n Peak day: {summary.peakDay.date} ({formatNumber(summary.peakDay.tokens)} tokens)\n \n )}\n \n\n {/* Clean, centered SVG Calendar Grid (Fits panel smoothly) */}\n {calendarGrid && (\n
\n \n {/* Month labels */}\n {monthHeaders.map((m, i) => (\n \n {m.label}\n \n ))}\n\n {/* Day of week labels */}\n \n Mon\n \n \n Wed\n \n \n Fri\n \n\n {/* Day Tiles: centered and proportioned */}\n {calendarGrid.map((week, colIdx) => (\n \n {week.map((day, rowIdx) => {\n if (!day) return null;\n\n const lvl = day.stats.level || 0;\n const bg = DEEPSEEK_LEVELS[lvl];\n const isSelected = selectedDate === day.date;\n const isHovered = hoveredDay?.date === day.date;\n\n // GitHub-style glow & outline\n const strokeColor = isSelected\n ? \"#ffffff\"\n : isHovered\n ? \"#5686fe\"\n : lvl === 0\n ? \"rgba(255, 255, 255, 0.06)\"\n : \"transparent\";\n\n const strokeWidth = isSelected ? 1.6 : isHovered ? 1.3 : 1;\n\n return (\n {\n setSelectedDate(isSelected ? null : day.date);\n }}\n onMouseEnter={(e) => {\n setHoveredDay({ date: day.date, stats: day.stats });\n const rect = e.currentTarget.getBoundingClientRect();\n setTooltipPos({ x: rect.left + rect.width / 2, y: rect.top - 8 });\n }}\n onMouseLeave={() => {\n setHoveredDay(null);\n setTooltipPos(null);\n }}\n />\n );\n })}\n \n ))}\n \n\n {/* Bottom Legend */}\n \n \n Tip: Click any day to inspect sessions and token breakdown\n \n\n
\n Less\n {DEEPSEEK_LEVELS.map((c, i) => (\n \n ))}\n More\n
\n
\n \n )}\n \n\n {/* Secondary Metrics Grid (Under the chart, cache color cleared to neutral) */}\n \n {/* Card 1: Cache Hit Ratio (CLEARED BLUE COLOR -> Neutral/Clean White) */}\n \n
\n Cache Hit Ratio\n
\n
\n {cacheHitPercent}%\n
\n
\n {formatNumber(summary.totalCacheReadTokens)} cached reads\n
\n \n\n {/* Card 2: Daily Average / Turns */}\n \n
\n {selectedSessionId === \"all\" ? \"Daily Average\" : \"Total Steps\"}\n
\n
\n {selectedSessionId === \"all\" ? formatNumber(summary.dailyAverage) : summary.totalTurns}\n
\n
\n {selectedSessionId === \"all\" ? `Across ${summary.activeDays} active days` : `${formatNumber(summary.totalInputTokens + summary.totalOutputTokens)} billed`}\n
\n \n\n {/* Card 3: Streak / Activity */}\n \n
\n {selectedSessionId === \"all\" ? \"Active Streak\" : \"Active Days\"}\n
\n
\n {selectedSessionId === \"all\" ? `${summary.currentStreak} days` : `${summary.activeDays} days`}\n
\n
\n {selectedSessionId === \"all\" ? `Longest streak: ${summary.longestStreak} days` : `Session: ${activeView.sessionTitle.slice(0, 24)}`}\n
\n \n\n {/* Card 4: Peak Day */}\n \n
\n Peak Volume Day\n
\n
\n {summary.peakDay ? formatNumber(summary.peakDay.tokens) : \"None\"}\n
\n
\n {summary.peakDay ? summary.peakDay.date : \"No activity\"}\n
\n \n \n\n {/* Top Models Breakdown Section */}\n {activeView.models && activeView.models.length > 0 && (\n \n \n
\n Model Usage Breakdown\n
\n
\n {activeView.models.length} model{activeView.models.length === 1 ? \"\" : \"s\"} tracked\n
\n \n\n
\n {activeView.models.map((m) => (\n
\n \n \n {m.model}\n \n \n {formatNumber(m.totalTokens)} ({m.percentage}%) \u00B7 {m.turns} requests\n \n
\n \n \n
\n \n ))}\n \n \n )}\n\n {/* Token Usage Trends Graph Card */}\n \n \n
\n
\n Token Usage Trends\n
\n
\n Daily billed vs cached token volume over time\n
\n
\n\n
\n {/* Legend: Billed vs Cached */}\n
\n
\n \n Billed Tokens\n
\n
\n \n Cache Reads\n
\n
\n\n {/* Range Toggle: 14D | 30D | 90D */}\n \n {[14, 30, 90].map((days) => (\n setChartRange(days as any)}\n style={{\n border: 0,\n background: chartRange === days ? \"var(--dsw-alias-bg-layer-2, #2c2c2c)\" : \"transparent\",\n color: chartRange === days ? \"#4f85ff\" : \"var(--dsw-alias-label-tertiary)\",\n fontSize: 11,\n padding: \"3px 8px\",\n borderRadius: 6,\n cursor: \"pointer\",\n fontWeight: chartRange === days ? 600 : 400,\n }}\n >\n {days}D\n \n ))}\n
\n \n \n\n {/* The SVG Timeline Bar Chart */}\n
\n
\n \n {/* Horizontal grid lines & Y labels */}\n {[1, 0.66, 0.33, 0].map((ratio, i) => {\n const y = 20 + (1 - ratio) * 125;\n const tokenVal = Math.round(maxChartTokens * ratio);\n return (\n \n \n \n {formatNumber(tokenVal)}\n \n \n );\n })}\n\n {/* Bars for each day in chartData */}\n {chartData.map((d, idx) => {\n const totalBarWidth = 825 / chartData.length;\n const barWidth = Math.max(5, Math.min(22, totalBarWidth - 5));\n const x = 50 + idx * totalBarWidth + (totalBarWidth - barWidth) / 2;\n\n const totalH = (d.totalTokens / maxChartTokens) * 125;\n const cachedH = d.totalTokens > 0 ? (d.cachedTokens / d.totalTokens) * totalH : 0;\n const billedH = totalH - cachedH;\n const yBase = 145;\n\n const isHovered = hoveredBar?.date === d.date;\n\n return (\n {\n setHoveredBar(d);\n const rect = e.currentTarget.getBoundingClientRect();\n setTooltipPos({ x: rect.left + rect.width / 2, y: rect.top - 8 });\n }}\n onMouseLeave={() => {\n setHoveredBar(null);\n setTooltipPos(null);\n }}\n onClick={() => setSelectedDate(selectedDate === d.date ? null : d.date)}\n >\n {/* Hover hit area */}\n \n\n {/* Empty/Zero line marker if 0 tokens */}\n {d.totalTokens === 0 ? (\n \n ) : (\n \n {/* Cached segment (bottom) */}\n {cachedH > 0 && (\n \n )}\n {/* Billed segment (top) */}\n {billedH > 0 && (\n \n )}\n \n )}\n\n {/* X-axis date labels: show every Nth label depending on range */}\n {(chartRange === 14 || idx % (chartRange === 30 ? 3 : 7) === 0) && (\n \n {d.label}\n \n )}\n \n );\n })}\n \n
\n
\n \n\n {/* Day Inspection Detail Drawer (Opens when any day is clicked) */}\n {selectedDate && (\n \n {/* Header of the Day Detail */}\n \n
\n
\n {new Date(selectedDate + \"T00:00:00\").toLocaleDateString(undefined, {\n weekday: \"long\",\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n })}\n
\n
\n {inspectedDayStats && inspectedDayStats.totalTokens > 0 ? (\n \n {formatExact(inspectedDayStats.totalTokens)} total tokens across{\" \"}\n {inspectedDayStats.turns} turns\n \n ) : (\n \"No activity recorded\"\n )}\n
\n
\n\n setSelectedDate(null)}\n style={{\n background: \"transparent\",\n border: \"none\",\n color: \"var(--dsw-alias-label-tertiary)\",\n cursor: \"pointer\",\n fontSize: 16,\n padding: \"4px 8px\",\n borderRadius: 4,\n }}\n title=\"Close day view\"\n >\n \u2715\n \n \n\n {/* Breakdown Pills for the day */}\n {inspectedDayStats && inspectedDayStats.totalTokens > 0 && (\n \n
\n Billed Input: \n {formatNumber(inspectedDayStats.inputTokens)}\n
\n
\n Output Generated: \n {formatNumber(inspectedDayStats.outputTokens)}\n
\n
\n Cache Reads: \n {formatNumber(inspectedDayStats.cacheReadTokens)} (\n {Math.round((inspectedDayStats.cacheReadTokens / inspectedDayStats.totalTokens) * 100)}%)\n
\n \n )}\n\n {/* Sessions List for that Day */}\n
\n
\n Sessions Active on this Day ({(inspectedDayStats?.sessions || []).length})\n
\n\n {inspectedDayStats?.sessions && inspectedDayStats.sessions.length > 0 ? (\n
\n {inspectedDayStats.sessions.map((sess, idx) => (\n \n
\n
\n {sess.sessionTitle}\n
\n
\n Started {new Date(sess.createdAt).toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" })} \u00B7 ID: {sess.sessionId.slice(0, 10)}\n
\n
\n\n
\n
\n {formatNumber(sess.totalTokens)} tokens\n
\n
\n {sess.turns} turns \u00B7 {formatNumber(sess.inputTokens + sess.outputTokens)} billed\n
\n
\n
\n ))}\n
\n ) : (\n
\n No session records found for this date.\n
\n )}\n \n \n )}\n\n {/* Floating Hover Tooltip for Calendar Day */}\n {hoveredDay && tooltipPos && !selectedDate && (\n \n
\n {new Date(hoveredDay.date + \"T00:00:00\").toLocaleDateString(undefined, {\n weekday: \"short\",\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n })}\n
\n {hoveredDay.stats.totalTokens === 0 ? (\n
No activity recorded
\n ) : (\n
\n
\n {formatExact(hoveredDay.stats.totalTokens)} tokens\n
\n
\n {hoveredDay.stats.turns} requests / steps\n
\n
\n Click to inspect sessions on this day\n
\n
\n )}\n \n )}\n\n {/* Floating Hover Tooltip for Chart Bar */}\n {hoveredBar && tooltipPos && (\n \n
\n {new Date(hoveredBar.date + \"T00:00:00\").toLocaleDateString(undefined, {\n weekday: \"short\",\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n })}\n
\n {hoveredBar.totalTokens === 0 ? (\n
No activity recorded
\n ) : (\n
\n
\n {formatExact(hoveredBar.totalTokens)} total tokens\n
\n
\n Billed: {formatNumber(hoveredBar.billedTokens)} \u00B7 Cached: {formatNumber(hoveredBar.cachedTokens)}\n
\n
\n {hoveredBar.turns} turns \u00B7 Click to inspect day\n
\n
\n )}\n \n )}\n \n );\n}\n\nclass HeatmapErrorBoundary extends React.Component<\n { children: React.ReactNode },\n { hasError: boolean; error: any }\n> {\n constructor(props: any) {\n super(props);\n this.state = { hasError: false, error: null };\n }\n static getDerivedStateFromError(error: any) {\n return { hasError: true, error };\n }\n componentDidCatch(error: any, info: any) {\n console.error(\"Heatmap rendering error:\", error, info);\n }\n render() {\n if (this.state.hasError) {\n return (\n
\n

Token Heatmap Encountered an Error

\n \n {String(this.state.error?.message || this.state.error)}\n \n this.setState({ hasError: false, error: null })}\n style={{\n marginTop: 12,\n padding: \"6px 14px\",\n background: \"var(--dsw-alias-brand-primary, #3964fe)\",\n color: \"#fff\",\n border: \"none\",\n borderRadius: 6,\n cursor: \"pointer\",\n }}\n >\n Retry\n \n
\n );\n }\n return this.props.children;\n }\n}\n\nexport function HeatmapView() {\n return (\n \n \n \n );\n}\n", "import React from \"react\";\nimport { HeatmapView } from \"./HeatmapView.js\";\n\ninterface HeatmapModalProps {\n open: boolean;\n onClose: () => void;\n}\n\nexport function HeatmapModal({ open, onClose }: HeatmapModalProps) {\n if (!open) return null;\n\n return (\n \n e.stopPropagation()}\n style={{\n position: \"relative\",\n width: \"100%\",\n maxWidth: \"1040px\",\n maxHeight: \"92vh\",\n overflowY: \"auto\",\n background: \"var(--dsw-alias-bg-layer-1, #1e1e1e)\",\n border: \"1px solid var(--dsw-alias-border-l2, #494949)\",\n borderRadius: \"14px\",\n boxShadow: \"0 16px 40px rgba(0, 0, 0, 0.5)\",\n padding: \"24px 28px\",\n }}\n >\n {/* Close button */}\n \n \u2715\n \n\n \n \n \n );\n}\n"], + "mappings": "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", "names": ["import_react", "React", "import_react", "React", "React"] } diff --git a/src/client/HeatmapView.tsx b/src/client/HeatmapView.tsx index ed68a0d..8408bc7 100644 --- a/src/client/HeatmapView.tsx +++ b/src/client/HeatmapView.tsx @@ -22,6 +22,16 @@ export interface DayStats { sessions?: DaySessionDetail[]; } +export interface ModelUsage { + model: string; + totalTokens: number; + inputTokens: number; + outputTokens: number; + cacheReadTokens: number; + turns: number; + percentage: number; +} + export interface SessionSummary { id: string; title: string; @@ -32,6 +42,7 @@ export interface SessionSummary { cacheReadTokens: number; turns: number; days: Record; + models?: Record; } export interface SummaryStats { @@ -51,6 +62,7 @@ export interface HeatmapResponse { summary: SummaryStats; days: Record; sessions: SessionSummary[]; + models?: ModelUsage[]; startDate: string; endDate: string; } @@ -78,6 +90,15 @@ const DEEPSEEK_LEVELS = [ "#4f85ff", // Level 4: Bright electric DeepSeek blue ]; +// Clean depth for tiles (subtle glow on peak tiles only) +const TILE_SHADOWS = [ + "none", + "none", + "none", + "0 0 3px rgba(51, 109, 250, 0.35)", + "0 0 6px rgba(79, 133, 255, 0.65)", +]; + function HeatmapInnerView() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); @@ -95,7 +116,27 @@ function HeatmapInnerView() { turns: number; } | null>(null); const [tooltipPos, setTooltipPos] = useState<{ x: number; y: number } | null>(null); - const scrollContainerRef = useRef(null); + + // Auto-resize the settings dialog panel smoothly on mount so it comfortably fits the content + useEffect(() => { + if (typeof document === "undefined") return; + const panel = document.querySelector('[class*="_panel"]') as HTMLElement | null; + if (panel) { + const prevWidth = panel.style.width; + const prevMaxWidth = panel.style.maxWidth; + const prevTransition = panel.style.transition; + + panel.style.transition = "width 0.25s cubic-bezier(0.16, 1, 0.3, 1), max-width 0.25s ease"; + panel.style.width = "min(1080px, calc(100vw - 32px))"; + panel.style.maxWidth = "calc(100vw - 32px)"; + + return () => { + panel.style.width = prevWidth; + panel.style.maxWidth = prevMaxWidth; + panel.style.transition = prevTransition; + }; + } + }, []); const fetchStats = () => { setLoading(true); @@ -126,6 +167,7 @@ function HeatmapInnerView() { return { summary: data.summary, days: data.days, + models: data.models || [], sessionTitle: "All Sessions (Global)", }; } @@ -135,6 +177,7 @@ function HeatmapInnerView() { return { summary: data.summary, days: data.days, + models: data.models || [], sessionTitle: "All Sessions (Global)", }; } @@ -180,6 +223,21 @@ function HeatmapInnerView() { leveledDays[date] = { ...stats, level: lvl }; } + // Models for this session + const sessionModels: ModelUsage[] = session.models + ? Object.entries(session.models) + .map(([model, m]) => ({ + model, + totalTokens: m.totalTokens, + inputTokens: m.inputTokens, + outputTokens: m.outputTokens, + cacheReadTokens: m.cacheReadTokens, + turns: m.turns, + percentage: totalTokens > 0 ? Math.round((m.totalTokens / totalTokens) * 1000) / 10 : 0, + })) + .sort((a, b) => b.totalTokens - a.totalTokens) + : []; + return { summary: { totalTokens, @@ -194,6 +252,7 @@ function HeatmapInnerView() { dailyAverage, }, days: leveledDays, + models: sessionModels, sessionTitle: session.title, }; }, [data, selectedSessionId]); @@ -262,12 +321,6 @@ function HeatmapInnerView() { return headers; }, [calendarGrid]); - useEffect(() => { - if (scrollContainerRef.current) { - scrollContainerRef.current.scrollLeft = scrollContainerRef.current.scrollWidth; - } - }, [calendarGrid]); - // Daily token timeline chart data for the selected range (14, 30, or 90 days) const chartData = useMemo(() => { if (!activeView) return []; @@ -351,12 +404,13 @@ function HeatmapInnerView() { return (

- Daily token volume and request step intensity across sessions + Daily token throughput and model breakdown across sessions

@@ -464,37 +518,210 @@ function HeatmapInnerView() {
- {/* Summary Cards Row (Codex / OpenCode style) */} + {/* Prominent Centered Total Token Usage Hero (Centered above the chart) */} +
+
+ Total Tokens Processed +
+
+ {formatExact(summary.totalTokens)} +
+
+ {formatNumber(summary.totalTokens)} tokens across {summary.activeDays} active days ({summary.totalTurns} total requests) +
+
+ + {/* Contribution Calendar Card (Centered and scaled to fit without horizontal scrollbars) */} +
+
+ Contribution Calendar + {summary.peakDay && ( + + Peak day: {summary.peakDay.date} ({formatNumber(summary.peakDay.tokens)} tokens) + + )} +
+ + {/* Clean, centered SVG Calendar Grid (Fits panel smoothly) */} + {calendarGrid && ( +
+ + {/* Month labels */} + {monthHeaders.map((m, i) => ( + + {m.label} + + ))} + + {/* Day of week labels */} + + Mon + + + Wed + + + Fri + + + {/* Day Tiles: centered and proportioned */} + {calendarGrid.map((week, colIdx) => ( + + {week.map((day, rowIdx) => { + if (!day) return null; + + const lvl = day.stats.level || 0; + const bg = DEEPSEEK_LEVELS[lvl]; + const isSelected = selectedDate === day.date; + const isHovered = hoveredDay?.date === day.date; + + // GitHub-style glow & outline + const strokeColor = isSelected + ? "#ffffff" + : isHovered + ? "#5686fe" + : lvl === 0 + ? "rgba(255, 255, 255, 0.06)" + : "transparent"; + + const strokeWidth = isSelected ? 1.6 : isHovered ? 1.3 : 1; + + return ( + { + setSelectedDate(isSelected ? null : day.date); + }} + 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); + }} + /> + ); + })} + + ))} + + + {/* Bottom Legend */} +
+ + Tip: Click any day to inspect sessions and token breakdown + + +
+ Less + {DEEPSEEK_LEVELS.map((c, i) => ( +
+ ))} + More +
+
+
+ )} +
+ + {/* Secondary Metrics Grid (Under the chart, cache color cleared to neutral) */}
- {/* Card 1: Total Tokens */} -
-
- Total Tokens -
-
- {formatNumber(summary.totalTokens)} -
-
- {formatExact(summary.totalTokens)} tokens -
-
- - {/* Card 2: Cache Hit */} + {/* Card 1: Cache Hit Ratio (CLEARED BLUE COLOR -> Neutral/Clean White) */}
Cache Hit Ratio
-
+
{cacheHitPercent}%
@@ -515,7 +742,7 @@ function HeatmapInnerView() {
- {/* Card 3: Daily Average / Turns */} + {/* Card 2: Daily Average / Turns */}
- {/* Card 4: Streak / Activity */} + {/* Card 3: Streak / Activity */}
-
- {/* Heatmap Card (Auto-resizes responsive SVG panel, GitHub style) */} -
+ {/* Card 4: Peak Day */}
- Contribution Calendar - {summary.peakDay && ( - - Peak day: {summary.peakDay.date} ({formatNumber(summary.peakDay.tokens)} tokens) - - )} +
+ Peak Volume Day +
+
+ {summary.peakDay ? formatNumber(summary.peakDay.tokens) : "None"} +
+
+ {summary.peakDay ? summary.peakDay.date : "No activity"} +
+
- {/* Generously sized GitHub-Style Calendar with smooth horizontal scroll */} - {calendarGrid && ( + {/* Top Models Breakdown Section */} + {activeView.models && activeView.models.length > 0 && ( +
-
- - {/* Month labels */} - {monthHeaders.map((m, i) => ( - - {m.label} - - ))} - - {/* Day of week labels */} - - Mon - - - Wed - - - Fri - - - {/* Day Tiles: Generously sized 13px tiles with 16.5px step */} - {calendarGrid.map((week, colIdx) => ( - - {week.map((day, rowIdx) => { - if (!day) return null; - - const lvl = day.stats.level || 0; - const bg = DEEPSEEK_LEVELS[lvl]; - const isSelected = selectedDate === day.date; - const isHovered = hoveredDay?.date === day.date; - - // GitHub-style glow & outline - const strokeColor = isSelected - ? "#ffffff" - : isHovered - ? "#5686fe" - : lvl === 0 - ? "rgba(255, 255, 255, 0.06)" - : "transparent"; - - const strokeWidth = isSelected ? 2 : isHovered ? 1.5 : 1; - - return ( - { - setSelectedDate(isSelected ? null : day.date); - }} - 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); - }} - /> - ); - })} - - ))} - +
+ Model Usage Breakdown
- - {/* Bottom Legend */} -
- - Tip: Click any day to inspect sessions and token breakdown - - -
- Less - {DEEPSEEK_LEVELS.map((c, i) => ( -
- ))} - More -
+
+ {activeView.models.length} model{activeView.models.length === 1 ? "" : "s"} tracked
- )} -
+ +
+ {activeView.models.map((m) => ( +
+
+ + {m.model} + + + {formatNumber(m.totalTokens)} ({m.percentage}%) ยท {m.turns} requests + +
+
+
+
+
+ ))} +
+
+ )} {/* Token Usage Trends Graph Card */}
-
+
Token Usage Trends
-
+
Daily billed vs cached token volume over time
@@ -771,11 +911,11 @@ function HeatmapInnerView() { {/* Legend: Billed vs Cached */}
- + Billed Tokens
- + Cache Reads
@@ -814,7 +954,7 @@ function HeatmapInnerView() { {/* The SVG Timeline Bar Chart */}
-
+
Cache Reads: - {formatNumber(inspectedDayStats.cacheReadTokens)} ( + {formatNumber(inspectedDayStats.cacheReadTokens)} ( {Math.round((inspectedDayStats.cacheReadTokens / inspectedDayStats.totalTokens) * 100)}%)
@@ -1038,7 +1178,7 @@ function HeatmapInnerView() { {/* Sessions List for that Day */}
- Sessions Active on this Day ({inspectedDayStats?.sessions?.length || 0}) + Sessions Active on this Day ({(inspectedDayStats?.sessions || []).length})
{inspectedDayStats?.sessions && inspectedDayStats.sessions.length > 0 ? ( @@ -1085,7 +1225,7 @@ function HeatmapInnerView() {
)} - {/* Floating Hover Tooltip */} + {/* Floating Hover Tooltip for Calendar Day */} {hoveredDay && tooltipPos && !selectedDate && (
; + models?: Record; } export interface SummaryStats { @@ -54,6 +65,7 @@ export interface HeatmapResponse { summary: SummaryStats; days: Record; sessions: SessionSummary[]; + models: ModelUsage[]; startDate: string; endDate: string; } @@ -93,6 +105,7 @@ async function parseSessionFile(filePath: string): Promise = {}; + const sessionModels: Record = {}; let sessionId = path.basename(path.dirname(filePath)); let title = ""; let createdAt = stat.birthtimeMs || stat.mtimeMs; @@ -137,6 +150,7 @@ async function parseSessionFile(filePath: string): Promise b.createdAt - a.createdAt); const days: Record = {}; + const allModelsMap: Record = {}; for (const session of parsedSessions) { for (const [date, stats] of Object.entries(session.days)) { @@ -262,6 +287,19 @@ export async function aggregateTokens(customSessionsRoot?: string): Promise ({ + model, + totalTokens: m.totalTokens, + inputTokens: m.inputTokens, + outputTokens: m.outputTokens, + cacheReadTokens: m.cacheReadTokens, + turns: m.turns, + percentage: totalTokens > 0 ? Math.round((m.totalTokens / totalTokens) * 1000) / 10 : 0, + })) + .sort((a, b) => b.totalTokens - a.totalTokens); + const activeDays = Object.values(days).filter((d) => d.totalTokens > 0).length; const dailyAverage = activeDays > 0 ? Math.round(totalTokens / activeDays) : 0; @@ -388,6 +439,7 @@ export async function aggregateTokens(customSessionsRoot?: string): Promise