用户引导沙箱优化

This commit is contained in:
董宏凯 committed 2026-08-26 14:14:26 +08:00
1 parent 21f3787b8b
commit 910cee9e67
8 files changed
+604 -224

No files matched your search

@@ -77,6 +77,10 @@ import {
useUpdateArtifactContent,
} from "@/core/artifacts/hooks";
import { resolveArtifactURL, urlOfArtifact } from "@/core/artifacts/utils";
import {
isSandboxStreamPerfEnabled,
useSandboxFollowScroll,
} from "@/core/artifacts/sandbox-stream-perf";
import { isArtifactApiPath } from "@/core/artifacts/streaming-write-preview";
import { useI18n } from "@/core/i18n/hooks";
import { downloadMarkdownAsDocx } from "@/core/scheduled-tasks/markdown-docx";
@@ -401,6 +405,14 @@ export function ArtifactFileDetail({
: (canEditArtifact || (canUseAiEditor && hasLocalEdits)
? draftContent
: (content ?? ""));
const streamPerfEnabled = isSandboxStreamPerfEnabled();
const isLiveStreaming = thread.isLoading || documentRewriteActive;
const sandboxDisplayText = useMemo(() => {
const raw = displayContent ?? "";
if (disableDesensitize) return raw;
if (streamPerfEnabled && isLiveStreaming) return raw;
return desensitize(raw);
}, [disableDesensitize, displayContent, isLiveStreaming, streamPerfEnabled]);
const artifactContentPending =
isCodeFile &&
!useWriteFileStreaming &&
@@ -924,39 +936,12 @@ export function ArtifactFileDetail({
[],
);
useEffect(() => {
if (!(thread.isLoading || documentRewriteActive) || !autoScrollEnabled) {
return;
}
const tick = () => {
scrollArtifactToBottom("auto");
};
tick();
const intervalId = window.setInterval(tick, 180);
return () => window.clearInterval(intervalId);
}, [
autoScrollEnabled,
documentRewriteActive,
scrollArtifactToBottom,
thread.isLoading,
]);
useEffect(() => {
if (!(thread.isLoading || documentRewriteActive) || !autoScrollEnabled) {
return;
}
scrollArtifactToBottom("auto");
}, [
autoScrollEnabled,
displayContent,
documentRewriteActive,
scrollArtifactToBottom,
thread.isLoading,
useSandboxFollowScroll({
follow: isLiveStreaming && autoScrollEnabled,
scrollToBottom: scrollArtifactToBottom,
contentKey: sandboxDisplayText,
viewMode,
]);
});
useEffect(() => {
const handleResumeEvent = () => {
@@ -1741,7 +1726,7 @@ export function ArtifactFileDetail({
language === "html" ||
language === "mermaid") && (
<ArtifactFilePreview
content={disableDesensitize ? (displayContent ?? "") : desensitize(displayContent ?? "")}
content={sandboxDisplayText}
language={language ?? "text"}
threadId={threadId}
publicShareCode={publicShareCode}
@@ -1778,7 +1763,7 @@ export function ArtifactFileDetail({
value={
canEditArtifact || disableDesensitize
? (displayContent ?? "")
: desensitize(displayContent ?? "")
: sandboxDisplayText
}
readonly={!canEditArtifact || documentRewriteActive}
onChange={
@@ -1789,6 +1774,7 @@ export function ArtifactFileDetail({
}
: undefined
}
isStreaming={thread.isLoading}
streamingReadonlyFallback={!canEditArtifact}
/>
</div>
@@ -11,12 +11,11 @@ import { basicLightInit } from "@uiw/codemirror-theme-basic";
import { monokaiInit } from "@uiw/codemirror-theme-monokai";
import CodeMirror from "@uiw/react-codemirror";
import { useTheme } from "next-themes";
import { useMemo } from "react";
import { memo, useMemo } from "react";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
import { useThread } from "./messages/context";
const customDarkTheme = monokaiInit({
settings: {
background: "transparent",
@@ -34,7 +33,7 @@ const customLightTheme = basicLightInit({
},
});
export function CodeEditor({
export const CodeEditor = memo(function CodeEditor({
className,
placeholder,
value,
@@ -43,6 +42,7 @@ export function CodeEditor({
disabled,
autoFocus,
settings,
isStreaming = false,
streamingReadonlyFallback = true,
}: {
className?: string;
@@ -53,11 +53,10 @@ export function CodeEditor({
disabled?: boolean;
autoFocus?: boolean;
settings?: unknown;
/** Parent-owned streaming flag so this editor does not subscribe to `thread`. */
isStreaming?: boolean;
streamingReadonlyFallback?: boolean;
}) {
const {
thread: { isLoading },
} = useThread();
const { resolvedTheme } = useTheme();
const extensions = useMemo(() => {
@@ -81,7 +80,7 @@ export function CodeEditor({
className,
)}
>
{isLoading && streamingReadonlyFallback ? (
{isStreaming && streamingReadonlyFallback ? (
<Textarea
className={cn(
"h-full overflow-auto font-mono [&_.cm-editor]:h-full [&_.cm-focused]:outline-none!",
@@ -116,4 +115,4 @@ export function CodeEditor({
)}
</div>
);
}
});