Fix duplicate headers on HTMX nav, editor content loading, and double mount

- Nest admin header inside post-header-child (layouts.py/helpers.py) so
  full-page DOM matches OOB swap structure, eliminating duplicate headers
- Clear post-header-child on post layout OOB to remove stale admin rows
- Read SX initial content from #sx-content-input instead of
  window.__SX_INITIAL__ to avoid escaping issues through SX pipeline
- Fix client-side SX parser RE_STRING to handle escaped newlines
- Clear root element in SxEditor.mount() to prevent double content on
  HTMX re-mount
- Remove unused ~blog-editor-sx-initial component

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 16:27:47 +00:00
parent df8b19ccb8
commit a98354c0f0
6 changed files with 14 additions and 19 deletions

View File

@@ -153,10 +153,6 @@
" sync();"
"})();"))
;; SX initial content script for edit form
(defcomp ~blog-editor-sx-initial (&key sx-content)
(script (str "window.__SX_INITIAL__ = '" sx-content "' || null;")))
(defcomp ~blog-editor-styles (&key css-href)
(<> (link :rel "stylesheet" :href css-href)
(style

View File

@@ -1271,7 +1271,7 @@ def render_editor_panel(save_error: str | None = None, is_page: bool = False) ->
"\n"
" if (typeof SxEditor !== 'undefined') {\n"
" SxEditor.mount('sx-editor', {\n"
" initialSx: window.__SX_INITIAL__ || null,\n"
" initialSx: (document.getElementById('sx-content-input') || {}).value || null,\n"
" csrfToken: csrfToken,\n"
" uploadUrls: uploadUrls,\n"
f" oembedUrl: '{oembed_url}',\n"
@@ -1748,16 +1748,13 @@ def _post_edit_content_sx(ctx: dict) -> str:
badge_parts.append(f'(span :class "inline-block px-2 py-0.5 rounded-full text-xs font-semibold bg-green-100 text-green-800" "Emailed{suffix}")')
footer_extra_sx = SxExpr("(<> " + " ".join(badge_parts) + ")") if badge_parts else None
# Escape sx_content for JS string literal
sx_initial_escaped = sx_content.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n').replace('\r', '')
parts: list[str] = []
# Error banner
if save_error:
parts.append(sx_call("blog-editor-error", error=save_error))
# Form
# Form (sx_content_val populates #sx-content-input; JS reads from there)
parts.append(sx_call("blog-editor-edit-form",
csrf=csrf,
updated_at=str(updated_at),
@@ -1778,9 +1775,6 @@ def _post_edit_content_sx(ctx: dict) -> str:
# Publish-mode JS
parts.append(sx_call("blog-editor-publish-js", already_emailed=already_emailed))
# SX initial content
parts.append(sx_call("blog-editor-sx-initial", sx_content=sx_initial_escaped))
# Editor CSS + styles
parts.append(sx_call("blog-editor-styles", css_href=editor_css))
parts.append(sx_call("sx-editor-styles"))
@@ -1863,7 +1857,7 @@ def _post_edit_content_sx(ctx: dict) -> str:
' });'
" if (typeof SxEditor !== 'undefined') {"
" SxEditor.mount('sx-editor', {"
" initialSx: window.__SX_INITIAL__ || null,"
" initialSx: (document.getElementById('sx-content-input') || {}).value || null,"
' csrfToken: csrfToken,'
' uploadUrls: uploadUrls,'
f" oembedUrl: '{oembed_url}',"