- Add user preferences persistence (model, migration, router, frontend core) - Add agent name desensitization config - Add agent_id to user custom prompts with migration - Enhance agents persistence (base, memory, model, sql) and scheduler service - Improve frontend agent cards/gallery, scheduled tasks, custom prompt menu - Add pagination UI component; remove stale package-lock.json Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
83 lines
2.7 KiB
TypeScript
83 lines
2.7 KiB
TypeScript
"use client";
|
|
|
|
import { GripVerticalIcon } from "lucide-react";
|
|
import * as React from "react";
|
|
import * as ResizablePrimitive from "react-resizable-panels";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
function ResizablePanelGroup({
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<typeof ResizablePrimitive.Group>) {
|
|
return (
|
|
<ResizablePrimitive.Group
|
|
data-slot="resizable-panel-group"
|
|
className={cn(
|
|
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
|
|
className,
|
|
)}
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// react-resizable-panels v4 changed size semantics: a bare *number* is now
|
|
// interpreted as PIXELS, while a unit-less *string* is a percentage. Every call
|
|
// site in this app passes sizes as percentages (the v2/v3 convention), so a
|
|
// value like `maxSize={60}` would otherwise pin a panel to 60px and freeze the
|
|
// layout/drag. Normalize numbers to "<n>%" to preserve the intended percentage.
|
|
const asPercent = (v: number | string | undefined) =>
|
|
typeof v === "number" ? `${v}%` : v;
|
|
|
|
function ResizablePanel({
|
|
defaultSize,
|
|
minSize,
|
|
maxSize,
|
|
collapsedSize,
|
|
...props
|
|
}: React.ComponentProps<typeof ResizablePrimitive.Panel>) {
|
|
return (
|
|
<ResizablePrimitive.Panel
|
|
data-slot="resizable-panel"
|
|
defaultSize={asPercent(defaultSize)}
|
|
minSize={asPercent(minSize)}
|
|
maxSize={asPercent(maxSize)}
|
|
collapsedSize={asPercent(collapsedSize)}
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
function ResizableHandle({
|
|
withHandle,
|
|
className,
|
|
...props
|
|
}: React.ComponentProps<typeof ResizablePrimitive.Separator> & {
|
|
withHandle?: boolean;
|
|
}) {
|
|
return (
|
|
<ResizablePrimitive.Separator
|
|
data-slot="resizable-handle"
|
|
className={cn(
|
|
// Thin visible divider, but a generous invisible hit area on both sides
|
|
// (after:w-4 → 16px) so the handle is easy to grab and drag. Hovering /
|
|
// dragging tints the divider so it's obvious it's interactive.
|
|
"bg-border hover:bg-primary/60 active:bg-primary relative flex w-px items-center justify-center transition-colors",
|
|
"after:absolute after:inset-y-0 after:left-1/2 after:w-4 after:-translate-x-1/2 after:cursor-col-resize",
|
|
"focus-visible:ring-ring focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden",
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
{withHandle && (
|
|
<div className="bg-border z-10 flex h-8 w-4 items-center justify-center rounded-sm border shadow-sm">
|
|
<GripVerticalIcon className="size-3.5" />
|
|
</div>
|
|
)}
|
|
</ResizablePrimitive.Separator>
|
|
);
|
|
}
|
|
|
|
export { ResizablePanelGroup, ResizablePanel, ResizableHandle };
|