feat: complete HTML-first architecture implementation (Phase 1 & 2)
Major architectural simplification removing content type complexity: Database Schema: - Remove 'type' field from content and content_versions tables - Simplify to pure HTML storage with html_content + original_template - Regenerate all sqlc models for SQLite and PostgreSQL API Simplification: - Remove content type routing and validation - Eliminate type-specific handlers (text/markdown/structured) - Unified HTML-first approach for all content operations - Simplify CreateContent and UpdateContent to HTML-only Backend Enhancements: - Update enhancer to only generate data-content-id (no data-content-type) - Improve container expansion utilities with comprehensive block/inline rules - Add Phase 3 preparation with boundary-respecting traversal logic - Strengthen element classification for viable children detection Documentation: - Update TODO.md to reflect Phase 1-3 completion status - Add WORKING_ON.md documenting the architectural transformation - Mark container expansion and HTML-first architecture as complete This completes the transition to a unified HTML-first content management system with automatic style detection and element-based behavior, eliminating the complex multi-type system in favor of semantic HTML-driven editing.
This commit is contained in:
@@ -314,14 +314,7 @@ func (h *ContentHandler) CreateContent(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Determine content type: use provided type, fallback to existing type, default to "text"
|
||||
contentType := req.Type
|
||||
if contentType == "" && contentExists {
|
||||
contentType = h.getContentType(existingContent)
|
||||
}
|
||||
if contentType == "" {
|
||||
contentType = "text" // default type for new content
|
||||
}
|
||||
// HTML-first approach: no content type needed
|
||||
|
||||
var content interface{}
|
||||
var err error
|
||||
@@ -333,7 +326,6 @@ func (h *ContentHandler) CreateContent(w http.ResponseWriter, r *http.Request) {
|
||||
SiteID: siteID,
|
||||
HtmlContent: req.HTMLContent,
|
||||
OriginalTemplate: toNullString(req.OriginalTemplate),
|
||||
Type: contentType,
|
||||
LastEditedBy: userID,
|
||||
})
|
||||
case "postgresql":
|
||||
@@ -342,7 +334,6 @@ func (h *ContentHandler) CreateContent(w http.ResponseWriter, r *http.Request) {
|
||||
SiteID: siteID,
|
||||
HtmlContent: req.HTMLContent,
|
||||
OriginalTemplate: toNullString(req.OriginalTemplate),
|
||||
Type: contentType,
|
||||
LastEditedBy: userID,
|
||||
})
|
||||
default:
|
||||
@@ -447,7 +438,6 @@ func (h *ContentHandler) UpdateContent(w http.ResponseWriter, r *http.Request) {
|
||||
case "sqlite3":
|
||||
updatedContent, err = h.database.GetSQLiteQueries().UpdateContent(context.Background(), sqlite.UpdateContentParams{
|
||||
HtmlContent: req.HTMLContent,
|
||||
Type: h.getContentType(existingContent),
|
||||
LastEditedBy: userID,
|
||||
ID: contentID,
|
||||
SiteID: siteID,
|
||||
@@ -455,7 +445,6 @@ func (h *ContentHandler) UpdateContent(w http.ResponseWriter, r *http.Request) {
|
||||
case "postgresql":
|
||||
updatedContent, err = h.database.GetPostgreSQLQueries().UpdateContent(context.Background(), postgresql.UpdateContentParams{
|
||||
HtmlContent: req.HTMLContent,
|
||||
Type: h.getContentType(existingContent),
|
||||
LastEditedBy: userID,
|
||||
ID: contentID,
|
||||
SiteID: siteID,
|
||||
@@ -664,7 +653,6 @@ func (h *ContentHandler) RollbackContent(w http.ResponseWriter, r *http.Request)
|
||||
sqliteVersion := targetVersion.(sqlite.ContentVersion)
|
||||
updatedContent, err = h.database.GetSQLiteQueries().UpdateContent(context.Background(), sqlite.UpdateContentParams{
|
||||
HtmlContent: sqliteVersion.HtmlContent,
|
||||
Type: sqliteVersion.Type,
|
||||
LastEditedBy: userID,
|
||||
ID: contentID,
|
||||
SiteID: siteID,
|
||||
@@ -673,7 +661,6 @@ func (h *ContentHandler) RollbackContent(w http.ResponseWriter, r *http.Request)
|
||||
pgVersion := targetVersion.(postgresql.ContentVersion)
|
||||
updatedContent, err = h.database.GetPostgreSQLQueries().UpdateContent(context.Background(), postgresql.UpdateContentParams{
|
||||
HtmlContent: pgVersion.HtmlContent,
|
||||
Type: pgVersion.Type,
|
||||
LastEditedBy: userID,
|
||||
ID: contentID,
|
||||
SiteID: siteID,
|
||||
@@ -704,7 +691,6 @@ func (h *ContentHandler) convertToAPIContent(content interface{}) ContentItem {
|
||||
SiteID: c.SiteID,
|
||||
HTMLContent: c.HtmlContent,
|
||||
OriginalTemplate: fromNullString(c.OriginalTemplate),
|
||||
Type: c.Type,
|
||||
CreatedAt: time.Unix(c.CreatedAt, 0),
|
||||
UpdatedAt: time.Unix(c.UpdatedAt, 0),
|
||||
LastEditedBy: c.LastEditedBy,
|
||||
@@ -716,7 +702,6 @@ func (h *ContentHandler) convertToAPIContent(content interface{}) ContentItem {
|
||||
SiteID: c.SiteID,
|
||||
HTMLContent: c.HtmlContent,
|
||||
OriginalTemplate: fromNullString(c.OriginalTemplate),
|
||||
Type: c.Type,
|
||||
CreatedAt: time.Unix(c.CreatedAt, 0),
|
||||
UpdatedAt: time.Unix(c.UpdatedAt, 0),
|
||||
LastEditedBy: c.LastEditedBy,
|
||||
@@ -757,7 +742,6 @@ func (h *ContentHandler) convertToAPIVersionList(versionList interface{}) []Cont
|
||||
SiteID: version.SiteID,
|
||||
HTMLContent: version.HtmlContent,
|
||||
OriginalTemplate: fromNullString(version.OriginalTemplate),
|
||||
Type: version.Type,
|
||||
CreatedAt: time.Unix(version.CreatedAt, 0),
|
||||
CreatedBy: version.CreatedBy,
|
||||
}
|
||||
@@ -773,7 +757,6 @@ func (h *ContentHandler) convertToAPIVersionList(versionList interface{}) []Cont
|
||||
SiteID: version.SiteID,
|
||||
HTMLContent: version.HtmlContent,
|
||||
OriginalTemplate: fromNullString(version.OriginalTemplate),
|
||||
Type: version.Type,
|
||||
CreatedAt: time.Unix(version.CreatedAt, 0),
|
||||
CreatedBy: version.CreatedBy,
|
||||
}
|
||||
@@ -792,7 +775,6 @@ func (h *ContentHandler) createContentVersion(content interface{}) error {
|
||||
SiteID: c.SiteID,
|
||||
HtmlContent: c.HtmlContent,
|
||||
OriginalTemplate: c.OriginalTemplate,
|
||||
Type: c.Type,
|
||||
CreatedBy: c.LastEditedBy,
|
||||
})
|
||||
case "postgresql":
|
||||
@@ -802,23 +784,12 @@ func (h *ContentHandler) createContentVersion(content interface{}) error {
|
||||
SiteID: c.SiteID,
|
||||
HtmlContent: c.HtmlContent,
|
||||
OriginalTemplate: c.OriginalTemplate,
|
||||
Type: c.Type,
|
||||
CreatedBy: c.LastEditedBy,
|
||||
})
|
||||
}
|
||||
return fmt.Errorf("unsupported database type")
|
||||
}
|
||||
|
||||
func (h *ContentHandler) getContentType(content interface{}) string {
|
||||
switch h.database.GetDBType() {
|
||||
case "sqlite3":
|
||||
return content.(sqlite.Content).Type
|
||||
case "postgresql":
|
||||
return content.(postgresql.Content).Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *ContentHandler) versionMatches(version interface{}, contentID, siteID string) bool {
|
||||
switch h.database.GetDBType() {
|
||||
case "sqlite3":
|
||||
|
||||
@@ -8,7 +8,6 @@ type ContentItem struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HTMLContent string `json:"html_content"`
|
||||
OriginalTemplate string `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
@@ -20,7 +19,6 @@ type ContentVersion struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HTMLContent string `json:"html_content"`
|
||||
OriginalTemplate string `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
@@ -48,7 +46,6 @@ type CreateContentRequest struct {
|
||||
FilePath string `json:"file_path"` // File path for consistent ID generation
|
||||
HTMLContent string `json:"html_content"` // HTML content value
|
||||
OriginalTemplate string `json:"original_template"` // Original template markup
|
||||
Type string `json:"type"` // Content type
|
||||
SiteID string `json:"site_id,omitempty"` // Site identifier
|
||||
CreatedBy string `json:"created_by,omitempty"` // User who created the content
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ func (c *HTTPClient) GetAllContent(siteID string) (map[string]engine.ContentItem
|
||||
}
|
||||
|
||||
// CreateContent creates a new content item via HTTP API
|
||||
func (c *HTTPClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*engine.ContentItem, error) {
|
||||
func (c *HTTPClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*engine.ContentItem, error) {
|
||||
// For now, HTTPClient CreateContent is not implemented for enhancer use
|
||||
// This would typically be used in API-driven enhancement scenarios
|
||||
return nil, fmt.Errorf("CreateContent not implemented for HTTPClient - use DatabaseClient for enhancement")
|
||||
|
||||
@@ -144,7 +144,6 @@ func (d *DatabaseClient) convertToContentItem(content interface{}) engine.Conten
|
||||
SiteID: c.SiteID,
|
||||
HTMLContent: c.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(c.OriginalTemplate),
|
||||
Type: c.Type,
|
||||
UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339),
|
||||
}
|
||||
case "postgresql":
|
||||
@@ -154,7 +153,6 @@ func (d *DatabaseClient) convertToContentItem(content interface{}) engine.Conten
|
||||
SiteID: c.SiteID,
|
||||
HTMLContent: c.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(c.OriginalTemplate),
|
||||
Type: c.Type,
|
||||
UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
@@ -182,8 +180,8 @@ func (d *DatabaseClient) convertToContentItemList(contentList interface{}) []eng
|
||||
return []engine.ContentItem{} // Should never happen
|
||||
}
|
||||
|
||||
// CreateContent creates a new content item
|
||||
func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*engine.ContentItem, error) {
|
||||
// CreateContent creates a new content item
|
||||
func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*engine.ContentItem, error) {
|
||||
switch c.db.GetDBType() {
|
||||
case "sqlite3":
|
||||
content, err := c.db.GetSQLiteQueries().CreateContent(context.Background(), sqlite.CreateContentParams{
|
||||
@@ -191,7 +189,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
||||
SiteID: siteID,
|
||||
HtmlContent: htmlContent,
|
||||
OriginalTemplate: toNullString(originalTemplate),
|
||||
Type: contentType,
|
||||
LastEditedBy: lastEditedBy,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -202,7 +199,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}, nil
|
||||
|
||||
@@ -212,7 +208,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
||||
SiteID: siteID,
|
||||
HtmlContent: htmlContent,
|
||||
OriginalTemplate: toNullString(originalTemplate),
|
||||
Type: contentType,
|
||||
LastEditedBy: lastEditedBy,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -223,7 +218,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}, nil
|
||||
|
||||
|
||||
@@ -20,15 +20,15 @@ func NewMockClient() *MockClient {
|
||||
ID: "navbar-logo-2b10ad",
|
||||
SiteID: "demo",
|
||||
HTMLContent: "Acme Consulting Solutions",
|
||||
Type: "text",
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
"navbar-logo-2b10ad-a44bad": {
|
||||
ID: "navbar-logo-2b10ad-a44bad",
|
||||
SiteID: "demo",
|
||||
HTMLContent: "Acme Business Advisors",
|
||||
Type: "text",
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
|
||||
// Hero Section - index.html (updated with actual IDs)
|
||||
@@ -36,22 +36,22 @@ func NewMockClient() *MockClient {
|
||||
ID: "hero-title-7cfeea",
|
||||
SiteID: "demo",
|
||||
HTMLContent: "Transform Your Business with Strategic Expertise",
|
||||
Type: "text",
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
"hero-lead-e47475": {
|
||||
ID: "hero-lead-e47475",
|
||||
SiteID: "demo",
|
||||
HTMLContent: "We help <strong>ambitious businesses</strong> grow through strategic planning, process optimization, and digital transformation. Our team brings 20+ years of experience to accelerate your success.",
|
||||
Type: "text",
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
"hero-link-76c620": {
|
||||
ID: "hero-link-76c620",
|
||||
SiteID: "demo",
|
||||
HTMLContent: "Schedule Free Consultation",
|
||||
Type: "link",
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
|
||||
// Hero Section - about.html
|
||||
@@ -59,15 +59,15 @@ func NewMockClient() *MockClient {
|
||||
ID: "hero-title-c70343",
|
||||
SiteID: "demo",
|
||||
HTMLContent: "About Our Consulting Expertise",
|
||||
Type: "text",
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
"hero-lead-673026": {
|
||||
ID: "hero-lead-673026",
|
||||
SiteID: "demo",
|
||||
HTMLContent: "We're a team of <strong>experienced consultants</strong> dedicated to helping small businesses thrive in today's competitive marketplace through proven strategies.",
|
||||
Type: "text",
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
|
||||
// Services Section
|
||||
@@ -75,15 +75,15 @@ func NewMockClient() *MockClient {
|
||||
ID: "services-subtitle-c8927c",
|
||||
SiteID: "demo",
|
||||
HTMLContent: "Our Story",
|
||||
Type: "text",
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
"services-text-0d96da": {
|
||||
ID: "services-text-0d96da",
|
||||
SiteID: "demo",
|
||||
HTMLContent: "<strong>Founded in 2020</strong>, Acme Consulting emerged from a simple observation: small businesses needed access to the same high-quality strategic advice that large corporations receive, but in a format that was accessible, affordable, and actionable.",
|
||||
Type: "text",
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
|
||||
// Default fallback for any missing content
|
||||
@@ -91,8 +91,8 @@ func NewMockClient() *MockClient {
|
||||
ID: "default",
|
||||
SiteID: "demo",
|
||||
HTMLContent: "[Enhanced Content]",
|
||||
Type: "text",
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -140,20 +140,19 @@ func (m *MockClient) GetAllContent(siteID string) (map[string]engine.ContentItem
|
||||
}
|
||||
|
||||
// CreateContent creates a new mock content item
|
||||
func (m *MockClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*engine.ContentItem, error) {
|
||||
func (m *MockClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*engine.ContentItem, error) {
|
||||
// For mock client, just create and store the item
|
||||
item := engine.ContentItem{
|
||||
ID: contentID,
|
||||
SiteID: siteID,
|
||||
HTMLContent: htmlContent,
|
||||
OriginalTemplate: originalTemplate,
|
||||
Type: contentType,
|
||||
UpdatedAt: time.Now().Format(time.RFC3339),
|
||||
LastEditedBy: lastEditedBy,
|
||||
}
|
||||
|
||||
|
||||
// Store in mock data
|
||||
m.data[contentID] = item
|
||||
|
||||
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
)
|
||||
|
||||
const createContent = `-- name: CreateContent :one
|
||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type CreateContentParams struct {
|
||||
@@ -22,7 +22,6 @@ type CreateContentParams struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
@@ -32,7 +31,6 @@ func (q *Queries) CreateContent(ctx context.Context, arg CreateContentParams) (C
|
||||
arg.SiteID,
|
||||
arg.HtmlContent,
|
||||
arg.OriginalTemplate,
|
||||
arg.Type,
|
||||
arg.LastEditedBy,
|
||||
)
|
||||
var i Content
|
||||
@@ -41,7 +39,6 @@ func (q *Queries) CreateContent(ctx context.Context, arg CreateContentParams) (C
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
@@ -75,7 +72,7 @@ func (q *Queries) DeleteContent(ctx context.Context, arg DeleteContentParams) er
|
||||
}
|
||||
|
||||
const getAllContent = `-- name: GetAllContent :many
|
||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE site_id = $1
|
||||
ORDER BY updated_at DESC
|
||||
@@ -95,7 +92,6 @@ func (q *Queries) GetAllContent(ctx context.Context, siteID string) ([]Content,
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
@@ -114,7 +110,7 @@ func (q *Queries) GetAllContent(ctx context.Context, siteID string) ([]Content,
|
||||
}
|
||||
|
||||
const getBulkContent = `-- name: GetBulkContent :many
|
||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE site_id = $1 AND id IN ($2)
|
||||
`
|
||||
@@ -149,7 +145,6 @@ func (q *Queries) GetBulkContent(ctx context.Context, arg GetBulkContentParams)
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
@@ -168,7 +163,7 @@ func (q *Queries) GetBulkContent(ctx context.Context, arg GetBulkContentParams)
|
||||
}
|
||||
|
||||
const getContent = `-- name: GetContent :one
|
||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE id = $1 AND site_id = $2
|
||||
`
|
||||
@@ -186,7 +181,6 @@ func (q *Queries) GetContent(ctx context.Context, arg GetContentParams) (Content
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
@@ -196,14 +190,13 @@ func (q *Queries) GetContent(ctx context.Context, arg GetContentParams) (Content
|
||||
|
||||
const updateContent = `-- name: UpdateContent :one
|
||||
UPDATE content
|
||||
SET html_content = $1, type = $2, last_edited_by = $3
|
||||
WHERE id = $4 AND site_id = $5
|
||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SET html_content = $1, last_edited_by = $2
|
||||
WHERE id = $3 AND site_id = $4
|
||||
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type UpdateContentParams struct {
|
||||
HtmlContent string `json:"html_content"`
|
||||
Type string `json:"type"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
@@ -212,7 +205,6 @@ type UpdateContentParams struct {
|
||||
func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (Content, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateContent,
|
||||
arg.HtmlContent,
|
||||
arg.Type,
|
||||
arg.LastEditedBy,
|
||||
arg.ID,
|
||||
arg.SiteID,
|
||||
@@ -223,7 +215,6 @@ func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (C
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
@@ -232,13 +223,12 @@ func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (C
|
||||
}
|
||||
|
||||
const upsertContent = `-- name: UpsertContent :one
|
||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT(id, site_id) DO UPDATE SET
|
||||
html_content = EXCLUDED.html_content,
|
||||
type = EXCLUDED.type,
|
||||
last_edited_by = EXCLUDED.last_edited_by
|
||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type UpsertContentParams struct {
|
||||
@@ -246,7 +236,6 @@ type UpsertContentParams struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
@@ -256,7 +245,6 @@ func (q *Queries) UpsertContent(ctx context.Context, arg UpsertContentParams) (C
|
||||
arg.SiteID,
|
||||
arg.HtmlContent,
|
||||
arg.OriginalTemplate,
|
||||
arg.Type,
|
||||
arg.LastEditedBy,
|
||||
)
|
||||
var i Content
|
||||
@@ -265,7 +253,6 @@ func (q *Queries) UpsertContent(ctx context.Context, arg UpsertContentParams) (C
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
|
||||
@@ -13,7 +13,6 @@ type Content struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
@@ -25,7 +24,6 @@ type ContentVersion struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ CREATE TABLE content (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL,
|
||||
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||
updated_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
@@ -19,7 +18,6 @@ CREATE TABLE content_versions (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL,
|
||||
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
@@ -4,7 +4,6 @@ CREATE TABLE IF NOT EXISTS content (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL CHECK (type IN ('text', 'link')),
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
updated_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
@@ -18,7 +17,6 @@ CREATE TABLE IF NOT EXISTS content_versions (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
@@ -57,7 +57,6 @@ CREATE TABLE IF NOT EXISTS content (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL CHECK (type IN ('text', 'link')),
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
updated_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
@@ -77,7 +76,6 @@ CREATE TABLE IF NOT EXISTS content_versions (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
)
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
)
|
||||
|
||||
const createContentVersion = `-- name: CreateContentVersion :exec
|
||||
INSERT INTO content_versions (content_id, site_id, html_content, original_template, type, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
INSERT INTO content_versions (content_id, site_id, html_content, original_template, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
`
|
||||
|
||||
type CreateContentVersionParams struct {
|
||||
@@ -20,7 +20,6 @@ type CreateContentVersionParams struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
|
||||
@@ -30,7 +29,6 @@ func (q *Queries) CreateContentVersion(ctx context.Context, arg CreateContentVer
|
||||
arg.SiteID,
|
||||
arg.HtmlContent,
|
||||
arg.OriginalTemplate,
|
||||
arg.Type,
|
||||
arg.CreatedBy,
|
||||
)
|
||||
return err
|
||||
@@ -53,7 +51,7 @@ func (q *Queries) DeleteOldVersions(ctx context.Context, arg DeleteOldVersionsPa
|
||||
|
||||
const getAllVersionsForSite = `-- name: GetAllVersionsForSite :many
|
||||
SELECT
|
||||
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.type, cv.created_at, cv.created_by,
|
||||
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.created_at, cv.created_by,
|
||||
c.html_content as current_html_content
|
||||
FROM content_versions cv
|
||||
LEFT JOIN content c ON cv.content_id = c.id AND cv.site_id = c.site_id
|
||||
@@ -73,7 +71,6 @@ type GetAllVersionsForSiteRow struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
CurrentHtmlContent sql.NullString `json:"current_html_content"`
|
||||
@@ -94,7 +91,6 @@ func (q *Queries) GetAllVersionsForSite(ctx context.Context, arg GetAllVersionsF
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
&i.CurrentHtmlContent,
|
||||
@@ -113,7 +109,7 @@ func (q *Queries) GetAllVersionsForSite(ctx context.Context, arg GetAllVersionsF
|
||||
}
|
||||
|
||||
const getContentVersion = `-- name: GetContentVersion :one
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||
FROM content_versions
|
||||
WHERE version_id = $1
|
||||
`
|
||||
@@ -127,7 +123,6 @@ func (q *Queries) GetContentVersion(ctx context.Context, versionID int32) (Conte
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
)
|
||||
@@ -135,7 +130,7 @@ func (q *Queries) GetContentVersion(ctx context.Context, versionID int32) (Conte
|
||||
}
|
||||
|
||||
const getContentVersionHistory = `-- name: GetContentVersionHistory :many
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||
FROM content_versions
|
||||
WHERE content_id = $1 AND site_id = $2
|
||||
ORDER BY created_at DESC
|
||||
@@ -163,7 +158,6 @@ func (q *Queries) GetContentVersionHistory(ctx context.Context, arg GetContentVe
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
); err != nil {
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
-- name: GetContent :one
|
||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id);
|
||||
|
||||
-- name: GetAllContent :many
|
||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE site_id = sqlc.arg(site_id)
|
||||
ORDER BY updated_at DESC;
|
||||
|
||||
-- name: GetBulkContent :many
|
||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE site_id = sqlc.arg(site_id) AND id IN (sqlc.slice('ids'));
|
||||
|
||||
-- name: CreateContent :one
|
||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
||||
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(type), sqlc.arg(last_edited_by))
|
||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by;
|
||||
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(last_edited_by))
|
||||
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: UpdateContent :one
|
||||
UPDATE content
|
||||
SET html_content = sqlc.arg(html_content), type = sqlc.arg(type), last_edited_by = sqlc.arg(last_edited_by)
|
||||
SET html_content = sqlc.arg(html_content), last_edited_by = sqlc.arg(last_edited_by)
|
||||
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id)
|
||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by;
|
||||
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: UpsertContent :one
|
||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
||||
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(type), sqlc.arg(last_edited_by))
|
||||
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(last_edited_by))
|
||||
ON CONFLICT(id, site_id) DO UPDATE SET
|
||||
html_content = EXCLUDED.html_content,
|
||||
type = EXCLUDED.type,
|
||||
last_edited_by = EXCLUDED.last_edited_by
|
||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by;
|
||||
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: DeleteContent :exec
|
||||
DELETE FROM content
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
-- name: CreateContentVersion :exec
|
||||
INSERT INTO content_versions (content_id, site_id, html_content, original_template, type, created_by)
|
||||
VALUES (sqlc.arg(content_id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(type), sqlc.arg(created_by));
|
||||
INSERT INTO content_versions (content_id, site_id, html_content, original_template, created_by)
|
||||
VALUES (sqlc.arg(content_id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(original_template), sqlc.arg(created_by));
|
||||
|
||||
-- name: GetContentVersionHistory :many
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||
FROM content_versions
|
||||
WHERE content_id = sqlc.arg(content_id) AND site_id = sqlc.arg(site_id)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT sqlc.arg(limit_count);
|
||||
|
||||
-- name: GetContentVersion :one
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||
FROM content_versions
|
||||
WHERE version_id = sqlc.arg(version_id);
|
||||
|
||||
-- name: GetAllVersionsForSite :many
|
||||
SELECT
|
||||
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.type, cv.created_at, cv.created_by,
|
||||
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.created_at, cv.created_by,
|
||||
c.html_content as current_html_content
|
||||
FROM content_versions cv
|
||||
LEFT JOIN content c ON cv.content_id = c.id AND cv.site_id = c.site_id
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
)
|
||||
|
||||
const createContent = `-- name: CreateContent :one
|
||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type CreateContentParams struct {
|
||||
@@ -22,7 +22,6 @@ type CreateContentParams struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
@@ -32,7 +31,6 @@ func (q *Queries) CreateContent(ctx context.Context, arg CreateContentParams) (C
|
||||
arg.SiteID,
|
||||
arg.HtmlContent,
|
||||
arg.OriginalTemplate,
|
||||
arg.Type,
|
||||
arg.LastEditedBy,
|
||||
)
|
||||
var i Content
|
||||
@@ -41,7 +39,6 @@ func (q *Queries) CreateContent(ctx context.Context, arg CreateContentParams) (C
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
@@ -75,7 +72,7 @@ func (q *Queries) DeleteContent(ctx context.Context, arg DeleteContentParams) er
|
||||
}
|
||||
|
||||
const getAllContent = `-- name: GetAllContent :many
|
||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE site_id = ?1
|
||||
ORDER BY updated_at DESC
|
||||
@@ -95,7 +92,6 @@ func (q *Queries) GetAllContent(ctx context.Context, siteID string) ([]Content,
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
@@ -114,7 +110,7 @@ func (q *Queries) GetAllContent(ctx context.Context, siteID string) ([]Content,
|
||||
}
|
||||
|
||||
const getBulkContent = `-- name: GetBulkContent :many
|
||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE site_id = ?1 AND id IN (/*SLICE:ids*/?)
|
||||
`
|
||||
@@ -149,7 +145,6 @@ func (q *Queries) GetBulkContent(ctx context.Context, arg GetBulkContentParams)
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
@@ -168,7 +163,7 @@ func (q *Queries) GetBulkContent(ctx context.Context, arg GetBulkContentParams)
|
||||
}
|
||||
|
||||
const getContent = `-- name: GetContent :one
|
||||
SELECT id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SELECT id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE id = ?1 AND site_id = ?2
|
||||
`
|
||||
@@ -186,7 +181,6 @@ func (q *Queries) GetContent(ctx context.Context, arg GetContentParams) (Content
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
@@ -196,14 +190,13 @@ func (q *Queries) GetContent(ctx context.Context, arg GetContentParams) (Content
|
||||
|
||||
const updateContent = `-- name: UpdateContent :one
|
||||
UPDATE content
|
||||
SET html_content = ?1, type = ?2, last_edited_by = ?3
|
||||
WHERE id = ?4 AND site_id = ?5
|
||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
SET html_content = ?1, last_edited_by = ?2
|
||||
WHERE id = ?3 AND site_id = ?4
|
||||
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type UpdateContentParams struct {
|
||||
HtmlContent string `json:"html_content"`
|
||||
Type string `json:"type"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
@@ -212,7 +205,6 @@ type UpdateContentParams struct {
|
||||
func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (Content, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateContent,
|
||||
arg.HtmlContent,
|
||||
arg.Type,
|
||||
arg.LastEditedBy,
|
||||
arg.ID,
|
||||
arg.SiteID,
|
||||
@@ -223,7 +215,6 @@ func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (C
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
@@ -232,13 +223,12 @@ func (q *Queries) UpdateContent(ctx context.Context, arg UpdateContentParams) (C
|
||||
}
|
||||
|
||||
const upsertContent = `-- name: UpsertContent :one
|
||||
INSERT INTO content (id, site_id, html_content, original_template, type, last_edited_by)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
||||
INSERT INTO content (id, site_id, html_content, original_template, last_edited_by)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||
ON CONFLICT(id, site_id) DO UPDATE SET
|
||||
html_content = EXCLUDED.html_content,
|
||||
type = EXCLUDED.type,
|
||||
last_edited_by = EXCLUDED.last_edited_by
|
||||
RETURNING id, site_id, html_content, original_template, type, created_at, updated_at, last_edited_by
|
||||
RETURNING id, site_id, html_content, original_template, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type UpsertContentParams struct {
|
||||
@@ -246,7 +236,6 @@ type UpsertContentParams struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
@@ -256,7 +245,6 @@ func (q *Queries) UpsertContent(ctx context.Context, arg UpsertContentParams) (C
|
||||
arg.SiteID,
|
||||
arg.HtmlContent,
|
||||
arg.OriginalTemplate,
|
||||
arg.Type,
|
||||
arg.LastEditedBy,
|
||||
)
|
||||
var i Content
|
||||
@@ -265,7 +253,6 @@ func (q *Queries) UpsertContent(ctx context.Context, arg UpsertContentParams) (C
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
|
||||
@@ -13,7 +13,6 @@ type Content struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
@@ -25,7 +24,6 @@ type ContentVersion struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ CREATE TABLE content (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
@@ -19,7 +18,6 @@ CREATE TABLE content_versions (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
@@ -4,7 +4,6 @@ CREATE TABLE IF NOT EXISTS content (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL CHECK (type IN ('text', 'link')),
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
@@ -18,7 +17,6 @@ CREATE TABLE IF NOT EXISTS content_versions (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
@@ -15,7 +15,6 @@ CREATE TABLE IF NOT EXISTS content (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL CHECK (type IN ('text', 'link')),
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
@@ -35,7 +34,6 @@ CREATE TABLE IF NOT EXISTS content_versions (
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
original_template TEXT,
|
||||
type TEXT NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
)
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
)
|
||||
|
||||
const createContentVersion = `-- name: CreateContentVersion :exec
|
||||
INSERT INTO content_versions (content_id, site_id, html_content, original_template, type, created_by)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
||||
INSERT INTO content_versions (content_id, site_id, html_content, original_template, created_by)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||
`
|
||||
|
||||
type CreateContentVersionParams struct {
|
||||
@@ -20,7 +20,6 @@ type CreateContentVersionParams struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
|
||||
@@ -30,7 +29,6 @@ func (q *Queries) CreateContentVersion(ctx context.Context, arg CreateContentVer
|
||||
arg.SiteID,
|
||||
arg.HtmlContent,
|
||||
arg.OriginalTemplate,
|
||||
arg.Type,
|
||||
arg.CreatedBy,
|
||||
)
|
||||
return err
|
||||
@@ -53,7 +51,7 @@ func (q *Queries) DeleteOldVersions(ctx context.Context, arg DeleteOldVersionsPa
|
||||
|
||||
const getAllVersionsForSite = `-- name: GetAllVersionsForSite :many
|
||||
SELECT
|
||||
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.type, cv.created_at, cv.created_by,
|
||||
cv.version_id, cv.content_id, cv.site_id, cv.html_content, cv.original_template, cv.created_at, cv.created_by,
|
||||
c.html_content as current_html_content
|
||||
FROM content_versions cv
|
||||
LEFT JOIN content c ON cv.content_id = c.id AND cv.site_id = c.site_id
|
||||
@@ -73,7 +71,6 @@ type GetAllVersionsForSiteRow struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
OriginalTemplate sql.NullString `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
CurrentHtmlContent sql.NullString `json:"current_html_content"`
|
||||
@@ -94,7 +91,6 @@ func (q *Queries) GetAllVersionsForSite(ctx context.Context, arg GetAllVersionsF
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
&i.CurrentHtmlContent,
|
||||
@@ -113,7 +109,7 @@ func (q *Queries) GetAllVersionsForSite(ctx context.Context, arg GetAllVersionsF
|
||||
}
|
||||
|
||||
const getContentVersion = `-- name: GetContentVersion :one
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||
FROM content_versions
|
||||
WHERE version_id = ?1
|
||||
`
|
||||
@@ -127,7 +123,6 @@ func (q *Queries) GetContentVersion(ctx context.Context, versionID int64) (Conte
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
)
|
||||
@@ -135,7 +130,7 @@ func (q *Queries) GetContentVersion(ctx context.Context, versionID int64) (Conte
|
||||
}
|
||||
|
||||
const getContentVersionHistory = `-- name: GetContentVersionHistory :many
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, type, created_at, created_by
|
||||
SELECT version_id, content_id, site_id, html_content, original_template, created_at, created_by
|
||||
FROM content_versions
|
||||
WHERE content_id = ?1 AND site_id = ?2
|
||||
ORDER BY created_at DESC
|
||||
@@ -163,7 +158,6 @@ func (q *Queries) GetContentVersionHistory(ctx context.Context, arg GetContentVe
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.OriginalTemplate,
|
||||
&i.Type,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
); err != nil {
|
||||
|
||||
@@ -46,7 +46,6 @@ func (c *DatabaseClient) GetContent(siteID, contentID string) (*ContentItem, err
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}, nil
|
||||
|
||||
@@ -63,7 +62,6 @@ func (c *DatabaseClient) GetContent(siteID, contentID string) (*ContentItem, err
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}, nil
|
||||
|
||||
@@ -91,7 +89,6 @@ func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}
|
||||
}
|
||||
@@ -113,7 +110,6 @@ func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}
|
||||
}
|
||||
@@ -140,7 +136,6 @@ func (c *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, e
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}
|
||||
}
|
||||
@@ -159,7 +154,6 @@ func (c *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, e
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}
|
||||
}
|
||||
@@ -171,7 +165,7 @@ func (c *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, e
|
||||
}
|
||||
|
||||
// CreateContent creates a new content item
|
||||
func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*ContentItem, error) {
|
||||
func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*ContentItem, error) {
|
||||
switch c.database.GetDBType() {
|
||||
case "sqlite3":
|
||||
content, err := c.database.GetSQLiteQueries().CreateContent(context.Background(), sqlite.CreateContentParams{
|
||||
@@ -179,7 +173,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
||||
SiteID: siteID,
|
||||
HtmlContent: htmlContent,
|
||||
OriginalTemplate: toNullString(originalTemplate),
|
||||
Type: contentType,
|
||||
LastEditedBy: lastEditedBy,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -190,7 +183,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}, nil
|
||||
|
||||
@@ -200,7 +192,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
||||
SiteID: siteID,
|
||||
HtmlContent: htmlContent,
|
||||
OriginalTemplate: toNullString(originalTemplate),
|
||||
Type: contentType,
|
||||
LastEditedBy: lastEditedBy,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -211,7 +202,6 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}, nil
|
||||
|
||||
@@ -220,7 +210,7 @@ func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalT
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to convert string to sql.NullString
|
||||
// Helper function to convert string to sql.NullString
|
||||
func toNullString(s string) sql.NullString {
|
||||
if s == "" {
|
||||
return sql.NullString{Valid: false}
|
||||
|
||||
@@ -35,7 +35,6 @@ type ContentResult struct {
|
||||
type ProcessedElement struct {
|
||||
Node *html.Node // HTML node
|
||||
ID string // Generated content ID
|
||||
Type string // Content type (text, link)
|
||||
Content string // Injected content (if any)
|
||||
Generated bool // Whether ID was generated (vs existing)
|
||||
Tag string // Element tag name
|
||||
@@ -48,7 +47,7 @@ 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)
|
||||
CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*ContentItem, error)
|
||||
CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*ContentItem, error)
|
||||
}
|
||||
|
||||
// ContentItem represents a piece of content from the database
|
||||
@@ -57,7 +56,6 @@ type ContentItem struct {
|
||||
SiteID string `json:"site_id"`
|
||||
HTMLContent string `json:"html_content"`
|
||||
OriginalTemplate string `json:"original_template"`
|
||||
Type string `json:"type"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by,omitempty"`
|
||||
}
|
||||
|
||||
@@ -179,40 +179,149 @@ func isContainer(node *html.Node) bool {
|
||||
"main": true,
|
||||
"aside": true,
|
||||
"nav": true,
|
||||
"ul": true, // Phase 3: Lists are containers
|
||||
"ol": true,
|
||||
}
|
||||
|
||||
return containerTags[node.Data]
|
||||
}
|
||||
|
||||
// findViableChildren finds all child elements that are viable for editing
|
||||
// findViableChildren finds all descendant elements that should get .insertr class
|
||||
// Phase 3: Recursive traversal with block/inline classification and boundary respect
|
||||
func findViableChildren(node *html.Node) []*html.Node {
|
||||
var viable []*html.Node
|
||||
traverseForViableElements(node, &viable)
|
||||
return viable
|
||||
}
|
||||
|
||||
// traverseForViableElements recursively traverses all descendants, stopping at .insertr boundaries
|
||||
func traverseForViableElements(node *html.Node, viable *[]*html.Node) {
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
// Skip whitespace-only text nodes
|
||||
if child.Type == html.TextNode {
|
||||
if strings.TrimSpace(child.Data) == "" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Only consider element nodes
|
||||
if child.Type != html.ElementNode {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip self-closing elements for now
|
||||
if isSelfClosing(child) {
|
||||
// BOUNDARY: Stop if element already has .insertr class
|
||||
if hasInsertrClass(child) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if element has editable content (improved logic)
|
||||
if hasEditableContent(child) {
|
||||
viable = append(viable, child)
|
||||
// Skip deferred complex elements (tables, forms)
|
||||
if isDeferredElement(child) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Determine if this element should get .insertr
|
||||
if shouldGetInsertrClass(child) {
|
||||
*viable = append(*viable, child)
|
||||
// Don't traverse children - they're handled by this element's expansion
|
||||
continue
|
||||
}
|
||||
|
||||
// Continue traversing if this is just a container
|
||||
traverseForViableElements(child, viable)
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Block vs Inline element classification
|
||||
func isBlockElement(node *html.Node) bool {
|
||||
blockTags := map[string]bool{
|
||||
// Content blocks
|
||||
"h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true,
|
||||
"p": true, "div": true, "article": true, "section": true, "nav": true,
|
||||
"header": true, "footer": true, "main": true, "aside": true,
|
||||
// Lists
|
||||
"ul": true, "ol": true, "li": true,
|
||||
// Interactive (when at block level)
|
||||
"button": true, "a": true, "img": true, "video": true, "audio": true,
|
||||
}
|
||||
|
||||
return viable
|
||||
return blockTags[node.Data]
|
||||
}
|
||||
|
||||
// isInlineElement checks if element is inline formatting (never gets .insertr)
|
||||
func isInlineElement(node *html.Node) bool {
|
||||
inlineTags := map[string]bool{
|
||||
"strong": true, "b": true, "em": true, "i": true, "span": true,
|
||||
"code": true, "small": true, "sub": true, "sup": true, "br": true,
|
||||
"mark": true, "kbd": true,
|
||||
}
|
||||
|
||||
return inlineTags[node.Data]
|
||||
}
|
||||
|
||||
// isContextSensitive checks if element can be block or inline (a, button)
|
||||
func isContextSensitive(node *html.Node) bool {
|
||||
contextTags := map[string]bool{
|
||||
"a": true,
|
||||
"button": true,
|
||||
}
|
||||
|
||||
return contextTags[node.Data]
|
||||
}
|
||||
|
||||
// isInBlockContext determines if context-sensitive element should be treated as block
|
||||
func isInBlockContext(node *html.Node) bool {
|
||||
parent := node.Parent
|
||||
if parent == nil || parent.Type != html.ElementNode {
|
||||
return true
|
||||
}
|
||||
|
||||
// If parent is a content element, this is inline formatting
|
||||
contentElements := map[string]bool{
|
||||
"p": true, "h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true,
|
||||
"li": true, "td": true, "th": true,
|
||||
}
|
||||
|
||||
return !contentElements[parent.Data]
|
||||
}
|
||||
|
||||
// shouldGetInsertrClass determines if element should receive .insertr class
|
||||
func shouldGetInsertrClass(node *html.Node) bool {
|
||||
// Always block elements get .insertr
|
||||
if isBlockElement(node) && !isContextSensitive(node) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Context-sensitive elements depend on parent context
|
||||
if isContextSensitive(node) {
|
||||
return isInBlockContext(node)
|
||||
}
|
||||
|
||||
// Inline elements never get .insertr
|
||||
if isInlineElement(node) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Self-closing elements - only img gets .insertr when block-level
|
||||
if isSelfClosing(node) {
|
||||
return node.Data == "img" && isInBlockContext(node)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// isDeferredElement checks for complex elements that need separate planning
|
||||
func isDeferredElement(node *html.Node) bool {
|
||||
deferredTags := map[string]bool{
|
||||
"table": true, "tr": true, "td": true, "th": true,
|
||||
"thead": true, "tbody": true, "tfoot": true,
|
||||
"form": true, "input": true, "textarea": true, "select": true, "option": true,
|
||||
}
|
||||
|
||||
return deferredTags[node.Data]
|
||||
}
|
||||
|
||||
// hasInsertrClass checks if node has class="insertr"
|
||||
func hasInsertrClass(node *html.Node) bool {
|
||||
classes := GetClasses(node)
|
||||
for _, class := range classes {
|
||||
if class == "insertr" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// findViableChildrenLegacy uses the old text-only logic for backwards compatibility
|
||||
|
||||
Reference in New Issue
Block a user