let activityChart = null;
function cssVar(name) {
return getComputedStyle(document.documentElement)
.getPropertyValue(name)
.trim();
}
/* turn hex/rgb/rgba into rgba with alpha */
function withAlpha(color, alpha) {
if (!color) return `rgba(0,0,0,${alpha})`;
// #RGB or #RRGGBB
if (color[0] === "#") {
let hex = color.slice(1).trim();
if (hex.length === 3) hex = hex.split("").map(c => c + c).join("");
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
// rgb(...)
if (color.startsWith("rgb(")) {
return color.replace("rgb(", "rgba(").replace(")", `, ${alpha})`);
}
// rgba(...)
if (color.startsWith("rgba(")) {
const inside = color.slice(5, -1).split(",").map(s => s.trim());
return `rgba(${inside[0]}, ${inside[1]}, ${inside[2]}, ${alpha})`;
}
// fallback (named colours etc.)
return color;
}
/* helper for tooltip glass background (canvas, so no real blur) */
function tooltipBg() {
const theme = document.documentElement.dataset.theme || "light";
return theme === "dark"
? "rgba(68, 68, 78, 0.85)"
: "rgba(255, 253, 242, 0.85)";
}
function applyTheme(chart) {
const accent = cssVar("--accent");
const text = cssVar("--text");
const muted = cssVar("--muted");
const grid = cssVar("--line");
// datasets (order matters!)
const dsKeys = chart.data.datasets[0];
const dsLines = chart.data.datasets[1];
const dsHours = chart.data.datasets[2];
dsKeys.borderColor = accent;
dsKeys.backgroundColor = withAlpha(accent, 0.14);
dsLines.borderColor = withAlpha(accent, 0.65);
dsLines.backgroundColor = withAlpha(accent, 0.10);
dsHours.borderColor = withAlpha(accent, 0.80);
dsHours.backgroundColor = withAlpha(accent, 0.08);
// legend
chart.options.plugins.legend.labels.color = muted;
// tooltip
chart.options.plugins.tooltip.titleColor = text;
chart.options.plugins.tooltip.bodyColor = text;
chart.options.plugins.tooltip.backgroundColor = tooltipBg();
chart.options.plugins.tooltip.borderColor = grid;
// axes
chart.options.scales.x.ticks.color = muted;
chart.options.scales.x.grid.color = grid;
chart.options.scales.y.ticks.color = muted;
chart.options.scales.y.grid.color = grid;
chart.options.scales.y.title.color = muted;
chart.options.scales.y1.ticks.color = muted;
chart.options.scales.y1.title.color = muted;
chart.update("none");
}
document.addEventListener("DOMContentLoaded", () => {
const canvas = document.getElementById("activityChart");
if (!canvas || typeof Chart === "undefined") return;
const ctx = canvas.getContext("2d");
const days =
typeof initialDaysData !== "undefined" && initialDaysData
? initialDaysData
: [];
const labels = days.map(d => d.date);
const keysPressed = days.map(d => d.keys_pressed);
const hours = days.map(d => d.hours);
const lines = days.map(d => d.lines);
activityChart = new Chart(ctx, {
type: "line",
data: {
labels,
datasets: [
{
label: "Keys pressed",
data: keysPressed,
borderWidth: 2,
tension: 0.35,
pointRadius: 3,
pointHoverRadius: 5,
yAxisID: "y"
},
{
label: "Lines",
data: lines,
borderWidth: 2,
tension: 0.35,
pointRadius: 3,
pointHoverRadius: 5,
yAxisID: "y"
},
{
label: "Hours",
data: hours,
borderWidth: 2,
tension: 0.35,
pointRadius: 3,
pointHoverRadius: 5,
borderDash: [6, 6],
yAxisID: "y1"
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { mode: "index", intersect: false },
plugins: {
legend: {
labels: {
boxWidth: 12,
boxHeight: 12
}
},
tooltip: {
borderWidth: 1,
cornerRadius: 10,
padding: 12,
boxPadding: 6,
titleFont: { weight: "600" },
bodyFont: { weight: "500" },
callbacks: {
label: (c) => ` ${c.dataset.label}: ${c.formattedValue}`
}
}
},
scales: {
x: {
ticks: { maxRotation: 45, minRotation: 45 }
},
y: {
position: "left",
title: { display: true, text: "Keys / lines" }
},
y1: {
position: "right",
grid: { drawOnChartArea: false },
title: { display: true, text: "Hours" }
}
}
}
});
applyTheme(activityChart);
});
window.addEventListener("theme:changed", () => {
if (!activityChart) return;
requestAnimationFrame(() => applyTheme(activityChart));
});