Commit f2b000fe authored by edy's avatar edy

Polish Xiaohongshu expert starter prompts

parent 3234cfd9
Pipeline #18451 failed
......@@ -200,8 +200,15 @@ export function MessageList({
onRegenerateAssistantMessage,
onToggleMessageReaction
}: MessageListProps) {
const messageListClassName = [
"message-list",
"chat-scroll-smooth",
viewMode === "chat" ? "message-list-home" : "",
viewMode === "experts" && activeExpertKey === "xiaohongshu" ? "message-list-xiaohongshu-empty" : ""
].filter(Boolean).join(" ")
return (
<div ref={messageListRef} onScroll={onScroll} className={"message-list chat-scroll-smooth" + (viewMode === "chat" ? " message-list-home" : "")}>
<div ref={messageListRef} onScroll={onScroll} className={messageListClassName}>
{messages.map((message) => {
const isWaitingForContent = message.role === "assistant" && message.streamState === "streaming" && !message.content.trim()
const videoStatusCard = isWaitingForContent ? buildDouyinVideoStatusCard(message, activeExpertKey) : null
......
......@@ -19,6 +19,12 @@ export type ExpertVisualKey =
| "leads"
| "sales"
export interface ExpertStarterPrompt {
title: string
description: string
prompt: string
}
export interface ExpertGuideContent {
greeting: string
summary: string
......@@ -33,6 +39,7 @@ export interface ExpertGuideContent {
continueHint?: string
placeholder?: string
prompts: string[]
starterPrompts?: ExpertStarterPrompt[]
}
interface ExpertsViewProps {
......@@ -101,6 +108,38 @@ export function ExpertEmptyState({
starterQuestionsHint,
onStarterPrompt
}: ExpertEmptyStateProps) {
if (activeExpertKey === "xiaohongshu") {
const starterPrompts = activeExpertGuide.starterPrompts ?? activeExpertGuide.prompts.map((prompt) => ({
title: prompt,
description: activeExpertGuide.summary,
prompt
}))
return (
<div className="empty-state expert-empty-state expert-empty-state-xiaohongshu">
<div className="xiaohongshu-empty-copy">
<strong className="xiaohongshu-empty-title">{activeExpertGuide.greeting}</strong>
<p>{activeExpertGuide.summary}</p>
</div>
<div className="starter-prompt-list" aria-label="小红书任务入口">
{starterPrompts.map((item) => (
<button
key={item.prompt}
type="button"
className="starter-prompt"
title={item.prompt}
aria-label={item.prompt}
onClick={() => onStarterPrompt(item.prompt)}
>
<span className="starter-prompt-title">{item.title}</span>
<span className="starter-prompt-desc">{item.description}</span>
</button>
))}
</div>
</div>
)
}
if (activeExpertKey === "douyin") {
return (
<div className="empty-state expert-empty-state expert-empty-state-douyin">
......@@ -157,7 +196,7 @@ export function ExpertEmptyState({
}
return (
<div className={"empty-state expert-empty-state" + (activeExpertKey === "xiaohongshu" ? " expert-empty-state-xiaohongshu" : "")}>
<div className="empty-state expert-empty-state">
<span className="empty-state-kicker">{activeExpertName}</span>
<strong>{activeExpertGuide.greeting}</strong>
<p>{starterQuestionsHint}</p>
......
import type { ExpertDefinition } from "@qjclaw/shared-types"
import type { MessageListMessage } from "../chat/MessageList"
import type { ExpertGuideContent, ExpertKey } from "./ExpertsView"
import type { ExpertGuideContent, ExpertKey, ExpertStarterPrompt } from "./ExpertsView"
import type { ExpertProject, ExpertVisualKey } from "../shell/ExpertTree"
export interface VideoStatusCardContent {
......@@ -9,6 +9,29 @@ export interface VideoStatusCardContent {
hint?: string
}
const XIAOHONGSHU_STARTER_PROMPTS: ExpertStarterPrompt[] = [
{
title: "帮我做一篇推荐平价火锅的笔记",
description: "适合餐饮探店、门店种草和收藏型内容。",
prompt: "帮我做一篇推荐平价火锅的笔记"
},
{
title: "做一篇制作抹茶奶酪欧包教程的爆文",
description: "适合教程结构、标题钩子和步骤拆解。",
prompt: "做一篇制作抹茶奶酪欧包教程的爆文"
},
{
title: "给通勤女生做一篇春季穿搭笔记",
description: "适合人群细分、场景搭配和封面建议。",
prompt: "给通勤女生做一篇春季穿搭笔记"
},
{
title: "做一篇针对年轻女性推荐防晒霜的爆款图文",
description: "适合美妆护肤种草、卖点提炼和图文结构。",
prompt: "做一篇针对年轻女性推荐防晒霜的爆款图文"
}
]
export function getProjectDisplayName(project: ExpertProject | undefined, defaultChatName = "千匠问天"): string {
return project?.displayName ?? project?.name ?? defaultChatName
}
......@@ -102,13 +125,10 @@ function getExpertGuide(project: ExpertProject | undefined): ExpertGuideContent
switch (resolveExpertKey(project)) {
case "xiaohongshu":
return {
greeting: "把产品、场景和目标人群说清楚,我先给你一版能直接开工的小红书任务。",
summary: "适合先做选题判断、笔记结构、标题草案、配图思路和发布时间建议。",
prompts: [
"帮我做一篇推荐平价火锅的笔记",
"做一篇制作抹茶奶酪欧包教程的爆文。",
"给通勤女生做一篇春季穿搭笔记。"
]
greeting: "准备好了,开始种草",
summary: "说清产品、场景、人群和目标,我会先拆选题、标题、笔记结构和配图方向。",
prompts: XIAOHONGSHU_STARTER_PROMPTS.map((item) => item.prompt),
starterPrompts: XIAOHONGSHU_STARTER_PROMPTS
}
case "douyin":
return {
......
......@@ -604,6 +604,11 @@
align-content: center;
}
.conversation-shell .message-list-xiaohongshu-empty:has(.expert-empty-state-xiaohongshu) {
align-content: center;
justify-items: center;
}
.conversation-shell .home-empty-state {
width: min(100%, 760px);
justify-self: center;
......@@ -651,7 +656,7 @@
align-content: start;
gap: 6px;
padding: 15px 16px 14px;
border-radius: 14px;
border-radius: 18px;
border-color: rgba(191, 219, 254, 0.62);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(248, 251, 255, 0.9)),
......@@ -699,6 +704,116 @@
box-shadow: 0 0 0 5px rgba(147, 197, 253, 0.28);
}
.conversation-shell.conversation-shell-experts .expert-empty-state-xiaohongshu {
width: min(100%, 760px);
justify-self: center;
align-self: center;
justify-items: center;
align-content: center;
gap: 24px;
padding: 16px 4px;
border: 0;
background: transparent;
}
.conversation-shell .xiaohongshu-empty-copy {
display: grid;
justify-items: center;
gap: 9px;
width: min(100%, 620px);
text-align: center;
}
.conversation-shell .xiaohongshu-empty-title {
color: #be123c;
font-size: 24px;
line-height: 1.28;
letter-spacing: 0;
background: linear-gradient(92deg, #e11d48 0%, #fb7185 30%, #60a5fa 72%, #e11d48 100%);
background-size: 220% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 10px 24px rgba(225, 29, 72, 0.08);
}
.conversation-shell .xiaohongshu-empty-copy p {
width: min(100%, 580px);
color: #64748b;
font-size: 13px;
line-height: 1.7;
}
.conversation-shell .expert-empty-state-xiaohongshu .starter-prompt-list {
width: min(100%, 680px);
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px;
}
.conversation-shell .expert-empty-state-xiaohongshu .starter-prompt {
min-height: 92px;
position: relative;
display: grid;
align-content: start;
gap: 6px;
padding: 15px 16px 14px;
border-radius: 18px;
border-color: rgba(251, 113, 133, 0.42);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.97), rgba(255, 247, 250, 0.9)),
radial-gradient(circle at 16px 0, rgba(244, 63, 94, 0.09), transparent 42%);
color: var(--revamp-text);
line-height: 1.5;
overflow: hidden;
cursor: pointer;
box-shadow:
0 10px 24px rgba(136, 40, 64, 0.06),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
transition: border-color 140ms ease, background 140ms ease, box-shadow 140ms ease;
}
.conversation-shell .expert-empty-state-xiaohongshu .starter-prompt::before {
content: "";
position: absolute;
inset: 0 auto 0 0;
width: 3px;
background: linear-gradient(180deg, rgba(244, 63, 94, 0.84), rgba(96, 165, 250, 0.36));
opacity: 0.72;
}
.conversation-shell .expert-empty-state-xiaohongshu .starter-prompt::after {
content: "";
position: absolute;
inset: 0 0 auto 0;
height: 1px;
background: linear-gradient(90deg, rgba(251, 113, 133, 0), rgba(251, 113, 133, 0.58), rgba(96, 165, 250, 0));
opacity: 0.68;
}
.conversation-shell .expert-empty-state-xiaohongshu .starter-prompt:hover {
border-color: rgba(244, 63, 94, 0.68);
background:
linear-gradient(180deg, #ffffff, rgba(255, 247, 250, 0.96)),
radial-gradient(circle at 18px 0, rgba(244, 63, 94, 0.13), transparent 44%);
box-shadow:
0 14px 28px rgba(225, 29, 72, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.92);
}
.conversation-shell .expert-empty-state-xiaohongshu .starter-prompt:focus-visible {
outline: 2px solid rgba(225, 29, 72, 0.56);
outline-offset: 3px;
box-shadow: 0 0 0 5px rgba(251, 113, 133, 0.18);
}
.conversation-shell .expert-empty-state-xiaohongshu .starter-prompt-title {
color: #881337;
}
.conversation-shell .expert-empty-state-xiaohongshu .starter-prompt-desc {
color: #64748b;
}
.conversation-shell .starter-prompt-title,
.conversation-shell .starter-prompt-desc {
display: block;
......@@ -737,7 +852,8 @@
width: min(100%, 620px);
}
.conversation-shell .home-empty-state .starter-prompt-list {
.conversation-shell .home-empty-state .starter-prompt-list,
.conversation-shell .expert-empty-state-xiaohongshu .starter-prompt-list {
grid-template-columns: 1fr;
width: min(100%, 520px);
}
......@@ -761,6 +877,20 @@
min-height: 82px;
padding: 12px 14px;
}
.conversation-shell.conversation-shell-experts .expert-empty-state-xiaohongshu {
gap: 20px;
padding-block: 10px;
}
.conversation-shell .xiaohongshu-empty-title {
font-size: 22px;
}
.conversation-shell .expert-empty-state-xiaohongshu .starter-prompt {
min-height: 84px;
padding: 12px 14px;
}
}
@media (prefers-reduced-motion: reduce) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment