since3-portal/modules/templates/ai/knowledge.ts
2026-10-11 18:22:32 +02:00

80 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { brandName } from "../lib/content"
import type { FieldDef, ListFieldDef, SectionData, TemplateContent, TemplateDefinition } from "../lib/types"
const clean = (value: string) => value.replace(/\*/g, "").replace(/\s*\n\s*/g, " · ").trim()
type PageSection = { label: string; fields: (FieldDef | ListFieldDef)[]; data: SectionData | undefined; hidden: boolean }
// Feste Sektionen der Vorlage und – im Baukasten – die Bausteine in ihrer Reihenfolge
function pageSections(def: TemplateDefinition, content: TemplateContent): PageSection[] {
const fixed = def.sections.map((section) => ({
label: section.label,
fields: section.fields,
data: content.data[section.key],
hidden: content.hidden.includes(section.key),
}))
const library = new Map(def.builder?.library.map((block) => [block.type, block]))
const blocks = content.blocks.flatMap((block) => {
const definition = library.get(block.type)
if (!definition) return []
const menu = typeof block.data.menu === "string" ? block.data.menu.trim() : ""
return [{ label: menu || definition.label, fields: definition.fields, data: block.data, hidden: block.hidden }]
})
return [...fixed, ...blocks]
}
export function businessName(def: TemplateDefinition, content: TemplateContent) {
return brandName(content) ?? def.name
}
// Alles, was auf der Webseite steht, als Wissen für die KI – Bilder ausgenommen, ausgeblendete Sektionen auch
export function buildKnowledge(def: TemplateDefinition, content: TemplateContent) {
const parts = [`Betrieb: ${businessName(def, content)}`, `Branche: ${def.industry}`]
for (const section of pageSections(def, content)) {
const data = section.data
if (!data || section.hidden) continue
const lines: string[] = []
for (const field of section.fields) {
const value = data[field.key]
if (field.type === "list") {
if (!Array.isArray(value)) continue
const items = value
.map((item) =>
field.fields
.filter((sub) => sub.type !== "image" && item[sub.key]?.trim())
.map((sub) => `${sub.label}: ${clean(item[sub.key])}`)
.join("; ")
)
.filter(Boolean)
if (items.length) lines.push(`${field.label}:\n${items.map((item) => `- ${item}`).join("\n")}`)
} else if (field.type !== "image" && typeof value === "string" && value.trim()) {
lines.push(`${field.label}: ${clean(value)}`)
}
}
if (lines.length) parts.push(`\n## ${section.label}\n${lines.join("\n")}`)
}
if (content.ai.knowledge.trim()) parts.push(`\n## Zusatzwissen\n${content.ai.knowledge.trim()}`)
return parts.join("\n")
}
// Bild-Links der Variante, z. B. für die Post-Vorschauen im Content-Generator
export function collectImages(def: TemplateDefinition, content: TemplateContent) {
const images: string[] = []
for (const section of pageSections(def, content)) {
const data = section.data
if (!data) continue
for (const field of section.fields) {
const value = data[field.key]
if (field.type === "image" && typeof value === "string") images.push(value)
if (field.type === "list" && Array.isArray(value)) {
for (const sub of field.fields.filter((f) => f.type === "image")) {
images.push(...value.map((item) => item[sub.key] ?? ""))
}
}
}
}
return [...new Set(images.filter((src) => src.startsWith("http")))]
}