feat: 优化输入框交互与添加技能选择按钮
- 重构输入框展开/收缩逻辑,支持 isNewThread 时保持展开 - 添加 onFocusChange 回调支持 - 新增 IframeSkillDialogButton 替换原有的 ModeHoverGuide - 为 AddAttachmentsButton 和 IframeSkillDialogButton 添加 hover 变色效果 - 调整输入框布局结构,移出 PromptInputSubmit 到外层 - 优化 Footer 工具栏的过渡动画 - 添加 selectSkill 国际化文本
This commit is contained in:
parent
4897a4da58
commit
6130f12790
|
|
@ -19,7 +19,6 @@ import {
|
|||
useThreadChat,
|
||||
} from "@/components/workspace/chats";
|
||||
import { DevTodoList } from "@/components/workspace/dev-todo-list";
|
||||
import { Tooltip } from "@/components/workspace/tooltip";
|
||||
import { InputBox } from "@/components/workspace/input-box";
|
||||
import { MessageList } from "@/components/workspace/messages";
|
||||
import { ThreadContext } from "@/components/workspace/messages/context";
|
||||
|
|
@ -69,6 +68,7 @@ export default function ChatPage() {
|
|||
}
|
||||
},
|
||||
});
|
||||
console.log(thread.values.todos);
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
(message: PromptInputMessage) => {
|
||||
|
|
@ -86,25 +86,25 @@ export default function ChatPage() {
|
|||
<div className="relative flex size-full min-h-0 bg-background justify-between">
|
||||
<header
|
||||
className={cn(
|
||||
"absolute top-0 right-0 left-0 z-30 grid grid-cols-3 h-14 px-[20px] py-[20px] shrink-0 items-center rounded-t-[20px]",
|
||||
"absolute top-0 right-0 left-0 z-30 grid grid-cols-3 h-[58px] px-[20px] py-[15px] shrink-0 items-center rounded-t-[20px]",
|
||||
isNewThread
|
||||
? "bg-background/0 backdrop-blur-none"
|
||||
: "bg-background/80 shadow-xs backdrop-blur",
|
||||
)}
|
||||
>
|
||||
{/* 返回查看结果左箭头 */}
|
||||
<div className=" w-full items-center h-[18px] text-sm font-medium">
|
||||
<button onClick={() => setShowExitDialog(true)}>
|
||||
<div className="flex w-full items-center h-full text-sm font-medium">
|
||||
<button className="bg-transparent" 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=" w-full text-center items-center h-[18px] text-sm font-medium">
|
||||
<div className="flex w-full justify-center items-center h-full text-sm font-medium overflow-hidden">
|
||||
<ThreadTitle threadId={threadId} thread={thread} />
|
||||
</div>
|
||||
<div className="flex justify-end items-center gap-2">
|
||||
<div className="flex overflow-hidden justify-end items-center gap-2">
|
||||
<DevTodoList
|
||||
className="bg-white"
|
||||
todos={thread.values.todos ?? []}
|
||||
|
|
@ -112,11 +112,9 @@ export default function ChatPage() {
|
|||
!thread.values.todos || thread.values.todos.length === 0
|
||||
}
|
||||
trigger={
|
||||
<Tooltip content="Show Todo">
|
||||
<Button size="sm" variant="ghost" className="text-sm font-medium h-[18px]">
|
||||
<ListTodoIcon className="size-4" /> Todo
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button size="sm" variant="ghost" className="text-sm font-medium py-[5px] px-[10px] h-full">
|
||||
<ListTodoIcon className="size-4" /> Todo
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<ArtifactTrigger />
|
||||
|
|
@ -143,7 +141,7 @@ export default function ChatPage() {
|
|||
)}
|
||||
>
|
||||
<InputBox
|
||||
className={cn("bg-background/5 w-full -translate-y-4")}
|
||||
className={cn("bg-[#FBFAFC] w-full -translate-y-4 ")}
|
||||
isNewThread={isNewThread}
|
||||
threadId={threadId}
|
||||
autoFocus={isNewThread}
|
||||
|
|
@ -176,10 +174,12 @@ export default function ChatPage() {
|
|||
退出后,当前会话结束并销毁,请先下载保存当前结果!
|
||||
</p>
|
||||
<DevDialogFooter >
|
||||
<Button className="w-full " variant="outline" onClick={() => setShowExitDialog(false)}>
|
||||
<Button className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white" variant="ghost" onClick={() => setShowExitDialog(false)}>
|
||||
取消
|
||||
</Button>
|
||||
<Button className="w-full bg-[#8E47F0] hover:!bg-[#8E47F0]" onClick={() => setShowExitDialog(false)}>去充值</Button>
|
||||
<Button className="w-full bg-[#f9f8fa] hover:bg-[#8E47F0] hover:text-white" variant="ghost" onClick={() => setShowExitDialog(false)}>
|
||||
确定
|
||||
</Button>
|
||||
</DevDialogFooter>
|
||||
</DevDialogContent>
|
||||
</DevDialog>
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ export const ChainOfThoughtContent = memo(
|
|||
<Collapsible open={isOpen}>
|
||||
<CollapsibleContent
|
||||
className={cn(
|
||||
"mt-2 space-y-3",
|
||||
"mt-2 space-y-3 bg-[#ffffff]",
|
||||
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground data-[state=closed]:animate-out data-[state=open]:animate-in outline-none",
|
||||
className,
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export const ConversationContent = ({
|
|||
...props
|
||||
}: ConversationContentProps) => (
|
||||
<StickToBottom.Content
|
||||
className={cn("flex flex-col gap-8 p-4", className)}
|
||||
className={cn("flex flex-col gap-8 p-[20px]", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ export type MessageProps = HTMLAttributes<HTMLDivElement> & {
|
|||
export const Message = ({ className, from, ...props }: MessageProps) => (
|
||||
<div
|
||||
className={cn(
|
||||
"group flex w-full flex-col gap-2",
|
||||
from === "user" ? "is-user ml-auto justify-end" : "is-assistant",
|
||||
"group flex w-full flex-col gap-2 rounded-[10px] p-[20px]",
|
||||
from === "user" ? "is-user ml-auto justify-end" : "is-assistant bg-[#ffffff]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -457,10 +457,13 @@ export type PromptInputProps = Omit<
|
|||
message: PromptInputMessage,
|
||||
event: FormEvent<HTMLFormElement>,
|
||||
) => void | Promise<void>;
|
||||
// className for InputGroup (passes through to inner InputGroup component)
|
||||
inputGroupClassName?: string;
|
||||
};
|
||||
|
||||
export const PromptInput = ({
|
||||
className,
|
||||
inputGroupClassName,
|
||||
accept,
|
||||
disabled,
|
||||
multiple,
|
||||
|
|
@ -794,7 +797,7 @@ export const PromptInput = ({
|
|||
ref={formRef}
|
||||
{...props}
|
||||
>
|
||||
<InputGroup>{children}</InputGroup>
|
||||
<InputGroup className={inputGroupClassName}>{children}</InputGroup>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
|
|
@ -1027,7 +1030,7 @@ export type PromptInputSubmitProps = ComponentProps<typeof InputGroupButton> & {
|
|||
export const PromptInputSubmit = ({
|
||||
className,
|
||||
variant = "default",
|
||||
size = "icon-sm",
|
||||
size = "sm",
|
||||
status,
|
||||
children,
|
||||
...props
|
||||
|
|
@ -1045,13 +1048,15 @@ export const PromptInputSubmit = ({
|
|||
return (
|
||||
<InputGroupButton
|
||||
aria-label="Submit"
|
||||
className={cn(className)}
|
||||
// 被button{bgc:#fff}覆盖了,只能加"!"
|
||||
className={cn(className,'rounded-[10px] w-[140px] h-[40px] text-[#8E47F0] font-bold !bg-[#F0E8FB] hover:!bg-[#8E47F0] hover:text-[#FFFFFF]')}
|
||||
size={size}
|
||||
type="submit"
|
||||
variant={variant}
|
||||
{...props}
|
||||
>
|
||||
{children ?? Icon}
|
||||
发送
|
||||
</InputGroupButton>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -186,8 +186,8 @@ export const QueueList = ({
|
|||
className,
|
||||
...props
|
||||
}: QueueListProps) => (
|
||||
<ScrollArea className={cn("-mb-1 mt-2", className)} {...props}>
|
||||
<div className="max-h-40 pr-4">
|
||||
<ScrollArea className={cn("-mb-1", className)} {...props}>
|
||||
<div className="max-h-40">
|
||||
<ul>{children}</ul>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export const Suggestion = ({
|
|||
return (
|
||||
<Button
|
||||
className={cn(
|
||||
"text-muted-foreground cursor-pointer rounded-full px-4 text-xs font-normal",
|
||||
"text-muted-foreground cursor-pointer rounded-full px-[20px] py-[15px] text-xs font-normal",
|
||||
className,
|
||||
)}
|
||||
onClick={handleClick}
|
||||
|
|
@ -70,7 +70,7 @@ export const Suggestion = ({
|
|||
variant={variant}
|
||||
{...props}
|
||||
>
|
||||
{Icon && <Icon className="size-4" />}
|
||||
{/* {Icon && <Icon className="size-4" />} */}
|
||||
{children || suggestion}
|
||||
</Button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const buttonVariants = cva(
|
|||
secondary:
|
||||
"cursor-pointer bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost:
|
||||
"cursor-pointer hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
"cursor-pointer bg-transparent hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "cursor-pointer text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|||
<div
|
||||
data-slot="card"
|
||||
className={cn(
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6",
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-[20px] not-first:px-[20px] not-first:py-[15px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ function DevDialogContent({
|
|||
<DialogPrimitive.Content
|
||||
data-slot="dev-dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-[400px] max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-[40px] shadow-lg duration-200 outline-none sm:max-w-lg",
|
||||
"bg-[#ffffff] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-[400px] max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-[40px] shadow-lg duration-200 outline-none sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,13 @@ function DropdownMenuPortal({
|
|||
}
|
||||
|
||||
function DropdownMenuTrigger({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Trigger
|
||||
data-slot="dropdown-menu-trigger"
|
||||
className={cn(className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
|
@ -42,7 +44,7 @@ function DropdownMenuContent({
|
|||
data-slot="dropdown-menu-content"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-[20px] border p-[20px] shadow-md",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -230,7 +232,7 @@ function DropdownMenuSubContent({
|
|||
<DropdownMenuPrimitive.SubContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-[20px] border p-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
||||
export interface DropdownSelectorOption<T extends string> {
|
||||
value: T;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface DropdownSelectorProps<T extends string> {
|
||||
value: T;
|
||||
options: DropdownSelectorOption<T>[];
|
||||
onChange: (value: T) => void;
|
||||
triggerClassName?: string;
|
||||
contentClassName?: string;
|
||||
}
|
||||
|
||||
export function DropdownSelector<T extends string>({
|
||||
value,
|
||||
options,
|
||||
onChange,
|
||||
triggerClassName,
|
||||
contentClassName,
|
||||
}: DropdownSelectorProps<T>) {
|
||||
const selectedOption = options.find((opt) => opt.value === value);
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
className={triggerClassName ?? "border-none bg-transparent shadow-none select-none focus:outline-none"}
|
||||
>
|
||||
{selectedOption?.label ?? value}
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className={contentClassName}>
|
||||
<DropdownMenuRadioGroup value={value} onValueChange={onChange}>
|
||||
{options.map((option) => (
|
||||
<DropdownMenuRadioItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|||
data-slot="input-group"
|
||||
role="group"
|
||||
className={cn(
|
||||
"group/input-group border-input/50 dark:bg-background/80 relative flex w-full items-center rounded-md border bg-white/80 shadow-xs transition-[color,box-shadow] outline-none",
|
||||
"group/input-group overflow-hidden border-input/50 dark:bg-background/80 relative flex w-full items-center rounded-md border transition-[color,box-shadow] outline-none",
|
||||
"h-9 min-w-0 has-[>textarea]:h-auto",
|
||||
|
||||
// Variants based on alignment.
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ function TooltipContent({
|
|||
data-slot="tooltip-content"
|
||||
sideOffset={sideOffset ?? 4}
|
||||
className={cn(
|
||||
"animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-foreground text-background dark:text-foreground z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md border px-3 py-1.5 text-xs text-balance shadow-xs dark:border-white/18 dark:bg-[#050504]",
|
||||
"animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-tooltip-background text-background dark:text-foreground z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md border px-3 py-1.5 text-xs text-balance shadow-xs dark:border-white/18 dark:bg-[#050504]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,7 @@ import {
|
|||
ArtifactHeader,
|
||||
ArtifactTitle,
|
||||
} from "@/components/ai-elements/artifact";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { DropdownSelector } from "@/components/ui/dropdown-selector";
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||||
import { CodeEditor } from "@/components/workspace/code-editor";
|
||||
import { useArtifactContent } from "@/core/artifacts/hooks";
|
||||
|
|
@ -91,6 +85,13 @@ export function ArtifactFileDetail({
|
|||
|
||||
const displayContent = content ?? "";
|
||||
|
||||
const artifactOptions = useMemo(() => {
|
||||
return (artifacts ?? []).map((artifactPath) => ({
|
||||
value: artifactPath,
|
||||
label: getFileName(artifactPath),
|
||||
}));
|
||||
}, [artifacts]);
|
||||
|
||||
const [viewMode, setViewMode] = useState<"code" | "preview">("code");
|
||||
const [isInstalling, setIsInstalling] = useState(false);
|
||||
const { isMock } = useThread();
|
||||
|
|
@ -153,20 +154,11 @@ export function ArtifactFileDetail({
|
|||
{isWriteFile ? (
|
||||
<div className="px-2">{getFileName(filepath)}</div>
|
||||
) : (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="border-none bg-transparent shadow-none select-none focus:outline-none">
|
||||
{getFileName(filepath)}
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuRadioGroup value={filepath} onValueChange={select}>
|
||||
{(artifacts ?? []).map((artifactPath) => (
|
||||
<DropdownMenuRadioItem key={artifactPath} value={artifactPath}>
|
||||
{getFileName(artifactPath)}
|
||||
</DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<DropdownSelector
|
||||
value={filepath}
|
||||
options={artifactOptions}
|
||||
onChange={select}
|
||||
/>
|
||||
)}
|
||||
</ArtifactTitle>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -76,14 +76,14 @@ export function ArtifactFileList({
|
|||
{files.map((file) => (
|
||||
<Card
|
||||
key={file}
|
||||
className="relative cursor-pointer p-3"
|
||||
className="relative cursor-pointer py-[15px] px-[20px]"
|
||||
onClick={() => handleClick(file)}
|
||||
>
|
||||
<CardHeader className="pr-2 pl-1">
|
||||
<CardTitle className="relative pl-8">
|
||||
<div>{getFileName(file)}</div>
|
||||
<div className="absolute top-2 -left-0.5">
|
||||
{getFileIcon(file, "size-6")}
|
||||
{getFileIcon(file, "text-[#333333] size-6 stroke-[1.5px]")}
|
||||
</div>
|
||||
</CardTitle>
|
||||
<CardDescription className="pl-8 text-xs">
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ export const ArtifactTrigger = () => {
|
|||
return null;
|
||||
}
|
||||
return (
|
||||
<Tooltip content="Show artifacts of this conversation">
|
||||
<Tooltip content="点击可查看生成的文件结果">
|
||||
<Button
|
||||
className="text-sm font-medium h-[18px]"
|
||||
className="text-sm font-medium py-[5px] px-[10px] h-full"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
setArtifactsOpen(true);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import type { GroupImperativeHandle } from "react-resizable-panels";
|
|||
|
||||
import { ConversationEmptyState } from "@/components/ai-elements/conversation";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DropdownSelector } from "@/components/ui/dropdown-selector";
|
||||
import {
|
||||
ResizableHandle,
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from "@/components/ui/resizable";
|
||||
import { getFileName } from "@/core/utils/files";
|
||||
import { env } from "@/env";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
|
@ -149,8 +151,19 @@ const ChatBox: React.FC<{ children: React.ReactNode; threadId: string }> = ({
|
|||
/>
|
||||
) : (
|
||||
<div className="flex size-full max-w-(--container-width-sm) flex-col justify-center p-4 pt-8">
|
||||
<header className="shrink-0">
|
||||
<h2 className="text-lg font-medium">Artifacts</h2>
|
||||
<header className="shrink-0 flex justify-center">
|
||||
{/* 遍历thread.values.artifacts选择器*/}
|
||||
{thread.values.artifacts && thread.values.artifacts.length > 0 && (
|
||||
<DropdownSelector
|
||||
value={selectedArtifact ?? thread.values.artifacts[0]!}
|
||||
options={thread.values.artifacts.map((artifact) => ({
|
||||
value: artifact,
|
||||
label: getFileName(artifact),
|
||||
}))}
|
||||
onChange={selectArtifact}
|
||||
triggerClassName="text-lg font-medium bg-transparent"
|
||||
/>
|
||||
)}
|
||||
</header>
|
||||
<main className="min-h-0 grow">
|
||||
<ArtifactFileList
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
|
||||
import type { Todo } from "@/core/todos";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import {
|
||||
QueueItem,
|
||||
|
|
@ -26,10 +27,14 @@ export function DevTodoList({
|
|||
trigger: React.ReactNode;
|
||||
hidden: boolean;
|
||||
}) {
|
||||
if (hidden) {
|
||||
return null;
|
||||
}
|
||||
console.log(todos);
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className={hidden ? "hidden" : ""} asChild>{trigger}</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className={className} align="start" side="top">
|
||||
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className={cn("bg-white z-[100]", className)} align="start" side="top">
|
||||
<QueueList className="w-64">
|
||||
{todos.map((todo, i) => (
|
||||
<QueueItem key={i + (todo.content ?? "")}>
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@ export function FlipDisplay({
|
|||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key={uniqueKey}
|
||||
initial={{ y: 8, opacity: 0 }}
|
||||
animate={{ y: 2, opacity: 1 }}
|
||||
exit={{ y: -8, opacity: 0 }}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="overflow-ellipsis overflow-hidden whitespace-nowrap"
|
||||
// transition={{ duration: 0.25, ease: [0.4, 0, 0.2, 1] }}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ export function InputBox({
|
|||
onContextChange,
|
||||
onSubmit,
|
||||
onStop,
|
||||
onFocusChange,
|
||||
...props
|
||||
}: Omit<ComponentProps<typeof PromptInput>, "onSubmit"> & {
|
||||
assistantId?: string | null;
|
||||
|
|
@ -138,6 +139,7 @@ export function InputBox({
|
|||
) => void;
|
||||
onSubmit?: (message: PromptInputMessage) => void;
|
||||
onStop?: () => void;
|
||||
onFocusChange?: (focused: boolean) => void;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const searchParams = useSearchParams();
|
||||
|
|
@ -162,18 +164,22 @@ export function InputBox({
|
|||
const [isFocused, setIsFocused] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// isNewThread 时禁用收缩,始终保持展开
|
||||
const effectiveIsFocused = isNewThread || isFocused;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isFocused) return;
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
||||
setIsFocused(false);
|
||||
onFocusChange?.(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||
}, [isFocused]);
|
||||
}, [isFocused, onFocusChange]);
|
||||
|
||||
useEffect(() => {
|
||||
if (models.length === 0) {
|
||||
|
|
@ -390,29 +396,36 @@ export function InputBox({
|
|||
|
||||
return (
|
||||
<div ref={(el) => { promptRootRef.current = el; containerRef.current = el; }} className="relative">
|
||||
{extraHeader && (
|
||||
<div className="absolute right-0 bottom-full left-0 z-30 flex items-center justify-center pb-4">
|
||||
{extraHeader}
|
||||
</div>
|
||||
)}
|
||||
{/* 输入框主容器 */}
|
||||
<PromptInput
|
||||
className={cn(
|
||||
"bg-background/85 rounded-2xl backdrop-blur-sm transition-all duration-300 ease-out *:data-[slot='input-group']:rounded-2xl",
|
||||
!isFocused && "h-12",
|
||||
"w-full",
|
||||
className,
|
||||
)}
|
||||
inputGroupClassName={cn(
|
||||
" backdrop-blur-sm rounded-[20px]",
|
||||
"transition-[height] duration-300 ease-out",
|
||||
!isNewThread && "shadow-[0_0_20px_2px_rgba(0,0,0,0.10)]",
|
||||
effectiveIsFocused ? "h-[200px]" : "h-12",
|
||||
)}
|
||||
disabled={disabled}
|
||||
globalDrop
|
||||
multiple
|
||||
onSubmit={handleSubmit}
|
||||
{...props}
|
||||
>
|
||||
{extraHeader && (
|
||||
<div className="absolute top-0 right-0 left-0 z-10">
|
||||
<div className="absolute right-0 bottom-0 left-0 flex items-center justify-center">
|
||||
{extraHeader}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<PromptInputAttachments>
|
||||
{(attachment) => <PromptInputAttachment data={attachment} />}
|
||||
</PromptInputAttachments>
|
||||
<PromptInputBody className={cn("absolute top-0 right-0 left-0 z-3", !isFocused && "opacity-0 pointer-events-none")}>
|
||||
<PromptInputBody className={cn(
|
||||
"transition-[opacity,transform] duration-300 ease-out",
|
||||
!effectiveIsFocused && "opacity-0 pointer-events-none"
|
||||
)}>
|
||||
<PromptInputTextarea
|
||||
ref={textareaRef}
|
||||
className={cn("size-full")}
|
||||
|
|
@ -420,9 +433,13 @@ export function InputBox({
|
|||
placeholder={t.inputBox.placeholder}
|
||||
autoFocus={autoFocus}
|
||||
defaultValue={initialValue}
|
||||
onFocus={() => {
|
||||
setIsFocused(true);
|
||||
onFocusChange?.(true);
|
||||
}}
|
||||
/>
|
||||
</PromptInputBody>
|
||||
{!isFocused && (
|
||||
{!effectiveIsFocused && (
|
||||
<div
|
||||
className="absolute inset-0 z-1 cursor-text"
|
||||
onClick={() => {
|
||||
|
|
@ -431,11 +448,15 @@ export function InputBox({
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
<PromptInputFooter className={cn("flex", !isFocused && "hidden")}>
|
||||
<PromptInputFooter className={cn(
|
||||
"flex transition-all duration-300 ease-out",
|
||||
// height和padding都为0来隐藏
|
||||
!effectiveIsFocused && "invisible h-[0px] p-[0px] opacity-0 translate-y-2 pointer-events-none"
|
||||
)}>
|
||||
<PromptInputTools>
|
||||
<AddAttachmentsButton className="px-2!" />
|
||||
<PromptInputActionMenu>
|
||||
<ModeHoverGuide
|
||||
{/* <ModeHoverGuide
|
||||
mode={
|
||||
context.mode === "flash" ||
|
||||
context.mode === "thinking" ||
|
||||
|
|
@ -457,6 +478,7 @@ export function InputBox({
|
|||
{context.mode === "ultra" && (
|
||||
<RocketIcon className="size-3 text-[#dabb5e]" />
|
||||
)}
|
||||
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
|
|
@ -470,7 +492,8 @@ export function InputBox({
|
|||
(context.mode === "ultra" && t.inputBox.ultraMode)}
|
||||
</div>
|
||||
</PromptInputActionMenuTrigger>
|
||||
</ModeHoverGuide>
|
||||
</ModeHoverGuide> */}
|
||||
<IframeSkillDialogButton className="px-2!"/>
|
||||
<PromptInputActionMenuContent className="w-80">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel className="text-muted-foreground text-xs">
|
||||
|
|
@ -746,24 +769,27 @@ export function InputBox({
|
|||
</ModelSelectorList>
|
||||
</ModelSelectorContent>
|
||||
</ModelSelector>
|
||||
<PromptInputSubmit
|
||||
className="rounded-full"
|
||||
disabled={disabled}
|
||||
variant="outline"
|
||||
status={status}
|
||||
/>
|
||||
<div className="w-[150px]"></div>
|
||||
</PromptInputTools>
|
||||
</PromptInputFooter>
|
||||
{isNewThread && searchParams.get("mode") !== "skill" && (
|
||||
<div className="absolute right-0 -bottom-20 left-0 z-0 flex items-center justify-center">
|
||||
<SuggestionList />
|
||||
</div>
|
||||
)}
|
||||
{/* 移动出来 */}
|
||||
<PromptInputSubmit
|
||||
className="absolute right-3 bottom-3 z-[20]"
|
||||
disabled={disabled}
|
||||
variant="outline"
|
||||
status={status}
|
||||
/>
|
||||
{/* TODO: 神秘空div */}
|
||||
{/* {!isNewThread && (
|
||||
<div className="bg-background absolute right-0 -bottom-[17px] left-0 z-0 h-4"></div>
|
||||
)} */}
|
||||
</PromptInput>
|
||||
{/* 小惊喜等 */}
|
||||
{isNewThread && searchParams.get("mode") !== "skill" && (
|
||||
<div className="absolute right-0 bottom-0 left-0 z-0 flex items-center justify-center translate-y-full pt-4">
|
||||
<SuggestionList />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!disabled &&
|
||||
!isNewThread &&
|
||||
|
|
@ -850,14 +876,14 @@ function SuggestionList() {
|
|||
);
|
||||
return (
|
||||
<Suggestions className="min-h-16 w-fit items-start">
|
||||
<ConfettiButton
|
||||
className="text-muted-foreground cursor-pointer rounded-full px-4 text-xs font-normal"
|
||||
{/* <ConfettiButton
|
||||
className="text-muted-foreground cursor-pointer rounded-full px-[20px] py-[15px] text-xs font-normal"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleSuggestionClick(t.inputBox.surpriseMePrompt)}
|
||||
>
|
||||
<SparklesIcon className="size-4" /> {t.inputBox.surpriseMe}
|
||||
</ConfettiButton>
|
||||
{t.inputBox.surpriseMe}
|
||||
</ConfettiButton> */}
|
||||
{t.inputBox.suggestions.map((suggestion) => (
|
||||
<Suggestion
|
||||
key={suggestion.suggestion}
|
||||
|
|
@ -900,10 +926,32 @@ function AddAttachmentsButton({ className }: { className?: string }) {
|
|||
return (
|
||||
<Tooltip content={t.inputBox.addAttachments}>
|
||||
<PromptInputButton
|
||||
className={cn("px-2!", className)}
|
||||
className={cn("group px-2! hover:!bg-[#EAE2F5]", className)}
|
||||
onClick={() => attachments.openFileDialog()}
|
||||
>
|
||||
<PaperclipIcon className="size-3" />
|
||||
<svg width="18" height="15" viewBox="0 0 18 15" fill="none" xmlns="http://www.w3.org/2000/svg" className="transition-[stroke] duration-200 [&>path]:transition-[fill,stroke] [&>path]:duration-200 [&>path:first-child]:group-hover:fill-[#8E47F0] [&>path:last-child]:group-hover:stroke-[#8E47F0]">
|
||||
<path d="M7.05042 7.65254C6.9754 7.72756 6.90039 7.80257 6.90039 7.95258C6.90039 8.02759 6.9754 8.1776 7.05042 8.25262C7.20043 8.40263 7.42545 8.40263 7.57546 8.25262L8.8506 6.97747V10.7279C8.8506 10.9529 9.00061 11.1029 9.22563 11.1029C9.30065 11.1029 9.45066 11.0279 9.52567 11.0279C9.60067 10.9529 9.67568 10.8779 9.67568 10.7279V6.97747L10.9508 8.25262C11.1008 8.40263 11.3259 8.40263 11.4759 8.25262C11.5509 8.1776 11.6259 8.10259 11.6259 7.95258C11.6259 7.87757 11.5509 7.72756 11.4759 7.65254L9.52567 5.70235C9.37564 5.55234 9.15062 5.55234 9.00061 5.70235L7.05042 7.65254Z" fill="#150033"/>
|
||||
<path d="M1.12695 0.5H6.67871C6.87077 0.500077 7.01409 0.574515 7.07324 0.648438L7.09082 0.669922L8.30762 1.88672C8.6222 2.20119 9.01344 2.3681 9.44629 2.36816H16.875C17.2382 2.36842 17.5012 2.63339 17.5 2.99414V13.8848C17.5048 14.2408 17.2454 14.5056 16.8818 14.5059H1.12695C0.764649 14.5057 0.5 14.2401 0.5 13.877V1.12793C0.500049 0.810129 0.702664 0.567404 0.996094 0.511719L1.12695 0.5Z" stroke="#150033"/>
|
||||
</svg>
|
||||
|
||||
</PromptInputButton>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
function IframeSkillDialogButton({ className }: { className?: string }) {
|
||||
const { t } = useI18n();
|
||||
const attachments = usePromptInputAttachments();
|
||||
return (
|
||||
<Tooltip content={t.inputBox.selectSkill}>
|
||||
<PromptInputButton
|
||||
className={cn("group px-2! hover:!bg-[#EAE2F5]", className)}
|
||||
onClick={() => attachments.openFileDialog()}
|
||||
>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="size-4 transition-[stroke] duration-200 [&>path]:transition-[stroke] [&>path]:duration-200 [&>path]:group-hover:stroke-[#8E47F0]" viewBox="0 0 12 16" fill="none">
|
||||
<path d="M3.7998 0.5H9.19922C9.24033 0.5 9.26852 0.518136 9.28516 0.541992C9.30124 0.565318 9.30411 0.588767 9.29395 0.613281H9.29297L7.43066 5.07422L7.1416 5.76758H11.3994C11.4295 5.76765 11.4474 5.77552 11.459 5.7832C11.4724 5.79207 11.4846 5.80503 11.4922 5.82129C11.4997 5.83745 11.5013 5.85253 11.5 5.86328C11.4989 5.87156 11.4953 5.88556 11.4785 5.9043L2.87891 15.4629V15.4639C2.85396 15.4914 2.83406 15.4971 2.82031 15.499C2.80144 15.5016 2.77553 15.4981 2.74902 15.4844C2.72225 15.4705 2.70837 15.453 2.70312 15.4424C2.70056 15.4372 2.69457 15.4253 2.70312 15.3936V15.3926L4.30273 9.49512L4.47461 8.86426H0.600586C0.559682 8.86424 0.531324 8.84587 0.514648 8.82227C0.498608 8.79944 0.496551 8.777 0.505859 8.75293L3.70508 0.558594C3.71075 0.544183 3.72173 0.529788 3.73828 0.518555C3.74688 0.51277 3.75704 0.508037 3.76758 0.504883L3.7998 0.5Z" stroke="#150033"/>
|
||||
</svg>
|
||||
</PromptInputButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ export function MessageGroup({
|
|||
const rehypePlugins = useRehypeSplitWordsIntoSpans(isLoading);
|
||||
return (
|
||||
<ChainOfThought
|
||||
className={cn("w-full gap-2 rounded-lg border", className)}
|
||||
className={cn("w-full gap-2 rounded-lg bg-white", className)}
|
||||
open={true}
|
||||
>
|
||||
{aboveLastToolCallSteps.length > 0 && (
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ export function MessageList({
|
|||
<Conversation
|
||||
className={cn("flex size-full flex-col justify-center", className)}
|
||||
>
|
||||
<ConversationContent className="mx-auto w-full max-w-(--container-width-md) gap-8 pt-12">
|
||||
{/* mx-auto max-w-(--container-width-md) w-full */}
|
||||
<ConversationContent className="min-w-(--container-width-xs) gap-8 pt-12">
|
||||
{groupMessages(messages, (group) => {
|
||||
if (group.type === "human" || group.type === "assistant") {
|
||||
return group.messages.map((msg) => {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export function SubtaskCard({
|
|||
}, [task.status]);
|
||||
return (
|
||||
<ChainOfThought
|
||||
className={cn("relative w-full gap-2 rounded-lg border py-0", className)}
|
||||
className={cn("relative w-full gap-2 rounded-lg py-0", className)}
|
||||
open={!collapsed}
|
||||
>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ export function Welcome({
|
|||
`✨ ${t.welcome.createYourOwnSkill} ✨`
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={cn("inline-block", !waved ? "animate-wave" : "")}>
|
||||
{/* <div className={cn("inline-block", !waved ? "animate-wave" : "")}>
|
||||
{isUltra ? "🚀" : "👋"}
|
||||
</div>
|
||||
<AuroraText colors={colors}>{t.welcome.greeting}</AuroraText>
|
||||
</div> */}
|
||||
<AuroraText className="text-[#150033] text-[18px]" colors={colors}>{t.welcome.greeting}</AuroraText>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -59,13 +59,14 @@ export function Welcome({
|
|||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-muted-foreground text-sm">
|
||||
{t.welcome.description.includes("\n") ? (
|
||||
<pre className="whitespace-pre">{t.welcome.description}</pre>
|
||||
) : (
|
||||
<p>{t.welcome.description}</p>
|
||||
)}
|
||||
</div>
|
||||
// <div className="text-muted-foreground text-sm">
|
||||
// {t.welcome.description.includes("\n") ? (
|
||||
// <pre className="whitespace-pre">{t.welcome.description}</pre>
|
||||
// ) : (
|
||||
// <p>{t.welcome.description}</p>
|
||||
// )}
|
||||
// </div>
|
||||
<div> </div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -104,26 +104,30 @@ export const enUS: Translations = {
|
|||
followupConfirmReplace: "Replace & send",
|
||||
suggestions: [
|
||||
{
|
||||
suggestion: "Write",
|
||||
prompt: "Write a blog post about the latest trends on [topic]",
|
||||
suggestion: "Paper Writing",
|
||||
prompt: "Write an academic paper about [topic], including abstract, introduction, body and references.",
|
||||
icon: PenLineIcon,
|
||||
},
|
||||
{
|
||||
suggestion: "Research",
|
||||
prompt:
|
||||
"Conduct a deep dive research on [topic], and summarize the findings.",
|
||||
suggestion: "Report Generation",
|
||||
prompt: "Analyze [topic] in depth and generate a well-structured research report.",
|
||||
icon: MicroscopeIcon,
|
||||
},
|
||||
{
|
||||
suggestion: "Collect",
|
||||
prompt: "Collect data from [source] and create a report.",
|
||||
suggestion: "Copywriting",
|
||||
prompt: "Create a complete planning proposal and promotional copy for [project/event].",
|
||||
icon: ShapesIcon,
|
||||
},
|
||||
{
|
||||
suggestion: "Learn",
|
||||
prompt: "Learn about [topic] and create a tutorial.",
|
||||
suggestion: "PPT Generation",
|
||||
prompt: "Generate a PPT presentation outline and content about [topic].",
|
||||
icon: GraduationCapIcon,
|
||||
},
|
||||
{
|
||||
suggestion: "Document Processing",
|
||||
prompt: "Process [document] with reading, summarizing, translating or format conversion.",
|
||||
icon: CompassIcon,
|
||||
},
|
||||
],
|
||||
suggestionsCreate: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ export interface Translations {
|
|||
placeholder: string;
|
||||
createSkillPrompt: string;
|
||||
addAttachments: string;
|
||||
selectSkill:string;
|
||||
mode: string;
|
||||
flashMode: string;
|
||||
flashModeDescription: string;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export const zhCN: Translations = {
|
|||
|
||||
// Welcome
|
||||
welcome: {
|
||||
greeting: "你好,欢迎回来!",
|
||||
greeting: "使用Skills",
|
||||
description:
|
||||
"欢迎使用 🦌 DeerFlow,一个完全开源的超级智能体。通过内置和自定义的 Skills,\nDeerFlow 可以帮你搜索网络、分析数据,还能为你生成幻灯片、\n图片、视频、播客及网页等,几乎可以做任何事情。",
|
||||
|
||||
|
|
@ -66,10 +66,11 @@ export const zhCN: Translations = {
|
|||
|
||||
// Input Box
|
||||
inputBox: {
|
||||
placeholder: "今天我能为你做些什么?",
|
||||
placeholder: "先输入说明需求,选择Skill,开始使用吧?",
|
||||
createSkillPrompt:
|
||||
"我们一起用 skill-creator 技能来创建一个技能吧。先问问我希望这个技能能做什么。",
|
||||
addAttachments: "添加附件",
|
||||
selectSkill:"选择Skill",
|
||||
mode: "模式",
|
||||
flashMode: "闪速",
|
||||
flashModeDescription: "快速且高效的完成任务,但可能不够精准",
|
||||
|
|
@ -99,25 +100,30 @@ export const zhCN: Translations = {
|
|||
followupConfirmReplace: "替换并发送",
|
||||
suggestions: [
|
||||
{
|
||||
suggestion: "写作",
|
||||
prompt: "撰写一篇关于[主题]的博客文章",
|
||||
suggestion: "论文写作",
|
||||
prompt: "撰写一篇关于[主题]的学术论文,包含摘要、引言、正文和参考文献。",
|
||||
icon: PenLineIcon,
|
||||
},
|
||||
{
|
||||
suggestion: "研究",
|
||||
prompt: "深入浅出的研究一下[主题],并总结发现。",
|
||||
suggestion: "报告生成",
|
||||
prompt: "深入分析[主题],生成一份结构清晰的调研报告。",
|
||||
icon: MicroscopeIcon,
|
||||
},
|
||||
{
|
||||
suggestion: "收集",
|
||||
prompt: "从[来源]收集数据并创建报告。",
|
||||
suggestion: "策划文案",
|
||||
prompt: "为[项目/活动]撰写一份完整的策划方案和宣传文案。",
|
||||
icon: ShapesIcon,
|
||||
},
|
||||
{
|
||||
suggestion: "学习",
|
||||
prompt: "学习关于[主题]并创建教程。",
|
||||
suggestion: "PPT生成",
|
||||
prompt: "生成一个关于[主题]的PPT演示文稿大纲和内容。",
|
||||
icon: GraduationCapIcon,
|
||||
},
|
||||
{
|
||||
suggestion: "文档处理",
|
||||
prompt: "对[文档]进行阅读、总结、翻译或格式转换等处理。",
|
||||
icon: CompassIcon,
|
||||
},
|
||||
],
|
||||
suggestionsCreate: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@
|
|||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-tooltip-background: var(--tooltip-background);
|
||||
--animate-aurora: aurora 8s ease-in-out infinite alternate;
|
||||
@keyframes aurora {
|
||||
0% {
|
||||
|
|
@ -226,17 +227,20 @@
|
|||
--radius: 0.625rem;
|
||||
--background: #F9F8FA;
|
||||
--foreground: oklch(0.145 0 0);
|
||||
/* --foreground: #00000066; */
|
||||
/* --card: oklch(1 0.0098 87.47); */
|
||||
--card: #ffffff;
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0.0098 87.47);
|
||||
/* --popover: oklch(1 0.0098 87.47); */
|
||||
--popover: #ffffff;
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
/* --secondary: oklch(0.9455 0.0098 87.47); */
|
||||
--secondary: #1500331A;
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0.0098 87.47);
|
||||
/* --muted: oklch(0.97 0.0098 87.47); */
|
||||
--muted: #1500331A;
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
/* --accent: oklch(0.94 0.0098 87.47); */
|
||||
--accent: #1500331A;
|
||||
|
|
@ -258,6 +262,7 @@
|
|||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0.0098 87.47);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
--tooltip-background: #00000066;
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
|
@ -292,6 +297,7 @@
|
|||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
--tooltip-background: oklch(0.85 0 0);
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
|
|
@ -387,6 +393,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Hide scrollbar but keep scroll behavior */
|
||||
* {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari, Opera */
|
||||
}
|
||||
|
||||
:root {
|
||||
--container-width-xs: calc(var(--spacing) * 72);
|
||||
--container-width-sm: calc(var(--spacing) * 144);
|
||||
|
|
|
|||
Loading…
Reference in New Issue