diff --git a/packages/replay-next/components/console/StackRenderer.tsx b/packages/replay-next/components/console/StackRenderer.tsx
index e1335e552e6..ae9c403729a 100644
--- a/packages/replay-next/components/console/StackRenderer.tsx
+++ b/packages/replay-next/components/console/StackRenderer.tsx
@@ -1,8 +1,7 @@
import { CallStack, Frame, FrameId } from "@replayio/protocol";
-import React, { Suspense, memo, useContext } from "react";
+import { Suspense, memo } from "react";
import Loader from "replay-next/components/Loader";
-import { ReplayClientContext } from "shared/client/ReplayClientContext";
import Source from "./Source";
import styles from "./StackRenderer.module.css";
@@ -26,8 +25,6 @@ function StackRenderer({ frames, stack }: { frames: Frame[]; stack: CallStack })
}
function StackFrameRenderer({ frameId, frames }: { frameId: FrameId; frames: Frame[] }) {
- const client = useContext(ReplayClientContext);
-
const frame = frames.find(frame => frame.frameId === frameId);
if (frame == null) {
return null;
diff --git a/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/DependencyGraph.tsx b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/DependencyGraph.tsx
index 8a20e41b1c5..0271f428c6b 100644
--- a/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/DependencyGraph.tsx
+++ b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/DependencyGraph.tsx
@@ -1,7 +1,10 @@
-import { ExecutionPoint } from "@replayio/protocol";
+import { ExecutionPoint, Location } from "@replayio/protocol";
import { Suspense, useContext } from "react";
import { PanelLoader } from "replay-next/components/PanelLoader";
+import { sourcesByIdCache, sourcesByUrlCache } from "replay-next/src/suspense/SourcesCache";
+import { getSourceToDisplayForUrl } from "replay-next/src/utils/sources";
+import { suspendInParallel } from "replay-next/src/utils/suspense";
import { ReplayClientContext } from "shared/client/ReplayClientContext";
import { DependencyGraphMode } from "shared/client/types";
import { depGraphCache } from "ui/suspense/depGraphCache";
@@ -25,27 +28,48 @@ export function DependencyGraph(props: Props) {
function DependencyGraphSuspends({ mode, point }: Props) {
const replayClient = useContext(ReplayClientContext);
- const depGraphValue = depGraphCache.read(replayClient, point ?? null, mode ?? null);
+ const [depGraphValue, sourcesById, sourcesByUrl] = suspendInParallel(
+ () => depGraphCache.read(replayClient, point ?? null, mode ?? null),
+ () => sourcesByIdCache.read(replayClient),
+ () => sourcesByUrlCache.read(replayClient)
+ );
+
const valueDescending = depGraphValue?.slice().reverse();
return (
- {valueDescending?.map((entry, index) => (
- - {
+ let location: Location | null = null;
+ if ("calleeLocation" in entry && entry.calleeLocation != null) {
+ const source = getSourceToDisplayForUrl(
+ sourcesById,
+ sourcesByUrl,
+ entry.calleeLocation.url
+ );
+ if (source) {
+ location = {
+ ...entry.calleeLocation,
+ sourceId: source.sourceId,
+ };
}
- />
- ))}
+ }
+
+ return (
+
+ );
+ })}
);
}
diff --git a/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/Item.module.css b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/Item.module.css
index 3dc9aa5b63c..4d33da2c674 100644
--- a/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/Item.module.css
+++ b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/Item.module.css
@@ -9,10 +9,6 @@
background-color: var(--theme-selection-background-hover);
cursor: pointer;
}
-.Item[data-selected] {
- background-color: var(--theme-selection-background);
- color: white;
-}
.Item[data-disabled] {
color: var(--color-dim);
}
@@ -20,6 +16,11 @@
background-color: transparent;
cursor: default;
}
+.Item[data-selected],
+.Item[data-selected]:hover {
+ background-color: var(--theme-selection-background);
+ color: white;
+}
.NameColumn {
overflow: hidden;
@@ -41,5 +42,5 @@
text-overflow: ellipsis;
}
.Item[data-selected] .LocationColumn {
- display: none;
+ color: white;
}
diff --git a/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/Item.tsx b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/Item.tsx
index f99a906b964..f2c6be45a56 100644
--- a/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/Item.tsx
+++ b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/Item.tsx
@@ -1,40 +1,40 @@
-import { TimeStampedPoint } from "@replayio/protocol";
+import { Location, TimeStampedPoint } from "@replayio/protocol";
+import { selectDependencyGraphNode } from "devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/selectDependencyGraphNode";
+import { useIsCurrentItem } from "devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/useIsCurrentItem";
import { getRawSourceURL } from "devtools/client/debugger/src/utils/source";
import { getURL } from "devtools/client/debugger/src/utils/sources-tree";
import { formatTimestamp } from "replay-next/src/utils/time";
-import { seek } from "ui/actions/timeline";
import { useAppDispatch } from "ui/setup/hooks";
import { LocationWithUrl } from "ui/suspense/depGraphCache";
import styles from "./Item.module.css";
export function Item({
- isCurrent,
name,
location,
timeStampedPoint,
}: {
name: string;
- isCurrent?: boolean;
- location: LocationWithUrl | null;
+ location: Location | LocationWithUrl | null;
timeStampedPoint: TimeStampedPoint | null;
}) {
const dispatch = useAppDispatch();
- const onClick = () => {
- timeStampedPoint &&
- dispatch(
- seek({
- executionPoint: timeStampedPoint.point,
- openSource: true,
- time: timeStampedPoint.time,
- })
- );
+ const isCurrent = useIsCurrentItem(timeStampedPoint, location);
+
+ const isDisabled = !location || !timeStampedPoint;
+
+ const onClick = async () => {
+ if (!location || !timeStampedPoint) {
+ return;
+ }
+
+ dispatch(selectDependencyGraphNode(location, timeStampedPoint));
};
let source = null;
- if (location) {
+ if (location && "url" in location) {
source = `${getRawSourceURL(getURL(location).filename)}:${location.line}`;
}
@@ -42,9 +42,8 @@ export function Item({
{name}
{timeStampedPoint && (
diff --git a/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/ReactComponentStack.tsx b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/ReactComponentStack.tsx
index 7887abac527..458fe607e6c 100644
--- a/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/ReactComponentStack.tsx
+++ b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/ReactComponentStack.tsx
@@ -32,16 +32,15 @@ function ReactComponentStackSuspends({ timeStampedPoint }: Props) {
return
No frames to display;
}
- const firstItem = reactStackValue[0];
+ const leafNode = reactStackValue[0];
return (
<>
- {firstItem && (
+ {leafNode && (
)}
{reactStackValue?.map((entry, index) => (
diff --git a/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/selectDependencyGraphNode.ts b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/selectDependencyGraphNode.ts
new file mode 100644
index 00000000000..b10fb7d6547
--- /dev/null
+++ b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/selectDependencyGraphNode.ts
@@ -0,0 +1,27 @@
+import { Location, TimeStampedPoint } from "@replayio/protocol";
+
+import { selectLocation } from "devtools/client/debugger/src/actions/sources";
+import { frameSelected, getThreadContext } from "devtools/client/debugger/src/selectors";
+import { pauseIdCache } from "replay-next/src/suspense/PauseCache";
+import { UIThunkAction } from "ui/actions";
+
+export function selectDependencyGraphNode(
+ location: Location,
+ timeStampedPoint: TimeStampedPoint
+): UIThunkAction {
+ return async (dispatch, getState, { replayClient }) => {
+ const pauseId = await pauseIdCache.readAsync(
+ replayClient,
+ timeStampedPoint.point,
+ timeStampedPoint.time
+ );
+
+ const context = getThreadContext(getState());
+
+ // TODO Is this legit?
+ const frameId = "0";
+
+ dispatch(frameSelected({ cx: context, pauseId, frameId }));
+ dispatch(selectLocation(context, location));
+ };
+}
diff --git a/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/useIsCurrentItem.ts b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/useIsCurrentItem.ts
new file mode 100644
index 00000000000..ad57abafe19
--- /dev/null
+++ b/src/devtools/client/debugger/src/components/SecondaryPanes/DependencyGraph/useIsCurrentItem.ts
@@ -0,0 +1,24 @@
+import { Location, TimeStampedPoint } from "@replayio/protocol";
+
+import { getSelectedFrameId } from "devtools/client/debugger/src/selectors";
+import { comparePosition } from "devtools/client/debugger/src/utils/location";
+import { getPointAndTimeForPauseId } from "replay-next/src/suspense/PauseCache";
+import { getSelectedLocation } from "ui/reducers/sources";
+import { useAppSelector } from "ui/setup/hooks";
+
+export function useIsCurrentItem(
+ timeStampedPoint: TimeStampedPoint | null,
+ location: Location | null
+) {
+ const { pauseId } = useAppSelector(getSelectedFrameId) ?? {};
+ const selectedLocation = useAppSelector(getSelectedLocation);
+
+ const [selectedExecutionPoint] = pauseId ? getPointAndTimeForPauseId(pauseId) : [];
+
+ const isMatchingExecutionPoint =
+ selectedExecutionPoint != null && timeStampedPoint?.point === selectedExecutionPoint;
+ const isMatchingLocation =
+ location && selectedLocation && comparePosition(location, selectedLocation);
+
+ return isMatchingExecutionPoint && isMatchingLocation;
+}
diff --git a/src/devtools/client/debugger/src/utils/location.ts b/src/devtools/client/debugger/src/utils/location.ts
index a9359f13efe..37fe20933fb 100644
--- a/src/devtools/client/debugger/src/utils/location.ts
+++ b/src/devtools/client/debugger/src/utils/location.ts
@@ -4,7 +4,7 @@
import type { Location, SourceLocation } from "@replayio/protocol";
import sortBy from "lodash/sortBy";
-export function comparePosition(a: Location, b: Location) {
+export function comparePosition(a: Partial
, b: Partial) {
return a && b && a.line == b.line && a.column == b.column;
}