fix(workspace): 兼容缺失 threadId 并移除建议请求
This commit is contained in:
parent
fbe52a7780
commit
248bf67ec9
|
|
@ -14,7 +14,6 @@ import {
|
|||
DevDialogTitle,
|
||||
} from "@/components/ui/dev-dialog";
|
||||
import { useSidebar } from "@/components/ui/sidebar";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import {
|
||||
ArtifactFileDetail,
|
||||
ArtifactFileList,
|
||||
|
|
@ -27,7 +26,6 @@ import { InputBox } from "@/components/workspace/input-box";
|
|||
import { MessageList } from "@/components/workspace/messages";
|
||||
import { ThreadContext } from "@/components/workspace/messages/context";
|
||||
import { ThreadTitle } from "@/components/workspace/thread-title";
|
||||
import { TokenUsageIndicator } from "@/components/workspace/token-usage-indicator";
|
||||
import { Tooltip } from "@/components/workspace/tooltip";
|
||||
import { useSpecificChatMode } from "@/components/workspace/use-chat-mode";
|
||||
import { Welcome } from "@/components/workspace/welcome";
|
||||
|
|
@ -260,7 +258,6 @@ export default function ChatPage() {
|
|||
)}
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-2 overflow-hidden">
|
||||
{/* <TokenUsageIndicator messages={thread.messages} /> */}
|
||||
<DevTodoList
|
||||
className="bg-white"
|
||||
todos={thread.values.todos ?? []}
|
||||
|
|
@ -521,21 +518,3 @@ export default function ChatPage() {
|
|||
</ThreadContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function InputBoxSkeleton() {
|
||||
return (
|
||||
<div className="w-full rounded-[20px] bg-[#FBFAFC] p-4 shadow-[0_0_20px_0_rgba(0,0,0,0.10)]">
|
||||
<div className="flex flex-col gap-4">
|
||||
<Skeleton className="h-6 w-[220px]" />
|
||||
<Skeleton className="h-[120px] w-full rounded-[16px]" />
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Skeleton className="h-9 w-9 rounded-full" />
|
||||
<Skeleton className="h-9 w-9 rounded-full" />
|
||||
</div>
|
||||
<Skeleton className="h-9 w-20 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import { CodeEditor } from "@/components/workspace/code-editor";
|
|||
import { useArtifactContent } from "@/core/artifacts/hooks";
|
||||
import { urlOfArtifact } from "@/core/artifacts/utils";
|
||||
import { useI18n } from "@/core/i18n/hooks";
|
||||
import { installSkill } from "@/core/skills/api";
|
||||
import { streamdownPlugins } from "@/core/streamdown";
|
||||
import { checkCodeFile, getFileName } from "@/core/utils/files";
|
||||
import { useMarkdownDownload } from "@/core/utils/markdown-download";
|
||||
|
|
@ -61,7 +60,7 @@ export function ArtifactFileDetail({
|
|||
}: {
|
||||
className?: string;
|
||||
filepath: string;
|
||||
threadId: string;
|
||||
threadId?: string;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const { artifacts, setOpen, select, fullscreen, setFullscreen } =
|
||||
|
|
@ -98,12 +97,18 @@ export function ArtifactFileDetail({
|
|||
return (language === "html" && !isWriteFile) || language === "markdown";
|
||||
}, [isWriteFile, language]);
|
||||
const artifactUrl = useMemo(() => {
|
||||
if (!threadId) {
|
||||
return "";
|
||||
}
|
||||
return urlOfArtifact({ filepath, threadId });
|
||||
}, [filepath, threadId]);
|
||||
const artifactPreviewKind = useMemo(() => {
|
||||
return getArtifactPreviewKind(filepath);
|
||||
}, [filepath]);
|
||||
const artifactViewerSrcDoc = useMemo(() => {
|
||||
if (!artifactUrl) {
|
||||
return "";
|
||||
}
|
||||
return buildArtifactViewerSrcDoc({
|
||||
artifactUrl,
|
||||
fileName,
|
||||
|
|
@ -113,7 +118,7 @@ export function ArtifactFileDetail({
|
|||
const { content } = useArtifactContent({
|
||||
threadId,
|
||||
filepath: filepathFromProps,
|
||||
enabled: isCodeFile && !isWriteFile,
|
||||
enabled: Boolean(threadId) && isCodeFile && !isWriteFile,
|
||||
});
|
||||
|
||||
const displayContent = content ?? "";
|
||||
|
|
@ -126,7 +131,6 @@ export function ArtifactFileDetail({
|
|||
}, [artifacts]);
|
||||
|
||||
const [viewMode, setViewMode] = useState<"code" | "preview">("code");
|
||||
const [isInstalling, setIsInstalling] = useState(false);
|
||||
const [zoom, setZoom] = useState(80);
|
||||
|
||||
// 是否可以转换为docx/pdf(仅markdown文件支持)
|
||||
|
|
@ -172,28 +176,6 @@ export function ArtifactFileDetail({
|
|||
}
|
||||
}, [previewable]);
|
||||
|
||||
const handleInstallSkill = useCallback(async () => {
|
||||
if (isInstalling) return;
|
||||
|
||||
setIsInstalling(true);
|
||||
try {
|
||||
const result = await installSkill({
|
||||
thread_id: threadId,
|
||||
path: filepath,
|
||||
});
|
||||
if (result.success) {
|
||||
toast.success(result.message);
|
||||
} else {
|
||||
toast.error(result.message ?? "Failed to install skill");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to install skill:", error);
|
||||
toast.error("Failed to install skill");
|
||||
} finally {
|
||||
setIsInstalling(false);
|
||||
}
|
||||
}, [threadId, filepath, isInstalling]);
|
||||
|
||||
return (
|
||||
// 给滚动遮挡头部定位relative
|
||||
<Artifact
|
||||
|
|
@ -358,7 +340,7 @@ export function ArtifactFileDetail({
|
|||
<a
|
||||
href={urlOfArtifact({
|
||||
filepath,
|
||||
threadId,
|
||||
threadId: threadId ?? "",
|
||||
download: true,
|
||||
})}
|
||||
target="_blank"
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function ArtifactFileList({
|
|||
}: {
|
||||
className?: string;
|
||||
files: string[];
|
||||
threadId: string;
|
||||
threadId?: string;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const { select: selectArtifact, setOpen } = useArtifacts();
|
||||
|
|
@ -48,6 +48,7 @@ export function ArtifactFileList({
|
|||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (!threadId) return;
|
||||
if (installingFile) return;
|
||||
|
||||
setInstallingFile(filepath);
|
||||
|
|
@ -98,7 +99,7 @@ export function ArtifactFileList({
|
|||
{file.endsWith(".skill") && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
disabled={installingFile === file}
|
||||
disabled={!threadId || installingFile === file}
|
||||
onClick={(e) => handleInstallSkill(e, file)}
|
||||
>
|
||||
{installingFile === file ? (
|
||||
|
|
@ -109,20 +110,27 @@ export function ArtifactFileList({
|
|||
{t.common.install}
|
||||
</Button>
|
||||
)}
|
||||
<a
|
||||
href={urlOfArtifact({
|
||||
filepath: file,
|
||||
threadId: threadId,
|
||||
download: true,
|
||||
})}
|
||||
target="_blank"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Button variant="ghost">
|
||||
{threadId ? (
|
||||
<a
|
||||
href={urlOfArtifact({
|
||||
filepath: file,
|
||||
threadId,
|
||||
download: true,
|
||||
})}
|
||||
target="_blank"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Button variant="ghost">
|
||||
<DownloadIcon className="size-4" />
|
||||
{t.common.download}
|
||||
</Button>
|
||||
</a>
|
||||
) : (
|
||||
<Button variant="ghost" disabled>
|
||||
<DownloadIcon className="size-4" />
|
||||
{t.common.download}
|
||||
</Button>
|
||||
</a>
|
||||
)}
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import type { AgentThread } from "@/core/threads/types";
|
|||
import { useThread } from "./messages/context";
|
||||
import { Tooltip } from "./tooltip";
|
||||
|
||||
export function ExportTrigger({ threadId }: { threadId: string }) {
|
||||
export function ExportTrigger({ threadId }: { threadId?: string }) {
|
||||
const { t } = useI18n();
|
||||
const { thread } = useThread();
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ export function ExportTrigger({ threadId }: { threadId: string }) {
|
|||
[messages, thread.values, threadId, t],
|
||||
);
|
||||
|
||||
if (messages.length === 0) {
|
||||
if (!threadId || messages.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export function MessageList({
|
|||
paddingBottom = 160,
|
||||
}: {
|
||||
className?: string;
|
||||
threadId: string;
|
||||
threadId?: string;
|
||||
thread: UseStream<AgentThreadState>;
|
||||
/** When set (e.g. from onFinish), use instead of thread.messages so SSE end shows complete state. */
|
||||
messagesOverride?: Message[];
|
||||
|
|
@ -98,7 +98,9 @@ export function MessageList({
|
|||
className="mb-4"
|
||||
/>
|
||||
)}
|
||||
<ArtifactFileList files={files} threadId={threadId} />
|
||||
{threadId ? (
|
||||
<ArtifactFileList files={files} threadId={threadId} />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
} else if (group.type === "assistant:subagent") {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ export function ThreadTitle({
|
|||
t.pages.newChat,
|
||||
t.pages.untitled,
|
||||
t.pages.appName,
|
||||
thread,
|
||||
thread?.isThreadLoading,
|
||||
thread?.values,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export function useArtifactContent({
|
|||
enabled,
|
||||
}: {
|
||||
filepath: string;
|
||||
threadId: string;
|
||||
threadId?: string;
|
||||
enabled?: boolean;
|
||||
}) {
|
||||
const isWriteFile = useMemo(() => {
|
||||
|
|
@ -25,12 +25,17 @@ export function useArtifactContent({
|
|||
return null;
|
||||
}, [filepath, isWriteFile, thread]);
|
||||
|
||||
const canFetch = Boolean(threadId) && enabled !== false;
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ["artifact", filepath, threadId, isMock],
|
||||
queryFn: () => {
|
||||
return loadArtifactContent({ filepath, threadId, isMock });
|
||||
return loadArtifactContent({
|
||||
filepath,
|
||||
threadId: threadId ?? "",
|
||||
isMock,
|
||||
});
|
||||
},
|
||||
enabled,
|
||||
enabled: canFetch,
|
||||
// Cache artifact content for 5 minutes to avoid repeated fetches (especially for .skill ZIP extraction)
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import {
|
||||
CompassIcon,
|
||||
GraduationCapIcon,
|
||||
ImageIcon,
|
||||
MicroscopeIcon,
|
||||
PenLineIcon,
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@ export function useThreadStream({
|
|||
: context.mode === "thinking"
|
||||
? "low"
|
||||
: undefined),
|
||||
thread_id: resolvedThreadId,
|
||||
...(resolvedThreadId ? { thread_id: resolvedThreadId } : {}),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
@ -580,7 +580,7 @@ export function useSubmitThread({
|
|||
},
|
||||
context: {
|
||||
...threadContext,
|
||||
thread_id: threadId,
|
||||
...(threadId ? { thread_id: threadId } : {}),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -156,7 +156,6 @@ export async function downloadMarkdownAsPdf(
|
|||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
async function loadHtml2Pdf(): Promise<Function> {
|
||||
const html2pdf = await import("html2pdf.js");
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
return html2pdf.default;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ interface SkillError {
|
|||
|
||||
interface UseSelectedSkillListenerOptions {
|
||||
/** 当前会话 thread_id,用于调用 bootstrapRemoteSkill */
|
||||
threadId: string | null;
|
||||
threadId?: string | null;
|
||||
}
|
||||
|
||||
interface UseSelectedSkillListenerReturn {
|
||||
|
|
|
|||
Loading…
Reference in New Issue