Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qjclaw-dmg
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
AI-甘富林
qjclaw-dmg
Commits
f2b000fe
Commit
f2b000fe
authored
May 12, 2026
by
edy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Polish Xiaohongshu expert starter prompts
parent
3234cfd9
Pipeline
#18451
failed
Changes
4
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
208 additions
and
12 deletions
+208
-12
MessageList.tsx
apps/ui/src/features/chat/MessageList.tsx
+8
-1
ExpertsView.tsx
apps/ui/src/features/experts/ExpertsView.tsx
+40
-1
expertDisplay.ts
apps/ui/src/features/experts/expertDisplay.ts
+28
-8
theme-openclaw.css
apps/ui/src/styles/theme-openclaw.css
+132
-2
No files found.
apps/ui/src/features/chat/MessageList.tsx
View file @
f2b000fe
...
@@ -200,8 +200,15 @@ export function MessageList({
...
@@ -200,8 +200,15 @@ export function MessageList({
onRegenerateAssistantMessage
,
onRegenerateAssistantMessage
,
onToggleMessageReaction
onToggleMessageReaction
}:
MessageListProps
)
{
}:
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
(
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
)
=>
{
{
messages
.
map
((
message
)
=>
{
const
isWaitingForContent
=
message
.
role
===
"assistant"
&&
message
.
streamState
===
"streaming"
&&
!
message
.
content
.
trim
()
const
isWaitingForContent
=
message
.
role
===
"assistant"
&&
message
.
streamState
===
"streaming"
&&
!
message
.
content
.
trim
()
const
videoStatusCard
=
isWaitingForContent
?
buildDouyinVideoStatusCard
(
message
,
activeExpertKey
)
:
null
const
videoStatusCard
=
isWaitingForContent
?
buildDouyinVideoStatusCard
(
message
,
activeExpertKey
)
:
null
...
...
apps/ui/src/features/experts/ExpertsView.tsx
View file @
f2b000fe
...
@@ -19,6 +19,12 @@ export type ExpertVisualKey =
...
@@ -19,6 +19,12 @@ export type ExpertVisualKey =
|
"leads"
|
"leads"
|
"sales"
|
"sales"
export
interface
ExpertStarterPrompt
{
title
:
string
description
:
string
prompt
:
string
}
export
interface
ExpertGuideContent
{
export
interface
ExpertGuideContent
{
greeting
:
string
greeting
:
string
summary
:
string
summary
:
string
...
@@ -33,6 +39,7 @@ export interface ExpertGuideContent {
...
@@ -33,6 +39,7 @@ export interface ExpertGuideContent {
continueHint
?:
string
continueHint
?:
string
placeholder
?:
string
placeholder
?:
string
prompts
:
string
[]
prompts
:
string
[]
starterPrompts
?:
ExpertStarterPrompt
[]
}
}
interface
ExpertsViewProps
{
interface
ExpertsViewProps
{
...
@@ -101,6 +108,38 @@ export function ExpertEmptyState({
...
@@ -101,6 +108,38 @@ export function ExpertEmptyState({
starterQuestionsHint
,
starterQuestionsHint
,
onStarterPrompt
onStarterPrompt
}:
ExpertEmptyStateProps
)
{
}:
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"
)
{
if
(
activeExpertKey
===
"douyin"
)
{
return
(
return
(
<
div
className=
"empty-state expert-empty-state expert-empty-state-douyin"
>
<
div
className=
"empty-state expert-empty-state expert-empty-state-douyin"
>
...
@@ -157,7 +196,7 @@ export function ExpertEmptyState({
...
@@ -157,7 +196,7 @@ export function ExpertEmptyState({
}
}
return
(
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
>
<
span
className=
"empty-state-kicker"
>
{
activeExpertName
}
</
span
>
<
strong
>
{
activeExpertGuide
.
greeting
}
</
strong
>
<
strong
>
{
activeExpertGuide
.
greeting
}
</
strong
>
<
p
>
{
starterQuestionsHint
}
</
p
>
<
p
>
{
starterQuestionsHint
}
</
p
>
...
...
apps/ui/src/features/experts/expertDisplay.ts
View file @
f2b000fe
import
type
{
ExpertDefinition
}
from
"@qjclaw/shared-types"
import
type
{
ExpertDefinition
}
from
"@qjclaw/shared-types"
import
type
{
MessageListMessage
}
from
"../chat/MessageList"
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"
import
type
{
ExpertProject
,
ExpertVisualKey
}
from
"../shell/ExpertTree"
export
interface
VideoStatusCardContent
{
export
interface
VideoStatusCardContent
{
...
@@ -9,6 +9,29 @@ export interface VideoStatusCardContent {
...
@@ -9,6 +9,29 @@ export interface VideoStatusCardContent {
hint
?:
string
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
{
export
function
getProjectDisplayName
(
project
:
ExpertProject
|
undefined
,
defaultChatName
=
"千匠问天"
):
string
{
return
project
?.
displayName
??
project
?.
name
??
defaultChatName
return
project
?.
displayName
??
project
?.
name
??
defaultChatName
}
}
...
@@ -102,13 +125,10 @@ function getExpertGuide(project: ExpertProject | undefined): ExpertGuideContent
...
@@ -102,13 +125,10 @@ function getExpertGuide(project: ExpertProject | undefined): ExpertGuideContent
switch
(
resolveExpertKey
(
project
))
{
switch
(
resolveExpertKey
(
project
))
{
case
"xiaohongshu"
:
case
"xiaohongshu"
:
return
{
return
{
greeting
:
"把产品、场景和目标人群说清楚,我先给你一版能直接开工的小红书任务。"
,
greeting
:
"准备好了,开始种草"
,
summary
:
"适合先做选题判断、笔记结构、标题草案、配图思路和发布时间建议。"
,
summary
:
"说清产品、场景、人群和目标,我会先拆选题、标题、笔记结构和配图方向。"
,
prompts
:
[
prompts
:
XIAOHONGSHU_STARTER_PROMPTS
.
map
((
item
)
=>
item
.
prompt
),
"帮我做一篇推荐平价火锅的笔记"
,
starterPrompts
:
XIAOHONGSHU_STARTER_PROMPTS
"做一篇制作抹茶奶酪欧包教程的爆文。"
,
"给通勤女生做一篇春季穿搭笔记。"
]
}
}
case
"douyin"
:
case
"douyin"
:
return
{
return
{
...
...
apps/ui/src/styles/theme-openclaw.css
View file @
f2b000fe
...
@@ -604,6 +604,11 @@
...
@@ -604,6 +604,11 @@
align-content
:
center
;
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
{
.conversation-shell
.home-empty-state
{
width
:
min
(
100%
,
760px
);
width
:
min
(
100%
,
760px
);
justify-self
:
center
;
justify-self
:
center
;
...
@@ -651,7 +656,7 @@
...
@@ -651,7 +656,7 @@
align-content
:
start
;
align-content
:
start
;
gap
:
6px
;
gap
:
6px
;
padding
:
15px
16px
14px
;
padding
:
15px
16px
14px
;
border-radius
:
1
4
px
;
border-radius
:
1
8
px
;
border-color
:
rgba
(
191
,
219
,
254
,
0.62
);
border-color
:
rgba
(
191
,
219
,
254
,
0.62
);
background
:
background
:
linear-gradient
(
180deg
,
rgba
(
255
,
255
,
255
,
0.96
),
rgba
(
248
,
251
,
255
,
0.9
)),
linear-gradient
(
180deg
,
rgba
(
255
,
255
,
255
,
0.96
),
rgba
(
248
,
251
,
255
,
0.9
)),
...
@@ -699,6 +704,116 @@
...
@@ -699,6 +704,116 @@
box-shadow
:
0
0
0
5px
rgba
(
147
,
197
,
253
,
0.28
);
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
,
1
fr
));
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-title
,
.conversation-shell
.starter-prompt-desc
{
.conversation-shell
.starter-prompt-desc
{
display
:
block
;
display
:
block
;
...
@@ -737,7 +852,8 @@
...
@@ -737,7 +852,8 @@
width
:
min
(
100%
,
620px
);
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
:
1
fr
;
grid-template-columns
:
1
fr
;
width
:
min
(
100%
,
520px
);
width
:
min
(
100%
,
520px
);
}
}
...
@@ -761,6 +877,20 @@
...
@@ -761,6 +877,20 @@
min-height
:
82px
;
min-height
:
82px
;
padding
:
12px
14px
;
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
)
{
@media
(
prefers-reduced-motion
:
reduce
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment