@paroicms/site-generator-plugin 0.8.0 → 0.9.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/gen-backend/dist/commands/actions.js +49 -0
- package/gen-backend/dist/db/db.queries.js +3 -3
- package/gen-backend/dist/errors.js +20 -0
- package/gen-backend/dist/generator/actions.js +45 -0
- package/gen-backend/dist/generator/fake-content-generator.ts/augment-fields.js +51 -0
- package/gen-backend/dist/generator/fake-content-generator.ts/content-helpers.js +17 -0
- package/gen-backend/dist/generator/fake-content-generator.ts/create-database-with-fake-content.js +7 -4
- package/gen-backend/dist/generator/generator-session.js +33 -0
- package/gen-backend/dist/generator/generator-types.js +1 -0
- package/gen-backend/dist/generator/lib/parse-llm-response.js +10 -16
- package/gen-backend/dist/generator/lib/token-tracking.js +118 -0
- package/gen-backend/dist/generator/llm-queries/invoke-new-site-analysis.js +24 -5
- package/gen-backend/dist/generator/session/generator-session.js +33 -0
- package/gen-backend/dist/generator/session/session-command.js +17 -0
- package/gen-backend/dist/generator/site-generator/theme-scss.js +262 -0
- package/gen-backend/dist/generator/site-schema-generator/create-site-schema.js +1 -0
- package/gen-backend/dist/lib/generator-context.js +14 -0
- package/gen-backend/dist/plugin.js +2 -2
- package/gen-backend/prompts/0-context.md +4 -2
- package/gen-backend/prompts/new-site-1-analysis.md +23 -7
- package/gen-backend/prompts/new-site-2-fields.md +1 -0
- package/gen-backend/prompts/update-site-schema-2-execute.md +20 -4
- package/gen-front/dist/gen-front.css +1 -1
- package/gen-front/dist/gen-front.mjs +64 -64
- package/package.json +3 -3
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
export function getThemeCssContent() {
|
|
2
|
+
return `/* Reset */
|
|
3
|
+
* {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
a,
|
|
8
|
+
a:visited {
|
|
9
|
+
color: inherit;
|
|
10
|
+
text-decoration: none;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Elements */
|
|
14
|
+
|
|
15
|
+
body {
|
|
16
|
+
background-color: #aaa;
|
|
17
|
+
margin: 0;
|
|
18
|
+
padding: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
img {
|
|
22
|
+
display: block;
|
|
23
|
+
max-width: 100%;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
picture {
|
|
27
|
+
display: block;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Tile */
|
|
31
|
+
|
|
32
|
+
a > article {
|
|
33
|
+
background-color: #fff;
|
|
34
|
+
border-radius: 6px;
|
|
35
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
36
|
+
display: flex;
|
|
37
|
+
max-width: 340px;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
}
|
|
40
|
+
a > article div:first-child {
|
|
41
|
+
flex: 0 0 120px;
|
|
42
|
+
max-width: 120px;
|
|
43
|
+
}
|
|
44
|
+
a > article img {
|
|
45
|
+
height: 100%;
|
|
46
|
+
object-fit: cover;
|
|
47
|
+
width: 100%;
|
|
48
|
+
}
|
|
49
|
+
a > article div:last-child {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex: 1;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
padding: 15px;
|
|
54
|
+
}
|
|
55
|
+
a > article h3 {
|
|
56
|
+
margin: 0 0 8px;
|
|
57
|
+
font-size: 16px;
|
|
58
|
+
font-weight: bold;
|
|
59
|
+
color: #333;
|
|
60
|
+
}
|
|
61
|
+
a > article p {
|
|
62
|
+
color: #666;
|
|
63
|
+
font-size: 14px;
|
|
64
|
+
line-height: 1.4;
|
|
65
|
+
margin: 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
a:hover > article {
|
|
69
|
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Classes */
|
|
73
|
+
|
|
74
|
+
._bg2 {
|
|
75
|
+
background-color: #444;
|
|
76
|
+
color: #eee;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.Container {
|
|
80
|
+
max-width: 1200px;
|
|
81
|
+
margin: 0 auto;
|
|
82
|
+
padding: 0 20px;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.Page {
|
|
86
|
+
background-color: #fff;
|
|
87
|
+
color: #333;
|
|
88
|
+
padding: 5px 40px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.TextWidth {
|
|
92
|
+
max-width: 600px;
|
|
93
|
+
margin: 0 auto;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.List {
|
|
97
|
+
display: flex;
|
|
98
|
+
gap: 20px;
|
|
99
|
+
flex-wrap: wrap;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.Header {
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
justify-content: space-between;
|
|
106
|
+
padding-bottom: 15px;
|
|
107
|
+
padding-top: 15px;
|
|
108
|
+
color: #fff;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@media (max-width: 768px) {
|
|
112
|
+
.Header {
|
|
113
|
+
flex-wrap: wrap;
|
|
114
|
+
padding: 15px 0 5px;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.Header-logo {
|
|
119
|
+
display: flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.Header-logo img {
|
|
124
|
+
margin-right: 10px;
|
|
125
|
+
border-radius: 50%;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.Header-title {
|
|
129
|
+
font-size: 20px;
|
|
130
|
+
font-weight: bold;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.Header-search {
|
|
134
|
+
padding: 0 10px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.Text::after {
|
|
138
|
+
clear: both;
|
|
139
|
+
content: "";
|
|
140
|
+
display: block;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.Text .Img {
|
|
144
|
+
height: auto;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.Text .Img.left {
|
|
148
|
+
float: left;
|
|
149
|
+
margin: 5px 20px 10px 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.Text .Img.right {
|
|
153
|
+
float: right;
|
|
154
|
+
margin: 5px 0 10px 20px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.Text .Img.left, .Text .Img.right {
|
|
158
|
+
max-width: 50%;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.Text .Img.center {
|
|
162
|
+
display: block;
|
|
163
|
+
margin: 20px auto;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.Text a,
|
|
167
|
+
.Text a:visited,
|
|
168
|
+
.TextLink,
|
|
169
|
+
.TextLink:visited {
|
|
170
|
+
color: #007bff;
|
|
171
|
+
cursor: pointer;
|
|
172
|
+
text-decoration: underline;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.Hero img {
|
|
176
|
+
height: auto;
|
|
177
|
+
width: 100%;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.InfiniteLoading-actionArea {
|
|
181
|
+
align-items: center;
|
|
182
|
+
display: flex;
|
|
183
|
+
justify-content: center;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.Button,
|
|
187
|
+
.SearchOpenerBtn,
|
|
188
|
+
.InfiniteLoading-btn {
|
|
189
|
+
background-color: #3498db;
|
|
190
|
+
border: 1px solid #3498db;
|
|
191
|
+
border-radius: 5px;
|
|
192
|
+
color: #fff;
|
|
193
|
+
cursor: pointer;
|
|
194
|
+
display: inline-block;
|
|
195
|
+
font-size: 16px;
|
|
196
|
+
margin: 10px 0;
|
|
197
|
+
padding: 10px 20px;
|
|
198
|
+
text-align: center;
|
|
199
|
+
text-decoration: none;
|
|
200
|
+
}
|
|
201
|
+
.Button:hover,
|
|
202
|
+
.SearchOpenerBtn:hover,
|
|
203
|
+
.InfiniteLoading-btn:hover {
|
|
204
|
+
background-color: #2980b9;
|
|
205
|
+
border-color: #2980b9;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.SearchOpenerBtn {
|
|
209
|
+
display: flex;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.Navbar {
|
|
213
|
+
align-items: center;
|
|
214
|
+
display: flex;
|
|
215
|
+
flex-grow: 1;
|
|
216
|
+
justify-content: center;
|
|
217
|
+
margin: 0 15px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@media (max-width: 768px) {
|
|
221
|
+
.Navbar {
|
|
222
|
+
flex-basis: 100%;
|
|
223
|
+
order: 3;
|
|
224
|
+
margin: 10px 0 5px;
|
|
225
|
+
justify-content: flex-start;
|
|
226
|
+
overflow-x: auto;
|
|
227
|
+
padding-bottom: 5px;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.NavButton {
|
|
232
|
+
color: rgba(255, 255, 255, 0.85);
|
|
233
|
+
padding: 8px;
|
|
234
|
+
margin: 0 4px;
|
|
235
|
+
border-radius: 4px;
|
|
236
|
+
font-size: 17px;
|
|
237
|
+
font-weight: bold;
|
|
238
|
+
position: relative;
|
|
239
|
+
transition: all 0.2s ease;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.NavButton:hover {
|
|
243
|
+
color: #fff;
|
|
244
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.NavButton.active {
|
|
248
|
+
color: #fff;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.NavButton.active::after {
|
|
252
|
+
content: "";
|
|
253
|
+
position: absolute;
|
|
254
|
+
bottom: 0;
|
|
255
|
+
left: 16px;
|
|
256
|
+
right: 16px;
|
|
257
|
+
height: 3px;
|
|
258
|
+
background-color: #3498db;
|
|
259
|
+
border-radius: 1.5px;
|
|
260
|
+
}
|
|
261
|
+
`;
|
|
262
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function createCommandContext(service, pluginConf, rawContext) {
|
|
2
|
+
const packConf = service.connector.getSitePackConf(pluginConf.packName);
|
|
3
|
+
const { sitesDir, packName } = packConf;
|
|
4
|
+
if (!sitesDir) {
|
|
5
|
+
throw new Error(`Site-generator plugin can generate sites only for pack with "sitesDir", but pack "${packName}" doesn't have it`);
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
...rawContext,
|
|
9
|
+
sitesDir,
|
|
10
|
+
packName,
|
|
11
|
+
service,
|
|
12
|
+
logger: service.logger,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -31,7 +31,7 @@ const plugin = {
|
|
|
31
31
|
modelName: "claude-3-7-sonnet-20250219",
|
|
32
32
|
anthropicApiKey: pluginConf.anthropicApiKey,
|
|
33
33
|
temperature: 0.1,
|
|
34
|
-
maxTokens:
|
|
34
|
+
maxTokens: 6500,
|
|
35
35
|
clientOptions: {
|
|
36
36
|
timeout: 60_000,
|
|
37
37
|
},
|
|
@@ -40,7 +40,7 @@ const plugin = {
|
|
|
40
40
|
modelName: "claude-3-7-sonnet-20250219",
|
|
41
41
|
anthropicApiKey: pluginConf.anthropicApiKey,
|
|
42
42
|
temperature: 0.1,
|
|
43
|
-
maxTokens:
|
|
43
|
+
maxTokens: 3000,
|
|
44
44
|
clientOptions: {
|
|
45
45
|
timeout: 20_000,
|
|
46
46
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
We use **ParoiCMS**
|
|
1
|
+
We use **ParoiCMS** for creating a website. With this technology, a web page is called a **document**. A website is a tree of documents. The home page is a document, the site section with news posts is a document, each post is a document. Each document has its own path in the URL.
|
|
2
2
|
|
|
3
3
|
There is a special kind of documents that we want to detect: **routing documents** are the site sections. They can't be duplicated. They are never items of a list. For example, the homepage document, the search-page document, the "about us" document, the parent page of blog posts are _routing documents_. Other documents are **regular documents**, and they are always items of a list.
|
|
4
4
|
|
|
@@ -6,4 +6,6 @@ A document always has the following base attributes: a localized _title_, a _pub
|
|
|
6
6
|
|
|
7
7
|
A document can contain lists of **parts**. A _part_ is a sub-section of a document, or of another _part_. A part always has a _publish date_ and a _draft_ flag. It may contain a sequence of fields and/or a sequence of child parts. A part is always an item of a list.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Any routing document which is parent of regular documents can be used as a **taxonomy**. Then, the terms are the regular child documents. Then a taxonomy can be used in any document or part, by declaring a **labeling field**.
|
|
10
|
+
|
|
11
|
+
Documents and parts are **nodes** in a tree. Children are part of the definition of a node type. When 2 node types appear identical, but their children types are not the same, then they are 2 different node types and they should have 2 different names.
|
|
@@ -30,16 +30,17 @@ For this second step, follow these instructions for creating the bullet list:
|
|
|
30
30
|
|
|
31
31
|
1. Carefully read and analyze the website description.
|
|
32
32
|
2. Identify the main documents and parts of the website.
|
|
33
|
-
- Notice: Children are part of the definition of a node type. When 2 node types appear identical, but their children types are not the same, then they are 2 different node types and they should have 2 different names.
|
|
34
33
|
3. Determine the hierarchical relationships between documents and parts.
|
|
35
34
|
4. Create a tree structure using a limited Markdown format to represent the website's tree structure.
|
|
36
35
|
|
|
36
|
+
Whenever you have a choice, keep it simple. If the user is not directive, if he gives a general and vague instruction, for example with a short prompt like “create a blog”, then limit the site sections to two main entries (in the example of a blog, a list of posts and a list of pages). Plus the usual contact and search pages.
|
|
37
|
+
|
|
37
38
|
Guidelines for creating the hierarchical bullet list:
|
|
38
39
|
|
|
39
40
|
- Write in English.
|
|
40
41
|
- But, if the website description is not in English: do not translate book or post titles.
|
|
41
42
|
- Use a Markdown syntax. Indent with 2 spaces. Use indentation to show parent-child relationships between node types.
|
|
42
|
-
- Always
|
|
43
|
+
- Always start with the homepage, with key `home` at the top level.
|
|
43
44
|
- When you define an identifier for a node type name or a list name, use camel case and follow the identifier syntax of JavaScript.
|
|
44
45
|
- Use `contactPage` as the default type name for contact page, and `searchPage` for the search page.
|
|
45
46
|
- Bullet point format for a _routing document_:
|
|
@@ -100,7 +101,9 @@ Here's an example of correct output using parts, and with the default contact an
|
|
|
100
101
|
* `home` (routing document)
|
|
101
102
|
* list of `homeSection` (parts), list name: `homeSections`
|
|
102
103
|
* `news` (routing document)
|
|
103
|
-
* list of `
|
|
104
|
+
* list of `post` (regular documents)
|
|
105
|
+
* `tags` (routing document)
|
|
106
|
+
* list of `tag` (regular documents)
|
|
104
107
|
* `pages` (routing document)
|
|
105
108
|
* list of `page` (regular documents)
|
|
106
109
|
* list of `pageSection` (parts), list name: `pageSections`
|
|
@@ -125,6 +128,7 @@ Guidelines for creating the dictionnary YAML:
|
|
|
125
128
|
- ogType: (optional, and document only) If you think of a particular Open-Graph type for this document, give it here.
|
|
126
129
|
- label: A label of the node type, in the _website language_.
|
|
127
130
|
- description: A description (5-40 words) for describing the purpose and theme of the node type. Write it in the _website language_.
|
|
131
|
+
- prompt: This is an optional property. If there is an information to process later about this node type (a description of fields), then write it here. Keep is short.
|
|
128
132
|
- For a list type (only for part list, never for document list), provide the following properties:
|
|
129
133
|
- confidence: Your confidence level for the accuracy of this node type (0.0-1.0).
|
|
130
134
|
- kind: Must be `partList`.
|
|
@@ -155,14 +159,26 @@ news:
|
|
|
155
159
|
kind: routingDocument
|
|
156
160
|
entryPage: true
|
|
157
161
|
label: News
|
|
158
|
-
description: This is the blog section of the website. The news document contains all the topical
|
|
159
|
-
|
|
162
|
+
description: This is the blog section of the website. The news document contains all the topical posts.
|
|
163
|
+
post:
|
|
160
164
|
confidence: 0.8
|
|
161
165
|
kind: regularDocument
|
|
162
166
|
temporal: true
|
|
163
167
|
ogType: article
|
|
164
|
-
label:
|
|
165
|
-
description: A topical
|
|
168
|
+
label: Post
|
|
169
|
+
description: A topical post about the subject of the website whatever it is.
|
|
170
|
+
prompt: Add a labeling field using the tags taxonomy
|
|
171
|
+
tags:
|
|
172
|
+
confidence: 0.9
|
|
173
|
+
kind: routingDocument
|
|
174
|
+
label: Tags
|
|
175
|
+
description: Tags taxonomy for post documents.
|
|
176
|
+
tag:
|
|
177
|
+
confidence: 0.9
|
|
178
|
+
kind: regularDocument
|
|
179
|
+
temporal: true
|
|
180
|
+
label: Tag
|
|
181
|
+
description: A tag is a term in the tags taxonomy.
|
|
166
182
|
pages:
|
|
167
183
|
confidence: 0.9
|
|
168
184
|
kind: routingDocument
|
|
@@ -33,6 +33,7 @@ Guidelines for creating the dictionnary YAML:
|
|
|
33
33
|
- By default, for most of node types, if you are not sure about what could be the best fields, then remember that a document is a webpage and just use a `[htmlContent]`.
|
|
34
34
|
- Except if there are specific instructions in the website description, here is the default value for the `_site` node type: `["logo", "footerMention"]`.
|
|
35
35
|
- Gallery of medias: there is a predefined field named `"gallery"`. It contains a list of medias. The theme can render it as a carousel, a slider, an image gallery, a slideshow, etc.
|
|
36
|
+
- This task is about predefined fields only. Custom fields will be added in a further step.
|
|
36
37
|
|
|
37
38
|
Here is an example of expected output:
|
|
38
39
|
|
|
@@ -73,7 +73,23 @@ Important:
|
|
|
73
73
|
- Never add an unknown predefined field.
|
|
74
74
|
- The type name of the "site" node type is omitted from the JSON but its value is always `_site`.
|
|
75
75
|
|
|
76
|
-
# 3.
|
|
76
|
+
# 3. Labeling fields (using a taxonomy)
|
|
77
|
+
|
|
78
|
+
A labeling field lets the user assign taxonomy terms to a document (or part).
|
|
79
|
+
|
|
80
|
+
<field_type_example>
|
|
81
|
+
{{
|
|
82
|
+
"name": "tags",
|
|
83
|
+
"localized": false,
|
|
84
|
+
"storedAs": "labeling",
|
|
85
|
+
"taxonomy": "tags",
|
|
86
|
+
"multiple": true
|
|
87
|
+
}},
|
|
88
|
+
</field_type_example>
|
|
89
|
+
|
|
90
|
+
Most of the time, the field name will be the same as the taxonomy type name.
|
|
91
|
+
|
|
92
|
+
# 4. Examine the current JSON data, which conforms to the `JtSiteSchema` type:
|
|
77
93
|
|
|
78
94
|
<site_schema_json>
|
|
79
95
|
{siteSchemaJson}
|
|
@@ -85,19 +101,19 @@ Also, the attached locales:
|
|
|
85
101
|
{l10nJson}
|
|
86
102
|
</l10n_json>
|
|
87
103
|
|
|
88
|
-
#
|
|
104
|
+
# 5. Now, here is what to do:
|
|
89
105
|
|
|
90
106
|
<user_request>
|
|
91
107
|
{taskDetailsMd}
|
|
92
108
|
</user_request>
|
|
93
109
|
|
|
94
|
-
#
|
|
110
|
+
# 6. Guidelines
|
|
95
111
|
|
|
96
112
|
- Don't assume how the CMS works. If you are not sure how to do something, don't do it.
|
|
97
113
|
- You are allowed to be proactive, but only when the user asks you to do something.
|
|
98
114
|
- Remember to adhere strictly to the TypeScript typing when making changes. If the update message requests changes that would violate the typing, then prioritize maintaining the correct structure over making those specific changes.
|
|
99
115
|
|
|
100
|
-
#
|
|
116
|
+
# 7. Output
|
|
101
117
|
|
|
102
118
|
If there is a change in the site schema, then provide the updated site schema in JSON within <updated_site_schema_json> tags. Otherwise, let this tag empty.
|
|
103
119
|
|