From 438d8bc7afcaa79c2da9000e9ef230fb37e1db0d Mon Sep 17 00:00:00 2001 From: KaifAhmad1 Date: Mon, 27 Apr 2026 22:21:41 +0530 Subject: [PATCH] fix(explorer): address PR #503 review findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extract ENTITY_SHAPE_ALIASES and classifyEntityShape into a shared graphEntityShape.ts utility — resolveEntityShape was duplicated with divergent signatures in useLoadGraph.ts and graphSceneState.ts; both now import from one place so aliases can never drift - graphSceneState.resolveEntityShape falls back to classifyEntityShape for nodes created programmatically that bypass useLoadGraph - Fix graphTheme.ts indentation around fullGraphStructure, fullGraphStructureLayer, and interaction — closing braces were at wrong indent levels making the nesting visually misleading - Add comment on fullGraphStructureLayer.mode explaining it is intentionally "off" as a staged-rollout gate (flip to "auto" to enable cross-community canvas curve rendering) Co-authored-by: Mohd Kaif <98801504+KaifAhmad1@users.noreply.github.com> Co-authored-by: Zohaib Hassnain <109234410+ZohaibHassan16@users.noreply.github.com> --- .../GraphWorkspace/graphEntityShape.ts | 34 +++++++++++++++++++ .../GraphWorkspace/graphSceneState.ts | 11 +++++- .../workspaces/GraphWorkspace/graphTheme.ts | 32 +++++++++-------- .../workspaces/GraphWorkspace/useLoadGraph.ts | 25 +++----------- 4 files changed, 65 insertions(+), 37 deletions(-) create mode 100644 explorer/src/workspaces/GraphWorkspace/graphEntityShape.ts diff --git a/explorer/src/workspaces/GraphWorkspace/graphEntityShape.ts b/explorer/src/workspaces/GraphWorkspace/graphEntityShape.ts new file mode 100644 index 00000000..f2cfc45e --- /dev/null +++ b/explorer/src/workspaces/GraphWorkspace/graphEntityShape.ts @@ -0,0 +1,34 @@ +import type { GraphEntityShapeVariant } from "./graphTheme"; + +export const ENTITY_SHAPE_ALIASES: Array<[GraphEntityShapeVariant, RegExp]> = [ + ["biomolecule", /\b(gene|protein|enzyme|receptor|target|transcript|rna|dna|mirna|biomolecule|peptide)\b/i], + ["condition", /\b(disease|condition|phenotype|symptom|disorder|syndrome|diagnosis|pathology|trait)\b/i], + ["compound", /\b(drug|chemical|compound|metabolite|molecule|small[_\s-]?molecule|ligand|therapeutic|medication|substance)\b/i], + ["process", /\b(pathway|process|mechanism|function|ontology|biological[_\s-]?process|cellular[_\s-]?process|program|module)\b/i], +]; + +export function classifyEntityShape( + nodeType?: string, + semanticGroup?: string, + content?: string, + properties?: Record, +): GraphEntityShapeVariant { + const values = [ + nodeType, + semanticGroup, + content, + String(properties?.type ?? ""), + String(properties?.category ?? ""), + String(properties?.label ?? ""), + ] + .filter((value) => typeof value === "string" && value.trim().length > 0) + .join(" "); + + for (const [shape, pattern] of ENTITY_SHAPE_ALIASES) { + if (pattern.test(values)) { + return shape; + } + } + + return "entity"; +} diff --git a/explorer/src/workspaces/GraphWorkspace/graphSceneState.ts b/explorer/src/workspaces/GraphWorkspace/graphSceneState.ts index d2ef696f..698254c7 100644 --- a/explorer/src/workspaces/GraphWorkspace/graphSceneState.ts +++ b/explorer/src/workspaces/GraphWorkspace/graphSceneState.ts @@ -19,6 +19,7 @@ import { withAlpha, zoomTierAtLeast, } from "./graphTheme"; +import { classifyEntityShape } from "./graphEntityShape"; import { computeGraphAnalyticsBase } from "./graphAnalytics"; import type { GraphDisplayMeta, @@ -1090,7 +1091,15 @@ export function resolveEntityShape(attrs: NodeAttributes): GraphEntityShapeVaria return "community"; } - return attrs.entityShape || "entity"; + // Prefer the shape stamped at load time; fall back to classifyEntityShape for + // nodes created programmatically that bypassed useLoadGraph. + return attrs.entityShape + || classifyEntityShape( + attrs.nodeType, + attrs.semanticGroup, + attrs.content, + attrs.properties as Record | undefined, + ); } export function resolveEdgeVariant(state: GraphEdgeVisualState, attrs: EdgeAttributes): GraphEdgeVariant { diff --git a/explorer/src/workspaces/GraphWorkspace/graphTheme.ts b/explorer/src/workspaces/GraphWorkspace/graphTheme.ts index dfa0ebb8..e6677244 100644 --- a/explorer/src/workspaces/GraphWorkspace/graphTheme.ts +++ b/explorer/src/workspaces/GraphWorkspace/graphTheme.ts @@ -832,8 +832,10 @@ export const GRAPH_THEME: GraphTheme = { bridgeMaxSize: 0.7, structureEdgeAlpha: 0.12, inspectionEdgeAlpha: 0.1, - }, - fullGraphStructureLayer: { + }, + // Staged rollout — set mode to "auto" to enable cross-community curve rendering. + // Currently "off" so the canvas overlay layer is inactive in production. + fullGraphStructureLayer: { mode: "off", minimumLiteralEdges: 24, minimumCurves: 8, @@ -843,19 +845,19 @@ export const GRAPH_THEME: GraphTheme = { bridgeLineWidth: 0.9, backboneLineWidth: 0.62, curveStrength: 0.12, - }, - }, - interaction: { - localContextAlpha: 0.32, - hoverContextAlpha: 0.32, - selectedEdgeAlpha: 0.6, - pathEdgeAlpha: 0.76, - localContextMaxSize: 0.6, - selectedEdgeMaxSize: 1.0, - pathEdgeMaxSize: 1.4, - pathOverlayAlpha: 0.16, - }, - overlays: { + }, + }, + interaction: { + localContextAlpha: 0.32, + hoverContextAlpha: 0.32, + selectedEdgeAlpha: 0.6, + pathEdgeAlpha: 0.76, + localContextMaxSize: 0.6, + selectedEdgeMaxSize: 1.0, + pathEdgeMaxSize: 1.4, + pathOverlayAlpha: 0.16, + }, + overlays: { hoverGlowAlpha: 0.18, pathGlowAlpha: 0.16, glowRadiusMultiplier: 4.8, diff --git a/explorer/src/workspaces/GraphWorkspace/useLoadGraph.ts b/explorer/src/workspaces/GraphWorkspace/useLoadGraph.ts index 02214e85..f23c563a 100644 --- a/explorer/src/workspaces/GraphWorkspace/useLoadGraph.ts +++ b/explorer/src/workspaces/GraphWorkspace/useLoadGraph.ts @@ -14,6 +14,7 @@ import { type GraphLabelVisibilityPolicy, type GraphNodeShapeVariant, } from "./graphTheme"; +import { classifyEntityShape } from "./graphEntityShape"; import { createGraphLoadProgress } from "./graphLoading"; import type { GraphLoadProgress, GraphLoadSummary } from "./types"; @@ -30,12 +31,6 @@ const SEMANTIC_COLOR_FIELDS = [ ] as const; const PROVENANCE_KEYS = ["source", "source_url", "pmid", "pmids", "evidence", "provenance", "confidence"] as const; -const ENTITY_SHAPE_ALIASES: Array<[GraphEntityShapeVariant, RegExp]> = [ - ["biomolecule", /\b(gene|protein|enzyme|receptor|target|transcript|rna|dna|mirna|biomolecule|peptide)\b/i], - ["condition", /\b(disease|condition|phenotype|symptom|disorder|syndrome|diagnosis|pathology|trait)\b/i], - ["compound", /\b(drug|chemical|compound|metabolite|molecule|small[_\s-]?molecule|ligand|therapeutic|medication|substance)\b/i], - ["process", /\b(pathway|process|mechanism|function|ontology|biological[_\s-]?process|cellular[_\s-]?process|program|module)\b/i], -]; function getSemanticFieldValue(attributes: NodeAttributes, field: (typeof SEMANTIC_COLOR_FIELDS)[number]): string | null { if (field === "nodeType") { @@ -204,24 +199,12 @@ function getProvenanceCount(properties: Record): number { } function resolveEntityShape(attributes: NodeAttributes, semanticGroup: string): GraphEntityShapeVariant { - const values = [ + return classifyEntityShape( attributes.nodeType, semanticGroup, attributes.content, - String(attributes.properties?.type ?? ""), - String(attributes.properties?.category ?? ""), - String(attributes.properties?.label ?? ""), - ] - .filter((value) => typeof value === "string" && value.trim().length > 0) - .join(" "); - - for (const [shape, pattern] of ENTITY_SHAPE_ALIASES) { - if (pattern.test(values)) { - return shape; - } - } - - return "entity"; + attributes.properties as Record | undefined, + ); } function resolveNodeVariantMetadata(