style: prettier
This commit is contained in:
parent
cb0ebf41bb
commit
4df604d491
|
|
@ -25,7 +25,7 @@ export default async function RootLayout({
|
||||||
return (
|
return (
|
||||||
<html
|
<html
|
||||||
lang={locale}
|
lang={locale}
|
||||||
className={geist.variable+""}
|
className={geist.variable + ""}
|
||||||
suppressContentEditableWarning
|
suppressContentEditableWarning
|
||||||
suppressHydrationWarning
|
suppressHydrationWarning
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,4 @@ export default function ChatLayout({
|
||||||
</ArtifactsProvider>
|
</ArtifactsProvider>
|
||||||
</SubtasksProvider>
|
</SubtasksProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -252,10 +252,12 @@ export default function ChatPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThreadContext.Provider value={{ threadId, thread }}>
|
<ThreadContext.Provider value={{ threadId, thread }}>
|
||||||
<div className={cn(
|
<div
|
||||||
"m-auto flex h-screen min-h-svh overflow-hidden rounded-t-[20px] transition-[width] duration-300 ease-in-out",
|
className={cn(
|
||||||
artifactsOpen ? "w-full" : "w-[50%]",
|
"m-auto flex h-screen min-h-svh overflow-hidden rounded-t-[20px] transition-[width] duration-300 ease-in-out",
|
||||||
)}>
|
artifactsOpen ? "w-full" : "w-[50%]",
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div className="relative flex size-full min-h-0 justify-between rounded-t-[20px]">
|
<div className="relative flex size-full min-h-0 justify-between rounded-t-[20px]">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
@ -264,276 +266,285 @@ export default function ChatPage() {
|
||||||
fullscreen && "hidden",
|
fullscreen && "hidden",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="relative flex size-full min-h-0 justify-between rounded-t-[20px]">
|
<div className="relative flex size-full min-h-0 justify-between rounded-t-[20px]">
|
||||||
<header
|
<header
|
||||||
|
className={cn(
|
||||||
|
"bg-background absolute top-0 right-0 left-0 z-30 mx-4 grid h-[58px] shrink-0 grid-cols-3 items-center border-b transition-all duration-300 ease-in-out",
|
||||||
|
isNewThread && !hasSubmitted ? "hidden" : "",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-start overflow-hidden text-sm font-medium">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
className="px-[10px] py-[5px] text-sm font-medium text-[#150033] hover:text-[#150033]/80"
|
||||||
|
onClick={() => setShowExitDialog(true)}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.5 10H13.25H15.6875H16.5M3.5 10L7.5625 6M3.5 10L7.5625 14"
|
||||||
|
stroke="#666666"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-center overflow-hidden text-sm font-bold font-medium whitespace-nowrap text-[#333333]">
|
||||||
|
{title !== "Untitled" && (
|
||||||
|
<ThreadTitle threadId={threadId} threadTitle={title} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-end gap-2 overflow-hidden">
|
||||||
|
<DevTodoList
|
||||||
|
className="bg-white"
|
||||||
|
todos={thread.values.todos ?? []}
|
||||||
|
hidden={
|
||||||
|
!thread.values.todos || thread.values.todos.length === 0
|
||||||
|
}
|
||||||
|
trigger={
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
className="h-full px-[10px] py-[5px] text-sm font-medium text-[#150033] hover:text-[#150033]"
|
||||||
|
>
|
||||||
|
<ListTodoIcon className="size-4" /> To-dos
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{artifacts?.length > 0 && !artifactsOpen && (
|
||||||
|
<Tooltip content="点击可查看生成的文件结果">
|
||||||
|
<Button
|
||||||
|
className="text-[#150033] hover:text-[#150033]/80"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => {
|
||||||
|
setArtifactsOpen(true);
|
||||||
|
setSidebarOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FilesIcon />
|
||||||
|
{t.common.artifacts}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main
|
||||||
|
className={cn(
|
||||||
|
"flex min-h-0 max-w-full grow flex-col",
|
||||||
|
isNewThread && !hasSubmitted ? "bg-white" : "bg-background",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex size-full justify-center">
|
||||||
|
<MessageList
|
||||||
|
className={cn(
|
||||||
|
"size-full",
|
||||||
|
(!isNewThread || hasSubmitted) && "pt-10",
|
||||||
|
)}
|
||||||
|
threadId={threadId}
|
||||||
|
thread={thread}
|
||||||
|
suppressThreadLoading={suppressExistingThreadPrefetchUi}
|
||||||
|
messagesOverride={
|
||||||
|
suppressExistingThreadPrefetchUi
|
||||||
|
? []
|
||||||
|
: !thread.isLoading && finalState?.messages
|
||||||
|
? (finalState.messages as Message[])
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
paddingBottom={todoListCollapsed ? 160 : 280}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"bg-background ml-[20px] rounded-t-[20px] transition-all duration-300 ease-in-out",
|
||||||
|
!artifactsOpen && "opacity-0",
|
||||||
|
artifactPanelOpen
|
||||||
|
? fullscreen
|
||||||
|
? "ml-0 w-full"
|
||||||
|
: "w-[70%]"
|
||||||
|
: "w-0",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-background absolute top-0 right-0 left-0 z-30 mx-4 grid h-[58px] shrink-0 grid-cols-3 items-center border-b transition-all duration-300 ease-in-out",
|
"h-full w-full transition-transform duration-300 ease-in-out",
|
||||||
isNewThread && !hasSubmitted ? "hidden" : "",
|
artifactPanelOpen ? "translate-x-0" : "translate-x-full",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-start overflow-hidden text-sm font-medium">
|
{selectedArtifact ? (
|
||||||
<Button
|
<ArtifactFileDetail
|
||||||
size="sm"
|
className="size-full"
|
||||||
variant="ghost"
|
filepath={selectedArtifact}
|
||||||
className="px-[10px] py-[5px] text-sm font-medium text-[#150033] hover:text-[#150033]/80"
|
threadId={threadId}
|
||||||
onClick={() => setShowExitDialog(true)}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M3.5 10H13.25H15.6875H16.5M3.5 10L7.5625 6M3.5 10L7.5625 14"
|
|
||||||
stroke="#666666"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-center whitespace-nowrap text-[#333333] font-bold overflow-hidden text-sm font-medium">
|
|
||||||
{title !== "Untitled" && (
|
|
||||||
<ThreadTitle threadId={threadId} threadTitle={title} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-end gap-2 overflow-hidden">
|
|
||||||
<DevTodoList
|
|
||||||
className="bg-white"
|
|
||||||
todos={thread.values.todos ?? []}
|
|
||||||
hidden={
|
|
||||||
!thread.values.todos || thread.values.todos.length === 0
|
|
||||||
}
|
|
||||||
trigger={
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="ghost"
|
|
||||||
className="h-full px-[10px] py-[5px] text-sm font-medium text-[#150033] hover:text-[#150033]"
|
|
||||||
>
|
|
||||||
<ListTodoIcon className="size-4" /> To-dos
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
{artifacts?.length > 0 && !artifactsOpen && (
|
<div className="relative flex size-full justify-center px-[20px]">
|
||||||
<Tooltip content="点击可查看生成的文件结果">
|
<div className="absolute top-2 right-2 z-30">
|
||||||
<Button
|
<Button
|
||||||
className="text-[#150033] hover:text-[#150033]/80"
|
size="icon-sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setArtifactsOpen(true);
|
setArtifactsOpen(false);
|
||||||
setSidebarOpen(false);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FilesIcon />
|
<XIcon />
|
||||||
{t.common.artifacts}
|
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</div>
|
||||||
)}
|
{thread.values.artifacts?.length === 0 ? (
|
||||||
</div>
|
<ConversationEmptyState
|
||||||
</header>
|
icon={<FilesIcon />}
|
||||||
<main
|
title="No artifact selected"
|
||||||
className={cn(
|
description="Select an artifact to view its details"
|
||||||
"flex min-h-0 max-w-full grow flex-col",
|
/>
|
||||||
isNewThread && !hasSubmitted ? "bg-white" : "bg-background",
|
) : (
|
||||||
|
<div className="flex size-full max-w-(--container-width-sm) flex-col justify-center p-4">
|
||||||
|
<header className="shrink-0">
|
||||||
|
<h2 className="text-[14px] font-bold text-[#333333]">
|
||||||
|
{t.common.artifacts}
|
||||||
|
</h2>
|
||||||
|
</header>
|
||||||
|
<main className="min-h-0 grow">
|
||||||
|
<ArtifactFileList
|
||||||
|
className="max-w-(--container-width-sm) p-4 pt-12"
|
||||||
|
files={thread.values.artifacts ?? []}
|
||||||
|
threadId={threadId}
|
||||||
|
/>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
>
|
</div>
|
||||||
<div className="flex size-full justify-center">
|
|
||||||
<MessageList
|
|
||||||
className={cn("size-full", (!isNewThread || hasSubmitted) && "pt-10")}
|
|
||||||
threadId={threadId}
|
|
||||||
thread={thread}
|
|
||||||
suppressThreadLoading={suppressExistingThreadPrefetchUi}
|
|
||||||
messagesOverride={
|
|
||||||
suppressExistingThreadPrefetchUi
|
|
||||||
? []
|
|
||||||
: !thread.isLoading && finalState?.messages
|
|
||||||
? (finalState.messages as Message[])
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
paddingBottom={todoListCollapsed ? 160 : 280}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Fixed 底部居中输入框容器 */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-background ml-[20px] rounded-t-[20px] transition-all duration-300 ease-in-out",
|
"pointer-events-none fixed right-0 bottom-3 left-0 z-30 flex justify-center px-4",
|
||||||
!artifactsOpen && "opacity-0",
|
"transition-all duration-300 ease-in-out",
|
||||||
artifactPanelOpen ? (fullscreen ? "w-full ml-0" : "w-[70%]") : "w-0",
|
fullscreen ? "hidden" : "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-full w-full transition-transform duration-300 ease-in-out",
|
"pointer-events-auto relative w-full max-w-[720px]",
|
||||||
artifactPanelOpen ? "translate-x-0" : "translate-x-full",
|
isNewThread && !hasSubmitted && "-translate-y-[calc(50vh-96px)]",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{selectedArtifact ? (
|
<InputBox
|
||||||
<ArtifactFileDetail
|
className={cn("w-full rounded-[20px] bg-[#FBFAFC]")}
|
||||||
className="size-full"
|
isNewThread={isNewThread}
|
||||||
filepath={selectedArtifact}
|
hasSubmitted={hasSubmitted}
|
||||||
threadId={threadId}
|
autoFocus={isNewThread}
|
||||||
/>
|
status={
|
||||||
) : (
|
suppressExistingThreadPrefetchUi
|
||||||
<div className="relative flex size-full justify-center px-[20px]">
|
? "ready"
|
||||||
<div className="absolute top-2 right-2 z-30">
|
: thread.isLoading
|
||||||
<Button
|
? "streaming"
|
||||||
size="icon-sm"
|
: "ready"
|
||||||
variant="ghost"
|
}
|
||||||
onClick={() => {
|
context={settings.context}
|
||||||
setArtifactsOpen(false);
|
extraHeader={
|
||||||
}}
|
<div className="flex flex-col gap-4">
|
||||||
>
|
{isNewThread && !hasSubmitted && (
|
||||||
<XIcon />
|
<Welcome mode={settings.context.mode} />
|
||||||
</Button>
|
)}
|
||||||
</div>
|
</div>
|
||||||
{thread.values.artifacts?.length === 0 ? (
|
}
|
||||||
<ConversationEmptyState
|
disabled={
|
||||||
icon={<FilesIcon />}
|
env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" ||
|
||||||
title="No artifact selected"
|
isSelectedSkillBootstrapping
|
||||||
description="Select an artifact to view its details"
|
}
|
||||||
/>
|
onContextChange={(context) => setSettings("context", context)}
|
||||||
) : (
|
onSubmit={handleSubmit}
|
||||||
<div className="flex size-full max-w-(--container-width-sm) flex-col justify-center p-4">
|
onStop={handleStop}
|
||||||
<header className="shrink-0">
|
/>
|
||||||
<h2 className="text-[14px] text-[#333333] font-bold">
|
{isSelectedSkillBootstrapping && (
|
||||||
{t.common.artifacts}
|
<div className="text-muted-foreground w-full translate-y-8 text-center text-xs">
|
||||||
</h2>
|
正在初始化 Skill 文件...
|
||||||
</header>
|
</div>
|
||||||
<main className="min-h-0 grow">
|
)}
|
||||||
<ArtifactFileList
|
{env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" && (
|
||||||
className="max-w-(--container-width-sm) p-4 pt-12"
|
<div className="text-muted-foreground/67 w-full translate-y-12 text-center text-xs">
|
||||||
files={thread.values.artifacts ?? []}
|
{t.common.notAvailableInDemoMode}
|
||||||
threadId={threadId}
|
|
||||||
/>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Fixed 底部居中输入框容器 */}
|
{/* 退出确认对话框 */}
|
||||||
<div
|
<DevDialog open={showExitDialog} onOpenChange={setShowExitDialog}>
|
||||||
className={cn(
|
<DevDialogContent>
|
||||||
"pointer-events-none fixed right-0 bottom-3 left-0 z-30 flex justify-center px-4",
|
<DevDialogHeader>
|
||||||
"transition-all duration-300 ease-in-out",
|
<DevDialogTitle>提示</DevDialogTitle>
|
||||||
fullscreen ? "hidden" : "",
|
</DevDialogHeader>
|
||||||
)}
|
<p className="text-muted-foreground text-sm">
|
||||||
>
|
退出后,当前会话结束并销毁,请先下载保存当前结果!
|
||||||
<div
|
</p>
|
||||||
className={cn(
|
<DevDialogFooter>
|
||||||
"pointer-events-auto relative w-full max-w-[720px]",
|
<Button
|
||||||
isNewThread && !hasSubmitted && "-translate-y-[calc(50vh-96px)]",
|
className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white"
|
||||||
)}
|
variant="ghost"
|
||||||
|
onClick={() => setShowExitDialog(false)}
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => {
|
||||||
|
setShowExitDialog(false);
|
||||||
|
router.push("/workspace/chats/new");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</Button>
|
||||||
|
</DevDialogFooter>
|
||||||
|
</DevDialogContent>
|
||||||
|
</DevDialog>
|
||||||
|
|
||||||
|
{/* selectedSkill 失败:错误弹窗 */}
|
||||||
|
<DevDialog
|
||||||
|
open={!!selectedSkillError}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) clearSelectedSkillError();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<InputBox
|
<DevDialogContent>
|
||||||
className={cn("w-full rounded-[20px] bg-[#FBFAFC]")}
|
<DevDialogHeader>
|
||||||
isNewThread={isNewThread}
|
<DevDialogTitle>
|
||||||
hasSubmitted={hasSubmitted}
|
⚠️ {selectedSkillError?.title ?? "技能加载失败"}
|
||||||
autoFocus={isNewThread}
|
</DevDialogTitle>
|
||||||
status={
|
</DevDialogHeader>
|
||||||
suppressExistingThreadPrefetchUi
|
<p className="text-muted-foreground text-sm">
|
||||||
? "ready"
|
{selectedSkillError?.message ?? "发生了未知错误,请稍后重试。"}
|
||||||
: thread.isLoading
|
</p>
|
||||||
? "streaming"
|
<DevDialogFooter singleColumn>
|
||||||
: "ready"
|
<Button
|
||||||
}
|
className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white"
|
||||||
context={settings.context}
|
variant="ghost"
|
||||||
extraHeader={
|
onClick={clearSelectedSkillError}
|
||||||
<div className="flex flex-col gap-4">
|
>
|
||||||
{isNewThread && !hasSubmitted && <Welcome mode={settings.context.mode} />}
|
关闭
|
||||||
</div>
|
</Button>
|
||||||
}
|
</DevDialogFooter>
|
||||||
disabled={
|
</DevDialogContent>
|
||||||
env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" ||
|
</DevDialog>
|
||||||
isSelectedSkillBootstrapping
|
|
||||||
}
|
|
||||||
onContextChange={(context) => setSettings("context", context)}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
onStop={handleStop}
|
|
||||||
/>
|
|
||||||
{isSelectedSkillBootstrapping && (
|
|
||||||
<div className="text-muted-foreground w-full translate-y-8 text-center text-xs">
|
|
||||||
正在初始化 Skill 文件...
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" && (
|
|
||||||
<div className="text-muted-foreground/67 w-full translate-y-12 text-center text-xs">
|
|
||||||
{t.common.notAvailableInDemoMode}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 退出确认对话框 */}
|
{/* MARK: 开发测试:iframe 通信功能测试面板 */}
|
||||||
<DevDialog open={showExitDialog} onOpenChange={setShowExitDialog}>
|
{/* <IframeTestPanel /> */}
|
||||||
<DevDialogContent>
|
|
||||||
<DevDialogHeader>
|
|
||||||
<DevDialogTitle>提示</DevDialogTitle>
|
|
||||||
</DevDialogHeader>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
退出后,当前会话结束并销毁,请先下载保存当前结果!
|
|
||||||
</p>
|
|
||||||
<DevDialogFooter>
|
|
||||||
<Button
|
|
||||||
className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => setShowExitDialog(false)}
|
|
||||||
>
|
|
||||||
取消
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => {
|
|
||||||
setShowExitDialog(false);
|
|
||||||
router.push("/workspace/chats/new");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
确定
|
|
||||||
</Button>
|
|
||||||
</DevDialogFooter>
|
|
||||||
</DevDialogContent>
|
|
||||||
</DevDialog>
|
|
||||||
|
|
||||||
{/* selectedSkill 失败:错误弹窗 */}
|
|
||||||
<DevDialog
|
|
||||||
open={!!selectedSkillError}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) clearSelectedSkillError();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DevDialogContent>
|
|
||||||
<DevDialogHeader>
|
|
||||||
<DevDialogTitle>
|
|
||||||
⚠️ {selectedSkillError?.title ?? "技能加载失败"}
|
|
||||||
</DevDialogTitle>
|
|
||||||
</DevDialogHeader>
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
{selectedSkillError?.message ?? "发生了未知错误,请稍后重试。"}
|
|
||||||
</p>
|
|
||||||
<DevDialogFooter singleColumn>
|
|
||||||
<Button
|
|
||||||
className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={clearSelectedSkillError}
|
|
||||||
>
|
|
||||||
关闭
|
|
||||||
</Button>
|
|
||||||
</DevDialogFooter>
|
|
||||||
</DevDialogContent>
|
|
||||||
</DevDialog>
|
|
||||||
|
|
||||||
{/* MARK: 开发测试:iframe 通信功能测试面板 */}
|
|
||||||
{/* <IframeTestPanel /> */}
|
|
||||||
</div>
|
</div>
|
||||||
</ThreadContext.Provider>
|
</ThreadContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -76,4 +76,4 @@ export default function WorkspaceLayout({
|
||||||
/>
|
/>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,7 @@ export const ArtifactHeader = ({
|
||||||
...props
|
...props
|
||||||
}: ArtifactHeaderProps) => (
|
}: ArtifactHeaderProps) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn("mb-[10px] flex items-center justify-between", className)}
|
||||||
"mb-[10px] flex items-center justify-between",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ export const ChainOfThoughtStep = memo(
|
||||||
{isValidElement(Icon) ? (
|
{isValidElement(Icon) ? (
|
||||||
Icon
|
Icon
|
||||||
) : (
|
) : (
|
||||||
<Icon className="size-4 stroke-[1.5px] stroke-[#333333] text-[#333333]" />
|
<Icon className="size-4 stroke-[#333333] stroke-[1.5px] text-[#333333]" />
|
||||||
)}
|
)}
|
||||||
<div className="bg-border absolute top-7 bottom-0 left-1/2 -mx-px w-px" />
|
<div className="bg-border absolute top-7 bottom-0 left-1/2 -mx-px w-px" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ export const Suggestion = ({
|
||||||
<Button
|
<Button
|
||||||
className={cn(
|
className={cn(
|
||||||
"cursor-pointer rounded-full px-[20px] py-[15px] text-xs font-normal",
|
"cursor-pointer rounded-full px-[20px] py-[15px] text-xs font-normal",
|
||||||
"bg-[#F9F8FA] text-[#666666] border-none",
|
"border-none bg-[#F9F8FA] text-[#666666]",
|
||||||
"hover:bg-[#EAE9EB] hover:text-[#150033]",
|
"hover:bg-[#EAE9EB] hover:text-[#150033]",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ function ToggleGroupItem({
|
||||||
variant: context.variant || variant,
|
variant: context.variant || variant,
|
||||||
size: context.size || size,
|
size: context.size || size,
|
||||||
}),
|
}),
|
||||||
"w-[50px] h-full min-w-0 shrink-0 bg-white cursor-pointer px-3 focus:z-10 focus-visible:z-10",
|
"h-full w-[50px] min-w-0 shrink-0 cursor-pointer bg-white px-3 focus:z-10 focus-visible:z-10",
|
||||||
"data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]",
|
"data-[spacing=0]:data-[variant=outline] data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,8 @@ export function ArtifactFileDetail({
|
||||||
threadId: string;
|
threadId: string;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { artifacts, setOpen, select, fullscreen, setFullscreen } = useArtifacts();
|
const { artifacts, setOpen, select, fullscreen, setFullscreen } =
|
||||||
|
useArtifacts();
|
||||||
const isWriteFile = useMemo(() => {
|
const isWriteFile = useMemo(() => {
|
||||||
return filepathFromProps.startsWith("write-file:");
|
return filepathFromProps.startsWith("write-file:");
|
||||||
}, [filepathFromProps]);
|
}, [filepathFromProps]);
|
||||||
|
|
@ -117,7 +118,10 @@ export function ArtifactFileDetail({
|
||||||
const handleFullscreenToggle = useCallback(() => {
|
const handleFullscreenToggle = useCallback(() => {
|
||||||
const newFullscreen = !fullscreen;
|
const newFullscreen = !fullscreen;
|
||||||
setFullscreen(newFullscreen);
|
setFullscreen(newFullscreen);
|
||||||
sendToParent({ type: POST_MESSAGE_TYPES.FULLSCREEN, fullscreen: newFullscreen });
|
sendToParent({
|
||||||
|
type: POST_MESSAGE_TYPES.FULLSCREEN,
|
||||||
|
fullscreen: newFullscreen,
|
||||||
|
});
|
||||||
}, [fullscreen, setFullscreen]);
|
}, [fullscreen, setFullscreen]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -167,18 +171,47 @@ export function ArtifactFileDetail({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ToggleGroupItem value="code">
|
<ToggleGroupItem value="code">
|
||||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg
|
||||||
<path d="M5 6L2 9L5 12" stroke="#150033" stroke-linecap="round" stroke-linejoin="round"/>
|
width="18"
|
||||||
<path d="M11 3L7 15" stroke="#150033" stroke-linecap="round" stroke-linejoin="round"/>
|
height="18"
|
||||||
<path d="M13 6L16 9L13 12" stroke="#150033" stroke-linecap="round" stroke-linejoin="round"/>
|
viewBox="0 0 18 18"
|
||||||
</svg>
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5 6L2 9L5 12"
|
||||||
|
stroke="#150033"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M11 3L7 15"
|
||||||
|
stroke="#150033"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M13 6L16 9L13 12"
|
||||||
|
stroke="#150033"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem value="preview">
|
<ToggleGroupItem value="preview">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="10" viewBox="0 0 16 10" fill="none">
|
<svg
|
||||||
<path d="M8 0.5C10.4943 0.5 12.8473 1.84466 14.792 4.21973C15.1644 4.67466 15.1644 5.32534 14.792 5.78027C12.8473 8.15534 10.4943 9.5 8 9.5C5.50561 9.49989 3.15269 8.15543 1.20801 5.78027C0.835561 5.32534 0.835562 4.67466 1.20801 4.21973C3.15269 1.84457 5.50561 0.500106 8 0.5Z" stroke="#666666"/>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<circle cx="8" cy="5" r="1.5" stroke="#666666"/>
|
width="16"
|
||||||
</svg>
|
height="10"
|
||||||
|
viewBox="0 0 16 10"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8 0.5C10.4943 0.5 12.8473 1.84466 14.792 4.21973C15.1644 4.67466 15.1644 5.32534 14.792 5.78027C12.8473 8.15534 10.4943 9.5 8 9.5C5.50561 9.49989 3.15269 8.15543 1.20801 5.78027C0.835561 5.32534 0.835562 4.67466 1.20801 4.21973C3.15269 1.84457 5.50561 0.500106 8 0.5Z"
|
||||||
|
stroke="#666666"
|
||||||
|
/>
|
||||||
|
<circle cx="8" cy="5" r="1.5" stroke="#666666" />
|
||||||
|
</svg>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
)}
|
)}
|
||||||
|
|
@ -199,7 +232,6 @@ export function ArtifactFileDetail({
|
||||||
</ArtifactTitle>
|
</ArtifactTitle>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-end overflow-hidden">
|
<div className="flex items-center justify-end overflow-hidden">
|
||||||
|
|
||||||
<ArtifactActions>
|
<ArtifactActions>
|
||||||
{isCodeFile && (
|
{isCodeFile && (
|
||||||
<ArtifactAction
|
<ArtifactAction
|
||||||
|
|
@ -464,7 +496,7 @@ export const ArtifactZoomSelector = ({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center gap-1 rounded-[10px] h-[28px] bg-white backdrop-blur-sm",
|
"inline-flex h-[28px] items-center gap-1 rounded-[10px] bg-white backdrop-blur-sm",
|
||||||
"dark:border-gray-700/50 dark:bg-gray-800/90",
|
"dark:border-gray-700/50 dark:bg-gray-800/90",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|
@ -475,18 +507,32 @@ export const ArtifactZoomSelector = ({
|
||||||
onClick={handleZoomIn}
|
onClick={handleZoomIn}
|
||||||
disabled={!canZoomIn}
|
disabled={!canZoomIn}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-full w-10 py-1 items-center justify-center rounded transition-colors",
|
"flex h-full w-10 items-center justify-center rounded py-1 transition-colors",
|
||||||
"text-gray-400 hover:bg-gray-100 hover:text-gray-600",
|
"text-gray-400 hover:bg-gray-100 hover:text-gray-600",
|
||||||
"disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent",
|
"disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent",
|
||||||
"dark:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-gray-300",
|
"dark:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-gray-300",
|
||||||
)}
|
)}
|
||||||
aria-label="放大"
|
aria-label="放大"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
<svg
|
||||||
<circle cx="7.55558" cy="7.55534" r="6.16667" stroke="#666666"/>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path d="M13.8688 15.4646C14.064 15.6598 14.3806 15.6598 14.5759 15.4646C14.7711 15.2693 14.7711 14.9527 14.5759 14.7574L14.2223 15.111L13.8688 15.4646ZM14.2223 15.111L14.5759 14.7574L11.9092 12.0908L11.5557 12.4443L11.2021 12.7979L13.8688 15.4646L14.2223 15.111Z" fill="#666666"/>
|
width="16"
|
||||||
<path d="M5.33325 7.5H9.7777M7.55547 5V10" stroke="#666666" stroke-linecap="round" stroke-linejoin="round"/>
|
height="16"
|
||||||
</svg>
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<circle cx="7.55558" cy="7.55534" r="6.16667" stroke="#666666" />
|
||||||
|
<path
|
||||||
|
d="M13.8688 15.4646C14.064 15.6598 14.3806 15.6598 14.5759 15.4646C14.7711 15.2693 14.7711 14.9527 14.5759 14.7574L14.2223 15.111L13.8688 15.4646ZM14.2223 15.111L14.5759 14.7574L11.9092 12.0908L11.5557 12.4443L11.2021 12.7979L13.8688 15.4646L14.2223 15.111Z"
|
||||||
|
fill="#666666"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5.33325 7.5H9.7777M7.55547 5V10"
|
||||||
|
stroke="#666666"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
@ -508,11 +554,25 @@ export const ArtifactZoomSelector = ({
|
||||||
)}
|
)}
|
||||||
aria-label="缩小"
|
aria-label="缩小"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
<svg
|
||||||
<circle cx="7.55558" cy="7.55534" r="6.16667" stroke="#666666"/>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path d="M13.8688 15.4646C14.064 15.6598 14.3806 15.6598 14.5759 15.4646C14.7711 15.2693 14.7711 14.9527 14.5759 14.7574L14.2223 15.111L13.8688 15.4646ZM14.2223 15.111L14.5759 14.7574L11.9092 12.0908L11.5557 12.4443L11.2021 12.7979L13.8688 15.4646L14.2223 15.111Z" fill="#666666"/>
|
width="16"
|
||||||
<path d="M4.99927 7.5H9.99927" stroke="#666666" stroke-linecap="round" stroke-linejoin="round"/>
|
height="16"
|
||||||
</svg>
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<circle cx="7.55558" cy="7.55534" r="6.16667" stroke="#666666" />
|
||||||
|
<path
|
||||||
|
d="M13.8688 15.4646C14.064 15.6598 14.3806 15.6598 14.5759 15.4646C14.7711 15.2693 14.7711 14.9527 14.5759 14.7574L14.2223 15.111L13.8688 15.4646ZM14.2223 15.111L14.5759 14.7574L11.9092 12.0908L11.5557 12.4443L11.2021 12.7979L13.8688 15.4646L14.2223 15.111Z"
|
||||||
|
fill="#666666"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4.99927 7.5H9.99927"
|
||||||
|
stroke="#666666"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,8 @@ export function InputBox({
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
|
||||||
// isNewThread 时禁用收缩,始终保持展开(除非已提交消息)
|
// isNewThread 时禁用收缩,始终保持展开(除非已提交消息)
|
||||||
const effectiveIsFocused = (isNewThread ?? false) && !hasSubmitted || isFocused;
|
const effectiveIsFocused =
|
||||||
|
((isNewThread ?? false) && !hasSubmitted) || isFocused;
|
||||||
|
|
||||||
// 点击外部区域时收起输入框
|
// 点击外部区域时收起输入框
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ export function MessageList({
|
||||||
<Conversation
|
<Conversation
|
||||||
className={cn("flex size-full flex-col justify-center", className)}
|
className={cn("flex size-full flex-col justify-center", className)}
|
||||||
>
|
>
|
||||||
<ConversationContent className="px-[20px] w-full gap-8 pt-12">
|
<ConversationContent className="w-full gap-8 px-[20px] pt-12">
|
||||||
{groupMessages(messages, (group) => {
|
{groupMessages(messages, (group) => {
|
||||||
if (group.type === "human" || group.type === "assistant") {
|
if (group.type === "human" || group.type === "assistant") {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,8 @@ export const zhCN: Translations = {
|
||||||
suggestions: [
|
suggestions: [
|
||||||
{
|
{
|
||||||
suggestion: "自媒体文案",
|
suggestion: "自媒体文案",
|
||||||
prompt: "为[主题/产品]撰写吸引人的自媒体文案,包括标题、正文和话题标签。",
|
prompt:
|
||||||
|
"为[主题/产品]撰写吸引人的自媒体文案,包括标题、正文和话题标签。",
|
||||||
icon: PenLineIcon,
|
icon: PenLineIcon,
|
||||||
skill_id: "432",
|
skill_id: "432",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@ export const RECEIVE_MESSAGE_TYPES = {
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// 消息类型
|
// 消息类型
|
||||||
export type PostMessageType = (typeof POST_MESSAGE_TYPES)[keyof typeof POST_MESSAGE_TYPES];
|
export type PostMessageType =
|
||||||
export type ReceiveMessageType = (typeof RECEIVE_MESSAGE_TYPES)[keyof typeof RECEIVE_MESSAGE_TYPES];
|
(typeof POST_MESSAGE_TYPES)[keyof typeof POST_MESSAGE_TYPES];
|
||||||
|
export type ReceiveMessageType =
|
||||||
|
(typeof RECEIVE_MESSAGE_TYPES)[keyof typeof RECEIVE_MESSAGE_TYPES];
|
||||||
|
|
||||||
// 消息数据类型
|
// 消息数据类型
|
||||||
export interface FullscreenMessage {
|
export interface FullscreenMessage {
|
||||||
|
|
@ -48,8 +50,10 @@ export interface SelectedSkillMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送消息的辅助函数
|
// 发送消息的辅助函数
|
||||||
export function sendToParent(message: FullscreenMessage | SelectSkillMessage | OpenSkillDialogMessage): void {
|
export function sendToParent(
|
||||||
|
message: FullscreenMessage | SelectSkillMessage | OpenSkillDialogMessage,
|
||||||
|
): void {
|
||||||
if (window.parent !== window) {
|
if (window.parent !== window) {
|
||||||
window.parent.postMessage(message, "*");
|
window.parent.postMessage(message, "*");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,9 @@ export function useSelectedSkillListener({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[useSelectedSkillListener] 开始初始化技能: ${title} (${id})`);
|
console.log(
|
||||||
|
`[useSelectedSkillListener] 开始初始化技能: ${title} (${id})`,
|
||||||
|
);
|
||||||
setIsBootstrapping(true);
|
setIsBootstrapping(true);
|
||||||
toast.loading(`正在加载技能「${title}」...`, { id: "skill-bootstrap" });
|
toast.loading(`正在加载技能「${title}」...`, { id: "skill-bootstrap" });
|
||||||
|
|
||||||
|
|
@ -88,7 +90,8 @@ export function useSelectedSkillListener({
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
skillBootstrappedKeyRef.current = initKey;
|
skillBootstrappedKeyRef.current = initKey;
|
||||||
toast.success(`技能「${title}」加载成功`, {
|
toast.success(`技能「${title}」加载成功`, {
|
||||||
description: result.message || `已创建 ${result.created_files} 个文件`,
|
description:
|
||||||
|
result.message || `已创建 ${result.created_files} 个文件`,
|
||||||
duration: 4000,
|
duration: 4000,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -127,7 +130,10 @@ export function useSelectedSkillListener({
|
||||||
if (data?.type !== "selectedSkill") return;
|
if (data?.type !== "selectedSkill") return;
|
||||||
|
|
||||||
const { id, title } = data;
|
const { id, title } = data;
|
||||||
console.log("[useSelectedSkillListener] 收到 postMessage selectedSkill:", data);
|
console.log(
|
||||||
|
"[useSelectedSkillListener] 收到 postMessage selectedSkill:",
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
|
||||||
setSelectedSkill({ skill_id: String(id), title });
|
setSelectedSkill({ skill_id: String(id), title });
|
||||||
void performBootstrap(id, title);
|
void performBootstrap(id, title);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue