Files
DemonGatanjieuandClaude Opus 5.5 01dcdf37a9 Combo group on the pointer: Esc cancels, prompt nodes always, common negative
- While a combo group follows the pointer, Esc removes it again; the activity log
  holds back until it is put down, so a cancelled group leaves no entry.
- The positive and negative prompt nodes are always laid out (positive empty when
  the combo has no prompt); the negative takes Prompt Studio's common negative card
  (starter cards moved to prompt_composition.js as PROMPT_PRESETS).
- The UNet "connect clip yourself" message shows once the group is down; it used to
  pop up while the group followed the pointer, and closing it put the group down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 02:09:25 +08:00

73 lines
3.2 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. What each
* entry did is handed to canvas_undo.js, so the log can undo it.
*/
import { app } from "../../../scripts/app.js";
import { diffSnapshots, snapshotGraph } from './activity_diff.js';
import { postCanvasActivity } from './activity_log.js';
import { keepUndo, workflowKey as openWorkflowKey } from './canvas_undo.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 = () => openWorkflowKey(app);
// <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 = () => {
// A combo group following the pointer is logged once it is put down; Esc removes it.
if (!baseline || owner.placingCombo) 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);
const opened = workflowKey() !== workflow;
const changes = opened ? [{ kind: 'opened', node: workflowName(), after: String(after.size) }] : diffSnapshots(nodes, after);
if (!changes.length) return;
postCanvasActivity({ changes: changes.slice(0, MAX_SENT), total: changes.length, workflow: workflowName() })
.then((data) => {
if (!opened) keepUndo(data?.entry?.id, workflow, nodes, after);
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;
}