mirror of
https://github.com/penpot/penpot.git
synced 2026-08-25 06:08:47 +00:00
🐛 Guard renders against heavy graphs; add ?safe escape hatch
A heavy file could freeze the tab on load-and-render despite the animation gate: the render guard counted nodes only, and the edge-bundling plugin is iteration-heavy in edges. Guard now also trips on edges (8000), edge bundling only activates at <= 300 edges, and /dbg/graph?safe disables auto-render entirely (counts + "Render anyway"), so a page that hung can always be re-entered with the session intact. Signed-off-by: Álvaro Tejero Cantero <alvorithm@teje.ro>
This commit is contained in:
parent
82746bbf06
commit
dd5c9b0cba
@ -570,9 +570,16 @@ Graph Console
|
|||||||
// Legend glyph characters mirroring the G6 node types above.
|
// Legend glyph characters mirroring the G6 node types above.
|
||||||
const GLYPH_CHARS = { diamond: "◆", rect: "■", hexagon: "⬢",
|
const GLYPH_CHARS = { diamond: "◆", rect: "■", hexagon: "⬢",
|
||||||
circle: "●", triangle: "▲", star: "★" };
|
circle: "●", triangle: "▲", star: "★" };
|
||||||
// Above this many nodes the view is not rendered automatically; the
|
// Above this many nodes/edges the view is not rendered automatically;
|
||||||
// "Render anyway" button forces it (~1.5 s per 2k nodes, measured).
|
// the "Render anyway" button forces it (~1.5 s per 2k nodes, measured;
|
||||||
|
// edges gate the guard too since plugin/combo cost scales with them).
|
||||||
|
// Escape hatch: /dbg/graph?safe disables auto-render entirely, so a
|
||||||
|
// page that hung on render can always be re-entered.
|
||||||
const RENDER_GUARD_NODES = 4000;
|
const RENDER_GUARD_NODES = 4000;
|
||||||
|
const RENDER_GUARD_EDGES = 8000;
|
||||||
|
// Edge bundling is iteration-heavy in the number of edges — a
|
||||||
|
// page-freezing cost at scale; bundle only small graphs.
|
||||||
|
const BUNDLE_MAX_EDGES = 300;
|
||||||
// G6's entrance/update animation is nice didactics on small graphs but
|
// G6's entrance/update animation is nice didactics on small graphs but
|
||||||
// the performance killer at scale (>2 min at 1700 nodes vs ~1.5 s off);
|
// the performance killer at scale (>2 min at 1700 nodes vs ~1.5 s off);
|
||||||
// keep it only below this node count.
|
// keep it only below this node count.
|
||||||
@ -910,7 +917,7 @@ Graph Console
|
|||||||
// empty canvas clears. Runs alongside the node:click inspector.
|
// empty canvas clears. Runs alongside the node:click inspector.
|
||||||
{ type: "click-select", degree: 1,
|
{ type: "click-select", degree: 1,
|
||||||
state: "selected", neighborState: "active", unselectedState: "inactive" }],
|
state: "selected", neighborState: "active", unselectedState: "inactive" }],
|
||||||
plugins: [{key: "edge-bundling", type: "edge-bundling"},{
|
plugins: [{
|
||||||
key: "toolbar",
|
key: "toolbar",
|
||||||
type: "toolbar",
|
type: "toolbar",
|
||||||
position: "top-left",
|
position: "top-left",
|
||||||
@ -936,6 +943,9 @@ Graph Console
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
if (layoutCfg) opts.layout = layoutCfg;
|
if (layoutCfg) opts.layout = layoutCfg;
|
||||||
|
if (g6data.edges.length <= BUNDLE_MAX_EDGES) {
|
||||||
|
opts.plugins.unshift({ key: "edge-bundling", type: "edge-bundling" });
|
||||||
|
}
|
||||||
g6graph = new G6.Graph(opts);
|
g6graph = new G6.Graph(opts);
|
||||||
// Re-attached on every instance creation (layout/animation switches
|
// Re-attached on every instance creation (layout/animation switches
|
||||||
// destroy and recreate the graph).
|
// destroy and recreate the graph).
|
||||||
@ -1070,9 +1080,14 @@ Graph Console
|
|||||||
? " — query filter: " + data.nodes.length + " of "
|
? " — query filter: " + data.nodes.length + " of "
|
||||||
+ lastGraphData.nodes.length + " nodes"
|
+ lastGraphData.nodes.length + " nodes"
|
||||||
: "";
|
: "";
|
||||||
if (shown.nodes.length > RENDER_GUARD_NODES && !renderForced) {
|
const safeMode = new URLSearchParams(location.search).has("safe");
|
||||||
|
if ((safeMode
|
||||||
|
|| shown.nodes.length > RENDER_GUARD_NODES
|
||||||
|
|| shown.edges.length > RENDER_GUARD_EDGES) && !renderForced) {
|
||||||
graphViewStatus.textContent =
|
graphViewStatus.textContent =
|
||||||
graphStatusText(data) + filterNote + " — too large to render automatically";
|
graphStatusText(data) + filterNote
|
||||||
|
+ (safeMode ? " — safe mode (?safe): auto-render off"
|
||||||
|
: " — too large to render automatically");
|
||||||
if (anywayBtn) anywayBtn.style.display = "inline";
|
if (anywayBtn) anywayBtn.style.display = "inline";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user