@yuku123/z-agent-frontend-component 0.1.1

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.
Files changed (41) hide show
  1. package/README.md +66 -0
  2. package/dist/z-agent-frontend-component.css +1 -0
  3. package/dist/z-agent-frontend-component.es.js +9956 -0
  4. package/dist/z-agent-frontend-component.umd.js +219 -0
  5. package/package.json +77 -0
  6. package/src/api/apiRouter.js +78 -0
  7. package/src/api/index.js +23 -0
  8. package/src/api/request.js +59 -0
  9. package/src/api/routes.js +140 -0
  10. package/src/dev.jsx +80 -0
  11. package/src/index.js +86 -0
  12. package/src/pages/agent/app/index.jsx +2 -0
  13. package/src/pages/agent/editor/AgentAppEditor.jsx +456 -0
  14. package/src/pages/agent/editor/WorkflowEditor.jsx +495 -0
  15. package/src/pages/agent/editor/nodes/index.ts +225 -0
  16. package/src/pages/agent/index.jsx +1379 -0
  17. package/src/pages/agent/share.jsx +512 -0
  18. package/src/pages/ak/AkUsageDrawer.jsx +208 -0
  19. package/src/pages/ak/index.jsx +496 -0
  20. package/src/pages/llm/index.jsx +736 -0
  21. package/src/pages/llm/model/index.jsx +220 -0
  22. package/src/pages/llm/provider/index.jsx +173 -0
  23. package/src/pages/mcp/index.jsx +359 -0
  24. package/src/pages/oss/BucketList.jsx +320 -0
  25. package/src/pages/oss/ObjectBrowser.jsx +409 -0
  26. package/src/pages/product/execute.jsx +608 -0
  27. package/src/pages/product/index.jsx +628 -0
  28. package/src/pages/product/scene.jsx +746 -0
  29. package/src/pages/script/ApiBridgeEditor.jsx +255 -0
  30. package/src/pages/script/CurlImportModal.jsx +263 -0
  31. package/src/pages/script/FieldMappingEditor.jsx +131 -0
  32. package/src/pages/script/OpenApiImportModal.jsx +212 -0
  33. package/src/pages/script/index.jsx +532 -0
  34. package/src/pages/skill/index.jsx +1595 -0
  35. package/src/pages/trace/DebugPlayground.jsx +357 -0
  36. package/src/pages/trace/components/MetricsDashboard.jsx +164 -0
  37. package/src/pages/trace/components/RagFragments.jsx +134 -0
  38. package/src/pages/trace/components/Timeline.jsx +142 -0
  39. package/src/pages/trace/components/ToolCallTree.jsx +116 -0
  40. package/src/pages/trace/index.jsx +13 -0
  41. package/src/pages/usage/index.jsx +352 -0
@@ -0,0 +1,512 @@
1
+ import {useCallback, useEffect, useRef, useState} from 'react'
2
+ import {useParams} from 'react-router-dom'
3
+ import {Avatar, Button, Card, Input, List, Modal, Spin, Tag, Tooltip} from 'antd'
4
+ import {
5
+ CheckCircleFilled,
6
+ ClearOutlined,
7
+ ClockCircleOutlined,
8
+ CopyOutlined,
9
+ ExclamationCircleFilled,
10
+ LoadingOutlined,
11
+ RobotOutlined,
12
+ SendOutlined,
13
+ UserOutlined
14
+ } from '@ant-design/icons'
15
+ import {agentApi} from '../../api'
16
+ import ReactMarkdown from 'react-markdown'
17
+ import remarkGfm from 'remark-gfm'
18
+
19
+ const {TextArea} = Input
20
+
21
+ // 简单的代码块样式组件
22
+ const CodeBlock = ({children, className}) => {
23
+ const code = String(children).replace(/\n$/, '')
24
+ const [copied, setCopied] = useState(false)
25
+
26
+ const handleCopy = () => {
27
+ navigator.clipboard.writeText(code).then(() => {
28
+ setCopied(true)
29
+ setTimeout(() => setCopied(false), 2000)
30
+ })
31
+ }
32
+
33
+ return (
34
+ <div style={{position: 'relative', margin: '8px 0'}}>
35
+ <Button
36
+ size="small"
37
+ icon={copied ? <CheckCircleFilled/> : <CopyOutlined/>}
38
+ onClick={handleCopy}
39
+ style={{position: 'absolute', right: 8, top: 8, opacity: 0.7}}
40
+ >
41
+ {copied ? '已复制' : '复制'}
42
+ </Button>
43
+ <pre style={{
44
+ background: '#1e1e1e',
45
+ color: '#d4d4d4',
46
+ borderRadius: 6,
47
+ padding: '12px 16px',
48
+ overflow: 'auto',
49
+ fontSize: 13,
50
+ lineHeight: 1.5,
51
+ fontFamily: 'Consolas, Monaco, "Courier New", monospace',
52
+ }}>
53
+ <code className={className}>{code}</code>
54
+ </pre>
55
+ </div>
56
+ )
57
+ }
58
+
59
+ // 消息气泡
60
+ const MessageBubble = ({msg}) => {
61
+ const isUser = msg.role === 'user'
62
+ const isError = msg.status === 'FAILED'
63
+
64
+ return (
65
+ <div style={{
66
+ display: 'flex',
67
+ justifyContent: isUser ? 'flex-end' : 'flex-start',
68
+ marginBottom: 16,
69
+ animation: 'fadeIn 0.3s ease',
70
+ }}>
71
+ {!isUser && (
72
+ <Avatar icon={<RobotOutlined/>} size={36}
73
+ style={{marginRight: 10, background: '#1890ff', flexShrink: 0}}/>
74
+ )}
75
+ <div style={{
76
+ maxWidth: '72%',
77
+ display: 'flex',
78
+ flexDirection: 'column',
79
+ alignItems: isUser ? 'flex-end' : 'flex-start',
80
+ }}>
81
+ {isError && (
82
+ <Tag color="red" icon={<ExclamationCircleFilled/>} style={{marginBottom: 4}}>
83
+ 出错了
84
+ </Tag>
85
+ )}
86
+ {isUser ? (
87
+ <div style={{
88
+ padding: '10px 14px',
89
+ borderRadius: '16px 16px 4px 16px',
90
+ background: 'linear-gradient(135deg, #1890ff, #096dd9)',
91
+ color: '#fff',
92
+ lineHeight: 1.6,
93
+ fontSize: 15,
94
+ boxShadow: '0 2px 8px rgba(24,144,255,0.3)',
95
+ }}>
96
+ {msg.content}
97
+ </div>
98
+ ) : (
99
+ <div style={{
100
+ padding: '12px 16px',
101
+ borderRadius: '16px 16px 16px 4px',
102
+ background: '#fff',
103
+ color: '#333',
104
+ lineHeight: 1.7,
105
+ fontSize: 15,
106
+ boxShadow: '0 1px 4px rgba(0,0,0,0.1)',
107
+ border: '1px solid #f0f0f0',
108
+ }}>
109
+ <ReactMarkdown
110
+ remarkPlugins={[remarkGfm]}
111
+ components={{
112
+ code({node, inline, className, children, ...props}) {
113
+ if (inline) {
114
+ return <code style={{
115
+ background: '#f5f5f5',
116
+ padding: '2px 6px',
117
+ borderRadius: 4,
118
+ fontFamily: 'monospace',
119
+ fontSize: '0.9em',
120
+ color: '#c7254e'
121
+ }} {...props}>{children}</code>
122
+ }
123
+ return <CodeBlock className={className}>{children}</CodeBlock>
124
+ },
125
+ p({children}) {
126
+ return <p style={{margin: '0 0 8px 0'}}>{children}</p>
127
+ },
128
+ ul({children}) {
129
+ return <ul style={{margin: '4px 0', paddingLeft: 20}}>{children}</ul>
130
+ },
131
+ ol({children}) {
132
+ return <ol style={{margin: '4px 0', paddingLeft: 20}}>{children}</ol>
133
+ },
134
+ li({children}) {
135
+ return <li style={{marginBottom: 2}}>{children}</li>
136
+ },
137
+ strong({children}) {
138
+ return <strong style={{color: '#1890ff'}}>{children}</strong>
139
+ },
140
+ h1({children}) {
141
+ return <h1 style={{fontSize: 18, margin: '8px 0'}}>{children}</h1>
142
+ },
143
+ h2({children}) {
144
+ return <h2 style={{fontSize: 16, margin: '8px 0'}}>{children}</h2>
145
+ },
146
+ h3({children}) {
147
+ return <h3 style={{fontSize: 14, margin: '6px 0'}}>{children}</h3>
148
+ },
149
+ blockquote({children}) {
150
+ return <blockquote style={{
151
+ borderLeft: '3px solid #1890ff',
152
+ margin: '8px 0',
153
+ paddingLeft: 12,
154
+ color: '#666'
155
+ }}>{children}</blockquote>
156
+ },
157
+ }}
158
+ >
159
+ {msg.content}
160
+ </ReactMarkdown>
161
+ </div>
162
+ )}
163
+ {msg.latencyMs && !isUser && !isError && (
164
+ <span style={{fontSize: 11, color: '#bbb', marginTop: 4}}>
165
+ <ClockCircleOutlined style={{marginRight: 3}}/>
166
+ {msg.latencyMs}ms
167
+ </span>
168
+ )}
169
+ </div>
170
+ {isUser && (
171
+ <Avatar icon={<UserOutlined/>} size={36}
172
+ style={{marginLeft: 10, background: '#52c41a', flexShrink: 0}}/>
173
+ )}
174
+ </div>
175
+ )
176
+ }
177
+
178
+ // 欢迎页
179
+ const WelcomeScreen = ({appName, appDesc}) => (
180
+ <div style={{
181
+ display: 'flex',
182
+ flexDirection: 'column',
183
+ alignItems: 'center',
184
+ justifyContent: 'center',
185
+ height: '60vh',
186
+ color: '#666'
187
+ }}>
188
+ <div style={{
189
+ fontSize: 48,
190
+ marginBottom: 16,
191
+ background: 'linear-gradient(135deg, #1890ff, #52c41a)',
192
+ WebkitBackgroundClip: 'text',
193
+ WebkitTextFillColor: 'transparent',
194
+ fontWeight: 'bold'
195
+ }}>
196
+ {appName || 'Agent'}
197
+ </div>
198
+ {appDesc && <p style={{color: '#999', marginBottom: 24}}>{appDesc}</p>}
199
+ <div style={{
200
+ display: 'flex',
201
+ gap: 12,
202
+ flexWrap: 'wrap',
203
+ justifyContent: 'center',
204
+ maxWidth: 500
205
+ }}>
206
+ {['有什么可以帮助你的?', '告诉我你的问题', '试试问我任何事情'].map((tip, i) => (
207
+ <Tag key={i} style={{padding: '6px 12px', fontSize: 13, cursor: 'pointer', borderRadius: 16}}
208
+ onClick={() => {
209
+ const event = new CustomEvent('insertTip', {detail: tip})
210
+ window.dispatchEvent(event)
211
+ }}>
212
+ {tip}
213
+ </Tag>
214
+ ))}
215
+ </div>
216
+ </div>
217
+ )
218
+
219
+ const AgentSharePage = () => {
220
+ const {shareCode} = useParams()
221
+ const [loading, setLoading] = useState(true)
222
+ const [instanceCode, setInstanceCode] = useState('')
223
+ const [appName, setAppName] = useState('')
224
+ const [appDesc, setAppDesc] = useState('')
225
+ const [messages, setMessages] = useState([])
226
+ const [inputValue, setInputValue] = useState('')
227
+ const [sending, setSending] = useState(false)
228
+ const [connected, setConnected] = useState(false)
229
+ const [errorMsg, setErrorMsg] = useState('')
230
+ const [insufficientInfo, setInsufficientInfo] = useState(false)
231
+ const bottomRef = useRef(null)
232
+ const textareaRef = useRef(null)
233
+ const insertTipEventRef = useRef(null)
234
+
235
+ useEffect(() => {
236
+ verifyAndLoad()
237
+ // 监听快速插入提示
238
+ const handler = (e) => setInputValue(prev => prev ? prev : e.detail)
239
+ window.addEventListener('insertTip', handler)
240
+ return () => window.removeEventListener('insertTip', handler)
241
+ }, [shareCode])
242
+
243
+ useEffect(() => {
244
+ bottomRef.current?.scrollIntoView({behavior: 'smooth'})
245
+ }, [messages])
246
+
247
+ const verifyAndLoad = async () => {
248
+ setLoading(true)
249
+ setErrorMsg('')
250
+ try {
251
+ const res = await agentApi.shareVerify(shareCode)
252
+ if (res) {
253
+ setInstanceCode(res.instanceCode)
254
+ setAppName(res.appName || 'Agent')
255
+ setAppDesc(res.appDesc || '')
256
+ setConnected(true)
257
+ // 加载历史消息
258
+ try {
259
+ const historyRes = await agentApi.chatHistory(res.instanceCode, 50)
260
+ if (historyRes.data && historyRes.data.length > 0) {
261
+ const historyMsgs = []
262
+ historyRes.data.forEach(h => {
263
+ historyMsgs.push({
264
+ id: `u_${h.id}`,
265
+ role: 'user',
266
+ content: h.userMessage,
267
+ time: h.gmtCreate,
268
+ status: 'SUCCESS',
269
+ })
270
+ historyMsgs.push({
271
+ id: `a_${h.id}`,
272
+ role: 'assistant',
273
+ content: h.assistantMessage,
274
+ time: h.gmtCreate,
275
+ status: h.status,
276
+ latencyMs: h.latencyMs,
277
+ })
278
+ })
279
+ setMessages(historyMsgs)
280
+ }
281
+ } catch (e) {
282
+ console.warn('加载历史失败', e)
283
+ }
284
+ }
285
+ } catch (e) {
286
+ setErrorMsg('分享链接无效或已过期')
287
+ setInsufficientInfo(true)
288
+ }
289
+ setLoading(false)
290
+ }
291
+
292
+ const handleSend = useCallback(async () => {
293
+ const content = inputValue.trim()
294
+ if (!content || sending) return
295
+
296
+ const userMsgId = `u_${Date.now()}`
297
+ const userMsg = {id: userMsgId, role: 'user', content, status: 'SUCCESS'}
298
+ setMessages(prev => [...prev, userMsg])
299
+ setInputValue('')
300
+ setSending(true)
301
+
302
+ // 立即添加一条助手占位
303
+ const assistantMsgId = `a_${Date.now()}`
304
+ let assistantContent = ''
305
+ setMessages(prev => [...prev, {
306
+ id: assistantMsgId,
307
+ role: 'assistant',
308
+ content: '',
309
+ status: 'LOADING',
310
+ }])
311
+
312
+ try {
313
+ // 优先使用流式接口
314
+ const token = localStorage.getItem('token')
315
+ const eventSource = agentApi.chatStream(instanceCode, content, 'visitor', '游客', token)
316
+
317
+ eventSource.onmessage = (e) => {
318
+ if (e.data === '[DONE]') {
319
+ eventSource.close()
320
+ setMessages(prev => prev.map(m =>
321
+ m.id === assistantMsgId
322
+ ? {...m, content: assistantContent || '好的,我明白了。', status: 'SUCCESS'}
323
+ : m
324
+ ))
325
+ setSending(false)
326
+ } else {
327
+ assistantContent += e.data
328
+ setMessages(prev => prev.map(m =>
329
+ m.id === assistantMsgId
330
+ ? {...m, content: assistantContent, status: 'LOADING'}
331
+ : m
332
+ ))
333
+ }
334
+ }
335
+
336
+ eventSource.onerror = (e) => {
337
+ eventSource.close()
338
+ setMessages(prev => prev.map(m =>
339
+ m.id === assistantMsgId
340
+ ? {...m, content: '抱歉,AI 服务暂时不可用,请稍后重试。', status: 'FAILED'}
341
+ : m
342
+ ))
343
+ setSending(false)
344
+ }
345
+ } catch (e) {
346
+ setMessages(prev => prev.map(m =>
347
+ m.id === assistantMsgId
348
+ ? {...m, content: '抱歉,AI 服务暂时不可用,请稍后重试。', status: 'FAILED'}
349
+ : m
350
+ ))
351
+ setSending(false)
352
+ }
353
+ textareaRef.current?.focus()
354
+ }, [inputValue, sending, instanceCode])
355
+
356
+ const handleClear = () => {
357
+ Modal.confirm({
358
+ title: '清空对话',
359
+ content: '确定清空当前对话记录?此操作不可恢复。',
360
+ okText: '清空',
361
+ okButtonProps: {danger: true},
362
+ onOk: () => {
363
+ setMessages([])
364
+ agentApi.chatClear(instanceCode).catch(() => {
365
+ })
366
+ }
367
+ })
368
+ }
369
+
370
+ const handleKeyDown = (e) => {
371
+ if (e.key === 'Enter' && !e.shiftKey) {
372
+ e.preventDefault()
373
+ handleSend()
374
+ }
375
+ }
376
+
377
+ if (loading) {
378
+ return (
379
+ <div style={{
380
+ display: 'flex',
381
+ justifyContent: 'center',
382
+ alignItems: 'center',
383
+ height: '100vh',
384
+ background: '#f0f2f5'
385
+ }}>
386
+ <Spin indicator={<LoadingOutlined style={{fontSize: 32}} spin/>} tip="正在连接..."/>
387
+ </div>
388
+ )
389
+ }
390
+
391
+ if (insufficientInfo || errorMsg) {
392
+ return (
393
+ <div style={{
394
+ display: 'flex',
395
+ justifyContent: 'center',
396
+ alignItems: 'center',
397
+ height: '100vh',
398
+ background: '#f0f2f5'
399
+ }}>
400
+ <Card style={{textAlign: 'center', maxWidth: 400}}>
401
+ <ExclamationCircleFilled style={{fontSize: 48, color: '#ff4d4f', marginBottom: 16}}/>
402
+ <h3>{errorMsg || '无法访问此应用'}</h3>
403
+ <p style={{color: '#999'}}>可能的原因:链接已失效、应用已被删除或访问权限不足。</p>
404
+ <Button type="primary" href="/" icon={<RobotOutlined/>}>返回首页</Button>
405
+ </Card>
406
+ </div>
407
+ )
408
+ }
409
+
410
+ return (
411
+ <div style={{
412
+ height: '100vh',
413
+ display: 'flex',
414
+ flexDirection: 'column',
415
+ background: 'linear-gradient(180deg, #f0f2f5 0%, #fff 100%)'
416
+ }}>
417
+ {/* Header */}
418
+ <div style={{
419
+ borderBottom: '1px solid #e8e8e8',
420
+ background: '#fff',
421
+ padding: '12px 24px',
422
+ display: 'flex',
423
+ alignItems: 'center',
424
+ justifyContent: 'space-between',
425
+ flexShrink: 0
426
+ }}>
427
+ <div style={{display: 'flex', alignItems: 'center', gap: 10}}>
428
+ <Avatar shape="square" size={36} icon={<RobotOutlined/>} style={{background: '#1890ff'}}/>
429
+ <div>
430
+ <div style={{fontWeight: 600, fontSize: 16}}>{appName}</div>
431
+ {appDesc && <div style={{fontSize: 12, color: '#999'}}>{appDesc}</div>}
432
+ </div>
433
+ </div>
434
+ <div style={{display: 'flex', alignItems: 'center', gap: 8}}>
435
+ <span style={{fontSize: 12, color: connected ? '#52c41a' : '#ff4d4f'}}>
436
+ <CheckCircleFilled style={{marginRight: 4}}/>
437
+ {connected ? '已连接' : '未连接'}
438
+ </span>
439
+ <Tooltip title="清空对话">
440
+ <Button size="small" icon={<ClearOutlined/>} onClick={handleClear}
441
+ disabled={messages.length === 0}>
442
+ 清空
443
+ </Button>
444
+ </Tooltip>
445
+ </div>
446
+ </div>
447
+
448
+ {/* Chat Area */}
449
+ <div style={{flex: 1, overflowY: 'auto', padding: '16px 24px'}}>
450
+ {messages.length === 0 ? (
451
+ <WelcomeScreen appName={appName} appDesc={appDesc}/>
452
+ ) : (
453
+ <>
454
+ <List
455
+ dataSource={messages}
456
+ renderItem={(item) => <MessageBubble msg={item}/>}
457
+ locale={{emptyText: ''}}
458
+ />
459
+ </>
460
+ )}
461
+ <div ref={bottomRef}/>
462
+ </div>
463
+
464
+ {/* Quick suggestions */}
465
+ {messages.length === 0 && (
466
+ <div style={{padding: '0 24px 8px', display: 'flex', gap: 8, flexWrap: 'wrap'}}>
467
+ {['你好', '你能做什么?', '介绍下自己'].map((t, i) => (
468
+ <Button key={i} size="small" type="default" onClick={() => setInputValue(t)}>{t}</Button>
469
+ ))}
470
+ </div>
471
+ )}
472
+
473
+ {/* Input Area */}
474
+ <div style={{
475
+ borderTop: '1px solid #e8e8e8',
476
+ background: '#fff',
477
+ padding: '12px 24px 20px',
478
+ flexShrink: 0
479
+ }}>
480
+ <div style={{display: 'flex', gap: 12, alignItems: 'flex-end'}}>
481
+ <TextArea
482
+ ref={textareaRef}
483
+ value={inputValue}
484
+ onChange={(e) => setInputValue(e.target.value)}
485
+ onKeyDown={handleKeyDown}
486
+ placeholder="输入消息,Enter 发送,Shift+Enter 换行"
487
+ autoSize={{minRows: 1, maxRows: 4}}
488
+ style={{flex: 1, borderRadius: 20}}
489
+ disabled={sending || !connected}
490
+ />
491
+ <Button
492
+ type="primary"
493
+ icon={sending ? <LoadingOutlined/> : <SendOutlined/>}
494
+ onClick={handleSend}
495
+ loading={sending}
496
+ disabled={!inputValue.trim() || !connected}
497
+ style={{borderRadius: 20, width: 56, height: 36}}
498
+ />
499
+ </div>
500
+ <div style={{fontSize: 11, color: '#ccc', marginTop: 6, textAlign: 'center'}}>
501
+ AI 助手可能会产生不准确的信息,请仔细甄别
502
+ </div>
503
+ </div>
504
+
505
+ <style>{`
506
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
507
+ `}</style>
508
+ </div>
509
+ )
510
+ }
511
+
512
+ export default AgentSharePage