Wire s-expression rendering into live app — blog link-card

- Add setup_sexp_bridge() and load_shared_components() to factory.py
  so all services get s-expression support automatically
- Create shared/sexp/components.py with ~link-card component definition
  (replaces 5 per-service Jinja link_card.html templates)
- Replace blog's link-card fragment handler to use sexp() instead of
  render_template() — first real s-expression rendered page content

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 14:38:51 +00:00
parent 5d9f1586af
commit 28c66c3650
3 changed files with 73 additions and 17 deletions

View File

@@ -28,6 +28,8 @@ from shared.browser.app.errors import errors
from .jinja_setup import setup_jinja
from .user_loader import load_current_user
from shared.sexp.jinja_bridge import setup_sexp_bridge
from shared.sexp.components import load_shared_components
# Async init of config (runs once at import)
@@ -104,6 +106,8 @@ def create_base_app(
register_db(app)
register_redis(app)
setup_jinja(app)
setup_sexp_bridge(app)
load_shared_components()
errors(app)
# Auto-register OAuth client blueprint for non-account apps

51
shared/sexp/components.py Normal file
View File

@@ -0,0 +1,51 @@
"""
Shared s-expression component definitions.
Loaded at app startup via ``load_shared_components()``. Each component
replaces a per-service Jinja fragment template with a single reusable
s-expression definition.
"""
from __future__ import annotations
from .jinja_bridge import register_components
def load_shared_components() -> None:
"""Register all shared s-expression components."""
register_components(_LINK_CARD)
# ---------------------------------------------------------------------------
# ~link-card
# ---------------------------------------------------------------------------
# Replaces: blog/templates/fragments/link_card.html
# market/templates/fragments/link_card.html
# events/templates/fragments/link_card.html
# federation/templates/fragments/link_card.html
# artdag/l1/app/templates/fragments/link_card.html
#
# Usage:
# sexp('(~link-card :link "/post/apple/" :title "Apple" :image "/img/a.jpg")')
# sexp('(~link-card :link url :title title :icon "fas fa-file-alt")', **ctx)
# ---------------------------------------------------------------------------
_LINK_CARD = '''
(defcomp ~link-card (&key link title image icon subtitle detail data-app)
(a :href link
:class "block rounded border border-stone-200 bg-white hover:bg-stone-50 transition-colors no-underline"
:data-fragment "link-card"
:data-app data-app
:data-hx-disable true
(div :class "flex flex-row items-start gap-3 p-3"
(if image
(img :src image :alt "" :class "flex-shrink-0 w-16 h-16 rounded object-cover")
(div :class "flex-shrink-0 w-16 h-16 rounded bg-stone-100 flex items-center justify-center text-stone-400"
(i :class icon)))
(div :class "flex-1 min-w-0"
(div :class "font-medium text-stone-900 text-sm clamp-2" title)
(when subtitle
(div :class "text-xs text-stone-500 mt-0.5" subtitle))
(when detail
(div :class "text-xs text-stone-400 mt-1" detail))))))
'''