Implement unified Discoverer + Enhancer architecture
- Rename AutoEnhancer to Discoverer with clear element discovery focus - Implement unified enhancement pipeline in Enhancer: * Phase 1: Element Discovery (configurable, respects existing insertr classes) * Phase 2: ID Generation via engine * Phase 3: Content Injection via engine - Add EnhancementConfig and DiscoveryConfig for flexible configuration - Update all method names and references (discoverNode, DiscoveryResult, etc.) - Support both manual class insertion and automatic discovery - Maintain single enhance command interface while providing unified internal pipeline - Update all constructors to use new configuration-based approach This establishes the clean Discoverer + Enhancer architecture discussed, with discovery as configurable first phase and enhancement as unified pipeline.
This commit is contained in:
@@ -53,11 +53,11 @@ func runAutoEnhance(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
|
|
||||||
// Create auto enhancer
|
// Create discoverer
|
||||||
enhancer := content.NewAutoEnhancer()
|
discoverer := content.NewDiscoverer()
|
||||||
|
|
||||||
// Run auto enhancement
|
// Run element discovery
|
||||||
result, err := enhancer.EnhanceDirectory(inputDir, autoEnhanceOutput, autoEnhanceAggressive)
|
result, err := discoverer.DiscoverDirectory(inputDir, autoEnhanceOutput, autoEnhanceAggressive)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("auto-enhancement failed: %w", err)
|
return fmt.Errorf("auto-enhancement failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func runEnhance(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create enhancer
|
// Create enhancer
|
||||||
enhancer := content.NewEnhancer(client, siteID)
|
enhancer := content.NewDefaultEnhancer(client, siteID)
|
||||||
|
|
||||||
fmt.Printf("🚀 Starting enhancement process...\n")
|
fmt.Printf("🚀 Starting enhancement process...\n")
|
||||||
fmt.Printf("📁 Input: %s\n", inputDir)
|
fmt.Printf("📁 Input: %s\n", inputDir)
|
||||||
|
|||||||
@@ -11,18 +11,18 @@ import (
|
|||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AutoEnhancer handles automatic enhancement of HTML files
|
// Discoverer handles automatic discovery of editable elements in HTML
|
||||||
type AutoEnhancer struct {
|
type Discoverer struct {
|
||||||
// Remove parser dependency - auto enhancement is now self-contained
|
// Element discovery is now self-contained and configurable
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAutoEnhancer creates a new AutoEnhancer instance
|
// NewDiscoverer creates a new Discoverer instance
|
||||||
func NewAutoEnhancer() *AutoEnhancer {
|
func NewDiscoverer() *Discoverer {
|
||||||
return &AutoEnhancer{}
|
return &Discoverer{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoEnhanceResult contains statistics about auto-enhancement
|
// DiscoveryResult contains statistics about element discovery
|
||||||
type AutoEnhanceResult struct {
|
type DiscoveryResult struct {
|
||||||
FilesProcessed int
|
FilesProcessed int
|
||||||
ElementsEnhanced int
|
ElementsEnhanced int
|
||||||
ContainersAdded int
|
ContainersAdded int
|
||||||
@@ -31,9 +31,9 @@ type AutoEnhanceResult struct {
|
|||||||
EnhancedFiles []string
|
EnhancedFiles []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnhanceDirectory automatically enhances all HTML files in a directory
|
// DiscoverDirectory discovers editable elements in all HTML files in a directory
|
||||||
func (ae *AutoEnhancer) EnhanceDirectory(inputDir, outputDir string, aggressive bool) (*AutoEnhanceResult, error) {
|
func (disc *Discoverer) DiscoverDirectory(inputDir, outputDir string, aggressive bool) (*DiscoveryResult, error) {
|
||||||
result := &AutoEnhanceResult{
|
result := &DiscoveryResult{
|
||||||
SkippedFiles: []string{},
|
SkippedFiles: []string{},
|
||||||
EnhancedFiles: []string{},
|
EnhancedFiles: []string{},
|
||||||
}
|
}
|
||||||
@@ -56,20 +56,20 @@ func (ae *AutoEnhancer) EnhanceDirectory(inputDir, outputDir string, aggressive
|
|||||||
// Only process HTML files
|
// Only process HTML files
|
||||||
if !strings.HasSuffix(strings.ToLower(path), ".html") {
|
if !strings.HasSuffix(strings.ToLower(path), ".html") {
|
||||||
// Copy non-HTML files as-is
|
// Copy non-HTML files as-is
|
||||||
return ae.copyFile(path, inputDir, outputDir)
|
return disc.copyFile(path, inputDir, outputDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enhance HTML file
|
// Discover elements in HTML file
|
||||||
enhanced, err := ae.enhanceFile(path, aggressive)
|
enhanced, err := disc.discoverFile(path, aggressive)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.SkippedFiles = append(result.SkippedFiles, path)
|
result.SkippedFiles = append(result.SkippedFiles, path)
|
||||||
// Copy original file on error
|
// Copy original file on error
|
||||||
return ae.copyFile(path, inputDir, outputDir)
|
return disc.copyFile(path, inputDir, outputDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write enhanced file
|
// Write enhanced file
|
||||||
outputPath := ae.getOutputPath(path, inputDir, outputDir)
|
outputPath := disc.getOutputPath(path, inputDir, outputDir)
|
||||||
if err := ae.writeEnhancedFile(outputPath, enhanced); err != nil {
|
if err := disc.writeEnhancedFile(outputPath, enhanced); err != nil {
|
||||||
return fmt.Errorf("failed to write enhanced file %s: %w", outputPath, err)
|
return fmt.Errorf("failed to write enhanced file %s: %w", outputPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,16 +85,16 @@ func (ae *AutoEnhancer) EnhanceDirectory(inputDir, outputDir string, aggressive
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnhancementResult contains details about a single file enhancement
|
// FileDiscoveryResult contains details about a single file discovery
|
||||||
type EnhancementResult struct {
|
type FileDiscoveryResult struct {
|
||||||
ElementsEnhanced int
|
ElementsEnhanced int
|
||||||
ContainersAdded int
|
ContainersAdded int
|
||||||
IndividualsAdded int
|
IndividualsAdded int
|
||||||
Document *html.Node
|
Document *html.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
// enhanceFile processes a single HTML file and adds insertr classes
|
// discoverFile processes a single HTML file and adds insertr classes
|
||||||
func (ae *AutoEnhancer) enhanceFile(filePath string, aggressive bool) (*EnhancementResult, error) {
|
func (disc *Discoverer) discoverFile(filePath string, aggressive bool) (*FileDiscoveryResult, error) {
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error opening file: %w", err)
|
return nil, fmt.Errorf("error opening file: %w", err)
|
||||||
@@ -106,35 +106,35 @@ func (ae *AutoEnhancer) enhanceFile(filePath string, aggressive bool) (*Enhancem
|
|||||||
return nil, fmt.Errorf("error parsing HTML: %w", err)
|
return nil, fmt.Errorf("error parsing HTML: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result := &EnhancementResult{Document: doc}
|
result := &FileDiscoveryResult{Document: doc}
|
||||||
|
|
||||||
// Find candidates for enhancement
|
// Find candidates for enhancement
|
||||||
ae.enhanceNode(doc, result, aggressive)
|
disc.discoverNode(doc, result, aggressive)
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// enhanceNode recursively enhances nodes in the document
|
// discoverNode recursively discovers editable nodes in the document
|
||||||
func (ae *AutoEnhancer) enhanceNode(node *html.Node, result *EnhancementResult, aggressive bool) {
|
func (disc *Discoverer) discoverNode(node *html.Node, result *FileDiscoveryResult, aggressive bool) {
|
||||||
if node.Type != html.ElementNode {
|
if node.Type != html.ElementNode {
|
||||||
// Recursively check children
|
// Recursively check children
|
||||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||||
ae.enhanceNode(child, result, aggressive)
|
disc.discoverNode(child, result, aggressive)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if already has insertr class
|
// Skip if already has insertr class
|
||||||
if ae.hasInsertrClass(node) {
|
if disc.hasInsertrClass(node) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a container that should use expansion
|
// Check if this is a container that should use expansion
|
||||||
if ae.isGoodContainer(node) {
|
if disc.isGoodContainer(node) {
|
||||||
viableChildren := engine.FindViableChildren(node)
|
viableChildren := engine.FindViableChildren(node)
|
||||||
if len(viableChildren) >= 2 || (aggressive && len(viableChildren) >= 1) {
|
if len(viableChildren) >= 2 || (aggressive && len(viableChildren) >= 1) {
|
||||||
// Add insertr class to container for expansion
|
// Add insertr class to container for expansion
|
||||||
ae.addInsertrClass(node)
|
disc.addInsertrClass(node)
|
||||||
result.ContainersAdded++
|
result.ContainersAdded++
|
||||||
result.ElementsEnhanced += len(viableChildren)
|
result.ElementsEnhanced += len(viableChildren)
|
||||||
|
|
||||||
@@ -144,8 +144,8 @@ func (ae *AutoEnhancer) enhanceNode(node *html.Node, result *EnhancementResult,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if this individual element should be enhanced
|
// Check if this individual element should be enhanced
|
||||||
if ae.isGoodIndividualElement(node) {
|
if disc.isGoodIndividualElement(node) {
|
||||||
ae.addInsertrClass(node)
|
disc.addInsertrClass(node)
|
||||||
result.IndividualsAdded++
|
result.IndividualsAdded++
|
||||||
result.ElementsEnhanced++
|
result.ElementsEnhanced++
|
||||||
|
|
||||||
@@ -155,12 +155,12 @@ func (ae *AutoEnhancer) enhanceNode(node *html.Node, result *EnhancementResult,
|
|||||||
|
|
||||||
// Recursively check children
|
// Recursively check children
|
||||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||||
ae.enhanceNode(child, result, aggressive)
|
disc.discoverNode(child, result, aggressive)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isGoodContainer checks if an element is a good candidate for container expansion
|
// isGoodContainer checks if an element is a good candidate for container expansion
|
||||||
func (ae *AutoEnhancer) isGoodContainer(node *html.Node) bool {
|
func (disc *Discoverer) isGoodContainer(node *html.Node) bool {
|
||||||
containerTags := map[string]bool{
|
containerTags := map[string]bool{
|
||||||
"div": true,
|
"div": true,
|
||||||
"section": true,
|
"section": true,
|
||||||
@@ -178,17 +178,17 @@ func (ae *AutoEnhancer) isGoodContainer(node *html.Node) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip containers that are clearly non-content
|
// Skip containers that are clearly non-content
|
||||||
if ae.isNonContentElement(node) {
|
if disc.isNonContentElement(node) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip containers in the head section
|
// Skip containers in the head section
|
||||||
if ae.isInHead(node) {
|
if disc.isInHead(node) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip containers with technical/framework-specific classes that suggest they're not content
|
// Skip containers with technical/framework-specific classes that suggest they're not content
|
||||||
classes := ae.getClasses(node)
|
classes := disc.getClasses(node)
|
||||||
for _, class := range classes {
|
for _, class := range classes {
|
||||||
lowerClass := strings.ToLower(class)
|
lowerClass := strings.ToLower(class)
|
||||||
// Skip Next.js internal classes and other framework artifacts
|
// Skip Next.js internal classes and other framework artifacts
|
||||||
@@ -205,39 +205,39 @@ func (ae *AutoEnhancer) isGoodContainer(node *html.Node) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isGoodIndividualElement checks if an element is a good candidate for individual enhancement
|
// isGoodIndividualElement checks if an element is a good candidate for individual enhancement
|
||||||
func (ae *AutoEnhancer) isGoodIndividualElement(node *html.Node) bool {
|
func (disc *Discoverer) isGoodIndividualElement(node *html.Node) bool {
|
||||||
// Skip self-closing elements
|
// Skip self-closing elements
|
||||||
if ae.isSelfClosing(node) {
|
if disc.isSelfClosing(node) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip non-content elements that should never be editable
|
// Skip non-content elements that should never be editable
|
||||||
if ae.isNonContentElement(node) {
|
if disc.isNonContentElement(node) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip elements inside head section
|
// Skip elements inside head section
|
||||||
if ae.isInHead(node) {
|
if disc.isInHead(node) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip elements with no meaningful content
|
// Skip elements with no meaningful content
|
||||||
if ae.hasNoMeaningfulContent(node) {
|
if disc.hasNoMeaningfulContent(node) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if element has editable content
|
// Check if element has editable content
|
||||||
return ae.hasEditableContent(node)
|
return disc.hasEditableContent(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasEditableContent uses the engine's enhanced detection logic
|
// hasEditableContent uses the engine's enhanced detection logic
|
||||||
func (ae *AutoEnhancer) hasEditableContent(node *html.Node) bool {
|
func (disc *Discoverer) hasEditableContent(node *html.Node) bool {
|
||||||
return engine.HasEditableContent(node)
|
return engine.HasEditableContent(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasInsertrClass checks if a node already has the insertr class
|
// hasInsertrClass checks if a node already has the insertr class
|
||||||
func (ae *AutoEnhancer) hasInsertrClass(node *html.Node) bool {
|
func (disc *Discoverer) hasInsertrClass(node *html.Node) bool {
|
||||||
classes := ae.getClasses(node)
|
classes := disc.getClasses(node)
|
||||||
for _, class := range classes {
|
for _, class := range classes {
|
||||||
if class == "insertr" {
|
if class == "insertr" {
|
||||||
return true
|
return true
|
||||||
@@ -247,14 +247,14 @@ func (ae *AutoEnhancer) hasInsertrClass(node *html.Node) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addInsertrClass adds the insertr class to a node
|
// addInsertrClass adds the insertr class to a node
|
||||||
func (ae *AutoEnhancer) addInsertrClass(node *html.Node) {
|
func (disc *Discoverer) addInsertrClass(node *html.Node) {
|
||||||
classes := ae.getClasses(node)
|
classes := disc.getClasses(node)
|
||||||
classes = append(classes, "insertr")
|
classes = append(classes, "insertr")
|
||||||
ae.setClasses(node, classes)
|
disc.setClasses(node, classes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getClasses extracts CSS classes from a node
|
// getClasses extracts CSS classes from a node
|
||||||
func (ae *AutoEnhancer) getClasses(node *html.Node) []string {
|
func (disc *Discoverer) getClasses(node *html.Node) []string {
|
||||||
for i, attr := range node.Attr {
|
for i, attr := range node.Attr {
|
||||||
if attr.Key == "class" {
|
if attr.Key == "class" {
|
||||||
if attr.Val == "" {
|
if attr.Val == "" {
|
||||||
@@ -272,7 +272,7 @@ func (ae *AutoEnhancer) getClasses(node *html.Node) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setClasses sets CSS classes on a node
|
// setClasses sets CSS classes on a node
|
||||||
func (ae *AutoEnhancer) setClasses(node *html.Node, classes []string) {
|
func (disc *Discoverer) setClasses(node *html.Node, classes []string) {
|
||||||
classValue := strings.Join(classes, " ")
|
classValue := strings.Join(classes, " ")
|
||||||
|
|
||||||
// Update existing class attribute or add new one
|
// Update existing class attribute or add new one
|
||||||
@@ -291,7 +291,7 @@ func (ae *AutoEnhancer) setClasses(node *html.Node, classes []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isSelfClosing checks if an element is self-closing
|
// isSelfClosing checks if an element is self-closing
|
||||||
func (ae *AutoEnhancer) isSelfClosing(node *html.Node) bool {
|
func (disc *Discoverer) isSelfClosing(node *html.Node) bool {
|
||||||
selfClosingTags := map[string]bool{
|
selfClosingTags := map[string]bool{
|
||||||
"img": true, "input": true, "br": true, "hr": true,
|
"img": true, "input": true, "br": true, "hr": true,
|
||||||
"meta": true, "link": true, "area": true, "base": true,
|
"meta": true, "link": true, "area": true, "base": true,
|
||||||
@@ -301,7 +301,7 @@ func (ae *AutoEnhancer) isSelfClosing(node *html.Node) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isNonContentElement checks if an element should never be editable
|
// isNonContentElement checks if an element should never be editable
|
||||||
func (ae *AutoEnhancer) isNonContentElement(node *html.Node) bool {
|
func (disc *Discoverer) isNonContentElement(node *html.Node) bool {
|
||||||
nonContentTags := map[string]bool{
|
nonContentTags := map[string]bool{
|
||||||
"script": true, // JavaScript code
|
"script": true, // JavaScript code
|
||||||
"style": true, // CSS styles
|
"style": true, // CSS styles
|
||||||
@@ -334,7 +334,7 @@ func (ae *AutoEnhancer) isNonContentElement(node *html.Node) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isInHead checks if a node is inside the document head
|
// isInHead checks if a node is inside the document head
|
||||||
func (ae *AutoEnhancer) isInHead(node *html.Node) bool {
|
func (disc *Discoverer) isInHead(node *html.Node) bool {
|
||||||
current := node.Parent
|
current := node.Parent
|
||||||
for current != nil {
|
for current != nil {
|
||||||
if current.Type == html.ElementNode && strings.ToLower(current.Data) == "head" {
|
if current.Type == html.ElementNode && strings.ToLower(current.Data) == "head" {
|
||||||
@@ -346,14 +346,14 @@ func (ae *AutoEnhancer) isInHead(node *html.Node) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// hasNoMeaningfulContent checks if an element has no meaningful text content
|
// hasNoMeaningfulContent checks if an element has no meaningful text content
|
||||||
func (ae *AutoEnhancer) hasNoMeaningfulContent(node *html.Node) bool {
|
func (disc *Discoverer) hasNoMeaningfulContent(node *html.Node) bool {
|
||||||
if node.Type != html.ElementNode {
|
if node.Type != html.ElementNode {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract text content
|
// Extract text content
|
||||||
var text strings.Builder
|
var text strings.Builder
|
||||||
ae.extractTextRecursive(node, &text)
|
disc.extractTextRecursive(node, &text)
|
||||||
content := strings.TrimSpace(text.String())
|
content := strings.TrimSpace(text.String())
|
||||||
|
|
||||||
// Empty or whitespace-only content
|
// Empty or whitespace-only content
|
||||||
@@ -384,7 +384,7 @@ func (ae *AutoEnhancer) hasNoMeaningfulContent(node *html.Node) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// extractTextRecursive extracts text content from a node and its children
|
// extractTextRecursive extracts text content from a node and its children
|
||||||
func (ae *AutoEnhancer) extractTextRecursive(node *html.Node, text *strings.Builder) {
|
func (disc *Discoverer) extractTextRecursive(node *html.Node, text *strings.Builder) {
|
||||||
if node.Type == html.TextNode {
|
if node.Type == html.TextNode {
|
||||||
text.WriteString(node.Data)
|
text.WriteString(node.Data)
|
||||||
return
|
return
|
||||||
@@ -398,13 +398,13 @@ func (ae *AutoEnhancer) extractTextRecursive(node *html.Node, text *strings.Buil
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ae.extractTextRecursive(child, text)
|
disc.extractTextRecursive(child, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyFile copies a file from input to output directory
|
// copyFile copies a file from input to output directory
|
||||||
func (ae *AutoEnhancer) copyFile(filePath, inputDir, outputDir string) error {
|
func (disc *Discoverer) copyFile(filePath, inputDir, outputDir string) error {
|
||||||
outputPath := ae.getOutputPath(filePath, inputDir, outputDir)
|
outputPath := disc.getOutputPath(filePath, inputDir, outputDir)
|
||||||
|
|
||||||
// Create output directory for the file
|
// Create output directory for the file
|
||||||
if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil {
|
||||||
@@ -420,13 +420,13 @@ func (ae *AutoEnhancer) copyFile(filePath, inputDir, outputDir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getOutputPath converts input path to output path
|
// getOutputPath converts input path to output path
|
||||||
func (ae *AutoEnhancer) getOutputPath(filePath, inputDir, outputDir string) string {
|
func (disc *Discoverer) getOutputPath(filePath, inputDir, outputDir string) string {
|
||||||
relPath, _ := filepath.Rel(inputDir, filePath)
|
relPath, _ := filepath.Rel(inputDir, filePath)
|
||||||
return filepath.Join(outputDir, relPath)
|
return filepath.Join(outputDir, relPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeEnhancedFile writes the enhanced HTML document to a file
|
// writeEnhancedFile writes the enhanced HTML document to a file
|
||||||
func (ae *AutoEnhancer) writeEnhancedFile(outputPath string, enhanced *EnhancementResult) error {
|
func (disc *Discoverer) writeEnhancedFile(outputPath string, enhanced *FileDiscoveryResult) error {
|
||||||
// Create output directory
|
// Create output directory
|
||||||
if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -2,49 +2,93 @@ package content
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"golang.org/x/net/html"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/insertr/insertr/internal/engine"
|
"github.com/insertr/insertr/internal/engine"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enhancer combines parsing and content injection using unified engine
|
// EnhancementConfig configures the enhancement pipeline
|
||||||
|
type EnhancementConfig struct {
|
||||||
|
Discovery DiscoveryConfig
|
||||||
|
ContentInjection bool
|
||||||
|
GenerateIDs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscoveryConfig configures element discovery
|
||||||
|
type DiscoveryConfig struct {
|
||||||
|
Enabled bool
|
||||||
|
Aggressive bool
|
||||||
|
Containers bool
|
||||||
|
Individual bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enhancer combines discovery, ID generation, and content injection in unified pipeline
|
||||||
type Enhancer struct {
|
type Enhancer struct {
|
||||||
engine *engine.ContentEngine
|
engine *engine.ContentEngine
|
||||||
// injector functionality will be integrated into engine
|
discoverer *Discoverer
|
||||||
}
|
config EnhancementConfig
|
||||||
|
siteID string
|
||||||
// NewEnhancer creates a new HTML enhancer using unified engine
|
|
||||||
func NewEnhancer(client engine.ContentClient, siteID string) *Enhancer {
|
|
||||||
// Create database client for engine
|
|
||||||
var engineClient engine.ContentClient
|
|
||||||
if dbClient, ok := client.(*DatabaseClient); ok {
|
|
||||||
engineClient = engine.NewDatabaseClient(dbClient.db)
|
|
||||||
} else {
|
|
||||||
// For non-database clients, we'll implement proper handling later
|
|
||||||
engineClient = engine.NewDatabaseClient(nil) // This will need to be fixed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewEnhancer creates a new HTML enhancer with unified pipeline
|
||||||
|
func NewEnhancer(client engine.ContentClient, siteID string, config EnhancementConfig) *Enhancer {
|
||||||
return &Enhancer{
|
return &Enhancer{
|
||||||
engine: engine.NewContentEngine(engineClient),
|
engine: engine.NewContentEngine(client),
|
||||||
|
discoverer: NewDiscoverer(),
|
||||||
|
config: config,
|
||||||
|
siteID: siteID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnhanceFile processes an HTML file and injects content
|
// NewDefaultEnhancer creates an enhancer with default configuration
|
||||||
|
func NewDefaultEnhancer(client engine.ContentClient, siteID string) *Enhancer {
|
||||||
|
defaultConfig := EnhancementConfig{
|
||||||
|
Discovery: DiscoveryConfig{
|
||||||
|
Enabled: true,
|
||||||
|
Aggressive: false,
|
||||||
|
Containers: true,
|
||||||
|
Individual: true,
|
||||||
|
},
|
||||||
|
ContentInjection: true,
|
||||||
|
GenerateIDs: true,
|
||||||
|
}
|
||||||
|
return NewEnhancer(client, siteID, defaultConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnhanceFile processes a single HTML file through the complete pipeline
|
||||||
func (e *Enhancer) EnhanceFile(inputPath, outputPath string) error {
|
func (e *Enhancer) EnhanceFile(inputPath, outputPath string) error {
|
||||||
// TODO: Implement with unified engine
|
// Read HTML file
|
||||||
// For now, just copy the file to maintain functionality
|
htmlContent, err := os.ReadFile(inputPath)
|
||||||
return e.copyFile(inputPath, outputPath)
|
if err != nil {
|
||||||
|
return fmt.Errorf("reading file %s: %w", inputPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnhanceDirectory processes all HTML files in a directory
|
// Process through unified pipeline
|
||||||
|
processedHTML, err := e.processHTML(htmlContent, filepath.Base(inputPath))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("processing HTML %s: %w", inputPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create output directory
|
||||||
|
if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil {
|
||||||
|
return fmt.Errorf("creating output directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write processed HTML
|
||||||
|
return os.WriteFile(outputPath, processedHTML, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnhanceDirectory processes all files in a directory through the unified pipeline
|
||||||
func (e *Enhancer) EnhanceDirectory(inputDir, outputDir string) error {
|
func (e *Enhancer) EnhanceDirectory(inputDir, outputDir string) error {
|
||||||
// Create output directory
|
// Create output directory
|
||||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||||
return fmt.Errorf("creating output directory: %w", err)
|
return fmt.Errorf("creating output directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk input directory and copy files for now
|
// Walk input directory
|
||||||
return filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
|
return filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -62,16 +106,96 @@ func (e *Enhancer) EnhanceDirectory(inputDir, outputDir string) error {
|
|||||||
return os.MkdirAll(outputPath, info.Mode())
|
return os.MkdirAll(outputPath, info.Mode())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy files (HTML processing will be implemented later)
|
// Process HTML files through enhancement pipeline
|
||||||
|
if strings.HasSuffix(strings.ToLower(path), ".html") {
|
||||||
|
return e.EnhanceFile(path, outputPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy non-HTML files as-is
|
||||||
return e.copyFile(path, outputPath)
|
return e.copyFile(path, outputPath)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processHTML implements the unified enhancement pipeline
|
||||||
|
func (e *Enhancer) processHTML(htmlContent []byte, filePath string) ([]byte, error) {
|
||||||
|
var processedHTML []byte = htmlContent
|
||||||
|
|
||||||
|
// Phase 1: Element Discovery (if enabled)
|
||||||
|
if e.config.Discovery.Enabled {
|
||||||
|
discoveredHTML, err := e.discoverElements(processedHTML, filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("element discovery: %w", err)
|
||||||
|
}
|
||||||
|
processedHTML = discoveredHTML
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2 & 3: ID Generation + Content Injection (via engine)
|
||||||
|
if e.config.GenerateIDs || e.config.ContentInjection {
|
||||||
|
enhancedHTML, err := e.enhanceWithEngine(processedHTML, filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("engine enhancement: %w", err)
|
||||||
|
}
|
||||||
|
processedHTML = enhancedHTML
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedHTML, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// discoverElements adds insertr classes to viable elements
|
||||||
|
func (e *Enhancer) discoverElements(htmlContent []byte, filePath string) ([]byte, error) {
|
||||||
|
// Parse HTML
|
||||||
|
doc, err := html.Parse(strings.NewReader(string(htmlContent)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing HTML: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find and mark viable elements
|
||||||
|
result := &FileDiscoveryResult{Document: doc}
|
||||||
|
e.discoverer.discoverNode(doc, result, e.config.Discovery.Aggressive)
|
||||||
|
|
||||||
|
// Render back to HTML
|
||||||
|
var buf strings.Builder
|
||||||
|
if err := html.Render(&buf, doc); err != nil {
|
||||||
|
return nil, fmt.Errorf("rendering HTML: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(buf.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// enhanceWithEngine uses the unified engine for ID generation and content injection
|
||||||
|
func (e *Enhancer) enhanceWithEngine(htmlContent []byte, filePath string) ([]byte, error) {
|
||||||
|
// Determine processing mode
|
||||||
|
var mode engine.ProcessMode
|
||||||
|
if e.config.ContentInjection {
|
||||||
|
mode = engine.Enhancement // ID generation + content injection
|
||||||
|
} else {
|
||||||
|
mode = engine.IDGeneration // ID generation only
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process with engine
|
||||||
|
result, err := e.engine.ProcessContent(engine.ContentInput{
|
||||||
|
HTML: htmlContent,
|
||||||
|
FilePath: filePath,
|
||||||
|
SiteID: e.siteID,
|
||||||
|
Mode: mode,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("engine processing: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render enhanced document
|
||||||
|
var buf strings.Builder
|
||||||
|
if err := html.Render(&buf, result.Document); err != nil {
|
||||||
|
return nil, fmt.Errorf("rendering enhanced HTML: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(buf.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
// EnhanceInPlace performs in-place enhancement of static site files
|
// EnhanceInPlace performs in-place enhancement of static site files
|
||||||
func (e *Enhancer) EnhanceInPlace(sitePath string, siteID string) error {
|
func (e *Enhancer) EnhanceInPlace(sitePath string, siteID string) error {
|
||||||
// TODO: Implement with unified engine
|
// TODO: Implement in-place enhancement using the unified pipeline
|
||||||
// For now, just log that enhancement was requested
|
fmt.Printf("📄 Enhancement requested for site %s at %s (unified pipeline implementation needed)\n", siteID, sitePath)
|
||||||
fmt.Printf("📄 Enhancement requested for site %s at %s (stub implementation)\n", siteID, sitePath)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func NewSiteManager(contentClient engine.ContentClient, backupDir string, devMod
|
|||||||
|
|
||||||
return &SiteManager{
|
return &SiteManager{
|
||||||
sites: make(map[string]*SiteConfig),
|
sites: make(map[string]*SiteConfig),
|
||||||
enhancer: NewEnhancer(contentClient, ""), // siteID will be set per operation
|
enhancer: NewDefaultEnhancer(contentClient, ""), // siteID will be set per operation
|
||||||
backupDir: backupDir,
|
backupDir: backupDir,
|
||||||
devMode: devMode,
|
devMode: devMode,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user