claude-task-viewer 1.0.0 → 1.2.0

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 CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  A web-based Kanban board for viewing Claude Code tasks. Watch your tasks update in real-time as Claude works.
4
4
 
5
+ ![Dark mode](screenshot-dark.png)
6
+
7
+ ![Light mode](screenshot-light.png)
8
+
5
9
  ## Installation
6
10
 
7
11
  ### Quick start (npx)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-task-viewer",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "A web-based Kanban board for viewing Claude Code tasks",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/index.html CHANGED
@@ -21,6 +21,45 @@
21
21
  }
22
22
  }
23
23
  </script>
24
+ <style id="theme-styles">
25
+ /* Light mode overrides */
26
+ body.light { background: #f8fafc !important; color: #1e293b !important; }
27
+ .light .bg-gray-900 { background: #ffffff !important; }
28
+ .light .bg-gray-950 { background: #f8fafc !important; }
29
+ .light .bg-gray-900\/50 { background: rgba(255,255,255,0.9) !important; }
30
+ .light .bg-gray-800 { background: #f1f5f9 !important; }
31
+ .light .bg-gray-800\/50 { background: rgba(241,245,249,0.7) !important; }
32
+ .light .bg-gray-700 { background: #e2e8f0 !important; }
33
+ .light .bg-gray-700\/50 { background: rgba(226,232,240,0.5) !important; }
34
+ .light .border-gray-800 { border-color: #e2e8f0 !important; }
35
+ .light .border-gray-700 { border-color: #cbd5e1 !important; }
36
+ .light .text-gray-100 { color: #1e293b !important; }
37
+ .light .text-gray-200 { color: #334155 !important; }
38
+ .light .text-gray-300 { color: #475569 !important; }
39
+ .light .text-gray-400 { color: #64748b !important; }
40
+ .light .text-gray-500 { color: #64748b !important; }
41
+ .light .text-gray-600 { color: #94a3b8 !important; }
42
+ .light .hover\:text-gray-300:hover { color: #334155 !important; }
43
+ .light .hover\:text-gray-400:hover { color: #475569 !important; }
44
+ .light .hover\:bg-gray-800:hover { background: #f1f5f9 !important; }
45
+ .light .hover\:bg-gray-800\/50:hover { background: rgba(241,245,249,0.5) !important; }
46
+ .light .border-green-500\/30 { border-color: rgba(34,197,94,0.4) !important; }
47
+ .light .bg-green-500\/10 { background: rgba(34,197,94,0.15) !important; }
48
+ .light .bg-green-500\/20 { background: rgba(34,197,94,0.2) !important; }
49
+ .light .border-claude-orange\/30 { border-color: rgba(232,111,51,0.4) !important; }
50
+ .light .bg-claude-orange\/10 { background: rgba(232,111,51,0.12) !important; }
51
+ .light .bg-claude-orange\/20 { background: rgba(232,111,51,0.2) !important; }
52
+ .light .border-yellow-500\/20 { border-color: rgba(234,179,8,0.4) !important; }
53
+ .light .bg-yellow-500\/10 { background: rgba(234,179,8,0.15) !important; }
54
+ .light .bg-yellow-500\/20 { background: rgba(234,179,8,0.25) !important; }
55
+ .light .border-blue-500\/20 { border-color: rgba(59,130,246,0.4) !important; }
56
+ .light .bg-blue-500\/10 { background: rgba(59,130,246,0.15) !important; }
57
+ .light .prose pre { background: #f1f5f9 !important; }
58
+ .light .prose code { background: #e2e8f0 !important; }
59
+ .light ::-webkit-scrollbar-thumb { background: #e2e8f0 !important; }
60
+ .light ::-webkit-scrollbar-thumb:hover { background: #cbd5e1 !important; }
61
+ body.light { scrollbar-color: #e2e8f0 transparent; }
62
+ </style>
24
63
  <style>
25
64
  .prose pre { background: #1f2937; padding: 1rem; border-radius: 0.5rem; overflow-x: auto; }
26
65
  .prose code { background: #374151; padding: 0.125rem 0.375rem; border-radius: 0.25rem; font-size: 0.875em; }
@@ -28,6 +67,15 @@
28
67
  .status-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
29
68
  @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
30
69
  .kanban-column { min-height: calc(100vh - 200px); }
70
+
71
+ /* Custom scrollbar - dark mode default */
72
+ ::-webkit-scrollbar { width: 8px; height: 8px; }
73
+ ::-webkit-scrollbar-track { background: transparent; }
74
+ ::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb, #374151); border-radius: 4px; }
75
+ ::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover, #4b5563); }
76
+ * { scrollbar-width: thin; scrollbar-color: var(--scrollbar-thumb, #374151) transparent; }
77
+ :root { --scrollbar-thumb: #374151; --scrollbar-thumb-hover: #4b5563; }
78
+ body.light { --scrollbar-thumb: #d1d5db; --scrollbar-thumb-hover: #9ca3af; }
31
79
  </style>
32
80
  </head>
33
81
  <body class="bg-gray-950 text-gray-100 min-h-screen">
@@ -63,6 +111,10 @@
63
111
  </svg>
64
112
  <span class="text-sm text-gray-300">All Tasks</span>
65
113
  </button>
114
+ <label class="flex items-center gap-2 px-3 py-2 mt-1 text-xs text-gray-500 cursor-pointer hover:text-gray-400">
115
+ <input type="checkbox" id="hide-inactive" onchange="toggleHideInactive()" class="rounded border-gray-600 bg-gray-800 text-claude-orange focus:ring-claude-orange focus:ring-offset-0">
116
+ <span>Show active only</span>
117
+ </label>
66
118
  </div>
67
119
 
68
120
  <nav id="sessions-list" class="flex-1 overflow-y-auto p-2">
@@ -70,9 +122,11 @@
70
122
  </nav>
71
123
 
72
124
  <footer class="p-3 border-t border-gray-800 text-xs text-gray-600">
73
- <a href="https://github.com" class="hover:text-gray-400">GitHub</a>
125
+ <a href="https://github.com/L1AD/claude-task-viewer" target="_blank" class="hover:text-gray-400">GitHub</a>
74
126
  <span class="mx-2">·</span>
75
- <span>v1.0.0</span>
127
+ <a href="https://policylayer.com" target="_blank" class="hover:text-gray-400">PolicyLayer</a>
128
+ <span class="mx-2">·</span>
129
+ <span>v1.2.0</span>
76
130
  </footer>
77
131
  </aside>
78
132
 
@@ -102,13 +156,21 @@
102
156
  </div>
103
157
  <span id="progress-percent" class="text-sm font-medium text-claude-orange">0%</span>
104
158
  </div>
159
+ <button id="theme-toggle" onclick="toggleTheme()" class="p-2 rounded-lg hover:bg-gray-800 text-gray-400 hover:text-gray-200 transition-colors" title="Toggle theme">
160
+ <svg id="theme-icon-dark" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
161
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/>
162
+ </svg>
163
+ <svg id="theme-icon-light" class="w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
164
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/>
165
+ </svg>
166
+ </button>
105
167
  </div>
106
168
  </div>
107
169
  </header>
108
170
 
109
171
  <!-- Kanban board -->
110
172
  <div class="flex-1 overflow-x-auto p-4">
111
- <div class="flex gap-4 h-full min-w-max">
173
+ <div class="flex gap-8 h-full min-w-max">
112
174
  <!-- Pending column -->
113
175
  <div class="w-80 flex flex-col">
114
176
  <div class="flex items-center gap-2 mb-3 px-1">
@@ -166,6 +228,7 @@
166
228
  let currentSessionId = null;
167
229
  let currentTasks = [];
168
230
  let viewMode = 'session'; // 'session' or 'all'
231
+ let hideInactive = localStorage.getItem('hideInactive') === 'true';
169
232
 
170
233
  // DOM elements
171
234
  const sessionsList = document.getElementById('sessions-list');
@@ -251,17 +314,22 @@
251
314
  }`;
252
315
  }
253
316
 
254
- if (sessions.length === 0) {
317
+ // Filter sessions based on hideInactive
318
+ const filteredSessions = hideInactive
319
+ ? sessions.filter(s => s.pending > 0 || s.inProgress > 0)
320
+ : sessions;
321
+
322
+ if (filteredSessions.length === 0) {
255
323
  sessionsList.innerHTML = `
256
324
  <div class="text-gray-500 text-sm p-4 text-center">
257
- <p>No sessions found</p>
258
- <p class="mt-2 text-xs">Tasks will appear here when you use Claude Code</p>
325
+ <p>${hideInactive ? 'No active sessions' : 'No sessions found'}</p>
326
+ <p class="mt-2 text-xs">${hideInactive ? 'Uncheck "Show active only" to see all' : 'Tasks will appear here when you use Claude Code'}</p>
259
327
  </div>
260
328
  `;
261
329
  return;
262
330
  }
263
331
 
264
- sessionsList.innerHTML = sessions.map(session => {
332
+ sessionsList.innerHTML = filteredSessions.map(session => {
265
333
  const total = session.taskCount;
266
334
  const percent = total > 0 ? Math.round((session.completed / total) * 100) : 0;
267
335
  const isActive = session.id === currentSessionId && viewMode === 'session';
@@ -483,16 +551,55 @@
483
551
  return div.innerHTML;
484
552
  }
485
553
 
554
+ // Toggle hide inactive sessions
555
+ function toggleHideInactive() {
556
+ hideInactive = document.getElementById('hide-inactive').checked;
557
+ localStorage.setItem('hideInactive', hideInactive);
558
+ renderSessions();
559
+ }
560
+
561
+ // Theme toggle
562
+ function toggleTheme() {
563
+ const body = document.body;
564
+ const isLight = body.classList.contains('light');
565
+
566
+ if (isLight) {
567
+ body.classList.remove('light');
568
+ localStorage.setItem('theme', 'dark');
569
+ document.getElementById('theme-icon-dark').classList.remove('hidden');
570
+ document.getElementById('theme-icon-light').classList.add('hidden');
571
+ } else {
572
+ body.classList.add('light');
573
+ localStorage.setItem('theme', 'light');
574
+ document.getElementById('theme-icon-dark').classList.add('hidden');
575
+ document.getElementById('theme-icon-light').classList.remove('hidden');
576
+ }
577
+ }
578
+
579
+ // Load saved theme
580
+ function loadTheme() {
581
+ const saved = localStorage.getItem('theme');
582
+ if (saved === 'light') {
583
+ document.body.classList.add('light');
584
+ document.getElementById('theme-icon-dark').classList.add('hidden');
585
+ document.getElementById('theme-icon-light').classList.remove('hidden');
586
+ }
587
+ }
588
+
589
+ // Load saved preferences
590
+ function loadPreferences() {
591
+ document.getElementById('hide-inactive').checked = hideInactive;
592
+ }
593
+
486
594
  // Initialize
595
+ loadTheme();
596
+ loadPreferences();
487
597
  fetchSessions();
488
598
  setupEventSource();
489
599
 
490
- // Auto-select most recent session with activity
600
+ // Default to All Tasks view
491
601
  setTimeout(() => {
492
- if (sessions.length > 0 && !currentSessionId) {
493
- const activeSession = sessions.find(s => s.inProgress > 0) || sessions[0];
494
- fetchTasks(activeSession.id);
495
- }
602
+ showAllTasks();
496
603
  }, 500);
497
604
  </script>
498
605
  </body>