@paroicms/site-generator-plugin 0.11.0 → 0.13.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/generator/fake-content-generator/content-report.js +81 -0
- package/gen-backend/dist/generator/fake-content-generator/create-database-with-fake-content.js +80 -0
- package/gen-backend/dist/generator/fake-content-generator/fill-nodels.js +115 -0
- package/gen-backend/dist/generator/fake-content-generator/fill-site-fields.js +63 -0
- package/gen-backend/dist/generator/fake-content-generator/fill-taxonomy-fields.js +57 -0
- package/gen-backend/dist/generator/lib/create-prompt.js +4 -4
- package/gen-backend/dist/generator/llm-queries/invoke-message-guard.js +1 -1
- package/gen-backend/dist/generator/llm-queries/invoke-new-site-analysis.js +2 -2
- package/gen-backend/dist/generator/llm-queries/invoke-update-site-schema.js +2 -2
- package/gen-backend/dist/generator/site-generator/common-template-creator.js +1 -1
- package/gen-backend/dist/generator/site-generator/document-template-creator.js +11 -9
- package/gen-backend/dist/generator/site-generator/site-generator.js +3 -1
- package/gen-backend/dist/generator/site-generator/theme-creator.js +4 -4
- package/gen-backend/dist/generator/site-generator/theme-css.js +20 -12
- package/gen-backend/dist/generator/site-schema-generator/create-site-schema.js +8 -2
- package/gen-backend/prompts/initial-1-analysis.md +3 -1
- package/gen-front/dist/gen-front.css +1 -1
- package/gen-front/dist/gen-front.mjs +45 -47
- package/package.json +10 -8
- package/gen-backend/dist/generator/fake-content-generator.ts/content-report.js +0 -15
- package/gen-backend/dist/generator/fake-content-generator.ts/create-database-with-fake-content.js +0 -238
- /package/gen-backend/dist/generator/{fake-content-generator.ts → fake-content-generator}/content-helpers.js +0 -0
- /package/gen-backend/dist/generator/{fake-content-generator.ts → fake-content-generator}/create-node-contents.js +0 -0
- /package/gen-backend/dist/generator/{fake-content-generator.ts → fake-content-generator}/fake-content-types.js +0 -0
- /package/gen-backend/dist/generator/{fake-content-generator.ts → fake-content-generator}/generate-fake-content.js +0 -0
- /package/gen-backend/dist/generator/{fake-content-generator.ts → fake-content-generator}/invoke-generate-fake-content.js +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export function createGeneratedContentReport() {
|
|
2
|
+
let totalEntryCount = 0;
|
|
3
|
+
const llmReports = [];
|
|
4
|
+
const internalIds = {};
|
|
5
|
+
const childNodeIds = new Map();
|
|
6
|
+
return {
|
|
7
|
+
getResults() {
|
|
8
|
+
return {
|
|
9
|
+
entryCount: totalEntryCount,
|
|
10
|
+
llmReports,
|
|
11
|
+
idPicker: createIdPicker(internalIds, childNodeIds),
|
|
12
|
+
};
|
|
13
|
+
},
|
|
14
|
+
add(entryCount, llmReport) {
|
|
15
|
+
totalEntryCount += entryCount;
|
|
16
|
+
if (llmReport) {
|
|
17
|
+
llmReports.push(llmReport);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
addId({ typeName, id: idOrIds, parentNodeId }) {
|
|
21
|
+
for (const { language, nodeId } of Array.isArray(idOrIds) ? idOrIds : [idOrIds]) {
|
|
22
|
+
let languageDict = internalIds[language];
|
|
23
|
+
if (!languageDict) {
|
|
24
|
+
languageDict = {};
|
|
25
|
+
internalIds[language] = languageDict;
|
|
26
|
+
}
|
|
27
|
+
let nodeIds = languageDict[typeName];
|
|
28
|
+
if (!nodeIds) {
|
|
29
|
+
nodeIds = new Set();
|
|
30
|
+
languageDict[typeName] = nodeIds;
|
|
31
|
+
}
|
|
32
|
+
nodeIds.add(nodeId);
|
|
33
|
+
if (parentNodeId !== undefined) {
|
|
34
|
+
let childIds = childNodeIds.get(parentNodeId);
|
|
35
|
+
if (!childIds) {
|
|
36
|
+
childIds = new Set();
|
|
37
|
+
childNodeIds.set(parentNodeId, childIds);
|
|
38
|
+
}
|
|
39
|
+
childIds.add(nodeId);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function createIdPicker(ids, childNodeIds) {
|
|
46
|
+
const getNodeIdsByLanguageAndType = (language, typeName) => {
|
|
47
|
+
const languageDict = ids[language];
|
|
48
|
+
if (!languageDict)
|
|
49
|
+
return [];
|
|
50
|
+
const nodeIds = languageDict[typeName];
|
|
51
|
+
if (!nodeIds)
|
|
52
|
+
return [];
|
|
53
|
+
return Array.from(nodeIds);
|
|
54
|
+
};
|
|
55
|
+
const getNodeIdsByType = (typeName) => {
|
|
56
|
+
const nodeIds = new Set();
|
|
57
|
+
for (const language of Object.keys(ids)) {
|
|
58
|
+
const found = getNodeIdsByLanguageAndType(language, typeName);
|
|
59
|
+
found.forEach((id) => nodeIds.add(id));
|
|
60
|
+
}
|
|
61
|
+
return Array.from(nodeIds);
|
|
62
|
+
};
|
|
63
|
+
const getNodeIdsByParent = (parentNodeId) => {
|
|
64
|
+
const nodeIds = childNodeIds.get(parentNodeId);
|
|
65
|
+
if (!nodeIds)
|
|
66
|
+
return [];
|
|
67
|
+
return Array.from(nodeIds);
|
|
68
|
+
};
|
|
69
|
+
const picker = {
|
|
70
|
+
pickNodeIds(selector, count) {
|
|
71
|
+
const nodeIds = "typeName" in selector
|
|
72
|
+
? getNodeIdsByType(selector.typeName)
|
|
73
|
+
: getNodeIdsByParent(selector.parentNodeId);
|
|
74
|
+
if (count === undefined || nodeIds.length <= count)
|
|
75
|
+
return nodeIds;
|
|
76
|
+
const shuffled = [...nodeIds].sort(() => Math.random() - 0.5);
|
|
77
|
+
return shuffled.slice(0, count);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
return picker;
|
|
81
|
+
}
|
package/gen-backend/dist/generator/fake-content-generator/create-database-with-fake-content.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getPartTypeByName, getRegularDocumentTypeByName, getRoutingDocumentTypeByName, } from "@paroicms/internal-anywhere-lib";
|
|
2
|
+
import { createSimpleTranslator, } from "@paroicms/public-server-lib";
|
|
3
|
+
import { updateGeneratedSiteStepSetAsCompleted, } from "../../db/db-write.queries.js";
|
|
4
|
+
import { createTaskCollector } from "../lib/tasks.js";
|
|
5
|
+
import { createGeneratedContentReport } from "./content-report.js";
|
|
6
|
+
import { addParts, addRegularDocuments, updateRoutingDocument } from "./fill-nodels.js";
|
|
7
|
+
import { updateSiteFields } from "./fill-site-fields.js";
|
|
8
|
+
import { updateNodelsWithTaxonomies } from "./fill-taxonomy-fields.js";
|
|
9
|
+
export async function fillSiteWithFakeContent(ctx, stepHandle, { regSite, localizedValues }) {
|
|
10
|
+
const { service } = ctx;
|
|
11
|
+
const { fqdn } = regSite;
|
|
12
|
+
const report = createGeneratedContentReport();
|
|
13
|
+
const { siteSchema, siteIds } = await service.connector.loadSiteSchemaAndIds(fqdn);
|
|
14
|
+
const schemaI18n = createSimpleTranslator({
|
|
15
|
+
labels: siteSchema.l10n,
|
|
16
|
+
logger: ctx.logger,
|
|
17
|
+
});
|
|
18
|
+
await updateSiteFields(ctx, report, { fqdn, siteSchema, siteTitle: localizedValues.siteTitle });
|
|
19
|
+
const tasks = createTaskCollector(ctx);
|
|
20
|
+
fillRoutingDocumentAndAddChildren(ctx, tasks, report, {
|
|
21
|
+
fqdn,
|
|
22
|
+
siteSchema,
|
|
23
|
+
schemaI18n,
|
|
24
|
+
}, {
|
|
25
|
+
routingIds: siteIds.homeIds,
|
|
26
|
+
nodeType: siteSchema.nodeTypes.home,
|
|
27
|
+
});
|
|
28
|
+
const { promise } = tasks.runAll({ maxParallel: 10, rateLimitPerSecond: 3 });
|
|
29
|
+
const { doneCount, errorMessages } = await promise;
|
|
30
|
+
const results = report.getResults();
|
|
31
|
+
await updateNodelsWithTaxonomies(ctx, {
|
|
32
|
+
siteSchema,
|
|
33
|
+
idPicker: results.idPicker,
|
|
34
|
+
fqdn,
|
|
35
|
+
});
|
|
36
|
+
if (errorMessages.length > 0) {
|
|
37
|
+
ctx.logger.warn(`Failed to generate documents:\n - ${errorMessages.join("\n - ")}`);
|
|
38
|
+
}
|
|
39
|
+
ctx.logger.debug(`… Executed ${doneCount} generating tasks`);
|
|
40
|
+
await updateGeneratedSiteStepSetAsCompleted(ctx, stepHandle, {
|
|
41
|
+
status: "completed",
|
|
42
|
+
contentEntryCount: results.entryCount,
|
|
43
|
+
contentInputTokenCount: results.llmReports.reduce((acc, r) => acc + r.inputTokenCount, 0),
|
|
44
|
+
contentOutputTokenCount: results.llmReports.reduce((acc, r) => acc + (r.outputTokenCount ?? 0), 0),
|
|
45
|
+
contentErrors: errorMessages.length > 0 ? errorMessages.join("\n - ") : null,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function fillRoutingDocumentAndAddChildren(ctx, tasks, report, siteOptions, nodeOptions) {
|
|
49
|
+
const { routingIds, nodeType } = nodeOptions;
|
|
50
|
+
const { siteSchema } = siteOptions;
|
|
51
|
+
tasks.add(() => updateRoutingDocument(ctx, report, siteOptions, nodeOptions));
|
|
52
|
+
for (const listType of nodeType.lists ?? []) {
|
|
53
|
+
for (const typeName of listType.parts) {
|
|
54
|
+
const partType = getPartTypeByName(siteSchema, typeName);
|
|
55
|
+
tasks.add(() => addParts(ctx, report, siteOptions, {
|
|
56
|
+
parentNodeId: routingIds.nodeId,
|
|
57
|
+
nodeType: partType,
|
|
58
|
+
documentType: nodeType,
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const typeName of nodeType.routingChildren ?? []) {
|
|
63
|
+
const childType = getRoutingDocumentTypeByName(siteSchema, typeName);
|
|
64
|
+
const childIds = routingIds.children?.[typeName];
|
|
65
|
+
if (!childIds)
|
|
66
|
+
throw new Error(`Missing childIds for ${typeName}`);
|
|
67
|
+
fillRoutingDocumentAndAddChildren(ctx, tasks, report, siteOptions, {
|
|
68
|
+
routingIds: childIds,
|
|
69
|
+
nodeType: childType,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
for (const typeName of nodeType.regularChildren ?? []) {
|
|
73
|
+
const childType = getRegularDocumentTypeByName(siteSchema, typeName);
|
|
74
|
+
tasks.add(() => addRegularDocuments(ctx, report, siteOptions, {
|
|
75
|
+
parentNodeId: routingIds.nodeId,
|
|
76
|
+
nodeType: childType,
|
|
77
|
+
documentType: childType,
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { dedupMessages } from "./content-helpers.js";
|
|
2
|
+
import { generateFieldSetContent, generateMultipleFieldSetContents, } from "./generate-fake-content.js";
|
|
3
|
+
export async function updateRoutingDocument(ctx, report, siteOptions, nodeOptions) {
|
|
4
|
+
ctx.logger.debug(`[TASK] Updating routing document "${nodeOptions.nodeType.typeName}"…`);
|
|
5
|
+
const { routingIds, nodeType } = nodeOptions;
|
|
6
|
+
const { fqdn, siteSchema, schemaI18n } = siteOptions;
|
|
7
|
+
const content = await generateFieldSetContent(ctx, {
|
|
8
|
+
nodeType,
|
|
9
|
+
documentType: nodeType,
|
|
10
|
+
siteSchema,
|
|
11
|
+
schemaI18n,
|
|
12
|
+
withTitle: false,
|
|
13
|
+
llmTaskName: nodeType.kebabName,
|
|
14
|
+
}, report);
|
|
15
|
+
await ctx.service.connector.updateNodeContent(fqdn, {
|
|
16
|
+
nodeId: routingIds.nodeId,
|
|
17
|
+
content: toRiDocumentContent(content, nodeType),
|
|
18
|
+
});
|
|
19
|
+
for (const language of siteSchema.languages) {
|
|
20
|
+
report.addId({
|
|
21
|
+
typeName: nodeType.typeName,
|
|
22
|
+
id: {
|
|
23
|
+
nodeId: routingIds.nodeId,
|
|
24
|
+
language,
|
|
25
|
+
},
|
|
26
|
+
parentNodeId: undefined,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function addRegularDocuments(ctx, report, siteOptions, nodeOptions) {
|
|
31
|
+
ctx.logger.debug(`[TASK] Adding regular documents "${nodeOptions.nodeType.typeName}"…`);
|
|
32
|
+
const { parentNodeId, nodeType, documentType } = nodeOptions;
|
|
33
|
+
const { fqdn, siteSchema, schemaI18n } = siteOptions;
|
|
34
|
+
const tolerateErrors = {
|
|
35
|
+
errorMessages: [],
|
|
36
|
+
};
|
|
37
|
+
const list = await generateMultipleFieldSetContents(ctx, {
|
|
38
|
+
siteSchema,
|
|
39
|
+
nodeType,
|
|
40
|
+
documentType,
|
|
41
|
+
schemaI18n,
|
|
42
|
+
count: getDefaultNodeContentCount(nodeType),
|
|
43
|
+
withTitle: true,
|
|
44
|
+
tolerateErrors,
|
|
45
|
+
llmTaskName: nodeType.kebabName,
|
|
46
|
+
}, report);
|
|
47
|
+
const errorMessages = dedupMessages(tolerateErrors.errorMessages);
|
|
48
|
+
if (errorMessages.length > 0) {
|
|
49
|
+
ctx.logger.warn(`Error generating content for ${nodeType.typeName}:\n - ${errorMessages.join("\n - ")}`);
|
|
50
|
+
}
|
|
51
|
+
const insertedIds = await ctx.service.connector.addMultipleDocumentContents(fqdn, {
|
|
52
|
+
parentNodeId,
|
|
53
|
+
contents: list.map((content) => toRiDocumentContent(content, nodeType)),
|
|
54
|
+
});
|
|
55
|
+
report.addId({ typeName: nodeType.typeName, id: insertedIds, parentNodeId });
|
|
56
|
+
}
|
|
57
|
+
export async function addParts(ctx, report, siteOptions, nodeOptions) {
|
|
58
|
+
ctx.logger.debug(`[TASK] Adding parts "${nodeOptions.nodeType.typeName}"…`);
|
|
59
|
+
const { parentNodeId, nodeType, documentType } = nodeOptions;
|
|
60
|
+
const { fqdn, siteSchema, schemaI18n } = siteOptions;
|
|
61
|
+
const tolerateErrors = {
|
|
62
|
+
errorMessages: [],
|
|
63
|
+
};
|
|
64
|
+
const list = await generateMultipleFieldSetContents(ctx, {
|
|
65
|
+
siteSchema,
|
|
66
|
+
nodeType,
|
|
67
|
+
documentType,
|
|
68
|
+
schemaI18n,
|
|
69
|
+
count: getDefaultNodeContentCount(nodeType),
|
|
70
|
+
withTitle: true,
|
|
71
|
+
tolerateErrors,
|
|
72
|
+
llmTaskName: nodeType.kebabName,
|
|
73
|
+
}, report);
|
|
74
|
+
const errorMessages = dedupMessages(tolerateErrors.errorMessages);
|
|
75
|
+
if (errorMessages.length > 0) {
|
|
76
|
+
ctx.logger.warn(`Error generating content for ${nodeType.typeName}:\n - ${errorMessages.join("\n - ")}`);
|
|
77
|
+
}
|
|
78
|
+
const insertedIds = await ctx.service.connector.addMultiplePartContents(fqdn, {
|
|
79
|
+
parentNodeId,
|
|
80
|
+
contents: list.map((content) => toRiPartContent(content, nodeType)),
|
|
81
|
+
});
|
|
82
|
+
report.addId({ typeName: nodeType.typeName, id: insertedIds, parentNodeId });
|
|
83
|
+
}
|
|
84
|
+
function toRiDocumentContent(content, nodeType) {
|
|
85
|
+
const { title, fields, featuredImage } = content;
|
|
86
|
+
return {
|
|
87
|
+
kind: "document",
|
|
88
|
+
typeName: nodeType.typeName,
|
|
89
|
+
title,
|
|
90
|
+
featuredImage,
|
|
91
|
+
fields,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function toRiPartContent(content, nodeType) {
|
|
95
|
+
const { fields } = content;
|
|
96
|
+
return {
|
|
97
|
+
kind: "part",
|
|
98
|
+
typeName: nodeType.typeName,
|
|
99
|
+
fields,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function getDefaultNodeContentCount(nodeType) {
|
|
103
|
+
if (nodeType.kind === "site")
|
|
104
|
+
throw new Error("Cannot generate content for site node type");
|
|
105
|
+
if (nodeType.kind === "document") {
|
|
106
|
+
if (nodeType.documentKind === "routing")
|
|
107
|
+
return 1;
|
|
108
|
+
if (nodeType.route === ":yyyy/:mm/:dd/:relativeId-:slug")
|
|
109
|
+
return 14; // 4
|
|
110
|
+
return 8; // 2
|
|
111
|
+
}
|
|
112
|
+
if (nodeType.kind === "part")
|
|
113
|
+
return 8; // 2
|
|
114
|
+
throw new Error(`Unknown node type kind: ${nodeType.kind}`);
|
|
115
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getRandomImagePath } from "../lib/images-lib.js";
|
|
2
|
+
import { generateLocalizedFooterMention } from "./create-node-contents.js";
|
|
3
|
+
export async function updateSiteFields(ctx, _report, options) {
|
|
4
|
+
const { service, logger } = ctx;
|
|
5
|
+
logger.debug("Updating site fields…");
|
|
6
|
+
const { fqdn, siteSchema, siteTitle } = options;
|
|
7
|
+
const siteType = siteSchema.nodeTypes._site;
|
|
8
|
+
const logoImageFile = getRandomImagePath();
|
|
9
|
+
const fields = [
|
|
10
|
+
[
|
|
11
|
+
"title",
|
|
12
|
+
{
|
|
13
|
+
dataType: "string",
|
|
14
|
+
localized: true,
|
|
15
|
+
value: siteTitle,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
"favicon",
|
|
20
|
+
{
|
|
21
|
+
dataType: "media",
|
|
22
|
+
localized: false,
|
|
23
|
+
value: {
|
|
24
|
+
file: logoImageFile,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
[
|
|
29
|
+
"ogImage",
|
|
30
|
+
{
|
|
31
|
+
dataType: "media",
|
|
32
|
+
localized: false,
|
|
33
|
+
value: {
|
|
34
|
+
file: logoImageFile,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
];
|
|
39
|
+
if (siteType.fields.some((f) => f.name === "logo")) {
|
|
40
|
+
fields.push([
|
|
41
|
+
"logo",
|
|
42
|
+
{
|
|
43
|
+
dataType: "media",
|
|
44
|
+
localized: false,
|
|
45
|
+
value: {
|
|
46
|
+
file: logoImageFile,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
}
|
|
51
|
+
if (siteType.fields.some((f) => f.name === "footerMention")) {
|
|
52
|
+
fields.push([
|
|
53
|
+
"footerMention",
|
|
54
|
+
{
|
|
55
|
+
dataType: "json",
|
|
56
|
+
localized: true,
|
|
57
|
+
value: generateLocalizedFooterMention(siteSchema),
|
|
58
|
+
},
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
await service.connector.updateSiteFields(fqdn, Object.fromEntries(fields));
|
|
62
|
+
logger.debug("… Site fields updated");
|
|
63
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export async function updateNodelsWithTaxonomies(ctx, options) {
|
|
2
|
+
const { service } = ctx;
|
|
3
|
+
const { siteSchema, idPicker, fqdn } = options;
|
|
4
|
+
const { nodeTypes } = siteSchema;
|
|
5
|
+
for (const nodeType of Object.values(nodeTypes)) {
|
|
6
|
+
const labelingFields = nodeType.fields?.filter((f) => f.dataType === "labeling");
|
|
7
|
+
if (!labelingFields || labelingFields.length === 0)
|
|
8
|
+
continue;
|
|
9
|
+
await updateLabelingFields(ctx, {
|
|
10
|
+
idPicker,
|
|
11
|
+
fqdn,
|
|
12
|
+
nodeType,
|
|
13
|
+
labelingFields,
|
|
14
|
+
}, service);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function updateLabelingFields(ctx, options, service) {
|
|
18
|
+
const { fqdn, nodeType, labelingFields, idPicker } = options;
|
|
19
|
+
if (nodeType.kind !== "document" && nodeType.kind !== "part")
|
|
20
|
+
return;
|
|
21
|
+
const nodeIds = idPicker.pickNodeIds({ typeName: nodeType.typeName });
|
|
22
|
+
for (const nodeId of nodeIds) {
|
|
23
|
+
const fieldValues = [];
|
|
24
|
+
for (const field of labelingFields) {
|
|
25
|
+
const taxonomyIds = idPicker.pickNodeIds({ typeName: field.taxonomy });
|
|
26
|
+
if (taxonomyIds.length !== 1) {
|
|
27
|
+
if (taxonomyIds.length > 1) {
|
|
28
|
+
ctx.logger.warn(`Expected one taxonomy ID for "${field.taxonomy}", got ${taxonomyIds.length}`);
|
|
29
|
+
}
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const taxonomyNodeId = taxonomyIds[0];
|
|
33
|
+
const max = field.multiple ? Math.floor(Math.random() * 2) + 1 : 1;
|
|
34
|
+
const termNodeIds = idPicker.pickNodeIds({ parentNodeId: taxonomyNodeId }, max);
|
|
35
|
+
if (termNodeIds.length === 0)
|
|
36
|
+
continue;
|
|
37
|
+
fieldValues.push([
|
|
38
|
+
field.name,
|
|
39
|
+
{
|
|
40
|
+
dataType: "labeling",
|
|
41
|
+
localized: false,
|
|
42
|
+
value: { t: termNodeIds },
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
}
|
|
46
|
+
if (fieldValues.length === 0)
|
|
47
|
+
continue;
|
|
48
|
+
await service.connector.updateNodeContent(fqdn, {
|
|
49
|
+
nodeId,
|
|
50
|
+
content: {
|
|
51
|
+
kind: nodeType.kind,
|
|
52
|
+
typeName: nodeType.typeName,
|
|
53
|
+
fields: Object.fromEntries(fieldValues),
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -7,8 +7,8 @@ const contextContent = await readPromptFile("0-context.md");
|
|
|
7
7
|
const siteSchemaTsDefs = await readFile(join(connectorPackageDir, "typeonly", "site-schema-json-types.d.ts"), "utf-8");
|
|
8
8
|
const predefinedFields = JSON.parse(await readPromptFile("predefined-fields.json"));
|
|
9
9
|
export async function createPromptTemplate(options) {
|
|
10
|
-
const {
|
|
11
|
-
const promptContent = await readPromptFile(
|
|
10
|
+
const { filename, withSiteSchemaTsDefs } = options;
|
|
11
|
+
const promptContent = await readPromptFile(filename);
|
|
12
12
|
const schemaTypeDefTemplate = withSiteSchemaTsDefs
|
|
13
13
|
? `
|
|
14
14
|
|
|
@@ -29,8 +29,8 @@ ${promptContent}
|
|
|
29
29
|
`;
|
|
30
30
|
return buildPromptTemplate(template);
|
|
31
31
|
}
|
|
32
|
-
export async function readPromptFile(
|
|
33
|
-
return await readFile(join(projectDir, "prompts",
|
|
32
|
+
export async function readPromptFile(filename) {
|
|
33
|
+
return await readFile(join(projectDir, "prompts", filename), "utf-8");
|
|
34
34
|
}
|
|
35
35
|
export function getPredefinedFields() {
|
|
36
36
|
if (!predefinedFields)
|
|
@@ -5,7 +5,7 @@ import { createPromptTemplate } from "../lib/create-prompt.js";
|
|
|
5
5
|
import { debugLlmOutput } from "../lib/debug-utils.js";
|
|
6
6
|
import { parseLlmResponseAsProperties } from "../lib/parse-llm-response.js";
|
|
7
7
|
const guardPrompt = await createPromptTemplate({
|
|
8
|
-
|
|
8
|
+
filename: "message-guard.md",
|
|
9
9
|
});
|
|
10
10
|
const invalidCauses = new Set(["rude", "malicious", "outOfScope", "technicalLimits", "noSense"]);
|
|
11
11
|
export async function invokeMessageGuard(ctx, input, { skipOutOfScopeCheck = false } = {}) {
|
|
@@ -11,11 +11,11 @@ import { createL10n } from "../site-schema-generator/create-l10n.js";
|
|
|
11
11
|
import { createSiteSchemaFromAnalysis } from "../site-schema-generator/create-site-schema.js";
|
|
12
12
|
import { invokeUpdateSiteSchema } from "./invoke-update-site-schema.js";
|
|
13
13
|
export const analyzePrompt = await createPromptTemplate({
|
|
14
|
-
|
|
14
|
+
filename: "initial-1-analysis.md",
|
|
15
15
|
withSiteSchemaTsDefs: true,
|
|
16
16
|
});
|
|
17
17
|
const fieldsPrompt = await createPromptTemplate({
|
|
18
|
-
|
|
18
|
+
filename: "initial-2-fields.md",
|
|
19
19
|
withSiteSchemaTsDefs: true,
|
|
20
20
|
});
|
|
21
21
|
export async function startInitialAnalysis(ctx, input) {
|
|
@@ -6,10 +6,10 @@ import { debugLlmOutput } from "../lib/debug-utils.js";
|
|
|
6
6
|
import { parseLlmResponseAsProperties } from "../lib/parse-llm-response.js";
|
|
7
7
|
import { safeCallStep } from "../lib/session-utils.js";
|
|
8
8
|
const prompt1Tpl = await createPromptTemplate({
|
|
9
|
-
|
|
9
|
+
filename: "update-site-schema-1-write-details.md",
|
|
10
10
|
});
|
|
11
11
|
const prompt2Tpl = await createPromptTemplate({
|
|
12
|
-
|
|
12
|
+
filename: "update-site-schema-2-execute.md",
|
|
13
13
|
});
|
|
14
14
|
export async function startUpdateSiteSchema(ctx, input) {
|
|
15
15
|
const fromStepSchema = await readStepSchema(ctx, input.fromStepNumber);
|
|
@@ -37,7 +37,7 @@ function templateOfSiteLogoTitle(ctx) {
|
|
|
37
37
|
const content = [
|
|
38
38
|
siteType.fields?.includes("logo")
|
|
39
39
|
? `{% if site.field.logo %}
|
|
40
|
-
{% useImage logo
|
|
40
|
+
{% useImage logo image: site.field.logo resize: "50x50" %}
|
|
41
41
|
<img
|
|
42
42
|
src="{{ logo.url }}"
|
|
43
43
|
width="{{ logo.width }}"
|
|
@@ -197,7 +197,7 @@ function templateOfField(ctx, fieldOrName, parentKey) {
|
|
|
197
197
|
if (dataType === "gallery") {
|
|
198
198
|
return `<div class="Field">
|
|
199
199
|
{% for media in ${parentKey}.${fieldName} %}
|
|
200
|
-
{% useImage im
|
|
200
|
+
{% useImage im image: media resize: "150x150" %}
|
|
201
201
|
<img
|
|
202
202
|
class="Field-img"
|
|
203
203
|
src="{{ im.url }}"
|
|
@@ -205,13 +205,14 @@ function templateOfField(ctx, fieldOrName, parentKey) {
|
|
|
205
205
|
height="{{ im.height }}"
|
|
206
206
|
loading="lazy"
|
|
207
207
|
alt=""
|
|
208
|
-
|
|
208
|
+
{{ media | zoomable }}
|
|
209
|
+
>
|
|
209
210
|
{% endfor %}
|
|
210
211
|
</div>`;
|
|
211
212
|
}
|
|
212
213
|
if (dataType === "media") {
|
|
213
|
-
const
|
|
214
|
-
return `{% useImage im
|
|
214
|
+
const mediaKey = `${parentKey}.${fieldName}`;
|
|
215
|
+
return `{% useImage im image: ${mediaKey} resize: "x250x" %}
|
|
215
216
|
<div class="Field">
|
|
216
217
|
<img
|
|
217
218
|
class="Field-img"
|
|
@@ -220,7 +221,8 @@ function templateOfField(ctx, fieldOrName, parentKey) {
|
|
|
220
221
|
height="{{ im.height }}"
|
|
221
222
|
loading="lazy"
|
|
222
223
|
alt=""
|
|
223
|
-
|
|
224
|
+
{{ ${mediaKey} | zoomable }}
|
|
225
|
+
>
|
|
224
226
|
</div>`;
|
|
225
227
|
}
|
|
226
228
|
if (fieldName === "title") {
|
|
@@ -230,15 +232,15 @@ function templateOfField(ctx, fieldOrName, parentKey) {
|
|
|
230
232
|
}
|
|
231
233
|
function templateOfPicture({ imageKey }) {
|
|
232
234
|
return `{% if ${imageKey} %}
|
|
233
|
-
{% useImage smallIm
|
|
234
|
-
{% useImage largeIm
|
|
235
|
+
{% useImage smallIm image: ${imageKey} resize: "360x48" %}
|
|
236
|
+
{% useImage largeIm image: ${imageKey} resize: "1200x160" %}
|
|
235
237
|
<div class="Container">
|
|
236
238
|
<picture class="Hero">
|
|
237
239
|
<source
|
|
238
240
|
srcset="{{ largeIm.url }}"
|
|
239
241
|
width="{{ largeIm.width }}"
|
|
240
242
|
height="{{ largeIm.height }}"
|
|
241
|
-
media="(min-width:
|
|
243
|
+
media="(min-width: 361px)">
|
|
242
244
|
<img
|
|
243
245
|
src="{{ smallIm.url }}"
|
|
244
246
|
width="{{ smallIm.width }}"
|
|
@@ -327,7 +329,7 @@ function templateOfDocumentTile(docVariableName) {
|
|
|
327
329
|
<article>
|
|
328
330
|
{% if ${docVariableName}.defaultImage %}
|
|
329
331
|
<div>
|
|
330
|
-
{% useImage im
|
|
332
|
+
{% useImage im image: ${docVariableName}.defaultImage resize: "120x120" %}
|
|
331
333
|
<img
|
|
332
334
|
src="{{ im.url }}"
|
|
333
335
|
width="{{ im.width }}"
|
|
@@ -4,7 +4,7 @@ import { mkdir, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { loadStep, readStepSchema } from "../../db/db-read.queries.js";
|
|
6
6
|
import { insertStep, saveGeneratedSiteStep, } from "../../db/db-write.queries.js";
|
|
7
|
-
import { fillSiteWithFakeContent } from "../fake-content-generator
|
|
7
|
+
import { fillSiteWithFakeContent } from "../fake-content-generator/create-database-with-fake-content.js";
|
|
8
8
|
import { safeCallStep } from "../lib/session-utils.js";
|
|
9
9
|
import { createTheme } from "./theme-creator.js";
|
|
10
10
|
export async function generateSite(ctx, input) {
|
|
@@ -78,8 +78,10 @@ function getPackageJsonContent(options) {
|
|
|
78
78
|
dependencies: {
|
|
79
79
|
"@paroicms/contact-form-plugin": "*",
|
|
80
80
|
"@paroicms/content-loading-plugin": "*",
|
|
81
|
+
"@paroicms/internal-link-plugin": "*",
|
|
81
82
|
"@paroicms/public-menu-plugin": "*",
|
|
82
83
|
"@paroicms/quill-editor-plugin": "*",
|
|
84
|
+
"@paroicms/video-plugin": "*",
|
|
83
85
|
"@paroicms/server": "*",
|
|
84
86
|
},
|
|
85
87
|
devDependencies: {
|
|
@@ -59,12 +59,12 @@ function createThemeCreatorContext(siteSchema) {
|
|
|
59
59
|
f[language] = value;
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
|
-
hasLiquidFile(directory,
|
|
63
|
-
const path = directory === "root" ? `templates/${
|
|
62
|
+
hasLiquidFile(directory, filename) {
|
|
63
|
+
const path = directory === "root" ? `templates/${filename}` : `templates/${directory}/${filename}`;
|
|
64
64
|
return liquidFiles.has(path);
|
|
65
65
|
},
|
|
66
|
-
addLiquidFile(directory,
|
|
67
|
-
const path = directory === "root" ? `templates/${
|
|
66
|
+
addLiquidFile(directory, filename, content, { skipIfExists = false } = {}) {
|
|
67
|
+
const path = directory === "root" ? `templates/${filename}` : `templates/${directory}/${filename}`;
|
|
68
68
|
if (liquidFiles.has(path)) {
|
|
69
69
|
if (skipIfExists)
|
|
70
70
|
return;
|
|
@@ -216,27 +216,35 @@ nav a.active::after {
|
|
|
216
216
|
display: block;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
.Text .
|
|
220
|
-
|
|
219
|
+
.Text .Fig {
|
|
220
|
+
display: table;
|
|
221
|
+
margin: 0;
|
|
221
222
|
}
|
|
222
|
-
|
|
223
|
-
.Text .Img.left {
|
|
223
|
+
.Text .Fig.left {
|
|
224
224
|
float: left;
|
|
225
225
|
margin: 5px 20px 10px 0;
|
|
226
226
|
}
|
|
227
|
-
|
|
228
|
-
.Text .Img.right {
|
|
227
|
+
.Text .Fig.right {
|
|
229
228
|
float: right;
|
|
230
229
|
margin: 5px 0 10px 20px;
|
|
231
230
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
231
|
+
.Text .Fig.center {
|
|
232
|
+
clear: both;
|
|
233
|
+
margin: 20px auto;
|
|
235
234
|
}
|
|
236
|
-
|
|
237
|
-
.Text .Img.center {
|
|
235
|
+
.Text .Fig-media {
|
|
238
236
|
display: block;
|
|
239
|
-
|
|
237
|
+
height: auto;
|
|
238
|
+
max-height: 100%;
|
|
239
|
+
max-width: 100%;
|
|
240
|
+
}
|
|
241
|
+
.Text .Fig-caption {
|
|
242
|
+
caption-side: bottom;
|
|
243
|
+
color: #666;
|
|
244
|
+
display: table-caption;
|
|
245
|
+
font-size: 0.875rem;
|
|
246
|
+
margin-top: 8px;
|
|
247
|
+
text-align: center;
|
|
240
248
|
}
|
|
241
249
|
|
|
242
250
|
.Text a,
|
|
@@ -5,10 +5,11 @@ export function createSiteSchemaFromAnalysis(analysis) {
|
|
|
5
5
|
version: "8",
|
|
6
6
|
languages: [siteProperties.language],
|
|
7
7
|
plugins: [
|
|
8
|
-
"@paroicms/
|
|
8
|
+
"@paroicms/contact-form-plugin",
|
|
9
9
|
"@paroicms/content-loading-plugin",
|
|
10
|
+
"@paroicms/internal-link-plugin",
|
|
10
11
|
"@paroicms/public-menu-plugin",
|
|
11
|
-
"@paroicms/
|
|
12
|
+
"@paroicms/quill-editor-plugin",
|
|
12
13
|
"@paroicms/video-plugin",
|
|
13
14
|
],
|
|
14
15
|
nodeTypes: [
|
|
@@ -227,6 +228,11 @@ function toRoutingDocumentNodeType(typeName, saType) {
|
|
|
227
228
|
ogType: saType.ogType,
|
|
228
229
|
route: generateSlug(saType.label),
|
|
229
230
|
withFeaturedImage: typeName !== "home" && !!saType.entryPage,
|
|
231
|
+
backOffice: saType.taxonomy
|
|
232
|
+
? {
|
|
233
|
+
menuPlacement: "popup",
|
|
234
|
+
}
|
|
235
|
+
: undefined,
|
|
230
236
|
};
|
|
231
237
|
}
|
|
232
238
|
function toRegularDocumentNodeType(typeName, saType) {
|
|
@@ -126,6 +126,7 @@ Guidelines for creating the dictionnary YAML:
|
|
|
126
126
|
- confidence: Your confidence level for the accuracy of this node type (0.0-1.0).
|
|
127
127
|
- kind: Can be `routingDocument`, `regularDocument`, `part`.
|
|
128
128
|
- entryPage: A Boolean indicating whether this is one of the very few first pages the visitor lands on (optional, routing document only).
|
|
129
|
+
- taxonomy: A boolean to indicate if this is a taxonomy (optional, routing document only).
|
|
129
130
|
- temporal: A boolean to indicate if there is a temporal nature for this type of node (optional, regular document only).
|
|
130
131
|
- ogType: (optional, and document only) If you think of a particular Open-Graph type for this document, give it here.
|
|
131
132
|
- label: A label of the node type, in the _website language_.
|
|
@@ -199,12 +200,13 @@ post:
|
|
|
199
200
|
tags:
|
|
200
201
|
confidence: 0.9
|
|
201
202
|
kind: routingDocument
|
|
203
|
+
taxonomy: true
|
|
202
204
|
label: Tags
|
|
203
205
|
description: Tags taxonomy for post documents.
|
|
204
206
|
tag:
|
|
205
207
|
confidence: 0.9
|
|
206
208
|
kind: regularDocument
|
|
207
|
-
temporal:
|
|
209
|
+
temporal: false
|
|
208
210
|
label: Tag
|
|
209
211
|
description: A tag is a term in the tags taxonomy.
|
|
210
212
|
</correct_example>
|