Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/typecheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:

- name: 🔎 Type check
run: pnpm run typecheck
env:
NODE_OPTIONS: --max-old-space-size=8192

- name: 🔎 Check exports
run: pnpm run check-exports
4 changes: 3 additions & 1 deletion apps/webapp/app/components/primitives/Buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonPropsType>(
type LinkPropsType = Pick<
LinkProps,
"to" | "target" | "onClick" | "onMouseDown" | "onMouseEnter" | "onMouseLeave" | "download"
> & { disabled?: boolean } & React.ComponentProps<typeof ButtonContent>;
> & { disabled?: boolean; replace?: boolean } & React.ComponentProps<typeof ButtonContent>;
export const LinkButton = ({
to,
onClick,
Expand All @@ -340,6 +340,7 @@ export const LinkButton = ({
onMouseLeave,
download,
disabled = false,
replace,
...props
}: LinkPropsType) => {
const innerRef = useRef<HTMLAnchorElement>(null);
Expand Down Expand Up @@ -387,6 +388,7 @@ export const LinkButton = ({
<Link
to={to}
ref={innerRef}
replace={replace}
className={cn("group/button block focus-custom", props.fullWidth ? "w-full" : "")}
onClick={onClick}
onMouseDown={onMouseDown}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
} from "@trigger.dev/core/v3";
import type { RuntimeEnvironmentType } from "@trigger.dev/database";
import { motion } from "framer-motion";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { redirect } from "remix-typedjson";
import { MoveToTopIcon } from "~/assets/icons/MoveToTopIcon";
Expand Down Expand Up @@ -140,10 +140,10 @@ const resizableSettings = {
type TraceEvent = NonNullable<SerializeFrom<typeof loader>["trace"]>["events"][0];

type RunsListNavigation = {
runs: Array<{ friendlyId: string }>;
runs: Array<{ friendlyId: string; spanId: string }>;
pagination: { next?: string; previous?: string };
prevPageLastRun?: { friendlyId: string; cursor: string };
nextPageFirstRun?: { friendlyId: string; cursor: string };
prevPageLastRun?: { friendlyId: string; spanId: string; cursor: string };
nextPageFirstRun?: { friendlyId: string; spanId: string; cursor: string };
};

async function getRunsListFromTableState({
Expand Down Expand Up @@ -204,6 +204,7 @@ async function getRunsListFromTableState({
if (prevPageResult.runs.length > 0) {
runsList.prevPageLastRun = {
friendlyId: prevPageResult.runs[0].friendlyId,
spanId: prevPageResult.runs[0].spanId,
cursor: currentPageResult.pagination.previous,
};
}
Expand All @@ -222,6 +223,7 @@ async function getRunsListFromTableState({
if (nextPageResult.runs.length > 0) {
runsList.nextPageFirstRun = {
friendlyId: nextPageResult.runs[0].friendlyId,
spanId: nextPageResult.runs[0].spanId,
cursor: currentPageResult.pagination.next,
};
}
Expand Down Expand Up @@ -296,7 +298,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
type LoaderData = SerializeFrom<typeof loader>;

export default function Page() {
const { run, trace, resizable, maximumLiveReloadingSetting, runsList } = useLoaderData<typeof loader>();
const { run, trace, maximumLiveReloadingSetting, runsList } = useLoaderData<typeof loader>();
const organization = useOrganization();
const project = useProject();
const environment = useEnvironment();
Expand All @@ -308,8 +310,10 @@ export default function Page() {
const tableState = decodeURIComponent(value("tableState") ?? "");
const tableStateSearchParams = new URLSearchParams(tableState);
const filters = getRunFiltersFromSearchParams(tableStateSearchParams);
const tabParam = value("tab") ?? undefined;
const spanParam = value("span") ?? undefined;

const [previousRunPath, nextRunPath] = useAdjacentRunPaths({organization, project, environment, tableState, run, runsList});
const [previousRunPath, nextRunPath] = useAdjacentRunPaths({organization, project, environment, tableState, run, runsList, tabParam, useSpan: !!spanParam});

return (
<>
Expand All @@ -320,7 +324,7 @@ export default function Page() {
text: "Runs",
}}
title={<>
<CopyableText value={run.friendlyId} variant="text-below" className="font-mono"/>
<CopyableText value={run.friendlyId} variant="text-below" className="font-mono px-0 py-0 pb-[2px]"/>
{tableState && (<div className="flex">
<PreviousRunButton to={previousRunPath} />
<NextRunButton to={nextRunPath} />
Expand Down Expand Up @@ -940,7 +944,6 @@ function TimelineView({
scale,
rootSpanStatus,
rootStartedAt,
parentRef,
timelineScrollRef,
virtualizer,
events,
Expand Down Expand Up @@ -1127,7 +1130,8 @@ function TimelineView({
"-ml-[0.5px] h-[0.5625rem] w-px rounded-none",
eventBackgroundClassName(node.data)
)}
layoutId={`${node.id}-${event.name}`}
layoutId={node.data.isPartial ? `${node.id}-${event.name}` : undefined}
animate={!node.data.isPartial ? false : undefined}
/>
)}
</Timeline.Point>
Expand All @@ -1145,7 +1149,8 @@ function TimelineView({
"-ml-[0.1562rem] size-[0.3125rem] rounded-full border bg-background-bright",
eventBorderClassName(node.data)
)}
layoutId={`${node.id}-${event.name}`}
layoutId={node.data.isPartial ? `${node.id}-${event.name}` : undefined}
animate={!node.data.isPartial ? false : undefined}
/>
)}
</Timeline.Point>
Expand All @@ -1164,7 +1169,8 @@ function TimelineView({
>
<motion.div
className={cn("h-px w-full", eventBackgroundClassName(node.data))}
layoutId={`mark-${node.id}`}
layoutId={node.data.isPartial ? `mark-${node.id}` : undefined}
animate={!node.data.isPartial ? false : undefined}
/>
</Timeline.Span>
) : null}
Expand Down Expand Up @@ -1201,7 +1207,8 @@ function TimelineView({
"-ml-0.5 size-3 rounded-full border-2 border-background-bright",
eventBackgroundClassName(node.data)
)}
layoutId={node.id}
layoutId={node.data.isPartial ? node.id : undefined}
animate={!node.data.isPartial ? false : undefined}
/>
)}
</Timeline.Point>
Expand Down Expand Up @@ -1444,7 +1451,8 @@ function SpanWithDuration({
fadeLeft ? "rounded-r-sm bg-gradient-to-r from-black/50 to-transparent" : "rounded-sm"
)}
style={{ backgroundSize: "20px 100%", backgroundRepeat: "no-repeat" }}
layoutId={node.id}
layoutId={node.data.isPartial ? node.id : undefined}
animate={!node.data.isPartial ? false : undefined}
>
{node.data.isPartial && (
<div
Expand All @@ -1457,10 +1465,12 @@ function SpanWithDuration({
"sticky left-0 z-10 transition-opacity group-hover:opacity-100",
!showDuration && "opacity-0"
)}
animate={!node.data.isPartial ? false : undefined}
>
<motion.div
className="whitespace-nowrap rounded-sm px-1 py-0.5 text-xxs text-text-bright text-shadow-custom"
layout="position"
layout={node.data.isPartial ? "position" : undefined}
animate={!node.data.isPartial ? false : undefined}
>
{formatDurationMilliseconds(props.durationMs, {
style: "short",
Expand Down Expand Up @@ -1543,12 +1553,11 @@ function KeyboardShortcuts({
expandAllBelowDepth,
collapseAllBelowDepth,
toggleExpandLevel,
setShowDurations,
}: {
expandAllBelowDepth: (depth: number) => void;
collapseAllBelowDepth: (depth: number) => void;
toggleExpandLevel: (depth: number) => void;
setShowDurations: (show: (show: boolean) => boolean) => void;
setShowDurations?: (show: (show: boolean) => boolean) => void;
}) {
return (
<>
Expand Down Expand Up @@ -1666,63 +1675,77 @@ function useAdjacentRunPaths({
tableState,
run,
runsList,
tabParam,
useSpan
}: {
organization: { slug: string };
project: { slug: string };
environment: { slug: string };
tableState: string;
run: { friendlyId: string };
run: { friendlyId: string, spanId: string };
runsList: RunsListNavigation | null;
tabParam?: string;
useSpan?: boolean;
}): [string | null, string | null] {
return useMemo(() => {
if (!runsList || runsList.runs.length === 0) {
return [null, null];
}

const currentIndex = runsList.runs.findIndex((r) => r.friendlyId === run.friendlyId);

if (currentIndex === -1) {
return [null, null];
}
if (!runsList || runsList.runs.length === 0) {
return [null, null];
}

// Determine previous run: use prevPageLastRun if at first position, otherwise use previous run in list
let previousRun: { friendlyId: string } | null = null;
const previousRunTableState = new URLSearchParams(tableState);
if (currentIndex > 0) {
previousRun = runsList.runs[currentIndex - 1];
} else if (runsList.prevPageLastRun) {
previousRun = runsList.prevPageLastRun;
// Update tableState with the new cursor for the previous page
previousRunTableState.set("cursor", runsList.prevPageLastRun.cursor);
previousRunTableState.set("direction", "backward");
}
const currentIndex = runsList.runs.findIndex((r) => r.friendlyId === run.friendlyId);

if (currentIndex === -1) {
return [null, null];
}

// Determine next run: use nextPageFirstRun if at last position, otherwise use next run in list
let nextRun: { friendlyId: string } | null = null;
const nextRunTableState = new URLSearchParams(tableState);
if (currentIndex < runsList.runs.length - 1) {
nextRun = runsList.runs[currentIndex + 1];
} else if (runsList.nextPageFirstRun) {
nextRun = runsList.nextPageFirstRun;
// Update tableState with the new cursor for the next page
nextRunTableState.set("cursor", runsList.nextPageFirstRun.cursor);
nextRunTableState.set("direction", "forward");
}
// Determine previous run: use prevPageLastRun if at first position, otherwise use previous run in list
let previousRun: { friendlyId: string; spanId: string } | null = null;
const previousRunTableState = new URLSearchParams(tableState);
if (currentIndex > 0) {
previousRun = runsList.runs[currentIndex - 1];
} else if (runsList.prevPageLastRun) {
previousRun = runsList.prevPageLastRun;
// Update tableState with the new cursor for the previous page
previousRunTableState.set("cursor", runsList.prevPageLastRun.cursor);
previousRunTableState.set("direction", "backward");
}

const previousURLSearchParams = new URLSearchParams();
previousURLSearchParams.set("tableState", previousRunTableState.toString());
const previousRunPath = previousRun
? v3RunPath(organization, project, environment, previousRun, previousURLSearchParams)
: null;
// Determine next run: use nextPageFirstRun if at last position, otherwise use next run in list
let nextRun: { friendlyId: string; spanId: string } | null = null;
const nextRunTableState = new URLSearchParams(tableState);
if (currentIndex < runsList.runs.length - 1) {
nextRun = runsList.runs[currentIndex + 1];
} else if (runsList.nextPageFirstRun) {
nextRun = runsList.nextPageFirstRun;
// Update tableState with the new cursor for the next page
nextRunTableState.set("cursor", runsList.nextPageFirstRun.cursor);
nextRunTableState.set("direction", "forward");
}

const nextURLSearchParams = new URLSearchParams();
nextURLSearchParams.set("tableState", nextRunTableState.toString());
const nextRunPath = nextRun
? v3RunPath(organization, project, environment, nextRun, nextURLSearchParams)
: null;
const previousURLSearchParams = new URLSearchParams();
previousURLSearchParams.set("tableState", previousRunTableState.toString());
if (previousRun && useSpan) {
previousURLSearchParams.set("span", previousRun.spanId);
}
if (tabParam && useSpan) {
previousURLSearchParams.set("tab", tabParam);
}
const previousRunPath = previousRun
? v3RunPath(organization, project, environment, previousRun, previousURLSearchParams)
: null;

const nextURLSearchParams = new URLSearchParams();
nextURLSearchParams.set("tableState", nextRunTableState.toString());
if (nextRun && useSpan) {
nextURLSearchParams.set("span", nextRun.spanId);
}
if (tabParam && useSpan) {
nextURLSearchParams.set("tab", tabParam);
}
const nextRunPath = nextRun
? v3RunPath(organization, project, environment, nextRun, nextURLSearchParams)
: null;

return [previousRunPath, nextRunPath];
}, [organization, project, environment, tableState, run.friendlyId, runsList]);
return [previousRunPath, nextRunPath];
}


Expand All @@ -1741,6 +1764,7 @@ function PreviousRunButton({ to }: { to: string | null }) {
shortcut={{ key: "[" }}
tooltip="Previous Run"
disabled={!to}
replace
/>
</div>
);
Expand All @@ -1761,6 +1785,7 @@ function NextRunButton({ to }: { to: string | null }) {
shortcut={{ key: "]" }}
tooltip="Next Run"
disabled={!to}
replace
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
v3CreateBulkActionPath,
v3ProjectPath,
v3TestPath,
v3TestTaskPath,
} from "~/utils/pathBuilder";
import { ListPagination } from "../../components/ListPagination";
import { CreateBulkActionInspector } from "../resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.bulkaction";
Expand Down Expand Up @@ -235,7 +236,13 @@ function RunsList({
list.possibleTasks.length === 0 ? (
<CreateFirstTaskInstructions />
) : (
<RunTaskInstructions />
<RunTaskInstructions
task={
list.filters.tasks.length === 1
? list.possibleTasks.find((t) => t.slug === list.filters.tasks[0])
: undefined
}
/>
)
) : (
<div className={cn("grid h-full max-h-full grid-rows-[auto_1fr] overflow-hidden")}>
Expand Down Expand Up @@ -339,7 +346,7 @@ function CreateFirstTaskInstructions() {
);
}

function RunTaskInstructions() {
function RunTaskInstructions({ task }: { task?: { slug: string } }) {
const organization = useOrganization();
const project = useProject();
const environment = useEnvironment();
Expand All @@ -352,7 +359,11 @@ function RunTaskInstructions() {
Perform a test run with a payload directly from the dashboard.
</Paragraph>
<LinkButton
to={v3TestPath(organization, project, environment)}
to={
task
? v3TestTaskPath(organization, project, environment, { taskIdentifier: task.slug })
: v3TestPath(organization, project, environment)
}
variant="secondary/medium"
LeadingIcon={BeakerIcon}
leadingIconClassName="text-lime-500"
Expand Down