Compare commits

...

34 Commits

Author SHA1 Message Date
James Ritchie 1d58ef97bc Min size for resizable panel on test page 2024-08-29 17:47:04 +01:00
James Ritchie 4510fc0ed0 Inline code blocks behave nicer when text wraps 2024-08-24 12:10:08 +01:00
James Ritchie 3952421f4a Updated resizable storybook 2024-08-23 18:08:06 +01:00
James Ritchie 1d3cd1d8e9 Added isStaticAtRest prop to resizable panel 2024-08-23 17:47:24 +01:00
James Ritchie 442e3b466e styled the handle focus state 2024-08-23 17:47:06 +01:00
James Ritchie 1e68485298 Small padding fix 2024-08-23 16:38:17 +01:00
James Ritchie bb93adca35 Callout if runs don’t start right away now has some top margin 2024-08-23 16:33:08 +01:00
James Ritchie d36f07b79c Updated to the latest version: react-window-splitter 2024-08-23 16:18:49 +01:00
James Ritchie 075623007f Merge remote-tracking branch 'origin/main' into react-window-splitter
# Conflicts:
#	.changeset/hungry-sloths-promise.md
#	apps/webapp/app/models/runtimeEnvironment.server.ts
#	apps/webapp/app/presenters/v3/ConcurrencyPresenter.server.ts
#	apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.schedules/route.tsx
#	apps/webapp/app/v3/services/completeAttempt.server.ts
#	apps/webapp/app/v3/services/createCheckpoint.server.ts
#	apps/webapp/app/v3/services/resumeBatchRun.server.ts
#	apps/webapp/app/v3/services/resumeTaskDependency.server.ts
#	apps/webapp/app/v3/taskStatus.ts
#	docs/troubleshooting.mdx
#	integrations/airtable/CHANGELOG.md
#	integrations/airtable/package.json
#	integrations/github/CHANGELOG.md
#	integrations/github/package.json
#	integrations/linear/CHANGELOG.md
#	integrations/linear/package.json
#	integrations/openai/CHANGELOG.md
#	integrations/openai/package.json
#	integrations/plain/CHANGELOG.md
#	integrations/plain/package.json
#	integrations/replicate/CHANGELOG.md
#	integrations/replicate/package.json
#	integrations/resend/CHANGELOG.md
#	integrations/resend/package.json
#	integrations/sendgrid/CHANGELOG.md
#	integrations/sendgrid/package.json
#	integrations/shopify/CHANGELOG.md
#	integrations/shopify/package.json
#	integrations/slack/CHANGELOG.md
#	integrations/slack/package.json
#	integrations/stripe/CHANGELOG.md
#	integrations/stripe/package.json
#	integrations/supabase/CHANGELOG.md
#	integrations/supabase/package.json
#	integrations/typeform/CHANGELOG.md
#	integrations/typeform/package.json
#	packages/astro/CHANGELOG.md
#	packages/astro/package.json
#	packages/cli-v3/CHANGELOG.md
#	packages/cli-v3/package.json
#	packages/cli-v3/src/entryPoints/deploy-run-controller.ts
#	packages/cli/CHANGELOG.md
#	packages/cli/package.json
#	packages/core-apps/CHANGELOG.md
#	packages/core-apps/package.json
#	packages/core-backend/CHANGELOG.md
#	packages/core-backend/package.json
#	packages/core/CHANGELOG.md
#	packages/core/package.json
#	packages/eslint-plugin/CHANGELOG.md
#	packages/eslint-plugin/package.json
#	packages/express/CHANGELOG.md
#	packages/express/package.json
#	packages/hono/CHANGELOG.md
#	packages/hono/package.json
#	packages/integration-kit/CHANGELOG.md
#	packages/integration-kit/package.json
#	packages/nestjs/CHANGELOG.md
#	packages/nestjs/package.json
#	packages/nextjs/CHANGELOG.md
#	packages/nextjs/package.json
#	packages/otlp-importer/CHANGELOG.md
#	packages/otlp-importer/package.json
#	packages/react/CHANGELOG.md
#	packages/react/package.json
#	packages/remix/CHANGELOG.md
#	packages/remix/package.json
#	packages/sveltekit/CHANGELOG.md
#	packages/sveltekit/package.json
#	packages/testing/CHANGELOG.md
#	packages/testing/package.json
#	packages/trigger-sdk/CHANGELOG.md
#	packages/trigger-sdk/package.json
#	packages/yalt/CHANGELOG.md
#	packages/yalt/package.json
#	pnpm-lock.yaml
2024-08-23 16:09:08 +01:00
Matt Aitken 2416f426b8 Latest version of react-window-splitter (0.2.5) 2024-08-18 12:46:24 +01:00
Matt Aitken 4027045f32 Schedules page 2024-08-15 11:11:11 +01:00
Matt Aitken c3298f974f Test page 2024-08-15 11:11:08 +01:00
Matt Aitken 68945a4e4b Deployments page 2024-08-15 10:56:46 +01:00
Matt Aitken 194c56aa62 Set sensible defaults for the run page 2024-08-15 10:34:39 +01:00
Matt Aitken d3810fc5bc The latest react-window-splitter fixes the ESM issues 2024-08-15 10:34:31 +01:00
James Ritchie fa24ae69a6 Added a note to use batchTrigger() instead of trigger() 2024-08-15 10:10:53 +01:00
nicktrn 95b3754685 Release 3.0.0-beta.53 2024-08-15 10:10:53 +01:00
github-actions[bot] 31da143be0 chore: Update version for release (beta) (#1256)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-15 10:10:53 +01:00
James Ritchie cd10d47ae0 Added a description panel to the Concurrency admin page 2024-08-15 10:10:53 +01:00
James Ritchie 494e1eadaa Fix for schedule page not scrolling 2024-08-15 10:10:53 +01:00
nicktrn cbaaa3bb1f Fix restored container names 2024-08-15 10:10:53 +01:00
Matt Aitken 26e872aa54 Include the old message data when replacing, so we get the projectId etc. 2024-08-15 10:10:53 +01:00
James Ritchie ff435fb8ad Added depenenciesToBundle guide to bundle all packages 2024-08-15 10:10:53 +01:00
Matt Aitken 41c551de3d Added messageReplaced to concurrency tracking (when freezing) 2024-08-15 10:10:53 +01:00
Matt Aitken 6b0c37ae6d Call subscriber.messageDequeued in dequeueMessageInSharedQueue 2024-08-15 10:10:53 +01:00
Matt Aitken 0e990fd2b5 Added more logging to TaskRunConcurrencyTracker and some more try/catches 2024-08-15 10:10:52 +01:00
nicktrn 4148921dff v3: fix unfreezable state crashes for runs with multiple waits (#1253)
* support named capture groups

* write crash errors to attempt.error

* make restored pod names unique per checkpoint

* use last eight characters of checkpoint id instead

* add more chaos monkey env vars

* Ignore unfreezable states

* prevent excessive queue config parsing errors

* handle dependency resume edge case

* better entry point logging

* ignore checkpoint cancellation timeouts

* add missing idempotency keys to wait for dep replays

* remove checkpoints between attempts

* fix retry container names on kubernetes

* add changeset

* fix types

* bring back internal duration timers
2024-08-15 10:10:52 +01:00
Matt Aitken 2c80e9a715 Concurrency page and more accurate tracking (#1252)
* Initial TaskRunConcurrencyTracker implementation

* MARQS calls a subscriber to events

* When enqueuing add the extra required metadata

* Track concurrency per environment for tasks too

* Admin page for global concurrency

* Use the new concurrency tracker on the tasks page

* Useful performance test task

* getAllTaskIdentifiers()

* New page for concurrency

* BackgroundWorkerTask index for quick lookup of task identifiers

* Added a way to get concurrency for environments

* Added upgrade/request more concurrency button

* Queued task column working

* Use defer and suspense

* Added queue column to the concurrency environments table

* Some comments added for clarity

* Fixed bad log message

* Sidemenu: move lower and rename to “Concurrency limits”

* Only show the environments, not tasks. Renamed to “Concurrency limits”
2024-08-15 10:10:52 +01:00
James Ritchie 37fd0882ae Fixed v2 usage meter displaying when on paid plan (#1255)
* Fixed v2 usage meter displaying when on paid plan

* Show the free usage panel only for v3 projects
2024-08-15 10:10:52 +01:00
nicktrn a8f725d643 Increase indexing memory limit 2024-08-15 10:10:52 +01:00
Matt Aitken b46698b820 Resizable storybook page 2024-08-12 16:40:59 +01:00
Matt Aitken 1d12f8a619 Use the cookie package 2024-08-12 15:15:23 +01:00
Matt Aitken 8afd4c4d35 WIP with new resizable panels and SSR 2024-08-12 15:04:20 +01:00
Matt Aitken 40b8aeccea WIP on using react-window-splitter 2024-08-12 12:15:37 +01:00
15 changed files with 1655 additions and 114 deletions
+5
View File
@@ -580,6 +580,11 @@ class TaskCoordinator {
return;
}
if (message.version === "v2") {
completeWithoutCheckpoint(true);
return;
}
const { canCheckpoint, willSimulate } = await this.#checkpointer.init();
const willCheckpointAndRestore = canCheckpoint || willSimulate;
@@ -1,7 +1,7 @@
import { cn } from "~/utils/cn";
const inlineCode =
"px-1 py-0.5 rounded border border-charcoal-700 bg-charcoal-800 text-charcoal-200 font-mono";
"px-1 py-0.5 rounded border border-charcoal-700 bg-charcoal-800 text-charcoal-200 font-mono text-wrap";
const variants = {
"extra-extra-small": "text-xxs",
@@ -1,32 +1,32 @@
"use client";
import * as ResizablePrimitive from "react-resizable-panels";
import React from "react";
import { PanelGroup, Panel, PanelResizer } from "react-window-splitter";
import { cn } from "~/utils/cn";
const ResizablePanelGroup = ({
className,
...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
<ResizablePrimitive.PanelGroup
const ResizablePanelGroup = ({ className, ...props }: React.ComponentProps<typeof PanelGroup>) => (
<PanelGroup
className={cn("flex w-full data-[panel-group-direction=vertical]:flex-col", className)}
autosaveStrategy="cookie"
{...props}
/>
);
const ResizablePanel = ResizablePrimitive.Panel;
const ResizablePanel = Panel;
const ResizableHandle = ({
withHandle,
withHandle = true,
className,
...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
}: React.ComponentProps<typeof PanelResizer> & {
withHandle?: boolean;
}) => (
<ResizablePrimitive.PanelResizeHandle
<PanelResizer
className={cn(
"focus-visible:ring-ring group relative flex w-0.75 items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 hover:w-0.75 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-offset-1 [&[data-panel-group-direction=vertical]>div]:rotate-90",
"focus-visible:ring-ring group relative flex w-0.75 items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 hover:w-0.75 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-secondary/50 focus-visible:ring-offset-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
className
)}
size="3px"
{...props}
>
<div className="absolute left-[0.0625rem] top-0 h-full w-px bg-grid-bright transition group-hover:left-0 group-hover:w-0.75 group-hover:bg-lavender-500" />
@@ -37,7 +37,9 @@ const ResizableHandle = ({
))}
</div>
)}
</ResizablePrimitive.PanelResizeHandle>
</PanelResizer>
);
export { ResizableHandle, ResizablePanel, ResizablePanelGroup };
export type ResizableSnapshot = React.ComponentProps<typeof PanelGroup>["snapshot"];
@@ -6,6 +6,7 @@ import { displayableEnvironment } from "~/models/runtimeEnvironment.server";
import { getAllTaskIdentifiers } from "~/models/task.server";
import { isCancellableRunStatus, isFinalRunStatus } from "~/v3/taskStatus";
import { BasePresenter } from "./basePresenter.server";
import { getAllTaskIdentifiers } from "~/models/task.server";
export type RunListOptions = {
userId?: string;
@@ -104,8 +104,8 @@ export default function Page() {
<PageTitle title="Deployments" />
</NavBar>
<PageBody scrollable={false}>
<ResizablePanelGroup direction="horizontal" className="h-full max-h-full">
<ResizablePanel order={1} minSize={20} defaultSize={60}>
<ResizablePanelGroup orientation="horizontal" className="h-full max-h-full">
<ResizablePanel id="deployments-main" min="100px">
{hasDeployments ? (
<div className="flex flex-col gap-4 p-3">
<Table>
@@ -211,8 +211,8 @@ export default function Page() {
{deploymentParam && (
<>
<ResizableHandle withHandle />
<ResizablePanel order={2} minSize={20} defaultSize={40}>
<ResizableHandle id="deployments-handle" />
<ResizablePanel id="deployments-inspector" min="100px" max="600px">
<Outlet />
</ResizablePanel>
</>
@@ -10,8 +10,8 @@ import {
StopCircleIcon,
} from "@heroicons/react/20/solid";
import type { Location } from "@remix-run/react";
import { useLoaderData, useParams, useRevalidator } from "@remix-run/react";
import { LoaderFunctionArgs, SerializeFrom, json } from "@remix-run/server-runtime";
import { useParams, useRevalidator } from "@remix-run/react";
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
import { Virtualizer } from "@tanstack/react-virtual";
import {
formatDurationMilliseconds,
@@ -22,6 +22,7 @@ import { RuntimeEnvironmentType } from "@trigger.dev/database";
import { motion } from "framer-motion";
import { useCallback, useEffect, useRef, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { UseDataFunctionReturn, typedjson, useTypedLoaderData } from "remix-typedjson";
import { ShowParentIcon, ShowParentIconSelected } from "~/assets/icons/ShowParentIcon";
import tileBgPath from "~/assets/images/error-banner-tile@2x.png";
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
@@ -66,7 +67,8 @@ import { useReplaceLocation } from "~/hooks/useReplaceLocation";
import { Shortcut, useShortcutKeys } from "~/hooks/useShortcutKeys";
import { useUser } from "~/hooks/useUser";
import { RunPresenter } from "~/presenters/v3/RunPresenter.server";
import { getResizableRunSettings, setResizableRunSettings } from "~/services/resizablePanel";
import { logger } from "~/services/logger.server";
import { getResizableSnapshot } from "~/services/resizablePanel.server";
import { requireUserId } from "~/services/session.server";
import { cn } from "~/utils/cn";
import { lerp } from "~/utils/lerp";
@@ -78,10 +80,40 @@ import {
v3RunStreamingPath,
v3RunsPath,
} from "~/utils/pathBuilder";
import { SpanView } from "../resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route";
import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route";
import { SpanView } from "../resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route";
type TraceEvent = NonNullable<SerializeFrom<typeof loader>["trace"]>["events"][0];
type TraceEvent = NonNullable<UseDataFunctionReturn<typeof loader>["trace"]>["events"][0];
const resizableSettings = {
parent: {
autosaveId: "panel-run-parent",
handleId: "parent-handle",
main: {
id: "run",
min: "100px" as const,
},
inspector: {
id: "inspector",
default: "430px" as const,
min: "50px" as const,
},
},
tree: {
autosaveId: "panel-run-tree",
handleId: "tree-handle",
tree: {
id: "tree",
default: "50%" as const,
min: "50px" as const,
},
timeline: {
id: "timeline",
default: "50%" as const,
min: "50px" as const,
},
},
};
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
const userId = await requireUserId(request);
@@ -96,17 +128,26 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
});
//resizable settings
const resizeSettings = await getResizableRunSettings(request);
const parent = await getResizableSnapshot(request, resizableSettings.parent.autosaveId);
const tree = await getResizableSnapshot(request, resizableSettings.tree.autosaveId);
return json({
logger.log("Resizable snapshots", {
parent,
tree,
});
return typedjson({
run: result.run,
trace: result.trace,
maximumLiveReloadingSetting: env.MAXIMUM_LIVE_RELOADING_EVENTS,
resizeSettings,
resizable: {
parent,
tree,
},
});
};
type LoaderData = SerializeFrom<typeof loader>;
type LoaderData = UseDataFunctionReturn<typeof loader>;
function getSpanId(location: Location<any>): string | undefined {
const search = new URLSearchParams(location.search);
@@ -114,8 +155,8 @@ function getSpanId(location: Location<any>): string | undefined {
}
export default function Page() {
const { run, trace, resizeSettings, maximumLiveReloadingSetting } =
useLoaderData<typeof loader>();
const { run, trace, resizable, maximumLiveReloadingSetting } =
useTypedLoaderData<typeof loader>();
const user = useUser();
const organization = useOrganization();
const project = useProject();
@@ -208,14 +249,14 @@ export default function Page() {
run={run}
trace={trace}
maximumLiveReloadingSetting={maximumLiveReloadingSetting}
resizeSettings={resizeSettings}
resizable={resizable}
/>
) : (
<NoLogsView
run={run}
trace={trace}
maximumLiveReloadingSetting={maximumLiveReloadingSetting}
resizeSettings={resizeSettings}
resizable={resizable}
/>
)}
</PageBody>
@@ -223,7 +264,7 @@ export default function Page() {
);
}
function TraceView({ run, trace, maximumLiveReloadingSetting, resizeSettings }: LoaderData) {
function TraceView({ run, trace, maximumLiveReloadingSetting, resizable }: LoaderData) {
const organization = useOrganization();
const project = useProject();
const { location, replaceSearchParam } = useReplaceLocation();
@@ -255,15 +296,14 @@ function TraceView({ run, trace, maximumLiveReloadingSetting, resizeSettings }:
return (
<div className={cn("grid h-full max-h-full grid-cols-1 overflow-hidden")}>
<ResizablePanelGroup
direction="horizontal"
autosaveId={resizableSettings.parent.autosaveId}
snapshot={resizable.parent}
className="h-full max-h-full"
onLayout={(layout) => {
if (layout.length !== 2) return;
if (!selectedSpanId) return;
setResizableRunSettings(document, layout);
}}
>
<ResizablePanel order={1} minSize={30} defaultSize={resizeSettings.layout?.[0] ?? 70}>
<ResizablePanel
id={resizableSettings.parent.main.id}
min={resizableSettings.parent.main.min}
>
<TasksTreeView
selectedId={selectedSpanId}
key={events[0]?.id ?? "-"}
@@ -286,9 +326,14 @@ function TraceView({ run, trace, maximumLiveReloadingSetting, resizeSettings }:
maximumLiveReloadingSetting={maximumLiveReloadingSetting}
/>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizableHandle id={resizableSettings.parent.handleId} />
{selectedSpanId && (
<ResizablePanel order={2} minSize={25} defaultSize={resizeSettings.layout?.[1] ?? 30}>
<ResizablePanel
id={resizableSettings.parent.inspector.id}
default={resizableSettings.parent.inspector.default}
min={resizableSettings.parent.inspector.min}
isStaticAtRest
>
<SpanView
runParam={run.friendlyId}
spanId={selectedSpanId}
@@ -301,7 +346,7 @@ function TraceView({ run, trace, maximumLiveReloadingSetting, resizeSettings }:
);
}
function NoLogsView({ run, resizeSettings }: LoaderData) {
function NoLogsView({ run, resizable }: LoaderData) {
const plan = useCurrentPlan();
const organization = useOrganization();
@@ -320,14 +365,14 @@ function NoLogsView({ run, resizeSettings }: LoaderData) {
return (
<div className={cn("grid h-full max-h-full grid-cols-1 overflow-hidden")}>
<ResizablePanelGroup
direction="horizontal"
autosaveId={resizableSettings.parent.autosaveId}
snapshot={resizable.parent}
className="h-full max-h-full"
onLayout={(layout) => {
if (layout.length !== 2) return;
setResizableRunSettings(document, layout);
}}
>
<ResizablePanel order={1} minSize={30} defaultSize={resizeSettings.layout?.[0] ?? 70}>
<ResizablePanel
id={resizableSettings.parent.main.id}
min={resizableSettings.parent.main.min}
>
<div className="grid h-full place-items-center">
{daysSinceCompleted === undefined ? (
<InfoPanel variant="info" icon={InformationCircleIcon} title="We delete old logs">
@@ -375,8 +420,13 @@ function NoLogsView({ run, resizeSettings }: LoaderData) {
)}
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel order={2} minSize={25} defaultSize={resizeSettings.layout?.[1] ?? 30}>
<ResizableHandle id={resizableSettings.parent.handleId} />{" "}
<ResizablePanel
id={resizableSettings.parent.inspector.id}
default={resizableSettings.parent.inspector.default}
min={resizableSettings.parent.inspector.min}
isStaticAtRest
>
<SpanView runParam={run.friendlyId} spanId={run.spanId} />
</ResizablePanel>
</ResizablePanelGroup>
@@ -464,15 +514,14 @@ function TasksTreeView({
/>
</div>
</div>
<ResizablePanelGroup
direction="horizontal"
onLayout={(layout) => {
if (layout.length !== 2) return;
setResizableRunSettings(document, layout);
}}
>
<ResizablePanelGroup autosaveId={resizableSettings.tree.autosaveId}>
{/* Tree list */}
<ResizablePanel order={1} minSize={20} defaultSize={50} className="pl-3">
<ResizablePanel
id={resizableSettings.tree.tree.id}
default={resizableSettings.tree.tree.default}
min={resizableSettings.tree.tree.min}
className="pl-3"
>
<div className="grid h-full grid-rows-[2rem_1fr] overflow-hidden">
<div className="flex items-center pr-2">
{parentRunFriendlyId ? (
@@ -578,9 +627,13 @@ function TasksTreeView({
/>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizableHandle id={resizableSettings.tree.handleId} />
{/* Timeline */}
<ResizablePanel order={2} minSize={20} defaultSize={50}>
<ResizablePanel
id={resizableSettings.tree.timeline.id}
default={resizableSettings.tree.timeline.default}
min={resizableSettings.tree.timeline.min}
>
<TimelineView
totalDuration={totalDuration}
scale={scale}
@@ -599,7 +652,7 @@ function TasksTreeView({
/>
</ResizablePanel>
</ResizablePanelGroup>
<div className="flex items-center justify-between gap-2 border-t border-grid-dimmed px-2">
<div className="flex items-center justify-between gap-2 border-t border-grid-dimmed px-4">
<div className="grow @container">
<div className="hidden items-center gap-4 @[42rem]:flex">
<KeyboardShortcuts
@@ -1091,11 +1144,11 @@ function ConnectedDevWarning() {
return (
<div
className={cn(
"flex items-center overflow-hidden pl-5 pr-2 transition-opacity duration-500",
"mt-2.5 flex items-center overflow-hidden pl-5 pr-2 transition-opacity duration-500",
isVisible ? "opacity-100" : "h-0 opacity-0"
)}
>
<Callout variant="info">
<Callout variant="info" className="overflow-hidden">
<div className="flex flex-col gap-1">
<Paragraph variant="small">
Runs usually start within 1 second in{" "}
@@ -177,8 +177,8 @@ export default function Page() {
</PageAccessories>
</NavBar>
<PageBody scrollable={false}>
<ResizablePanelGroup direction="horizontal" className="h-full max-h-full">
<ResizablePanel order={1} minSize={20} defaultSize={60}>
<ResizablePanelGroup orientation="horizontal" className="h-full max-h-full">
<ResizablePanel id="schedules-main" min={"100px"}>
<div className="max-h-full overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
{possibleTasks.length === 0 ? (
<CreateScheduledTaskInstructions />
@@ -250,8 +250,8 @@ export default function Page() {
</ResizablePanel>
{(isShowingNewPane || isShowingSchedule) && (
<>
<ResizableHandle withHandle />
<ResizablePanel order={2} minSize={20} defaultSize={40}>
<ResizableHandle id="schedules-handle" />
<ResizablePanel id="schedules-inspector" min="100px" default="500px">
<Outlet />
</ResizablePanel>
</>
@@ -180,8 +180,8 @@ function StandardTaskForm({ task, runs }: { task: TestTask["task"]; runs: Standa
onSubmit={(e) => submitForm(e)}
>
<input type="hidden" name="triggerSource" value={"STANDARD"} />
<ResizablePanelGroup direction="horizontal">
<ResizablePanel order={1} minSize={30} defaultSize={60}>
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel id="test-task-main" min="100px" default="60%">
<div className="h-full bg-charcoal-900">
<JSONEditor
defaultValue={defaultJson}
@@ -207,8 +207,8 @@ function StandardTaskForm({ task, runs }: { task: TestTask["task"]; runs: Standa
/>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel order={2} minSize={20} defaultSize={40}>
<ResizableHandle id="test-task-handle" />
<ResizablePanel id="test-task-inspector" min="100px">
<RecentPayloads
runs={runs}
selectedId={selectedCodeSampleId}
@@ -308,8 +308,8 @@ function ScheduledTaskForm({
{...conform.input(environmentId, { type: "hidden" })}
value={task.environment.id}
/>
<ResizablePanelGroup direction="horizontal">
<ResizablePanel order={1} minSize={30} defaultSize={60}>
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel id="test-task-main" min="100px" default="60%">
<div className="p-3">
<Fieldset>
<InputGroup>
@@ -402,8 +402,8 @@ function ScheduledTaskForm({
</Fieldset>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel order={2} minSize={20} defaultSize={40}>
<ResizableHandle id="test-task-handle" />
<ResizablePanel id="test-task-inspector" min="100px">
<RecentPayloads
runs={runs}
selectedId={selectedCodeSampleId}
@@ -85,8 +85,8 @@ export default function Page() {
</NavBar>
<PageBody scrollable={false}>
<div className={cn("grid h-full max-h-full grid-cols-1")}>
<ResizablePanelGroup direction="horizontal" className="h-full max-h-full">
<ResizablePanel order={1} minSize={20} defaultSize={30}>
<ResizablePanelGroup orientation="horizontal" className="h-full max-h-full">
<ResizablePanel id="test-selector" min="100px" default="30%">
<div className="grid h-full max-h-full grid-rows-[5.625rem_1fr] overflow-hidden">
<div className="mx-3 flex flex-col gap-1 border-b border-grid-dimmed">
<div className="flex h-10 items-center">
@@ -117,7 +117,7 @@ export default function Page() {
<Spinner />
</div>
) : hasSelectedEnvironment ? (
<div className="grid grid-rows-[2.5rem_1fr] overflow-hidden">
<div className="grid grid-rows-[2.5rem_1fr] overflow-hidden">
<div className="mx-3 flex items-center border-b border-grid-dimmed">
<Header2>Select a task</Header2>
</div>
@@ -135,8 +135,8 @@ export default function Page() {
)}
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel order={2} minSize={30} defaultSize={70}>
<ResizableHandle id="test-handle" />
<ResizablePanel id="test-main" min="225px">
<Outlet key={taskParam} />
</ResizablePanel>
</ResizablePanelGroup>
@@ -157,7 +157,7 @@ function TaskSelector({
return (
<div className="divide-y divide-charcoal-800 overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<div className="px-2 pb-2">
<div className="p-2">
<Input
placeholder="Search tasks"
variant="medium"
@@ -0,0 +1,24 @@
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "~/components/primitives/Resizable";
export default function Story() {
return (
<div className="h-full">
<ResizablePanelGroup>
<ResizablePanel id={"1-left"} className="bg-sky-500" />
<ResizableHandle id="1-split" />
<ResizablePanel
id={"1-right"}
default="100px"
min="100px"
max="600px"
className="bg-teal-500"
isStaticAtRest
/>
</ResizablePanelGroup>
</div>
);
}
@@ -72,6 +72,10 @@ const stories: Story[] = [
name: "Radio group",
slug: "radio-group",
},
{
name: "Resizable",
slug: "resizable",
},
{
name: "Segemented control",
slug: "segmented-control",
@@ -0,0 +1,39 @@
import { parse } from "cookie";
import { type ResizableSnapshot } from "~/components/primitives/Resizable";
import { logger } from "./logger.server";
export async function getResizableSnapshot(
request: Request,
id: string
): Promise<ResizableSnapshot | undefined> {
try {
const cookieHeader = request.headers.get("Cookie");
if (!cookieHeader) {
return undefined;
}
const cookies = parse(cookieHeader);
const cookieValue = cookies[id];
if (cookieValue) {
try {
const parsedValue = JSON.parse(cookieValue) as any;
if (typeof parsedValue === "object" && "status" in parsedValue) {
return parsedValue as ResizableSnapshot;
}
} catch (parseError) {
logger.error("getResizableSnapshot() error parsing cookie value:", {
parseError,
cookieValue,
});
}
}
return undefined;
} catch (error) {
logger.error("getResizableSnapshot() error:", {
error,
});
return undefined;
}
}
@@ -1,31 +0,0 @@
import { z } from "zod";
const ResizableConfig = z
.object({ layout: z.array(z.number()).optional() })
.default({ layout: undefined });
type ResizableConfig = z.infer<typeof ResizableConfig>;
function getCookieValue(cookieHeader: string | null, cookieName: string): ResizableConfig {
const cookieValue = cookieHeader?.split(`${cookieName}=`)[1]?.split(";")[0];
if (!cookieValue) {
return { layout: undefined };
}
try {
const json = JSON.parse(cookieValue);
return ResizableConfig.parse(json);
} catch (e) {
return { layout: undefined };
}
}
//run page
const runResizableName = "resizable-panels:run";
export async function getResizableRunSettings(request: Request): Promise<ResizableConfig> {
const cookieHeader = request.headers.get("Cookie");
return getCookieValue(cookieHeader, runResizableName);
}
export async function setResizableRunSettings(document: Document, layout: number[]) {
document.cookie = `${runResizableName}=${JSON.stringify({ layout })}`;
}
+3
View File
@@ -107,6 +107,7 @@
"class-variance-authority": "^0.5.2",
"clsx": "^1.2.1",
"compression": "^1.7.4",
"cookie": "^0.6.0",
"cron-parser": "^4.9.0",
"cronstrue": "^2.21.0",
"cross-env": "^7.0.3",
@@ -150,6 +151,7 @@
"react-resizable-panels": "^2.0.9",
"react-stately": "^3.29.1",
"react-use": "^17.4.0",
"react-window-splitter": "^0.4.0",
"recharts": "^2.12.6",
"regression": "^2.0.1",
"remix-auth": "^3.6.0",
@@ -190,6 +192,7 @@
"@total-typescript/ts-reset": "^0.4.2",
"@types/bcryptjs": "^2.4.2",
"@types/compression": "^1.7.2",
"@types/cookie": "^0.6.0",
"@types/eslint": "^8.4.6",
"@types/express": "^4.17.13",
"@types/humanize-duration": "^3.27.1",
+1444 -3
View File
File diff suppressed because it is too large Load Diff