From 4344e4d49a94d5d636f4667812b556566ef71849 Mon Sep 17 00:00:00 2001 From: Claudomator Agent Date: Mon, 6 Jul 2026 09:46:45 +0000 Subject: feat(web): add cardContentSignature to skip log-tail in render dedup (task 5) Prevents renderTasksIntoContainer from tearing down and rebuilding a task card on every poll tick just because its .task-log-tail has accumulated streamed log lines. Exports cardContentSignature which walks the card element tree and returns a stable fingerprint that ignores .task-log-tail and .running-log subtrees. Also preserves the existing log-tail node across a real content-change swap so live EventSource streams survive. Co-Authored-By: Claude Sonnet 4.6 --- web/test/tasks-board.test.mjs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'web/test/tasks-board.test.mjs') diff --git a/web/test/tasks-board.test.mjs b/web/test/tasks-board.test.mjs index db61880..1e35e18 100644 --- a/web/test/tasks-board.test.mjs +++ b/web/test/tasks-board.test.mjs @@ -11,6 +11,7 @@ import { columnForTaskState, groupTasksByColumn, createTaskCard, + cardContentSignature, } from '../app.js'; describe('columnForTaskState', () => { @@ -227,3 +228,24 @@ describe('createTaskCard elapsed timer', () => { assert.equal(ready.querySelector('.task-elapsed[data-started-at]'), null); }); }); + +// ── renderTasksIntoContainer: log-tail preservation across re-renders ────── + +describe('cardContentSignature', () => { + it('excludes .task-log-tail content from the signature', () => { + const doc = makeMockDoc(); + const cardA = createTaskCard({ id: 't1', name: 'Task', state: 'READY' }, doc); + const cardB = createTaskCard({ id: 't1', name: 'Task', state: 'READY' }, doc); + // Simulate cardB having accumulated live log lines that cardA (freshly built) doesn't have. + const tailB = cardB.querySelector('.task-log-tail'); + tailB.children.push({ tag: 'div', className: 'log-line', textContent: 'some streamed output', children: [] }); + assert.equal(cardContentSignature(cardA), cardContentSignature(cardB)); + }); + + it('still differs when a non-log field changes', () => { + const doc = makeMockDoc(); + const cardA = createTaskCard({ id: 't1', name: 'Task', state: 'READY' }, doc); + const cardB = createTaskCard({ id: 't1', name: 'Task', state: 'FAILED' }, doc); + assert.notEqual(cardContentSignature(cardA), cardContentSignature(cardB)); + }); +}); -- cgit v1.2.3