Skip to content
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
## 2026-07-12 - Optimize renderTaskRow DOM allocations
**Learning:** Caching unattached template nodes and instantiating them via `.cloneNode(false)` reduces DOM instantiation overhead in O(N) render loops significantly.
**Action:** Apply this optimization to other hot-path rendering elements such as rows, cells, and stack containers.
## 2026-08-05 - Optimize DOM allocations in text/badge cell rendering
**Learning:** Repeatedly calling document.createElement() in hot O(N) render paths for small elements like text wrappers, owner badges, and status badges causes high JS-to-C++ bridge overhead and GC pressure.
**Action:** Cache these small structural DOM elements as unattached templates on first use, and instantiate them via .cloneNode(false) to significantly reduce allocation costs.
32 changes: 26 additions & 6 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -919,17 +919,27 @@ function createTreeCellContent(value, depth) {
return treeValue;
}

// ⚡ Bolt: Cache text cell DOM structures as templates to avoid repetitive document.createElement() overhead
let textCellWrapperTemplate = null;
let textCellValidationTemplate = null;

function createTextCellContent(value, warning = '') {
if (!value) {
return warning ? createWarningBadge(warning) : createEmptyCell();
}
if (!warning) {
return document.createTextNode(value);
}
const wrapper = document.createElement('div');

if (!textCellWrapperTemplate) {
textCellWrapperTemplate = document.createElement('div');
textCellValidationTemplate = document.createElement('div');
textCellValidationTemplate.className = 'validation-message';
}

const wrapper = textCellWrapperTemplate.cloneNode(false);
wrapper.appendChild(document.createTextNode(value));
const validation = document.createElement('div');
validation.className = 'validation-message';
const validation = textCellValidationTemplate.cloneNode(false);
validation.textContent = warning;
wrapper.appendChild(validation);
return wrapper;
Expand Down Expand Up @@ -972,6 +982,7 @@ function createWarningBadge(warning) {
}

const persistentOwnerColorMap = new Map();
let ownerBadgeTemplate = null;

function createOwnerCellContent(owner) {
if (!owner) {
Expand All @@ -982,18 +993,27 @@ function createOwnerCellContent(owner) {
persistentOwnerColorMap.set(owner, OWNER_COLORS[persistentOwnerColorMap.size % OWNER_COLORS.length]);
}

const badge = document.createElement('span');
badge.className = 'owner-badge';
if (!ownerBadgeTemplate) {
ownerBadgeTemplate = document.createElement('span');
ownerBadgeTemplate.className = 'owner-badge';
}

const badge = ownerBadgeTemplate.cloneNode(false);
badge.style.background = persistentOwnerColorMap.get(owner);
badge.textContent = owner;
return badge;
}

let statusBadgeTemplate = null;

function createStatusCellContent(progressState) {
if (!progressState.label) {
return createEmptyCell();
}
const badge = document.createElement('span');
if (!statusBadgeTemplate) {
statusBadgeTemplate = document.createElement('span');
}
const badge = statusBadgeTemplate.cloneNode(false);
badge.className = `status-badge ${progressState.className}`;
badge.textContent = progressState.label;
if (progressState.description) {
Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"dependencies": {
"@hono/node-server": "^2.0.12",
"hono": "^4.13.1"
"hono": "^4.12.32"
},
"devDependencies": {
"@playwright/test": "1.61.1",
Expand Down
Loading