Recurse appearance sweeps into subgraphs

This commit is contained in:
ausboss
2026-08-12 22:51:04 -05:00
parent 8182212fec
commit 63beebcfed
3 changed files with 46 additions and 2 deletions
+2 -2
View File
@@ -2,6 +2,7 @@ import { app } from "/scripts/app.js";
import {
DEFAULT_SCHEME,
NODE_COLOR_SCHEMES,
collectGraphNodes,
schemeColors,
shouldRecolor,
titleInk,
@@ -30,10 +31,9 @@ function applyScheme(node, colors) {
}
function repaintAll(nextScheme, previousScheme) {
const nodes = app.graph?._nodes || app.graph?.nodes || [];
const next = schemeColors(nextScheme);
const previous = schemeColors(previousScheme);
for (const node of nodes) {
for (const node of collectGraphNodes(app.graph)) {
if (!isAusbossNode(node)) continue;
if (!shouldRecolor(node, previous)) continue;
applyScheme(node, next);
+16
View File
@@ -36,6 +36,22 @@ export function titleInk(hex) {
return luminance > 0.6 ? "#1a1a1a" : "#ffffff";
}
// Flatten every node reachable from a graph, descending into subgraph
// containers via `node.subgraph`. Accepts a graph exposing `_nodes` or
// `nodes`, or a plain node array; the visited set breaks reference cycles.
export function collectGraphNodes(graph, visited = new Set()) {
if (!graph || visited.has(graph)) return [];
visited.add(graph);
const nodes = Array.isArray(graph) ? graph : graph._nodes || graph.nodes || [];
const collected = [];
for (const node of nodes) {
if (!node) continue;
collected.push(node);
if (node.subgraph) collected.push(...collectGraphNodes(node.subgraph, visited));
}
return collected;
}
// A node is repainted only while it is still "following the setting": either
// it carries no colors while the previous scheme was the theme default, or it
// still wears exactly the previous scheme's pair. Anything else — a color the
+28
View File
@@ -4,6 +4,7 @@ import assert from "node:assert/strict";
import {
DEFAULT_SCHEME,
NODE_COLOR_SCHEMES,
collectGraphNodes,
schemeColors,
shouldRecolor,
titleInk,
@@ -78,3 +79,30 @@ test("titleInk falls back to light ink on malformed input", () => {
assert.equal(titleInk("e0e0e0"), "#ffffff");
assert.equal(titleInk("#zzzzzz"), "#ffffff");
});
test("collectGraphNodes flattens nested subgraphs", () => {
const inner = { _nodes: [{ id: 3 }] };
const graph = { _nodes: [{ id: 1 }, { id: 2, subgraph: inner }] };
assert.deepEqual(
collectGraphNodes(graph).map((node) => node.id),
[1, 2, 3],
);
});
test("collectGraphNodes accepts nodes arrays and plain arrays", () => {
const viaNodes = { nodes: [{ id: 1 }] };
assert.deepEqual(collectGraphNodes(viaNodes).map((node) => node.id), [1]);
const plain = [{ id: 1 }, { id: 2, subgraph: [{ id: 3 }] }];
assert.deepEqual(collectGraphNodes(plain).map((node) => node.id), [1, 2, 3]);
assert.deepEqual(collectGraphNodes(null), []);
assert.deepEqual(collectGraphNodes({}), []);
});
test("collectGraphNodes survives subgraph reference cycles", () => {
const graph = { _nodes: [{ id: 1 }] };
graph._nodes.push({ id: 2, subgraph: graph });
assert.deepEqual(
collectGraphNodes(graph).map((node) => node.id),
[1, 2],
);
});