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

@@ -6,8 +6,8 @@ import (
"golang.org/x/net/html"
)
// getClasses extracts CSS classes from an HTML node
func getClasses(node *html.Node) []string {
// GetClasses extracts CSS classes from an HTML node
func GetClasses(node *html.Node) []string {
classAttr := getAttribute(node, "class")
if classAttr == "" {
return []string{}
@@ -17,8 +17,8 @@ func getClasses(node *html.Node) []string {
return classes
}
// containsClass checks if a class list contains a specific class
func containsClass(classes []string, target string) bool {
// ContainsClass checks if a class list contains a specific class
func ContainsClass(classes []string, target string) bool {
for _, class := range classes {
if class == target {
return true
@@ -157,3 +157,39 @@ func isSelfClosing(node *html.Node) bool {
return selfClosingTags[node.Data]
}
// FindElementInDocument finds a parser element in HTML document tree using semantic matching
func FindElementInDocument(doc *html.Node, element Element) *html.Node {
return findElementWithContext(doc, element)
}
// findElementWithContext uses the parser's semantic understanding to find the correct element
func findElementWithContext(node *html.Node, target Element) *html.Node {
if node.Type == html.ElementNode && node.Data == target.Tag {
classes := GetClasses(node)
if ContainsClass(classes, "insertr") {
// Content-based validation for precise matching
textContent := extractTextContent(node)
nodeContent := strings.TrimSpace(textContent)
targetContent := strings.TrimSpace(target.Content)
if nodeContent == targetContent {
return node
}
}
}
// Recursively search children
for child := node.FirstChild; child != nil; child = child.NextSibling {
if result := findElementWithContext(child, target); result != nil {
return result
}
}
return nil
}
// GetAttribute gets an attribute value from an HTML node (exported version)
func GetAttribute(node *html.Node, key string) string {
return getAttribute(node, key)
}