From d1d2294cf50ac5ca864d291b6ba7ff4cbcbb9e10 Mon Sep 17 00:00:00 2001 From: el-rabies Date: Fri, 27 Mar 2026 15:01:56 -0400 Subject: [PATCH 1/5] fix: Resolved NullInjector issue what was leading to console errors and tooltip visual bugs --- .../src/app/pipes/authorship-date-format.pipe.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/projects/pathway-browser/src/app/pipes/authorship-date-format.pipe.ts b/projects/pathway-browser/src/app/pipes/authorship-date-format.pipe.ts index 168e15d..6cc9528 100644 --- a/projects/pathway-browser/src/app/pipes/authorship-date-format.pipe.ts +++ b/projects/pathway-browser/src/app/pipes/authorship-date-format.pipe.ts @@ -1,17 +1,21 @@ import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core'; -import {DatePipe} from "@angular/common"; +import {formatDate} from "@angular/common"; @Pipe({ name: 'authorshipDateFormat', standalone: true }) export class AuthorshipDateFormatPipe implements PipeTransform { - constructor(private datePipe: DatePipe, @Inject(LOCALE_ID) private locale: string) { + constructor(@Inject(LOCALE_ID) private locale: string) { } transform(dateTime: string) { if (!dateTime) return; - return this.datePipe.transform(dateTime); + try { + return formatDate(dateTime, 'mediumDate', this.locale); + } catch { + return; + } } } From 6ce57f0940a3d080ed287177fd6b410099292bda Mon Sep 17 00:00:00 2001 From: el-rabies Date: Fri, 27 Mar 2026 16:08:21 -0400 Subject: [PATCH 2/5] fix: Certain diagrams not rendering + assertion issue --- .../src/app/services/diagram.service.ts | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/projects/pathway-browser/src/app/services/diagram.service.ts b/projects/pathway-browser/src/app/services/diagram.service.ts index a3e6ecb..73b4765 100644 --- a/projects/pathway-browser/src/app/services/diagram.service.ts +++ b/projects/pathway-browser/src/app/services/diagram.service.ts @@ -51,6 +51,9 @@ const squaredDist = (pos1: Position, pos2: Position) => { const dist = (pos1: Position, pos2: Position) => Math.sqrt(squaredDist(pos1, pos2)) +const isFinitePoint = (point: Position | undefined | null): point is Position => + !!point && Number.isFinite(point.x) && Number.isFinite(point.y); + const closestToAverage = (positions: Position[]): Position => { const average = avg(positions); let closest = positions[0]; @@ -582,7 +585,7 @@ export class DiagramService { if (equal(from, reactionP) || equal(to, reactionP)) d -= REACTION_RADIUS; if (classes.includes('positive-regulation') || classes.includes('catalysis') || classes.includes('production')) d -= ARROW_MULT * T; // console.assert(d > MIN_DIST, `The edge between reaction: R-HSA-${reaction.reactomeId} and entity: R-HSA-${node.reactomeId} in pathway ${id} has a visible length of ${d} which is shorter than ${MIN_DIST}`) - console.assert(d > MIN_DIST, `${id}\t${diagram.displayName}\t${hasFadeOut}\tR-HSA-${reaction.reactomeId}\tR-HSA-${node.reactomeId}\thttps://release.reactome.org/PathwayBrowser/#/${id}&SEL=R-HSA-${reaction.reactomeId}&FLG=R-HSA-${node.reactomeId}\thttps://reactome-pwp.github.io/PathwayBrowser/${id}?select=${reaction.reactomeId}&flag=${node.reactomeId}`) + console.assert(Math.abs(d) >= MIN_DIST, `${id}\t${diagram.displayName}\t${hasFadeOut}\tR-HSA-${reaction.reactomeId}\tR-HSA-${node.reactomeId}\thttps://release.reactome.org/PathwayBrowser/#/${id}&SEL=R-HSA-${reaction.reactomeId}&FLG=R-HSA-${node.reactomeId}\thttps://reactome-pwp.github.io/PathwayBrowser/${id}?select=${reaction.reactomeId}&flag=${node.reactomeId}`) let replacement, replacedBy; if (connector.isFadeOut) { @@ -595,7 +598,11 @@ export class DiagramService { } if (!connector.isFadeOut) { // First case: same node is used both special and normal context - replacement = node.connectors.find(otherConnector => otherConnector !== connector && otherConnector.isFadeOut && samePoint(idToEdges.get(otherConnector.edgeId)!.position, reaction.position))?.edgeId; + replacement = node.connectors.find(otherConnector => { + if (otherConnector === connector || !otherConnector.isFadeOut) return false; + const otherEdge = idToEdges.get(otherConnector.edgeId); + return samePoint(otherEdge?.position, reaction.position); + })?.edgeId; // console.log("Reaction edge", replacement) // Second case: different nodes are used between special and normal context @@ -603,6 +610,7 @@ export class DiagramService { // console.log("Reaction edge", replacement) } + const relativeSegments = this.getRelativeSegmentsData(relatives); const edge: cytoscape.EdgeDefinition = { data: { id: this.getEdgeId(source, connector, target, edgeIds), @@ -610,8 +618,7 @@ export class DiagramService { source: source.id + '', target: target.id + '', stoichiometry: connector.stoichiometry.value, - weights: relatives.weights.join(" "), - distances: relatives.distances.join(" "), + ...relativeSegments, sourceEndpoint: this.endpoint(sourceP, from), targetEndpoint: this.endpoint(targetP, to), pathway: eventIdToSubPathwayId.get(reaction.reactomeId), @@ -648,6 +655,7 @@ export class DiagramService { // points = addRoundness(from, to, points); const relatives = this.absoluteToRelative(from, to, points); + const relativeSegments = this.getRelativeSegmentsData(relatives); const classes = [...this.linkClassMap.get(link.renderableClass)!]; if (link.isDisease) classes.push('disease'); @@ -660,8 +668,7 @@ export class DiagramService { id: link.id + '', source: link.inputs[0].id + '', target: link.outputs[0].id + '', - weights: relatives.weights.join(" "), - distances: relatives.distances.join(" "), + ...relativeSegments, sourceEndpoint: this.endpoint(sourceP, from), targetEndpoint: this.endpoint(targetP, to), isFadeOut: link.isFadeOut, @@ -746,6 +753,18 @@ export class DiagramService { return `${point.x - source.x} ${point.y - source.y}` } + private getRelativeSegmentsData(relatives: RelativePosition): Partial<{weights: string, distances: string}> { + const weights = relatives.weights.filter(value => Number.isFinite(value)); + const distances = relatives.distances.filter(value => Number.isFinite(value)); + if (weights.length === 0 || distances.length === 0 || weights.length !== distances.length) { + return {}; + } + return { + weights: weights.join(" "), + distances: distances.join(" "), + }; + } + /** * Use Matrix power to convert points from an absolute coordinate system to an edge relative system @@ -761,19 +780,26 @@ export class DiagramService { const relatives: RelativePosition = {distances: [], weights: []}; if (toConvert.length === 0) return relatives; + if (!isFinitePoint(source) || !isFinitePoint(target) || equal(source, target)) return relatives; + const mainVector = array([target.x - source.x, target.y - source.y]); // Edge vector + if (mainVector.x === 0 && mainVector.y === 0) return relatives; const orthoVector = array([-mainVector.y, mainVector.x]) // Perpendicular vector .normalize(); //Normalized to have the distance expressed in pixels https://math.stackexchange.com/a/413235/683621 - let transform = array([ + const transform = array([ [mainVector.x, mainVector.y], [orthoVector.x, orthoVector.y], ]).inv(); // Should always be invertible if the ortho vector is indeed perpendicular for (let coord of toConvert) { + if (!isFinitePoint(coord)) continue; const absolute = array([[coord.x - source.x, coord.y - source.y]]); const relative = absolute.multiply(transform); - relatives.weights.push(relative.get(0, 0)) - relatives.distances.push(relative.get(0, 1)) + const weight = relative.get(0, 0); + const distance = relative.get(0, 1); + if (!Number.isFinite(weight) || !Number.isFinite(distance)) continue; + relatives.weights.push(weight) + relatives.distances.push(distance) } return relatives; } @@ -869,7 +895,8 @@ export class DiagramService { } } -function samePoint(p1: Position, p2: Position) { +function samePoint(p1?: Position | null, p2?: Position | null) { + if (!p1 || !p2) return false; return p1.x === p2.x && p1.y === p2.y } From 34a55741a19ef9fa55fc5090174005210c94fd5d Mon Sep 17 00:00:00 2001 From: el-rabies Date: Mon, 30 Mar 2026 14:08:52 -0400 Subject: [PATCH 3/5] fix: Resolved issue where certain lines weren't being diplayed in pathway diagram --- .../src/app/diagram/diagram.component.ts | 2 +- .../src/app/services/diagram.service.ts | 41 +++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/projects/pathway-browser/src/app/diagram/diagram.component.ts b/projects/pathway-browser/src/app/diagram/diagram.component.ts index a838fa1..c0b33e8 100644 --- a/projects/pathway-browser/src/app/diagram/diagram.component.ts +++ b/projects/pathway-browser/src/app/diagram/diagram.component.ts @@ -678,7 +678,7 @@ export class DiagramComponent implements AfterViewInit, OnDestroy { cy.batch(() => { this.setSubPathwayVisibility(true, cy); cy.elements().removeClass('flag'); - cy.edges('![?color]').style({'underlay-opacity': 0}) + cy.edges().not('[?color]').style({'underlay-opacity': 0}) }) return cy.collection() diff --git a/projects/pathway-browser/src/app/services/diagram.service.ts b/projects/pathway-browser/src/app/services/diagram.service.ts index 73b4765..9c248ca 100644 --- a/projects/pathway-browser/src/app/services/diagram.service.ts +++ b/projects/pathway-browser/src/app/services/diagram.service.ts @@ -565,8 +565,13 @@ export class DiagramService { this.addEdgeInfo(reaction, points, 'forward', targetP); let [from, to] = [points.shift()!, points.pop()!] - from = from ?? nodeP; // Quick fix to avoid problem with reaction without visible outputs like R-HSA-2424252 in R-HSA-1474244 - to = to ?? reactionP; // Quick fix to avoid problem with reaction without visible outputs like R-HSA-2424252 in R-HSA-1474244 + // Keep fallback direction aligned with edge source/target for connectors that have missing segment endpoints. + from = from ?? sourceP; + to = to ?? targetP; + if (equal(from, to)) { + from = sourceP; + to = targetP; + } if (connector.type === 'CATALYST' && connector.endShape) { to = scale(connector.endShape.centre || connector.endShape.c); } @@ -619,8 +624,7 @@ export class DiagramService { target: target.id + '', stoichiometry: connector.stoichiometry.value, ...relativeSegments, - sourceEndpoint: this.endpoint(sourceP, from), - targetEndpoint: this.endpoint(targetP, to), + ...this.getEndpointData(sourceP, targetP, from, to), pathway: eventIdToSubPathwayId.get(reaction.reactomeId), reactomeId: reaction.reactomeId, reactionId: reaction.id, @@ -652,6 +656,10 @@ export class DiagramService { let [from, to] = [points.shift()!, points.pop()!] from = from ?? sourceP; // Quick fix to avoid problem with reaction without visible outputs like R-HSA-2424252 in R-HSA-1474244 to = to ?? targetP; // Quick fix to avoid problem with reaction without visible outputs like R-HSA-2424252 in R-HSA-1474244 + if (equal(from, to)) { + from = sourceP; + to = targetP; + } // points = addRoundness(from, to, points); const relatives = this.absoluteToRelative(from, to, points); @@ -669,8 +677,7 @@ export class DiagramService { source: link.inputs[0].id + '', target: link.outputs[0].id + '', ...relativeSegments, - sourceEndpoint: this.endpoint(sourceP, from), - targetEndpoint: this.endpoint(targetP, to), + ...this.getEndpointData(sourceP, targetP, from, to), isFadeOut: link.isFadeOut, isBackground: isBackground }, @@ -749,8 +756,26 @@ export class DiagramService { } } - private endpoint(source: Position, point: Position): string { - return `${point.x - source.x} ${point.y - source.y}` + private endpoint(source: Position, point: Position): string | undefined { + const dx = point.x - source.x; + const dy = point.y - source.y; + if (!Number.isFinite(dx) || !Number.isFinite(dy)) return undefined; + return `${dx} ${dy}` + } + + private getEndpointData(source: Position, target: Position, from: Position, to: Position): Partial<{ sourceEndpoint: string, targetEndpoint: string }> { + if (!isFinitePoint(source) || !isFinitePoint(target) || !isFinitePoint(from) || !isFinitePoint(to)) { + return {}; + } + if (equal(source, target)) { + return {}; + } + const sourceEndpoint = this.endpoint(source, from); + const targetEndpoint = this.endpoint(target, to); + if (!sourceEndpoint || !targetEndpoint) { + return {}; + } + return {sourceEndpoint, targetEndpoint}; } private getRelativeSegmentsData(relatives: RelativePosition): Partial<{weights: string, distances: string}> { From e94de71de76593cbc37d03c0b9ed3adc142beeef Mon Sep 17 00:00:00 2001 From: el-rabies Date: Mon, 30 Mar 2026 14:15:30 -0400 Subject: [PATCH 4/5] fix: Removed reaction diagram from the pathbrowser details tab --- .../pathway-browser/src/app/details/details.component.html | 2 +- .../details/tabs/description-tab/description-tab.component.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/projects/pathway-browser/src/app/details/details.component.html b/projects/pathway-browser/src/app/details/details.component.html index c6bffad..72ed7ed 100644 --- a/projects/pathway-browser/src/app/details/details.component.html +++ b/projects/pathway-browser/src/app/details/details.component.html @@ -25,7 +25,7 @@

📋 Reactome Knowledgebase Details

Loading details of {{ state.select() || state.pathwayId() }}

} @else if (obj()) { - + } diff --git a/projects/pathway-browser/src/app/details/tabs/description-tab/description-tab.component.ts b/projects/pathway-browser/src/app/details/tabs/description-tab/description-tab.component.ts index fd5321e..6b4c13d 100644 --- a/projects/pathway-browser/src/app/details/tabs/description-tab/description-tab.component.ts +++ b/projects/pathway-browser/src/app/details/tabs/description-tab/description-tab.component.ts @@ -144,6 +144,7 @@ export class DescriptionTabComponent implements OnDestroy { readonly obj = input.required(); readonly analysisResult = input(); readonly showLocations = input(false); + readonly showReactionDiagram = input(true); static referenceTypeToNameSuffix = new Map([ ["ReferenceMolecule", ""], @@ -325,7 +326,7 @@ export class DescriptionTabComponent implements OnDestroy { label: 'Reaction Diagram', manual: true, template: this.reactionDiagramTemplate$ as Signal>, - isPresent: this.isReaction + isPresent: computed(() => this.isReaction() && this.showReactionDiagram()) }, {key: DataKeys.REFERENCE_ENTITY, label: Labels.EXTERNAL_REFERENCE, manual: true, template: this.referenceTemplate$}, {key: DataKeys.SUMMARISED_ENTITIES, label: Labels.SUMMARISED_ENTITIES}, From 5dc987354e23d1074af8d2b753b83ec3707a679f Mon Sep 17 00:00:00 2001 From: el-rabies Date: Mon, 30 Mar 2026 14:18:00 -0400 Subject: [PATCH 5/5] fix: Home breadcrumb link --- .../src/app/event-hierarchy/event-hierarchy.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/pathway-browser/src/app/event-hierarchy/event-hierarchy.component.html b/projects/pathway-browser/src/app/event-hierarchy/event-hierarchy.component.html index 77e0125..1c87338 100644 --- a/projects/pathway-browser/src/app/event-hierarchy/event-hierarchy.component.html +++ b/projects/pathway-browser/src/app/event-hierarchy/event-hierarchy.component.html @@ -1,6 +1,6 @@