Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 48fd7b372b |
@@ -29,7 +29,7 @@ import {
|
||||
} from "storybook-addon-remix-react-router";
|
||||
import AgentDetail from "./AgentDetail";
|
||||
import { RIGHT_PANEL_OPEN_KEY } from "./AgentDetailView";
|
||||
import type { AgentsOutletContext } from "./AgentsPage";
|
||||
import type { AgentsOutletContext } from "./AgentsLayout";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Layout wrapper – provides outlet context for the child route.
|
||||
|
||||
@@ -73,7 +73,7 @@ import {
|
||||
AgentDetailNotFoundView,
|
||||
AgentDetailView,
|
||||
} from "./AgentDetailView";
|
||||
import type { AgentsOutletContext } from "./AgentsPage";
|
||||
import type { AgentsOutletContext } from "./AgentsLayout";
|
||||
import {
|
||||
getModelCatalogStatusMessage,
|
||||
getModelOptionsFromCatalog,
|
||||
|
||||
+5
-5
@@ -7,7 +7,7 @@ import type { Chat } from "api/typesGenerated";
|
||||
import type { ModelSelectorOption } from "components/ai-elements";
|
||||
import { fn, spyOn } from "storybook/test";
|
||||
import { reactRouterParameters } from "storybook-addon-remix-react-router";
|
||||
import { AgentsPageView } from "./AgentsPageView";
|
||||
import { AgentsLayout } from "./AgentsLayout";
|
||||
|
||||
const defaultModelOptions: ModelSelectorOption[] = [
|
||||
{
|
||||
@@ -57,9 +57,9 @@ const agentsRouting = [
|
||||
...{ path: string; useStoryElement: boolean }[],
|
||||
];
|
||||
|
||||
const meta: Meta<typeof AgentsPageView> = {
|
||||
title: "pages/AgentsPage/AgentsPageView",
|
||||
component: AgentsPageView,
|
||||
const meta: Meta<typeof AgentsLayout> = {
|
||||
title: "pages/AgentsPage/AgentsLayout",
|
||||
component: AgentsLayout,
|
||||
decorators: [withAuthProvider, withDashboardProvider],
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
@@ -112,7 +112,7 @@ const meta: Meta<typeof AgentsPageView> = {
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof AgentsPageView>;
|
||||
type Story = StoryObj<typeof AgentsLayout>;
|
||||
|
||||
export const EmptyState: Story = {};
|
||||
|
||||
+46
-3
@@ -4,7 +4,7 @@ import { Button } from "components/Button/Button";
|
||||
import { ExternalImage } from "components/ExternalImage/ExternalImage";
|
||||
import { CoderIcon } from "components/Icons/CoderIcon";
|
||||
import { PanelLeftIcon } from "lucide-react";
|
||||
import { type FC, useState } from "react";
|
||||
import { type FC, useEffect, useState } from "react";
|
||||
import { NavLink, Outlet } from "react-router";
|
||||
import { cn } from "utils/cn";
|
||||
import { pageTitle } from "utils/page";
|
||||
@@ -29,7 +29,7 @@ export interface AgentsOutletContext {
|
||||
onToggleSidebarCollapsed: () => void;
|
||||
}
|
||||
|
||||
interface AgentsPageViewProps {
|
||||
interface AgentsLayoutProps {
|
||||
agentId: string | undefined;
|
||||
chatList: TypesGen.Chat[];
|
||||
catalogModelOptions: readonly ChatModelOption[];
|
||||
@@ -57,7 +57,7 @@ interface AgentsPageViewProps {
|
||||
onLoadMore: () => void;
|
||||
}
|
||||
|
||||
export const AgentsPageView: FC<AgentsPageViewProps> = ({
|
||||
export const AgentsLayout: FC<AgentsLayoutProps> = ({
|
||||
agentId,
|
||||
chatList,
|
||||
catalogModelOptions,
|
||||
@@ -84,6 +84,49 @@ export const AgentsPageView: FC<AgentsPageViewProps> = ({
|
||||
hasNextPage,
|
||||
onLoadMore,
|
||||
}) => {
|
||||
// The global CSS sets scrollbar-gutter: stable on <html> to prevent
|
||||
// layout shift on pages that toggle scrollbars. The agents page
|
||||
// uses its own internal scroll containers so the reserved gutter
|
||||
// space is unnecessary and wastes horizontal room.
|
||||
//
|
||||
// Removing the gutter requires three things:
|
||||
//
|
||||
// 1. overflow:hidden on both <html> and <body> so neither element
|
||||
// can produce a scrollbar.
|
||||
// 2. scrollbar-gutter:auto on <html> so the browser stops
|
||||
// reserving space for a scrollbar that will never appear.
|
||||
// This is what makes react-remove-scroll-bar measure a gap of
|
||||
// 0 when a Radix dropdown opens, so it injects no padding or
|
||||
// margin compensation.
|
||||
// 3. An injected <style> that overrides the global
|
||||
// `overflow-y: scroll !important` on body[data-scroll-locked].
|
||||
// Without this, opening any Radix dropdown would force a
|
||||
// scrollbar onto <body>, re-introducing the layout shift.
|
||||
useEffect(() => {
|
||||
const html = document.documentElement;
|
||||
const body = document.body;
|
||||
|
||||
const prevHtmlOverflow = html.style.overflow;
|
||||
const prevHtmlScrollbarGutter = html.style.scrollbarGutter;
|
||||
const prevBodyOverflow = body.style.overflow;
|
||||
|
||||
html.style.overflow = "hidden";
|
||||
html.style.scrollbarGutter = "auto";
|
||||
body.style.overflow = "hidden";
|
||||
|
||||
const style = document.createElement("style");
|
||||
style.textContent =
|
||||
"html body[data-scroll-locked] { overflow-y: hidden !important; }";
|
||||
document.head.appendChild(style);
|
||||
|
||||
return () => {
|
||||
html.style.overflow = prevHtmlOverflow;
|
||||
html.style.scrollbarGutter = prevHtmlScrollbarGutter;
|
||||
body.style.overflow = prevBodyOverflow;
|
||||
style.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const {
|
||||
chatErrorReasons,
|
||||
requestArchiveAgent,
|
||||
@@ -39,8 +39,8 @@ import {
|
||||
emptyInputStorageKey,
|
||||
} from "./AgentCreateForm";
|
||||
import { maybePlayChime } from "./AgentDetail/useAgentChime";
|
||||
import type { AgentsOutletContext } from "./AgentsPageView";
|
||||
import { AgentsPageView } from "./AgentsPageView";
|
||||
import type { AgentsOutletContext } from "./AgentsLayout";
|
||||
import { AgentsLayout } from "./AgentsLayout";
|
||||
import { getModelOptionsFromCatalog } from "./modelOptions";
|
||||
import { useAgentsPageKeybindings } from "./useAgentsPageKeybindings";
|
||||
import { useAgentsPWA } from "./useAgentsPWA";
|
||||
@@ -62,7 +62,7 @@ function isChatListSSEEvent(
|
||||
);
|
||||
}
|
||||
|
||||
export type { AgentsOutletContext } from "./AgentsPageView";
|
||||
export type { AgentsOutletContext } from "./AgentsLayout";
|
||||
|
||||
const AgentsPage: FC = () => {
|
||||
useAgentsPWA();
|
||||
@@ -75,49 +75,6 @@ const AgentsPage: FC = () => {
|
||||
permissions.editDeploymentConfig ||
|
||||
user.roles.some((role) => role.name === "owner" || role.name === "admin");
|
||||
|
||||
// The global CSS sets scrollbar-gutter: stable on <html> to prevent
|
||||
// layout shift on pages that toggle scrollbars. The agents page
|
||||
// uses its own internal scroll containers so the reserved gutter
|
||||
// space is unnecessary and wastes horizontal room.
|
||||
//
|
||||
// Removing the gutter requires three things:
|
||||
//
|
||||
// 1. overflow:hidden on both <html> and <body> so neither element
|
||||
// can produce a scrollbar.
|
||||
// 2. scrollbar-gutter:auto on <html> so the browser stops
|
||||
// reserving space for a scrollbar that will never appear.
|
||||
// This is what makes react-remove-scroll-bar measure a gap of
|
||||
// 0 when a Radix dropdown opens, so it injects no padding or
|
||||
// margin compensation.
|
||||
// 3. An injected <style> that overrides the global
|
||||
// `overflow-y: scroll !important` on body[data-scroll-locked].
|
||||
// Without this, opening any Radix dropdown would force a
|
||||
// scrollbar onto <body>, re-introducing the layout shift.
|
||||
useEffect(() => {
|
||||
const html = document.documentElement;
|
||||
const body = document.body;
|
||||
|
||||
const prevHtmlOverflow = html.style.overflow;
|
||||
const prevHtmlScrollbarGutter = html.style.scrollbarGutter;
|
||||
const prevBodyOverflow = body.style.overflow;
|
||||
|
||||
html.style.overflow = "hidden";
|
||||
html.style.scrollbarGutter = "auto";
|
||||
body.style.overflow = "hidden";
|
||||
|
||||
const style = document.createElement("style");
|
||||
style.textContent =
|
||||
"html body[data-scroll-locked] { overflow-y: hidden !important; }";
|
||||
document.head.appendChild(style);
|
||||
|
||||
return () => {
|
||||
html.style.overflow = prevHtmlOverflow;
|
||||
html.style.scrollbarGutter = prevHtmlScrollbarGutter;
|
||||
body.style.overflow = prevBodyOverflow;
|
||||
style.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const chatsQuery = useInfiniteQuery(infiniteChats());
|
||||
const chatModelsQuery = useQuery(chatModels());
|
||||
const chatModelConfigsQuery = useQuery(chatModelConfigs());
|
||||
@@ -458,7 +415,7 @@ const AgentsPage: FC = () => {
|
||||
});
|
||||
|
||||
return (
|
||||
<AgentsPageView
|
||||
<AgentsLayout
|
||||
agentId={agentId}
|
||||
chatList={chatList}
|
||||
catalogModelOptions={catalogModelOptions}
|
||||
|
||||
Reference in New Issue
Block a user