From e4f7fe24ab5948937061ff24c7aa747c1ca1c2fc Mon Sep 17 00:00:00 2001 From: Simon Carstensen Date: Fri, 15 May 2026 17:54:42 +0200 Subject: [PATCH 1/7] Add shared base.html chrome + context processor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Foundation for the M7 templates port: - base.html with HTML5 semantic skeleton (header / main / aside / footer). Includes a NOINDEX meta for private/secret-URL sites, matches the 2007 behavior. - partials/site_header.html — site title, subtitle, primary nav (recent changes / settings / design / signin·signout). Shows a "Claim this site" banner above the header when the site is unclaimed. - partials/site_sidebar.html — page list + new-page form. - partials/design_style.html — exposes the design row as CSS custom properties (--header-color, --title-font, etc) so the M9 stylesheet rewrite can reference them without coordinating with this template. - partials/form_error.html — inline form error block. - jottit/chrome.py — Flask context_processor that surfaces site, pages, design, is_signed_in, is_unclaimed, and a few helpers to every template. - list_pages() DB helper for the sidebar. Two simple existing templates (notfound, deleted) are ported to {% extends "base.html" %} as a smoke test. The bigger view/edit/admin/history templates land in subsequent commits. Co-Authored-By: Claude Opus 4.7 (1M context) --- jottit/__init__.py | 2 + jottit/chrome.py | 48 +++++++++++++++++++++ jottit/db.py | 13 ++++++ jottit/templates/base.html | 35 +++++++++++++++ jottit/templates/deleted.html | 25 ++++++----- jottit/templates/notfound.html | 22 +++++----- jottit/templates/partials/design_style.html | 23 ++++++++++ jottit/templates/partials/form_error.html | 3 ++ jottit/templates/partials/site_header.html | 34 +++++++++++++++ jottit/templates/partials/site_sidebar.html | 32 ++++++++++++++ 10 files changed, 215 insertions(+), 22 deletions(-) create mode 100644 jottit/chrome.py create mode 100644 jottit/templates/base.html create mode 100644 jottit/templates/partials/design_style.html create mode 100644 jottit/templates/partials/form_error.html create mode 100644 jottit/templates/partials/site_header.html create mode 100644 jottit/templates/partials/site_sidebar.html diff --git a/jottit/__init__.py b/jottit/__init__.py index 6cb1e43..e8e0036 100644 --- a/jottit/__init__.py +++ b/jottit/__init__.py @@ -10,6 +10,7 @@ from jottit.blueprints.root import root_bp from jottit.blueprints.secret import secret_bp from jottit.blueprints.site import site_bp +from jottit.chrome import chrome_context from jottit.db import close_request_conn, make_engine from jottit.site_resolver import resolve_site @@ -32,5 +33,6 @@ def create_app() -> Flask: app.before_request(resolve_site) app.teardown_request(close_request_conn) + app.context_processor(chrome_context) return app diff --git a/jottit/chrome.py b/jottit/chrome.py new file mode 100644 index 0000000..dea5464 --- /dev/null +++ b/jottit/chrome.py @@ -0,0 +1,48 @@ +"""Shared template context: site, page list, signin state, and design row. + +Wired into the Flask app as a `context_processor` so every render_template +call sees these variables without each view having to pass them. +""" + +from __future__ import annotations + +from typing import Any + +from flask import g, request + +from jottit import auth +from jottit.db import get_design, get_request_conn, list_pages +from jottit.urls import page_slug, site_root + + +def chrome_context() -> dict[str, Any]: + """Variables every page template can rely on. + + `site` and `pages` are `None` on apex-domain routes (the front page); + templates should branch on `site` before reading from it. + """ + site = getattr(g, "site", None) + if site is None: + return { + "site": None, + "pages": [], + "design": None, + "is_signed_in": False, + "is_unclaimed": False, + "site_root_path": "/", + "page_slug": page_slug, + } + + conn = get_request_conn() + pages = list_pages(conn, site_id=site.id) if conn is not None else [] + design = get_design(conn, site_id=site.id) if conn is not None else None + return { + "site": site, + "pages": pages, + "design": design, + "is_signed_in": auth.is_signed_in_to(site.id), + "is_unclaimed": site.password is None, + "site_root_path": site_root(), + "page_slug": page_slug, + "current_path": request.path, + } diff --git a/jottit/db.py b/jottit/db.py index eccfe0e..8e2eaab 100644 --- a/jottit/db.py +++ b/jottit/db.py @@ -635,6 +635,19 @@ def update_design(conn: Connection, *, site_id: int, **fields: object) -> None: # ---- Export ---- +def list_pages(conn: Connection, *, site_id: int) -> list[Row]: + """List a site's non-deleted pages, alphabetically by name. + + Used by the chrome sidebar to render a navigation list. Home (the + empty-name page) sorts first. + """ + rows = conn.execute( + select(pages.c.name).where(pages.c.site_id == site_id, pages.c.deleted.is_(False)) + ).all() + # Empty-name (home) goes to the top; remaining pages sort case-insensitively. + return sorted(rows, key=lambda r: (r.name != "", r.name.lower())) + + def get_pages_for_export(conn: Connection, *, site_id: int) -> list[Row]: """Latest non-deleted pages with their latest revision content, for export. diff --git a/jottit/templates/base.html b/jottit/templates/base.html new file mode 100644 index 0000000..1ef7ea3 --- /dev/null +++ b/jottit/templates/base.html @@ -0,0 +1,35 @@ + + + + + + {% block title %}{% if site and site.title %}{{ site.title }}{% else %}Jottit{% endif %}{% endblock %} + {% if site and (site.security == "private" or not site.public_url) %} + + {% endif %} + + {% block head %}{% endblock %} + {% if design %} + {% include "partials/design_style.html" %} + {% endif %} + + +
+ {% if site %} + {% include "partials/site_header.html" %} + {% endif %} + +
+ {% block content %}{% endblock %} +
+ + {% if site %} + {% include "partials/site_sidebar.html" %} + {% endif %} + + +
+ + diff --git a/jottit/templates/deleted.html b/jottit/templates/deleted.html index 959f5ba..f99da8c 100644 --- a/jottit/templates/deleted.html +++ b/jottit/templates/deleted.html @@ -1,11 +1,14 @@ - - - - - Deleted - - -

This page has been deleted

-

“{{ page_name }}” is gone. Append ?r=N to view a specific revision.

- - +{% extends "base.html" %} +{% block title %}Deleted{% endblock %} +{% block content %} +
+

This page has been deleted

+

“{{ page_name }}” is gone. Append ?r=N to view a specific revision, or restore it from the page's history.

+ {% if is_signed_in or is_unclaimed %} +
+ + +
+ {% endif %} +
+{% endblock %} diff --git a/jottit/templates/notfound.html b/jottit/templates/notfound.html index 263ec3d..3b6cbe4 100644 --- a/jottit/templates/notfound.html +++ b/jottit/templates/notfound.html @@ -1,11 +1,11 @@ - - - - - Not found - - -

Not found

-

There's no page named “{{ page_name }}” on this site.

- - +{% extends "base.html" %} +{% block title %}Not found{% endblock %} +{% block content %} +
+

Not found

+

There's no page named “{{ page_name }}” on this site.

+ {% if is_signed_in or is_unclaimed %} +

Create this page

+ {% endif %} +
+{% endblock %} diff --git a/jottit/templates/partials/design_style.html b/jottit/templates/partials/design_style.html new file mode 100644 index 0000000..628f361 --- /dev/null +++ b/jottit/templates/partials/design_style.html @@ -0,0 +1,23 @@ +{# Inline CSS variables sourced from the site's design row. + + The 2007 site embedded full per-site CSS rules here; we surface the + values as custom properties instead and let the global stylesheet + reference them. That keeps the per-request payload small and lets + the M9 stylesheet rewrite without coordinating with this template. #} + diff --git a/jottit/templates/partials/form_error.html b/jottit/templates/partials/form_error.html new file mode 100644 index 0000000..89de835 --- /dev/null +++ b/jottit/templates/partials/form_error.html @@ -0,0 +1,3 @@ +{# Inline form error. Hidden when no error message is set, so callers can + include this unconditionally above their form fields. #} +{% if error %}{% endif %} diff --git a/jottit/templates/partials/site_header.html b/jottit/templates/partials/site_header.html new file mode 100644 index 0000000..3cf80fe --- /dev/null +++ b/jottit/templates/partials/site_header.html @@ -0,0 +1,34 @@ +{# Header chrome rendered on every site page: title, subtitle, primary nav. + The unclaimed-site banner sits above the header so it's the most visible + action for a brand-new site. #} + diff --git a/jottit/templates/partials/site_sidebar.html b/jottit/templates/partials/site_sidebar.html new file mode 100644 index 0000000..2dd5b69 --- /dev/null +++ b/jottit/templates/partials/site_sidebar.html @@ -0,0 +1,32 @@ +{# Page list for the current site. Hidden when the site has only the + home page — it's noise on a fresh site. The "new page" form is a + plain link to the edit URL; M10 will replace the inline form with + a JS-driven inline create. #} +{% if pages|length > 1 or is_signed_in %} + +{% endif %} From b7cadd9eb1d2f1b6040f0c2ac51cfadf0113e3b6 Mon Sep 17 00:00:00 2001 From: Simon Carstensen Date: Fri, 15 May 2026 17:55:48 +0200 Subject: [PATCH 2/7] Port the front-page template Replaces the M2 placeholder textarea-only form with a real front page: hero with the Jottit tagline, a content textarea, and an optional custom-subdomain field that funnels into `public_url` on the index POST handler. The form is autofocused on the textarea and lives in a single
so the apex page reads cleanly even without the per-site chrome (header / sidebar are hidden when g.site is None). Co-Authored-By: Claude Opus 4.7 (1M context) --- jottit/templates/index.html | 39 ++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/jottit/templates/index.html b/jottit/templates/index.html index cff2767..3f33a0e 100644 --- a/jottit/templates/index.html +++ b/jottit/templates/index.html @@ -1,11 +1,28 @@ - - -Jottit - -

Jottit

-
-

-

-
- - +{% extends "base.html" %} +{% block title %}Jottit — make a site by filling out a textbox{% endblock %} +{% block content %} +
+
+

Jottit

+

Getting a website should be as easy as filling out a textbox.

+
+ +
+

+ +

+

+ +

+

+
+
+{% endblock %} From cd3386c662c365845cbc29218dafba4811bfb49d Mon Sep 17 00:00:00 2001 From: Simon Carstensen Date: Fri, 15 May 2026 20:36:42 +0200 Subject: [PATCH 3/7] Port view_page and edit_page templates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit view_page now extends base.html, uses semantic
/
/