@amaster.ai/components-templates 1.4.11 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -8
- package/components/ai-assistant/amaster.config.json +3 -0
- package/components/ai-assistant/package.json +3 -3
- package/components/ai-assistant/template/components/chat-assistant-message.tsx +1 -1
- package/components/ai-assistant/template/components/chat-banner.tsx +1 -1
- package/components/ai-assistant/template/components/chat-display-mode-switcher.tsx +6 -6
- package/components/ai-assistant/template/components/chat-floating-button.tsx +4 -5
- package/components/ai-assistant/template/components/chat-floating-card.tsx +3 -3
- package/components/ai-assistant/template/components/chat-header.tsx +5 -5
- package/components/ai-assistant/template/components/chat-input.tsx +21 -22
- package/components/ai-assistant/template/components/chat-messages.tsx +10 -2
- package/components/ai-assistant/template/components/chat-recommends.tsx +12 -14
- package/components/ai-assistant/template/components/chat-user-message.tsx +1 -1
- package/components/ai-assistant/template/components/ui-renderer.tsx +1 -1
- package/components/ai-assistant/template/components/voice-input.tsx +1 -1
- package/components/ai-assistant/template/hooks/useVoiceInput.ts +18 -20
- package/components/ai-assistant/template/inline-ai-assistant.tsx +1 -1
- package/components/ai-assistant-taro/amaster.config.json +3 -0
- package/components/ai-assistant-taro/package.json +94 -0
- package/components/ai-assistant-taro/template/components/ChatAssistantMessage.tsx +154 -0
- package/components/ai-assistant-taro/template/components/ChatHeader.tsx +27 -0
- package/components/ai-assistant-taro/template/components/ChatInput.tsx +204 -0
- package/components/ai-assistant-taro/template/components/ChatMessages.tsx +126 -0
- package/components/ai-assistant-taro/template/components/ChatUserMessage.tsx +25 -0
- package/components/ai-assistant-taro/template/components/VoiceInput.tsx +169 -0
- package/components/ai-assistant-taro/template/components/markdown.tsx +156 -0
- package/components/ai-assistant-taro/template/hooks/useConversation.ts +787 -0
- package/components/ai-assistant-taro/template/hooks/useSafeArea.ts +20 -0
- package/components/ai-assistant-taro/template/hooks/useVoiceInput.ts +204 -0
- package/components/ai-assistant-taro/template/i18n.ts +157 -0
- package/components/ai-assistant-taro/template/index.config.ts +10 -0
- package/components/ai-assistant-taro/template/index.tsx +83 -0
- package/components/ai-assistant-taro/template/types.ts +58 -0
- package/package.json +5 -2
- package/packages/cli/dist/index.js +14 -3
- package/packages/cli/dist/index.js.map +1 -1
- package/packages/cli/package.json +1 -1
- package/components/ai-assistant/example.md +0 -34
- package/components/ai-assistant/others.md +0 -16
package/README.md
CHANGED
|
@@ -137,12 +137,14 @@ npm run create:component
|
|
|
137
137
|
|
|
138
138
|
1. 在 `components/` 目录下创建新组件目录
|
|
139
139
|
2. 添加 `package.json` 配置文件
|
|
140
|
-
3.
|
|
140
|
+
3. 添加 `amaster.config.json` 配置文件(配置目标目录)
|
|
141
|
+
4. 在 `template/` 目录下放置模板文件
|
|
141
142
|
|
|
142
143
|
```
|
|
143
144
|
components/
|
|
144
145
|
└── your-component/
|
|
145
|
-
├── package.json #
|
|
146
|
+
├── package.json # 组件依赖配置
|
|
147
|
+
├── amaster.config.json # 组件模板配置
|
|
146
148
|
└── template/ # 模板文件
|
|
147
149
|
├── index.ts
|
|
148
150
|
└── ...
|
|
@@ -155,12 +157,6 @@ components/
|
|
|
155
157
|
"name": "your-component",
|
|
156
158
|
"version": "1.0.0",
|
|
157
159
|
"description": "组件描述",
|
|
158
|
-
"template": {
|
|
159
|
-
"name": "your-component",
|
|
160
|
-
"targetDir": "src/components/your-component",
|
|
161
|
-
"files": ["**/*"],
|
|
162
|
-
"ignore": ["node_modules", "*.log", ".DS_Store"]
|
|
163
|
-
},
|
|
164
160
|
"dependencies": {
|
|
165
161
|
"some-lib": "^1.0.0"
|
|
166
162
|
},
|
|
@@ -173,6 +169,14 @@ components/
|
|
|
173
169
|
}
|
|
174
170
|
```
|
|
175
171
|
|
|
172
|
+
### 组件 amaster.config.json 配置
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"targetDir": "src/components/your-component"
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
176
180
|
## 发布
|
|
177
181
|
|
|
178
182
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amaster-react-project",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite --force",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@a2a-js/sdk": "^0.3.7",
|
|
20
|
-
"@amaster.ai/client": "1.1.0-beta.
|
|
21
|
-
"@amaster.ai/vite-plugins": "1.1.0-beta.
|
|
20
|
+
"@amaster.ai/client": "1.1.0-beta.59",
|
|
21
|
+
"@amaster.ai/vite-plugins": "1.1.0-beta.59",
|
|
22
22
|
"@fontsource-variable/inter": "^5.2.8",
|
|
23
23
|
"@fortawesome/fontawesome-free": "^6.1.1",
|
|
24
24
|
"@hookform/resolvers": "^5.2.2",
|
|
@@ -195,7 +195,7 @@ const ChatAssistantMessage: React.FC<
|
|
|
195
195
|
)}
|
|
196
196
|
>
|
|
197
197
|
{showAvatar && (
|
|
198
|
-
<MessageSquare className="h-3.5 w-3.5 text-
|
|
198
|
+
<MessageSquare className="h-3.5 w-3.5 text-primary-foreground" strokeWidth={2} />
|
|
199
199
|
)}
|
|
200
200
|
</div>
|
|
201
201
|
<MessageContentRenderer message={message} {...rest} />
|
|
@@ -16,22 +16,22 @@ const ChatDisplayModeSwitcher: React.FC<{
|
|
|
16
16
|
}[] = [
|
|
17
17
|
{
|
|
18
18
|
mode: "floating",
|
|
19
|
-
icon: <Layers2 className="
|
|
19
|
+
icon: <Layers2 className="size-4" strokeWidth={1.75} />,
|
|
20
20
|
title: getText().displayMode.floating,
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
mode: "side-right",
|
|
24
|
-
icon: <Sidebar className="
|
|
24
|
+
icon: <Sidebar className="size-4 rotate-180" strokeWidth={1.75} />,
|
|
25
25
|
title: getText().displayMode.sideRight,
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
mode: "side-left",
|
|
29
|
-
icon: <Sidebar className="
|
|
29
|
+
icon: <Sidebar className="size-4 " strokeWidth={1.75} />,
|
|
30
30
|
title: getText().displayMode.sideLeft,
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
mode: "fullscreen",
|
|
34
|
-
icon: <Maximize className="
|
|
34
|
+
icon: <Maximize className="size-4" strokeWidth={1.75} />,
|
|
35
35
|
title: getText().displayMode.fullscreen,
|
|
36
36
|
},
|
|
37
37
|
];
|
|
@@ -39,7 +39,7 @@ const ChatDisplayModeSwitcher: React.FC<{
|
|
|
39
39
|
return (
|
|
40
40
|
<HoverCard openDelay={0}>
|
|
41
41
|
<HoverCardTrigger asChild>
|
|
42
|
-
<Button variant="ghost" size="icon" className="
|
|
42
|
+
<Button variant="ghost" size="icon" className="size-8">
|
|
43
43
|
{modes.find((m) => m.mode === displayMode)?.icon}
|
|
44
44
|
</Button>
|
|
45
45
|
</HoverCardTrigger>
|
|
@@ -48,7 +48,7 @@ const ChatDisplayModeSwitcher: React.FC<{
|
|
|
48
48
|
<Button
|
|
49
49
|
key={mode}
|
|
50
50
|
variant="ghost"
|
|
51
|
-
className="justify-start
|
|
51
|
+
className="justify-start"
|
|
52
52
|
onClick={() => onChange(mode)}
|
|
53
53
|
size="sm"
|
|
54
54
|
disabled={mode === displayMode}
|
|
@@ -10,7 +10,6 @@ interface ChatFloatingButtonProps {
|
|
|
10
10
|
style: React.CSSProperties;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
13
|
const ChatFloatingButton: React.FC<ChatFloatingButtonProps> = ({
|
|
15
14
|
onClick,
|
|
16
15
|
onMouseDown,
|
|
@@ -32,14 +31,14 @@ const ChatFloatingButton: React.FC<ChatFloatingButtonProps> = ({
|
|
|
32
31
|
className={`
|
|
33
32
|
group relative h-full w-full rounded-full
|
|
34
33
|
bg-gradient-to-br from-primary to-primary/40
|
|
35
|
-
text-
|
|
34
|
+
text-primary-foreground
|
|
36
35
|
border-0
|
|
37
36
|
transition-all duration-300 ease-out select-none
|
|
38
37
|
cursor-pointer
|
|
39
38
|
${
|
|
40
39
|
isDragging
|
|
41
|
-
? "shadow-
|
|
42
|
-
: "shadow-
|
|
40
|
+
? "shadow-2xl scale-105"
|
|
41
|
+
: "shadow-lg hover:shadow-2xl hover:scale-105"
|
|
43
42
|
}
|
|
44
43
|
`}
|
|
45
44
|
>
|
|
@@ -53,4 +52,4 @@ const ChatFloatingButton: React.FC<ChatFloatingButtonProps> = ({
|
|
|
53
52
|
);
|
|
54
53
|
};
|
|
55
54
|
|
|
56
|
-
export default ChatFloatingButton;
|
|
55
|
+
export default ChatFloatingButton;
|
|
@@ -23,14 +23,14 @@ const ChatFloatingCard: React.FC<ChatFloatingCardProps> = ({
|
|
|
23
23
|
<Card
|
|
24
24
|
ref={containerRef}
|
|
25
25
|
className={cn(
|
|
26
|
-
`flex flex-col overflow-hidden bg-
|
|
26
|
+
`flex flex-col overflow-hidden bg-card border border-border z-50`,
|
|
27
27
|
{
|
|
28
28
|
"fixed inset-0 w-auto h-auto animate-in fade-in-0 zoom-in-[0.98] duration-300 origin-top-left rounded-none": displayMode === "fullscreen",
|
|
29
29
|
"fixed top-0 right-0 w-[420px] h-full rounded-none": displayMode === "side-right",
|
|
30
30
|
"fixed top-0 left-0 w-[420px] h-full rounded-none": displayMode === "side-left",
|
|
31
31
|
"rounded-2xl": displayMode === "floating",
|
|
32
|
-
"shadow-
|
|
33
|
-
"shadow-
|
|
32
|
+
"shadow-2xl": displayMode === "floating" && isDragging,
|
|
33
|
+
"shadow-lg": displayMode === "floating" && !isDragging,
|
|
34
34
|
}
|
|
35
35
|
)}
|
|
36
36
|
style={displayMode !== "floating" ? {} : style}
|
|
@@ -27,14 +27,14 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
|
|
|
27
27
|
return (
|
|
28
28
|
<div
|
|
29
29
|
className={cn(
|
|
30
|
-
"flex items-center justify-between px-4",
|
|
30
|
+
"flex items-center justify-between px-4 py-1 pr-2",
|
|
31
31
|
!disabledDrag && onMouseDown
|
|
32
32
|
? isDragging
|
|
33
33
|
? "cursor-grabbing"
|
|
34
34
|
: "cursor-grab"
|
|
35
35
|
: "",
|
|
36
36
|
{
|
|
37
|
-
'border-b border-
|
|
37
|
+
'border-b border-border': displayMode === "floating",
|
|
38
38
|
},
|
|
39
39
|
)}
|
|
40
40
|
onMouseDown={!disabledDrag ? onMouseDown : undefined}
|
|
@@ -42,7 +42,7 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
|
|
|
42
42
|
>
|
|
43
43
|
<div className="flex items-center gap-2.5">
|
|
44
44
|
<div className="flex flex-col">
|
|
45
|
-
<span className="text-sm font-semibold text-
|
|
45
|
+
<span className="text-sm font-semibold text-foreground">
|
|
46
46
|
{getText().assistantName}
|
|
47
47
|
</span>
|
|
48
48
|
</div>
|
|
@@ -55,9 +55,9 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
|
|
|
55
55
|
variant="ghost"
|
|
56
56
|
size="icon"
|
|
57
57
|
onClick={onClose}
|
|
58
|
-
className="
|
|
58
|
+
className="size-8"
|
|
59
59
|
>
|
|
60
|
-
<X className="
|
|
60
|
+
<X className="size-4" strokeWidth={1.75} />
|
|
61
61
|
</Button>
|
|
62
62
|
)}
|
|
63
63
|
</div>
|
|
@@ -19,7 +19,10 @@ import {
|
|
|
19
19
|
} from "@/components/ui/tooltip";
|
|
20
20
|
import VoiceInputButton from "./voice-input";
|
|
21
21
|
|
|
22
|
-
const NewConvButton: React.FC<{ onNew?: () => void; disabled?: boolean }> = ({
|
|
22
|
+
const NewConvButton: React.FC<{ onNew?: () => void; disabled?: boolean }> = ({
|
|
23
|
+
onNew,
|
|
24
|
+
disabled,
|
|
25
|
+
}) => {
|
|
23
26
|
if (!onNew) return null;
|
|
24
27
|
return (
|
|
25
28
|
<TooltipProvider>
|
|
@@ -32,7 +35,7 @@ const NewConvButton: React.FC<{ onNew?: () => void; disabled?: boolean }> = ({ o
|
|
|
32
35
|
size="icon"
|
|
33
36
|
onClick={onNew}
|
|
34
37
|
disabled={disabled}
|
|
35
|
-
className="h-8 w-8
|
|
38
|
+
className="h-8 w-8 cursor-pointer"
|
|
36
39
|
>
|
|
37
40
|
<MessageCirclePlus className="size-5" />
|
|
38
41
|
</Button>
|
|
@@ -49,12 +52,11 @@ const SubmitButton: React.FC<{
|
|
|
49
52
|
}> = ({ disabled, starting, onClick }) => {
|
|
50
53
|
return (
|
|
51
54
|
<Button
|
|
52
|
-
type="button"
|
|
53
55
|
onClick={onClick}
|
|
54
56
|
disabled={disabled || starting}
|
|
55
57
|
size="icon"
|
|
56
58
|
className={cn(
|
|
57
|
-
"size-8 text-
|
|
59
|
+
"rounded-xl size-8 text-primary-foreground transition-all duration-200 cursor-pointer shadow-sm hover:shadow-2xl",
|
|
58
60
|
"bg-gradient-to-r from-primary to-primary/80",
|
|
59
61
|
"hover:from-primary/90 hover:to-primary/70",
|
|
60
62
|
"disabled:opacity-40 disabled:cursor-not-allowed",
|
|
@@ -76,20 +78,10 @@ const StopButton: React.FC<{ onClick: () => void; disabled?: boolean }> = ({
|
|
|
76
78
|
}) => {
|
|
77
79
|
return (
|
|
78
80
|
<Button
|
|
79
|
-
type="button"
|
|
80
81
|
onClick={onClick}
|
|
81
82
|
size="icon"
|
|
82
83
|
disabled={disabled}
|
|
83
|
-
className="
|
|
84
|
-
size-8
|
|
85
|
-
bg-foreground
|
|
86
|
-
hover:bg-foreground/50
|
|
87
|
-
text-white
|
|
88
|
-
rounded-xl
|
|
89
|
-
transition-all duration-200
|
|
90
|
-
cursor-pointer
|
|
91
|
-
shadow-[0_2px_8px_rgba(239,68,68,0.3)]
|
|
92
|
-
hover:shadow-[0_4px_12px_rgba(239,68,68,0.4)]"
|
|
84
|
+
className="size-8 bg-success hover:bg-success/50 text-success-foreground rounded-xl transition-all duration-200 cursor-pointer shadow-sm hover:shadow-2xl"
|
|
93
85
|
>
|
|
94
86
|
<Square className="h-3 w-3 fill-current" strokeWidth={2} />
|
|
95
87
|
</Button>
|
|
@@ -117,14 +109,14 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
117
109
|
onNew,
|
|
118
110
|
onCancel,
|
|
119
111
|
starting,
|
|
120
|
-
displayMode
|
|
112
|
+
displayMode,
|
|
121
113
|
}) => {
|
|
122
114
|
const hasConversations = conversations.length > 0;
|
|
123
115
|
const lastConv =
|
|
124
116
|
conversations.length > 0 ? conversations[conversations.length - 1] : null;
|
|
125
117
|
const lastIsDivider = useMemo(() => {
|
|
126
118
|
if (!lastConv) return false;
|
|
127
|
-
return
|
|
119
|
+
return lastConv.system?.level === "newConversation";
|
|
128
120
|
}, [lastConv]);
|
|
129
121
|
|
|
130
122
|
const [focus, setFocus] = useState(false);
|
|
@@ -139,7 +131,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
139
131
|
<div className="py-3 px-4">
|
|
140
132
|
<div
|
|
141
133
|
className={cn(
|
|
142
|
-
"w-full rounded-xl bg-
|
|
134
|
+
"w-full rounded-xl bg-card transition-all duration-200 p-3 border border-primary/80",
|
|
143
135
|
{
|
|
144
136
|
"border-primary ring-2 ring-primary/20": focus,
|
|
145
137
|
},
|
|
@@ -154,12 +146,19 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
154
146
|
placeholder={getText().typePlaceholder}
|
|
155
147
|
onFocus={() => setFocus(true)}
|
|
156
148
|
onBlur={() => setFocus(false)}
|
|
157
|
-
className="w-full text-sm text-
|
|
149
|
+
className="w-full text-sm text-foreground placeholder:text-muted-foreground outline-none resize-none leading-5 bg-card"
|
|
158
150
|
rows={4}
|
|
159
151
|
/>
|
|
160
152
|
<div className="flex items-center justify-between gap-2 mt-1">
|
|
161
153
|
<div className="flex items-center gap-1">
|
|
162
|
-
{onNew &&
|
|
154
|
+
{onNew && (
|
|
155
|
+
<NewConvButton
|
|
156
|
+
onNew={onNew}
|
|
157
|
+
disabled={
|
|
158
|
+
!hasConversations || lastIsDivider || starting || isLoading
|
|
159
|
+
}
|
|
160
|
+
/>
|
|
161
|
+
)}
|
|
163
162
|
</div>
|
|
164
163
|
<div className="flex items-center gap-2">
|
|
165
164
|
<VoiceInputButton
|
|
@@ -170,7 +169,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
170
169
|
value={inputValue}
|
|
171
170
|
disabled={isLoading || starting}
|
|
172
171
|
/>
|
|
173
|
-
{isLoading ? (
|
|
172
|
+
{!isLoading ? (
|
|
174
173
|
<StopButton onClick={onCancel || (() => {})} />
|
|
175
174
|
) : (
|
|
176
175
|
<SubmitButton
|
|
@@ -184,7 +183,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
184
183
|
</div>
|
|
185
184
|
|
|
186
185
|
{(conversations.length > 0 || displayMode !== "inline") && (
|
|
187
|
-
<p className="text-[10px] text-
|
|
186
|
+
<p className="text-[10px] text-muted-foreground/50 mt-2 text-center">
|
|
188
187
|
{getText().footerAiWarning}
|
|
189
188
|
</p>
|
|
190
189
|
)}
|
|
@@ -71,6 +71,13 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
|
|
|
71
71
|
},
|
|
72
72
|
className,
|
|
73
73
|
)}
|
|
74
|
+
style={{
|
|
75
|
+
maskImage:
|
|
76
|
+
"linear-gradient(to bottom, transparent, black 10px, black calc(100% - 10px), transparent)",
|
|
77
|
+
WebkitMaskImage:
|
|
78
|
+
"linear-gradient(to bottom, transparent, black 10px, black calc(100% - 10px), transparent)",
|
|
79
|
+
maskSize: "80% 100%",
|
|
80
|
+
}}
|
|
74
81
|
data-role="chat-messages"
|
|
75
82
|
>
|
|
76
83
|
{isLoadingHistory ? (
|
|
@@ -94,7 +101,8 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
|
|
|
94
101
|
const historyId = conversation.historyId || "";
|
|
95
102
|
const lastHistoryId = conversations[index - 1]?.historyId || "";
|
|
96
103
|
let addDivider =
|
|
97
|
-
index > 0 && historyId !== lastHistoryId ||
|
|
104
|
+
(index > 0 && historyId !== lastHistoryId) ||
|
|
105
|
+
conversation.system?.level === "newConversation";
|
|
98
106
|
|
|
99
107
|
return (
|
|
100
108
|
<div key={conversation.taskId} className="flex flex-col gap-4">
|
|
@@ -148,4 +156,4 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
|
|
|
148
156
|
);
|
|
149
157
|
};
|
|
150
158
|
|
|
151
|
-
export default ChatMessages;
|
|
159
|
+
export default ChatMessages;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Button } from "@/components/ui/button";
|
|
1
2
|
import { getText } from "../i18n";
|
|
2
3
|
|
|
3
4
|
const ChatRecommends: React.FC<{
|
|
@@ -5,31 +6,28 @@ const ChatRecommends: React.FC<{
|
|
|
5
6
|
data?: string[];
|
|
6
7
|
onSend: (prompt: string) => void;
|
|
7
8
|
disabled?: boolean;
|
|
8
|
-
}> = ({
|
|
9
|
+
}> = ({
|
|
10
|
+
hidden,
|
|
11
|
+
data = getText().defaultRecommendedQuestions,
|
|
12
|
+
onSend,
|
|
13
|
+
disabled,
|
|
14
|
+
}) => {
|
|
9
15
|
if (hidden || !data || data.length === 0) return null;
|
|
10
16
|
return (
|
|
11
17
|
<div className="flex flex-wrap gap-2 pt-2 px-4">
|
|
12
18
|
{data.map((prompt, index) => (
|
|
13
|
-
<
|
|
14
|
-
type="button"
|
|
19
|
+
<Button
|
|
15
20
|
key={prompt}
|
|
21
|
+
variant="outline"
|
|
16
22
|
onClick={() => onSend(prompt)}
|
|
17
23
|
disabled={disabled}
|
|
18
24
|
className="
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
text-[#374151] hover:text-[#111827]
|
|
22
|
-
rounded-full
|
|
23
|
-
border border-[#D1D5DB]
|
|
24
|
-
transition-all duration-200
|
|
25
|
-
cursor-pointer
|
|
26
|
-
animate-in fade-in-0 slide-in-from-bottom-1
|
|
27
|
-
text-nowrap
|
|
28
|
-
"
|
|
25
|
+
text-xs px-2 py-1 h-auto rounded-full cursor-pointer text-nowrap
|
|
26
|
+
transition-all duration-200 animate-in fade-in-0 slide-in-from-bottom-1"
|
|
29
27
|
style={{ animationDelay: `${index * 50}ms` }}
|
|
30
28
|
>
|
|
31
29
|
{prompt}
|
|
32
|
-
</
|
|
30
|
+
</Button>
|
|
33
31
|
))}
|
|
34
32
|
</div>
|
|
35
33
|
);
|
|
@@ -12,7 +12,7 @@ const ChatUserMessage: React.FC<{
|
|
|
12
12
|
className={`
|
|
13
13
|
px-4 py-2.5 rounded-2xl
|
|
14
14
|
transition-all duration-200 min-w-0 overflow-hidden max-w-full
|
|
15
|
-
bg-
|
|
15
|
+
bg-primary/10 text-foreground rounded-br-md
|
|
16
16
|
`}
|
|
17
17
|
>
|
|
18
18
|
<div className="text-sm leading-relaxed whitespace-pre-wrap break-words">
|
|
@@ -29,7 +29,7 @@ export const UIRenderer: React.FC<UIRendererProps> = ({ spec, className }) => {
|
|
|
29
29
|
return (
|
|
30
30
|
<Suspense
|
|
31
31
|
fallback={
|
|
32
|
-
<Skeleton className={cn("w-full h-[120px] bg-
|
|
32
|
+
<Skeleton className={cn("w-full h-[120px] bg-primary/20 flex items-center justify-center gap-2", className)}>
|
|
33
33
|
<LoaderCircle className="size-4 animate-spin" />
|
|
34
34
|
<span>{getText().loading}...</span>
|
|
35
35
|
</Skeleton>
|
|
@@ -29,7 +29,7 @@ const VoiceInputButton: React.FC<{
|
|
|
29
29
|
onClick={stoppable ? stop : status === "idle" ? start : undefined}
|
|
30
30
|
disabled={finalDisabled}
|
|
31
31
|
className={cn("h-8 w-8 rounded-lg cursor-pointer text-xs ", {
|
|
32
|
-
"bg-gradient-to-r from-
|
|
32
|
+
"bg-gradient-to-r from-primary to-primary/80 hover:from-primary/90 hover:to-primary/70 text-primary-foreground animate-pulse":
|
|
33
33
|
running,
|
|
34
34
|
"w-auto": statusText,
|
|
35
35
|
})}
|
|
@@ -8,10 +8,7 @@ type Status =
|
|
|
8
8
|
| "idle"
|
|
9
9
|
| "starting"
|
|
10
10
|
| "ready"
|
|
11
|
-
| "speaking"
|
|
12
|
-
| "recording"
|
|
13
11
|
| "stopping"
|
|
14
|
-
| "ended"
|
|
15
12
|
| "error"
|
|
16
13
|
| "closed";
|
|
17
14
|
|
|
@@ -30,10 +27,10 @@ export const useVoiceInput = ({
|
|
|
30
27
|
const AsrClientRef = useRef<ASRClient | null>(null);
|
|
31
28
|
const runningRef = useRef(false);
|
|
32
29
|
const stoppable = useMemo(() => {
|
|
33
|
-
return ["ready"
|
|
30
|
+
return ["ready"].includes(status);
|
|
34
31
|
}, [status]);
|
|
35
32
|
const disabledClick = useMemo(() => {
|
|
36
|
-
return ["starting", "
|
|
33
|
+
return ["starting", "stopping", "closed", "error"].includes(
|
|
37
34
|
status,
|
|
38
35
|
);
|
|
39
36
|
}, [status]);
|
|
@@ -53,13 +50,6 @@ export const useVoiceInput = ({
|
|
|
53
50
|
onReady() {
|
|
54
51
|
setStatus("ready");
|
|
55
52
|
},
|
|
56
|
-
onSpeechStart() {
|
|
57
|
-
setStatus((status) => (status !== "stopping" ? "recording" : status));
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
onSpeechEnd() {
|
|
61
|
-
setStatus((status) => (status !== "stopping" ? "recording" : status));
|
|
62
|
-
},
|
|
63
53
|
onTranscript(text, isFinal) {
|
|
64
54
|
if (!isFinal) {
|
|
65
55
|
temp = text;
|
|
@@ -70,10 +60,21 @@ export const useVoiceInput = ({
|
|
|
70
60
|
onChange(result);
|
|
71
61
|
}
|
|
72
62
|
},
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
onAudioBufferCommitted() {
|
|
64
|
+
console.debug("音频缓冲区已提交");
|
|
75
65
|
},
|
|
76
|
-
onError() {
|
|
66
|
+
onError(error) {
|
|
67
|
+
if (error.message === "NO_DEVICES_AVAILABLE") {
|
|
68
|
+
toast({
|
|
69
|
+
title: getText().voiceInputError.microphoneAccessDenied,
|
|
70
|
+
variant: "destructive",
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
toast({
|
|
74
|
+
title: getText().voiceInputError.unknownError,
|
|
75
|
+
variant: "destructive",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
77
78
|
reset();
|
|
78
79
|
},
|
|
79
80
|
onClose() {
|
|
@@ -89,7 +90,7 @@ export const useVoiceInput = ({
|
|
|
89
90
|
AsrClientRef.current = asrClient;
|
|
90
91
|
} catch (error) {
|
|
91
92
|
const message = error.message;
|
|
92
|
-
const showError = (title: string) => toast({title})
|
|
93
|
+
const showError = (title: string) => toast({ title });
|
|
93
94
|
if (message.includes("Microphone access denied")) {
|
|
94
95
|
showError(getText().voiceInputError.microphoneAccessDenied);
|
|
95
96
|
} else if (message.includes("No speech detected")) {
|
|
@@ -147,15 +148,12 @@ export const useVoiceInput = ({
|
|
|
147
148
|
}
|
|
148
149
|
}, [status]);
|
|
149
150
|
|
|
150
|
-
const statusTextMap = {
|
|
151
|
+
const statusTextMap: Record<Status, string> = {
|
|
151
152
|
// idle: t('translation:voiceInputStatus.idle'),
|
|
152
153
|
idle: "",
|
|
153
154
|
starting: getText().voiceInputStatus.starting,
|
|
154
155
|
ready: getText().voiceInputStatus.ready,
|
|
155
|
-
speaking: getText().voiceInputStatus.speaking,
|
|
156
|
-
recording: getText().voiceInputStatus.recording,
|
|
157
156
|
stopping: getText().voiceInputStatus.stopping,
|
|
158
|
-
ended: getText().voiceInputStatus.ended,
|
|
159
157
|
error: getText().voiceInputStatus.error,
|
|
160
158
|
closed: getText().voiceInputStatus.closed,
|
|
161
159
|
};
|
|
@@ -45,7 +45,7 @@ const InlineAIAssistant: React.FC<InlineAIAssistantProps> = ({
|
|
|
45
45
|
return (
|
|
46
46
|
<div
|
|
47
47
|
className={cn(
|
|
48
|
-
"w-full h-full max-h-screen relative flex flex-col justify-center overflow-hidden max-w-
|
|
48
|
+
"w-full h-full max-h-screen relative flex flex-col justify-center overflow-hidden max-w-6xl mx-auto",
|
|
49
49
|
className,
|
|
50
50
|
)}
|
|
51
51
|
style={style}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "taro-project",
|
|
3
|
+
"version": "1.6.0",
|
|
4
|
+
"description": "开箱即用的基于Taro + React + Zustand + TailwindCSS + TypeScript的模板",
|
|
5
|
+
"author": "amaster.ai",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"templateInfo": {
|
|
8
|
+
"name": "default",
|
|
9
|
+
"typescript": true,
|
|
10
|
+
"css": "sass"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"dev": "bun run build:h5 -- --watch",
|
|
14
|
+
"build:weapp": "taro build --type weapp",
|
|
15
|
+
"build:swan": "taro build --type swan",
|
|
16
|
+
"build:alipay": "taro build --type alipay",
|
|
17
|
+
"build:tt": "taro build --type tt",
|
|
18
|
+
"build:h5": "taro build --type h5",
|
|
19
|
+
"build:rn": "taro build --type rn",
|
|
20
|
+
"build:qq": "taro build --type qq",
|
|
21
|
+
"build:jd": "taro build --type jd",
|
|
22
|
+
"build:quickapp": "taro build --type quickapp",
|
|
23
|
+
"lint": "biome check --write .",
|
|
24
|
+
"lint:check": "biome check .",
|
|
25
|
+
"lint:fix": "biome check --write --unsafe .",
|
|
26
|
+
"lint:format": "prettier --write 'src/**/*.{ts,tsx,js,jsx,json,css,scss,md}'",
|
|
27
|
+
"type-check": "tsc --noEmit",
|
|
28
|
+
"check:build": "node scripts/check-build.mjs",
|
|
29
|
+
"precommit": "bun run lint:check && bun run type-check",
|
|
30
|
+
"prepare": "husky || true",
|
|
31
|
+
"postinstall": "weapp-tw patch || true",
|
|
32
|
+
"patch": "weapp-tw patch"
|
|
33
|
+
},
|
|
34
|
+
"browserslist": [
|
|
35
|
+
"last 5 years",
|
|
36
|
+
"Android >= 9",
|
|
37
|
+
"iOS >= 13",
|
|
38
|
+
"not dead"
|
|
39
|
+
],
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@a2a-js/sdk": "^0.3.7",
|
|
42
|
+
"@amaster.ai/bpm-ui": "1.1.0-beta.57",
|
|
43
|
+
"@amaster.ai/client": "1.1.0-beta.57",
|
|
44
|
+
"@amaster.ai/taro-echarts-ui": "1.1.0-beta.57",
|
|
45
|
+
"@amaster.ai/vite-plugins": "1.1.0-beta.57",
|
|
46
|
+
"@babel/runtime": "^7.28.3",
|
|
47
|
+
"@tarojs/components": "4.1.5",
|
|
48
|
+
"@tarojs/helper": "4.1.5",
|
|
49
|
+
"@tarojs/plugin-framework-react": "4.1.5",
|
|
50
|
+
"@tarojs/plugin-generator": "4.1.5",
|
|
51
|
+
"@tarojs/plugin-html": "4.1.5",
|
|
52
|
+
"@tarojs/plugin-http": "4.1.5",
|
|
53
|
+
"@tarojs/plugin-platform-h5": "4.1.5",
|
|
54
|
+
"@tarojs/plugin-platform-weapp": "4.1.5",
|
|
55
|
+
"@tarojs/react": "4.1.5",
|
|
56
|
+
"@tarojs/runtime": "4.1.5",
|
|
57
|
+
"@tarojs/shared": "4.1.5",
|
|
58
|
+
"@tarojs/taro": "4.1.5",
|
|
59
|
+
"immer": "^10.1.1",
|
|
60
|
+
"markdown-it": "^14.1.1",
|
|
61
|
+
"react": "^18.3.1",
|
|
62
|
+
"react-dom": "^18.3.1",
|
|
63
|
+
"zustand": "^5.0.8"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@babel/core": "^7.28.3",
|
|
67
|
+
"@babel/preset-react": "^7.24.1",
|
|
68
|
+
"@biomejs/biome": "^2.2.3",
|
|
69
|
+
"@egoist/tailwindcss-icons": "^1.9.0",
|
|
70
|
+
"@iconify-json/lucide": "^1.2.64",
|
|
71
|
+
"@iconify-json/mdi": "^1.2.3",
|
|
72
|
+
"@tarojs/cli": "4.1.5",
|
|
73
|
+
"@tarojs/taro-loader": "4.1.5",
|
|
74
|
+
"@tarojs/vite-runner": "4.1.5",
|
|
75
|
+
"@types/node": "^24.3.0",
|
|
76
|
+
"@types/react": "^18.3.24",
|
|
77
|
+
"@typescript/native-preview": "7.0.0-dev.20250827.1",
|
|
78
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
79
|
+
"babel-preset-taro": "4.1.5",
|
|
80
|
+
"husky": "^9.1.7",
|
|
81
|
+
"postcss": "^8.5.6",
|
|
82
|
+
"prettier": "^3.8.1",
|
|
83
|
+
"react-refresh": "^0.14.0",
|
|
84
|
+
"tailwindcss": "^3.4.17",
|
|
85
|
+
"ts-node": "^10.9.2",
|
|
86
|
+
"typescript": "^5.9.2",
|
|
87
|
+
"vite": "^4.5.14",
|
|
88
|
+
"weapp-tailwindcss": "^4.2.6"
|
|
89
|
+
},
|
|
90
|
+
"optionalDependencies": {
|
|
91
|
+
"@tarojs/binding-linux-arm64-gnu": "^4.1.10",
|
|
92
|
+
"@tarojs/binding-linux-x64-gnu": "4.1.5"
|
|
93
|
+
}
|
|
94
|
+
}
|