package content import ( "fmt" "os" "path/filepath" "strings" "golang.org/x/net/html" "github.com/insertr/cli/pkg/parser" ) // Enhancer combines parsing and content injection type Enhancer struct { parser *parser.Parser injector *Injector } // NewEnhancer creates a new HTML enhancer func NewEnhancer(client ContentClient, siteID string) *Enhancer { return &Enhancer{ parser: parser.New(), injector: NewInjector(client, siteID), } } // EnhanceFile processes an HTML file and injects content func (e *Enhancer) EnhanceFile(inputPath, outputPath string) error { // Use parser to get elements from file result, err := e.parser.ParseDirectory(filepath.Dir(inputPath)) if err != nil { return fmt.Errorf("parsing file: %w", err) } // Filter elements for this specific file var fileElements []parser.Element inputBaseName := filepath.Base(inputPath) for _, elem := range result.Elements { elemBaseName := filepath.Base(elem.FilePath) if elemBaseName == inputBaseName { fileElements = append(fileElements, elem) } } if len(fileElements) == 0 { // No insertr elements found, copy file as-is return e.copyFile(inputPath, outputPath) } // Read and parse HTML for modification htmlContent, err := os.ReadFile(inputPath) if err != nil { return fmt.Errorf("reading file %s: %w", inputPath, err) } doc, err := html.Parse(strings.NewReader(string(htmlContent))) if err != nil { return fmt.Errorf("parsing HTML: %w", err) } // Find and inject content for each element for _, elem := range fileElements { // Find the node in the parsed document // Note: This is a simplified approach - in production we'd need more robust node matching if err := e.injectElementContent(doc, elem); err != nil { fmt.Printf("⚠️ Warning: failed to inject content for %s: %v\n", elem.ContentID, err) } } // Inject editor assets for development e.injector.InjectEditorAssets(doc, true) // Write enhanced HTML if err := e.writeHTML(doc, outputPath); err != nil { return fmt.Errorf("writing enhanced HTML: %w", err) } fmt.Printf("✅ Enhanced: %s → %s (%d elements)\n", filepath.Base(inputPath), filepath.Base(outputPath), len(fileElements)) return nil } // injectElementContent finds and injects content for a specific element func (e *Enhancer) injectElementContent(doc *html.Node, elem parser.Element) error { // Fetch content from database contentItem, err := e.injector.client.GetContent(e.injector.siteID, elem.ContentID) if err != nil { return fmt.Errorf("fetching content: %w", err) } // Find nodes with insertr class and inject content e.findAndInjectNodes(doc, elem, contentItem) return nil } // findAndInjectNodes recursively finds nodes and injects content func (e *Enhancer) findAndInjectNodes(node *html.Node, elem parser.Element, contentItem *ContentItem) { if node.Type == html.ElementNode { // Check if this node matches our element criteria classes := getClasses(node) if containsClass(classes, "insertr") && node.Data == elem.Tag { // This might be our target node - inject content e.injector.addContentAttributes(node, elem.ContentID, string(elem.Type)) if contentItem != nil { switch elem.Type { case parser.ContentText: e.injector.injectTextContent(node, contentItem.Value) case parser.ContentMarkdown: e.injector.injectMarkdownContent(node, contentItem.Value) case parser.ContentLink: e.injector.injectLinkContent(node, contentItem.Value) } } } } // Recursively process children for child := node.FirstChild; child != nil; child = child.NextSibling { e.findAndInjectNodes(child, elem, contentItem) } } // Helper functions from parser package func getClasses(node *html.Node) []string { for _, attr := range node.Attr { if attr.Key == "class" { return strings.Fields(attr.Val) } } return []string{} } func containsClass(classes []string, target string) bool { for _, class := range classes { if class == target { return true } } return false } // EnhanceDirectory processes all HTML files in a directory func (e *Enhancer) EnhanceDirectory(inputDir, outputDir string) error { // Create output directory if err := os.MkdirAll(outputDir, 0755); err != nil { return fmt.Errorf("creating output directory: %w", err) } // Walk input directory return filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } // Calculate relative path and output path relPath, err := filepath.Rel(inputDir, path) if err != nil { return err } outputPath := filepath.Join(outputDir, relPath) // Handle directories if info.IsDir() { return os.MkdirAll(outputPath, info.Mode()) } // Handle HTML files if strings.HasSuffix(strings.ToLower(path), ".html") { return e.EnhanceFile(path, outputPath) } // Copy other files as-is return e.copyFile(path, outputPath) }) } // copyFile copies a file from src to dst func (e *Enhancer) copyFile(src, dst string) error { // Create directory for destination if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { return err } // Read source data, err := os.ReadFile(src) if err != nil { return err } // Write destination return os.WriteFile(dst, data, 0644) } // writeHTML writes an HTML document to a file func (e *Enhancer) writeHTML(doc *html.Node, outputPath string) error { // Create directory for output if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil { return err } // Create output file file, err := os.Create(outputPath) if err != nil { return err } defer file.Close() // Write HTML return html.Render(file, doc) }