feat: implement Theme Builder concept and Display Conditions resolver system

This commit is contained in:
Daniel S
2026-08-09 12:53:25 +02:00
parent 3453746fc0
commit b039aceb21
3 changed files with 201 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
export interface DisplayCondition {
type: 'include' | 'exclude';
scope: 'entire_site' | 'singular' | 'archive' | 'page_404';
entity?: 'page' | 'post' | 'category';
entityId?: string;
}
export interface TemplateMeta {
id: string;
name: string;
type: 'header' | 'footer' | 'single' | 'archive' | '404';
status: 'draft' | 'published';
priority: number;
conditions: DisplayCondition[];
createdAt?: string;
updatedAt?: string;
}
/**
* Resolves which template to use based on target type and request context conditions.
* Specific matches (singular/page_404) take precedence over general 'entire_site' matches.
*/
export function resolveTemplate(
templates: TemplateMeta[],
targetType: TemplateMeta['type'],
context: { url: string; routeType: 'singular' | 'archive' | '404' | 'home'; entityId?: string }
): TemplateMeta | null {
const candidates = templates.filter(t => t.type === targetType && t.status === 'published');
let bestMatch: TemplateMeta | null = null;
let highestScore = -1;
for (const tpl of candidates) {
let isIncluded = false;
let isExcluded = false;
let score = 0;
for (const cond of tpl.conditions) {
if (cond.type === 'exclude') {
if (cond.scope === 'entire_site') isExcluded = true;
if (cond.scope === 'singular' && context.routeType === 'singular' && (!cond.entityId || cond.entityId === context.entityId)) {
isExcluded = true;
}
if (cond.scope === 'page_404' && context.routeType === '404') isExcluded = true;
} else if (cond.type === 'include') {
if (cond.scope === 'entire_site') {
isIncluded = true;
score = Math.max(score, tpl.priority || 1);
} else if (cond.scope === 'singular' && context.routeType === 'singular') {
if (!cond.entityId || cond.entityId === context.entityId) {
isIncluded = true;
score = Math.max(score, (tpl.priority || 1) + 10);
}
} else if (cond.scope === 'page_404' && context.routeType === '404') {
isIncluded = true;
score = Math.max(score, (tpl.priority || 1) + 10);
}
}
}
if (isIncluded && !isExcluded && score > highestScore) {
highestScore = score;
bestMatch = tpl;
}
}
return bestMatch;
}