@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.
Files changed (26) hide show
  1. package/gen-backend/dist/generator/fake-content-generator/content-report.js +81 -0
  2. package/gen-backend/dist/generator/fake-content-generator/create-database-with-fake-content.js +80 -0
  3. package/gen-backend/dist/generator/fake-content-generator/fill-nodels.js +115 -0
  4. package/gen-backend/dist/generator/fake-content-generator/fill-site-fields.js +63 -0
  5. package/gen-backend/dist/generator/fake-content-generator/fill-taxonomy-fields.js +57 -0
  6. package/gen-backend/dist/generator/lib/create-prompt.js +4 -4
  7. package/gen-backend/dist/generator/llm-queries/invoke-message-guard.js +1 -1
  8. package/gen-backend/dist/generator/llm-queries/invoke-new-site-analysis.js +2 -2
  9. package/gen-backend/dist/generator/llm-queries/invoke-update-site-schema.js +2 -2
  10. package/gen-backend/dist/generator/site-generator/common-template-creator.js +1 -1
  11. package/gen-backend/dist/generator/site-generator/document-template-creator.js +11 -9
  12. package/gen-backend/dist/generator/site-generator/site-generator.js +3 -1
  13. package/gen-backend/dist/generator/site-generator/theme-creator.js +4 -4
  14. package/gen-backend/dist/generator/site-generator/theme-css.js +20 -12
  15. package/gen-backend/dist/generator/site-schema-generator/create-site-schema.js +8 -2
  16. package/gen-backend/prompts/initial-1-analysis.md +3 -1
  17. package/gen-front/dist/gen-front.css +1 -1
  18. package/gen-front/dist/gen-front.mjs +45 -47
  19. package/package.json +10 -8
  20. package/gen-backend/dist/generator/fake-content-generator.ts/content-report.js +0 -15
  21. package/gen-backend/dist/generator/fake-content-generator.ts/create-database-with-fake-content.js +0 -238
  22. /package/gen-backend/dist/generator/{fake-content-generator.ts → fake-content-generator}/content-helpers.js +0 -0
  23. /package/gen-backend/dist/generator/{fake-content-generator.ts → fake-content-generator}/create-node-contents.js +0 -0
  24. /package/gen-backend/dist/generator/{fake-content-generator.ts → fake-content-generator}/fake-content-types.js +0 -0
  25. /package/gen-backend/dist/generator/{fake-content-generator.ts → fake-content-generator}/generate-fake-content.js +0 -0
  26. /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
+ }
@@ -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 { fileName, withSiteSchemaTsDefs } = options;
11
- const promptContent = await readPromptFile(fileName);
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(fileName) {
33
- return await readFile(join(projectDir, "prompts", fileName), "utf-8");
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
- fileName: "message-guard.md",
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
- fileName: "initial-1-analysis.md",
14
+ filename: "initial-1-analysis.md",
15
15
  withSiteSchemaTsDefs: true,
16
16
  });
17
17
  const fieldsPrompt = await createPromptTemplate({
18
- fileName: "initial-2-fields.md",
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
- fileName: "update-site-schema-1-write-details.md",
9
+ filename: "update-site-schema-1-write-details.md",
10
10
  });
11
11
  const prompt2Tpl = await createPromptTemplate({
12
- fileName: "update-site-schema-2-execute.md",
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 uid: site.field.logo.uid size: "50x50" %}
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 uid: media.uid size: "150x150" %}
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
- data-zoom-src="{{ media.uid | imageZoomUrl }}">
208
+ {{ media | zoomable }}
209
+ >
209
210
  {% endfor %}
210
211
  </div>`;
211
212
  }
212
213
  if (dataType === "media") {
213
- const mediaUidKey = `${parentKey}.${fieldName}.uid`;
214
- return `{% useImage im uid:${mediaUidKey} size: "x250x" %}
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
- data-zoom-src="{{ ${mediaUidKey} | imageZoomUrl }}">>
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 uid: ${imageKey}.uid size: "360x48" %}
234
- {% useImage largeIm uid: ${imageKey}.uid size: "1200x160" %}
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: 360px)">
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 uid: ${docVariableName}.defaultImage.uid size: "120x120" %}
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.ts/create-database-with-fake-content.js";
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, fileName) {
63
- const path = directory === "root" ? `templates/${fileName}` : `templates/${directory}/${fileName}`;
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, fileName, content, { skipIfExists = false } = {}) {
67
- const path = directory === "root" ? `templates/${fileName}` : `templates/${directory}/${fileName}`;
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 .Img {
220
- height: auto;
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
- .Text .Img.left, .Text .Img.right {
234
- max-width: 50%;
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
- margin: 20px auto;
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/quill-editor-plugin",
8
+ "@paroicms/contact-form-plugin",
9
9
  "@paroicms/content-loading-plugin",
10
+ "@paroicms/internal-link-plugin",
10
11
  "@paroicms/public-menu-plugin",
11
- "@paroicms/contact-form-plugin",
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: true
209
+ temporal: false
208
210
  label: Tag
209
211
  description: A tag is a term in the tags taxonomy.
210
212
  </correct_example>