diff --git a/blog/sx/editor.sx b/blog/sx/editor.sx index a55b0ca..1704c74 100644 --- a/blog/sx/editor.sx +++ b/blog/sx/editor.sx @@ -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 diff --git a/blog/sx/sx_components.py b/blog/sx/sx_components.py index 50e1a55..9f383c1 100644 --- a/blog/sx/sx_components.py +++ b/blog/sx/sx_components.py @@ -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}'," diff --git a/shared/static/scripts/sx-editor.js b/shared/static/scripts/sx-editor.js index 80b8974..e72be18 100644 --- a/shared/static/scripts/sx-editor.js +++ b/shared/static/scripts/sx-editor.js @@ -2249,7 +2249,9 @@ return null; } - root.className = (root.className || "") + " sx-editor"; + // Clear any previous mount + root.innerHTML = ""; + root.className = (root.className || "").replace(/\bsx-editor\b/g, "").trim() + " sx-editor"; var container = el("div", { className: "sx-blocks-container" }); root.appendChild(container); diff --git a/shared/static/scripts/sx.js b/shared/static/scripts/sx.js index 536fa7f..136a3d0 100644 --- a/shared/static/scripts/sx.js +++ b/shared/static/scripts/sx.js @@ -76,7 +76,7 @@ var RE_WS = /\s+/y; var RE_COMMENT = /;[^\n]*/y; - var RE_STRING = /"(?:[^"\\]|\\.)*"/y; + var RE_STRING = /"(?:[^"\\]|\\[\s\S])*"/y; var RE_NUMBER = /-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/y; var RE_KEYWORD = /:[a-zA-Z_][a-zA-Z0-9_>:\-]*/y; var RE_SYMBOL = /[a-zA-Z_~*+\-><=/!?&][a-zA-Z0-9_~*+\-><=/!?.:&]*/y; diff --git a/shared/sx/helpers.py b/shared/sx/helpers.py index 0f044d4..32dd77d 100644 --- a/shared/sx/helpers.py +++ b/shared/sx/helpers.py @@ -254,7 +254,7 @@ def search_desktop_sx(ctx: dict) -> str: ) -def post_header_sx(ctx: dict, *, oob: bool = False) -> str: +def post_header_sx(ctx: dict, *, oob: bool = False, child: str = "") -> str: """Build the post-level header row as sx call string.""" post = ctx.get("post") or {} slug = post.get("slug", "") @@ -273,6 +273,7 @@ def post_header_sx(ctx: dict, *, oob: bool = False) -> str: link_label_content=SxExpr(label_sx), nav=SxExpr(nav_sx) if nav_sx else None, child_id="post-header-child", + child=SxExpr(child) if child else None, oob=oob, external=True, ) diff --git a/shared/sx/layouts.py b/shared/sx/layouts.py index 4b4bc0e..4a18b01 100644 --- a/shared/sx/layouts.py +++ b/shared/sx/layouts.py @@ -104,16 +104,18 @@ def _post_full(ctx: dict, **kw: Any) -> str: def _post_oob(ctx: dict, **kw: Any) -> str: post_hdr = post_header_sx(ctx, oob=True) - return post_hdr + # Also replace #post-header-child (empty — clears any nested admin rows) + child_oob = oob_header_sx("post-header-child", "", "") + return "(<> " + post_hdr + " " + child_oob + ")" def _post_admin_full(ctx: dict, **kw: Any) -> str: slug = ctx.get("post", {}).get("slug", "") selected = kw.get("selected", "") root_hdr = root_header_sx(ctx) - post_hdr = post_header_sx(ctx) admin_hdr = post_admin_header_sx(ctx, slug, selected=selected) - return "(<> " + root_hdr + " " + post_hdr + " " + admin_hdr + ")" + post_hdr = post_header_sx(ctx, child=admin_hdr) + return "(<> " + root_hdr + " " + post_hdr + ")" def _post_admin_oob(ctx: dict, **kw: Any) -> str: