feat: integrated with artifact resizable

This commit is contained in:
Henry Li
2026-01-16 21:55:31 +08:00
parent e1ddb1ee42
commit ca70e2dcf7
3 changed files with 43 additions and 33 deletions

View File

@@ -4,13 +4,13 @@ import { useParams, useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { BreadcrumbItem } from "@/components/ui/breadcrumb"; import { BreadcrumbItem } from "@/components/ui/breadcrumb";
import { ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
import { InputBox } from "@/components/workspace/input-box"; import { InputBox } from "@/components/workspace/input-box";
import { MessageList } from "@/components/workspace/message-list/message-list"; import { MessageList } from "@/components/workspace/message-list/message-list";
import { import {
WorkspaceContainer, WorkspaceContainer,
WorkspaceBody, WorkspaceBody,
WorkspaceHeader, WorkspaceHeader,
WorkspaceFooter,
} from "@/components/workspace/workspace-container"; } from "@/components/workspace/workspace-container";
import { useLocalSettings } from "@/core/settings"; import { useLocalSettings } from "@/core/settings";
import { type AgentThread } from "@/core/threads"; import { type AgentThread } from "@/core/threads";
@@ -61,21 +61,27 @@ export default function ChatPage() {
</BreadcrumbItem> </BreadcrumbItem>
</WorkspaceHeader> </WorkspaceHeader>
<WorkspaceBody> <WorkspaceBody>
<div className="flex size-full justify-center"> <ResizablePanelGroup orientation="horizontal">
<MessageList className="size-full" thread={thread} /> <ResizablePanel className="relative" defaultSize={46}>
</div> <div className="flex size-full justify-center">
<MessageList className="size-full" thread={thread} />
</div>
<div className="absolute right-0 bottom-0 left-0 flex justify-center px-4">
<InputBox
className="w-full max-w-(--container-width-md)"
autoFocus={isNewThread}
status={thread.isLoading ? "streaming" : "ready"}
context={threadContext}
onContextChange={setThreadContext}
onSubmit={handleSubmit}
onStop={handleStop}
/>
</div>
</ResizablePanel>
{/* <ResizableHandle />
<ResizablePanel defaultSize={64}></ResizablePanel> */}
</ResizablePanelGroup>
</WorkspaceBody> </WorkspaceBody>
<WorkspaceFooter>
<InputBox
className="max-w-(--container-width-md)"
autoFocus={isNewThread}
status={thread.isLoading ? "streaming" : "ready"}
context={threadContext}
onContextChange={setThreadContext}
onSubmit={handleSubmit}
onStop={handleStop}
/>
</WorkspaceFooter>
</WorkspaceContainer> </WorkspaceContainer>
); );
} }

View File

@@ -1,6 +1,7 @@
import type { Message } from "@langchain/langgraph-sdk"; import type { Message } from "@langchain/langgraph-sdk";
import { import {
BookOpenTextIcon, BookOpenTextIcon,
FileTextIcon,
FolderOpenIcon, FolderOpenIcon,
GlobeIcon, GlobeIcon,
LightbulbIcon, LightbulbIcon,
@@ -219,6 +220,24 @@ function ToolCall({
)} )}
</ChainOfThoughtStep> </ChainOfThoughtStep>
); );
} else if (name === "present_files") {
return (
<ChainOfThoughtStep
key={id}
label={`Present file${(args as { filepaths: string[] }).filepaths.length > 1 ? "s" : ""}`}
icon={FileTextIcon}
>
<ChainOfThoughtSearchResult>
{(args as { filepaths: string[] }).filepaths.map(
(filepath: string) => (
<ChainOfThoughtSearchResult key={filepath}>
{filepath}
</ChainOfThoughtSearchResult>
),
)}
</ChainOfThoughtSearchResult>
</ChainOfThoughtStep>
);
} else { } else {
const description: string | undefined = (args as { description: string }) const description: string | undefined = (args as { description: string })
?.description; ?.description;
@@ -324,6 +343,9 @@ function describeStep(step: CoTStep | undefined): {
} else if (step.name === "bash") { } else if (step.name === "bash") {
label = "Execute command"; label = "Execute command";
icon = <SquareTerminalIcon className="size-4" />; icon = <SquareTerminalIcon className="size-4" />;
} else if (step.name === "present_files") {
label = `Present file${(step.args as { filepaths: string[] }).filepaths.length > 1 ? "s" : ""}`;
icon = <FileTextIcon className="size-4" />;
} else { } else {
label = `Call tool "${step.name}"`; label = `Call tool "${step.name}"`;
icon = <WrenchIcon className="size-4" />; icon = <WrenchIcon className="size-4" />;

View File

@@ -122,24 +122,6 @@ export function WorkspaceBody({
); );
} }
export function WorkspaceFooter({
className,
children,
...props
}: React.ComponentProps<"footer">) {
return (
<footer
className={cn(
"absolute right-0 bottom-0 left-0 z-30 flex justify-center",
className,
)}
{...props}
>
{children}
</footer>
);
}
function nameOfSegment(segment: string | undefined) { function nameOfSegment(segment: string | undefined) {
if (!segment) return "Home"; if (!segment) return "Home";
return segment[0]?.toUpperCase() + segment.slice(1); return segment[0]?.toUpperCase() + segment.slice(1);