From 00c2ba34e64e17760cf48597a83c6c2dab423dc4 Mon Sep 17 00:00:00 2001 From: Joakim Date: Fri, 19 Sep 2025 15:57:28 +0200 Subject: [PATCH] feat: Implement syntactic sugar and site-specific discovery config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add syntactic sugar for container transformation: .insertr containers → children get .insertr - Fix discovery auto-running when disabled with site-specific config loading - Add comprehensive styling test examples for HTML attribute preservation - Include test input for syntactic sugar validation - Update discovery defaults to respect developer intent (disabled by default) --- CLASSES.md | 556 +++++++++++++++++++++++++++++++ TODO.md | 51 +++ cmd/enhance.go | 45 ++- cmd/serve.go | 20 ++ demos/simple/index.html | 194 ++++++++++- insertr.yaml | 12 + internal/content/discoverer.go | 38 ++- internal/content/site_manager.go | 37 +- test-input/test-sugar.html | 37 ++ 9 files changed, 959 insertions(+), 31 deletions(-) create mode 100644 CLASSES.md create mode 100644 test-input/test-sugar.html diff --git a/CLASSES.md b/CLASSES.md new file mode 100644 index 0000000..e11f78c --- /dev/null +++ b/CLASSES.md @@ -0,0 +1,556 @@ +# Insertr Class Reference and Usage Guide +Complete definitions of Insertr's class-based functionality and editing behaviors. + +## Philosophy +Insertr uses semantic CSS classes to declare editing intent, following the principle of **"just add a class"**. Each class provides specific editing capabilities while maintaining zero configuration and respecting developer design intent. + +## .insertr +The basic insertr class makes an element editable. It preserves the context of the element and updates its inner HTML. The editing behavior is determined by the HTML element type, providing intuitive and predictable editing experiences. + +### Style Preservation (Shared with .insertr-content) +**Automatic nested element detection**: Insertr automatically analyzes existing markup within `.insertr` elements and preserves styled nested elements as formatting options during editing. + +```html + +

Welcome to Our Company

+

Save up to 50% today

+ Home +``` + +**Editor Experience**: +- **Preserved Elements**: ``, ``, `` +- **Formatting Options**: Users can apply these styles to other text portions +- **Design Safety**: Users cannot accidentally remove developer-intended styling +- **Element Addition**: If a styled anchor exists, users can add more links with the same styling + +### Style Detection Rules +- **One Layer Deep**: Only direct child elements are analyzed and preserved +- **All Elements Preserved**: Any nested element becomes a formatting option (spans, emphasis, icons, etc.) +- **Automatic Availability**: No configuration needed - styles are automatically detected and offered + +### Practical Examples + +#### **Branded Headings** +```html + +

Product Launch 2024

+ + +- Text: "Product Launch 2024" +- Style options: Normal text + "Brand color" style for spans +- User edits: "New Launch 2025" +- Result:

New Launch 2025

+``` + +#### **Navigation with Icons** +```html + + Home + + +- Text field: "Home" +- URL field: "/" +- Icon preserved automatically in all edits +- User can add more nav items with same icon pattern +``` + +#### **Styled Emphasis in Content** +```html + +

Save up to 50% today

+ + +- Text: "Save up to 50% today" +- Style options: Normal text + "Highlight price" style +- User edits: "Save 75% this week" +- Result:

Save 75% this week

+``` + +#### **Call-to-Action Buttons** +```html + + + + +- Text: "Buy Now →" +- Arrow styling preserved automatically +- User can change text while keeping arrow design +``` + +### Element Behavior Categories + +**Text Elements**: Direct content editing with simple text input fields +**Interactive Elements**: Content plus relevant attributes (href, etc.) +**Media Elements**: Asset management with metadata editing +**Container Elements**: Automatic expansion to edit viable children +**Structured Elements**: Smart handling of lists, tables, and hierarchical content + +| Element | Behaviour | Use Cases | +| -------------- | --------------- | --------- | +| **Text Elements** |-|-| +| `h1, h2, h3, h4, h5, h6` | Content + nested style preservation | Page titles, section headers, headlines | +| `p` | Content + nested style preservation | Simple paragraphs, descriptions | +| `span` | Content + nested style preservation | Inline text, labels, tags | +| `label` | Content + nested style preservation | Form labels, captions | +| `blockquote` | Content + nested style preservation | Quotes, testimonials | +| `code` | Content + nested style preservation | Code snippets, technical text | +| **Interactive Elements** |-|-| +| `a` | Content + href + nested style preservation | Navigation links, CTAs, external links | +| `button` | Content + nested style preservation | Buttons, CTAs, form actions | +| **Media Elements** |-|-| +| `img` | src + alt + title | Images, photos, illustrations | +| **Container Elements** |-|-| +| `div` | Container expansion | Generic containers, cards, sections | +| `section` | Container expansion | Page sections, content blocks | +| `article` | Container expansion | Blog posts, news articles | +| `header` | Container expansion | Page/section headers | +| `footer` | Container expansion | Page/section footers | +| `nav` | Container expansion | Navigation menus | +| **List Elements** |-|-| +| `li` | Content only | List items, menu items | +| `ul, ol` | Container expansion | Lists (expands to li elements) | +| **Table Elements** |-|-| +| `td, th` | Content only | Table cells and headers | +| `tr` | Container expansion | Table rows (expands to td/th) | +| `table` | Container expansion | Tables (expands to rows) | + +### Special Considerations + +**Attribute Preservation**: All elements preserve their existing classes, IDs, data attributes, and styling context during content updates. + +**Content Type Detection**: +- Simple elements (h1-h6, p, span) → Text input with style preservation +- Complex elements (a, img) → Multi-field editors with style preservation +- Containers (div, section) → Child element expansion +- Rich content containers → Suggested for `.insertr-content` class + +**Nested Element Handling**: +- Container expansion stops at elements that already have `.insertr` class +- Styled nested elements (one layer deep) are preserved as formatting options +- Avoid infinite nesting by respecting existing editorial boundaries +- Preserve semantic HTML structure during content updates + +**Fallback Behavior**: +- Unknown/unsupported elements default to content-only editing +- Custom elements inherit behavior from their semantic role + +## Shared Functionality: Style Preservation + +All Insertr editing classes (`.insertr` and `.insertr-content`) share the same core style preservation system: + +### **Unified Style Detection** +- **One Layer Analysis**: Examines direct child elements for styling +- **Automatic Preservation**: Styled elements become formatting options +- **Zero Configuration**: No setup required - works automatically +- **Design Safety**: Prevents users from breaking developer styling + +### **Cross-Class Consistency** +- **`.insertr`**: Individual elements with style preservation +- **`.insertr-content`**: Rich text areas with style preservation +- **Same Logic**: Both use identical style detection and preservation + +### **Benefits** +- ✅ **Unified Experience**: Consistent behavior across all editing types +- ✅ **Design Integrity**: Developer styling always preserved +- ✅ **User Safety**: Cannot accidentally break carefully crafted designs +- ✅ **Zero Learning Curve**: Same formatting options everywhere + +### Syntactic Sugar Transformation +**Container Convenience**: When developers add `.insertr` to container elements (div, section, article), Insertr automatically transforms this "syntactic sugar" into granular editing: + +```html + +
+

Hero Title

+

Hero description

+ +
+ + +
+

Hero Title

+

Hero description

+ +
+``` + +**Benefits**: +- **Developer convenience**: One class enables section-wide editing +- **Granular control**: Individual element editing rather than container editing +- **Predictable behavior**: Each element maintains its semantic editing behavior + + +## .insertr-content +Rich content editing for blog posts, articles, recipes, and complex nested content that requires formatting preservation. + +### Purpose +The `.insertr-content` class enables rich text editing while maintaining the developer's original styling and design intent. Unlike basic `.insertr` elements that edit individual components, `.insertr-content` treats the entire container as a single rich content area. + +### Developer Styles → Editor Options (Shared with .insertr) +**Zero-configuration style detection**: Insertr automatically analyzes the existing markup within `.insertr-content` elements and makes those styles available as formatting options in the rich text editor. This same functionality applies to all `.insertr` elements. + +```html + +
+

Blog Post Title

+

Introduction with branded emphasis and + styled links.

+

Section Header

+

Regular content with italic text and more formatting.

+
Important quote with custom styling
+
+``` + +**Automatic Editor Palette Generation**: +- **Headings**: h1, h2 (based on existing structure) +- **Text Formatting**: Bold (default + `.brand-highlight` style), Italic +- **Links**: Default links + `.fancy-link` styled links +- **Blocks**: Paragraph, `.testimonial` blockquotes +- **All Nested Elements**: Any styled element becomes a formatting option + +### Behavior +- **Rich Text Editor**: Opens a modal or inline editor with formatting toolbar +- **Style Preservation**: All developer-defined classes and styling preserved +- **HTML Storage**: Content stored as HTML to maintain full fidelity +- **Nested Handling**: Respects existing `.insertr` elements as editorial boundaries + +### Content Type +- **Storage**: HTML format with complete attribute preservation +- **Enhancement**: Direct HTML injection into static files +- **Editor UI**: Rich text editor with developer-style-based formatting options + +### Use Cases +- Blog post content +- Article bodies +- Recipe instructions +- Product descriptions +- Documentation content +- Any complex content requiring rich formatting with design consistency + +## .insertr-add +Dynamic content creation and management for repeatable elements like testimonials, team members, product cards, or any collection of similar items. + +### Purpose +The `.insertr-add` class transforms a container into a dynamic collection where users can add, remove, and reorder similar items. The system automatically detects the template pattern from existing children and provides duplication capabilities. + +### Template Detection +**Automatic Pattern Recognition**: Insertr analyzes the existing child elements to understand the template structure for new items. + +```html + +
+
+
Not all that is gold does glitter
+ Tolkien +
+
+
The journey of a thousand miles begins with one step
+ Lao Tzu +
+
+``` + +**Template Structure**: System recognizes the pattern: +- Container: `.testimonial-item` +- Editable fields: `blockquote.insertr` + `cite.insertr` +- Styling: All classes and structure preserved + +### User Interface +- **Add Button**: Floating "+" button. +- **Remove Controls**: Delete option for each item (with confirmation) +- **Reorder Capability**: Drag-and-drop or up/down controls +- **Duplication**: New items inherit the exact structure and styling + +### Behavior +- **Template Preservation**: New items maintain exact HTML structure +- **Content Reset**: Editable fields cleared for new content entry +- **ID Generation**: Automatic unique IDs for new content items +- **Styling Inheritance**: All CSS classes and attributes preserved +- **Template Differnetiation**: If multiple templates are defined by the developer they are treated as variants available to the editor. + +### Data Storage +- **Array Format**: Stored as array of item data +- **Template Storage**: Base template structure preserved +- **Individual Content**: Each item's editable content stored separately + +### Use Cases +- Testimonial collections +- Team member grids +- Product catalogs +- FAQ sections +- Step-by-step instructions +- Portfolio items +- Feature lists +- Any repeating content pattern +## .insertr-gate +Authentication trigger that enables editing functionality when clicked. + +### Purpose +The `.insertr-gate` class transforms any element into an authentication trigger - typically a button or link placed unobtrusively on the page (like in the footer). When clicked, it initiates the authentication flow, and once authenticated, all `.insertr` elements on the page become editable. + +### How It Works +- **Trigger Element**: Not a boundary, but a clickable authentication trigger +- **Unobtrusive Placement**: Designed to not interfere with page layout or content flow +- **Developer Styled**: Can be styled however the developer wishes (button, link, icon, etc.) +- **Single Responsibility**: Just triggers authentication, doesn't control content + +### Implementation Examples + +#### **Footer Login Link** +```html + +``` + +#### **Styled Button in Corner** +```html + + + +``` + +#### **Discrete Text Link** +```html + +``` + +#### **Icon in Header** +```html +
+

My Blog

+ ⚙️ +
+``` + +### User Experience Flow +1. **Normal Visitor**: Sees the page normally, gate trigger is just a small button/link +2. **Editor Discovers**: Clicks the gate trigger (login link, edit button, etc.) +3. **Authentication**: Login flow initiates (popup, redirect, or modal) +4. **Authenticated**: All `.insertr` elements become editable, editing UI appears +5. **Editing**: User can edit any content marked with Insertr classes +6. **Logout**: Gate can also serve as logout trigger when authenticated + +### Behavior and States +- **Unauthenticated**: Shows login trigger (styled by developer) +- **Authenticated**: Can show logout option or editing status +- **Loading**: Shows loading state during authentication process +- **Error**: Shows error state if authentication fails + +### Multiple Gates +Multiple gates can exist on a page and will have identical functionality: +```html + + + + +``` + +### Styling and Placement +The gate is completely developer-controlled for styling and positioning: + +```css +/* Discrete footer link */ +.insertr-gate { + color: #666; + text-decoration: none; + font-size: 0.8em; +} + +/* Floating edit button */ +.insertr-gate.floating { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1000; +} + +/* Hidden until hover */ +.insertr-gate.hidden { + opacity: 0; + transition: opacity 0.3s; +} +.insertr-gate.hidden:hover { + opacity: 1; +} +``` + +### Authentication Integration +- **Provider Agnostic**: Works with configured auth providers (mock, Authentik, custom) +- **Session Management**: Respects existing authentication state +- **Token Based**: Supports JWT tokens and session-based authentication +- **Logout Support**: Same element can trigger logout when authenticated + +### Benefits +- ✅ **Unobtrusive**: Doesn't interfere with page layout or content +- ✅ **Flexible**: Can be any element styled however developer wants +- ✅ **Simple**: Just triggers authentication, no complex logic +- ✅ **Discoverable**: Easy for editors to find when they need it +- ✅ **Secure**: All editing requires authentication through the gate + +### Use Cases +- Footer login links for personal blogs +- Discrete admin buttons for marketing sites +- Hidden edit triggers for client sites +- Branded login buttons for company sites +- Development access triggers for staging sites + +**Design Philosophy**: The gate should feel natural to the site's design while providing clear access to editing capabilities for authorized users. + +## Class Combinations and Strategy + +### Combining Classes +Multiple Insertr classes can be used together to create sophisticated editing experiences with unified style preservation: + +```html + +
+

Welcome to Our Site

+ +
+ + +
+

Blog Post Title

+

Introduction with branded emphasis

+
Styled quote block
+
+ + +
+
+
Quote with styling
+ Author Name +
+
+ + + +``` + +### Content Architecture Patterns + +#### **Simple Website** (Landing pages, marketing sites) +```html +
+ +
+ +
+ +
+``` + +#### **Blog/Publication** (Content-heavy sites) +```html +
+
+ +
+ +
+ +
+ +
+ +
+
+``` + +#### **Application Interface** (Dashboards, admin panels) +```html +
+
+ +
+ +
+
+ +
+
+
+``` + +### Best Practices + +#### **Start Simple, Add Complexity** +1. Begin with basic `.insertr` for individual elements +2. Add `.insertr-content` for areas needing rich formatting +3. Introduce `.insertr-add` for collections that need growth +4. Apply `.insertr-gate` for access control as needed + +#### **Respect Content Hierarchy** +- Use container expansion (`.insertr`) for structural editing +- Use rich content (`.insertr-content`) for narrative content +- Use dynamic collections (`.insertr-add`) for repeatable patterns +- Use gates (`.insertr-gate`) for permission boundaries + +#### **Design System Integration** +- Insertr automatically respects your existing CSS and design system +- All developer-defined nested styles become available formatting options +- Works consistently across individual elements (`.insertr`) and rich content (`.insertr-content`) +- No need to configure or duplicate styling information +- Content editors work within design constraints naturally +- One-layer style detection ensures predictable behavior + +## Migration and Adoption + +### Progressive Enhancement +Insertr classes can be added incrementally to existing sites: + +```html + +

Page Title

+ + +
+ +
+ + +
+ +
+ + + Admin Login +``` + +### Framework Compatibility +Works seamlessly with all static site generators and frameworks: +- **Jekyll, Hugo, Gatsby**: Add classes to templates +- **Next.js, Nuxt**: Add classes to components +- **WordPress, Drupal**: Add classes to themes +- **Plain HTML**: Add classes directly to markup + +The class-based approach ensures Insertr integrates cleanly without requiring framework-specific implementations or build process changes. diff --git a/TODO.md b/TODO.md index c29143a..4365f7f 100644 --- a/TODO.md +++ b/TODO.md @@ -484,3 +484,54 @@ The HTTP server has been successfully implemented and is production-ready: 5. **✅ Performance** - Static file serving with dynamic content injection 6. **✅ Multi-Site** - Single server manages multiple enhanced sites 7. **✅ Zero Downtime** - Updates don't require site rebuilds or deploys + +## 🎯 **Future Class Extensions: Advanced Access Control** (Planned) + +### **Granular Permission System for `.insertr-gate`** + +**Current Implementation**: Simple boolean authentication gate for page-level editing access. + +**Future Enhancement Concept**: Role-based access control and section-level permissions for enterprise applications. + +#### **Potential Extended Gate Classes** +```html + +
+ + +
+
Admin-only dynamic content
+
+ +
+
Editor-level rich content
+
+ +
+
Premium subscriber content
+
+``` + +#### **Enterprise Use Cases** +- **Multi-tenant Applications**: Different organizations editing separate content areas +- **Editorial Workflows**: Writers, editors, and admins with different capabilities +- **Subscription Content**: Different content areas for different subscription tiers +- **Department Permissions**: Marketing vs Engineering vs Sales content areas +- **Geographic Restrictions**: Region-specific editing permissions + +#### **Implementation Considerations** +- **Configuration Complexity**: How to maintain zero-config philosophy while supporting complex permissions +- **Role Definition**: Where and how to define roles and permissions +- **Authentication Integration**: Extending current auth system vs external RBAC systems +- **Fallback Behavior**: Graceful degradation for unauthorized users +- **Performance Impact**: Permission checks shouldn't slow down regular site visitors + +#### **Integration Points** +- **Authentik OIDC**: Leverage existing auth provider for role information +- **Database Schema**: Extend current content system with permission metadata +- **API Endpoints**: New endpoints for permission management and validation +- **Editor Interface**: Dynamic interface based on user permissions + +**Priority**: Low - implement after core functionality is stable and enterprise customers request advanced permissions. + +**Design Principle**: Keep simple `.insertr-gate` as default, add optional complexity only when needed. diff --git a/cmd/enhance.go b/cmd/enhance.go index ac6a826..f566049 100644 --- a/cmd/enhance.go +++ b/cmd/enhance.go @@ -92,8 +92,49 @@ func runEnhance(cmd *cobra.Command, args []string) { client = content.NewMockClient() } - // Create enhancer - enhancer := content.NewDefaultEnhancer(client, siteID) + // Load site-specific configuration + enhancementConfig := content.EnhancementConfig{ + Discovery: content.DiscoveryConfig{ + Enabled: false, // Default: disabled for explicit class="insertr" markings only + Aggressive: false, + Containers: true, + Individual: true, + }, + ContentInjection: true, + GenerateIDs: true, + } + + // Override with site-specific discovery config if available + if siteConfigs := viper.Get("server.sites"); siteConfigs != nil { + if configs, ok := siteConfigs.([]interface{}); ok { + for _, configInterface := range configs { + if configMap, ok := configInterface.(map[string]interface{}); ok { + if configSiteID, ok := configMap["site_id"].(string); ok && configSiteID == siteID { + // Found matching site config, load discovery settings + if discoveryMap, ok := configMap["discovery"].(map[string]interface{}); ok { + if enabled, ok := discoveryMap["enabled"].(bool); ok { + enhancementConfig.Discovery.Enabled = enabled + fmt.Printf("🔧 Site '%s': discovery.enabled=%v\n", siteID, enabled) + } + if aggressive, ok := discoveryMap["aggressive"].(bool); ok { + enhancementConfig.Discovery.Aggressive = aggressive + } + if containers, ok := discoveryMap["containers"].(bool); ok { + enhancementConfig.Discovery.Containers = containers + } + if individual, ok := discoveryMap["individual"].(bool); ok { + enhancementConfig.Discovery.Individual = individual + } + } + break + } + } + } + } + } + + // Create enhancer with loaded configuration + enhancer := content.NewEnhancer(client, siteID, enhancementConfig) fmt.Printf("🚀 Starting enhancement process...\n") fmt.Printf("📁 Input: %s\n", inputDir) diff --git a/cmd/serve.go b/cmd/serve.go index af2109a..d1b0137 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -138,6 +138,26 @@ func runServe(cmd *cobra.Command, args []string) { if autoEnhance, ok := configMap["auto_enhance"].(bool); ok { site.AutoEnhance = autoEnhance } + // Parse discovery config if present + if discoveryMap, ok := configMap["discovery"].(map[string]interface{}); ok { + discovery := &content.DiscoveryConfig{ + Containers: true, // defaults + Individual: true, + } + if enabled, ok := discoveryMap["enabled"].(bool); ok { + discovery.Enabled = enabled + } + if aggressive, ok := discoveryMap["aggressive"].(bool); ok { + discovery.Aggressive = aggressive + } + if containers, ok := discoveryMap["containers"].(bool); ok { + discovery.Containers = containers + } + if individual, ok := discoveryMap["individual"].(bool); ok { + discovery.Individual = individual + } + site.Discovery = discovery + } if site.SiteID != "" && site.Path != "" { sites = append(sites, site) } diff --git a/demos/simple/index.html b/demos/simple/index.html index a303d9a..25f214e 100644 --- a/demos/simple/index.html +++ b/demos/simple/index.html @@ -1,19 +1,183 @@ - - Simple Test - + + + + + + Insertr Styling Test Examples + + -

Welcome, you!!

-

This is a test paragraph with a link.

-
-

Section Title

-

Another paragraph here.

- +

Insertr Styling Preservation Test Examples

+

These examples test how well Insertr preserves styling during the markdown editing process.

+ + +
+

Example 1: Styled Strong Element

+
+ Tests preservation of class styling on bold elements. Original has "emph" class for red styling. +
+

Hello world and welcome!

+ +
+

Example 2: Styled Link Element

+
+ Tests preservation of link classes and styles. The "fancy" class adds purple color and bottom border. +
+

Visit our about page for more info.

+
-
- -
\ No newline at end of file + +
+

Example 3: Mixed Content with Complex Attributes

+
+ Tests preservation of rel, target attributes and multiple styled elements. +
+

Visit our site for amazing content!

+
+ + +
+

Example 4: Multiple Styled Elements

+
+ Tests handling of multiple different styled elements in one paragraph. +
+

Welcome to Acme Corp where we create innovative solutions for modern businesses.

+
+ + +
+

Example 5: Button with Data Attributes

+
+ Tests preservation of button elements and data attributes for analytics. +
+

Ready to start? and begin your journey!

+
+ + +
+

Example 6: Nested Styled Elements

+
+ Tests complex nesting with multiple levels of styling. +
+

Check out our latest products and deals today!

+
+ + +
+

Example 7: Complex Structure

+
+ Tests editing of a more complex structure with multiple elements. This might be too complex for markdown. +
+
+

Welcome Back!

+

Hello valued customer, ready to explore our new features?

+
+
+ + +
+

Example 8: Simple Text (Control)

+
+ Simple text without any styling - should work perfectly with markdown. +
+

This is just plain text with some basic markdown formatting like bold and italic.

+
+ + +
+

Example 9: Link with Multiple Attributes

+
+ Tests preservation of multiple attributes including id, class, and title. +
+

Need help? Contact our support team anytime.

+
+ + \ No newline at end of file diff --git a/insertr.yaml b/insertr.yaml index 9f438e3..03b7e2f 100644 --- a/insertr.yaml +++ b/insertr.yaml @@ -16,18 +16,30 @@ server: path: "./demos/default_enhanced" source_path: "./demos/default" auto_enhance: true + discovery: + enabled: false # Uses explicit class="insertr" markings + aggressive: false - site_id: "simple" path: "./demos/simple_enhanced" source_path: "./demos/simple" auto_enhance: true + discovery: + enabled: false # Uses explicit class="insertr" markings + aggressive: false - site_id: "dan-eden-portfolio" path: "./demos/dan-eden-portfolio_enhanced" source_path: "./demos/dan-eden-portfolio" auto_enhance: true + discovery: + enabled: true # Auto-discover elements (no explicit markings) + aggressive: false - site_id: "devigo-web" path: "./demos/devigo-web_enhanced" source_path: "./demos/devigo-web" auto_enhance: true + discovery: + enabled: false # Uses explicit class="insertr" markings + aggressive: false # Example additional site configuration: # - site_id: "mysite" # path: "/var/www/mysite" diff --git a/internal/content/discoverer.go b/internal/content/discoverer.go index 64928d5..cb2566b 100644 --- a/internal/content/discoverer.go +++ b/internal/content/discoverer.go @@ -90,6 +90,7 @@ type FileDiscoveryResult struct { ElementsEnhanced int ContainersAdded int IndividualsAdded int + SugarTransformed int // Count of syntactic sugar transformations Document *html.Node } @@ -124,7 +125,30 @@ func (disc *Discoverer) discoverNode(node *html.Node, result *FileDiscoveryResul return } - // Skip if already has insertr class + // Handle syntactic sugar: containers with .insertr class + if disc.hasInsertrClass(node) && disc.isGoodContainer(node) { + // Syntactic sugar transformation: remove .insertr from container, add to viable children + viableChildren := engine.FindViableChildren(node) + if len(viableChildren) >= 1 { + disc.removeInsertrClass(node) // Remove from container + for _, child := range viableChildren { + if !disc.hasInsertrClass(child) { + disc.addInsertrClass(child) + result.IndividualsAdded++ + result.ElementsEnhanced++ + } + } + result.ContainersAdded++ + result.SugarTransformed++ // Track sugar transformations + + // Don't process children since we just processed them + return + } + // If no viable children, leave .insertr on the element (individual editing) + return + } + + // Skip if already has insertr class (individual editing elements) if disc.hasInsertrClass(node) { return } @@ -258,6 +282,18 @@ func (disc *Discoverer) addInsertrClass(node *html.Node) { disc.setClasses(node, classes) } +// removeInsertrClass removes the insertr class from a node +func (disc *Discoverer) removeInsertrClass(node *html.Node) { + classes := disc.getClasses(node) + var filteredClasses []string + for _, class := range classes { + if class != "insertr" { + filteredClasses = append(filteredClasses, class) + } + } + disc.setClasses(node, filteredClasses) +} + // getClasses extracts CSS classes from a node func (disc *Discoverer) getClasses(node *html.Node) []string { for i, attr := range node.Attr { diff --git a/internal/content/site_manager.go b/internal/content/site_manager.go index a57a20b..72002b2 100644 --- a/internal/content/site_manager.go +++ b/internal/content/site_manager.go @@ -13,11 +13,12 @@ import ( // SiteConfig represents configuration for a registered site type SiteConfig struct { - SiteID string `yaml:"site_id"` - Path string `yaml:"path"` // Served path (enhanced output) - SourcePath string `yaml:"source_path"` // Source path (for enhancement) - Domain string `yaml:"domain,omitempty"` - AutoEnhance bool `yaml:"auto_enhance"` + SiteID string `yaml:"site_id"` + Path string `yaml:"path"` // Served path (enhanced output) + SourcePath string `yaml:"source_path"` // Source path (for enhancement) + Domain string `yaml:"domain,omitempty"` + AutoEnhance bool `yaml:"auto_enhance"` + Discovery *DiscoveryConfig `yaml:"discovery,omitempty"` // Override discovery settings } // SiteManager handles registration and enhancement of static sites @@ -159,17 +160,27 @@ func (sm *SiteManager) EnhanceSite(siteID string) error { } // Create enhancer with auth provider for this operation - defaultConfig := EnhancementConfig{ - Discovery: DiscoveryConfig{ - Enabled: true, - Aggressive: false, - Containers: true, - Individual: true, - }, + // Discovery disabled by default - developers should explicitly mark elements with class="insertr" + discoveryConfig := DiscoveryConfig{ + Enabled: false, // Changed from true - respect developer intent + Aggressive: false, + Containers: true, + Individual: true, + } + + // Override with site-specific discovery config if provided + if site.Discovery != nil { + discoveryConfig = *site.Discovery + log.Printf("🔧 Using site-specific discovery config for %s: enabled=%v, aggressive=%v", + siteID, discoveryConfig.Enabled, discoveryConfig.Aggressive) + } + + config := EnhancementConfig{ + Discovery: discoveryConfig, ContentInjection: true, GenerateIDs: true, } - enhancer := NewEnhancerWithAuth(sm.contentClient, siteID, defaultConfig, sm.authProvider) + enhancer := NewEnhancerWithAuth(sm.contentClient, siteID, config, sm.authProvider) // Perform enhancement from source to output if err := enhancer.EnhanceDirectory(sourcePath, outputPath); err != nil { diff --git a/test-input/test-sugar.html b/test-input/test-sugar.html new file mode 100644 index 0000000..91dd1c4 --- /dev/null +++ b/test-input/test-sugar.html @@ -0,0 +1,37 @@ + + + + + + Syntactic Sugar Test + + +

Syntactic Sugar Transformation Test

+ + +
+

Hero Title

+

Hero description text

+ +
+ + +

Individual Header

+ + +
+

Card Title

+

Card description

+
+ + +
+
+

Blog Post Title

+ +
+

First paragraph of content

+

Second paragraph with formatting

+
+ + \ No newline at end of file