diff --git a/src/web/public/app.js b/src/web/public/app.js index bf713531..68895790 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -2365,7 +2365,9 @@ class ClaudemanApp { if (!grid) return; if (todos.length === 0) { - grid.innerHTML = '
No tasks detected
'; + if (grid.children.length !== 1 || !grid.querySelector('.inner-state-empty')) { + grid.innerHTML = '
No tasks detected
'; + } return; } @@ -2375,16 +2377,59 @@ class ClaudemanApp { return (order[a.status] || 1) - (order[b.status] || 1); }); - grid.innerHTML = sorted.map(todo => { - const statusClass = `task-${todo.status.replace('_', '-')}`; - const icon = this.getRalphTaskIcon(todo.status); - return ` -
- ${icon} - ${this.escapeHtml(todo.content)} -
- `; - }).join(''); + // Incremental DOM update - reuse existing elements where possible + const existingCards = grid.querySelectorAll('.ralph-task-card'); + const fragment = document.createDocumentFragment(); + let needsRebuild = existingCards.length !== sorted.length; + + // Check if we can do incremental update + if (!needsRebuild) { + // Update existing cards in place + sorted.forEach((todo, i) => { + const card = existingCards[i]; + const statusClass = `task-${todo.status.replace('_', '-')}`; + const icon = this.getRalphTaskIcon(todo.status); + + // Update class if changed + if (!card.classList.contains(statusClass)) { + card.className = `ralph-task-card ${statusClass}`; + } + + // Update icon if changed + const iconEl = card.querySelector('.ralph-task-icon'); + if (iconEl && iconEl.textContent !== icon) { + iconEl.textContent = icon; + } + + // Update content if changed + const contentEl = card.querySelector('.ralph-task-content'); + if (contentEl && contentEl.textContent !== todo.content) { + contentEl.textContent = todo.content; + } + }); + } else { + // Full rebuild needed - use DocumentFragment for efficiency + sorted.forEach(todo => { + const card = document.createElement('div'); + const statusClass = `task-${todo.status.replace('_', '-')}`; + card.className = `ralph-task-card ${statusClass}`; + + const iconSpan = document.createElement('span'); + iconSpan.className = 'ralph-task-icon'; + iconSpan.textContent = this.getRalphTaskIcon(todo.status); + + const contentSpan = document.createElement('span'); + contentSpan.className = 'ralph-task-content'; + contentSpan.textContent = todo.content; + + card.appendChild(iconSpan); + card.appendChild(contentSpan); + fragment.appendChild(card); + }); + + grid.innerHTML = ''; + grid.appendChild(fragment); + } } getRalphTaskIcon(status) {