feat(tour):漫游导航
This commit is contained in:
parent
f0d93ab342
commit
eb45bba7ff
|
|
@ -58,6 +58,7 @@
|
||||||
"@uiw/react-codemirror": "^4.25.4",
|
"@uiw/react-codemirror": "^4.25.4",
|
||||||
"@xyflow/react": "^12.10.0",
|
"@xyflow/react": "^12.10.0",
|
||||||
"ai": "^6.0.33",
|
"ai": "^6.0.33",
|
||||||
|
"antd": "^6.3.6",
|
||||||
"best-effort-json-parser": "^1.2.1",
|
"best-effort-json-parser": "^1.2.1",
|
||||||
"better-auth": "^1.3",
|
"better-auth": "^1.3",
|
||||||
"canvas-confetti": "^1.9.4",
|
"canvas-confetti": "^1.9.4",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1064,7 +1064,7 @@ export const PromptInputTools = ({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: PromptInputToolsProps) => (
|
}: PromptInputToolsProps) => (
|
||||||
<div className={cn("flex items-center gap-1", className)} {...props} />
|
<div className={cn("flex items-center h-full gap-1", className)} {...props} />
|
||||||
);
|
);
|
||||||
|
|
||||||
export type PromptInputButtonProps = ComponentProps<typeof InputGroupButton>;
|
export type PromptInputButtonProps = ComponentProps<typeof InputGroupButton>;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputGroupAddonVariants = cva(
|
const inputGroupAddonVariants = cva(
|
||||||
"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
|
"text-muted-foreground flex h-[58px] cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
align: {
|
align: {
|
||||||
|
|
@ -46,9 +46,9 @@ const inputGroupAddonVariants = cva(
|
||||||
"inline-end":
|
"inline-end":
|
||||||
"order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
|
"order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
|
||||||
"block-start":
|
"block-start":
|
||||||
"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
|
"order-first w-full justify-start px-3 pt-5 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
|
||||||
"block-end":
|
"block-end":
|
||||||
"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5",
|
"order-last w-full justify-start px-3 py-0 pb-5 group-has-[>input]/input-group:pb-2.5",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type { ChatStatus } from "ai";
|
import type { ChatStatus } from "ai";
|
||||||
|
import { Tour } from "antd";
|
||||||
import {
|
import {
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
GraduationCapIcon,
|
GraduationCapIcon,
|
||||||
|
|
@ -17,6 +18,7 @@ import type { AppRouterInstance } from "next/dist/shared/lib/app-router-context.
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
|
forwardRef,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
|
|
@ -25,6 +27,7 @@ import {
|
||||||
type ChangeEvent,
|
type ChangeEvent,
|
||||||
type KeyboardEvent,
|
type KeyboardEvent,
|
||||||
type ComponentProps,
|
type ComponentProps,
|
||||||
|
type RefObject,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
|
@ -99,6 +102,25 @@ import { Tooltip } from "./tooltip";
|
||||||
|
|
||||||
|
|
||||||
const MAX_REFERENCES_PER_MESSAGE = 10;
|
const MAX_REFERENCES_PER_MESSAGE = 10;
|
||||||
|
const INPUT_TOOLS_TOUR_SEEN_KEY = "workspace.input_tools_tour_seen.v1";
|
||||||
|
|
||||||
|
type WorkspaceToolButtonProps = ComponentProps<typeof PromptInputButton>;
|
||||||
|
|
||||||
|
function WorkspaceToolButton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: WorkspaceToolButtonProps) {
|
||||||
|
return (
|
||||||
|
<PromptInputButton
|
||||||
|
className={cn(
|
||||||
|
// border border-[rgba(0,0,0,0.08)]
|
||||||
|
"group h-full p-[10px]! rounded-[10px] hover:bg-[#EAE2F5] hover:text-[#8E47F0]",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
type MentionCandidate = {
|
type MentionCandidate = {
|
||||||
key: string;
|
key: string;
|
||||||
|
|
@ -215,6 +237,10 @@ export function InputBox({
|
||||||
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
|
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const mentionTriggerRef = useRef<HTMLButtonElement | null>(null);
|
const mentionTriggerRef = useRef<HTMLButtonElement | null>(null);
|
||||||
|
const historyButtonTourRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const attachmentsButtonTourRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const skillButtonTourRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const suggestionListTourRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [followups, setFollowups] = useState<string[]>([]);
|
const [followups, setFollowups] = useState<string[]>([]);
|
||||||
const [followupsHidden, setFollowupsHidden] = useState(false);
|
const [followupsHidden, setFollowupsHidden] = useState(false);
|
||||||
const [followupsLoading, setFollowupsLoading] = useState(false);
|
const [followupsLoading, setFollowupsLoading] = useState(false);
|
||||||
|
|
@ -231,11 +257,97 @@ export function InputBox({
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const [isInputToolsTourOpen, setIsInputToolsTourOpen] = useState(false);
|
||||||
|
const [isInputToolsTourReady, setIsInputToolsTourReady] = useState(false);
|
||||||
const { data: uploadedFilesData } = useUploadedFiles(threadIdFromProps);
|
const { data: uploadedFilesData } = useUploadedFiles(threadIdFromProps);
|
||||||
|
|
||||||
// isNewThread 时禁用收缩,始终保持展开(除非已提交消息)
|
// isNewThread 时禁用收缩,始终保持展开(除非已提交消息)
|
||||||
const effectiveIsFocused =
|
const effectiveIsFocused =
|
||||||
((showWelcomeStyle ?? false) && !hasSubmitted) || isFocused;
|
((showWelcomeStyle ?? false) && !hasSubmitted) || isFocused;
|
||||||
|
const shouldShowSuggestionList =
|
||||||
|
showWelcomeStyle && !hasSubmitted && searchParams.get("mode") !== "skill";
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!showWelcomeStyle || hasSubmitted) {
|
||||||
|
setIsInputToolsTourReady(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const frameId = window.requestAnimationFrame(() => {
|
||||||
|
setIsInputToolsTourReady(
|
||||||
|
Boolean(
|
||||||
|
historyButtonTourRef.current &&
|
||||||
|
attachmentsButtonTourRef.current &&
|
||||||
|
skillButtonTourRef.current &&
|
||||||
|
(!shouldShowSuggestionList || suggestionListTourRef.current),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return () => window.cancelAnimationFrame(frameId);
|
||||||
|
}, [
|
||||||
|
showWelcomeStyle,
|
||||||
|
hasSubmitted,
|
||||||
|
shouldShowSuggestionList,
|
||||||
|
iframeSkill.isBootstrapping,
|
||||||
|
iframeSkill.selectedSkills.length,
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!showWelcomeStyle || hasSubmitted || !isInputToolsTourReady) {
|
||||||
|
setIsInputToolsTourOpen(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hasSeenTour = window.localStorage.getItem(INPUT_TOOLS_TOUR_SEEN_KEY);
|
||||||
|
if (!hasSeenTour) {
|
||||||
|
setIsInputToolsTourOpen(true);
|
||||||
|
}
|
||||||
|
}, [showWelcomeStyle, hasSubmitted, isInputToolsTourReady]);
|
||||||
|
|
||||||
|
const closeInputToolsTour = useCallback(() => {
|
||||||
|
window.localStorage.setItem(INPUT_TOOLS_TOUR_SEEN_KEY, "1");
|
||||||
|
setIsInputToolsTourOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const inputToolsTourSteps = useMemo(() => {
|
||||||
|
const baseSteps = [
|
||||||
|
{
|
||||||
|
title: "查看历史",
|
||||||
|
description: "点击这里,可以查看历史会话与文档。",
|
||||||
|
target: () => historyButtonTourRef.current ?? document.body,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "上传附件",
|
||||||
|
description: "点击这里,上传参考文档或拟处理的文档。",
|
||||||
|
target: () => attachmentsButtonTourRef.current ?? document.body,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "选择 Skill",
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
点击这里,从“我的skill”中选择要使用的skill。
|
||||||
|
<br />
|
||||||
|
在广场中选择skill,在详情页选择“去使用”,也可选中skill。
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
target: () => skillButtonTourRef.current ?? document.body,
|
||||||
|
},
|
||||||
|
...(shouldShowSuggestionList
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
title: "试试我吧",
|
||||||
|
target: () => suggestionListTourRef.current ?? document.body,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
];
|
||||||
|
|
||||||
|
return baseSteps.map((step, index) => ({
|
||||||
|
...step,
|
||||||
|
prevButtonProps: { children: "上一步" },
|
||||||
|
nextButtonProps: {
|
||||||
|
children: index === baseSteps.length - 1 ? "完成" : "下一步",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}, [shouldShowSuggestionList]);
|
||||||
|
|
||||||
// 点击外部区域时收起输入框
|
// 点击外部区域时收起输入框
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -287,9 +399,9 @@ export function InputBox({
|
||||||
isImage: isImageFilename(filename),
|
isImage: isImageFilename(filename),
|
||||||
previewUrl: threadId
|
previewUrl: threadId
|
||||||
? urlOfArtifact({
|
? urlOfArtifact({
|
||||||
filepath: path,
|
filepath: path,
|
||||||
threadId,
|
threadId,
|
||||||
})
|
})
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -615,6 +727,21 @@ export function InputBox({
|
||||||
}}
|
}}
|
||||||
className="relative w-full"
|
className="relative w-full"
|
||||||
>
|
>
|
||||||
|
<Tour
|
||||||
|
open={isInputToolsTourOpen}
|
||||||
|
onClose={closeInputToolsTour}
|
||||||
|
onFinish={closeInputToolsTour}
|
||||||
|
gap={
|
||||||
|
{ offset: 4 , radius: 2 }
|
||||||
|
}
|
||||||
|
mask={{
|
||||||
|
style: {
|
||||||
|
boxShadow: 'inset 0 0 15px #333',
|
||||||
|
},
|
||||||
|
color: 'rgba(255,255,255, .8)',
|
||||||
|
}}
|
||||||
|
steps={inputToolsTourSteps}
|
||||||
|
/>
|
||||||
<AttachmentPreviewBar
|
<AttachmentPreviewBar
|
||||||
references={references}
|
references={references}
|
||||||
threadId={threadId}
|
threadId={threadId}
|
||||||
|
|
@ -781,10 +908,10 @@ export function InputBox({
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex transition-all duration-300 ease-out",
|
"flex transition-all duration-300 ease-out",
|
||||||
!effectiveIsFocused &&
|
!effectiveIsFocused &&
|
||||||
"pointer-events-none invisible h-[0px] translate-y-2 p-[0px] opacity-0",
|
"pointer-events-none invisible h-[0px] translate-y-2 p-[0px] opacity-0",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<PromptInputTools className="min-w-0 flex-1 gap-[20px]">
|
<PromptInputTools className="min-w-0 w-full overflow-hidden gap-[20px]">
|
||||||
{/* TODO: Add more connectors here
|
{/* TODO: Add more connectors here
|
||||||
<PromptInputActionMenu>
|
<PromptInputActionMenu>
|
||||||
<PromptInputActionMenuTrigger className="px-2!" />
|
<PromptInputActionMenuTrigger className="px-2!" />
|
||||||
|
|
@ -795,20 +922,26 @@ export function InputBox({
|
||||||
</PromptInputActionMenuContent>
|
</PromptInputActionMenuContent>
|
||||||
</PromptInputActionMenu> */}
|
</PromptInputActionMenu> */}
|
||||||
{showWelcomeStyle && (
|
{showWelcomeStyle && (
|
||||||
<HistoryButton
|
<div ref={historyButtonTourRef} className="shrink-0 h-full">
|
||||||
className="px-2!"
|
<HistoryButton
|
||||||
router={router}
|
router={router}
|
||||||
threadId={threadIdFromProps}
|
threadId={threadIdFromProps}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<AddAttachmentsButton className="px-2!" />
|
<div ref={attachmentsButtonTourRef} className="shrink-0 h-full">
|
||||||
<IframeSkillDialogButton
|
<AddAttachmentsButton />
|
||||||
className="px-2!"
|
</div>
|
||||||
selectedSkills={iframeSkill.selectedSkills}
|
<div className="min-w-0 grow basis-0 h-full">
|
||||||
isBootstrapping={iframeSkill.isBootstrapping}
|
<IframeSkillDialogButton
|
||||||
openSkillDialog={iframeSkill.openSkillDialog}
|
skillButtonRef={skillButtonTourRef}
|
||||||
clearSkill={iframeSkill.clearSkill}
|
selectedSkills={iframeSkill.selectedSkills}
|
||||||
/>
|
isBootstrapping={iframeSkill.isBootstrapping}
|
||||||
|
openSkillDialog={iframeSkill.openSkillDialog}
|
||||||
|
clearSkill={iframeSkill.clearSkill}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* <div className="h-[40px] w-[140px] shrink-0" aria-hidden="true" /> */}
|
||||||
|
|
||||||
{/* 参考 kexue 版本隐藏运行模式切换按钮 */}
|
{/* 参考 kexue 版本隐藏运行模式切换按钮 */}
|
||||||
</PromptInputTools>
|
</PromptInputTools>
|
||||||
|
|
@ -845,7 +978,7 @@ export function InputBox({
|
||||||
</ModelSelector> */}
|
</ModelSelector> */}
|
||||||
<PromptInputTools>
|
<PromptInputTools>
|
||||||
{/* 占位符 */}
|
{/* 占位符 */}
|
||||||
<div className="w-[150px]"></div>
|
<div className="w-[150px] h-[40px]"></div>
|
||||||
</PromptInputTools>
|
</PromptInputTools>
|
||||||
</PromptInputFooter>
|
</PromptInputFooter>
|
||||||
<PromptInputSubmit
|
<PromptInputSubmit
|
||||||
|
|
@ -856,10 +989,9 @@ export function InputBox({
|
||||||
/>
|
/>
|
||||||
</PromptInput>
|
</PromptInput>
|
||||||
|
|
||||||
{showWelcomeStyle &&
|
{shouldShowSuggestionList && (
|
||||||
!hasSubmitted &&
|
|
||||||
searchParams.get("mode") !== "skill" && (
|
|
||||||
<SuggestionListContainer
|
<SuggestionListContainer
|
||||||
|
ref={suggestionListTourRef}
|
||||||
bootstrapAndLockSkills={iframeSkill.bootstrapAndLockSkills}
|
bootstrapAndLockSkills={iframeSkill.bootstrapAndLockSkills}
|
||||||
isBootstrapping={iframeSkill.isBootstrapping}
|
isBootstrapping={iframeSkill.isBootstrapping}
|
||||||
/>
|
/>
|
||||||
|
|
@ -926,25 +1058,29 @@ export function InputBox({
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuggestionList 容器
|
// SuggestionList 容器
|
||||||
function SuggestionListContainer({
|
const SuggestionListContainer = forwardRef<HTMLDivElement, {
|
||||||
bootstrapAndLockSkills,
|
|
||||||
isBootstrapping,
|
|
||||||
}: {
|
|
||||||
bootstrapAndLockSkills: (params: {
|
bootstrapAndLockSkills: (params: {
|
||||||
selectedSkills: SelectedSkillPayloadItem[];
|
selectedSkills: SelectedSkillPayloadItem[];
|
||||||
title: string;
|
title: string;
|
||||||
}) => Promise<boolean>;
|
}) => Promise<boolean>;
|
||||||
isBootstrapping: boolean;
|
isBootstrapping: boolean;
|
||||||
}) {
|
}>(
|
||||||
return (
|
function SuggestionListContainer(
|
||||||
|
{ bootstrapAndLockSkills, isBootstrapping },
|
||||||
|
ref,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
<div className="absolute right-0 bottom-0 left-0 z-0 flex translate-y-full items-center justify-center pt-4">
|
<div className="absolute right-0 bottom-0 left-0 z-0 flex translate-y-full items-center justify-center pt-4">
|
||||||
|
<div ref={ref} className="w-fit">
|
||||||
<SuggestionList
|
<SuggestionList
|
||||||
bootstrapAndLockSkills={bootstrapAndLockSkills}
|
bootstrapAndLockSkills={bootstrapAndLockSkills}
|
||||||
isBootstrapping={isBootstrapping}
|
isBootstrapping={isBootstrapping}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// 快速选择skillbutton
|
// 快速选择skillbutton
|
||||||
function SuggestionList({
|
function SuggestionList({
|
||||||
|
|
@ -1046,28 +1182,28 @@ function AddAttachmentsButton({ className }: { className?: string }) {
|
||||||
const attachments = usePromptInputAttachments();
|
const attachments = usePromptInputAttachments();
|
||||||
return (
|
return (
|
||||||
<Tooltip content={t.inputBox.addAttachments}>
|
<Tooltip content={t.inputBox.addAttachments}>
|
||||||
<PromptInputButton
|
<WorkspaceToolButton
|
||||||
className={cn("group px-2! hover:bg-[#EAE2F5]", className)}
|
className={className}
|
||||||
onClick={() => attachments.openFileDialog()}
|
onClick={() => attachments.openFileDialog()}
|
||||||
|
>
|
||||||
|
<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]"
|
||||||
>
|
>
|
||||||
<svg
|
<path
|
||||||
width="18"
|
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"
|
||||||
height="15"
|
fill="#150033"
|
||||||
viewBox="0 0 18 15"
|
/>
|
||||||
fill="none"
|
<path
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
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"
|
||||||
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]"
|
stroke="#150033"
|
||||||
>
|
/>
|
||||||
<path
|
</svg>
|
||||||
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"
|
</WorkspaceToolButton>
|
||||||
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>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1084,46 +1220,48 @@ function HistoryButton({
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
return (
|
return (
|
||||||
<Tooltip content={t.inputBox.history}>
|
<Tooltip content={t.inputBox.history}>
|
||||||
<PromptInputButton
|
<WorkspaceToolButton
|
||||||
className={cn("group px-2! hover:bg-[#EAE2F5]", className)}
|
className={className}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.replace(`/workspace/chats/${threadId}?is_chatting=true`)
|
router.replace(`/workspace/chats/${threadId}?is_chatting=true`)
|
||||||
}
|
}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="transition-[stroke] duration-200"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 18 18"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<svg
|
<circle
|
||||||
className="transition-[stroke] duration-200"
|
className="stroke-[#150033] transition-[stroke] duration-200 group-hover:stroke-[#8E47F0]"
|
||||||
width="18"
|
cx="9"
|
||||||
height="18"
|
cy="9"
|
||||||
viewBox="0 0 18 18"
|
r="8.5"
|
||||||
fill="none"
|
/>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<path
|
||||||
>
|
className="stroke-[#150033] transition-[stroke] duration-200 group-hover:stroke-[#8E47F0]"
|
||||||
<circle
|
d="M9 6V10H12"
|
||||||
className="stroke-[#150033] transition-[stroke] duration-200 group-hover:stroke-[#8E47F0]"
|
strokeLinecap="round"
|
||||||
cx="9"
|
strokeLinejoin="round"
|
||||||
cy="9"
|
/>
|
||||||
r="8.5"
|
</svg>
|
||||||
/>
|
</WorkspaceToolButton>
|
||||||
<path
|
|
||||||
className="stroke-[#150033] transition-[stroke] duration-200 group-hover:stroke-[#8E47F0]"
|
|
||||||
d="M9 6V10H12"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</PromptInputButton>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// 启动iframeSkillDialog
|
// 启动iframeSkillDialog
|
||||||
function IframeSkillDialogButton({
|
function IframeSkillDialogButton({
|
||||||
className,
|
className,
|
||||||
|
skillButtonRef,
|
||||||
selectedSkills,
|
selectedSkills,
|
||||||
isBootstrapping,
|
isBootstrapping,
|
||||||
openSkillDialog,
|
openSkillDialog,
|
||||||
clearSkill,
|
clearSkill,
|
||||||
}: {
|
}: {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
skillButtonRef?: RefObject<HTMLDivElement | null>;
|
||||||
selectedSkills: Array<{ skill_id: string; title: string }>;
|
selectedSkills: Array<{ skill_id: string; title: string }>;
|
||||||
isBootstrapping: boolean;
|
isBootstrapping: boolean;
|
||||||
openSkillDialog: () => void;
|
openSkillDialog: () => void;
|
||||||
|
|
@ -1132,24 +1270,26 @@ function IframeSkillDialogButton({
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-w-0 flex-1 items-center gap-2">
|
<div className="flex min-w-0 w-full items-center h-full gap-2">
|
||||||
<Tooltip content={t.inputBox.selectSkill}>
|
<Tooltip content={t.inputBox.selectSkill}>
|
||||||
<PromptInputButton
|
<div ref={skillButtonRef} className="shrink-0">
|
||||||
className={cn("group shrink-0 px-2! hover:bg-[#EAE2F5]", className)}
|
<WorkspaceToolButton
|
||||||
onClick={openSkillDialog}
|
className={cn("shrink-0", className)}
|
||||||
>
|
onClick={openSkillDialog}
|
||||||
<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
|
<svg
|
||||||
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"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
stroke="#150033"
|
className="size-4 transition-[stroke] duration-200 [&>path]:transition-[stroke] [&>path]:duration-200 [&>path]:group-hover:stroke-[#8E47F0]"
|
||||||
/>
|
viewBox="0 0 12 16"
|
||||||
</svg>
|
fill="none"
|
||||||
</PromptInputButton>
|
>
|
||||||
|
<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>
|
||||||
|
</WorkspaceToolButton>
|
||||||
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{isBootstrapping ? (
|
{isBootstrapping ? (
|
||||||
<Tag className="bg-background text-muted-foreground gap-2 border">
|
<Tag className="bg-background text-muted-foreground gap-2 border">
|
||||||
|
|
@ -1159,7 +1299,7 @@ function IframeSkillDialogButton({
|
||||||
) : null}
|
) : null}
|
||||||
{!isBootstrapping && selectedSkills.length > 0 ? (
|
{!isBootstrapping && selectedSkills.length > 0 ? (
|
||||||
<div
|
<div
|
||||||
className="flex min-w-0 flex-1 items-center gap-2 overflow-x-auto overflow-y-hidden whitespace-nowrap [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
className="flex min-w-0 grow basis-0 items-center gap-2 overflow-x-auto overflow-y-hidden whitespace-nowrap [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||||
onWheel={(event) => {
|
onWheel={(event) => {
|
||||||
if (event.deltaY === 0) return;
|
if (event.deltaY === 0) return;
|
||||||
event.currentTarget.scrollLeft += event.deltaY;
|
event.currentTarget.scrollLeft += event.deltaY;
|
||||||
|
|
@ -1227,9 +1367,9 @@ function AttachmentPreviewBar({
|
||||||
const referenceUrl =
|
const referenceUrl =
|
||||||
threadId && reference.path
|
threadId && reference.path
|
||||||
? urlOfArtifact({
|
? urlOfArtifact({
|
||||||
filepath: reference.path,
|
filepath: reference.path,
|
||||||
threadId,
|
threadId,
|
||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
const filename = reference.filename ?? "reference";
|
const filename = reference.filename ?? "reference";
|
||||||
const imageMatch = /\.(png|jpe?g|gif|webp|bmp|svg)$/i.exec(filename);
|
const imageMatch = /\.(png|jpe?g|gif|webp|bmp|svg)$/i.exec(filename);
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ export const zhCN: Translations = {
|
||||||
// Input Box
|
// Input Box
|
||||||
inputBox: {
|
inputBox: {
|
||||||
placeholder: "可直接对话; 或输入需求并选择skill,完成专业任务;",
|
placeholder: "可直接对话; 或输入需求并选择skill,完成专业任务;",
|
||||||
welcomePlaceholder: "可直接对话; 或输入需求并选择skill,完成专业任务;",
|
welcomePlaceholder: "可直接对话; 或输入需求并选择skill,完成专业任务。",
|
||||||
chatPlaceholder: "“@”可引用文件。",
|
chatPlaceholder: "“@”可引用文件。",
|
||||||
createSkillPrompt:
|
createSkillPrompt:
|
||||||
"我们一起用 skill-creator 技能来创建一个技能吧。先问问我希望这个技能能做什么。",
|
"我们一起用 skill-creator 技能来创建一个技能吧。先问问我希望这个技能能做什么。",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue