Skip to content

Commit 390657e

Browse files
author
xiaowei.mao
committed
add asdaction
1 parent 332d51e commit 390657e

File tree

4 files changed

+94
-18
lines changed

4 files changed

+94
-18
lines changed

backend/chainlit/types.py

+2
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ class Starter(DataClassJsonMixin):
257257
label: str
258258
message: str
259259
icon: Optional[str] = None
260+
commands: Optional[List[str]] = None
261+
toggle_commands: Optional[List[str]] = None
260262

261263

262264
@dataclass

frontend/src/components/chat/Messages/Message/AskActionButtons.tsx

+26-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
TooltipProvider,
1212
TooltipTrigger
1313
} from '@/components/ui/tooltip';
14+
import { cn } from '@/lib/utils';
1415

1516
const AskActionButton = ({ action }: { action: IAction }) => {
1617
const { loading, askUser } = useContext(MessageContext);
@@ -28,14 +29,30 @@ const AskActionButton = ({ action }: { action: IAction }) => {
2829
return null;
2930
}, [action]);
3031

32+
// 创建自定义样式对象
33+
const customStyle = {};
34+
if (action.bgColor) {
35+
customStyle['backgroundColor'] = action.bgColor;
36+
}
37+
if (action.textColor) {
38+
customStyle['color'] = action.textColor;
39+
}
40+
3141
const button = (
3242
<Button
33-
className="break-words h-auto min-h-10 whitespace-normal"
43+
className={cn(
44+
"break-words h-auto min-h-10 whitespace-normal",
45+
action.fullWidth ? "w-full justify-start" : "",
46+
action.className,
47+
icon ? "gap-2" : ""
48+
)}
3449
id={action.id}
3550
onClick={() => {
3651
askUser?.callback(action);
3752
}}
38-
variant="outline"
53+
variant={action.variant as any || "outline"}
54+
size={action.size as any || "default"}
55+
style={customStyle}
3956
disabled={loading}
4057
>
4158
{icon}
@@ -76,8 +93,14 @@ const AskActionButtons = ({
7693

7794
if (!belongsToMessage || !isAskingAction || !actions.length) return null;
7895

96+
// 检查是否有任何按钮设置了fullWidth属性
97+
const hasFullWidthButtons = filteredActions.some(a => a.fullWidth);
98+
7999
return (
80-
<div className="flex items-center gap-1 flex-wrap">
100+
<div className={cn(
101+
"flex gap-1",
102+
hasFullWidthButtons ? "flex-col w-full" : "items-center flex-wrap"
103+
)}>
81104
{filteredActions.map((a) => (
82105
<AskActionButton key={a.id} action={a} />
83106
))}

frontend/src/components/chat/Starter.tsx

+64-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useCallback, useContext } from 'react';
2-
import { useRecoilValue } from 'recoil';
2+
import { useRecoilState, useRecoilValue } from 'recoil';
33
import { v4 as uuidv4 } from 'uuid';
44

55
import {
@@ -8,31 +8,68 @@ import {
88
IStep,
99
useAuth,
1010
useChatData,
11-
useChatInteract
11+
useChatInteract,
12+
commandsState,
13+
toggleCommandsState
1214
} from '@chainlit/react-client';
1315

16+
import Icon from '@/components/Icon';
1417
import { Button } from '@/components/ui/button';
1518

16-
import { persistentCommandState } from '@/state/chat';
19+
import { IToggleable, persistentCommandState, toggleableStates } from '@/state/chat';
1720

1821
interface StarterProps {
1922
starter: IStarter;
2023
}
2124

2225
export default function Starter({ starter }: StarterProps) {
2326
const apiClient = useContext(ChainlitContext);
24-
const selectedCommand = useRecoilValue(persistentCommandState);
27+
const [selectedCommand, setSelectedCommand] = useRecoilState(persistentCommandState);
28+
const [toggleables, setToggleables] = useRecoilState(toggleableStates);
2529
const { sendMessage } = useChatInteract();
2630
const { loading, connected } = useChatData();
2731
const { user } = useAuth();
32+
const commands = useRecoilValue(commandsState);
33+
const toggleCommands = useRecoilValue(toggleCommandsState);
2834

2935
const disabled = loading || !connected;
3036

3137
const onSubmit = useCallback(async () => {
38+
// 处理命令激活
39+
if (starter.commands && starter.commands.length > 0) {
40+
const commandToActivate = commands.find(cmd => cmd.id === starter.commands![0]);
41+
if (commandToActivate) {
42+
setSelectedCommand(commandToActivate);
43+
}
44+
}
45+
46+
// 处理可切换命令激活
47+
if (starter.toggle_commands && starter.toggle_commands.length > 0) {
48+
const newToggleables: IToggleable[] = [...toggleables];
49+
50+
// 移除所有非持久的toggleables
51+
const persistentOnly = newToggleables.filter(t => t.persistent);
52+
53+
// 添加starter中指定的toggle_commands
54+
starter.toggle_commands.forEach(cmdId => {
55+
const toggleCmd = toggleCommands.find(cmd => cmd.id === cmdId);
56+
if (toggleCmd) {
57+
persistentOnly.push({
58+
id: toggleCmd.id,
59+
active: true,
60+
persistent: toggleCmd.persistent
61+
});
62+
}
63+
});
64+
65+
setToggleables(persistentOnly);
66+
}
67+
3268
const message: IStep = {
3369
threadId: '',
3470
id: uuidv4(),
35-
command: selectedCommand?.id,
71+
command: starter.commands && starter.commands.length > 0 ? starter.commands[0] : selectedCommand?.id,
72+
toggleables: starter.toggle_commands || [],
3673
name: user?.identifier || 'User',
3774
type: 'user_message',
3875
output: starter.message,
@@ -41,7 +78,15 @@ export default function Starter({ starter }: StarterProps) {
4178
};
4279

4380
sendMessage(message, []);
44-
}, [user, selectedCommand, sendMessage, starter]);
81+
}, [user, selectedCommand, toggleables, commands, toggleCommands, sendMessage, starter, setSelectedCommand, setToggleables]);
82+
83+
// 检查是否是图片URL
84+
const isImageUrl = (url: string): boolean => {
85+
return url.startsWith('http') ||
86+
url.startsWith('/') ||
87+
url.startsWith('./') ||
88+
url.startsWith('../');
89+
};
4590

4691
return (
4792
<Button
@@ -53,15 +98,19 @@ export default function Starter({ starter }: StarterProps) {
5398
>
5499
<div className="flex gap-2">
55100
{starter.icon ? (
56-
<img
57-
className="h-5 w-5 rounded-md"
58-
src={
59-
starter.icon?.startsWith('/public')
60-
? apiClient.buildEndpoint(starter.icon)
61-
: starter.icon
62-
}
63-
alt={starter.label}
64-
/>
101+
isImageUrl(starter.icon) ? (
102+
<img
103+
className="h-5 w-5 rounded-md"
104+
src={
105+
starter.icon?.startsWith('/public')
106+
? apiClient.buildEndpoint(starter.icon)
107+
: starter.icon
108+
}
109+
alt={starter.label}
110+
/>
111+
) : (
112+
<Icon name={starter.icon} className="!h-5 !w-5" />
113+
)
65114
) : null}
66115
<p className="text-sm text-muted-foreground truncate">
67116
{starter.label}

libs/react-client/src/types/config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ export interface IStarter {
22
label: string;
33
message: string;
44
icon?: string;
5+
commands?: string[];
6+
toggle_commands?: string[];
57
}
68

79
export interface ChatProfile {

0 commit comments

Comments
 (0)