plugin-build-guide-block 1.0.10 → 1.0.12
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/dist/client/UserGuideBlock.d.ts +2 -0
- package/dist/client/UserGuideBlockInitializer.d.ts +2 -0
- package/dist/client/UserGuideBlockProvider.d.ts +2 -0
- package/dist/client/UserGuideManager.d.ts +2 -0
- package/dist/client/components/BuildButton.d.ts +2 -0
- package/dist/client/components/LLMServiceSelect.d.ts +2 -0
- package/dist/client/components/ModelSelect.d.ts +2 -0
- package/dist/client/components/StatusTag.d.ts +2 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -1
- package/dist/client/locale.d.ts +3 -0
- package/dist/client/models/UserGuideBlockModel.d.ts +9 -0
- package/dist/client/models/index.d.ts +9 -0
- package/dist/client/plugin.d.ts +5 -0
- package/dist/client/schemaSettings.d.ts +2 -0
- package/dist/client/schemas/spacesSchema.d.ts +356 -0
- package/dist/externalVersion.js +8 -8
- package/dist/index.d.ts +2 -0
- package/dist/locale/en-US.json +3 -0
- package/dist/locale/namespace.d.ts +6 -0
- package/dist/locale/namespace.js +36 -0
- package/dist/locale/vi-VN.json +3 -0
- package/dist/locale/zh-CN.json +3 -0
- package/dist/node_modules/marked/bin/main.js +279 -0
- package/dist/node_modules/marked/bin/marked.js +15 -0
- package/dist/node_modules/marked/lib/marked.cjs +1 -0
- package/dist/node_modules/marked/lib/marked.d.cts +657 -0
- package/dist/node_modules/marked/lib/marked.d.ts +657 -0
- package/dist/node_modules/marked/lib/marked.esm.js +2432 -0
- package/dist/node_modules/marked/lib/marked.umd.js +2456 -0
- package/dist/node_modules/marked/man/marked.1 +111 -0
- package/dist/node_modules/marked/marked.min.js +6 -0
- package/dist/node_modules/marked/package.json +1 -0
- package/dist/node_modules/sanitize-html/index.js +2 -2
- package/dist/node_modules/sanitize-html/package.json +1 -1
- package/dist/server/actions/build.d.ts +2 -0
- package/dist/server/actions/build.js +66 -52
- package/dist/server/actions/getHtml.d.ts +2 -0
- package/dist/server/actions/getMarkdown.d.ts +2 -0
- package/dist/server/actions/getMarkdown.js +53 -0
- package/dist/server/collections/ai-build-guide-spaces.d.ts +2 -0
- package/dist/server/collections/ai-build-guide-spaces.js +9 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +3 -4
- package/dist/server/plugin.d.ts +12 -0
- package/dist/server/plugin.js +8 -1
- package/package.json +51 -31
- package/src/client/UserGuideManager.tsx +3 -2
- package/src/client/locale.ts +18 -0
- package/src/client/plugin.tsx +52 -30
- package/src/client/schemaSettings.ts +75 -0
- package/src/client/schemas/spacesSchema.ts +42 -1
- package/src/locale/en-US.json +3 -0
- package/src/locale/namespace.ts +6 -0
- package/src/locale/vi-VN.json +3 -0
- package/src/locale/zh-CN.json +3 -0
- package/src/server/actions/build.ts +46 -33
- package/src/server/actions/getMarkdown.ts +26 -0
- package/src/server/collections/ai-build-guide-spaces.ts +9 -0
- package/src/server/index.ts +1 -2
- package/src/server/plugin.ts +83 -76
- package/src/server/collections/.gitkeep +0 -0
|
@@ -75,6 +75,18 @@ export const spacesSchema = {
|
|
|
75
75
|
},
|
|
76
76
|
},
|
|
77
77
|
},
|
|
78
|
+
outputFormat: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
title: '{{t("Output format")}}',
|
|
81
|
+
required: true,
|
|
82
|
+
default: 'html',
|
|
83
|
+
'x-decorator': 'FormItem',
|
|
84
|
+
'x-component': 'Select',
|
|
85
|
+
enum: [
|
|
86
|
+
{ label: 'HTML', value: 'html' },
|
|
87
|
+
{ label: 'Markdown', value: 'markdown' },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
78
90
|
systemPrompt: {
|
|
79
91
|
type: 'string',
|
|
80
92
|
title: '{{t("System Prompt")}}',
|
|
@@ -84,7 +96,7 @@ export const spacesSchema = {
|
|
|
84
96
|
rows: 4,
|
|
85
97
|
},
|
|
86
98
|
default:
|
|
87
|
-
'You are an expert technical writer.
|
|
99
|
+
'You are an expert technical writer. Generate a comprehensive user guide based on the provided documents.',
|
|
88
100
|
},
|
|
89
101
|
documents: {
|
|
90
102
|
type: 'array',
|
|
@@ -231,6 +243,17 @@ export const spacesSchema = {
|
|
|
231
243
|
'x-decorator': 'FormItem',
|
|
232
244
|
'x-component': 'ModelSelect',
|
|
233
245
|
},
|
|
246
|
+
outputFormat: {
|
|
247
|
+
type: 'string',
|
|
248
|
+
title: '{{t("Output format")}}',
|
|
249
|
+
required: true,
|
|
250
|
+
'x-decorator': 'FormItem',
|
|
251
|
+
'x-component': 'Select',
|
|
252
|
+
enum: [
|
|
253
|
+
{ label: 'HTML', value: 'html' },
|
|
254
|
+
{ label: 'Markdown', value: 'markdown' },
|
|
255
|
+
],
|
|
256
|
+
},
|
|
234
257
|
systemPrompt: {
|
|
235
258
|
type: 'string',
|
|
236
259
|
title: '{{t("System Prompt")}}',
|
|
@@ -261,6 +284,24 @@ export const spacesSchema = {
|
|
|
261
284
|
},
|
|
262
285
|
'x-read-pretty': true,
|
|
263
286
|
},
|
|
287
|
+
generatedMarkdown: {
|
|
288
|
+
type: 'string',
|
|
289
|
+
title: '{{t("Generated Markdown")}}',
|
|
290
|
+
'x-decorator': 'FormItem',
|
|
291
|
+
'x-component': 'Input.TextArea',
|
|
292
|
+
'x-component-props': {
|
|
293
|
+
rows: 6,
|
|
294
|
+
},
|
|
295
|
+
'x-read-pretty': true,
|
|
296
|
+
'x-reactions': {
|
|
297
|
+
dependencies: ['outputFormat'],
|
|
298
|
+
fulfill: {
|
|
299
|
+
state: {
|
|
300
|
+
visible: '{{$deps[0] === "markdown"}}',
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
},
|
|
264
305
|
buildLog: {
|
|
265
306
|
type: 'string',
|
|
266
307
|
title: '{{t("Build Log")}}',
|
package/src/locale/en-US.json
CHANGED
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
"Edit": "Edit",
|
|
17
17
|
"Edit space": "Edit space",
|
|
18
18
|
"Generated HTML": "Generated HTML",
|
|
19
|
+
"Generated Markdown": "Generated Markdown",
|
|
20
|
+
"Output format": "Output format",
|
|
21
|
+
"Select Space": "Select Space",
|
|
19
22
|
"Build Log": "Build Log",
|
|
20
23
|
"Delete": "Delete",
|
|
21
24
|
"Saved successfully": "Saved successfully",
|
package/src/locale/vi-VN.json
CHANGED
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
"Edit": "Sửa",
|
|
17
17
|
"Edit space": "Sửa Space",
|
|
18
18
|
"Generated HTML": "HTML Đã tạo",
|
|
19
|
+
"Generated Markdown": "Markdown Đã tạo",
|
|
20
|
+
"Output format": "Định dạng đầu ra",
|
|
21
|
+
"Select Space": "Chọn Space",
|
|
19
22
|
"Build Log": "Log quá trình Build",
|
|
20
23
|
"Delete": "Xóa",
|
|
21
24
|
"Saved successfully": "Lưu thành công",
|
package/src/locale/zh-CN.json
CHANGED
|
@@ -9,6 +9,28 @@ import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
|
|
9
9
|
import axios from 'axios';
|
|
10
10
|
import fs from 'fs';
|
|
11
11
|
import path from 'path';
|
|
12
|
+
import { marked } from 'marked';
|
|
13
|
+
|
|
14
|
+
const SANITIZE_OPTIONS: sanitizeHtml.IOptions = {
|
|
15
|
+
allowedTags: [
|
|
16
|
+
'div', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
17
|
+
'ul', 'ol', 'li', 'table', 'thead', 'tbody', 'tr', 'td', 'th',
|
|
18
|
+
'a', 'img', 'span', 'strong', 'em', 'code', 'pre', 'blockquote', 'br', 'hr',
|
|
19
|
+
],
|
|
20
|
+
allowedAttributes: {
|
|
21
|
+
a: ['href', 'target'],
|
|
22
|
+
img: ['src', 'alt', 'width', 'height'],
|
|
23
|
+
'*': ['style', 'class'],
|
|
24
|
+
},
|
|
25
|
+
allowedStyles: {
|
|
26
|
+
'*': {
|
|
27
|
+
color: [/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, /^rgb/, /^rgba/],
|
|
28
|
+
'background-color': [/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, /^rgb/, /^rgba/],
|
|
29
|
+
'text-align': [/^left$/, /^right$/, /^center$/, /^justify$/],
|
|
30
|
+
'font-size': [/^\d+(?:px|em|%)$/],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
12
34
|
|
|
13
35
|
async function fetchFileContent(app: any, file: any): Promise<string> {
|
|
14
36
|
const fileManager = app.pm.get('file-manager') as PluginFileManagerServer;
|
|
@@ -87,7 +109,8 @@ export async function build(ctx: Context, next: Next) {
|
|
|
87
109
|
throw new Error('Plugin AI is not available');
|
|
88
110
|
}
|
|
89
111
|
|
|
90
|
-
const { llmService, model, systemPrompt } = space.get();
|
|
112
|
+
const { llmService, model, systemPrompt, outputFormat } = space.get();
|
|
113
|
+
const format = outputFormat === 'markdown' ? 'markdown' : 'html';
|
|
91
114
|
|
|
92
115
|
if (!llmService || !model) {
|
|
93
116
|
throw new Error('LLM Service or model is missing in space configuration');
|
|
@@ -101,44 +124,34 @@ export async function build(ctx: Context, next: Next) {
|
|
|
101
124
|
messages.push(new SystemMessage(systemPrompt));
|
|
102
125
|
}
|
|
103
126
|
|
|
104
|
-
const instruction =
|
|
127
|
+
const instruction =
|
|
128
|
+
format === 'markdown'
|
|
129
|
+
? `Please generate a comprehensive user guide in pure Markdown based on the following documents. Output ONLY Markdown content (no HTML wrappers, no code-fence around the whole document).\n\nDocuments:\n${documentsText}`
|
|
130
|
+
: `Please generate an HTML user guide based on the following documents. Output ONLY valid HTML without Markdown blocks.\n\nDocuments:\n${documentsText}`;
|
|
105
131
|
messages.push(new HumanMessage(instruction));
|
|
106
132
|
|
|
107
133
|
const response = await provider.chatModel.invoke(messages);
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
allowedStyles: {
|
|
126
|
-
'*': {
|
|
127
|
-
'color': [/^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, /^rgb/, /^rgba/],
|
|
128
|
-
'background-color': [/^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, /^rgb/, /^rgba/],
|
|
129
|
-
'text-align': [/^left$/, /^right$/, /^center$/, /^justify$/],
|
|
130
|
-
'font-size': [/^\d+(?:px|em|%)$/]
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
+
const rawText =
|
|
135
|
+
typeof response.content === 'string' ? response.content : JSON.stringify(response.content);
|
|
136
|
+
|
|
137
|
+
const updateValues: Record<string, unknown> = { status: 'completed' };
|
|
138
|
+
|
|
139
|
+
if (format === 'markdown') {
|
|
140
|
+
const rawMarkdown = rawText.replace(/^```(?:markdown|md)?\s*/, '').replace(/```\s*$/, '');
|
|
141
|
+
const renderedHtml = await marked.parse(rawMarkdown, { async: true });
|
|
142
|
+
const cleanHtml = sanitizeHtml(renderedHtml, SANITIZE_OPTIONS);
|
|
143
|
+
updateValues.generatedMarkdown = rawMarkdown;
|
|
144
|
+
updateValues.generatedHtml = cleanHtml;
|
|
145
|
+
} else {
|
|
146
|
+
const rawHtml = rawText.replace(/^```html\s*/, '').replace(/```\s*$/, '');
|
|
147
|
+
const cleanHtml = sanitizeHtml(rawHtml, SANITIZE_OPTIONS);
|
|
148
|
+
updateValues.generatedHtml = cleanHtml;
|
|
149
|
+
updateValues.generatedMarkdown = null;
|
|
150
|
+
}
|
|
134
151
|
|
|
135
|
-
// 2d. Save generated HTML
|
|
136
152
|
await bgRepo.update({
|
|
137
153
|
filterByTk,
|
|
138
|
-
values:
|
|
139
|
-
generatedHtml: cleanHtml,
|
|
140
|
-
status: 'completed',
|
|
141
|
-
},
|
|
154
|
+
values: updateValues,
|
|
142
155
|
});
|
|
143
156
|
})();
|
|
144
157
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Context, Next } from '@nocobase/actions';
|
|
2
|
+
import { Repository } from '@nocobase/database';
|
|
3
|
+
|
|
4
|
+
export async function getMarkdown(ctx: Context, next: Next) {
|
|
5
|
+
const { filterByTk } = ctx.action.params;
|
|
6
|
+
const { resourceName } = ctx.action;
|
|
7
|
+
const repository = ctx.db.getRepository<any>(resourceName) as Repository;
|
|
8
|
+
const model = await repository.findById(filterByTk);
|
|
9
|
+
|
|
10
|
+
if (!model) {
|
|
11
|
+
ctx.throw(404, 'User Guide not found');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (model.get('status') !== 'completed') {
|
|
15
|
+
ctx.throw(400, 'User Guide is not ready yet');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
ctx.body = model.get('generatedMarkdown') || '';
|
|
19
|
+
ctx.withoutDataWrapping = true;
|
|
20
|
+
|
|
21
|
+
ctx.set({
|
|
22
|
+
'Content-Type': 'text/markdown; charset=UTF-8',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await next();
|
|
26
|
+
}
|
|
@@ -28,10 +28,19 @@ export default defineCollection({
|
|
|
28
28
|
type: 'text',
|
|
29
29
|
name: 'systemPrompt',
|
|
30
30
|
},
|
|
31
|
+
{
|
|
32
|
+
type: 'string',
|
|
33
|
+
name: 'outputFormat',
|
|
34
|
+
defaultValue: 'html',
|
|
35
|
+
},
|
|
31
36
|
{
|
|
32
37
|
type: 'text',
|
|
33
38
|
name: 'generatedHtml',
|
|
34
39
|
},
|
|
40
|
+
{
|
|
41
|
+
type: 'text',
|
|
42
|
+
name: 'generatedMarkdown',
|
|
43
|
+
},
|
|
35
44
|
{
|
|
36
45
|
type: 'string',
|
|
37
46
|
name: 'status',
|
package/src/server/index.ts
CHANGED
package/src/server/plugin.ts
CHANGED
|
@@ -1,76 +1,83 @@
|
|
|
1
|
-
import { InstallOptions, Plugin } from '@nocobase/server';
|
|
2
|
-
import
|
|
3
|
-
import { build } from './actions/build';
|
|
4
|
-
import { getHtml } from './actions/getHtml';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
this.app.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
await
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
1
|
+
import { InstallOptions, Plugin } from '@nocobase/server';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
import { build } from './actions/build';
|
|
4
|
+
import { getHtml } from './actions/getHtml';
|
|
5
|
+
import { getMarkdown } from './actions/getMarkdown';
|
|
6
|
+
|
|
7
|
+
export class PluginBuildGuideBlockServer extends Plugin {
|
|
8
|
+
afterAdd() {}
|
|
9
|
+
|
|
10
|
+
beforeLoad() {}
|
|
11
|
+
|
|
12
|
+
async load() {
|
|
13
|
+
await this.db.import({
|
|
14
|
+
directory: resolve(__dirname, 'collections'),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
this.app.resourceManager.registerActionHandlers({
|
|
18
|
+
'aiBuildGuideSpaces:build': build,
|
|
19
|
+
'aiBuildGuideSpaces:getHtml': getHtml,
|
|
20
|
+
'aiBuildGuideSpaces:getMarkdown': getMarkdown,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
this.app.acl.allow('aiBuildGuideSpaces', 'getHtml', 'loggedIn');
|
|
24
|
+
this.app.acl.allow('aiBuildGuideSpaces', 'getMarkdown', 'loggedIn');
|
|
25
|
+
this.app.acl.registerSnippet({
|
|
26
|
+
name: 'pm.ai-build-guide',
|
|
27
|
+
actions: [
|
|
28
|
+
'aiBuildGuideSpaces:create',
|
|
29
|
+
'aiBuildGuideSpaces:update',
|
|
30
|
+
'aiBuildGuideSpaces:destroy',
|
|
31
|
+
'aiBuildGuideSpaces:list',
|
|
32
|
+
'aiBuildGuideSpaces:get',
|
|
33
|
+
'aiBuildGuideSpaces:build',
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Recover stale "building" status after server restart
|
|
38
|
+
this.app.on('afterStart', async () => {
|
|
39
|
+
try {
|
|
40
|
+
const repo = this.db.getRepository('aiBuildGuideSpaces');
|
|
41
|
+
await repo.update({
|
|
42
|
+
filter: { status: 'building' },
|
|
43
|
+
values: {
|
|
44
|
+
status: 'error',
|
|
45
|
+
buildLog: 'Build interrupted by server restart',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
} catch (err) {
|
|
49
|
+
this.app.logger.warn('[plugin-build-guide-block] Failed to recover stale builds', err);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async install(options?: InstallOptions) {
|
|
55
|
+
const collection = this.db.getCollection('aiBuildGuideSpaces');
|
|
56
|
+
if (collection) {
|
|
57
|
+
await collection.model.sync();
|
|
58
|
+
}
|
|
59
|
+
const repo = this.db.getRepository<any>('collections');
|
|
60
|
+
if (repo) {
|
|
61
|
+
await repo.db2cm('aiBuildGuideSpaces');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async upgrade() {
|
|
66
|
+
const collection = this.db.getCollection('aiBuildGuideSpaces');
|
|
67
|
+
if (collection) {
|
|
68
|
+
await collection.model.sync();
|
|
69
|
+
}
|
|
70
|
+
const repo = this.db.getRepository<any>('collections');
|
|
71
|
+
if (repo) {
|
|
72
|
+
await repo.db2cm('aiBuildGuideSpaces');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async afterEnable() {}
|
|
77
|
+
|
|
78
|
+
async afterDisable() {}
|
|
79
|
+
|
|
80
|
+
async remove() {}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default PluginBuildGuideBlockServer;
|
|
File without changes
|