@mybricks/plugin-ai 0.0.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.
- package/package.json +27 -0
- package/src/agents/app.ts +173 -0
- package/src/agents/common.ts +111 -0
- package/src/agents/index.ts +7 -0
- package/src/components/icons/index.less +8 -0
- package/src/components/icons/index.tsx +24 -0
- package/src/components/messages/index.less +806 -0
- package/src/components/messages/index.tsx +236 -0
- package/src/context/index.ts +21 -0
- package/src/data.ts +5 -0
- package/src/index.tsx +84 -0
- package/src/mock.ts +1267 -0
- package/src/startView/index.less +216 -0
- package/src/startView/index.tsx +229 -0
- package/src/tools/analyze-and-expand-prd.ts +166 -0
- package/src/tools/answer-user.ts +35 -0
- package/src/tools/focus-element.ts +47 -0
- package/src/tools/generate-page.ts +750 -0
- package/src/tools/get-component-info-by-ids.ts +166 -0
- package/src/tools/get-component-info.ts +53 -0
- package/src/tools/get-components-doc-and-prd.ts +137 -0
- package/src/tools/get-focus-mybricks-dsl.ts +26 -0
- package/src/tools/get-mybricks-dsl.ts +73 -0
- package/src/tools/index.ts +25 -0
- package/src/tools/modify-component.ts +385 -0
- package/src/tools/type.d.ts +12 -0
- package/src/tools/utils.ts +62 -0
- package/src/types.d.ts +65 -0
- package/src/view/components/header/header.less +17 -0
- package/src/view/components/header/header.tsx +15 -0
- package/src/view/components/index.ts +3 -0
- package/src/view/components/messages/messages.less +228 -0
- package/src/view/components/messages/messages.tsx +172 -0
- package/src/view/components/sender/sender.less +44 -0
- package/src/view/components/sender/sender.tsx +62 -0
- package/src/view/index.less +5 -0
- package/src/view/index.tsx +18 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import React, { useRef, useState, useEffect, useLayoutEffect } from "react"
|
|
2
|
+
import classNames from "classnames"
|
|
3
|
+
import { Rxai } from "@mybricks/rxai"
|
|
4
|
+
import { marked } from "marked";
|
|
5
|
+
import { Loading, Success, Close } from "../icons";
|
|
6
|
+
import css from "./index.less"
|
|
7
|
+
|
|
8
|
+
const renderer = new marked.Renderer();
|
|
9
|
+
renderer.paragraph = (paragraph) => {
|
|
10
|
+
return `<p>${paragraph.text}</p>`;
|
|
11
|
+
};
|
|
12
|
+
marked.use({ renderer });
|
|
13
|
+
|
|
14
|
+
interface User {
|
|
15
|
+
/** 名称 */
|
|
16
|
+
name: string;
|
|
17
|
+
/** 头像地址 */
|
|
18
|
+
avatar: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface MessagesParams {
|
|
22
|
+
/** 用户信息 */
|
|
23
|
+
user: User;
|
|
24
|
+
/** ai助手信息 */
|
|
25
|
+
copilot: User;
|
|
26
|
+
|
|
27
|
+
rxai: Rxai;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type Plans = Rxai['cacheMessages'];
|
|
31
|
+
|
|
32
|
+
const Messages = (params: MessagesParams) => {
|
|
33
|
+
const { user, rxai, copilot } = params;
|
|
34
|
+
|
|
35
|
+
const destroysRef = useRef<(() => void)[]>([]);
|
|
36
|
+
const [plans, setPlans] = useState<Plans>([]);
|
|
37
|
+
|
|
38
|
+
useLayoutEffect(() => {
|
|
39
|
+
destroysRef.current.push(rxai.events.on('plan', (plans) => {
|
|
40
|
+
setPlans([...plans])
|
|
41
|
+
}, true))
|
|
42
|
+
}, [])
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
return () => {
|
|
46
|
+
for (const destroy of destroysRef.current) {
|
|
47
|
+
destroy()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}, [])
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<main className={css['ai-chat-messages']}>
|
|
54
|
+
{plans.map((plan, index) => {
|
|
55
|
+
return <Bubble key={index} user={user} plan={plan} copilot={copilot} />
|
|
56
|
+
})}
|
|
57
|
+
<div className={css['anchor']} />
|
|
58
|
+
</main>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type Plan = Rxai['cacheMessages'][number];
|
|
63
|
+
|
|
64
|
+
type UserFriendlyMessages = Plan['userFriendlyMessages'];
|
|
65
|
+
|
|
66
|
+
interface BubbleParams {
|
|
67
|
+
user: User;
|
|
68
|
+
copilot: User;
|
|
69
|
+
plan: Plan;
|
|
70
|
+
}
|
|
71
|
+
const Bubble = (params: BubbleParams) => {
|
|
72
|
+
const { user, plan, copilot } = params;
|
|
73
|
+
|
|
74
|
+
const [messages, setMessages] = useState<UserFriendlyMessages>([])
|
|
75
|
+
const [message, setMessage] = useState("")
|
|
76
|
+
const [loading, setLoading] = useState(true)
|
|
77
|
+
|
|
78
|
+
const destroysRef = useRef<(() => void)[]>([]);
|
|
79
|
+
|
|
80
|
+
useLayoutEffect(() => {
|
|
81
|
+
destroysRef.current.push(
|
|
82
|
+
plan.events.on('loading', (loading: boolean) => {
|
|
83
|
+
setLoading(loading);
|
|
84
|
+
}, true),
|
|
85
|
+
plan.events.on('messageStream', (messageStream: string) => {
|
|
86
|
+
setMessage((pre) => {
|
|
87
|
+
return pre + messageStream;
|
|
88
|
+
})
|
|
89
|
+
}, true),
|
|
90
|
+
plan.events.on('userFriendlyMessages', (messages: UserFriendlyMessages) => {
|
|
91
|
+
setMessages([...messages])
|
|
92
|
+
setMessage("")
|
|
93
|
+
}, true),
|
|
94
|
+
)
|
|
95
|
+
}, [])
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
return () => {
|
|
99
|
+
for (const destroy of destroysRef.current) {
|
|
100
|
+
destroy()
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}, [])
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<>
|
|
107
|
+
{messages[0] && <BubbleUser user={user} message={messages[0]} />}
|
|
108
|
+
{messages[0] && <BubbleCopilot messages={messages.slice(1)} copilot={copilot} message={message} loading={loading} />}
|
|
109
|
+
</>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface BubbleMessageParams {
|
|
114
|
+
message: string;
|
|
115
|
+
}
|
|
116
|
+
const BubbleMessage = (params: BubbleMessageParams) => {
|
|
117
|
+
const { message } = params;
|
|
118
|
+
const messageRef = useRef<HTMLDivElement>(null);
|
|
119
|
+
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
messageRef.current!.innerHTML = marked.parse(message) as string;
|
|
122
|
+
}, [message])
|
|
123
|
+
|
|
124
|
+
return <span ref={messageRef} />
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
interface BubbleUserParams {
|
|
128
|
+
user: User;
|
|
129
|
+
message: UserFriendlyMessages[number]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const BubbleUser = (params: BubbleUserParams) => {
|
|
133
|
+
const { user, message } = params;
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<article className={css['chat-bubble']}>
|
|
137
|
+
<header className={css['chat-bubble-header']}>
|
|
138
|
+
<span className={css['chat-bubble-header-avatar']}>
|
|
139
|
+
<img className={css['user-avatar']} src={user.avatar} />
|
|
140
|
+
</span>
|
|
141
|
+
<span className={css['chat-bubble-header-name']}>{user.name}</span>
|
|
142
|
+
</header>
|
|
143
|
+
<section className={classNames(css['chat-message-container'], css['user-message'])}>
|
|
144
|
+
<div className={css['ai-chat-markdown']}>
|
|
145
|
+
<span>{typeof message.content === "string" ? message.content : message.content.find((content: any) => {
|
|
146
|
+
if (content.type === "text") {
|
|
147
|
+
return content
|
|
148
|
+
}
|
|
149
|
+
}).text}</span>
|
|
150
|
+
</div>
|
|
151
|
+
</section>
|
|
152
|
+
</article>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface BubbleCopilotParams {
|
|
157
|
+
copilot: User;
|
|
158
|
+
messages: UserFriendlyMessages;
|
|
159
|
+
message: string;
|
|
160
|
+
loading: boolean;
|
|
161
|
+
}
|
|
162
|
+
const BubbleCopilot = (params: BubbleCopilotParams) => {
|
|
163
|
+
const { messages, copilot, message, loading } = params;
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<article className={css['chat-bubble']}>
|
|
167
|
+
<header className={css['chat-bubble-header']}>
|
|
168
|
+
<span className={css['chat-bubble-header-avatar']}>
|
|
169
|
+
<img className={css['copilot-avatar']} src={copilot.avatar} />
|
|
170
|
+
</span>
|
|
171
|
+
<span className={css['chat-bubble-header-name']}>{copilot.name}</span>
|
|
172
|
+
</header>
|
|
173
|
+
<section className={classNames(css['chat-message-container'], css['ai-message'])}>
|
|
174
|
+
<div className={css['ai-chat-markdown']}>
|
|
175
|
+
{messages.map((message, index) => {
|
|
176
|
+
if (message.role === "tool") {
|
|
177
|
+
return <BubbleCopilotTool key={index + message.status} message={message} />
|
|
178
|
+
}
|
|
179
|
+
return <BubbleMessage message={message.content} />
|
|
180
|
+
})}
|
|
181
|
+
{message && <BubbleMessage message={message} />}
|
|
182
|
+
{!message && loading && (
|
|
183
|
+
<div className={css['think']}>
|
|
184
|
+
<span>正在思考</span>
|
|
185
|
+
<Loading />
|
|
186
|
+
</div>
|
|
187
|
+
)}
|
|
188
|
+
</div>
|
|
189
|
+
</section>
|
|
190
|
+
</article>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
interface BubbleCopilotToolParams {
|
|
195
|
+
message: {
|
|
196
|
+
type: "tool";
|
|
197
|
+
status: "pending" | "success" | "error" | "aborted";
|
|
198
|
+
content: {
|
|
199
|
+
name: string;
|
|
200
|
+
displayName: string;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const BubbleCopilotTool = (params: BubbleCopilotToolParams) => {
|
|
205
|
+
const { message } = params;
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<div className={classNames(css['ai-chat-collapsible-code-block'], css['collapsed'])}>
|
|
209
|
+
<span className={classNames(css['code-header'], css['collapsed'])}>
|
|
210
|
+
<span className={classNames(css['code-title'], css['collapsed'])}>{message.content.displayName || message.content.name}</span>
|
|
211
|
+
{message.status === "pending" && (
|
|
212
|
+
<span className={classNames(css['code-title-status'], css['collapsed'], css['pending'])}>
|
|
213
|
+
<Loading />
|
|
214
|
+
</span>
|
|
215
|
+
)}
|
|
216
|
+
{message.status === "success" && (
|
|
217
|
+
<span className={classNames(css['code-title-status'], css['collapsed'], css['success'])}>
|
|
218
|
+
<Success />
|
|
219
|
+
</span>
|
|
220
|
+
)}
|
|
221
|
+
{message.status === "error" && (
|
|
222
|
+
<span className={classNames(css['code-title-status'], css['collapsed'], css['error'])}>
|
|
223
|
+
<Close />
|
|
224
|
+
</span>
|
|
225
|
+
)}
|
|
226
|
+
{message.status === "aborted" && (
|
|
227
|
+
<span className={classNames(css['code-title-status'], css['collapsed'], css['aborted'])}>
|
|
228
|
+
取消
|
|
229
|
+
</span>
|
|
230
|
+
)}
|
|
231
|
+
</span>
|
|
232
|
+
</div>
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export { Messages }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Rxai } from "@mybricks/rxai"
|
|
2
|
+
|
|
3
|
+
class Context {
|
|
4
|
+
rxai!: Rxai
|
|
5
|
+
currentFocus?: AiServiceFocusParams;
|
|
6
|
+
api!: AiServiceAPI;
|
|
7
|
+
|
|
8
|
+
/** 应用传入的系统提示词 */
|
|
9
|
+
prompts?: any;
|
|
10
|
+
|
|
11
|
+
createRxai(options: ConstructorParameters<typeof Rxai>[0]) {
|
|
12
|
+
if (!this.rxai) {
|
|
13
|
+
this.rxai = new Rxai(options)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const context = new Context();
|
|
19
|
+
|
|
20
|
+
export { context };
|
|
21
|
+
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Rxai } from "@mybricks/rxai";
|
|
3
|
+
import data from './data';
|
|
4
|
+
|
|
5
|
+
import pkg from '../package.json';
|
|
6
|
+
|
|
7
|
+
console.log(`%c ${pkg.name} %c@${pkg.version}`, `color:#FFF;background:#fa6400`, ``, ``);
|
|
8
|
+
|
|
9
|
+
import { Agents } from './agents'
|
|
10
|
+
import { View } from "./view";
|
|
11
|
+
import { context } from './context';
|
|
12
|
+
import { StartView } from "./startView";
|
|
13
|
+
|
|
14
|
+
export { fileFormat } from '@mybricks/rxai'
|
|
15
|
+
|
|
16
|
+
export default function pluginAI({
|
|
17
|
+
user = {
|
|
18
|
+
name: "user",
|
|
19
|
+
avatar: "/default_avatar.png"
|
|
20
|
+
},
|
|
21
|
+
requestAsStream,
|
|
22
|
+
prompts
|
|
23
|
+
}: any): any {
|
|
24
|
+
|
|
25
|
+
const copilot = {
|
|
26
|
+
name: "MyBricks.ai",
|
|
27
|
+
avatar: "https://my.mybricks.world/image/icon.png"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
context.prompts = prompts
|
|
31
|
+
|
|
32
|
+
// window.requestGenerateCanvasAgent = requestGenerateCanvasAgent
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
name: '@mybricks/plugins/ai',
|
|
36
|
+
title: 'MyBricksAI助手',
|
|
37
|
+
author: 'MyBricks',
|
|
38
|
+
['author.zh']: 'MyBricks',
|
|
39
|
+
version: '1.0.0',
|
|
40
|
+
description: 'ai for MyBricks',
|
|
41
|
+
data,
|
|
42
|
+
contributes: {
|
|
43
|
+
aiService: {
|
|
44
|
+
init(api: AiServiceAPI) {
|
|
45
|
+
context.api = api;
|
|
46
|
+
context.createRxai({
|
|
47
|
+
request: {
|
|
48
|
+
requestAsStream
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
console.log("[init - API]", api)
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
focus(params: AiServiceFocusParams) {
|
|
56
|
+
if (!params) {
|
|
57
|
+
context.currentFocus = undefined;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
context.currentFocus = params;
|
|
61
|
+
},
|
|
62
|
+
request(params: AiServiceRequestParams) {
|
|
63
|
+
if (params.attachments?.length) {
|
|
64
|
+
params.message = "参考附件图片完成页面搭建\n" + params.message
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Agents.requestCommonAgent(params)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
aiView: {
|
|
73
|
+
render() {
|
|
74
|
+
return <View user={user} copilot={copilot}/>
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
aiStartView: {
|
|
78
|
+
render() {
|
|
79
|
+
return <StartView user={user} copilot={copilot}/>
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|