From 851f625165f16efbca433ae0aeb5b68b8a49c4e6 Mon Sep 17 00:00:00 2001 From: Simon Carstensen Date: Sat, 16 May 2026 16:31:15 +0200 Subject: [PATCH 1/6] Match 2007 chrome (claim banner, header, content card, sidebar) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split base.html into a bare shell + site_base.html (body.app — claim banner, header strip, white content card, page-list sidebar, footer) + main_base.html (body.site — logo, gray body, blue-link nav). Every content template extends one of the two layout bases; partials that used to inline header/sidebar/claim-banner are dropped. Rewrote tokens/reset/base/components/utilities to match the 2007 look: #f0f0ee panel, white content card with #ddd border, 60px left gutter + 200px right sidebar at ≥768px, dateline footer with grey button-style history/edit links, yellow "That was easy!" primer. Wired the /site/hide-primer POST endpoint to flip sites.show_primer=false and redirect home. Dropped the redundant "Home" h1 on view_page; the dateline now carries page actions. Co-Authored-By: Claude Opus 4.7 (1M context) --- jottit/chrome.py | 1 + jottit/static/base.css | 241 +++--- jottit/static/components.css | 684 +++++++++++++----- jottit/static/logo.png | Bin 0 -> 4761 bytes jottit/static/reset.css | 54 +- jottit/static/tokens.css | 98 ++- jottit/static/utilities.css | 32 +- jottit/templates/admin_change_password.html | 2 +- .../templates/admin_change_site_address.html | 2 +- jottit/templates/admin_delete.html | 2 +- jottit/templates/admin_design.html | 2 +- jottit/templates/admin_settings.html | 2 +- jottit/templates/base.html | 22 +- jottit/templates/change_password.html | 2 +- jottit/templates/changes.html | 2 +- jottit/templates/claim_site.html | 2 +- jottit/templates/deleted.html | 2 +- jottit/templates/diff.html | 2 +- jottit/templates/edit_page.html | 2 +- jottit/templates/forgot_password.html | 2 +- jottit/templates/history.html | 2 +- jottit/templates/index.html | 35 +- jottit/templates/main_base.html | 37 + jottit/templates/notfound.html | 2 +- jottit/templates/partials/design_style.html | 29 +- jottit/templates/partials/site_header.html | 34 - jottit/templates/partials/site_sidebar.html | 32 - jottit/templates/signin.html | 2 +- jottit/templates/site_base.html | 89 +++ jottit/templates/view_page.html | 75 +- jottit/views/site.py | 9 +- tests/test_page_view.py | 2 +- tests/test_secret_routing.py | 22 +- tests/test_subdomain_routing.py | 25 +- 34 files changed, 951 insertions(+), 600 deletions(-) create mode 100644 jottit/static/logo.png create mode 100644 jottit/templates/main_base.html delete mode 100644 jottit/templates/partials/site_header.html delete mode 100644 jottit/templates/partials/site_sidebar.html create mode 100644 jottit/templates/site_base.html diff --git a/jottit/chrome.py b/jottit/chrome.py index dea5464..965c2d5 100644 --- a/jottit/chrome.py +++ b/jottit/chrome.py @@ -31,6 +31,7 @@ def chrome_context() -> dict[str, Any]: "is_unclaimed": False, "site_root_path": "/", "page_slug": page_slug, + "current_path": request.path, } conn = get_request_conn() diff --git a/jottit/static/base.css b/jottit/static/base.css index eff48d2..1376fd4 100644 --- a/jottit/static/base.css +++ b/jottit/static/base.css @@ -1,104 +1,139 @@ -html { - font-family: var(--content-font); - font-size: var(--content-size); - line-height: 1.5; - color: var(--fg); - background: var(--bg); +@layer base { + body { + background: #ffffff; + color: var(--color-text); + font-family: var(--font-family-content); + font-size: var(--font-size-base); + line-height: var(--line-height-normal); + } + + body.app, + body.dialog-page { + background: var(--color-bg-app); + } + + h1, h2, h3, h4, h5, h6 { + font-family: var(--font-family-headings); + line-height: var(--line-height-tight); + margin: 1em 0 0.5em 0; + font-weight: bold; + } + + h1 { font-size: var(--font-size-h1); } + h2 { font-size: var(--font-size-h2); } + h3 { font-size: var(--font-size-h3); } + h4 { font-size: var(--font-size-h4); } + h5 { font-size: var(--font-size-h5); } + h6 { font-size: var(--font-size-h6); color: var(--color-text-muted); } + + p { margin: 0 0 1em 0; } + + a, a:visited { + color: var(--color-link); + text-decoration: underline; + } + + a img, a:visited img { border: 0; } + + strong { font-weight: bold; } + em { font-style: italic; } + + ul, ol { + margin: 1em 0 1em 2em; + padding: 0; + } + ul { list-style: disc; } + ol { list-style: decimal; } + + blockquote { + border-left: 3px solid var(--color-border); + padding: 0 0 0 12px; + margin: 1em 0 1em 20px; + color: var(--color-text-muted); + } + + code, kbd, samp { + font-family: var(--font-stack-mono); + font-size: 1em; + } + + pre { + background: var(--color-code-bg); + padding: 10px; + overflow-x: auto; + margin: 1em 0; + line-height: 1.4; + border: 1px solid var(--color-border); + } + pre code { + background: transparent; + padding: 0; + font-size: inherit; + } + + hr { + border: 0; + border-top: 1px solid var(--color-rule); + margin: 1em 0; + } + + table { margin: 1em 0; } + th, td { + padding: 4px 8px; + text-align: left; + vertical-align: top; + } + + label { + font-size: 0.9em; + font-weight: bold; + color: #555; + line-height: 1em; + margin-bottom: 1px; + } + + input, textarea, select, button { + font-family: inherit; + font-size: inherit; + color: inherit; + } + + textarea { + font-family: var(--font-stack-mono); + line-height: var(--line-height-normal); + resize: vertical; + border: 1px solid var(--color-border); + padding: 4px; + background: #fff; + } + + input[type="text"], + input[type="password"], + input[type="email"], + input[type="search"], + input[type="url"], + input[type="number"], + select { + border: 1px solid #ccc; + background: #fff; + padding: 2px 4px; + } + + button, + input[type="submit"], + input[type="button"], + input[type="reset"], + [role="button"] { + min-height: var(--tap-target); + } + + @media (min-width: 768px) { + button, + input[type="submit"], + input[type="button"], + input[type="reset"], + [role="button"] { + min-height: auto; + } + } } - -body { - min-height: 100vh; -} - -h1, h2, h3, h4, h5, h6 { - font-family: var(--headings-font); - line-height: 1.2; - margin-top: var(--space-5); - margin-bottom: var(--space-3); -} - -h1 { font-size: var(--headings-size); } -h2 { font-size: calc(var(--headings-size) * 0.85); } -h3 { font-size: calc(var(--headings-size) * 0.72); } - -p, ul, ol, dl, blockquote, pre, table, figure { - margin-top: 0; - margin-bottom: var(--space-4); -} - -ol, ul { - padding-left: var(--space-5); - list-style: revert; -} - -a { - color: var(--link); - text-decoration: underline; - text-underline-offset: 2px; -} - -a:visited { color: var(--link-visited); } -a:hover, a:focus { text-decoration-thickness: 2px; } - -button { - cursor: pointer; - padding: var(--space-2) var(--space-4); - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--surface); -} - -button:hover { background: var(--surface-alt); } - -input[type="text"], -input[type="password"], -input[type="email"], -input[type="number"], -textarea { - width: 100%; - padding: var(--space-2) var(--space-3); - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--surface); -} - -textarea { font-family: var(--content-font); resize: vertical; min-height: 12rem; } - -label { display: block; } - -fieldset { - border: 1px solid var(--border); - border-radius: var(--radius); - padding: var(--space-3) var(--space-4); - margin-bottom: var(--space-4); -} - -legend { padding: 0 var(--space-2); font-weight: 600; } - -hr { - border: 0; - border-top: 1px solid var(--border); - margin: var(--space-5) 0; -} - -code, pre, kbd, samp { - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 0.95em; -} - -pre { - padding: var(--space-3); - background: var(--surface-alt); - border-radius: var(--radius); - overflow-x: auto; -} - -blockquote { - border-left: 3px solid var(--border); - padding-left: var(--space-4); - color: var(--muted); -} - -small { color: var(--muted); } - -time { color: var(--muted); } diff --git a/jottit/static/components.css b/jottit/static/components.css index 13f6d4f..caf7dd1 100644 --- a/jottit/static/components.css +++ b/jottit/static/components.css @@ -1,238 +1,520 @@ -.page { - max-width: 60rem; - margin: 0 auto; - padding: var(--space-4); - display: grid; - grid-template-columns: 1fr; - gap: var(--space-5); -} +@layer components { + /* ---------- Site chrome (body.app) ---------- */ + + .site-header { + background: var(--color-header); + border-bottom: 1px solid var(--color-border); + padding: 0; + position: relative; + } -@media (min-width: 50rem) { - .page { - grid-template-columns: minmax(0, 1fr) 16rem; - grid-template-areas: - "header header" - "main sidebar" - "footer footer"; - } - .site-header { grid-area: header; } - main { grid-area: main; } - .site-sidebar { grid-area: sidebar; } - .site-footer { grid-area: footer; } -} + .site-header-inner { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + padding: 4px 7px 4px 56px; + } -.site-header { - background: var(--header-color); - color: var(--title-color); - padding: var(--space-5) var(--space-5) var(--space-3); - border-radius: var(--radius); -} + .site-titles { + flex: 1 1 auto; + min-width: 0; + } -.site-header a { color: var(--title-color); } + .site-title { + font-family: var(--font-family-title); + font-size: var(--font-size-h1); + line-height: var(--line-height-tight); + margin: 0; + color: var(--color-title); + font-weight: bold; + } -.site-header hgroup h1 { - font-family: var(--title-font); - font-size: var(--title-size); - margin: 0; -} + .site-title:has(a:empty) { display: none; } -.site-header .subtitle { - font-family: var(--subtitle-font); - font-size: var(--subtitle-size); - color: var(--subtitle-color); - margin: var(--space-1) 0 0; -} + .site-title a { + color: inherit; + text-decoration: none; + } -.claim-banner { - background: rgba(255,255,255,0.15); - padding: var(--space-2) var(--space-3); - margin: 0 0 var(--space-3); - border-radius: var(--radius); -} + .site-subtitle { + font-family: var(--font-family-subtitle); + font-size: 1.1em; + color: var(--color-subtitle); + margin: 0; + padding-left: 0.1em; + } -.site-nav { - margin-top: var(--space-3); -} + .site-subtitle:empty { display: none; } -.site-nav ul { - display: flex; - flex-wrap: wrap; - gap: var(--space-3); - padding: 0; - list-style: none; - margin: 0; -} + .site-nav { + flex: 0 0 auto; + font-family: var(--font-stack-system); + font-size: 0.9em; + display: flex; + gap: 6px; + align-items: center; + white-space: nowrap; + } -.site-nav a, -.site-nav button.link { - color: var(--subtitle-color); - text-decoration: none; - font-size: 0.9em; -} + .site-nav a { + color: var(--color-subtitle); + text-decoration: underline; + padding: 0 2px; + } + + .site-nav a:hover { + color: #fff; + background: #555; + text-decoration: none; + } -.site-nav a:hover, -.site-nav button.link:hover { text-decoration: underline; } + .site-nav .sep { color: var(--color-subtitle); } -.site-sidebar { - border-left: 1px solid var(--border); - padding-left: var(--space-4); -} + .site-nav form.inline { display: inline; margin: 0; padding: 0; } -@media (max-width: 50rem) { - .site-sidebar { - border-left: 0; - border-top: 1px solid var(--border); - padding-left: 0; - padding-top: var(--space-4); + /* Site body: gray panel holding the white content card + sidebar. */ + .site-body { + background: var(--color-bg-app); + padding: 10px var(--gutter); + max-width: 1000px; + margin: 0 auto; } -} -.page-list { - list-style: none; - padding: 0; - margin: 0 0 var(--space-4); - display: flex; - flex-direction: column; - gap: var(--space-1); -} + .site-content { + background: var(--color-bg-content); + border: 1px solid var(--color-border); + padding: 1em; + line-height: var(--line-height-normal); + font-family: var(--font-family-content); + } -.page-list strong { color: var(--fg); } + .site-content > :first-child { margin-top: 0; } -.new-page { - display: flex; - gap: var(--space-2); -} + .site-sidebar { font-size: 1.2em; } -.new-page input[type="text"] { flex: 1; } + .site-pages { + display: flex; + flex-direction: column; + margin: 0 0 2px 5px; + padding: 0 0 2px 0; + } -.site-footer { - text-align: center; - color: var(--muted); - font-size: 0.9em; - padding-top: var(--space-4); - border-top: 1px solid var(--border); -} + .site-pages a, + .site-pages strong { + font-size: 0.8em; + padding: 0 0 0.4em 0; + } -main > article { - max-width: var(--measure); -} + .site-pages a { + color: var(--color-link); + text-decoration: underline; + } -.front .lede { - font-size: 1.2em; - color: var(--muted); - margin-bottom: var(--space-5); -} + .site-pages [aria-current="page"] { + font-weight: bold; + color: var(--color-text); + } -.new-site, -.site-form, -.form { - display: flex; - flex-direction: column; - gap: var(--space-3); - max-width: var(--measure); -} + /* ---------- Page footer (small gray, centered, under content) ---------- */ + .page-footer { + background: var(--color-bg-app); + color: var(--color-text-quiet); + text-align: center; + font-size: 0.8em; + padding: 10px; + } -.form-error { - background: var(--danger-bg); - color: var(--danger); - padding: var(--space-2) var(--space-3); - border-radius: var(--radius); - border: 1px solid var(--danger); -} + .page-footer a { color: var(--color-text-quiet); } + .page-footer p { margin: 0 0 4px 0; } -button.destructive { - background: var(--danger); - color: #fff; - border-color: var(--danger); -} + /* ---------- Front-page chrome (body.site) ---------- */ + .main-header { + background: #fff; + border-bottom: 1px solid var(--color-border); + padding: 20px 16px 16px; + text-align: center; + } -button.destructive:hover { filter: brightness(0.9); } + .main-header img { + display: inline-block; + margin: 0 auto; + } -button.link { - background: none; - border: 0; - padding: 0; - color: var(--link); - text-decoration: underline; - cursor: pointer; -} + .page-main { + background: var(--color-bg-app); + padding: 8px 16px; + border-bottom: 1px solid var(--color-border); + } -.form.inline, -form.inline { display: inline; } - -.revision-banner { - background: var(--surface-alt); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: var(--space-3); - margin-bottom: var(--space-4); - display: flex; - flex-wrap: wrap; - gap: var(--space-3); - align-items: center; -} + .page-main > * { + max-width: 700px; + margin-left: auto; + margin-right: auto; + } -.page-meta { - color: var(--muted); - font-size: 0.9em; - margin-bottom: var(--space-3); - display: flex; - flex-wrap: wrap; - gap: var(--space-3); -} + .main-nav { + background: #fff; + text-align: center; + font-size: 1em; + padding: 24px 0 16px 0; + line-height: 1.6; + color: var(--color-text-muted); + } -.admin .admin-links { - display: flex; - flex-wrap: wrap; - gap: var(--space-3); - margin-bottom: var(--space-4); -} + .main-nav a { + color: var(--color-link); + text-decoration: underline; + font-weight: 500; + } -.revisions { - list-style: none; - padding: 0; - margin: 0 0 var(--space-4); - display: flex; - flex-direction: column; - gap: var(--space-2); -} + .main-nav [aria-current="page"] { + color: var(--color-text); + font-weight: bold; + text-decoration: none; + } -.revisions li { - padding: var(--space-2) 0; - border-bottom: 1px solid var(--border); - display: flex; - flex-wrap: wrap; - gap: var(--space-3); - align-items: baseline; -} + .main-nav-sep { + color: var(--color-text); + margin: 0 0.5em; + } -.revisions .changes { - color: var(--muted); - font-size: 0.9em; -} + .front-form { + text-align: center; + margin: 16px auto; + max-width: 700px; + } -.pagination { - margin-top: var(--space-4); -} + .front-form textarea { + display: block; + margin: 0 auto; + width: 450px; + max-width: 100%; + height: 250px; + resize: none; + } -.diff-body ins { - background: #e6ffed; - text-decoration: none; -} + .front-form p { margin: 12px 0 0 0; } + .front-form button { font-size: 1.1em; } -.diff-body del { - background: #ffebe9; - text-decoration: line-through; -} + /* ---------- Dialogs (signin, claim, change-password, deletes) ---------- */ + .dialog-wrapper { + display: flex; + align-items: center; + justify-content: center; + padding: 40px 16px; + } -.actions { - display: flex; - flex-wrap: wrap; - gap: var(--space-3); - margin-top: var(--space-4); -} + .dialog { + border: 1px solid #aaa; + padding: 15px 30px; + background: #fff; + font-family: var(--font-stack-system); + text-align: left; + max-width: 500px; + flex: 0 1 500px; + } + + .dialog-wide { + max-width: 700px; + flex-basis: 700px; + } + + .dialog h1 { + color: #000; + font-size: 150%; + margin-top: 0; + } + + .dialog label { color: #000; } + .dialog input { font-size: 120%; margin: 0; } + .dialog a { color: var(--color-link); padding: 2px; } + + /* ---------- Buttons ---------- */ + .button-cancel, + a.button-cancel { + color: red; + background: none; + border: 0; + padding: 2px; + text-decoration: underline; + cursor: pointer; + font-size: inherit; + } + + .button-cancel:hover, + a.button-cancel:hover { + color: #fff; + background: red; + text-decoration: none; + } + + .link-button { + background: none; + border: 0; + padding: 0; + color: var(--color-link); + cursor: pointer; + font: inherit; + text-decoration: underline; + min-height: auto; + } + + /* ---------- Forms ---------- */ + .form { + margin: 0; + padding: 0; + } + + .form-row { margin-bottom: 12px; } + .form-row label { + display: block; + margin-bottom: 2px; + } + + .form-actions { + margin: 10px 0; + font-size: 0.9em; + display: flex; + gap: 6px; + align-items: center; + flex-wrap: wrap; + } + + .form-error { + background: var(--color-error-bg); + border: 2px solid var(--color-error-border); + color: var(--color-error-text); + padding: 5px 12px; + margin: 15px 0; + } + + /* ---------- History / changes / diff ---------- */ + .history { margin-top: 10px; } + + .revisions { + margin: 10px 0; + } + + .revisions li { + display: grid; + grid-template-columns: auto auto 1fr; + gap: 8px; + padding: 4px 5px; + align-items: baseline; + } + + .revisions li:nth-child(even) { + background: var(--color-code-bg); + } + + .revisions time { + color: var(--color-text-muted); + font-size: 0.9em; + } + + .revisions .changes { + color: var(--color-text-muted); + font-size: 0.9em; + } + + .diff-body { + margin: 10px 0; + padding: 10px; + border: 1px solid #999; + background: var(--color-code-bg); + font-family: var(--font-stack-mono); + font-size: 0.9em; + white-space: pre-wrap; + } -.hint { - color: var(--muted); - font-size: 0.9em; + .diff-body ins { + background: var(--color-diff-insert); + text-decoration: none; + } + + .diff-body del { + background: var(--color-diff-delete); + text-decoration: line-through; + } + + span.replace { background: var(--color-diff-replace); } + span.insert { background: var(--color-diff-insert); } + span.delete { background: var(--color-diff-delete); } + + /* ---------- Revision banner (browse old revisions) ---------- */ + #browse_revs, + .revision-banner { + position: relative; + margin: 10px 0; + padding: 1em; + border: 1px solid #ccc; + background: #eee; + } + + #browse_revs a, + .revision-banner a { + color: var(--color-link); + } + + /* ---------- Pagination ---------- */ + .pagination { margin: 10px 4px; } + + .pagination a { + color: var(--color-link); + text-decoration: none; + } + + /* ---------- Settings table (admin pages) ---------- */ + .admin { + margin-top: 10px; + } + + .admin > header h1 { + margin-top: 0; + } + + .admin .form .form-actions { + margin-top: 16px; + } + + /* ---------- Primer welcome banner ---------- */ + .primer { + position: relative; + margin: 10px 0; + padding: 4px 8px; + background: var(--color-callout-bg); + border: 1px solid var(--color-callout-border); + } + + .primer h1 { + font-size: 14px; + margin: 8px 2px 5px 2px; + font-family: var(--font-stack-system); + } + + .primer p { + font-size: 11px; + margin: 0 2px 8px 2px; + font-family: var(--font-stack-system); + } + + .primer-dismiss { + position: absolute; + top: 8px; + right: 8px; + margin: 0; + } + + .primer .link-button { + color: var(--color-link); + font-size: 10px; + } + + /* ---------- Dateline (view-page footer) ---------- */ + .dateline { + font-family: var(--font-stack-system); + font-size: 0.9em; + margin-top: 3px; + padding-top: 2px; + border-top: 1px solid var(--color-rule); + text-align: right; + color: var(--color-text-quiet); + } + + .dateline a { + color: #555; + background: var(--color-code-bg); + padding: 2px; + text-decoration: none; + margin-left: 4px; + } + + .dateline a:hover { + color: #fff; + background: #555; + } + + /* ---------- Claim-this-site black banner ---------- */ + #claim_site { + text-align: left; + background: #000; + color: #fff; + padding: 0.7em 0.7em 0.7em 60px; + margin: 0; + font-size: 1em; + position: relative; + } + + #claim_site a { + color: #fff; + text-decoration: none; + } + + #claim_site label { color: #fff; font-weight: normal; } + + #claim_site #pick_password { + position: absolute; + right: 0.7em; + top: 0.6em; + } + + #claim_site #pick_password input, + #claim_site #pick_password button { + position: relative; + top: -1px; + margin: 0 0 0 3px; + font-size: 0.9em; + color: initial; + height: 22px; + min-height: 0; + box-sizing: border-box; + padding: 2px 4px; + } + + /* ---------- Sidebar "Create a new page" widget ---------- */ + #new_page button { + margin: 0 2px 15px 0; + background: #e5e5e5; + border: 1px solid #ccc; + padding: 4px 5px; + font-size: 0.8em; + font-weight: bold; + cursor: pointer; + min-height: auto; + } + + #new_page_input { + font-size: 0.7em; + margin: 0 5px 15px 0; + } + + #new_page_input input { + font-size: 1.1em; + padding-right: 2px; + } + + #new_page_name { + margin-bottom: 7px; + width: 150px; + } + + .link-close-newpage { + color: red; + padding-left: 2px; + } + + /* ---------- ≥768px: two-column site body ---------- */ + @media (min-width: 768px) { + .site-body { + display: grid; + grid-template-columns: 60px minmax(0, 1fr) 23px var(--site-sidebar-width); + grid-template-areas: ". main . sidebar"; + padding: 10px 0 0 0; + column-gap: 0; + background: var(--color-bg-app); + } + .site-content-column { grid-area: main; } + .site-sidebar { grid-area: sidebar; background: var(--color-bg-app); } + } } diff --git a/jottit/static/logo.png b/jottit/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2b5bfd716c8b5121cc5416b3416608078041511b GIT binary patch literal 4761 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 literal 0 HcmV?d00001 diff --git a/jottit/static/reset.css b/jottit/static/reset.css index 98538e2..deff3c9 100644 --- a/jottit/static/reset.css +++ b/jottit/static/reset.css @@ -1,17 +1,47 @@ -*, *::before, *::after { box-sizing: border-box; } +@layer reset { + *, *::before, *::after { box-sizing: border-box; } + * { margin: 0; } -html, body, h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ol, ul, fieldset { - margin: 0; - padding: 0; -} + html { + -webkit-text-size-adjust: 100%; + text-size-adjust: 100%; + tab-size: 4; + } -ol, ul { list-style: none; } + body { + min-height: 100svh; + line-height: var(--line-height-normal); + -webkit-font-smoothing: antialiased; + } -img, picture, svg, video { max-width: 100%; display: block; } + img, picture, video, canvas, svg { + display: block; + max-width: 100%; + } + img, video { height: auto; } -button, input, select, textarea { - font: inherit; - color: inherit; -} + input, textarea, select { + font: inherit; + color: inherit; + } + + p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; } + + a { + color: inherit; + text-decoration: none; + } -a { color: inherit; } + ul, ol { + padding: 0; + list-style: none; + } + + table { border-collapse: collapse; } + + textarea:focus, input:focus { outline: none; } + :focus-visible { + outline: 1px solid var(--color-accent); + outline-offset: 0; + } +} diff --git a/jottit/static/tokens.css b/jottit/static/tokens.css index 105fb69..28e7704 100644 --- a/jottit/static/tokens.css +++ b/jottit/static/tokens.css @@ -1,40 +1,60 @@ -:root { - --hue: 210; - --brightness: 100; - - --header-color: hsl(var(--hue) 60% calc(var(--brightness) * 0.22%)); - --title-color: #fff; - --subtitle-color: #bfe8ff; - - --bg: hsl(var(--hue) 30% calc(var(--brightness) * 0.99%)); - --fg: #222; - --muted: #666; - --link: #06c; - --link-visited: #639; - --border: #ddd; - --surface: #fff; - --surface-alt: #f7f7f7; - --danger: #b00020; - --danger-bg: #fdecea; - - --title-font: system-ui, sans-serif; - --subtitle-font: system-ui, sans-serif; - --headings-font: system-ui, sans-serif; - --content-font: system-ui, sans-serif; - - --title-size: 200%; - --subtitle-size: 100%; - --headings-size: 150%; - --content-size: 100%; - - --space-1: 0.25rem; - --space-2: 0.5rem; - --space-3: 0.75rem; - --space-4: 1rem; - --space-5: 1.5rem; - --space-6: 2rem; - --space-8: 3rem; - - --radius: 4px; - --measure: 38rem; +@layer tokens, reset, base, components, utilities; + +@layer tokens { + :root { + --color-header: #f0f0ee; + --color-title: #000000; + --color-subtitle: #555555; + + --font-stack-system: system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", Roboto, sans-serif; + --font-stack-mono: ui-monospace, SFMono-Regular, "SF Mono", + Menlo, Consolas, "Liberation Mono", monospace; + + --font-family-title: var(--font-stack-system); + --font-family-subtitle: var(--font-stack-system); + --font-family-headings: var(--font-stack-system); + --font-family-content: var(--font-stack-system); + + --color-bg-app: #f0f0ee; + --color-bg-content: #ffffff; + --color-text: #000000; + --color-text-muted: #777777; + --color-text-quiet: gray; + --color-border: #dddddd; + --color-rule: #dddddd; + --color-border-strong: #999999; + --color-link: blue; + --color-link-quiet: #555555; + --color-page-name: maroon; + --color-accent: #4a90e2; + + --color-code-bg: #eeeeee; + --color-callout-bg: #ffff99; + --color-callout-border: #cccc00; + --color-success-bg: #ddffdd; + --color-success-border: #bbeebb; + --color-success-text: #225522; + --color-error-bg: #ffd6d8; + --color-error-border: red; + --color-error-text: red; + --color-diff-insert: #aaffaa; + --color-diff-delete: #ffaaaa; + --color-diff-replace: #ffdd88; + + --font-size-base: 13px; + --line-height-tight: 1.2; + --line-height-normal: 1.5em; + + --font-size-h1: clamp(1.5em, 1.3em + 0.6vw, 2em); + --font-size-h2: 1.5em; + --font-size-h3: 1.2em; + --font-size-h4: 1.05em; + --font-size-h5: 1em; + --font-size-h6: 0.9em; + + --site-sidebar-width: 200px; + --gutter: 16px; + --tap-target: 44px; + } } diff --git a/jottit/static/utilities.css b/jottit/static/utilities.css index 19a3c90..c9b0400 100644 --- a/jottit/static/utilities.css +++ b/jottit/static/utilities.css @@ -1,19 +1,15 @@ -.visually-hidden { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0 0 0 0); - white-space: nowrap; - border: 0; +@layer utilities { + .visually-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; + } + + .text-muted { color: var(--color-text-muted); } } - -.deleted { color: var(--muted); } - -.empty { color: var(--muted); } - -.cancel { color: var(--muted); } - -.count { color: var(--muted); } diff --git a/jottit/templates/admin_change_password.html b/jottit/templates/admin_change_password.html index 4895e5a..5138564 100644 --- a/jottit/templates/admin_change_password.html +++ b/jottit/templates/admin_change_password.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Change password{% endblock %} {% block content %}
diff --git a/jottit/templates/admin_change_site_address.html b/jottit/templates/admin_change_site_address.html index 0f12da7..908d9e2 100644 --- a/jottit/templates/admin_change_site_address.html +++ b/jottit/templates/admin_change_site_address.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Change site address{% endblock %} {% block content %}
diff --git a/jottit/templates/admin_delete.html b/jottit/templates/admin_delete.html index f550055..940b3a2 100644 --- a/jottit/templates/admin_delete.html +++ b/jottit/templates/admin_delete.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Delete site{% endblock %} {% block content %}
diff --git a/jottit/templates/admin_design.html b/jottit/templates/admin_design.html index ae6312b..9491cad 100644 --- a/jottit/templates/admin_design.html +++ b/jottit/templates/admin_design.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Design{% endblock %} {% block content %}
diff --git a/jottit/templates/admin_settings.html b/jottit/templates/admin_settings.html index c9ce99d..7ed872b 100644 --- a/jottit/templates/admin_settings.html +++ b/jottit/templates/admin_settings.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Settings{% endblock %} {% block content %}
diff --git a/jottit/templates/base.html b/jottit/templates/base.html index b95f82c..90cd4d0 100644 --- a/jottit/templates/base.html +++ b/jottit/templates/base.html @@ -13,28 +13,12 @@ - {% block head %}{% endblock %} {% if design %} {% include "partials/design_style.html" %} {% endif %} + {% block head %}{% endblock %} - -
- {% if site %} - {% include "partials/site_header.html" %} - {% endif %} - -
- {% block content %}{% endblock %} -
- - {% if site %} - {% include "partials/site_sidebar.html" %} - {% endif %} - - -
+ + {% block body %}{% endblock %} diff --git a/jottit/templates/change_password.html b/jottit/templates/change_password.html index d4019fa..1360908 100644 --- a/jottit/templates/change_password.html +++ b/jottit/templates/change_password.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Set a new password{% endblock %} {% block content %}
diff --git a/jottit/templates/changes.html b/jottit/templates/changes.html index 123e8a9..2259d89 100644 --- a/jottit/templates/changes.html +++ b/jottit/templates/changes.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Recent changes{% endblock %} {% block head %} diff --git a/jottit/templates/claim_site.html b/jottit/templates/claim_site.html index b9b4993..06f14de 100644 --- a/jottit/templates/claim_site.html +++ b/jottit/templates/claim_site.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Claim this site{% endblock %} {% block content %}
diff --git a/jottit/templates/deleted.html b/jottit/templates/deleted.html index f99da8c..f6a3923 100644 --- a/jottit/templates/deleted.html +++ b/jottit/templates/deleted.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Deleted{% endblock %} {% block content %}
diff --git a/jottit/templates/diff.html b/jottit/templates/diff.html index d769b02..26fac8b 100644 --- a/jottit/templates/diff.html +++ b/jottit/templates/diff.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Diff: {{ page_name or "Home" }}{% endblock %} {% block content %} diff --git a/jottit/templates/edit_page.html b/jottit/templates/edit_page.html index d80d160..e2b35be 100644 --- a/jottit/templates/edit_page.html +++ b/jottit/templates/edit_page.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Edit: {{ page_name or "Home" }}{% if site and site.title %} — {{ site.title }}{% endif %}{% endblock %} diff --git a/jottit/templates/forgot_password.html b/jottit/templates/forgot_password.html index 3ed320c..305503d 100644 --- a/jottit/templates/forgot_password.html +++ b/jottit/templates/forgot_password.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Forgot your password?{% endblock %} {% block content %}
diff --git a/jottit/templates/history.html b/jottit/templates/history.html index 3a9e384..aa76d3e 100644 --- a/jottit/templates/history.html +++ b/jottit/templates/history.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}History: {{ page_name or "Home" }}{% endblock %} {% block head %} diff --git a/jottit/templates/index.html b/jottit/templates/index.html index 3f33a0e..3418d71 100644 --- a/jottit/templates/index.html +++ b/jottit/templates/index.html @@ -1,28 +1,11 @@ -{% 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.

-
+{% extends "main_base.html" %} +{% set page_url = "/" %} + +{% block title %}Jottit{% endblock %} -
-

- -

-

- -

-

-
-
+{% block content %} +
+ +

+
{% endblock %} diff --git a/jottit/templates/main_base.html b/jottit/templates/main_base.html new file mode 100644 index 0000000..ac35c5d --- /dev/null +++ b/jottit/templates/main_base.html @@ -0,0 +1,37 @@ +{# Front-page chrome (body.site): white header band with the green + Jottit logo, gray body holding the content, blue-link nav along + the bottom. The front page and any other jottit-the-service pages + (about, sites, help — to be ported later) extend this. #} +{% extends "base.html" %} + +{% block body_class %}site{% endblock %} + +{% block body %} +
+ {% block header %} + + {% endblock %} +
+ +
+ {% block content %}{% endblock %} +
+ + {% set _here = page_url|default("/") %} + {% set _nav = [ + ("/", "home"), + ("/about", "about"), + ("/sites", "find your site"), + ("/help", "help"), + ] %} + +{% endblock %} diff --git a/jottit/templates/notfound.html b/jottit/templates/notfound.html index 3b6cbe4..063fc20 100644 --- a/jottit/templates/notfound.html +++ b/jottit/templates/notfound.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Not found{% endblock %} {% block content %}
diff --git a/jottit/templates/partials/design_style.html b/jottit/templates/partials/design_style.html index 628f361..0b5cbc1 100644 --- a/jottit/templates/partials/design_style.html +++ b/jottit/templates/partials/design_style.html @@ -1,23 +1,14 @@ -{# 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. #} +{# Per-site overrides written into :root so the global stylesheet + picks them up via var(--…). Empty values leave the token defaults + from tokens.css in place. #} diff --git a/jottit/templates/partials/site_header.html b/jottit/templates/partials/site_header.html deleted file mode 100644 index 3cf80fe..0000000 --- a/jottit/templates/partials/site_header.html +++ /dev/null @@ -1,34 +0,0 @@ -{# 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 deleted file mode 100644 index 2dd5b69..0000000 --- a/jottit/templates/partials/site_sidebar.html +++ /dev/null @@ -1,32 +0,0 @@ -{# 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 %} diff --git a/jottit/templates/signin.html b/jottit/templates/signin.html index a716dbc..b54cbe0 100644 --- a/jottit/templates/signin.html +++ b/jottit/templates/signin.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} {% block title %}Sign in{% endblock %} {% block content %}
diff --git a/jottit/templates/site_base.html b/jottit/templates/site_base.html new file mode 100644 index 0000000..af990e1 --- /dev/null +++ b/jottit/templates/site_base.html @@ -0,0 +1,89 @@ +{# Per-site chrome (body.app): black claim banner above a colored header + strip, white content card on the left, page-list sidebar on the right, + footer credit beneath the content. Templates that render inside a + site (view_page, history, settings, …) extend this. #} +{% extends "base.html" %} + +{% block body_class %}app{% endblock %} + +{% block body %} + {% if site and is_unclaimed %} +
+ Claim this site + + + + + +
+ {% endif %} + + + +
+
+
+ {% block content %}{% endblock %} +
+ +
+ +
+{% endblock %} diff --git a/jottit/templates/view_page.html b/jottit/templates/view_page.html index 44327f6..2072d59 100644 --- a/jottit/templates/view_page.html +++ b/jottit/templates/view_page.html @@ -1,6 +1,6 @@ -{% extends "base.html" %} +{% extends "site_base.html" %} -{% block title %}{{ page_name or "Home" }}{% if site and site.title %} — {{ site.title }}{% endif %}{% endblock %} +{% block title %}{% if site and site.title %}{{ site.title }}: {% endif %}{{ page_name or "Home" }}{% endblock %} {% block head %} -
-

{{ page_name or "Home" }}

- {% if is_old_revision %} -

- Viewing revision {{ revision.revision }} - of {{ latest_revision_number }}. - Latest - {% if revision.revision > 1 %} - · prev - {% endif %} - {% if revision.revision < latest_revision_number %} - · next - {% endif %} -

+ {% if site.show_primer and (is_signed_in or is_unclaimed) and not page_name %} +
+ +

That was easy!

+

Here's your new site. Be sure to bookmark it so you can find it again. To change it, just press the edit button at the bottom of the page. Enjoy!

+
+ {% endif %} - {% if is_signed_in or is_unclaimed %} -
- - - -
+ {% if is_old_revision %} +
+

+ Viewing revision {{ revision.revision }} of {{ latest_revision_number }}. + Latest + {% if revision.revision > 1 %} + | « prev + {% endif %} + {% if revision.revision < latest_revision_number %} + | next » {% endif %} +

+ {% if is_signed_in or is_unclaimed %} +
+ + + +
{% endif %} -
- -
- {{ content_html|safe }}
+ {% endif %} -
-

- Revision {{ revision.revision }} · - - {% if is_signed_in or is_unclaimed %} - · edit - · history - {% endif %} -

-
-
+ {{ content_html|safe }} + + {% endblock %} diff --git a/jottit/views/site.py b/jottit/views/site.py index 0cdedfc..fcc0b92 100644 --- a/jottit/views/site.py +++ b/jottit/views/site.py @@ -12,6 +12,7 @@ get_request_conn, recover_password, set_change_pwd_token, + sites, ) from jottit.render import format_content from jottit.urls import page_slug, site_root @@ -320,5 +321,9 @@ def _absolute_url(path: str) -> str: return f"{request.scheme}://{request.host}{site_root()}{path}" -def hide_primer(site_slug: str) -> str: - return f"site:{site_slug} site/hide-primer POST (TODO)" +def hide_primer(site_slug: str) -> ResponseReturnValue: + if g.site is None: + abort(404) + conn = get_request_conn() + conn.execute(sites.update().where(sites.c.id == g.site.id).values(show_primer=False)) + return redirect(site_root(), code=303) diff --git a/tests/test_page_view.py b/tests/test_page_view.py index b8931f6..c57a033 100644 --- a/tests/test_page_view.py +++ b/tests/test_page_view.py @@ -40,7 +40,7 @@ def test_home_on_public_subdomain_renders_latest_revision( assert response.status_code == 200 body = response.data.decode() assert "hi there" in body - assert "Revision 1" in body + assert "changed" in body # dateline footer def test_named_page_renders_content(client: FlaskClient, db_engine: Engine) -> None: diff --git a/tests/test_secret_routing.py b/tests/test_secret_routing.py index e3fdc90..eb0e1fa 100644 --- a/tests/test_secret_routing.py +++ b/tests/test_secret_routing.py @@ -1,29 +1,13 @@ from __future__ import annotations -import pytest from flask.testing import FlaskClient APEX = "http://jottit.test/" -@pytest.mark.parametrize( - ("method", "path", "expected_substring"), - [ - # /site/claim, /site/signin, /site/signout, /site/forgot-password, - # /site/change-password, /site/changes, /site/changes.rss, and - # /site/changes.json are exercised end-to-end in their respective - # test files. - ("POST", "/abc123/site/hide-primer", "site/hide-primer POST"), - ], -) -def test_secret_site_routes( - client: FlaskClient, method: str, path: str, expected_substring: str -) -> None: - response = client.open(path, method=method, base_url=APEX) - assert response.status_code == 200 - body = response.data.decode() - assert body.startswith("site:abc123 ") - assert expected_substring in body +# /site/claim, /site/signin, /site/signout, /site/forgot-password, +# /site/change-password, /site/changes, /site/changes.rss, /site/changes.json, +# and /site/hide-primer are exercised end-to-end in their respective test files. # /admin/* routes via the secret blueprint are exercised end-to-end in diff --git a/tests/test_subdomain_routing.py b/tests/test_subdomain_routing.py index 1d4206e..5107e57 100644 --- a/tests/test_subdomain_routing.py +++ b/tests/test_subdomain_routing.py @@ -1,29 +1,10 @@ from __future__ import annotations -import pytest from flask.testing import FlaskClient -SITE_BASE = "http://mysite.jottit.test/" - - -@pytest.mark.parametrize( - ("method", "path", "expected_substring"), - [ - # /site/claim, /site/signin, /site/signout, /site/forgot-password, - # /site/change-password, /site/changes, /site/changes.rss, and - # /site/changes.json are exercised end-to-end in their respective - # test files. - ("POST", "/site/hide-primer", "site/hide-primer POST"), - ], -) -def test_site_blueprint( - client: FlaskClient, method: str, path: str, expected_substring: str -) -> None: - response = client.open(path, method=method, base_url=SITE_BASE) - assert response.status_code == 200 - body = response.data.decode() - assert body.startswith("site:mysite ") - assert expected_substring in body +# /site/claim, /site/signin, /site/signout, /site/forgot-password, +# /site/change-password, /site/changes, /site/changes.rss, /site/changes.json, +# and /site/hide-primer are exercised end-to-end in their respective test files. # /admin/* routes are exercised end-to-end in tests/test_admin_*.py. From 40bbb078264eb23cf7abbf5b5d6e1e66554a722d Mon Sep 17 00:00:00 2001 From: Simon Carstensen Date: Sat, 16 May 2026 16:31:15 +0200 Subject: [PATCH 2/6] Lay out admin pages in the 2007 3-column settings table Restructured admin_settings (title, subtitle, site address, password, email, kind-of-site) into the .settings table with label / input / explain columns separated by #ddd dividers, matching 2007. Nested .security radio table inside the "Kind of site" row. admin_change_password / admin_change_site_address / admin_delete get plain h1 + form-actions row (Save + red Cancel link). admin_design splits into Fonts / Colors / Sizes / Background sub-tables sharing the .settings layout. Co-Authored-By: Claude Opus 4.7 (1M context) --- jottit/static/components.css | 81 +++++++++++- jottit/templates/admin_change_password.html | 36 +++--- .../templates/admin_change_site_address.html | 31 +++-- jottit/templates/admin_delete.html | 17 +-- jottit/templates/admin_design.html | 120 +++++++++++++----- jottit/templates/admin_settings.html | 112 ++++++++++------ 6 files changed, 277 insertions(+), 120 deletions(-) diff --git a/jottit/static/components.css b/jottit/static/components.css index caf7dd1..1f1ed88 100644 --- a/jottit/static/components.css +++ b/jottit/static/components.css @@ -366,16 +366,87 @@ } /* ---------- Settings table (admin pages) ---------- */ - .admin { + .settings { + width: 100%; + border-collapse: collapse; margin-top: 10px; + table-layout: fixed; } - .admin > header h1 { - margin-top: 0; + .settings th, + .settings td { + vertical-align: top; + padding: 15px 0 20px 0; + border-bottom: 1px solid var(--color-rule); + } + + .settings th { + width: 140px; + white-space: nowrap; + padding-top: 16px; + padding-right: 40px; + text-align: left; + font-family: var(--font-stack-system); + font-weight: bold; + } + + .settings td.explain { + font-size: 90%; + font-weight: normal; + width: 220px; + padding-left: 20px; + color: var(--color-text-muted); + } + + .settings input[type="text"], + .settings input[type="password"], + .settings input[type="email"], + .settings input[type="url"] { + width: 240px; + max-width: 100%; + } + + .settings .security { + border-collapse: collapse; + width: auto; + } + + .settings .security td { + border: 0; + padding: 2px 8px 2px 0; + font-size: 1em; + } + + .settings .security td.explain { + font-size: 0.9em; + width: auto; + padding-left: 8px; + color: var(--color-text-muted); + } + + .settings .security label { + font-weight: normal; + color: var(--color-text); + } + + #delete_site { + margin-top: 40px; } - .admin .form .form-actions { - margin-top: 16px; + #delete_site h2 { + font-size: 1em; + border-bottom: 1px solid var(--color-rule); + margin-bottom: 10px; + } + + #delete_site a { + color: red; + } + + .admin-links { + margin-top: 32px; + font-size: 0.9em; + color: var(--color-text-muted); } /* ---------- Primer welcome banner ---------- */ diff --git a/jottit/templates/admin_change_password.html b/jottit/templates/admin_change_password.html index 5138564..0a2d798 100644 --- a/jottit/templates/admin_change_password.html +++ b/jottit/templates/admin_change_password.html @@ -1,25 +1,23 @@ {% extends "site_base.html" %} {% block title %}Change password{% endblock %} -{% block content %} -
-

Change password

- {% include "partials/form_error.html" %} +{% block content %} +

Change password

-
-

- -

-

- -

-

-
+ {% include "partials/form_error.html" %} -

Back to settings

-
+
+

+ + +

+

+ + +

+
+ + Cancel +
+
{% endblock %} diff --git a/jottit/templates/admin_change_site_address.html b/jottit/templates/admin_change_site_address.html index 908d9e2..e4532fb 100644 --- a/jottit/templates/admin_change_site_address.html +++ b/jottit/templates/admin_change_site_address.html @@ -1,22 +1,21 @@ {% extends "site_base.html" %} {% block title %}Change site address{% endblock %} -{% block content %} -
-

Change site address

- {% include "partials/form_error.html" %} +{% block content %} +

Change site address

-
-

- -

-

-
+ {% include "partials/form_error.html" %} -

Back to settings

-
+
+

+ + + Letters, numbers, and dashes. Leave blank to remove the subdomain. +

+
+ + Cancel +
+
{% endblock %} diff --git a/jottit/templates/admin_delete.html b/jottit/templates/admin_delete.html index 940b3a2..9e9051d 100644 --- a/jottit/templates/admin_delete.html +++ b/jottit/templates/admin_delete.html @@ -1,14 +1,15 @@ {% extends "site_base.html" %} {% block title %}Delete site{% endblock %} + {% block content %} -
-

Delete site

-

This will remove the site from public listings and free its subdomain. Pages and revisions stay on file in case we ever add a restore path.

+

Delete site

-
-

-
+

This will remove the site from public listings and free its subdomain. Pages and revisions stay on file in case we ever add a restore path.

-

Cancel

-
+
+
+ + Cancel +
+
{% endblock %} diff --git a/jottit/templates/admin_design.html b/jottit/templates/admin_design.html index 9491cad..5306922 100644 --- a/jottit/templates/admin_design.html +++ b/jottit/templates/admin_design.html @@ -1,44 +1,96 @@ {% extends "site_base.html" %} {% block title %}Design{% endblock %} -{% block content %} -
-

Design

- {% include "partials/form_error.html" %} +{% block content %} +

Design

-
-
- Fonts -

-

-

-

-
+ {% include "partials/form_error.html" %} -
- Colors -

-

-

-
+ +

Fonts

+ + + + + + + + + + + + + + + + + + + + + +
Title
Subtitle
Headings
Content
-
- Sizes (percent) -

-

-

-

-
+

Colors

+ + + + + + + + + + + + + + + + +
HeaderHex color, e.g. #003452
Title
Subtitle
-
- Background -

-

-
+

Sizes

+ + + + + + + + + + + + + + + + + + + + + +
TitlePercent (25–500)
Subtitle
Headings
Content
-

-
+

Background

+ + + + + + + + + + + +
Hue0–360
Brightness0–300
-

Back to settings

-
+
+ + Cancel +
+ {% endblock %} diff --git a/jottit/templates/admin_settings.html b/jottit/templates/admin_settings.html index 7ed872b..6c230a9 100644 --- a/jottit/templates/admin_settings.html +++ b/jottit/templates/admin_settings.html @@ -1,46 +1,82 @@ {% extends "site_base.html" %} {% block title %}Settings{% endblock %} -{% block content %} -
-

Settings

- {% include "partials/form_error.html" %} +{% block content %} + {% include "partials/form_error.html" %} -
-

- -

-

- -

-

- -

+ + + + + + + + + + + + + + + + + + {% if not is_unclaimed %} + + + + + + + + + + + + + + + + {% endif %} +
TitleAppears at the top of every page
SubtitleAppears below the title
Site address + {% if public_url %} + {{ public_url }} + (change) + {% else %} + Give your site an address + {% endif %} + Give your website an address that's easy to remember
PasswordChange passwordWith a password you can allow only the people who know it to change the settings, edit pages or view your site
Email addressIf you forget your password, we'll send it to this address
Kind of site + + + + + + + + + + + + + + + + +
Require password to view and edit site
Require password to edit site
Everyone can view and edit
+
-
- Security -

-

-

-
+
+ +
+
-

- +
+

Delete site

+

Deleting this site will immediately take it down. Delete this site.

+
- -
+ {% endblock %} From 55ffb9c8640e031b3fc027b741d12edfc61e64aa Mon Sep 17 00:00:00 2001 From: Simon Carstensen Date: Sat, 16 May 2026 16:31:15 +0200 Subject: [PATCH 3/6] Render auth screens in a centered dialog card MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added dialog_base.html — body.dialog-page, no site chrome, just a white card centered on the gray panel. signin / claim_site / forgot_password / change_password switch from site_base.html to dialog_base.html. claim_site uses the .settings 3-column table for password / email / kind-of-site, matching the 2007 #claim_dialog layout. Co-Authored-By: Claude Opus 4.7 (1M context) --- jottit/templates/change_password.html | 28 +++++------ jottit/templates/claim_site.html | 72 +++++++++++++++++---------- jottit/templates/dialog_base.html | 14 ++++++ jottit/templates/forgot_password.html | 27 +++++----- jottit/templates/signin.html | 31 ++++++------ 5 files changed, 104 insertions(+), 68 deletions(-) create mode 100644 jottit/templates/dialog_base.html diff --git a/jottit/templates/change_password.html b/jottit/templates/change_password.html index 1360908..d907e81 100644 --- a/jottit/templates/change_password.html +++ b/jottit/templates/change_password.html @@ -1,19 +1,19 @@ -{% extends "site_base.html" %} +{% extends "dialog_base.html" %} {% block title %}Set a new password{% endblock %} + {% block content %} -
-

Set a new password

+

Set a new password

- {% include "partials/form_error.html" %} + {% include "partials/form_error.html" %} -
- -

- -

-

-
-
+
+ +

+ + +

+
+ +
+
{% endblock %} diff --git a/jottit/templates/claim_site.html b/jottit/templates/claim_site.html index 06f14de..fdfd485 100644 --- a/jottit/templates/claim_site.html +++ b/jottit/templates/claim_site.html @@ -1,32 +1,52 @@ -{% extends "site_base.html" %} +{% extends "dialog_base.html" %} {% block title %}Claim this site{% endblock %} -{% block content %} -
-

Claim this site

-

Set a password and pick how open you want this site to be. We'll email you a recovery link if you forget the password later.

+{% block dialog_class %}dialog-wide{% endblock %} - {% include "partials/form_error.html" %} +{% block content %} +

Claim this site

+

Set a password and pick how open you want this site to be. We'll email you a recovery link if you forget the password later.

-
-

- -

-

- -

+ {% include "partials/form_error.html" %} -
- Security -

-

-

-
+ + + + + + + + + + + + + + + + + +
PasswordRequired to edit (and maybe view) this site
EmailWe'll send password resets here
Kind of site + + + + + + + + + + + + + + + + +
Require password to view and edit
Anyone can view, password to edit
Anyone can view and edit
+
-

-
-
+
+ +
+ {% endblock %} diff --git a/jottit/templates/dialog_base.html b/jottit/templates/dialog_base.html new file mode 100644 index 0000000..e6bb293 --- /dev/null +++ b/jottit/templates/dialog_base.html @@ -0,0 +1,14 @@ +{# Base for modal-style single-form pages: signin, claim, forgot + password, change password (token recovery flow). No site chrome — + just a centered white card on the gray page. #} +{% extends "base.html" %} + +{% block body_class %}page dialog-page{% endblock %} + +{% block body %} +
+
+ {% block content %}{% endblock %} +
+
+{% endblock %} diff --git a/jottit/templates/forgot_password.html b/jottit/templates/forgot_password.html index 305503d..5751aee 100644 --- a/jottit/templates/forgot_password.html +++ b/jottit/templates/forgot_password.html @@ -1,16 +1,19 @@ -{% extends "site_base.html" %} +{% extends "dialog_base.html" %} {% block title %}Forgot your password?{% endblock %} + {% block content %} -
-

Forgot your password?

+

Forgot your password?

- {% if sent %} -

We've emailed a password-reset link to the address on file. The link works once — if you need another, come back to this page.

- {% else %} -

We'll email a one-time password-reset link to the email address on file for this site.

-
-

-
- {% endif %} -
+ {% if sent %} +

We've emailed a password-reset link to the address on file. The link works once — if you need another, come back to this page.

+

Back to the site

+ {% else %} +

We'll email a one-time password-reset link to the email address on file for this site.

+
+
+ + Cancel +
+
+ {% endif %} {% endblock %} diff --git a/jottit/templates/signin.html b/jottit/templates/signin.html index b54cbe0..c91b008 100644 --- a/jottit/templates/signin.html +++ b/jottit/templates/signin.html @@ -1,21 +1,20 @@ -{% extends "site_base.html" %} +{% extends "dialog_base.html" %} {% block title %}Sign in{% endblock %} -{% block content %} -
-

Sign in

- {% include "partials/form_error.html" %} +{% block content %} +

Sign in

-
- -

- -

-

-
+ {% include "partials/form_error.html" %} -

Forgot your password?

-
+
+ +

+ + +

+
+ + I forgot the password +
+
{% endblock %} From 3f48262feeffec4031e8487fc3ec8d969642c9aa Mon Sep 17 00:00:00 2001 From: Simon Carstensen Date: Sat, 16 May 2026 16:31:15 +0200 Subject: [PATCH 4/6] Style the edit page (full-width textarea, maroon page name) Cleaner edit chrome inside the standard site_base.html: full-width monospace textarea, "Editing: PAGE_NAME" h1 with the page name in maroon (2007 #content h1 .page_name color), Save / red Cancel / Delete action row. Live preview/help pane is M9 (JS) work. Co-Authored-By: Claude Opus 4.7 (1M context) --- jottit/static/components.css | 28 ++++++++++++++++++++++++++++ jottit/templates/edit_page.html | 22 ++++++++++------------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/jottit/static/components.css b/jottit/static/components.css index 1f1ed88..880dd73 100644 --- a/jottit/static/components.css +++ b/jottit/static/components.css @@ -449,6 +449,34 @@ color: var(--color-text-muted); } + /* ---------- Edit page ---------- */ + .edit-page { margin-top: 0; } + + .edit-heading { + font-size: 1.1em; + margin: 0 0 0.3em 0; + } + + .edit-heading .page_name { + color: var(--color-page-name); + } + + #content_text, + .edit-page textarea { + width: 100%; + min-height: 320px; + font-family: var(--font-stack-mono); + line-height: var(--line-height-normal); + } + + .edit-actions { + margin-top: 6px; + } + + .edit-actions #delete { + margin-left: auto; + } + /* ---------- Primer welcome banner ---------- */ .primer { position: relative; diff --git a/jottit/templates/edit_page.html b/jottit/templates/edit_page.html index e2b35be..02721a3 100644 --- a/jottit/templates/edit_page.html +++ b/jottit/templates/edit_page.html @@ -3,24 +3,22 @@ {% block title %}Edit: {{ page_name or "Home" }}{% if site and site.title %} — {{ site.title }}{% endif %}{% endblock %} {% block content %} -
-

Edit: {{ page_name or "Home" }}

+
+

Editing: {{ page_name or "Home" }}

-
+

- + +

-

+

- Cancel + Cancel {% if page_name %} - + {% endif %} -

+
-
+ {% endblock %} From 9bef076bdd2a490ccf4b904dcb7638e52ae1beb1 Mon Sep 17 00:00:00 2001 From: Simon Carstensen Date: Sat, 16 May 2026 16:31:15 +0200 Subject: [PATCH 5/6] Simplify history, changes, diff, notfound, deleted templates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the redundant
wrappers from inside .site-content (it's already an article) — h1 + body content sit directly. Swap .empty / .deleted classes for the shared .text-muted utility, and use 2007 ids (#diff, #changes, #revisions) so the legacy id-targeted CSS hooks line up. Co-Authored-By: Claude Opus 4.7 (1M context) --- jottit/templates/changes.html | 38 ++++++++++++++++------------------ jottit/templates/deleted.html | 19 +++++++++-------- jottit/templates/diff.html | 22 ++++++++------------ jottit/templates/history.html | 34 ++++++++++++++---------------- jottit/templates/notfound.html | 13 ++++++------ 5 files changed, 58 insertions(+), 68 deletions(-) diff --git a/jottit/templates/changes.html b/jottit/templates/changes.html index 2259d89..e650028 100644 --- a/jottit/templates/changes.html +++ b/jottit/templates/changes.html @@ -9,28 +9,26 @@ {% endblock %} {% block content %} -
-

Recent changes

+

Recent changes

- {% if changes %} -
    - {% for c in changes %} -
  1. - {{ c.page_name or "Home" }} — revision {{ c.revision }} - - {% if c.page_deleted %}(page since deleted){% endif %} - {% if c.changes %}{{ c.changes|safe }}{% endif %} -
  2. - {% endfor %} -
+ {% if changes %} +
    + {% for c in changes %} +
  1. + {{ c.page_name or "Home" }} — revision {{ c.revision }} + + {% if c.page_deleted %}(page since deleted){% endif %} + {% if c.changes %}{{ c.changes|safe }}{% endif %} +
  2. + {% endfor %} +
- {% if older_before %} -

Older →

- {% endif %} - {% else %} -

No changes yet.

+ {% if older_before %} +

Older →

{% endif %} + {% else %} +

No changes yet.

+ {% endif %} -

Back to site

-
+

Back to site

{% endblock %} diff --git a/jottit/templates/deleted.html b/jottit/templates/deleted.html index f6a3923..e5c9c11 100644 --- a/jottit/templates/deleted.html +++ b/jottit/templates/deleted.html @@ -1,14 +1,15 @@ {% extends "site_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 %} -
- +

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 %} -
+ + + {% endif %} {% endblock %} diff --git a/jottit/templates/diff.html b/jottit/templates/diff.html index 26fac8b..0411fb2 100644 --- a/jottit/templates/diff.html +++ b/jottit/templates/diff.html @@ -2,19 +2,15 @@ {% block title %}Diff: {{ page_name or "Home" }}{% endblock %} {% block content %} - +

Back to history

{% endblock %} diff --git a/jottit/templates/history.html b/jottit/templates/history.html index aa76d3e..939c2a1 100644 --- a/jottit/templates/history.html +++ b/jottit/templates/history.html @@ -11,26 +11,22 @@ {% endblock %} {% block content %} -
-
-

History: {{ page_name or "Home" }}

-

{{ total }} revision{{ "s" if total != 1 }}.

-
+

History: {{ page_name or "Home" }}

+

{{ total }} revision{{ "s" if total != 1 }}.

-
    - {% for r in revisions %} -
  1. - Revision {{ r.revision }} - - {% if r.changes %}{{ r.changes|safe }}{% endif %} -
  2. - {% endfor %} -
+
    + {% for r in revisions %} +
  1. + Revision {{ r.revision }} + + {% if r.changes %}{{ r.changes|safe }}{% endif %} +
  2. + {% endfor %} +
- {% if older_before %} -

Older →

- {% endif %} + {% if older_before %} +

Older →

+ {% endif %} -

Back to page

-
+

Back to page

{% endblock %} diff --git a/jottit/templates/notfound.html b/jottit/templates/notfound.html index 063fc20..b275758 100644 --- a/jottit/templates/notfound.html +++ b/jottit/templates/notfound.html @@ -1,11 +1,10 @@ {% extends "site_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 %} -
+

Not found

+

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

+ {% if is_signed_in or is_unclaimed %} +

Create this page

+ {% endif %} {% endblock %} From 543f50e8376fe1608e7ca350ca4c0a717400d7d2 Mon Sep 17 00:00:00 2001 From: Simon Carstensen Date: Sat, 16 May 2026 17:00:51 +0200 Subject: [PATCH 6/6] Guard hide_primer against a missing DB connection Pyright flagged conn.execute on Optional[Connection]; mirror the other admin/site views and abort(500) when the request-scoped engine isn't set. Co-Authored-By: Claude Opus 4.7 (1M context) --- jottit/views/site.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jottit/views/site.py b/jottit/views/site.py index fcc0b92..5e449b9 100644 --- a/jottit/views/site.py +++ b/jottit/views/site.py @@ -325,5 +325,7 @@ def hide_primer(site_slug: str) -> ResponseReturnValue: if g.site is None: abort(404) conn = get_request_conn() + if conn is None: + abort(500) conn.execute(sites.update().where(sites.c.id == g.site.id).values(show_primer=False)) return redirect(site_root(), code=303)