Send all responses as sexp wire format with client-side rendering
- Server sends sexp source text, client (sexp.js) renders everything - SexpExpr marker class for nested sexp composition in serialize() - sexp_page() HTML shell with data-mount="body" for full page loads - sexp_response() returns text/sexp for OOB/partial responses - ~app-body layout component replaces ~app-layout (no raw!) - ~rich-text is the only component using raw! (for CMS HTML content) - Fragment endpoints return text/sexp, auto-wrapped in SexpExpr - All _*_html() helpers converted to _*_sexp() returning sexp source - Head auto-hoist: sexp.js moves meta/title/link/script[ld+json] from rendered body to document.head automatically - Unknown components render warning box instead of crashing page - Component kwargs preserve AST for lazy rendering (fixes <> in kwargs) - Fix unterminated paren in events/sexp/tickets.sexpr Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ from quart import (
|
||||
from shared.browser.app.redis_cacher import clear_all_cache
|
||||
from shared.browser.app.authz import require_admin
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
from shared.sexp.helpers import sexp_response
|
||||
from shared.config import config
|
||||
from datetime import datetime
|
||||
|
||||
@@ -35,10 +36,10 @@ def register(url_prefix):
|
||||
tctx = await get_template_context()
|
||||
if not is_htmx_request():
|
||||
html = await render_settings_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_settings_oob(tctx)
|
||||
|
||||
return await make_response(html)
|
||||
sexp_src = await render_settings_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@bp.get("/cache/")
|
||||
@require_admin
|
||||
@@ -49,9 +50,10 @@ def register(url_prefix):
|
||||
tctx = await get_template_context()
|
||||
if not is_htmx_request():
|
||||
html = await render_cache_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_cache_oob(tctx)
|
||||
return await make_response(html)
|
||||
sexp_src = await render_cache_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@bp.post("/cache_clear/")
|
||||
@require_admin
|
||||
@@ -61,7 +63,7 @@ def register(url_prefix):
|
||||
now = datetime.now()
|
||||
from shared.sexp.jinja_bridge import render as render_comp
|
||||
html = render_comp("cache-cleared", time_str=now.strftime("%H:%M:%S"))
|
||||
return html
|
||||
return sexp_response(html)
|
||||
|
||||
return redirect(url_for("settings.cache"))
|
||||
return bp
|
||||
|
||||
@@ -15,6 +15,7 @@ from sqlalchemy import select, delete
|
||||
from shared.browser.app.authz import require_admin
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
from shared.browser.app.redis_cacher import invalidate_tag_cache
|
||||
from shared.sexp.helpers import sexp_response
|
||||
|
||||
from models.tag_group import TagGroup, TagGroupTag
|
||||
from models.ghost_content import Tag
|
||||
@@ -65,7 +66,7 @@ def register():
|
||||
if not is_htmx_request():
|
||||
return await make_response(await render_tag_groups_page(tctx))
|
||||
else:
|
||||
return await make_response(await render_tag_groups_oob(tctx))
|
||||
return sexp_response(await render_tag_groups_oob(tctx))
|
||||
|
||||
@bp.post("/")
|
||||
@require_admin
|
||||
@@ -130,7 +131,7 @@ def register():
|
||||
if not is_htmx_request():
|
||||
return await make_response(await render_tag_group_edit_page(tctx))
|
||||
else:
|
||||
return await make_response(await render_tag_group_edit_oob(tctx))
|
||||
return sexp_response(await render_tag_group_edit_oob(tctx))
|
||||
|
||||
@bp.post("/<int:id>/")
|
||||
@require_admin
|
||||
|
||||
@@ -22,6 +22,7 @@ from .services.pages_data import pages_data
|
||||
from shared.browser.app.redis_cacher import cache_page, invalidate_tag_cache
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
from shared.browser.app.authz import require_admin
|
||||
from shared.sexp.helpers import sexp_response
|
||||
from shared.utils import host_url
|
||||
|
||||
def register(url_prefix, title):
|
||||
@@ -117,7 +118,7 @@ def register(url_prefix, title):
|
||||
post_slug = p_data["post"]["slug"]
|
||||
|
||||
# Fetch container nav from relations service
|
||||
container_nav_html = await fetch_fragment("relations", "container-nav", params={
|
||||
container_nav = await fetch_fragment("relations", "container-nav", params={
|
||||
"container_type": "page",
|
||||
"container_id": str(db_post_id),
|
||||
"post_slug": post_slug,
|
||||
@@ -126,7 +127,7 @@ def register(url_prefix, title):
|
||||
ctx = {
|
||||
**p_data,
|
||||
"base_title": get_config()["title"],
|
||||
"container_nav_html": container_nav_html,
|
||||
"container_nav": container_nav,
|
||||
}
|
||||
|
||||
# Page cart badge via HTTP
|
||||
@@ -149,9 +150,10 @@ def register(url_prefix, title):
|
||||
tctx.update(ctx)
|
||||
if not is_htmx_request():
|
||||
html = await render_home_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_home_oob(tctx)
|
||||
return await make_response(html)
|
||||
sexp_src = await render_home_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@blogs_bp.get("/index")
|
||||
@blogs_bp.get("/index/")
|
||||
@@ -186,11 +188,13 @@ def register(url_prefix, title):
|
||||
tctx.update(context)
|
||||
if not is_htmx_request():
|
||||
html = await render_blog_page(tctx)
|
||||
return await make_response(html)
|
||||
elif q.page > 1:
|
||||
html = await render_blog_page_cards(tctx)
|
||||
sexp_src = await render_blog_page_cards(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
else:
|
||||
html = await render_blog_oob(tctx)
|
||||
return await make_response(html)
|
||||
sexp_src = await render_blog_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
# Default: posts listing
|
||||
# Drafts filter requires login; ignore if not logged in
|
||||
@@ -227,12 +231,14 @@ def register(url_prefix, title):
|
||||
tctx.update(context)
|
||||
if not is_htmx_request():
|
||||
html = await render_blog_page(tctx)
|
||||
return await make_response(html)
|
||||
elif q.page > 1:
|
||||
html = await render_blog_cards(tctx)
|
||||
# Sexp wire format — client renders blog cards
|
||||
sexp_src = await render_blog_cards(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
else:
|
||||
html = await render_blog_oob(tctx)
|
||||
|
||||
return await make_response(html)
|
||||
sexp_src = await render_blog_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@blogs_bp.get("/new/")
|
||||
@require_admin
|
||||
@@ -244,9 +250,10 @@ def register(url_prefix, title):
|
||||
tctx["editor_html"] = render_editor_panel()
|
||||
if not is_htmx_request():
|
||||
html = await render_new_post_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_new_post_oob(tctx)
|
||||
return await make_response(html)
|
||||
sexp_src = await render_new_post_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@blogs_bp.post("/new/")
|
||||
@require_admin
|
||||
@@ -325,9 +332,10 @@ def register(url_prefix, title):
|
||||
tctx["is_page"] = True
|
||||
if not is_htmx_request():
|
||||
html = await render_new_post_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_new_post_oob(tctx)
|
||||
return await make_response(html)
|
||||
sexp_src = await render_new_post_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@blogs_bp.post("/new-page/")
|
||||
@require_admin
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Blog app fragment endpoints.
|
||||
|
||||
Exposes HTML fragments at ``/internal/fragments/<type>`` for consumption
|
||||
Exposes sexp fragments at ``/internal/fragments/<type>`` for consumption
|
||||
by other coop apps via the fragment client.
|
||||
"""
|
||||
|
||||
@@ -10,13 +10,11 @@ from quart import Blueprint, Response, g, render_template, request
|
||||
|
||||
from shared.infrastructure.fragments import FRAGMENT_HEADER
|
||||
from shared.services.navigation import get_navigation_tree
|
||||
from shared.sexp.jinja_bridge import sexp
|
||||
|
||||
|
||||
def register():
|
||||
bp = Blueprint("fragments", __name__, url_prefix="/internal/fragments")
|
||||
|
||||
# Registry of fragment handlers: type -> async callable returning HTML str
|
||||
_handlers: dict[str, object] = {}
|
||||
|
||||
@bp.before_request
|
||||
@@ -28,18 +26,19 @@ def register():
|
||||
async def get_fragment(fragment_type: str):
|
||||
handler = _handlers.get(fragment_type)
|
||||
if handler is None:
|
||||
return Response("", status=200, content_type="text/html")
|
||||
html = await handler()
|
||||
return Response(html, status=200, content_type="text/html")
|
||||
return Response("", status=200, content_type="text/sexp")
|
||||
result = await handler()
|
||||
# nav-tree still returns HTML (Jinja template) for now
|
||||
ct = "text/html" if fragment_type == "nav-tree" else "text/sexp"
|
||||
return Response(result, status=200, content_type=ct)
|
||||
|
||||
# --- nav-tree fragment ---
|
||||
# --- nav-tree fragment (still Jinja for now — complex template) ---
|
||||
async def _nav_tree_handler():
|
||||
app_name = request.args.get("app_name", "")
|
||||
path = request.args.get("path", "/")
|
||||
first_seg = path.strip("/").split("/")[0]
|
||||
menu_items = list(await get_navigation_tree(g.s))
|
||||
|
||||
# Append Art-DAG as a synthetic nav entry (not a DB MenuNode)
|
||||
class _NavItem:
|
||||
__slots__ = ("slug", "label", "feature_image")
|
||||
def __init__(self, slug, label, feature_image=None):
|
||||
@@ -58,20 +57,18 @@ def register():
|
||||
|
||||
_handlers["nav-tree"] = _nav_tree_handler
|
||||
|
||||
# --- link-card fragment (s-expression rendered) ---
|
||||
def _render_blog_link_card(post, link: str) -> str:
|
||||
"""Render a blog link-card via the ~link-card s-expression component."""
|
||||
# --- link-card fragment — returns sexp source ---
|
||||
def _blog_link_card_sexp(post, link: str) -> str:
|
||||
from shared.sexp.helpers import sexp_call
|
||||
published = post.published_at.strftime("%d %b %Y") if post.published_at else None
|
||||
return sexp(
|
||||
'(~link-card :link link :title title :image image'
|
||||
' :icon "fas fa-file-alt" :subtitle excerpt'
|
||||
' :detail published :data-app "blog")',
|
||||
link=link,
|
||||
title=post.title,
|
||||
image=post.feature_image,
|
||||
excerpt=post.custom_excerpt or post.excerpt,
|
||||
published=published,
|
||||
)
|
||||
return sexp_call("link-card",
|
||||
link=link,
|
||||
title=post.title,
|
||||
image=post.feature_image,
|
||||
icon="fas fa-file-alt",
|
||||
subtitle=post.custom_excerpt or post.excerpt,
|
||||
detail=published,
|
||||
data_app="blog")
|
||||
|
||||
async def _link_card_handler():
|
||||
from shared.services.registry import services
|
||||
@@ -88,7 +85,7 @@ def register():
|
||||
parts.append(f"<!-- fragment:{s} -->")
|
||||
post = await services.blog.get_post_by_slug(g.s, s)
|
||||
if post:
|
||||
parts.append(_render_blog_link_card(post, blog_url(f"/{post.slug}")))
|
||||
parts.append(_blog_link_card_sexp(post, blog_url(f"/{post.slug}")))
|
||||
return "\n".join(parts)
|
||||
|
||||
# Single mode
|
||||
@@ -97,11 +94,10 @@ def register():
|
||||
post = await services.blog.get_post_by_slug(g.s, slug)
|
||||
if not post:
|
||||
return ""
|
||||
return _render_blog_link_card(post, blog_url(f"/{post.slug}"))
|
||||
return _blog_link_card_sexp(post, blog_url(f"/{post.slug}"))
|
||||
|
||||
_handlers["link-card"] = _link_card_handler
|
||||
|
||||
# Store handlers dict on blueprint so app code can register handlers
|
||||
bp._fragment_handlers = _handlers
|
||||
|
||||
return bp
|
||||
|
||||
@@ -13,6 +13,7 @@ from .services.menu_items import (
|
||||
MenuItemError,
|
||||
)
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
from shared.sexp.helpers import sexp_response
|
||||
|
||||
def register():
|
||||
bp = Blueprint("menu_items", __name__, url_prefix='/settings/menu_items')
|
||||
@@ -36,10 +37,10 @@ def register():
|
||||
tctx["menu_items"] = menu_items
|
||||
if not is_htmx_request():
|
||||
html = await render_menu_items_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_menu_items_oob(tctx)
|
||||
|
||||
return await make_response(html)
|
||||
sexp_src = await render_menu_items_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@bp.get("/new/")
|
||||
@require_admin
|
||||
@@ -75,7 +76,7 @@ def register():
|
||||
from sexp.sexp_components import render_menu_items_list
|
||||
html = render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
return await make_response(html + nav_oob, 200)
|
||||
return sexp_response(html + nav_oob)
|
||||
|
||||
except MenuItemError as e:
|
||||
return jsonify({"message": str(e), "errors": {}}), 400
|
||||
@@ -118,7 +119,7 @@ def register():
|
||||
from sexp.sexp_components import render_menu_items_list
|
||||
html = render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
return await make_response(html + nav_oob, 200)
|
||||
return sexp_response(html + nav_oob)
|
||||
|
||||
except MenuItemError as e:
|
||||
return jsonify({"message": str(e), "errors": {}}), 400
|
||||
@@ -139,7 +140,7 @@ def register():
|
||||
from sexp.sexp_components import render_menu_items_list
|
||||
html = render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
return await make_response(html + nav_oob, 200)
|
||||
return sexp_response(html + nav_oob)
|
||||
|
||||
@bp.get("/pages/search/")
|
||||
@require_admin
|
||||
@@ -186,6 +187,6 @@ def register():
|
||||
from sexp.sexp_components import render_menu_items_list
|
||||
html = render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
return await make_response(html + nav_oob, 200)
|
||||
return sexp_response(html + nav_oob)
|
||||
|
||||
return bp
|
||||
|
||||
@@ -12,6 +12,7 @@ from quart import (
|
||||
)
|
||||
from shared.browser.app.authz import require_admin, require_post_author
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
from shared.sexp.helpers import sexp_response
|
||||
from shared.utils import host_url
|
||||
|
||||
def register():
|
||||
@@ -58,10 +59,10 @@ def register():
|
||||
tctx.update(ctx)
|
||||
if not is_htmx_request():
|
||||
html = await render_post_admin_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_post_admin_oob(tctx)
|
||||
|
||||
return await make_response(html)
|
||||
sexp_src = await render_post_admin_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@bp.put("/features/")
|
||||
@require_admin
|
||||
@@ -105,7 +106,7 @@ def register():
|
||||
sumup_merchant_code=result.get("sumup_merchant_code") or "",
|
||||
sumup_checkout_prefix=result.get("sumup_checkout_prefix") or "",
|
||||
)
|
||||
return await make_response(html)
|
||||
return sexp_response(html)
|
||||
|
||||
@bp.put("/admin/sumup/")
|
||||
@require_admin
|
||||
@@ -144,7 +145,7 @@ def register():
|
||||
sumup_merchant_code=result.get("sumup_merchant_code") or "",
|
||||
sumup_checkout_prefix=result.get("sumup_checkout_prefix") or "",
|
||||
)
|
||||
return await make_response(html)
|
||||
return sexp_response(html)
|
||||
|
||||
@bp.get("/data/")
|
||||
@require_admin
|
||||
@@ -157,10 +158,10 @@ def register():
|
||||
tctx["data_html"] = data_html
|
||||
if not is_htmx_request():
|
||||
html = await render_post_data_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_post_data_oob(tctx)
|
||||
|
||||
return await make_response(html)
|
||||
sexp_src = await render_post_data_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@bp.get("/entries/calendar/<int:calendar_id>/")
|
||||
@require_admin
|
||||
@@ -280,10 +281,10 @@ def register():
|
||||
tctx["entries_html"] = entries_html
|
||||
if not is_htmx_request():
|
||||
html = await render_post_entries_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_post_entries_oob(tctx)
|
||||
|
||||
return await make_response(html)
|
||||
sexp_src = await render_post_entries_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@bp.post("/entries/<int:entry_id>/toggle/")
|
||||
@require_admin
|
||||
@@ -335,7 +336,7 @@ def register():
|
||||
admin_list = render_associated_entries(all_calendars, associated_entry_ids, post["slug"])
|
||||
nav_entries_html = render_nav_entries_oob(associated_entries, calendars, post)
|
||||
|
||||
return await make_response(admin_list + nav_entries_html)
|
||||
return sexp_response(admin_list + nav_entries_html)
|
||||
|
||||
@bp.get("/settings/")
|
||||
@require_post_author
|
||||
@@ -359,10 +360,10 @@ def register():
|
||||
tctx["settings_html"] = settings_html
|
||||
if not is_htmx_request():
|
||||
html = await render_post_settings_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_post_settings_oob(tctx)
|
||||
|
||||
return await make_response(html)
|
||||
sexp_src = await render_post_settings_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@bp.post("/settings/")
|
||||
@require_post_author
|
||||
@@ -465,10 +466,10 @@ def register():
|
||||
tctx["edit_html"] = edit_html
|
||||
if not is_htmx_request():
|
||||
html = await render_post_edit_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_post_edit_oob(tctx)
|
||||
|
||||
return await make_response(html)
|
||||
sexp_src = await render_post_edit_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@bp.post("/edit/")
|
||||
@require_post_author
|
||||
@@ -598,8 +599,7 @@ def register():
|
||||
page_markets = await _fetch_page_markets(post_id)
|
||||
|
||||
from sexp.sexp_components import render_markets_panel
|
||||
html = render_markets_panel(page_markets, post)
|
||||
return await make_response(html)
|
||||
return sexp_response(render_markets_panel(page_markets, post))
|
||||
|
||||
@bp.post("/markets/new/")
|
||||
@require_admin
|
||||
@@ -625,8 +625,7 @@ def register():
|
||||
page_markets = await _fetch_page_markets(post_id)
|
||||
|
||||
from sexp.sexp_components import render_markets_panel
|
||||
html = render_markets_panel(page_markets, post)
|
||||
return await make_response(html)
|
||||
return sexp_response(render_markets_panel(page_markets, post))
|
||||
|
||||
@bp.delete("/markets/<market_slug>/")
|
||||
@require_admin
|
||||
@@ -646,7 +645,6 @@ def register():
|
||||
page_markets = await _fetch_page_markets(post_id)
|
||||
|
||||
from sexp.sexp_components import render_markets_panel
|
||||
html = render_markets_panel(page_markets, post)
|
||||
return await make_response(html)
|
||||
return sexp_response(render_markets_panel(page_markets, post))
|
||||
|
||||
return bp
|
||||
|
||||
@@ -21,6 +21,7 @@ from shared.browser.app.redis_cacher import cache_page, clear_cache
|
||||
from .admin.routes import register as register_admin
|
||||
from shared.config import config
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
from shared.sexp.helpers import sexp_response
|
||||
|
||||
def register():
|
||||
bp = Blueprint("post", __name__, url_prefix='/<slug>')
|
||||
@@ -70,7 +71,7 @@ def register():
|
||||
post_slug = (g.post_data.get("post") or {}).get("slug", "")
|
||||
|
||||
# Fetch container nav from relations service
|
||||
container_nav_html = await fetch_fragment("relations", "container-nav", params={
|
||||
container_nav = await fetch_fragment("relations", "container-nav", params={
|
||||
"container_type": "page",
|
||||
"container_id": str(db_post_id),
|
||||
"post_slug": post_slug,
|
||||
@@ -79,7 +80,7 @@ def register():
|
||||
ctx = {
|
||||
**p_data,
|
||||
"base_title": config()["title"],
|
||||
"container_nav_html": container_nav_html,
|
||||
"container_nav": container_nav,
|
||||
}
|
||||
|
||||
# Page cart badge via HTTP
|
||||
@@ -109,10 +110,10 @@ def register():
|
||||
tctx = await get_template_context()
|
||||
if not is_htmx_request():
|
||||
html = await render_post_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_post_oob(tctx)
|
||||
|
||||
return await make_response(html)
|
||||
sexp_src = await render_post_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@bp.post("/like/toggle/")
|
||||
@clear_cache(tag="post.post_detail", tag_scope="user")
|
||||
@@ -124,9 +125,7 @@ def register():
|
||||
|
||||
# Get post_id from g.post_data
|
||||
if not g.user:
|
||||
html = render_like_toggle_button(slug, False, like_url)
|
||||
resp = make_response(html, 403)
|
||||
return resp
|
||||
return sexp_response(render_like_toggle_button(slug, False, like_url), status=403)
|
||||
|
||||
post_id = g.post_data["post"]["id"]
|
||||
user_id = g.user.id
|
||||
@@ -136,8 +135,7 @@ def register():
|
||||
})
|
||||
liked = result["liked"]
|
||||
|
||||
html = render_like_toggle_button(slug, liked, like_url)
|
||||
return html
|
||||
return sexp_response(render_like_toggle_button(slug, liked, like_url))
|
||||
|
||||
@bp.get("/w/<widget_domain>/")
|
||||
async def widget_paginate(slug: str, widget_domain: str):
|
||||
|
||||
@@ -6,6 +6,7 @@ from sqlalchemy.orm import selectinload
|
||||
|
||||
from shared.browser.app.authz import require_login
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
from shared.sexp.helpers import sexp_response
|
||||
from models import Snippet
|
||||
|
||||
|
||||
@@ -46,10 +47,10 @@ def register():
|
||||
tctx["is_admin"] = is_admin
|
||||
if not is_htmx_request():
|
||||
html = await render_snippets_page(tctx)
|
||||
return await make_response(html)
|
||||
else:
|
||||
html = await render_snippets_oob(tctx)
|
||||
|
||||
return await make_response(html)
|
||||
sexp_src = await render_snippets_oob(tctx)
|
||||
return sexp_response(sexp_src)
|
||||
|
||||
@bp.delete("/<int:snippet_id>/")
|
||||
@require_login
|
||||
@@ -68,8 +69,7 @@ def register():
|
||||
|
||||
snippets = await _visible_snippets(g.s)
|
||||
from sexp.sexp_components import render_snippets_list
|
||||
html = render_snippets_list(snippets, is_admin)
|
||||
return await make_response(html)
|
||||
return sexp_response(render_snippets_list(snippets, is_admin))
|
||||
|
||||
@bp.patch("/<int:snippet_id>/visibility/")
|
||||
@require_login
|
||||
@@ -93,7 +93,6 @@ def register():
|
||||
|
||||
snippets = await _visible_snippets(g.s)
|
||||
from sexp.sexp_components import render_snippets_list
|
||||
html = render_snippets_list(snippets, True)
|
||||
return await make_response(html)
|
||||
return sexp_response(render_snippets_list(snippets, True))
|
||||
|
||||
return bp
|
||||
|
||||
Reference in New Issue
Block a user