💄 One row per operation in the Live changes table

Columns revn | op | id: the revn repeats across a batch, the op wears the canvas diff colors (shape/attrs detail on hover), and the id column shows the uuid last group with the full uuid on hover, or N/A for ops without a subject id (e.g. mov-objects).

Signed-off-by: Álvaro Tejero Cantero <alvorithm@teje.ro>
This commit is contained in:
Álvaro Tejero Cantero 2026-07-17 00:54:50 +02:00 committed by Andrey Antukh
parent c2672d086a
commit 7b89de6797

View File

@ -79,7 +79,8 @@ Graph Console
<thead> <thead>
<tr> <tr>
<th>revn</th> <th>revn</th>
<th>changes</th> <th>op</th>
<th>id</th>
</tr> </tr>
</thead> </thead>
<tbody id="graph-changelog-body"></tbody> <tbody id="graph-changelog-body"></tbody>
@ -1281,9 +1282,8 @@ Graph Console
return out; return out;
} }
function summarizeChange(change) { function changeDetail(change) {
const parts = [change.type]; const parts = [];
if (change.id) parts.push("id=" + change.id);
if (change.obj && change.obj.type) parts.push("shape=" + change.obj.type); if (change.obj && change.obj.type) parts.push("shape=" + change.obj.type);
if (change.operations && change.operations.length) { if (change.operations && change.operations.length) {
const attrs = change.operations const attrs = change.operations
@ -1294,11 +1294,6 @@ Graph Console
return parts.join(" "); return parts.join(" ");
} }
function summarizeChanges(changes) {
if (!changes || !changes.length) return "(empty)";
return changes.map(summarizeChange).join("; ");
}
function summarizeSkipped(skipped) { function summarizeSkipped(skipped) {
if (!skipped) return ""; if (!skipped) return "";
const items = Array.isArray(skipped) ? skipped : [skipped]; const items = Array.isArray(skipped) ? skipped : [skipped];
@ -1336,26 +1331,37 @@ Graph Console
.catch(function () {}); .catch(function () {});
} }
function appendChange(revn, summary) { // One row per operation (revn repeats across a batch); op wears the
// canvas diff colors, id shows the uuid's last group with the full
// uuid on hover, or N/A for ops without a subject id.
function appendChanges(revn, changes) {
// The whole box stays hidden until the first change arrives; feed // The whole box stays hidden until the first change arrives; feed
// state lives in the session fieldset. // state lives in the session fieldset.
changelogBox.style.display = "block"; changelogBox.style.display = "block";
let lastRow = null;
const row = document.createElement("tr"); (changes && changes.length ? changes : [{}]).forEach(function (change) {
const revnCell = document.createElement("td"); const row = document.createElement("tr");
const changesCell = document.createElement("td"); const revnCell = document.createElement("td");
revnCell.textContent = String(revn);
revnCell.textContent = String(revn); const opCell = document.createElement("td");
// add-obj / del-obj wear the diff-mark colors of the canvas. const op = change.type || "?";
changesCell.innerHTML = escapeHtml(summary) opCell.textContent = op;
.replace(/\badd-obj\b/g, const detail = changeDetail(change);
'<span style="color: ' + DIFF_ADDED_COLOR + ';">add-obj</span>') if (detail) opCell.title = detail;
.replace(/\bdel-obj\b/g, if (op === "add-obj") opCell.style.color = DIFF_ADDED_COLOR;
'<span style="color: ' + DIFF_REMOVED_COLOR + ';">del-obj</span>'); if (op === "del-obj") opCell.style.color = DIFF_REMOVED_COLOR;
row.appendChild(revnCell); const idCell = document.createElement("td");
row.appendChild(changesCell); const id = change.id ? String(change.id) : null;
changelogBody.appendChild(row); const groups = id ? id.split("-") : null;
row.scrollIntoView({ block: "nearest" }); idCell.textContent = groups ? groups[groups.length - 1] : "N/A";
if (id) idCell.title = id;
row.appendChild(revnCell);
row.appendChild(opCell);
row.appendChild(idCell);
changelogBody.appendChild(row);
lastRow = row;
});
if (lastRow) lastRow.scrollIntoView({ block: "nearest" });
} }
function handleMessage(raw) { function handleMessage(raw) {
@ -1368,7 +1374,7 @@ Graph Console
if (msg.type !== "file-change" || msg["file-id"] !== fileId) return; if (msg.type !== "file-change" || msg["file-id"] !== fileId) return;
appendChange(msg.revn, summarizeChanges(msg.changes)); appendChanges(msg.revn, msg.changes);
setTimeout(refreshSyncStatus, 150); setTimeout(refreshSyncStatus, 150);
scheduleGraphRefetch(); scheduleGraphRefetch();
} }