fix(workspace): 调整输入区与消息展示逻辑

- 输入框新增返回欢迎页按钮

- 人类消息展示保留原始换行

- 调整引用刷新策略与中英文文案
This commit is contained in:
肖应宇 2026-04-28 18:29:39 +08:00
parent 1fd7a5d4f7
commit d1cdb7eef7
7 changed files with 67 additions and 22 deletions

View File

@ -970,6 +970,14 @@ export function InputBox({
/> />
</div> </div>
)} )}
{!showWelcomeStyle && (
<div className="shrink-0 h-full">
<ExitChattingButton
router={router}
threadId={threadIdFromProps}
/>
</div>
)}
<div ref={attachmentsButtonTourRef} className="shrink-0 h-full"> <div ref={attachmentsButtonTourRef} className="shrink-0 h-full">
<AddAttachmentsButton /> <AddAttachmentsButton />
</div> </div>
@ -1292,6 +1300,53 @@ function HistoryButton({
</Tooltip> </Tooltip>
); );
} }
function ExitChattingButton({
className,
router,
threadId,
}: {
className?: string;
router: AppRouterInstance;
threadId: string;
}) {
const { t } = useI18n();
return (
<Tooltip content={t.inputBox.welcome}>
<WorkspaceToolButton
className={cn(
"text-ws-base-1 hover:text-ws-interactive-primary",
className,
)}
onClick={() =>
router.replace(`/workspace/chats/${threadId}?is_chatting=false`)
}
>
<svg
className="transition-[color] duration-200"
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle
className="stroke-current transition-[stroke] duration-200"
cx="9"
cy="9"
r="8.5"
/>
<path
className="stroke-current transition-[stroke] duration-200"
d="M6 9H12"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</WorkspaceToolButton>
</Tooltip>
);
}
// 启动iframeSkillDialog // 启动iframeSkillDialog
function IframeSkillDialogButton({ function IframeSkillDialogButton({
className, className,

View File

@ -39,7 +39,6 @@ import {
} from "@/core/messages/utils"; } from "@/core/messages/utils";
import { useRehypeSplitWordsIntoSpans } from "@/core/rehype"; import { useRehypeSplitWordsIntoSpans } from "@/core/rehype";
import { materializeSkillYaml } from "@/core/skills"; import { materializeSkillYaml } from "@/core/skills";
import { humanMessagePlugins } from "@/core/streamdown";
import { dispatchMentionReference } from "@/core/threads/reference-events"; import { dispatchMentionReference } from "@/core/threads/reference-events";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -225,13 +224,9 @@ function MessageContent_({
if (isHuman) { if (isHuman) {
const shouldRenderSummaryCollapse = isSummaryMessage && summaryBody; const shouldRenderSummaryCollapse = isSummaryMessage && summaryBody;
const messageResponse = contentToDisplay ? ( const messageResponse = contentToDisplay ? (
<AIElementMessageResponse <div className="whitespace-break-spaces break-words">
remarkPlugins={humanMessagePlugins.remarkPlugins}
rehypePlugins={humanMessagePlugins.rehypePlugins}
components={components}
>
{contentToDisplay} {contentToDisplay}
</AIElementMessageResponse> </div>
) : null; ) : null;
return ( return (
<div className={cn("ml-auto flex flex-col gap-2", className)}> <div className={cn("ml-auto flex flex-col gap-2", className)}>
@ -250,13 +245,9 @@ function MessageContent_({
: t.toolCalls.expandContent} : t.toolCalls.expandContent}
</summary> </summary>
<AIElementMessageContent className="w-fit border-t"> <AIElementMessageContent className="w-fit border-t">
<AIElementMessageResponse <div className="whitespace-break-spaces break-words">
remarkPlugins={humanMessagePlugins.remarkPlugins}
rehypePlugins={humanMessagePlugins.rehypePlugins}
components={components}
>
{summaryBody} {summaryBody}
</AIElementMessageResponse> </div>
</AIElementMessageContent> </AIElementMessageContent>
</details> </details>
)} )}

View File

@ -32,7 +32,6 @@ export function useReferenceFiles(threadId: string | undefined) {
queryKey: ["references", "list", threadId], queryKey: ["references", "list", threadId],
queryFn: () => listReferenceFiles(threadId ?? ""), queryFn: () => listReferenceFiles(threadId ?? ""),
enabled: Boolean(threadId), enabled: Boolean(threadId),
refetchInterval: 5000, refetchOnWindowFocus: false,
refetchOnWindowFocus: true,
}); });
} }

View File

@ -86,6 +86,7 @@ export const enUS: Translations = {
"Please note, this feature will consume tokens. Ensure your account balance is greater than 200 credits.", "Please note, this feature will consume tokens. Ensure your account balance is greater than 200 credits.",
addAttachments: "Add attachments", addAttachments: "Add attachments",
history: "History", history: "History",
welcome:"Welcome",
selectSkill: "Select Skill", selectSkill: "Select Skill",
mode: "Mode", mode: "Mode",
flashMode: "Flash", flashMode: "Flash",

View File

@ -75,6 +75,7 @@ export interface Translations {
createSkillPrompt: string; createSkillPrompt: string;
addAttachments: string; addAttachments: string;
history: string; history: string;
welcome:string;
selectSkill: string; selectSkill: string;
mode: string; mode: string;
flashMode: string; flashMode: string;

View File

@ -87,6 +87,7 @@ export const zhCN: Translations = {
"请注意此功能将消耗token请保证账户余额大于200可学豆。", "请注意此功能将消耗token请保证账户余额大于200可学豆。",
addAttachments: "添加附件", addAttachments: "添加附件",
history: "历史记录", history: "历史记录",
welcome:"欢迎页",
selectSkill: "选择Skill", selectSkill: "选择Skill",
mode: "模式", mode: "模式",
flashMode: "闪速", flashMode: "闪速",
@ -262,7 +263,7 @@ export const zhCN: Translations = {
noArtifactSelectedDescription: "请选择一个生成文件以查看详情", noArtifactSelectedDescription: "请选择一个生成文件以查看详情",
exitDialogTitle: "提示", exitDialogTitle: "提示",
exitDialogDescription: exitDialogDescription:
"历史记录每七天自动删除,现在将返回欢迎页,是否继续?", "每七天自动删除。现在将返回欢迎页且清空聊天消息,是否继续?",
exitDialogConfirm: "确定", exitDialogConfirm: "确定",
selectedSkillLoadFailed: "技能加载失败", selectedSkillLoadFailed: "技能加载失败",
unknownErrorRetry: "发生了未知错误,请稍后重试。", unknownErrorRetry: "发生了未知错误,请稍后重试。",

View File

@ -414,12 +414,9 @@ export function stripPriorityHintSuffix(content: string): string {
* - Split Chinese-numbered items (e.g. "1...") into separate paragraphs. * - Split Chinese-numbered items (e.g. "1...") into separate paragraphs.
*/ */
export function normalizeHumanMessageDisplayText(content: string): string { export function normalizeHumanMessageDisplayText(content: string): string {
return content // Preserve human input as-is for display; only decode escaped newlines
.replace(/\\n/g, "\n") // and normalize CRLF/CR to LF so line breaks render consistently.
.replace(/\r\n?/g, "\n") return content.replace(/\\n/g, "\n").replace(/\r\n?/g, "\n");
.replace(/\n(?=\d+[)]\s*)/g, "\n\n")
.replace(/\n{3,}/g, "\n\n")
.trim();
} }
export function parseUploadedFiles(content: string): FileInMessage[] { export function parseUploadedFiles(content: string): FileInMessage[] {