@viren/claude-code-dashboard 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/generate-dashboard.mjs +220 -27
- package/package.json +4 -3
- package/src/assembler.mjs +150 -0
- package/src/constants.mjs +1 -1
- package/src/demo.mjs +64 -1
- package/src/render.mjs +0 -4
- package/src/sections.mjs +413 -0
- package/src/watch.mjs +2 -2
- package/template/dashboard.css +1251 -0
- package/template/dashboard.html +51 -0
- package/template/dashboard.js +152 -0
- package/src/html-template.mjs +0 -814
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Claude Code Dashboard</title>
|
|
7
|
+
<style>
|
|
8
|
+
<!-- {{CSS}} -->
|
|
9
|
+
</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<!-- {{HEADER}} -->
|
|
13
|
+
|
|
14
|
+
<!-- {{STATS_BAR}} -->
|
|
15
|
+
|
|
16
|
+
<nav class="tab-nav">
|
|
17
|
+
<button class="tab-btn active" data-tab="overview">Overview</button>
|
|
18
|
+
<button class="tab-btn" data-tab="skills-mcp">Skills & MCP</button>
|
|
19
|
+
<button class="tab-btn" data-tab="analytics">Analytics</button>
|
|
20
|
+
<button class="tab-btn" data-tab="repos">Repos</button>
|
|
21
|
+
<button class="tab-btn" data-tab="reference">Reference</button>
|
|
22
|
+
</nav>
|
|
23
|
+
|
|
24
|
+
<div class="tab-content active" id="tab-overview">
|
|
25
|
+
<!-- {{TAB_OVERVIEW}} -->
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="tab-content" id="tab-skills-mcp">
|
|
29
|
+
<!-- {{TAB_SKILLS_MCP}} -->
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="tab-content" id="tab-analytics">
|
|
33
|
+
<!-- {{TAB_ANALYTICS}} -->
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="tab-content" id="tab-repos">
|
|
37
|
+
<!-- {{TAB_REPOS}} -->
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div class="tab-content" id="tab-reference">
|
|
41
|
+
<!-- {{TAB_REFERENCE}} -->
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<!-- {{FOOTER}} -->
|
|
45
|
+
|
|
46
|
+
<div class="chart-tooltip" id="chart-tooltip"></div>
|
|
47
|
+
<script>
|
|
48
|
+
/* {{JS}} */
|
|
49
|
+
</script>
|
|
50
|
+
</body>
|
|
51
|
+
</html>
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
function switchTab(tabName) {
|
|
2
|
+
document.querySelectorAll(".tab-btn").forEach(function (b) {
|
|
3
|
+
b.classList.remove("active");
|
|
4
|
+
});
|
|
5
|
+
document.querySelectorAll(".tab-content").forEach(function (c) {
|
|
6
|
+
c.classList.remove("active");
|
|
7
|
+
});
|
|
8
|
+
var btn = document.querySelector('.tab-btn[data-tab="' + tabName + '"]');
|
|
9
|
+
if (btn) btn.classList.add("active");
|
|
10
|
+
var content = document.getElementById("tab-" + tabName);
|
|
11
|
+
if (content) content.classList.add("active");
|
|
12
|
+
}
|
|
13
|
+
document.querySelectorAll(".tab-btn").forEach(function (btn) {
|
|
14
|
+
btn.addEventListener("click", function () {
|
|
15
|
+
switchTab(btn.dataset.tab);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
document.querySelectorAll(".stat[data-nav]").forEach(function (stat) {
|
|
19
|
+
stat.addEventListener("click", function () {
|
|
20
|
+
switchTab(stat.dataset.nav);
|
|
21
|
+
if (stat.dataset.section) {
|
|
22
|
+
var el = document.getElementById(stat.dataset.section);
|
|
23
|
+
if (el)
|
|
24
|
+
setTimeout(function () {
|
|
25
|
+
el.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
26
|
+
}, 50);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
var input = document.getElementById("search");
|
|
32
|
+
var hint = document.querySelector(".search-hint");
|
|
33
|
+
|
|
34
|
+
input.addEventListener("input", function (e) {
|
|
35
|
+
var q = e.target.value.toLowerCase();
|
|
36
|
+
hint.style.display = q ? "none" : "";
|
|
37
|
+
document.querySelectorAll(".repo-card").forEach(function (card) {
|
|
38
|
+
var name = card.dataset.name.toLowerCase();
|
|
39
|
+
var path = (card.dataset.path || "").toLowerCase();
|
|
40
|
+
var text = card.textContent.toLowerCase();
|
|
41
|
+
card.style.display =
|
|
42
|
+
q === "" || name.includes(q) || path.includes(q) || text.includes(q) ? "" : "none";
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
document.addEventListener("keydown", function (e) {
|
|
47
|
+
if (e.key === "/" && document.activeElement !== input) {
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
// Switch to repos tab first
|
|
50
|
+
document.querySelectorAll(".tab-btn").forEach(function (b) {
|
|
51
|
+
b.classList.remove("active");
|
|
52
|
+
});
|
|
53
|
+
document.querySelectorAll(".tab-content").forEach(function (c) {
|
|
54
|
+
c.classList.remove("active");
|
|
55
|
+
});
|
|
56
|
+
document.querySelector('[data-tab="repos"]').classList.add("active");
|
|
57
|
+
document.getElementById("tab-repos").classList.add("active");
|
|
58
|
+
input.focus();
|
|
59
|
+
}
|
|
60
|
+
if (e.key === "Escape" && document.activeElement === input) {
|
|
61
|
+
input.value = "";
|
|
62
|
+
input.dispatchEvent(new Event("input"));
|
|
63
|
+
input.blur();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
var toggle = document.getElementById("theme-toggle");
|
|
68
|
+
var saved = localStorage.getItem("ccd-theme");
|
|
69
|
+
if (saved) document.documentElement.setAttribute("data-theme", saved);
|
|
70
|
+
else if (window.matchMedia("(prefers-color-scheme: light)").matches) {
|
|
71
|
+
document.documentElement.setAttribute("data-theme", "light");
|
|
72
|
+
}
|
|
73
|
+
toggle.addEventListener("click", function () {
|
|
74
|
+
var current = document.documentElement.getAttribute("data-theme");
|
|
75
|
+
var next = current === "light" ? "dark" : "light";
|
|
76
|
+
document.documentElement.setAttribute("data-theme", next);
|
|
77
|
+
localStorage.setItem("ccd-theme", next);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
var groupSelect = document.getElementById("group-by");
|
|
81
|
+
groupSelect.addEventListener("change", function () {
|
|
82
|
+
var mode = this.value;
|
|
83
|
+
var grid = document.getElementById("repo-grid");
|
|
84
|
+
grid.querySelectorAll(".group-heading").forEach(function (h) {
|
|
85
|
+
h.remove();
|
|
86
|
+
});
|
|
87
|
+
var cards = Array.from(grid.querySelectorAll(".repo-card"));
|
|
88
|
+
if (mode === "none") {
|
|
89
|
+
cards.forEach(function (c) {
|
|
90
|
+
grid.appendChild(c);
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
var groups = {};
|
|
95
|
+
cards.forEach(function (card) {
|
|
96
|
+
if (mode === "stack") {
|
|
97
|
+
var stacks = (card.dataset.stack || "undetected").split(",");
|
|
98
|
+
stacks.forEach(function (s) {
|
|
99
|
+
var key = s.trim() || "undetected";
|
|
100
|
+
if (!groups[key]) groups[key] = [];
|
|
101
|
+
groups[key].push(card);
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
var key = card.dataset.parent || "~/";
|
|
105
|
+
if (!groups[key]) groups[key] = [];
|
|
106
|
+
groups[key].push(card);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
Object.keys(groups)
|
|
110
|
+
.sort()
|
|
111
|
+
.forEach(function (key) {
|
|
112
|
+
var h = document.createElement("div");
|
|
113
|
+
h.className = "group-heading";
|
|
114
|
+
h.textContent = key || "(none)";
|
|
115
|
+
grid.appendChild(h);
|
|
116
|
+
groups[key].forEach(function (card) {
|
|
117
|
+
grid.appendChild(card);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Custom tooltip for heatmap cells and peak bars
|
|
123
|
+
var tip = document.getElementById("chart-tooltip");
|
|
124
|
+
document.addEventListener("mouseover", function (e) {
|
|
125
|
+
var t = e.target.closest(".heatmap-cell, .peak-bar");
|
|
126
|
+
if (t && t.title) {
|
|
127
|
+
tip.textContent = t.title;
|
|
128
|
+
tip.classList.add("visible");
|
|
129
|
+
t.dataset.tip = t.title;
|
|
130
|
+
t.removeAttribute("title");
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
document.addEventListener("mousemove", function (e) {
|
|
134
|
+
if (tip.classList.contains("visible")) {
|
|
135
|
+
tip.style.left = e.clientX + 12 + "px";
|
|
136
|
+
tip.style.top = e.clientY - 28 + "px";
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
document.addEventListener("mouseout", function (e) {
|
|
140
|
+
var t = e.target.closest(".heatmap-cell, .peak-bar");
|
|
141
|
+
if (t && t.dataset.tip) {
|
|
142
|
+
t.title = t.dataset.tip;
|
|
143
|
+
delete t.dataset.tip;
|
|
144
|
+
}
|
|
145
|
+
if (
|
|
146
|
+
!e.relatedTarget ||
|
|
147
|
+
!e.relatedTarget.closest ||
|
|
148
|
+
!e.relatedTarget.closest(".heatmap-cell, .peak-bar")
|
|
149
|
+
) {
|
|
150
|
+
tip.classList.remove("visible");
|
|
151
|
+
}
|
|
152
|
+
});
|