fix: systematic element matching bug in enhancement pipeline

- Problem: Element ID collisions between similar elements (logo h1 vs hero h1)
  causing content to be injected into wrong elements
- Root cause: Enhancer used naive tag+class matching instead of parser's
  sophisticated semantic analysis for element identification

Systematic solution:
- Enhanced parser architecture with exported utilities (GetClasses, ContainsClass)
- Added FindElementInDocument() with content-based semantic matching
- Replaced naive findAndInjectNodes() with parser-based element matching
- Removed code duplication between parser and enhancer packages

Backend improvements:
- Moved ID generation to backend for single source of truth
- Added ElementContext struct for frontend-backend communication
- Updated API handlers to support context-based content ID generation

Frontend improvements:
- Enhanced getElementMetadata() to extract semantic context
- Updated save flow to handle both enhanced and non-enhanced elements
- Improved API client to use backend-generated content IDs

Result:
- Unique content IDs: navbar-logo-200530 vs hero-title-a1de7b
- Precise element matching using content validation
- Single source of truth for DOM utilities in parser package
- Eliminated 40+ lines of duplicate code while fixing core bug
This commit is contained in:
2025-09-11 14:14:57 +02:00
parent f73e21ce6e
commit ef1d1083ce
12 changed files with 575 additions and 314 deletions

View File

@@ -58,27 +58,39 @@ export class ApiClient {
}
}
async createContent(contentId, content, type) {
async createContent(contentId, content, type, elementContext = null) {
try {
const payload = {
value: content,
type: type
};
if (contentId) {
// Enhanced site - provide existing ID
payload.id = contentId;
} else if (elementContext) {
// Non-enhanced site - provide context for backend ID generation
payload.element_context = elementContext;
} else {
throw new Error('Either contentId or elementContext must be provided');
}
const response = await fetch(`${this.baseUrl}?site_id=${this.siteId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.getAuthToken()}`
},
body: JSON.stringify({
id: contentId,
value: content,
type: type
})
body: JSON.stringify(payload)
});
if (response.ok) {
console.log(`✅ Content created: ${contentId} (${type})`);
return true;
const result = await response.json();
console.log(`✅ Content created: ${result.id} (${result.type})`);
return result;
} else {
console.warn(`⚠️ Create failed (${response.status}): ${contentId}`);
return false;
console.warn(`⚠️ Create failed (${response.status}): ${contentId || 'backend-generated'}`);
return null;
}
} catch (error) {
if (error.name === 'TypeError' && error.message.includes('fetch')) {