Add admin preview views + fix markdown converter
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 5m31s

- Fix _markdown() in lexical_to_sx.py: render markdown to HTML with
  mistune.html() before storing in ~kg-html
- Add shared/sx/prettify.py: sx_to_pretty_sx and json_to_pretty_sx
  produce sx AST for syntax-highlighted DOM (uses canonical serialize())
- Add preview tab to admin header nav
- Add GET /preview/ route with 4 views: prettified sx, prettified
  lexical JSON, sx rendered HTML, lexical rendered HTML
- Add ~blog-preview-panel and ~blog-preview-section components
- Add syntax highlight CSS for sx/JSON tokens

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-02 00:50:57 +00:00
parent a8e06e87fb
commit 4ede0368dc
6 changed files with 300 additions and 1 deletions

View File

@@ -200,6 +200,63 @@ def register():
sx_src = await render_post_data_oob(tctx)
return sx_response(sx_src)
@bp.get("/preview/")
@require_admin
async def preview(slug: str):
from models.ghost_content import Post
from sqlalchemy import select as sa_select
from shared.sx.page import get_template_context
from sx.sx_components import render_post_preview_page, render_post_preview_oob
post_id = g.post_data["post"]["id"]
post = (await g.s.execute(
sa_select(Post).where(Post.id == post_id)
)).scalar_one_or_none()
# Build the 4 preview views
preview_ctx = {}
# 1. Prettified sx source
sx_content = getattr(post, "sx_content", None) or ""
if sx_content:
from shared.sx.prettify import sx_to_pretty_sx
preview_ctx["sx_pretty"] = sx_to_pretty_sx(sx_content)
# 2. Prettified lexical JSON
lexical_raw = getattr(post, "lexical", None) or ""
if lexical_raw:
from shared.sx.prettify import json_to_pretty_sx
preview_ctx["json_pretty"] = json_to_pretty_sx(lexical_raw)
# 3. SX rendered preview
if sx_content:
from shared.sx.parser import parse as sx_parse
from shared.sx.html import render as sx_html_render
from shared.sx.jinja_bridge import _COMPONENT_ENV
try:
parsed = sx_parse(sx_content)
preview_ctx["sx_rendered"] = sx_html_render(parsed, dict(_COMPONENT_ENV))
except Exception:
preview_ctx["sx_rendered"] = "<em>Error rendering sx</em>"
# 4. Lexical rendered preview
if lexical_raw:
from bp.blog.ghost.lexical_renderer import render_lexical
try:
preview_ctx["lex_rendered"] = render_lexical(lexical_raw)
except Exception:
preview_ctx["lex_rendered"] = "<em>Error rendering lexical</em>"
tctx = await get_template_context()
tctx.update(preview_ctx)
if not is_htmx_request():
html = await render_post_preview_page(tctx)
return await make_response(html)
else:
sx_src = await render_post_preview_oob(tctx)
return sx_response(sx_src)
@bp.get("/entries/calendar/<int:calendar_id>/")
@require_admin
async def calendar_view(slug: str, calendar_id: int):