diff --git a/cmd/enhance.go b/cmd/enhance.go
index d448567..f0ff5b9 100644
--- a/cmd/enhance.go
+++ b/cmd/enhance.go
@@ -10,6 +10,7 @@ import (
"github.com/insertr/insertr/internal/content"
"github.com/insertr/insertr/internal/db"
+ "github.com/insertr/insertr/internal/engine"
)
var enhanceCmd = &cobra.Command{
@@ -49,7 +50,7 @@ func runEnhance(cmd *cobra.Command, args []string) {
outputDir := viper.GetString("cli.output")
// Create content client
- var client content.ContentClient
+ var client engine.ContentClient
if apiURL != "" {
fmt.Printf("🌐 Using content API: %s\n", apiURL)
client = content.NewHTTPClient(apiURL, apiKey)
diff --git a/internal/content/client.go b/internal/content/client.go
index 673d21e..a44ab59 100644
--- a/internal/content/client.go
+++ b/internal/content/client.go
@@ -8,6 +8,8 @@ import (
"net/url"
"strings"
"time"
+
+ "github.com/insertr/insertr/internal/engine"
)
// HTTPClient implements ContentClient for HTTP API access
@@ -29,7 +31,7 @@ func NewHTTPClient(baseURL, apiKey string) *HTTPClient {
}
// GetContent fetches a single content item by ID
-func (c *HTTPClient) GetContent(siteID, contentID string) (*ContentItem, error) {
+func (c *HTTPClient) GetContent(siteID, contentID string) (*engine.ContentItem, error) {
url := fmt.Sprintf("%s/api/content/%s?site_id=%s", c.BaseURL, contentID, siteID)
req, err := http.NewRequest("GET", url, nil)
@@ -60,7 +62,7 @@ func (c *HTTPClient) GetContent(siteID, contentID string) (*ContentItem, error)
return nil, fmt.Errorf("reading response: %w", err)
}
- var item ContentItem
+ var item engine.ContentItem
if err := json.Unmarshal(body, &item); err != nil {
return nil, fmt.Errorf("parsing response: %w", err)
}
@@ -69,9 +71,9 @@ func (c *HTTPClient) GetContent(siteID, contentID string) (*ContentItem, error)
}
// GetBulkContent fetches multiple content items by IDs
-func (c *HTTPClient) GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error) {
+func (c *HTTPClient) GetBulkContent(siteID string, contentIDs []string) (map[string]engine.ContentItem, error) {
if len(contentIDs) == 0 {
- return make(map[string]ContentItem), nil
+ return make(map[string]engine.ContentItem), nil
}
// Build query parameters
@@ -107,13 +109,13 @@ func (c *HTTPClient) GetBulkContent(siteID string, contentIDs []string) (map[str
return nil, fmt.Errorf("reading response: %w", err)
}
- var response ContentResponse
+ var response engine.ContentResponse
if err := json.Unmarshal(body, &response); err != nil {
return nil, fmt.Errorf("parsing response: %w", err)
}
// Convert slice to map for easy lookup
- result := make(map[string]ContentItem)
+ result := make(map[string]engine.ContentItem)
for _, item := range response.Content {
result[item.ID] = item
}
@@ -122,7 +124,7 @@ func (c *HTTPClient) GetBulkContent(siteID string, contentIDs []string) (map[str
}
// GetAllContent fetches all content for a site
-func (c *HTTPClient) GetAllContent(siteID string) (map[string]ContentItem, error) {
+func (c *HTTPClient) GetAllContent(siteID string) (map[string]engine.ContentItem, error) {
url := fmt.Sprintf("%s/api/content?site_id=%s", c.BaseURL, siteID)
req, err := http.NewRequest("GET", url, nil)
@@ -149,13 +151,13 @@ func (c *HTTPClient) GetAllContent(siteID string) (map[string]ContentItem, error
return nil, fmt.Errorf("reading response: %w", err)
}
- var response ContentResponse
+ var response engine.ContentResponse
if err := json.Unmarshal(body, &response); err != nil {
return nil, fmt.Errorf("parsing response: %w", err)
}
// Convert slice to map for easy lookup
- result := make(map[string]ContentItem)
+ result := make(map[string]engine.ContentItem)
for _, item := range response.Content {
result[item.ID] = item
}
diff --git a/internal/content/database.go b/internal/content/database.go
index 4f2003d..09b8c51 100644
--- a/internal/content/database.go
+++ b/internal/content/database.go
@@ -9,6 +9,7 @@ import (
"github.com/insertr/insertr/internal/db"
"github.com/insertr/insertr/internal/db/postgresql"
"github.com/insertr/insertr/internal/db/sqlite"
+ "github.com/insertr/insertr/internal/engine"
)
// DatabaseClient implements ContentClient for direct database access
@@ -24,7 +25,7 @@ func NewDatabaseClient(database *db.Database) *DatabaseClient {
}
// GetContent fetches a single content item by ID
-func (d *DatabaseClient) GetContent(siteID, contentID string) (*ContentItem, error) {
+func (d *DatabaseClient) GetContent(siteID, contentID string) (*engine.ContentItem, error) {
ctx := context.Background()
var content interface{}
var err error
@@ -56,9 +57,9 @@ func (d *DatabaseClient) GetContent(siteID, contentID string) (*ContentItem, err
}
// GetBulkContent fetches multiple content items by IDs
-func (d *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error) {
+func (d *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map[string]engine.ContentItem, error) {
if len(contentIDs) == 0 {
- return make(map[string]ContentItem), nil
+ return make(map[string]engine.ContentItem), nil
}
ctx := context.Background()
@@ -87,7 +88,7 @@ func (d *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map
items := d.convertToContentItemList(dbContent)
// Convert slice to map for easy lookup
- result := make(map[string]ContentItem)
+ result := make(map[string]engine.ContentItem)
for _, item := range items {
result[item.ID] = item
}
@@ -96,7 +97,7 @@ func (d *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map
}
// GetAllContent fetches all content for a site
-func (d *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, error) {
+func (d *DatabaseClient) GetAllContent(siteID string) (map[string]engine.ContentItem, error) {
ctx := context.Background()
var dbContent interface{}
var err error
@@ -117,7 +118,7 @@ func (d *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, e
items := d.convertToContentItemList(dbContent)
// Convert slice to map for easy lookup
- result := make(map[string]ContentItem)
+ result := make(map[string]engine.ContentItem)
for _, item := range items {
result[item.ID] = item
}
@@ -125,12 +126,12 @@ func (d *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, e
return result, nil
}
-// convertToContentItem converts database models to content.ContentItem
-func (d *DatabaseClient) convertToContentItem(content interface{}) ContentItem {
+// convertToContentItem converts database models to engine.ContentItem
+func (d *DatabaseClient) convertToContentItem(content interface{}) engine.ContentItem {
switch d.db.GetDBType() {
case "sqlite3":
c := content.(sqlite.Content)
- return ContentItem{
+ return engine.ContentItem{
ID: c.ID,
SiteID: c.SiteID,
Value: c.Value,
@@ -139,7 +140,7 @@ func (d *DatabaseClient) convertToContentItem(content interface{}) ContentItem {
}
case "postgresql":
c := content.(postgresql.Content)
- return ContentItem{
+ return engine.ContentItem{
ID: c.ID,
SiteID: c.SiteID,
Value: c.Value,
@@ -147,26 +148,26 @@ func (d *DatabaseClient) convertToContentItem(content interface{}) ContentItem {
UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339),
}
}
- return ContentItem{} // Should never happen
+ return engine.ContentItem{} // Should never happen
}
-// convertToContentItemList converts database model lists to content.ContentItem slice
-func (d *DatabaseClient) convertToContentItemList(contentList interface{}) []ContentItem {
+// convertToContentItemList converts database model lists to engine.ContentItem slice
+func (d *DatabaseClient) convertToContentItemList(contentList interface{}) []engine.ContentItem {
switch d.db.GetDBType() {
case "sqlite3":
list := contentList.([]sqlite.Content)
- items := make([]ContentItem, len(list))
+ items := make([]engine.ContentItem, len(list))
for i, content := range list {
items[i] = d.convertToContentItem(content)
}
return items
case "postgresql":
list := contentList.([]postgresql.Content)
- items := make([]ContentItem, len(list))
+ items := make([]engine.ContentItem, len(list))
for i, content := range list {
items[i] = d.convertToContentItem(content)
}
return items
}
- return []ContentItem{} // Should never happen
+ return []engine.ContentItem{} // Should never happen
}
diff --git a/internal/content/enhancer.go b/internal/content/enhancer.go
index 373bbaf..041045c 100644
--- a/internal/content/enhancer.go
+++ b/internal/content/enhancer.go
@@ -15,7 +15,7 @@ type Enhancer struct {
}
// NewEnhancer creates a new HTML enhancer using unified engine
-func NewEnhancer(client ContentClient, siteID string) *Enhancer {
+func NewEnhancer(client engine.ContentClient, siteID string) *Enhancer {
// Create database client for engine
var engineClient engine.ContentClient
if dbClient, ok := client.(*DatabaseClient); ok {
diff --git a/internal/content/markdown.go b/internal/content/markdown.go
deleted file mode 100644
index 4b49db6..0000000
--- a/internal/content/markdown.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package content
-
-import (
- "bytes"
- "log"
- "strings"
-
- "github.com/yuin/goldmark"
- "github.com/yuin/goldmark/parser"
- "github.com/yuin/goldmark/renderer/html"
- "github.com/yuin/goldmark/util"
-)
-
-// MarkdownProcessor handles minimal markdown processing
-// Supports only: **bold**, *italic*, and [link](url)
-type MarkdownProcessor struct {
- parser goldmark.Markdown
-}
-
-// NewMarkdownProcessor creates a new markdown processor with minimal configuration
-func NewMarkdownProcessor() *MarkdownProcessor {
- // Configure goldmark to only support basic inline formatting
- md := goldmark.New(
- goldmark.WithParserOptions(
- parser.WithInlineParsers(
- // Bold (**text**) and italic (*text*) - same parser handles both
- util.Prioritized(parser.NewEmphasisParser(), 500),
-
- // Links [text](url)
- util.Prioritized(parser.NewLinkParser(), 600),
- ),
- // Disable all block parsers except paragraph (no headings, lists, etc.)
- parser.WithBlockParsers(
- util.Prioritized(parser.NewParagraphParser(), 200),
- ),
- ),
- goldmark.WithRendererOptions(
- html.WithXHTML(), //
instead of
- html.WithHardWraps(), // Line breaks become
- html.WithUnsafe(), // Allow existing HTML to pass through
- ),
- )
-
- return &MarkdownProcessor{parser: md}
-}
-
-// ToHTML converts markdown string to HTML
-func (mp *MarkdownProcessor) ToHTML(markdown string) (string, error) {
- if markdown == "" {
- return "", nil
- }
-
- var buf bytes.Buffer
- if err := mp.parser.Convert([]byte(markdown), &buf); err != nil {
- log.Printf("Markdown conversion failed: %v", err)
- return "", err
- }
-
- html := buf.String()
-
- // Clean up goldmark's paragraph wrapping for inline content
- // If content is wrapped in a single
tag, extract just the inner content - html = strings.TrimSpace(html) - - if strings.HasPrefix(html, "
") && strings.HasSuffix(html, "
") { - // Check if this is a single paragraph (no othertags inside) - inner := html[3 : len(html)-4] // Remove
and
- if !strings.Contains(inner, "") { - // Single paragraph - return just the inner content for inline injection - return inner, nil - } - } - - // Multiple paragraphs or other block content - return as-is - return html, nil -} diff --git a/internal/content/mock.go b/internal/content/mock.go index 9d33d70..2d45360 100644 --- a/internal/content/mock.go +++ b/internal/content/mock.go @@ -2,17 +2,19 @@ package content import ( "time" + + "github.com/insertr/insertr/internal/engine" ) // MockClient implements ContentClient with mock data for development type MockClient struct { - data map[string]ContentItem + data map[string]engine.ContentItem } // NewMockClient creates a new mock content client with sample data func NewMockClient() *MockClient { // Generate realistic mock content based on actual generated IDs - data := map[string]ContentItem{ + data := map[string]engine.ContentItem{ // Navigation (index.html has collision suffix) "navbar-logo-2b10ad": { ID: "navbar-logo-2b10ad", @@ -98,7 +100,7 @@ func NewMockClient() *MockClient { } // GetContent fetches a single content item by ID -func (m *MockClient) GetContent(siteID, contentID string) (*ContentItem, error) { +func (m *MockClient) GetContent(siteID, contentID string) (*engine.ContentItem, error) { if item, exists := m.data[contentID]; exists && item.SiteID == siteID { return &item, nil } @@ -108,8 +110,8 @@ func (m *MockClient) GetContent(siteID, contentID string) (*ContentItem, error) } // GetBulkContent fetches multiple content items by IDs -func (m *MockClient) GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error) { - result := make(map[string]ContentItem) +func (m *MockClient) GetBulkContent(siteID string, contentIDs []string) (map[string]engine.ContentItem, error) { + result := make(map[string]engine.ContentItem) for _, id := range contentIDs { item, err := m.GetContent(siteID, id) @@ -125,8 +127,8 @@ func (m *MockClient) GetBulkContent(siteID string, contentIDs []string) (map[str } // GetAllContent fetches all content for a site -func (m *MockClient) GetAllContent(siteID string) (map[string]ContentItem, error) { - result := make(map[string]ContentItem) +func (m *MockClient) GetAllContent(siteID string) (map[string]engine.ContentItem, error) { + result := make(map[string]engine.ContentItem) for _, item := range m.data { if item.SiteID == siteID { diff --git a/internal/content/site_manager.go b/internal/content/site_manager.go index 4ca9dc4..0d16714 100644 --- a/internal/content/site_manager.go +++ b/internal/content/site_manager.go @@ -7,6 +7,8 @@ import ( "path/filepath" "sync" "time" + + "github.com/insertr/insertr/internal/engine" ) // SiteConfig represents configuration for a registered site @@ -28,7 +30,7 @@ type SiteManager struct { } // NewSiteManager creates a new site manager -func NewSiteManager(contentClient ContentClient, backupDir string, devMode bool) *SiteManager { +func NewSiteManager(contentClient engine.ContentClient, backupDir string, devMode bool) *SiteManager { if backupDir == "" { backupDir = "./insertr-backups" } diff --git a/internal/content/types.go b/internal/content/types.go deleted file mode 100644 index b28270f..0000000 --- a/internal/content/types.go +++ /dev/null @@ -1,28 +0,0 @@ -package content - -// ContentItem represents a piece of content from the database -type ContentItem struct { - ID string `json:"id"` - SiteID string `json:"site_id"` - Value string `json:"value"` - Type string `json:"type"` - UpdatedAt string `json:"updated_at"` -} - -// ContentResponse represents the API response structure -type ContentResponse struct { - Content []ContentItem `json:"content"` - Error string `json:"error,omitempty"` -} - -// ContentClient interface for content retrieval -type ContentClient interface { - // GetContent fetches content by ID - GetContent(siteID, contentID string) (*ContentItem, error) - - // GetBulkContent fetches multiple content items by IDs - GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error) - - // GetAllContent fetches all content for a site - GetAllContent(siteID string) (map[string]ContentItem, error) -} diff --git a/internal/engine/database_client.go b/internal/engine/database_client.go index 4e99257..632d8ef 100644 --- a/internal/engine/database_client.go +++ b/internal/engine/database_client.go @@ -110,3 +110,47 @@ func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType()) } } + +// GetAllContent retrieves all content items for a site +func (c *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, error) { + switch c.database.GetDBType() { + case "sqlite3": + contents, err := c.database.GetSQLiteQueries().GetAllContent(context.Background(), siteID) + if err != nil { + return nil, err + } + + items := make(map[string]ContentItem) + for _, content := range contents { + items[content.ID] = ContentItem{ + ID: content.ID, + SiteID: content.SiteID, + Value: content.Value, + Type: content.Type, + LastEditedBy: content.LastEditedBy, + } + } + return items, nil + + case "postgresql": + contents, err := c.database.GetPostgreSQLQueries().GetAllContent(context.Background(), siteID) + if err != nil { + return nil, err + } + + items := make(map[string]ContentItem) + for _, content := range contents { + items[content.ID] = ContentItem{ + ID: content.ID, + SiteID: content.SiteID, + Value: content.Value, + Type: content.Type, + LastEditedBy: content.LastEditedBy, + } + } + return items, nil + + default: + return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType()) + } +} diff --git a/internal/engine/engine.go b/internal/engine/engine.go index 26787f8..4a79ed2 100644 --- a/internal/engine/engine.go +++ b/internal/engine/engine.go @@ -37,20 +37,33 @@ func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, erro processedElements := make([]ProcessedElement, len(elements)) for i, elem := range elements { - // Generate ID using the same algorithm as the parser - id := e.idGenerator.Generate(elem.Node, input.FilePath) + // Check if element already has a data-content-id + existingID := e.getAttribute(elem.Node, "data-content-id") + var id string + var wasGenerated bool + + if existingID != "" { + // Use existing ID from enhanced element + id = existingID + wasGenerated = false + } else { + // Generate new ID for unprocessed element + id = e.idGenerator.Generate(elem.Node, input.FilePath) + wasGenerated = true + } + generatedIDs[fmt.Sprintf("element_%d", i)] = id processedElements[i] = ProcessedElement{ Node: elem.Node, ID: id, Type: elem.Type, - Generated: true, + Generated: wasGenerated, Tag: elem.Node.Data, Classes: GetClasses(elem.Node), } - // Add content attributes to the node + // Add/update content attributes to the node e.addContentAttributes(elem.Node, id, elem.Type) } @@ -133,6 +146,16 @@ func (e *ContentEngine) addContentAttributes(node *html.Node, contentID, content e.setAttribute(node, "data-content-type", contentType) } +// getAttribute gets an attribute value from an HTML node +func (e *ContentEngine) getAttribute(node *html.Node, key string) string { + for _, attr := range node.Attr { + if attr.Key == key { + return attr.Val + } + } + return "" +} + // setAttribute sets an attribute on an HTML node func (e *ContentEngine) setAttribute(node *html.Node, key, value string) { // Remove existing attribute if it exists diff --git a/internal/engine/types.go b/internal/engine/types.go index 0518663..6530c4d 100644 --- a/internal/engine/types.go +++ b/internal/engine/types.go @@ -43,17 +43,25 @@ type ProcessedElement struct { } // ContentClient interface for accessing content data -// This will be implemented by database clients +// This will be implemented by database clients, HTTP clients, and mock clients type ContentClient interface { GetContent(siteID, contentID string) (*ContentItem, error) GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error) + GetAllContent(siteID string) (map[string]ContentItem, error) } // ContentItem represents a piece of content from the database type ContentItem struct { - ID string - SiteID string - Value string - Type string - LastEditedBy string + ID string `json:"id"` + SiteID string `json:"site_id"` + Value string `json:"value"` + Type string `json:"type"` + UpdatedAt string `json:"updated_at"` + LastEditedBy string `json:"last_edited_by,omitempty"` +} + +// ContentResponse represents the API response structure +type ContentResponse struct { + Content []ContentItem `json:"content"` + Error string `json:"error,omitempty"` } diff --git a/lib/src/core/api-client.js b/lib/src/core/api-client.js index b9e405c..8b08a1c 100644 --- a/lib/src/core/api-client.js +++ b/lib/src/core/api-client.js @@ -29,24 +29,15 @@ export class ApiClient { } - async createContent(contentId, content, type, htmlMarkup = null) { + async createContent(content, type, htmlMarkup) { try { const payload = { + html_markup: htmlMarkup, // Always send HTML markup - server extracts ID or generates new one value: content, type: type, file_path: this.getCurrentFilePath() // Always include file path for consistent ID generation }; - if (contentId) { - // Enhanced site - provide existing ID - payload.id = contentId; - } else if (htmlMarkup) { - // Non-enhanced site - provide HTML markup for unified engine ID generation - payload.html_markup = htmlMarkup; - } else { - throw new Error('Either contentId or htmlMarkup must be provided'); - } - const response = await fetch(`${this.baseUrl}?site_id=${this.siteId}`, { method: 'POST', headers: { @@ -61,7 +52,7 @@ export class ApiClient { console.log(`✅ Content created: ${result.id} (${result.type})`); return result; } else { - console.warn(`⚠️ Create failed (${response.status}): ${contentId || 'backend-generated'}`); + console.warn(`⚠️ Create failed (${response.status}): server will generate ID`); return null; } } catch (error) { @@ -69,7 +60,7 @@ export class ApiClient { console.warn(`🔌 API Server not reachable at ${this.baseUrl}`); console.warn('💡 Start full-stack development: just dev'); } else { - console.error('Failed to create content:', contentId, error); + console.error('Failed to create content:', error); } return false; } diff --git a/lib/src/core/editor.js b/lib/src/core/editor.js index 2ccc460..7161c73 100644 --- a/lib/src/core/editor.js +++ b/lib/src/core/editor.js @@ -102,13 +102,12 @@ export class InsertrEditor { contentValue = formData.text || formData; } - // Universal upsert - works for both new and existing content + // Universal upsert - server handles ID extraction/generation from markup const contentType = this.determineContentType(meta.element); const result = await this.apiClient.createContent( - meta.contentId, // Use existing ID if available, null if new contentValue, contentType, - meta.htmlMarkup + meta.htmlMarkup // Always send HTML markup - server is smart about ID handling ); if (result) { diff --git a/lib/src/core/insertr.js b/lib/src/core/insertr.js index f0a2d89..369adfa 100644 --- a/lib/src/core/insertr.js +++ b/lib/src/core/insertr.js @@ -106,6 +106,11 @@ export class InsertrCore { getElementMetadata(element) { const existingId = element.getAttribute('data-content-id'); + // Ensure element has insertr class for server processing + if (!element.classList.contains('insertr')) { + element.classList.add('insertr'); + } + // Send HTML markup to server for unified ID generation return { contentId: existingId, // null if new content, existing ID if updating diff --git a/test-sites/demo-site/insertr.js b/test-sites/demo-site/insertr.js index d118a0a..7599c8f 100644 --- a/test-sites/demo-site/insertr.js +++ b/test-sites/demo-site/insertr.js @@ -109,6 +109,11 @@ var Insertr = (function () { getElementMetadata(element) { const existingId = element.getAttribute('data-content-id'); + // Ensure element has insertr class for server processing + if (!element.classList.contains('insertr')) { + element.classList.add('insertr'); + } + // Send HTML markup to server for unified ID generation return { contentId: existingId, // null if new content, existing ID if updating @@ -2820,13 +2825,12 @@ Please report this to https://github.com/markedjs/marked.`,e){let r="
An error contentValue = formData.text || formData; } - // Universal upsert - works for both new and existing content + // Universal upsert - server handles ID extraction/generation from markup const contentType = this.determineContentType(meta.element); const result = await this.apiClient.createContent( - meta.contentId, // Use existing ID if available, null if new contentValue, contentType, - meta.htmlMarkup + meta.htmlMarkup // Always send HTML markup - server is smart about ID handling ); if (result) { @@ -3761,24 +3765,15 @@ Please report this to https://github.com/markedjs/marked.`,e){let r="
An error } - async createContent(contentId, content, type, htmlMarkup = null) { + async createContent(content, type, htmlMarkup) { try { const payload = { + html_markup: htmlMarkup, // Always send HTML markup - server extracts ID or generates new one value: content, type: type, file_path: this.getCurrentFilePath() // Always include file path for consistent ID generation }; - if (contentId) { - // Enhanced site - provide existing ID - payload.id = contentId; - } else if (htmlMarkup) { - // Non-enhanced site - provide HTML markup for unified engine ID generation - payload.html_markup = htmlMarkup; - } else { - throw new Error('Either contentId or htmlMarkup must be provided'); - } - const response = await fetch(`${this.baseUrl}?site_id=${this.siteId}`, { method: 'POST', headers: { @@ -3793,7 +3788,7 @@ Please report this to https://github.com/markedjs/marked.`,e){let r="
An error console.log(`✅ Content created: ${result.id} (${result.type})`); return result; } else { - console.warn(`⚠️ Create failed (${response.status}): ${contentId || 'backend-generated'}`); + console.warn(`⚠️ Create failed (${response.status}): server will generate ID`); return null; } } catch (error) { @@ -3801,7 +3796,7 @@ Please report this to https://github.com/markedjs/marked.`,e){let r="
An error console.warn(`🔌 API Server not reachable at ${this.baseUrl}`); console.warn('💡 Start full-stack development: just dev'); } else { - console.error('Failed to create content:', contentId, error); + console.error('Failed to create content:', error); } return false; }