A new Activity page on the rail (and the home page's recent list) shows, by day,
what the plugin changed. Record only; undo comes later (AD-018).
- api/activity_log.py keeps the newest 500 entries in the ComfyUI user folder.
A middleware records this plugin's and Anomalous_TTS's write requests after
they succeed (including the 200 + {"status": "error"} style as failure), from
one route table; the log never fails the request it records.
- activity_canvas.js: a press inside Anomalous snapshots the canvas; the next
press or key outside it (or the next press inside) logs the difference, so
every button, drag and dialog that writes nodes is covered and the user's own
edits are not. Opening another workflow is logged as that. <html> carries an
Anomalous class and does not count as the plugin's interface.
- activity_diff.js: pure snapshot and difference; activity_log.js: client and
wording; ui_activity.js: the page (filters, details, find on canvas, clear)
and the home list.
- Tests: tests/activity_diff.mjs, tests/test_activity_log.py (local).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
74 lines
3.0 KiB
JavaScript
74 lines
3.0 KiB
JavaScript
/**
|
|
* Records the canvas changes Anomalous makes. A pointer press anywhere in Anomalous's
|
|
* interface takes a snapshot of the canvas; the next press outside it (or any key
|
|
* outside it, or the next press inside) compares the canvas with that snapshot and
|
|
* logs the difference, before the user's own edit happens. So every button, drag and
|
|
* dialog that writes to nodes is covered without each one reporting itself.
|
|
* Opening another workflow meanwhile is logged as that, not as removed nodes.
|
|
*/
|
|
|
|
import { app } from "../../../scripts/app.js";
|
|
import { diffSnapshots, snapshotGraph } from './activity_diff.js';
|
|
import { postCanvasActivity } from './activity_log.js';
|
|
|
|
const AMB_UI = '#anomalous-modal, [id^="anomalous-"], [class*="anomalous-"]';
|
|
const MAX_SENT = 60;
|
|
|
|
const currentGraph = () => app.canvas?.graph || app.graph || null;
|
|
const activeWorkflow = () => app.extensionManager?.workflow?.activeWorkflow || null;
|
|
const workflowName = () => {
|
|
const workflow = activeWorkflow();
|
|
return String(workflow?.filename || workflow?.path || '');
|
|
};
|
|
const workflowKey = () => {
|
|
const workflow = activeWorkflow();
|
|
return String(workflow?.key || workflow?.path || workflow?.filename || '');
|
|
};
|
|
|
|
// <html> carries Anomalous classes too (the floating entry setting); it is not our interface.
|
|
const insideAnomalous = (target) => {
|
|
const hit = target instanceof Element ? target.closest(AMB_UI) : null;
|
|
return Boolean(hit) && hit !== document.documentElement && hit !== document.body;
|
|
};
|
|
|
|
/** Starts watching; sets `owner.flushCanvasActivity()` (log what is pending now). */
|
|
export function watchCanvasChanges(owner) {
|
|
let baseline = null;
|
|
|
|
const flush = () => {
|
|
if (!baseline) return;
|
|
const { graph, workflow, nodes } = baseline;
|
|
baseline = null;
|
|
const now = currentGraph();
|
|
if (!now || now !== graph) return; // moved into or out of a subgraph meanwhile
|
|
const after = snapshotGraph(now);
|
|
let changes;
|
|
if (workflowKey() !== workflow) {
|
|
changes = [{ kind: 'opened', node: workflowName(), after: String(after.size) }];
|
|
} else {
|
|
changes = diffSnapshots(nodes, after);
|
|
}
|
|
if (!changes.length) return;
|
|
postCanvasActivity({ changes: changes.slice(0, MAX_SENT), total: changes.length, workflow: workflowName() })
|
|
.then(() => owner.onActivityRecorded?.())
|
|
.catch((error) => console.warn('[AMB] Activity log: canvas change not recorded.', error));
|
|
};
|
|
|
|
const start = () => {
|
|
flush();
|
|
const graph = currentGraph();
|
|
baseline = graph ? { graph, workflow: workflowKey(), nodes: snapshotGraph(graph) } : null;
|
|
};
|
|
|
|
// Capture phase: runs before ComfyUI or Anomalous handle the event.
|
|
window.addEventListener('pointerdown', (e) => {
|
|
if (insideAnomalous(e.target)) start();
|
|
else flush();
|
|
}, true);
|
|
window.addEventListener('keydown', (e) => {
|
|
if (!insideAnomalous(e.target)) flush();
|
|
}, true);
|
|
|
|
owner.flushCanvasActivity = flush;
|
|
}
|