@mybricks/plugin-ai 0.0.1 → 0.0.2
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 +7 -2
- package/src/agents/app.ts +188 -60
- package/src/agents/common.ts +428 -68
- package/src/agents/custom.ts +14 -0
- package/src/agents/index.ts +31 -1
- package/src/agents/knowledges/README.md +614 -0
- package/src/agents/knowledges/SUMMARY.md +527 -0
- package/src/agents/knowledges/index.ts +8 -0
- package/src/agents/knowledges/knowledge-base.ts +565 -0
- package/src/agents/knowledges/knowledge-node.ts +266 -0
- package/src/agents/knowledges/types.ts +208 -0
- package/src/agents/utils/config.ts +427 -0
- package/src/agents/workspace/coding-manager.ts +31 -0
- package/src/agents/workspace/components-manager.ts +124 -0
- package/src/agents/workspace/outline-focus.ts +188 -0
- package/src/agents/workspace/outline-info.ts +520 -0
- package/src/agents/workspace/page-tree-generator.ts +83 -0
- package/src/agents/workspace/workspace.ts +319 -0
- package/src/agents/workspace-by-knowledges/MIGRATION.md +568 -0
- package/src/agents/workspace-by-knowledges/README.md +521 -0
- package/src/agents/workspace-by-knowledges/index.ts +11 -0
- package/src/agents/workspace-by-knowledges/providers/component-docs-provider.ts +92 -0
- package/src/agents/workspace-by-knowledges/providers/focus-info-provider.ts +131 -0
- package/src/agents/workspace-by-knowledges/providers/index.ts +8 -0
- package/src/agents/workspace-by-knowledges/providers/project-info-provider.ts +151 -0
- package/src/agents/workspace-by-knowledges/test.ts +240 -0
- package/src/agents/workspace-by-knowledges/types.ts +56 -0
- package/src/agents/workspace-by-knowledges/utils/components-manager.ts +145 -0
- package/src/agents/workspace-by-knowledges/utils/index.ts +8 -0
- package/src/agents/workspace-by-knowledges/utils/outline-focus.ts +178 -0
- package/src/agents/workspace-by-knowledges/utils/outline-info.ts +521 -0
- package/src/agents/workspace-by-knowledges/workspace.ts +166 -0
- package/src/api/cloud-components.ts +129 -0
- package/src/api-record-replay/README.md +187 -0
- package/src/api-record-replay/index.ts +11 -0
- package/src/api-record-replay/manager.ts +168 -0
- package/src/api-record-replay/recorder.ts +117 -0
- package/src/api-record-replay/replayer.ts +148 -0
- package/src/components/attachments/index.less +117 -0
- package/src/components/attachments/index.tsx +136 -0
- package/src/components/icons/index.tsx +21 -1
- package/src/components/index.less +34 -0
- package/src/components/mention/index.less +23 -0
- package/src/components/mention/index.tsx +19 -0
- package/src/components/messages/index.less +444 -237
- package/src/components/messages/index.tsx +371 -88
- package/src/components/sender/index.less +203 -0
- package/src/components/sender/index.tsx +298 -0
- package/src/components/types.ts +31 -0
- package/src/constants/index.ts +8 -0
- package/src/context/RequestStatusTracker.ts +50 -0
- package/src/context/index.ts +68 -6
- package/src/{types.d.ts → global.d.ts} +40 -5
- package/src/index.tsx +212 -32
- package/src/preset/agents.ts +380 -0
- package/src/preset/createTemplates.ts +25 -0
- package/src/preset/index.ts +12 -0
- package/src/preset/prompts.ts +235 -0
- package/src/preset/requestAsStream.ts +246 -0
- package/src/preset/user.ts +6 -0
- package/src/startView/components/header/header.less +17 -0
- package/src/startView/components/header/header.tsx +15 -0
- package/src/startView/components/index.ts +1 -0
- package/src/startView/index.less +22 -204
- package/src/startView/index.tsx +35 -203
- package/src/tools/analyze-and-expand-prd.ts +192 -86
- package/src/tools/analyze-requirement-and-components.ts +589 -0
- package/src/tools/answer.ts +59 -0
- package/src/tools/build-process.ts +1174 -0
- package/src/tools/coding-subagent-as-tool.ts +119 -0
- package/src/tools/generate-ui-content.ts +1083 -0
- package/src/tools/index.ts +22 -19
- package/src/tools/open-dsl.ts +69 -0
- package/src/tools/refactor-ui-content.ts +801 -0
- package/src/tools/utils.ts +880 -28
- package/src/types/index.ts +4 -0
- package/src/view/components/header/header.less +36 -2
- package/src/view/components/header/header.tsx +47 -2
- package/src/view/components/index.ts +0 -2
- package/src/view/index.tsx +158 -8
- package/src/tools/answer-user.ts +0 -35
- package/src/tools/focus-element.ts +0 -47
- package/src/tools/generate-page.ts +0 -750
- package/src/tools/get-component-info-by-ids.ts +0 -166
- package/src/tools/get-component-info.ts +0 -53
- package/src/tools/get-components-doc-and-prd.ts +0 -137
- package/src/tools/get-focus-mybricks-dsl.ts +0 -26
- package/src/tools/get-mybricks-dsl.ts +0 -73
- package/src/tools/modify-component.ts +0 -385
- package/src/view/components/messages/messages.less +0 -228
- package/src/view/components/messages/messages.tsx +0 -172
- package/src/view/components/sender/sender.less +0 -44
- package/src/view/components/sender/sender.tsx +0 -62
package/src/startView/index.tsx
CHANGED
|
@@ -1,228 +1,60 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from "react"
|
|
2
|
-
import classNames from "classnames";
|
|
3
|
-
import { message } from "antd";
|
|
4
|
-
import { ArrowUp, Attachment, Loading, Close } from "../components/icons";
|
|
5
2
|
import { Agents } from "../agents";
|
|
6
|
-
// import { Messages } from "../view/components/messages/messages";
|
|
7
3
|
import { Messages } from "../components/messages";
|
|
4
|
+
import { Sender, SenderRef, SenderProps } from "../components/sender";
|
|
8
5
|
import css from "./index.less"
|
|
9
6
|
import { context } from "../context";
|
|
7
|
+
import classNames from "classnames";
|
|
8
|
+
import { Header } from "./components";
|
|
10
9
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const reader = new FileReader();
|
|
14
|
-
reader.onload = function (event) {
|
|
15
|
-
if (event.target) {
|
|
16
|
-
const base64 = event.target.result as string;
|
|
17
|
-
resolve(base64);
|
|
18
|
-
} else {
|
|
19
|
-
reject(event);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
reader.onerror = function (event) {
|
|
23
|
-
reject(event);
|
|
24
|
-
};
|
|
25
|
-
reader.readAsDataURL(file);
|
|
26
|
-
})
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const StartView = ({ user, copilot }: any) => {
|
|
30
|
-
const inputEditorRef = useRef<HTMLDivElement>(null);
|
|
31
|
-
const [isComposing, setIsComposing] = useState(false);
|
|
32
|
-
const [inputContent, setInputContent] = useState<string | null>(null);
|
|
10
|
+
const StartView = ({ api, user, copilot }: any) => {
|
|
11
|
+
const senderRef = useRef<SenderRef>(null);
|
|
33
12
|
const [loading, setLoading] = useState(false);
|
|
34
|
-
const [
|
|
13
|
+
const [empty, setEmpty] = useState(true);
|
|
35
14
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!loading) {
|
|
17
|
+
senderRef.current!.focus();
|
|
18
|
+
}
|
|
19
|
+
}, [loading])
|
|
40
20
|
|
|
21
|
+
const onSend = (params: Parameters<SenderProps['onSend']>[0]) => {
|
|
22
|
+
setEmpty(false);
|
|
23
|
+
if (!loading) {
|
|
24
|
+
setLoading(true);
|
|
41
25
|
Agents.requestGenerateCanvasAgent({
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
content: attachment
|
|
26
|
+
...params,
|
|
27
|
+
onProgress: (status: string) => {
|
|
28
|
+
if (status === "ing") {
|
|
29
|
+
api.onProgress?.(status);
|
|
47
30
|
}
|
|
48
|
-
}
|
|
31
|
+
},
|
|
32
|
+
rxai: context.globalRxai
|
|
49
33
|
}).then(() => {
|
|
50
34
|
|
|
51
35
|
}).catch((e) => {
|
|
52
|
-
|
|
36
|
+
console.error("[pluginAI - startView - onSend]", e);
|
|
53
37
|
}).finally(() => {
|
|
54
38
|
setLoading(false);
|
|
55
39
|
});
|
|
56
40
|
}
|
|
57
41
|
}
|
|
58
42
|
|
|
59
|
-
const onInput = () => {
|
|
60
|
-
setInputContent(inputEditorRef.current!.textContent)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
64
|
-
if (event.key === "Enter") {
|
|
65
|
-
if (isComposing) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (event.shiftKey) {
|
|
70
|
-
|
|
71
|
-
} else {
|
|
72
|
-
event.preventDefault();
|
|
73
|
-
send();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const onCompositionStart = () => {
|
|
79
|
-
setIsComposing(true);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const onCompositionEnd = () => {
|
|
83
|
-
setIsComposing(false);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const uploadAttachment = () => {
|
|
87
|
-
const fileInput = document.createElement('input');
|
|
88
|
-
fileInput.type = 'file';
|
|
89
|
-
fileInput.accept = 'image/*';
|
|
90
|
-
|
|
91
|
-
fileInput.addEventListener('change', function (e) {
|
|
92
|
-
const target = e.target as HTMLInputElement;
|
|
93
|
-
if (!target) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
const file = target.files?.[0];
|
|
97
|
-
|
|
98
|
-
if (file) {
|
|
99
|
-
if (!file.type.startsWith('image/')) {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
readFileToBase64(file)
|
|
104
|
-
.then((base64) => {
|
|
105
|
-
setAttachments((attachments) => {
|
|
106
|
-
return [...attachments, base64]
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
|
-
.catch((event) => {
|
|
110
|
-
console.error("[@mybricks/plugin-ai - 上传附件失败]", event);
|
|
111
|
-
message.error("[@mybricks/plugin-ai - 上传附件失败]");
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
fileInput.click();
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
const deleteAttachment = (index: number) => {
|
|
120
|
-
setAttachments((attachments) => {
|
|
121
|
-
attachments.splice(index, 1)
|
|
122
|
-
return [...attachments]
|
|
123
|
-
})
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const onPaste = (event: React.ClipboardEvent<HTMLDivElement>) => {
|
|
127
|
-
event.preventDefault();
|
|
128
|
-
const file = event.clipboardData.files[0];
|
|
129
|
-
if (file?.type.startsWith('image/')) {
|
|
130
|
-
readFileToBase64(file)
|
|
131
|
-
.then((base64) => {
|
|
132
|
-
setAttachments((attachments) => {
|
|
133
|
-
return [...attachments, base64]
|
|
134
|
-
})
|
|
135
|
-
})
|
|
136
|
-
.catch((event) => {
|
|
137
|
-
console.error("[@mybricks/plugin-ai - 上传附件失败]", event);
|
|
138
|
-
message.error("[@mybricks/plugin-ai - 上传附件失败]");
|
|
139
|
-
})
|
|
140
|
-
} else {
|
|
141
|
-
const content = event.clipboardData.getData('text/plain');
|
|
142
|
-
|
|
143
|
-
if (!content) {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const selection = window.getSelection();
|
|
148
|
-
|
|
149
|
-
if (!selection?.rangeCount) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const range = selection.getRangeAt(0);
|
|
154
|
-
range.deleteContents();
|
|
155
|
-
const textNode = document.createTextNode(content);
|
|
156
|
-
range.insertNode(textNode);
|
|
157
|
-
range.setStartAfter(textNode);
|
|
158
|
-
range.setEndAfter(textNode);
|
|
159
|
-
|
|
160
|
-
setInputContent(inputEditorRef.current!.textContent)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
useEffect(() => {
|
|
165
|
-
inputEditorRef.current!.focus();
|
|
166
|
-
}, [])
|
|
167
|
-
|
|
168
43
|
return (
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
<Close />
|
|
183
|
-
</div>
|
|
184
|
-
</div>
|
|
185
|
-
</div>
|
|
186
|
-
)
|
|
187
|
-
})}
|
|
188
|
-
</div> : null}
|
|
189
|
-
<div className={css.input}>
|
|
190
|
-
<div className={css.inputEditorContainer}>
|
|
191
|
-
<div
|
|
192
|
-
ref={inputEditorRef}
|
|
193
|
-
className={css.inputEditor}
|
|
194
|
-
contentEditable={!loading}
|
|
195
|
-
onKeyDown={onKeyDown}
|
|
196
|
-
onCompositionStart={onCompositionStart}
|
|
197
|
-
onCompositionEnd={onCompositionEnd}
|
|
198
|
-
onInput={onInput}
|
|
199
|
-
onPaste={onPaste}
|
|
200
|
-
></div>
|
|
201
|
-
{!inputContent && <div className={css.inputPlaceholder}>
|
|
202
|
-
您好,我是智能助手,请详细描述您要搭建的应用内容
|
|
203
|
-
</div>}
|
|
204
|
-
</div>
|
|
205
|
-
</div>
|
|
206
|
-
<div className={css.editorAction}>
|
|
207
|
-
<div className={css.leftArea}>
|
|
208
|
-
<div className={css.attachmentButton} onClick={uploadAttachment}>
|
|
209
|
-
<Attachment />
|
|
210
|
-
</div>
|
|
211
|
-
</div>
|
|
212
|
-
<div className={css.rightArea}>
|
|
213
|
-
<div className={classNames(css.sendButtonContainer, {
|
|
214
|
-
[css.disabled]: !inputContent || loading
|
|
215
|
-
})} onClick={send}>
|
|
216
|
-
<div className={classNames(css.sendButton, {
|
|
217
|
-
[css.loadingButton]: loading
|
|
218
|
-
})}>
|
|
219
|
-
{loading ? <Loading /> : <ArrowUp />}
|
|
220
|
-
</div>
|
|
221
|
-
</div>
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
44
|
+
<div className={classNames(css['view'], {
|
|
45
|
+
[css['empty']]: empty
|
|
46
|
+
})}
|
|
47
|
+
>
|
|
48
|
+
{empty && <Header />}
|
|
49
|
+
<Messages user={user} rxai={context.globalRxai} copilot={copilot} />
|
|
50
|
+
<Sender
|
|
51
|
+
ref={senderRef}
|
|
52
|
+
loading={loading}
|
|
53
|
+
onSend={onSend}
|
|
54
|
+
placeholder={`您好,我是${context.name},请详细描述您要搭建的应用内容`}
|
|
55
|
+
attachmentsPrompt={"根据附件中的图片内容进行设计开发,要求尽可能还原其中的各类设计细节以及功能,在此基础上可做调整优化创新"}
|
|
56
|
+
/>
|
|
224
57
|
</div>
|
|
225
|
-
</>
|
|
226
58
|
)
|
|
227
59
|
}
|
|
228
60
|
|
|
@@ -1,64 +1,23 @@
|
|
|
1
|
-
import { fileFormat } from '@mybricks/rxai'
|
|
1
|
+
import { fileFormat, RxaiError } from '@mybricks/rxai'
|
|
2
2
|
import { getFiles } from './utils'
|
|
3
|
+
import { DeviceType } from './../types'
|
|
3
4
|
|
|
4
5
|
interface AnalyzeAndExpandPrdParams {
|
|
5
6
|
onProjectCreate: (projectJson: any) => void,
|
|
6
|
-
demo?: any
|
|
7
|
+
demo?: any,
|
|
8
|
+
deviceType: DeviceType
|
|
7
9
|
}
|
|
8
10
|
|
|
11
|
+
const NAME = 'analyze-and-expand-prd'
|
|
12
|
+
analyzeAndExpandPrd.toolName = NAME
|
|
13
|
+
|
|
9
14
|
export default function analyzeAndExpandPrd(config: AnalyzeAndExpandPrdParams): any {
|
|
10
|
-
const {
|
|
11
|
-
demo = {
|
|
12
|
-
title: '本地生活APP',
|
|
13
|
-
pages: [
|
|
14
|
-
{
|
|
15
|
-
title: '首页',
|
|
16
|
-
prd: `目的:诱导用户进行点击,完成商品转化
|
|
17
|
-
需求:
|
|
18
|
-
从上往下依次为
|
|
19
|
-
1. 搜索模块:提供全局的商品搜索能力
|
|
20
|
-
2. 导航入口:一般使用一行N列来提供各个子界面的快捷入口
|
|
21
|
-
3. 轮播图:轮播当前热门的活动图片
|
|
22
|
-
4. 猜你喜欢:使用商品瀑布流来展示各类商品`,
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
title: '首页',
|
|
26
|
-
prd: `目的:对所有商品提供分类索引入口
|
|
27
|
-
需求:
|
|
28
|
-
从上往下依次为
|
|
29
|
-
1. 搜索模块:提供全局的商品搜索能力
|
|
30
|
-
2. 分类模块:
|
|
31
|
-
2.1 分类侧栏:用于提供商品分类的快捷入口
|
|
32
|
-
2.2 商品列表:在分类侧栏的子项里展示当前分类的商品列表`,
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
title: '我的',
|
|
36
|
-
prd: `目的:提供对个人信息的查看以及修改界面
|
|
37
|
-
需求:
|
|
38
|
-
从上往下依次为
|
|
39
|
-
1. 个人信息:通常包含头像和昵称,以及二维码信息
|
|
40
|
-
2. 订单入口:提供对订单已支付、已收获等分类的快捷入口
|
|
41
|
-
3. 会员信息:提供各类会员优惠活动入口`,
|
|
42
|
-
}
|
|
43
|
-
],
|
|
44
|
-
style: `一个偏向活力的橙色风格是一个不错的选择
|
|
45
|
-
- 颜色
|
|
46
|
-
- 主颜色:活力橙 #FF5733
|
|
47
|
-
- 背景色:浅灰色 #F7F7F7
|
|
48
|
-
- 文本颜色:沉稳黑 #000000 可用于主要的文本
|
|
49
|
-
- 二级颜色:灰色 #A6A6A6 可用于边框或者浅色文本
|
|
50
|
-
- 样式
|
|
51
|
-
- 圆角:12px,增加柔和感
|
|
52
|
-
- 间距:16px
|
|
53
|
-
同样的,你也可以参考一些常见的APP,比如美团和饿了么的元素设计。`
|
|
54
|
-
}
|
|
55
|
-
} = config ?? {}
|
|
56
15
|
return {
|
|
57
|
-
name:
|
|
16
|
+
name: NAME,
|
|
58
17
|
displayName: "对原始产品需求文档分析并扩写",
|
|
59
18
|
description: `对用户提供的原始产品需求文档进行需求分析与扩写,最后拆分成不同的页面,输出结构化的结果。
|
|
60
19
|
参数:原始需求文档(可能是一句话、一个图片、甚至各种类型附件)
|
|
61
|
-
|
|
20
|
+
作用:将模糊、简略的需求转化为清晰、具体到每个页面、以及每个页面自上而下都有哪些具体的功能的详细描述,用于指导后续低代码搭建;
|
|
62
21
|
返回值:扩写后的结构化文档;`,
|
|
63
22
|
getPrompts: () => {
|
|
64
23
|
return `<工具总览>
|
|
@@ -75,34 +34,38 @@ export default function analyzeAndExpandPrd(config: AnalyzeAndExpandPrdParams):
|
|
|
75
34
|
- 对话可能由多轮构成,每轮对话中,用户会提出不同的问题或给与信息补充,你需要根据用户的问题、逐步分析处理。
|
|
76
35
|
- 你所面向的用户是MyBricks平台上的用户,这些用户不是专业的开发人员,因此你需要以简洁、易懂的方式,回答用户的问题。
|
|
77
36
|
- 如果附件中有图片,请在设计开发中作为重要参考,进行详细的需求及设计分析,当作用户的需求。
|
|
37
|
+
- 你的回答面向的是非专业开发人员,请务必使用**简洁、易懂、口语化**的语言。
|
|
78
38
|
</特别注意>
|
|
79
39
|
|
|
80
40
|
<遵循原则>
|
|
81
|
-
你要切换不同的角色来完成一个需求的设计和开发,同时特别注意,生成的页面数量不得超过
|
|
41
|
+
你要切换不同的角色来完成一个需求的设计和开发,同时特别注意,生成的页面数量不得超过3个。
|
|
82
42
|
</遵循原则>
|
|
83
43
|
|
|
84
44
|
<处理流程>
|
|
85
45
|
根据你的角色定义来完成以下任务,汇总给出一个Json
|
|
86
46
|
<任务一>
|
|
87
47
|
角色:产品经理
|
|
88
|
-
|
|
48
|
+
工作任务:梳理需求,并且分析总共需要几个页面来承接这个系统
|
|
89
49
|
返回内容:每个页面的名称以及需求
|
|
90
50
|
对应字段:页面字段中的title以及prd
|
|
91
51
|
返回规则:
|
|
92
|
-
-
|
|
52
|
+
- 如果是一句话需求,从上到下梳理并拓展需求,并整理成页面维度的需求
|
|
93
53
|
- 如何定义一句话需求?例如 "一个简历页面" "实现用户管理系统" "一个首页"
|
|
94
|
-
-
|
|
95
|
-
- "一个简历页面" -> "
|
|
54
|
+
- 如何拓展?拓展是仅分析功能模块就行,不要具体到细节,同时补充一下“不止于这些模块”的描述性词汇,告知这只是简略的需求,可以补充更多内容
|
|
55
|
+
- "一个简历页面" -> "一个个人简历页面,包含个人介绍、技能特长、项目经历、联系我等模块的页面"
|
|
96
56
|
- "实现用户管理系统" -> "
|
|
97
|
-
|
|
98
|
-
-
|
|
57
|
+
一个用户管理系统,包含以下页面:
|
|
58
|
+
- 登录页面
|
|
59
|
+
- 用户列表页面
|
|
99
60
|
- 新增用户页面
|
|
100
61
|
- 删除用户页面"
|
|
101
|
-
-
|
|
62
|
+
- 如果需求较为详实,比如附件中图片和文档可以提取出完整的需求,则整理之后,在满足用户需求的基础上,适当补充细节并整理成页面维度的需求
|
|
102
63
|
- 如何定义需求较为详实?例如 "一个包含导航、公司介绍、公司优势、页脚的公司官网",这种对内容有定义的需求就无需拓展
|
|
103
|
-
- 如果用户提供了图片,将用户提供的【用户需求】原样返回即可
|
|
104
64
|
注意:
|
|
105
|
-
- 由于我们不能实现太复杂的需求,需要控制拓展需求的规模,拓展不要超过
|
|
65
|
+
- 由于我们不能实现太复杂的需求,需要控制拓展需求的规模,拓展不要超过3个页面
|
|
66
|
+
- 需求必须是模糊的功能点,仅允许说明有这个模块:
|
|
67
|
+
对的功能点:“包含商品展示模块、规格选择、评价和推荐等模块,其他模块可以自行拓展”,这里没有具体到UI的实现,是合理的。
|
|
68
|
+
错误的功能点:“展示一个搜索框和按钮,下方提供推荐商品列表,商品需要包含标题”,UI过于具体,不合理,并且没有告知用户可以补充更多内容。
|
|
106
69
|
- 需求仅围绕UI的实现来拓展,不要涉及多语言、服务端、逻辑、SEO、打印、截图、动画,以及一些复杂交互的周边能力,我们仅关注UI部分
|
|
107
70
|
</任务一>
|
|
108
71
|
|
|
@@ -117,50 +80,193 @@ export default function analyzeAndExpandPrd(config: AnalyzeAndExpandPrdParams):
|
|
|
117
80
|
返回内容:给出设计规范的建议,范围局限于颜色和样式,和参考建议。不提供任何需求和布局方面的信息,风格化信息是给到下一轮大模型的提示词,提供建议即可。
|
|
118
81
|
对应字段:style
|
|
119
82
|
返回规则:
|
|
120
|
-
-
|
|
121
|
-
|
|
122
|
-
|
|
83
|
+
- 颜色系统很重要,需要给出主色、背景色、文本颜色、二级颜色的建议,并给出具体的颜色值。禁止使用太鲜艳或者太浅的颜色值。不要把点缀色或者操作色用于大面积的元素,例如按钮、卡片背景等。
|
|
84
|
+
- 样式系统很重要,需要给出圆角、间距的建议,并给出具体的数值
|
|
85
|
+
- 参考建议很重要,可以给出一些风格化参考,借鉴市面上成熟项目的设计风格
|
|
123
86
|
- 范围:目前限制在颜色、字体样式(不包含字体)、阴影和圆角这些基础UI细节,不要考虑视差滚动这类复杂样式
|
|
124
87
|
- 不要提供针对组件维度的样式建议,要从需求维度去建议
|
|
125
88
|
- 如果用户自己提了风格化主题相关需求,整理扩展即可
|
|
126
89
|
</任务三>
|
|
127
90
|
</处理流程>
|
|
128
91
|
|
|
92
|
+
<回答、输出流程>
|
|
93
|
+
**第一步:深度思考与需求分析(以Markdown格式呈现)**
|
|
94
|
+
在给出正式方案前,你必须先输出一个详细的“**思考过程**”部分。这部分的目标是让用户完全理解你的设计思路。请按照以下结构组织:
|
|
95
|
+
1. **需求解读**:用一两句话复述并理解用户的核心意图。
|
|
96
|
+
2. **页面规划逻辑**:
|
|
97
|
+
* 说明你计划设计哪几个页面(如首页、详情页、个人中心)。
|
|
98
|
+
* **为每个页面解释“为什么”**:例如,“因为电商项目的核心流程是‘浏览->查看->购买’,所以我们需要首页(用于浏览发现)、商品详情页(用于决策)、个人中心页(用于管理订单和购物车)”,最好能与最终的json结构保持一致。
|
|
99
|
+
* 可以制作一个简单的表格来对比各页面的核心目标和主要功能。
|
|
100
|
+
3. **设计风格构思**:
|
|
101
|
+
* 简要说明你为这个应用设定了怎样的视觉风格(如“现代简约的科技感”、“温暖亲和的社区感”)。
|
|
102
|
+
* 解释颜色选择的原因(如“主色选用蓝色,传递信任与专业感”)。
|
|
103
|
+
* 内容尽量丰富
|
|
104
|
+
|
|
105
|
+
**第二步:输出结构化方案(以JSON格式呈现)**
|
|
106
|
+
在“思考过程”之后,输出一个名为\`“【应用标题】项目需求文档.json”\`的JSON代码块。json格式参考<examples>提供的格式。
|
|
107
|
+
|
|
108
|
+
**注意**:
|
|
109
|
+
- “思考过程”直接输出即可,不要以一个标题的形式展现出来,否则对话比较割裂,不自然。
|
|
110
|
+
</回答、输出流程>
|
|
111
|
+
|
|
129
112
|
<examples>
|
|
113
|
+
${getExampleByDeviceType(config.deviceType)}
|
|
114
|
+
</examples>`;
|
|
115
|
+
},
|
|
116
|
+
aiRole: 'architect',
|
|
117
|
+
// aiRole: "expert",
|
|
118
|
+
stream(params: any) {
|
|
119
|
+
const { files, replaceContent } = params;
|
|
120
|
+
const file = files[0];
|
|
121
|
+
if (file) {
|
|
122
|
+
return replaceContent.replace(file.fileName, "正在编写需求文档...");
|
|
123
|
+
}
|
|
124
|
+
return replaceContent;
|
|
125
|
+
},
|
|
126
|
+
execute(params: any) {
|
|
127
|
+
const { files, content, replaceContent } = params;
|
|
128
|
+
let errorContent;
|
|
129
|
+
try {
|
|
130
|
+
errorContent = JSON.parse(content)
|
|
131
|
+
} catch (error) { }
|
|
132
|
+
if (errorContent && errorContent?.message) {
|
|
133
|
+
throw new RxaiError(`网络错误,${errorContent?.message}`, "request");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const projectFile: any = getFiles(files, { extName: 'json' });
|
|
137
|
+
let projectJson: any = {}
|
|
138
|
+
try {
|
|
139
|
+
projectJson = JSON.parse(projectFile?.content)
|
|
140
|
+
|
|
141
|
+
if (projectJson?.pages?.length) {
|
|
142
|
+
projectJson.pages = projectJson.pages.map(page => {
|
|
143
|
+
return {
|
|
144
|
+
...page,
|
|
145
|
+
prd: page.prd + '\n' + `注意:当前正在搭建一个${(config.deviceType === DeviceType.Desktop ? 'PC桌面端项目' : '移动端项目')},功能点仅为概述,分析和实现时请务必多补充内容和细节。`,
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
config.onProjectCreate(projectJson);
|
|
154
|
+
|
|
155
|
+
if (!projectFile) {
|
|
156
|
+
return content;
|
|
157
|
+
} else if (!projectJson.title) {
|
|
158
|
+
return replaceContent + `\n未生成合法项目文件。`
|
|
159
|
+
}
|
|
160
|
+
return replaceContent.replace(projectFile.fileName, "");
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function getExampleByDeviceType(deviceType: DeviceType) {
|
|
166
|
+
if (deviceType === DeviceType.Mobile) {
|
|
167
|
+
return `
|
|
130
168
|
<example>
|
|
131
169
|
<user_query>一个本地生活APP</user_query>
|
|
132
170
|
<assistant_response>
|
|
133
|
-
好的,即将为你生成一个关于一个本地生活APP
|
|
171
|
+
好的,即将为你生成一个关于一个本地生活APP的项目需求文档。
|
|
134
172
|
|
|
135
173
|
这是我的思考结果:
|
|
136
|
-
|
|
174
|
+
由于当前信息较少,我们来分析下需求,一个本地生活APP,一般包含「首页」、「精选活动」、「商品详情」、「我的」等界面。
|
|
137
175
|
|
|
138
176
|
从需求来看,我觉得可以给应用起「本地生活APP」这个标题。
|
|
139
177
|
|
|
140
|
-
|
|
178
|
+
同时作为我也会给出设计规范。
|
|
141
179
|
|
|
142
180
|
最终我们应该返回这样的结构
|
|
143
181
|
${fileFormat({
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
182
|
+
content: JSON.stringify({
|
|
183
|
+
title: '本地生活APP',
|
|
184
|
+
pages: [
|
|
185
|
+
{
|
|
186
|
+
title: '首页',
|
|
187
|
+
prd: `目的:诱导用户进行点击,完成商品转化
|
|
188
|
+
功能点:包含搜索功能、导航入口、活动轮播和商品推荐等基础模块,其余内容可自行发散`,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
title: '精选活动',
|
|
192
|
+
prd: `目的:展示平台的优惠活动,吸引用户进行购买
|
|
193
|
+
功能点:包含一级分类、优惠活动、限时秒杀和商品列表等核心内容,可以按照你的想法来丰富更多内容`,
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
title: '商品详情',
|
|
197
|
+
prd: `目的:展示的详细信息,诱导用户进行下单购买
|
|
198
|
+
功能点:包含商品展示模块、规格选择、评价和推荐等模块,其他模块可以自行拓展`,
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
title: '我的',
|
|
202
|
+
prd: `目的:提供对个人信息的查看以及修改界面
|
|
203
|
+
功能点:包含个人信息、会员信息、订单模块、导航入口等模块`,
|
|
204
|
+
}
|
|
205
|
+
],
|
|
206
|
+
style: `使用美团成熟的颜色系统是一个不错的选择
|
|
207
|
+
- 颜色
|
|
208
|
+
- 主颜色:美团黄 #FFD100
|
|
209
|
+
- 背景色:浅灰色 #F7F7F7
|
|
210
|
+
- 文本颜色:不全黑的黑色 #272727 可用于主要的文本(禁止使用 #000000,会导致无设计感)
|
|
211
|
+
- 二级颜色:灰色 #A6A6A6 可用于边框或者浅色文本
|
|
212
|
+
- 样式
|
|
213
|
+
- 圆角:12px,增加柔和感
|
|
214
|
+
- 间距:16px
|
|
215
|
+
同样的,你也可以参考一些常见的移动端APP,比如美团和饿了么的元素设计。注意颜色不要太浅,也不要太鲜艳,按钮等比较大块的元素禁止使用点缀色`
|
|
216
|
+
}, null, 2),
|
|
217
|
+
fileName: '本地生活APP项目需求文档.json'
|
|
218
|
+
})}
|
|
147
219
|
</assistant_response>
|
|
148
|
-
</example
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
let projectJson = {}
|
|
156
|
-
try {
|
|
157
|
-
projectJson = JSON.parse(projectFile?.content)
|
|
158
|
-
} catch (error) {
|
|
220
|
+
</example>`
|
|
221
|
+
} else if (deviceType === DeviceType.Desktop) {
|
|
222
|
+
return `
|
|
223
|
+
<example>
|
|
224
|
+
<user_query>一个问答社区网站</user_query>
|
|
225
|
+
<assistant_response>
|
|
226
|
+
好的,即将为你生成一个关于一个问答社区网站的项目需求文档。
|
|
159
227
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
228
|
+
这是我的思考结果:
|
|
229
|
+
由于当前信息较少,我们来分析下需求,一个问答社区网站,一般包含「首页」、「问答详情」、「我的主页」等界面。
|
|
230
|
+
|
|
231
|
+
从需求来看,我觉得可以给应用起「问答社区网站」这个标题。
|
|
232
|
+
|
|
233
|
+
同时作为我也会给出设计规范。
|
|
234
|
+
|
|
235
|
+
最终我们应该返回这样的结构
|
|
236
|
+
${fileFormat({
|
|
237
|
+
content: JSON.stringify({
|
|
238
|
+
title: '问答社区网站',
|
|
239
|
+
pages: [
|
|
240
|
+
{
|
|
241
|
+
title: '首页',
|
|
242
|
+
prd: `目的:引导用户进行问答内容的互动,推荐用户喜欢的问答内容
|
|
243
|
+
功能点:包含搜索、导航、热榜、推荐等基础内容,其余内容可自行发散`,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
title: '问答详情',
|
|
247
|
+
prd: `目的:展示问答的详细信息,引导用户进行互动,并且对不同对回答进行点赞
|
|
248
|
+
功能点:包含问题、不同用户回答、是否赞同等核心模块,其余可以按照你的想法来丰富更多内容`,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
title: '我的主页',
|
|
252
|
+
prd: `目的:提供对个人信息的查看以及互动界面
|
|
253
|
+
功能点:包含个人信息、会员信息、我的回答、点赞、创作数据等核心功能`,
|
|
254
|
+
}
|
|
255
|
+
],
|
|
256
|
+
style: `使用知乎成熟的颜色系统是一个不错的选择
|
|
257
|
+
- 颜色
|
|
258
|
+
- 主颜色:知乎蓝 #1772f6
|
|
259
|
+
- 背景色:浅灰蓝色 #f4f6f9
|
|
260
|
+
- 文本颜色:不全黑的黑色 #272727 可用于主要的文本(禁止使用 #000000,会导致无设计感)
|
|
261
|
+
- 二级颜色:灰色 #A6A6A6 可用于边框或者浅色文本
|
|
262
|
+
- 样式
|
|
263
|
+
- 圆角:2px,严谨又不失柔弱
|
|
264
|
+
- 间距:16px
|
|
265
|
+
同样的,你也可以参考一些其他常见的桌面端软件或者网站,比如知乎和思否的设计。注意取色和设计必须有设计感`
|
|
266
|
+
}, null, 2),
|
|
267
|
+
fileName: '问答社区网站需求文档.json'
|
|
268
|
+
})}
|
|
269
|
+
</assistant_response>
|
|
270
|
+
</example>`
|
|
271
|
+
}
|
|
166
272
|
}
|