supports-edges draw toggle — last row of the graph settings' first section

Off hides supports edges from the canvas: the renderer skips them when
building the shaft/head paths (draw-only — the physics sim still feels
them and detail cards still list them; settings never shift the graph).
Server-persisted with the rest (render-settings.json); sanitizer takes
real booleans only, anything else (missing key, legacy file) loads ON —
today's look. Toggle row styled as the Background-colors row; sanitiser
pinned in render-settings.test.
This commit is contained in:
amosblomqvist
2026-09-29 02:40:07 +03:00
parent 8355fbabdb
commit 6543d4f886
4 changed files with 40 additions and 2 deletions
+4 -1
View File
@@ -1232,11 +1232,14 @@ export class GraphCanvasRenderer {
// Edge shafts + arrowheads: tip lands on the target disc edge. Both
// relation types draw the same arrow for now — the double-shaft
// glyph was rejected (2026-09-15); differentiation is pending a new
// approach. CanvasEdge.type is plumbed for whatever comes next.
// approach. CanvasEdge.type is plumbed for whatever comes next —
// except the supports draw toggle (owner, 2026-09-29): off skips
// the paths entirely (a draw-only hide; the sim still feels them).
const arrowLen = settings.arrowLength;
const shafts = new Path2D();
const heads = new Path2D();
for (const e of data.edges) {
if (!settings.showSupports && e.type === "supports") continue;
if (!isVisible(e.from) || !isVisible(e.to)) continue;
const a = this.nodeById.get(e.from);
const b = this.nodeById.get(e.to);
+17 -1
View File
@@ -51,7 +51,9 @@
*
* Rendering settings (2026-09-13): the gear in the search panel reveals
* five sliders (node size, edge width, arrowhead length, label size, the
* zoom threshold) plus the cluster-labels checkbox. SERVER-persisted
* zoom threshold), the supports-edges draw toggle (2026-09-29: off hides
* supports edges from the canvas — draw-only; physics and detail cards
* still see them), and the cluster-labels checkbox. SERVER-persisted
* (data/render-settings.json via /api/graph/render-settings): loaded
* once after mount, saved debounced on every user change — one source
* of truth shared by every browser. The label-zoom slider is log-scaled
@@ -528,6 +530,20 @@ function GraphSettingsControl({
onChange={(p) =>
changeSettings((cur) => ({ ...cur, labelZoom: fromLadderPosition(p, LABEL_ZOOM_LADDER) }))}
/>
{/* Supports draw toggle (owner, 2026-09-29): hides the single-line
supports edges from the canvas — follows (⟹-meant) stay. Draw-only:
physics + detail cards still see them. Last row of the FIRST
section; the border-t below starts Regions. */}
<label className="flex cursor-pointer items-center justify-between text-[11px] text-muted-foreground">
<span>Supports edges</span>
<input
type="checkbox"
checked={settings.showSupports}
onChange={(e) =>
changeSettings((cur) => ({ ...cur, showSupports: e.target.checked }))}
className="accent-primary"
/>
</label>
{/* Regions: the blurred cluster tints. Sliders + a fixed-set
dropdown for the bitmap resolution (it's a discrete
choice, not a free number). Server-persisted like the
+8
View File
@@ -54,6 +54,11 @@ export interface GraphRenderSettings {
labelZoom: number;
/** Node + hover label font size, WORLD units (scales with camera). */
labelSize: number;
/** Draw `supports` edges at all (owner, 2026-09-29): off hides them
* from the canvas — a DRAW toggle only. The physics sim still feels
* them (settings never shift the graph: camera, one frame), and node
* detail cards still list them. */
showSupports: boolean;
/** Toggle for the cluster region labels (hulls always draw). */
clusterLabels: boolean;
/** Cluster label font size, px on screen (camera-compensated —
@@ -95,6 +100,8 @@ export function sanitizeRenderSettings(raw: unknown): GraphRenderSettings {
arrowLength: legacySizes ? DEFAULT_RENDER_SETTINGS.arrowLength : ladd(r.arrowLength, ARROW_LENGTH_LADDER, DEFAULT_RENDER_SETTINGS.arrowLength),
labelZoom: ladd(r.labelZoom, LABEL_ZOOM_LADDER, DEFAULT_RENDER_SETTINGS.labelZoom),
labelSize: legacySizes ? DEFAULT_RENDER_SETTINGS.labelSize : ladd(r.labelSize, LABEL_SIZE_LADDER, DEFAULT_RENDER_SETTINGS.labelSize),
showSupports:
typeof r.showSupports === "boolean" ? r.showSupports : DEFAULT_RENDER_SETTINGS.showSupports,
clusterLabels:
typeof r.clusterLabels === "boolean" ? r.clusterLabels : DEFAULT_RENDER_SETTINGS.clusterLabels,
clusterLabelSize: ladd(
@@ -262,6 +269,7 @@ export const DEFAULT_RENDER_SETTINGS: GraphRenderSettings = {
arrowLength: 2,
labelZoom: 4,
labelSize: 3,
showSupports: true,
clusterLabels: true,
clusterLabelSize: 14,
regions: DEFAULT_REGION_TUNING,
+11
View File
@@ -118,6 +118,7 @@ describe("sanitizeRenderSettings", () => {
arrowLength: 3,
labelZoom: 2,
labelSize: 4,
showSupports: false,
clusterLabels: false,
clusterLabelSize: 20,
regions: DEFAULT_REGION_TUNING,
@@ -126,6 +127,16 @@ describe("sanitizeRenderSettings", () => {
assert.deepEqual(sanitizeRenderSettings(valid), valid); // every value a ladder member
});
test("showSupports (2026-09-29): boolean-only, default on — the draw toggle", () => {
// A render-only hide for supports edges: anything but a real boolean
// (missing key, legacy file, garbage) loads as ON (today's look); a
// real false round-trips untouched.
assert.equal(sanitizeRenderSettings({}).showSupports, true);
assert.equal(sanitizeRenderSettings({ showSupports: false }).showSupports, false);
assert.equal(sanitizeRenderSettings({ showSupports: "no" }).showSupports, true);
assert.equal(sanitizeRenderSettings({ showSupports: 0 }).showSupports, true);
});
test("the pre-2026-09-13 stored shape (flat, no regions/physics) still loads", () => {
// data/render-settings.json on disk predates the nested sections —
// those loads must get defaults, not crash, not shift anything.