claude-code-kanban 1.11.1 → 1.12.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-kanban",
3
- "version": "1.11.1",
3
+ "version": "1.12.0",
4
4
  "description": "A web-based Kanban board for viewing Claude Code tasks with agent teams support",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/index.html CHANGED
@@ -65,15 +65,35 @@
65
65
 
66
66
  /* Sidebar */
67
67
  .sidebar {
68
- width: 300px;
68
+ width: var(--sidebar-width, 300px);
69
69
  background: var(--bg-surface);
70
70
  border-right: 1px solid var(--border);
71
71
  box-shadow: 1px 0 12px rgba(0, 0, 0, 0.04);
72
72
  display: flex;
73
73
  flex-direction: column;
74
74
  flex-shrink: 0;
75
+ position: relative;
76
+ overflow: hidden;
77
+ transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
75
78
  }
76
79
 
80
+ .sidebar.collapsed { width: 48px; }
81
+ .sidebar.collapsed .logo-text,
82
+ .sidebar.collapsed .connection,
83
+ .sidebar.collapsed .sidebar-section,
84
+ .sidebar.collapsed .sidebar-footer { display: none; }
85
+ .sidebar.collapsed .sidebar-header {
86
+ padding: 12px;
87
+ display: flex;
88
+ flex-direction: column;
89
+ align-items: center;
90
+ gap: 12px;
91
+ }
92
+ .sidebar.collapsed .sidebar-toggle-btn {
93
+ position: static;
94
+ }
95
+ .sidebar.resizing { transition: none; }
96
+
77
97
  .sidebar-header {
78
98
  padding: 20px 20px 16px;
79
99
  border-bottom: none;
@@ -81,8 +101,61 @@
81
101
  background-size: 100% 1px;
82
102
  background-repeat: no-repeat;
83
103
  background-position: bottom;
104
+ position: relative;
105
+ }
106
+
107
+ .sidebar-toggle-btn {
108
+ position: absolute;
109
+ top: 20px;
110
+ right: 8px;
111
+ width: 28px;
112
+ height: 28px;
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: center;
116
+ background: transparent;
117
+ border: 1px solid transparent;
118
+ border-radius: 6px;
119
+ color: var(--text-muted);
120
+ cursor: pointer;
121
+ transition: all 0.15s ease;
122
+ padding: 0;
123
+ }
124
+
125
+ .sidebar-toggle-btn:hover {
126
+ color: var(--text-secondary);
127
+ background: var(--bg-hover);
128
+ border-color: var(--border);
129
+ }
130
+
131
+ .sidebar-toggle-btn svg {
132
+ width: 16px;
133
+ height: 16px;
134
+ transition: transform 0.25s ease;
135
+ }
136
+
137
+ .sidebar.collapsed .sidebar-toggle-btn svg {
138
+ transform: rotate(180deg);
139
+ }
140
+
141
+ .sidebar-resize-handle {
142
+ position: absolute;
143
+ top: 0;
144
+ right: 0;
145
+ width: 4px;
146
+ height: 100%;
147
+ cursor: col-resize;
148
+ z-index: 10;
149
+ transition: background 0.15s;
150
+ }
151
+
152
+ .sidebar-resize-handle:hover,
153
+ .sidebar-resize-handle.dragging {
154
+ background: var(--accent-dim);
84
155
  }
85
156
 
157
+ .sidebar.collapsed .sidebar-resize-handle { display: none; }
158
+
86
159
  .logo {
87
160
  display: flex;
88
161
  align-items: center;
@@ -115,9 +188,9 @@
115
188
  .connection {
116
189
  display: flex;
117
190
  align-items: center;
118
- gap: 6px;
119
- margin-top: 12px;
120
- font-size: 12px;
191
+ gap: 5px;
192
+ margin-top: 10px;
193
+ font-size: 10px;
121
194
  color: var(--text-tertiary);
122
195
  text-transform: uppercase;
123
196
  letter-spacing: 0.05em;
@@ -387,6 +460,7 @@
387
460
 
388
461
  /* Footer */
389
462
  .sidebar-footer {
463
+ flex-shrink: 0;
390
464
  padding: 14px 20px;
391
465
  border-top: none;
392
466
  background-image: linear-gradient(to right, transparent, var(--border), transparent);
@@ -616,6 +690,7 @@
616
690
  display: flex;
617
691
  flex-direction: column;
618
692
  gap: 8px;
693
+ padding-right: 8px;
619
694
  }
620
695
 
621
696
  .column-empty {
@@ -1620,6 +1695,11 @@
1620
1695
  <span class="connection-dot"></span>
1621
1696
  <span>Connecting</span>
1622
1697
  </div>
1698
+ <button id="sidebar-toggle" class="sidebar-toggle-btn" onclick="toggleSidebar()" title="Toggle sidebar" aria-label="Toggle sidebar">
1699
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1700
+ <path d="M15 18l-6-6 6-6"/>
1701
+ </svg>
1702
+ </button>
1623
1703
  </header>
1624
1704
 
1625
1705
  <!-- Live Updates -->
@@ -1678,6 +1758,8 @@
1678
1758
  <div id="sessions-list" class="sessions-list"></div>
1679
1759
  </div>
1680
1760
 
1761
+ <div class="sidebar-footer" id="sidebar-footer"></div>
1762
+ <div class="sidebar-resize-handle" id="sidebar-resize"></div>
1681
1763
  </aside>
1682
1764
 
1683
1765
  <!-- Main -->
@@ -2130,8 +2212,8 @@
2130
2212
  <div class="live-item" onclick="openLiveTask('${task.sessionId}', '${task.id}')">
2131
2213
  <span class="pulse"></span>
2132
2214
  <div class="live-item-content">
2133
- <div class="live-item-action">${escapeHtml(task.activeForm || task.subject)}</div>
2134
- <div class="live-item-session">${escapeHtml(task.sessionName || task.sessionId.slice(0, 8))}</div>
2215
+ <div class="live-item-action" title="${escapeHtml(task.activeForm || task.subject)}">${escapeHtml(task.activeForm || task.subject)}</div>
2216
+ <div class="live-item-session" title="${escapeHtml(task.sessionName || task.sessionId)}">${escapeHtml(task.sessionName || task.sessionId)}</div>
2135
2217
  </div>
2136
2218
  </div>
2137
2219
  `).join('');
@@ -2565,6 +2647,10 @@
2565
2647
 
2566
2648
  focusZone = zone;
2567
2649
  if (zone === 'sidebar') {
2650
+ if (sidebar.classList.contains('collapsed')) {
2651
+ sidebar.classList.remove('collapsed');
2652
+ localStorage.setItem('sidebar-collapsed', false);
2653
+ }
2568
2654
  sidebar.classList.add('sidebar-focused');
2569
2655
  const items = getSessionItems();
2570
2656
  if (selectedSessionIdx < 0 && items.length > 0) {
@@ -2888,6 +2974,12 @@
2888
2974
 
2889
2975
  if (document.querySelector('.modal-overlay.visible')) return;
2890
2976
 
2977
+ if (e.key === '[') {
2978
+ e.preventDefault();
2979
+ toggleSidebar();
2980
+ return;
2981
+ }
2982
+
2891
2983
  // Tab toggles focus zone
2892
2984
  if (e.key === 'Tab') {
2893
2985
  e.preventDefault();
@@ -3141,6 +3233,62 @@
3141
3233
  updateThemeIcon();
3142
3234
  }
3143
3235
 
3236
+ function toggleSidebar() {
3237
+ const sidebar = document.querySelector('.sidebar');
3238
+ const collapsed = sidebar.classList.toggle('collapsed');
3239
+ localStorage.setItem('sidebar-collapsed', collapsed);
3240
+ if (collapsed) {
3241
+ sidebar.style.width = '';
3242
+ } else {
3243
+ const w = getComputedStyle(sidebar).getPropertyValue('--sidebar-width');
3244
+ if (w) sidebar.style.width = w;
3245
+ }
3246
+ }
3247
+
3248
+ function loadSidebarState() {
3249
+ const sidebar = document.querySelector('.sidebar');
3250
+ if (localStorage.getItem('sidebar-collapsed') === 'true') {
3251
+ sidebar.classList.add('collapsed');
3252
+ }
3253
+ const w = localStorage.getItem('sidebar-width');
3254
+ if (w) {
3255
+ sidebar.style.setProperty('--sidebar-width', w);
3256
+ }
3257
+ }
3258
+
3259
+ function initSidebarResize() {
3260
+ const sidebar = document.querySelector('.sidebar');
3261
+ const handle = document.getElementById('sidebar-resize');
3262
+ let startX, startWidth;
3263
+
3264
+ handle.addEventListener('mousedown', (e) => {
3265
+ if (sidebar.classList.contains('collapsed')) return;
3266
+ startX = e.clientX;
3267
+ startWidth = sidebar.offsetWidth;
3268
+ sidebar.classList.add('resizing');
3269
+ handle.classList.add('dragging');
3270
+ document.body.style.userSelect = 'none';
3271
+ document.addEventListener('mousemove', onMove);
3272
+ document.addEventListener('mouseup', onUp);
3273
+ e.preventDefault();
3274
+ });
3275
+
3276
+ function onMove(e) {
3277
+ const w = Math.min(600, Math.max(200, startWidth + e.clientX - startX));
3278
+ sidebar.style.setProperty('--sidebar-width', w + 'px');
3279
+ sidebar.style.width = w + 'px';
3280
+ }
3281
+
3282
+ function onUp() {
3283
+ sidebar.classList.remove('resizing');
3284
+ handle.classList.remove('dragging');
3285
+ document.body.style.userSelect = '';
3286
+ document.removeEventListener('mousemove', onMove);
3287
+ document.removeEventListener('mouseup', onUp);
3288
+ localStorage.setItem('sidebar-width', sidebar.style.getPropertyValue('--sidebar-width'));
3289
+ }
3290
+ }
3291
+
3144
3292
  function loadPreferences() {
3145
3293
  document.getElementById('session-filter').value = sessionFilter;
3146
3294
  document.getElementById('session-limit').value = sessionLimit;
@@ -3259,8 +3407,20 @@
3259
3407
  renderKanban();
3260
3408
  }
3261
3409
 
3410
+ // Sync sidebar header height with view header
3411
+ const sidebarHeader = document.querySelector('.sidebar-header');
3412
+ const viewHeader = document.querySelector('.view-header');
3413
+ new ResizeObserver(() => {
3414
+ sidebarHeader.style.height = viewHeader.offsetHeight + 'px';
3415
+ }).observe(viewHeader);
3416
+
3262
3417
  // Init
3263
3418
  loadTheme();
3419
+ loadSidebarState();
3420
+ initSidebarResize();
3421
+ fetch('/api/version').then(r => r.json()).then(d => {
3422
+ document.getElementById('sidebar-footer').textContent = 'v' + d.version;
3423
+ }).catch(() => {});
3264
3424
 
3265
3425
  const urlState = getUrlState();
3266
3426
  sessionFilter = urlState.filter || 'active';
@@ -3326,6 +3486,10 @@
3326
3486
  <td style="padding: 4px 0; color: var(--text-secondary);"><kbd style="background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-family: monospace;">Tab</kbd></td>
3327
3487
  <td style="padding: 4px 0; color: var(--text-primary);">Toggle sidebar / board focus</td>
3328
3488
  </tr>
3489
+ <tr>
3490
+ <td style="padding: 4px 0; color: var(--text-secondary);"><kbd style="background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-family: monospace;">[</kbd></td>
3491
+ <td style="padding: 4px 0; color: var(--text-primary);">Toggle sidebar collapse</td>
3492
+ </tr>
3329
3493
  </table>
3330
3494
  </div>
3331
3495
  <div>
package/server.js CHANGED
@@ -329,6 +329,11 @@ app.get('/api/teams/:name', (req, res) => {
329
329
  res.json(config);
330
330
  });
331
331
 
332
+ app.get('/api/version', (req, res) => {
333
+ const pkg = require('./package.json');
334
+ res.json({ version: pkg.version });
335
+ });
336
+
332
337
  // API: Get all tasks across all sessions
333
338
  app.get('/api/tasks/all', async (req, res) => {
334
339
  try {
@@ -516,6 +521,10 @@ projectsWatcher.on('all', (event, filePath) => {
516
521
  }
517
522
  });
518
523
 
524
+ app.use('/api', (req, res) => {
525
+ res.status(404).json({ error: 'Not found' });
526
+ });
527
+
519
528
  // Start server
520
529
  const server = app.listen(PORT, () => {
521
530
  const actualPort = server.address().port;