diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx index 5a953c0199b..f6c573e764c 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx @@ -71,7 +71,6 @@ import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server"; import { type GenerationRow, PromptPresenter } from "~/presenters/v3/PromptPresenter.server"; import { SpanView } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route"; import { clickhouseClient } from "~/services/clickhouseInstance.server"; -import { getResizableSnapshot } from "~/services/resizablePanel.server"; import { requireUserId } from "~/services/session.server"; import { PromptService } from "~/v3/services/promptService.server"; @@ -271,7 +270,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { console.error("Prompt generations query exception:", e); } - // Load distinct filter values and resizable snapshots in parallel + // Load distinct filter values in parallel const distinctQuery = (col: string, name: string) => clickhouseClient.reader.query({ name, @@ -281,16 +280,10 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { })({ environmentId: environment.id, promptSlug: prompt.slug }); const [ - resizableOuter, - resizableVertical, - resizableGenerations, [modelsErr, modelsRows], [opsErr, opsRows], [provsErr, provsRows], ] = await Promise.all([ - getResizableSnapshot(request, "prompt-detail"), - getResizableSnapshot(request, "prompt-vertical"), - getResizableSnapshot(request, "prompt-generations"), distinctQuery("response_model", "promptDistinctModels"), distinctQuery("operation_id", "promptDistinctOperations"), distinctQuery("gen_ai_system", "promptDistinctProviders"), @@ -302,9 +295,9 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { return typedjson({ resizable: { - outer: resizableOuter, - vertical: resizableVertical, - generations: resizableGenerations, + outer: undefined as ResizableSnapshot | undefined, + vertical: undefined as ResizableSnapshot | undefined, + generations: undefined as ResizableSnapshot | undefined, }, prompt: { id: prompt.id, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx index f35376a4275..6f279c3b1ee 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx @@ -12,7 +12,7 @@ import { StopCircleIcon, } from "@heroicons/react/20/solid"; -import { useLoaderData, useRevalidator } from "@remix-run/react"; +import { type ClientLoaderFunctionArgs, useLoaderData, useRevalidator } from "@remix-run/react"; import { type LoaderFunctionArgs, type SerializeFrom, json } from "@remix-run/server-runtime"; import { type Virtualizer } from "@tanstack/react-virtual"; import { @@ -95,7 +95,6 @@ import { RunEnvironmentMismatchError, RunPresenter } from "~/presenters/v3/RunPr import { clickhouseClient } from "~/services/clickhouseInstance.server"; import { getImpersonationId } from "~/services/impersonation.server"; 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"; @@ -279,10 +278,6 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { throw error; } - //resizable settings - const parent = await getResizableSnapshot(request, resizableSettings.parent.autosaveId); - const tree = await getResizableSnapshot(request, resizableSettings.tree.autosaveId); - const runsList = await getRunsListFromTableState({ tableStateParam: url.searchParams.get("tableState"), organizationSlug, @@ -297,13 +292,40 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { trace: result.trace, maximumLiveReloadingSetting: result.maximumLiveReloadingSetting, resizable: { - parent, - tree, + parent: undefined as ResizableSnapshot | undefined, + tree: undefined as ResizableSnapshot | undefined, }, runsList, }); }; +function getLocalStorageSnapshot(key: string): ResizableSnapshot | undefined { + try { + const raw = localStorage.getItem(key); + if (raw) { + const parsed: unknown = JSON.parse(raw); + if (parsed != null && typeof parsed === "object" && "status" in parsed) { + return parsed as ResizableSnapshot; + } + } + } catch { + // Silently ignore localStorage errors + } + return undefined; +} + +export async function clientLoader({ serverLoader }: ClientLoaderFunctionArgs) { + const serverData = await serverLoader(); + return { + ...serverData, + resizable: { + parent: getLocalStorageSnapshot(resizableSettings.parent.autosaveId), + tree: getLocalStorageSnapshot(resizableSettings.tree.autosaveId), + }, + }; +} +clientLoader.hydrate = true as const; + type LoaderData = SerializeFrom; export default function Page() { diff --git a/package.json b/package.json index ce34f5bad27..71a0ba8826f 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,8 @@ "@sentry/remix@9.46.0": "patches/@sentry__remix@9.46.0.patch", "@upstash/ratelimit@1.1.3": "patches/@upstash__ratelimit.patch", "antlr4ts@0.5.0-alpha.4": "patches/antlr4ts@0.5.0-alpha.4.patch", - "@window-splitter/state@0.4.1": "patches/@window-splitter__state@0.4.1.patch" + "@window-splitter/state@0.4.1": "patches/@window-splitter__state@0.4.1.patch", + "react-window-splitter@0.4.1": "patches/react-window-splitter@0.4.1.patch" }, "overrides": { "typescript": "5.5.4", diff --git a/patches/react-window-splitter@0.4.1.patch b/patches/react-window-splitter@0.4.1.patch new file mode 100644 index 00000000000..c7cc3d67575 --- /dev/null +++ b/patches/react-window-splitter@0.4.1.patch @@ -0,0 +1,38 @@ +diff --git a/dist/commonjs/index.js b/dist/commonjs/index.js +index 1f4ff1a53983a76140f6349c8ef9b91ca9eee6ef..a193ecbf5257e5bdc8524e7b75a23c0bcb19b179 100644 +--- a/dist/commonjs/index.js ++++ b/dist/commonjs/index.js +@@ -469,7 +469,7 @@ const PanelResizerVisible = react_1.default.forwardRef(function PanelResizerVisi + if (!panelBeforeHandle || !(0, state_1.isPanelData)(panelBeforeHandle)) { + return null; + } +- return (react_1.default.createElement("div", { ref: ref, role: "separator", "data-splitter-type": "handle", "data-splitter-id": handleId, "data-handle-orientation": orientation, "data-state": isDragging ? "dragging" : "idle", "aria-label": "Resize Handle", "aria-disabled": disabled, "aria-controls": panelBeforeHandle.id, "aria-valuemin": (0, state_1.getUnitPercentageValue)(groupsSize, panelBeforeHandle.min), "aria-valuemax": panelBeforeHandle.max === "1fr" ++ return (react_1.default.createElement("div", { ref: ref, role: "separator", suppressHydrationWarning: true, "data-splitter-type": "handle", "data-splitter-id": handleId, "data-handle-orientation": orientation, "data-state": isDragging ? "dragging" : "idle", "aria-label": "Resize Handle", "aria-disabled": disabled, "aria-controls": panelBeforeHandle.id, "aria-valuemin": (0, state_1.getUnitPercentageValue)(groupsSize, panelBeforeHandle.min), "aria-valuemax": panelBeforeHandle.max === "1fr" + ? 100 + : (0, state_1.getUnitPercentageValue)(groupsSize, panelBeforeHandle.max), "aria-valuenow": (0, state_1.getUnitPercentageValue)(groupsSize, panelBeforeHandle.currentValue), ...(0, react_aria_1.mergeProps)(props, disabled ? {} : buttonProps, disabled ? {} : moveProps, { onKeyDown }), tabIndex: 0, style: { + cursor, +diff --git a/dist/esm/index.js b/dist/esm/index.js +index 0102166ddd34988ec06d1991290dcf8d748a44cd..10e320df665c27e1b4cce4a6a6f47dd505d0c5a6 100644 +--- a/dist/esm/index.js ++++ b/dist/esm/index.js +@@ -440,7 +440,7 @@ const PanelResizerVisible = React.forwardRef(function PanelResizerVisible({ size + if (!panelBeforeHandle || !isPanelData(panelBeforeHandle)) { + return null; + } +- return (React.createElement("div", { ref: ref, role: "separator", "data-splitter-type": "handle", "data-splitter-id": handleId, "data-handle-orientation": orientation, "data-state": isDragging ? "dragging" : "idle", "aria-label": "Resize Handle", "aria-disabled": disabled, "aria-controls": panelBeforeHandle.id, "aria-valuemin": getUnitPercentageValue(groupsSize, panelBeforeHandle.min), "aria-valuemax": panelBeforeHandle.max === "1fr" ++ return (React.createElement("div", { ref: ref, role: "separator", suppressHydrationWarning: true, "data-splitter-type": "handle", "data-splitter-id": handleId, "data-handle-orientation": orientation, "data-state": isDragging ? "dragging" : "idle", "aria-label": "Resize Handle", "aria-disabled": disabled, "aria-controls": panelBeforeHandle.id, "aria-valuemin": getUnitPercentageValue(groupsSize, panelBeforeHandle.min), "aria-valuemax": panelBeforeHandle.max === "1fr" + ? 100 + : getUnitPercentageValue(groupsSize, panelBeforeHandle.max), "aria-valuenow": getUnitPercentageValue(groupsSize, panelBeforeHandle.currentValue), ...mergeProps(props, disabled ? {} : buttonProps, disabled ? {} : moveProps, { onKeyDown }), tabIndex: 0, style: { + cursor, +diff --git a/src/index.tsx b/src/index.tsx +index c00b560a0a19759ee6e32c0bd42b105d28ec41fc..ea14614d97f9e30c36131537dbb73192e047dc79 100644 +--- a/src/index.tsx ++++ b/src/index.tsx +@@ -805,6 +805,7 @@ const PanelResizerVisible = React.forwardRef< +
} + role="separator" ++ suppressHydrationWarning + data-splitter-type="handle" + data-splitter-id={handleId} + data-handle-orientation={orientation} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f3631a68b63..97e56e62ba5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,6 +46,9 @@ patchedDependencies: graphile-worker@0.16.6: hash: 798129c99ed02177430fc90a1fdef800ec94e5fd1d491b931297dc52f4c98ab1 path: patches/graphile-worker@0.16.6.patch + react-window-splitter@0.4.1: + hash: a545bdb56cdae0110ae6f6e3da57018eefc3276a03455669ec035aaa2e519a11 + path: patches/react-window-splitter@0.4.1.patch redlock@5.0.0-beta.2: hash: 52b17ac642f5f9776bf05e2229f4dd79588d37b0039d835c7684c478464632f2 path: patches/redlock@5.0.0-beta.2.patch @@ -742,7 +745,7 @@ importers: version: 17.5.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-window-splitter: specifier: ^0.4.1 - version: 0.4.1(@types/react@18.2.69)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 0.4.1(patch_hash=a545bdb56cdae0110ae6f6e3da57018eefc3276a03455669ec035aaa2e519a11)(@types/react@18.2.69)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) recharts: specifier: ^2.15.2 version: 2.15.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -1138,7 +1141,7 @@ importers: version: 18.3.1 react-email: specifier: ^2.1.1 - version: 2.1.2(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.15)(bufferutil@4.0.9)(eslint@8.31.0) + version: 2.1.2(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.15)(eslint@8.31.0) resend: specifier: ^3.2.0 version: 3.2.0 @@ -39233,7 +39236,7 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-email@2.1.2(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.15)(bufferutil@4.0.9)(eslint@8.31.0): + react-email@2.1.2(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.15)(eslint@8.31.0): dependencies: '@babel/parser': 7.24.1 '@radix-ui/colors': 1.0.1 @@ -39270,8 +39273,8 @@ snapshots: react: 18.3.1 react-dom: 18.2.0(react@18.3.1) shelljs: 0.8.5 - socket.io: 4.7.3(bufferutil@4.0.9) - socket.io-client: 4.7.3(bufferutil@4.0.9) + socket.io: 4.7.3 + socket.io-client: 4.7.3 sonner: 1.3.1(react-dom@18.2.0(react@18.3.1))(react@18.3.1) source-map-js: 1.0.2 stacktrace-parser: 0.1.10 @@ -39553,7 +39556,7 @@ snapshots: ts-easing: 0.2.0 tslib: 2.8.1 - react-window-splitter@0.4.1(@types/react@18.2.69)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + react-window-splitter@0.4.1(patch_hash=a545bdb56cdae0110ae6f6e3da57018eefc3276a03455669ec035aaa2e519a11)(@types/react@18.2.69)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.69)(react@18.2.0) '@testing-library/jest-dom': 6.5.0 @@ -40496,7 +40499,7 @@ snapshots: - supports-color - utf-8-validate - socket.io-client@4.7.3(bufferutil@4.0.9): + socket.io-client@4.7.3: dependencies: '@socket.io/component-emitter': 3.1.0 debug: 4.3.7(supports-color@10.0.0) @@ -40525,7 +40528,7 @@ snapshots: transitivePeerDependencies: - supports-color - socket.io@4.7.3(bufferutil@4.0.9): + socket.io@4.7.3: dependencies: accepts: 1.3.8 base64id: 2.0.0