fix: ²¹Æë Step3 ɳÏä²Ù×÷²¢Îȶ¨д×÷³ÎÇåÁ÷³Ì
Ϊ roundtable µÚÈý²½ɳÏ䲹»ØÐ´°¿ڴò¿ªÓëÏÂÔزÙ×÷£¬²¢ÐÞÕýд×÷ÅäÖóÎÇåµÄÌύÁ÷³ÌÓëĬÈÏËزÄÀ´Դ£¬±ÜÃâÓû§ÔڶƬ³¡¾°ÏÂÌáǰÌύ»ò½øÈ벻·ûºÏԤÆڵļìË÷·¾¶¡£
This commit is contained in:
1 parent
a81d8adc90
commit
56114562cb
6 files changed
+134
-69
No files matched your search
@@ -400,6 +400,47 @@ export function ArtifactFileDetail({
|
||||
}
|
||||
}, [displayContent, filepath]);
|
||||
|
||||
const handleOpenWriteFileInNewTab = useCallback(() => {
|
||||
const source = displayContent ?? "";
|
||||
if (!source.trim()) {
|
||||
toast.error("文件内容为空,暂时无法打开");
|
||||
return;
|
||||
}
|
||||
const blobType =
|
||||
language === "html"
|
||||
? "text/html;charset=utf-8"
|
||||
: language === "markdown"
|
||||
? "text/markdown;charset=utf-8"
|
||||
: "text/plain;charset=utf-8";
|
||||
const blob = new Blob([source], { type: blobType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
|
||||
}, [displayContent, language]);
|
||||
|
||||
const handleDownloadWriteFile = useCallback(() => {
|
||||
const source = displayContent ?? "";
|
||||
if (!source.trim()) {
|
||||
toast.error("文件内容为空,暂时无法下载");
|
||||
return;
|
||||
}
|
||||
const blobType =
|
||||
language === "html"
|
||||
? "text/html;charset=utf-8"
|
||||
: language === "markdown"
|
||||
? "text/markdown;charset=utf-8"
|
||||
: "text/plain;charset=utf-8";
|
||||
const blob = new Blob([source], { type: blobType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = getFileName(filepath);
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}, [displayContent, filepath, language]);
|
||||
|
||||
return (
|
||||
<Artifact className={cn(className)}>
|
||||
<ArtifactHeader className="grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3 px-4 py-3">
|
||||
@@ -500,30 +541,32 @@ export function ArtifactFileDetail({
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!isWriteFile && (
|
||||
<ArtifactAction
|
||||
icon={SquareArrowOutUpRightIcon}
|
||||
label={t.common.openInNewWindow}
|
||||
tooltip={t.common.openInNewWindow}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await openArtifactInNewTab({
|
||||
filepath,
|
||||
threadId,
|
||||
isMock,
|
||||
publicShareCode,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Failed to open artifact",
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<ArtifactAction
|
||||
icon={SquareArrowOutUpRightIcon}
|
||||
label={t.common.openInNewWindow}
|
||||
tooltip={t.common.openInNewWindow}
|
||||
onClick={async () => {
|
||||
if (isWriteFile) {
|
||||
handleOpenWriteFileInNewTab();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await openArtifactInNewTab({
|
||||
filepath,
|
||||
threadId,
|
||||
isMock,
|
||||
publicShareCode,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Failed to open artifact",
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{isCodeFile && (
|
||||
<ArtifactAction
|
||||
icon={CopyIcon}
|
||||
@@ -550,30 +593,32 @@ export function ArtifactFileDetail({
|
||||
onClick={handleDownloadAsWord}
|
||||
/>
|
||||
)}
|
||||
{!isWriteFile && (
|
||||
<ArtifactAction
|
||||
icon={DownloadIcon}
|
||||
label={t.common.download}
|
||||
tooltip={t.common.download}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await downloadArtifactFile({
|
||||
filepath,
|
||||
threadId,
|
||||
isMock,
|
||||
publicShareCode,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Download failed",
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<ArtifactAction
|
||||
icon={DownloadIcon}
|
||||
label={t.common.download}
|
||||
tooltip={t.common.download}
|
||||
onClick={async () => {
|
||||
if (isWriteFile) {
|
||||
handleDownloadWriteFile();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await downloadArtifactFile({
|
||||
filepath,
|
||||
threadId,
|
||||
isMock,
|
||||
publicShareCode,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Download failed",
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ArtifactAction
|
||||
className="hover:bg-background"
|
||||
icon={XIcon}
|
||||
|
||||
@@ -892,9 +892,14 @@ function ClarificationCard({
|
||||
return;
|
||||
}
|
||||
if (!allowMultiple) {
|
||||
const next = { selectedIds: [optionId], custom: "" };
|
||||
// 单选:仅当「只有这一张待答澄清卡」时才点击即发送。多张澄清卡同时出现时,
|
||||
// 点击只记录本卡选择,待最后一张的「全部发送」统一提交——否则点了第一张就立刻
|
||||
// 提交整批,后面的卡片再也无法选择(见 bug:选了第一个,第二个不让选)。
|
||||
const next = { selectedIds: [optionId], custom: selection.custom };
|
||||
onSelectionChange(next);
|
||||
void onSubmitAll(next);
|
||||
if (!multiPending) {
|
||||
void onSubmitAll(next);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const next = selection.selectedIds.includes(optionId)
|
||||
@@ -934,7 +939,11 @@ function ClarificationCard({
|
||||
{options.length > 0 ? (
|
||||
<div className="mt-4 grid gap-2">
|
||||
<div className="text-muted-foreground text-xs">
|
||||
{allowMultiple ? "可多选(再次点击取消选择)" : "请选择一项(点击即发送)"}
|
||||
{allowMultiple
|
||||
? "可多选(再次点击取消选择)"
|
||||
: multiPending
|
||||
? "请选择一项"
|
||||
: "请选择一项(点击即发送)"}
|
||||
</div>
|
||||
{options.map((option) => {
|
||||
const optionId = option.id || option.label;
|
||||
@@ -943,7 +952,7 @@ function ClarificationCard({
|
||||
<Button
|
||||
key={optionId}
|
||||
type="button"
|
||||
variant={isSelected && allowMultiple ? "default" : "outline"}
|
||||
variant={isSelected && (allowMultiple || multiPending) ? "default" : "outline"}
|
||||
className="h-auto justify-start whitespace-normal px-3 py-2 text-left"
|
||||
disabled={disabled}
|
||||
onClick={() => handleOptionClick(optionId)}
|
||||
@@ -963,7 +972,7 @@ function ClarificationCard({
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
{isSelected && allowMultiple ? (
|
||||
{isSelected && (allowMultiple || multiPending) ? (
|
||||
<CheckIcon className="ml-2 h-4 w-4 shrink-0" />
|
||||
) : null}
|
||||
</Button>
|
||||
@@ -988,10 +997,10 @@ function ClarificationCard({
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{isPending && (allowMultiple || payload.allow_custom !== false) ? (
|
||||
{isPending && (allowMultiple || multiPending || payload.allow_custom !== false) ? (
|
||||
isLastPending ? (
|
||||
<div className="mt-3 flex items-center justify-end gap-2">
|
||||
{multiPending && allowMultiple ? (
|
||||
{multiPending ? (
|
||||
<span className="text-muted-foreground text-xs">
|
||||
将一起提交以上 {pendingCount} 个问题的回答
|
||||
</span>
|
||||
@@ -1003,10 +1012,10 @@ function ClarificationCard({
|
||||
onClick={() => void onSubmitAll()}
|
||||
>
|
||||
<SendIcon className="h-4 w-4" />
|
||||
{submitting ? "发送中…" : multiPending && allowMultiple ? "全部发送" : "发送回答"}
|
||||
{submitting ? "发送中…" : multiPending ? "全部发送" : "发送回答"}
|
||||
</Button>
|
||||
</div>
|
||||
) : allowMultiple ? (
|
||||
) : allowMultiple || multiPending ? (
|
||||
<div className="text-muted-foreground mt-3 text-right text-xs">
|
||||
选择后等待最后一题统一提交
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user