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:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user