Add custom HTML template editor and clean up UI
- Replace CSS-only customization with full HTML template editing
- Users edit the entire page wrapper with {{content}} placeholder
- Add /style?reset escape hatch to recover from broken templates
- Move nav links to template, remove redundant nav from search page
- Delete remote pages when unsubscribing from an instance
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4df0ef03f5
commit
8741c2fffb
2 changed files with 36 additions and 32 deletions
46
handlers.py
46
handlers.py
|
|
@ -2,7 +2,7 @@ import json
|
|||
from datetime import datetime
|
||||
|
||||
from db import get_db, get_setting, set_setting, get_site_name, index_url, clean_url
|
||||
from templates import esc, snippet, wrap_page
|
||||
from templates import esc, snippet, wrap_page, DEFAULT_TEMPLATE
|
||||
from rns_client import fetch_remote_sites
|
||||
|
||||
|
||||
|
|
@ -186,19 +186,13 @@ def handle_search(query):
|
|||
if q and remote_rows:
|
||||
sub_count = f" + {len(remote_rows)} from subscriptions"
|
||||
return _respond(
|
||||
f'<h1><a href="/">{esc(name)}</a></h1>'
|
||||
f'<form method="get" action="/">'
|
||||
f'<input name="q" value="{esc(q)}" placeholder="search your index" size="40">'
|
||||
f' <button type="submit">search</button>'
|
||||
f'</form>'
|
||||
f'<p>{count} page(s) indexed.'
|
||||
f' <a href="/add">+ add url</a>'
|
||||
f' | <a href="/pages">browse</a>'
|
||||
f' | <a href="/tags">tags</a>'
|
||||
f' | <a href="/subscriptions">subscriptions</a>'
|
||||
f' | <a href="/style">customize</a>'
|
||||
f' | <a href="/about">about</a></p>'
|
||||
f'<hr>{result_html}{trusted_html}{remote_html}'
|
||||
f'<p class="meta">{count} pages indexed'
|
||||
f' · <a href="/add">+ add url</a></p>'
|
||||
f'{result_html}{trusted_html}{remote_html}'
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -366,8 +360,11 @@ def handle_import_submit(body):
|
|||
return handle_import_form(f"Imported {imported} page(s). {errors} error(s).")
|
||||
|
||||
|
||||
def handle_style_form(msg=""):
|
||||
css = get_setting("custom_css")
|
||||
def handle_style_form(msg="", query=None):
|
||||
if query and "reset" in query:
|
||||
set_setting("custom_template", "")
|
||||
msg = "Template reset to default."
|
||||
template = get_setting("custom_template") or DEFAULT_TEMPLATE
|
||||
name = get_site_name()
|
||||
sharing = get_setting("sharing_enabled", "0")
|
||||
checked = " checked" if sharing == "1" else ""
|
||||
|
|
@ -379,20 +376,10 @@ def handle_style_form(msg=""):
|
|||
f"<h2>sharing</h2>"
|
||||
f'<label><input type="checkbox" name="sharing_enabled" value="1"{checked}>'
|
||||
f" share your site list publicly at /api/sites</label><br><br>"
|
||||
f"<h2>custom css</h2>"
|
||||
f"<p>Some classes you can target:</p>"
|
||||
f"<pre>"
|
||||
f"body - page background, font\n"
|
||||
f"h1 - page titles\n"
|
||||
f"input, button - search bar\n"
|
||||
f"a - links\n"
|
||||
f".result - each search result\n"
|
||||
f".note - your notes on results\n"
|
||||
f".trusted - trusted sites dropdown\n"
|
||||
f"small - url text\n"
|
||||
f"ul, li - browse page list"
|
||||
f"</pre>"
|
||||
f'<textarea name="css" rows="16" cols="60">{esc(css)}</textarea><br><br>'
|
||||
f"<h2>custom html</h2>"
|
||||
f"<p>Edit the full page template. Use <code>{esc('{{content}}')}</code> "
|
||||
f"where page content should appear.</p>"
|
||||
f'<textarea name="template" rows="20" cols="60">{esc(template)}</textarea><br><br>'
|
||||
f'<button type="submit">save</button>'
|
||||
f"</form>"
|
||||
f"<h2>bookmarklet</h2>"
|
||||
|
|
@ -404,10 +391,10 @@ def handle_style_form(msg=""):
|
|||
|
||||
|
||||
def handle_style_submit(body):
|
||||
css = body.get("css", [""])[0]
|
||||
template = body.get("template", [""])[0]
|
||||
name = body.get("site_name", ["tinyweb"])[0].strip()
|
||||
sharing = "1" if body.get("sharing_enabled") else "0"
|
||||
set_setting("custom_css", css)
|
||||
set_setting("custom_template", template if template.strip() != DEFAULT_TEMPLATE.strip() else "")
|
||||
set_setting("site_name", name or "tinyweb")
|
||||
set_setting("sharing_enabled", sharing)
|
||||
return handle_style_form("Saved.")
|
||||
|
|
@ -755,6 +742,7 @@ def handle_subscription_autosync(sub_id):
|
|||
|
||||
def handle_subscription_delete(sub_id):
|
||||
db = get_db()
|
||||
db.execute("DELETE FROM remote_pages WHERE subscription_id = ?", (sub_id,))
|
||||
db.execute("DELETE FROM subscriptions WHERE id = ?", (sub_id,))
|
||||
db.commit()
|
||||
db.close()
|
||||
|
|
@ -826,7 +814,7 @@ def dispatch_request(data):
|
|||
elif path == "/bookmark":
|
||||
return handle_bookmark(query)
|
||||
elif path == "/style":
|
||||
return handle_style_form()
|
||||
return handle_style_form(query=query)
|
||||
elif path == "/about":
|
||||
return handle_about()
|
||||
elif path == "/export":
|
||||
|
|
|
|||
22
templates.py
22
templates.py
|
|
@ -15,7 +15,23 @@ def snippet(text, query, ctx=80):
|
|||
return ("..." if start > 0 else "") + text[start:end] + ("..." if end < len(text) else "")
|
||||
|
||||
|
||||
DEFAULT_TEMPLATE = "<html>\n<head>\n</head>\n<body>\n{{content}}\n</body>\n</html>"
|
||||
|
||||
|
||||
def _default_template():
|
||||
name = esc(get_setting("site_name", "tinyweb"))
|
||||
return (
|
||||
"<html>\n<head>\n</head>\n<body>\n"
|
||||
f'<p><b><a href="/">{name}</a></b>'
|
||||
' | <a href="/">search</a> | <a href="/pages">browse</a>'
|
||||
' | <a href="/tags">tags</a> | <a href="/subscriptions">subscriptions</a>'
|
||||
' | <a href="/style">customize</a> | <a href="/about">about</a></p>\n'
|
||||
"<hr>\n{{content}}\n</body>\n</html>"
|
||||
)
|
||||
|
||||
|
||||
def wrap_page(body_html):
|
||||
css = get_setting("custom_css")
|
||||
style = f"<style>{css}</style>" if css else ""
|
||||
return f"<html><head>{style}</head><body>{body_html}</body></html>"
|
||||
template = get_setting("custom_template") or _default_template()
|
||||
if "{{content}}" not in template:
|
||||
template = _default_template()
|
||||
return template.replace("{{content}}", body_html)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue