feat(ui): 宋对ui和输入框placeholder更改

This commit is contained in:
肖应宇 2026-04-15 17:37:20 +08:00
parent ac01d08eb5
commit c4fe34ed23
11 changed files with 117 additions and 35 deletions

View File

@ -472,8 +472,22 @@ export default function ChatPage() {
/> />
) : ( ) : (
<div className="relative flex size-full justify-center px-[20px]"> <div className="relative flex size-full justify-center px-[20px]">
<div className="absolute top-2 right-2 z-30"> <div className="z-30">
<Button
</div>
{thread.values.artifacts?.length === 0 ? (
<ConversationEmptyState
icon={<FilesIcon />}
title="No artifact selected"
description="Select an artifact to view its details"
/>
) : (
<div className="flex size-full max-w-(--container-width-sm) flex-col justify-center">
<header className="shrink-0 flex justify-between items-center border-b ">
<h2 className="text-[14px] h-[58px] leading-[58px] font-bold text-[#333333]">
<span>{t.common.artifacts}</span>
</h2>
<Button
data-testid="artifacts-panel-close" data-testid="artifacts-panel-close"
size="icon-sm" size="icon-sm"
variant="ghost" variant="ghost"
@ -483,23 +497,10 @@ export default function ChatPage() {
> >
<XIcon /> <XIcon />
</Button> </Button>
</div>
{thread.values.artifacts?.length === 0 ? (
<ConversationEmptyState
icon={<FilesIcon />}
title="No artifact selected"
description="Select an artifact to view its details"
/>
) : (
<div className="flex size-full max-w-(--container-width-sm) flex-col justify-center p-4">
<header className="shrink-0">
<h2 className="text-[14px] font-bold text-[#333333]">
{t.common.artifacts}
</h2>
</header> </header>
<main className="min-h-0 grow overflow-auto"> <main className="min-h-0 grow overflow-auto">
<ArtifactFileList <ArtifactFileList
className="mb-[207px] max-w-(--container-width-sm) p-4 pt-12" className="mb-[207px] max-w-(--container-width-sm) pt-[20px]"
files={thread.values.artifacts ?? []} files={thread.values.artifacts ?? []}
threadId={threadId} threadId={threadId}
/> />
@ -656,7 +657,7 @@ export default function ChatPage() {
</DevDialog> </DevDialog>
{/* MARK: 开发测试iframe 通信功能测试面板 */} {/* MARK: 开发测试iframe 通信功能测试面板 */}
{process.env.NODE_ENV !== "production" && <IframeTestPanel />} {/* {process.env.NODE_ENV !== "production" && <IframeTestPanel />} */}
</div> </div>
</ThreadContext.Provider> </ThreadContext.Provider>
); );

View File

@ -207,7 +207,7 @@ export const ChainOfThoughtContent = memo(
<Collapsible open={isOpen}> <Collapsible open={isOpen}>
<CollapsibleContent <CollapsibleContent
className={cn( className={cn(
"mt-2 space-y-3", "mt-4 space-y-3",
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground data-[state=closed]:animate-out data-[state=open]:animate-in outline-none", "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground data-[state=closed]:animate-out data-[state=open]:animate-in outline-none",
className, className,
)} )}

View File

@ -22,15 +22,21 @@ import { Streamdown } from "streamdown";
export type MessageProps = HTMLAttributes<HTMLDivElement> & { export type MessageProps = HTMLAttributes<HTMLDivElement> & {
from: UIMessage["role"]; from: UIMessage["role"];
isFirstInSession?: boolean;
}; };
export const Message = ({ className, from, ...props }: MessageProps) => ( export const Message = ({
className,
from,
isFirstInSession = false,
...props
}: MessageProps) => (
<div <div
className={cn( className={cn(
"group flex w-full flex-col gap-2", "group flex w-full flex-col gap-2",
from === "user" from === "user"
? "is-user ml-auto justify-end" ? cn("is-user ml-auto justify-end", !isFirstInSession && "mt-6")
: "is-assistant bg-white p-[20px]", : "is-assistant bg-white rounded-[10px] p-4",
className, className,
)} )}
{...props} {...props}

View File

@ -61,7 +61,7 @@ export const Suggestion = ({
return ( return (
<Button <Button
className={cn( className={cn(
"cursor-pointer rounded-full px-[20px] py-[15px] text-xs font-normal", "cursor-pointer rounded-full px-[20px] py-[15px] text-[14px] font-normal",
"border-none bg-[#F9F8FA] text-[#666666]", "border-none bg-[#F9F8FA] text-[#666666]",
"hover:bg-[#EAE9EB] hover:text-[#150033]", "hover:bg-[#EAE9EB] hover:text-[#150033]",
className, className,

View File

@ -80,12 +80,12 @@ export function ArtifactFileList({
{files.map((file) => ( {files.map((file) => (
<Card <Card
key={file} key={file}
className="relative cursor-pointer p-3" className="relative cursor-pointer p-4"
data-testid="artifact-file-card" data-testid="artifact-file-card"
onClick={() => handleClick(file)} onClick={() => handleClick(file)}
> >
<CardHeader className="pr-2 pl-1"> <CardHeader className="pr-2 pl-1">
<CardTitle className="relative overflow-hidden pl-8"> <CardTitle className="relative overflow-hidden pl-10">
<div <div
className="text-sm font-normal text-ellipsis whitespace-nowrap" className="text-sm font-normal text-ellipsis whitespace-nowrap"
title={getFileName(file)} title={getFileName(file)}
@ -93,10 +93,10 @@ export function ArtifactFileList({
{truncateMiddle(getFileName(file), 50)} {truncateMiddle(getFileName(file), 50)}
</div> </div>
</CardTitle> </CardTitle>
<div className="absolute top-5 left-3"> <div className="absolute top-5 left-4">
{getFileIcon(file, "size-6 stroke-[1.5px] stroke-[#333333]")} {getFileIcon(file, "size-9 stroke-[1px] stroke-[#333333]")}
</div> </div>
<CardDescription className="pl-8 text-xs"> <CardDescription className="pl-10 text-xs">
{getFileExtensionDisplayName(file)} file {getFileExtensionDisplayName(file)} file
</CardDescription> </CardDescription>
<CardAction> <CardAction>
@ -124,7 +124,10 @@ export function ArtifactFileList({
target="_blank" target="_blank"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<Button variant="ghost"> <Button variant="ghost"
className="h-full! text-[var(--muted-foreground)]! hover:bg-transparent! hover:text-[#333333]!"
>
<DownloadIcon className="size-4" /> <DownloadIcon className="size-4" />
{t.common.download} {t.common.download}
</Button> </Button>

View File

@ -741,7 +741,7 @@ export function InputBox({
"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"> <PromptInputTools className="min-w-0 flex-1 gap-[20px]">
{/* TODO: Add more connectors here {/* TODO: Add more connectors here
<PromptInputActionMenu> <PromptInputActionMenu>
<PromptInputActionMenuTrigger className="px-2!" /> <PromptInputActionMenuTrigger className="px-2!" />

View File

@ -125,7 +125,8 @@ export function MessageGroup({
{aboveLastToolCallSteps.length > 0 && ( {aboveLastToolCallSteps.length > 0 && (
<Button <Button
key="above" key="above"
className="w-full items-start justify-start text-left" // 等宋
className="w-full items-start justify-start text-left h-auto! py-4"
variant="ghost" variant="ghost"
onClick={(event) => { onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
@ -152,7 +153,7 @@ export function MessageGroup({
</Button> </Button>
)} )}
{shouldShowToolSteps && ( {shouldShowToolSteps && (
<ChainOfThoughtContent className="px-4 pb-2"> <ChainOfThoughtContent className="px-4 pb-4">
{showAbove && {showAbove &&
aboveLastToolCallSteps.map((step) => aboveLastToolCallSteps.map((step) =>
step.type === "reasoning" ? ( step.type === "reasoning" ? (

View File

@ -41,11 +41,13 @@ export function MessageListItem({
message, message,
isLoading, isLoading,
threadId, threadId,
isFirstInSession = false,
}: { }: {
className?: string; className?: string;
message: Message; message: Message;
isLoading?: boolean; isLoading?: boolean;
threadId: string; threadId: string;
isFirstInSession?: boolean;
}) { }) {
const isHuman = message.type === "human"; const isHuman = message.type === "human";
return ( return (
@ -55,6 +57,7 @@ export function MessageListItem({
className, className,
)} )}
from={isHuman ? "user" : "assistant"} from={isHuman ? "user" : "assistant"}
isFirstInSession={isFirstInSession}
> >
<MessageContent <MessageContent
className={isHuman ? "w-fit" : "w-full"} className={isHuman ? "w-fit" : "w-full"}
@ -223,7 +226,7 @@ function MessageContent_({
content={contentToDisplay} content={contentToDisplay}
isLoading={isLoading} isLoading={isLoading}
rehypePlugins={[...rehypePlugins, [rehypeKatex, { output: "html" }]]} rehypePlugins={[...rehypePlugins, [rehypeKatex, { output: "html" }]]}
className="my-3" // className="my-3"
components={components} components={components}
/> />
</AIElementMessageContent> </AIElementMessageContent>

View File

@ -55,6 +55,9 @@ export function MessageList({
const rehypePlugins = useRehypeSplitWordsIntoSpans(thread.isLoading); const rehypePlugins = useRehypeSplitWordsIntoSpans(thread.isLoading);
const updateSubtask = useUpdateSubtask(); const updateSubtask = useUpdateSubtask();
const messages = messagesOverride ?? thread.messages; const messages = messagesOverride ?? thread.messages;
const firstConversationMessageId = messages.find(
(message) => message.name !== "todo_reminder",
)?.id;
if (thread.isThreadLoading && !suppressThreadLoading) { if (thread.isThreadLoading && !suppressThreadLoading) {
return <MessageListSkeleton />; return <MessageListSkeleton />;
} }
@ -71,6 +74,9 @@ export function MessageList({
message={group.messages[0]!} message={group.messages[0]!}
isLoading={thread.isLoading} isLoading={thread.isLoading}
threadId={threadId} threadId={threadId}
isFirstInSession={
group.messages[0]?.id === firstConversationMessageId
}
/> />
); );
} else if (group.type === "assistant:clarification") { } else if (group.type === "assistant:clarification") {

View File

@ -77,7 +77,7 @@ export const zhCN: Translations = {
// Input Box // Input Box
inputBox: { inputBox: {
placeholder: "先输入说明需求选择Skill开始使用吧", placeholder: "可直接聊天或者输入需求并选择skill完成更专业的任务",
createSkillPrompt: createSkillPrompt:
"我们一起用 skill-creator 技能来创建一个技能吧。先问问我希望这个技能能做什么。", "我们一起用 skill-creator 技能来创建一个技能吧。先问问我希望这个技能能做什么。",
sendMessagePrice: sendMessagePrice:

View File

@ -247,7 +247,9 @@
--accent: #1500331a; --accent: #1500331a;
--accent-foreground: oklch(0.205 0 0); --accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325); --destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0.0098 87.47); --border: #00000015;
/* --border: oklch(92.23% 0.00983 87.442 / 0.09); */
--input: oklch(0.88 0.0098 87.47); --input: oklch(0.88 0.0098 87.47);
--ring: transparent; --ring: transparent;
--chart-1: oklch(0.646 0.222 41.116); --chart-1: oklch(0.646 0.222 41.116);
@ -282,7 +284,7 @@
--accent: oklch(0.32 0.0036 106.64); --accent: oklch(0.32 0.0036 106.64);
--accent-foreground: oklch(0.985 0 0); --accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216); --destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0.191 22.216 / 10%); --border: oklch(87.64% 0.06554 21.887 / 0.038);
--input: oklch(1 0 0 / 15%); --input: oklch(1 0 0 / 15%);
--ring: transparent; --ring: transparent;
--chart-1: oklch(0.488 0.243 264.376); --chart-1: oklch(0.488 0.243 264.376);
@ -441,6 +443,10 @@ pre {
font-family: font-family:
"Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif !important; "Microsoft YaHei", "微软雅黑", "PingFang SC", sans-serif !important;
} }
pre{
border-radius: 5px;
padding: 12px 16px;
}
/* 列表项 - 14px */ /* 列表项 - 14px */
[data-streamdown="list-item"] { [data-streamdown="list-item"] {
@ -465,6 +471,62 @@ pre {
font-size: calc(14px * var(--zoom-scale)); font-size: calc(14px * var(--zoom-scale));
} }
/* 代码块 - 14px */
[data-streamdown="table"] {
border: none;
border-collapse: separate;
border-spacing: 0;
}
[data-streamdown="table-cell"] {
background-color: transparent;
}
[data-streamdown="table-header"] {
background: #9c9b9b26;
height: 50px;
}
[data-streamdown="table-header"] th {
text-align: center;
}
/* 表格四角圆角:由四个角单元格承担视觉圆角 */
[data-streamdown="table-header"] tr:first-child > [data-streamdown="table-header-cell"]:first-child {
border-top-left-radius: 5px;
}
[data-streamdown="table-header"] tr:first-child > [data-streamdown="table-header-cell"]:last-child {
border-top-right-radius: 5px;
}
[data-streamdown="table-body"] tr:first-child td{
padding-top: 20px;
}
/* 行分隔线 */
[data-streamdown="table-body"] tr{
border-bottom: 1px solid var(--border);
}
[data-streamdown="table-body"] tr:last-child > [data-streamdown="table-cell"]:first-child {
border-bottom-left-radius: 5px;
}
[data-streamdown="table-body"] tr:last-child > [data-streamdown="table-cell"]:last-child {
border-bottom-right-radius: 5px;
}
[data-streamdown="table-body"] tr:last-child {
height: 50px;
}
[data-streamdown="table-row"] >[data-streamdown="table-cell"]{
line-height: 14px;
vertical-align: top;
text-align: center;
}
.cm-line { .cm-line {
font-size: calc(14px * var(--zoom-scale)); font-size: calc(14px * var(--zoom-scale));
white-space: pre-wrap; white-space: pre-wrap;