diff options
| author | grm <grm@eyesin.space> | 2026-09-13 00:38:24 +0300 |
|---|---|---|
| committer | grm <grm@eyesin.space> | 2026-09-13 00:38:24 +0300 |
| commit | d11e4c9544f544b3a024c7ac705a674b4096cf50 (patch) | |
| tree | a60be98a37b764dfcb655b2738c8a991a248103b /internal/web/templates/dashboard/layout.html | |
| parent | e974eb6f3feb06c8951f85c1d34161938905f807 (diff) | |
| download | blogspace-d11e4c9544f544b3a024c7ac705a674b4096cf50.tar.gz blogspace-d11e4c9544f544b3a024c7ac705a674b4096cf50.tar.bz2 blogspace-d11e4c9544f544b3a024c7ac705a674b4096cf50.zip | |
Add the Layout tab: five areas with modules, columns and a menu editor
A blog page is now header, left column, main content, right column and
footer, each holding an ordered list of modules (blog title, logo, menu,
archive by year/month, recent posts, custom HTML, footer text, RSS link,
site map). Side columns have percentage widths, can be hidden, and can
keep their space when empty; on phones they stack under the posts. The
menu becomes its own ordered list mixing pages and outside links.
Announcements are placed per column at its top or bottom.
Modules and menu items get tables; the migration derives them from each
blog's old theme (nav position, footer text, show-in-nav pages) so
existing blogs look the same. Custom HTML is deliberately stored and
served as-is (owner's decision; see AGENTS.md for why the blast radius
is the blogger's own origin).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
Diffstat (limited to 'internal/web/templates/dashboard/layout.html')
| -rw-r--r-- | internal/web/templates/dashboard/layout.html | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/internal/web/templates/dashboard/layout.html b/internal/web/templates/dashboard/layout.html new file mode 100644 index 0000000..7691b1e --- /dev/null +++ b/internal/web/templates/dashboard/layout.html @@ -0,0 +1,113 @@ +{{define "title"}}Layout · {{.Blog.Title}}{{end}} +{{define "content"}} +{{$t := .Data.theme}} +{{$u := print "/b/" .Blog.Subdomain "/layout"}} +<div class="pagehead"> + <h1>Layout</h1> + <p class="lead muted">Decide what goes where on your blog. Every change here is saved straight away; refresh <a href="{{.BlogURL}}" target="_blank">your blog ↗</a> to see it. Colours and fonts are on the <a href="/b/{{.Blog.Subdomain}}/design">Design</a> tab.</p> +</div> +<p class="jumps"><a href="#columns">Columns</a><a href="#header">Header</a><a href="#left">Left column</a><a href="#main">Main content</a><a href="#right">Right column</a><a href="#footer">Footer</a><a href="#menu">Menu</a></p> + +<div class="card" id="columns"> + <h2>Columns</h2> + <div class="lay"> + <div class="lay-row lay-header{{if not $t.HeaderOn}} off{{end}}">Header{{if not $t.HeaderOn}} (hidden){{end}}</div> + <div class="lay-cols"> + <div class="lay-col lay-side{{if not $t.LeftOn}} off{{end}}{{if not (index .Data.areas "left")}} empty{{end}}" style="width:{{$t.LeftWidth}}%">Left {{$t.LeftWidth}}%</div> + <div class="lay-col lay-main" style="width:{{$t.MainWidth}}%">Main content {{$t.MainWidth}}%</div> + <div class="lay-col lay-side{{if not $t.RightOn}} off{{end}}{{if not (index .Data.areas "right")}} empty{{end}}" style="width:{{$t.RightWidth}}%">Right {{$t.RightWidth}}%</div> + </div> + <div class="lay-row lay-footer{{if not $t.FooterOn}} off{{end}}">Footer{{if not $t.FooterOn}} (hidden){{end}}</div> + </div> + <form method="post" action="{{$u}}/widths"> + <input type="hidden" name="_csrf" value="{{.CSRF}}"> + <div class="row"> + <label>Left column width<br><input type="number" name="left_width" value="{{$t.LeftWidth}}" min="15" max="40" step="5"> %</label> + <label>Right column width<br><input type="number" name="right_width" value="{{$t.RightWidth}}" min="15" max="40" step="5"> %</label> + </div> + <p class="muted small">Between 15% and 40% each; the main content takes the rest. A column only appears when it has at least one module; as soon as one does, the blog spreads over the whole page. On phones the columns stack: main content first, then the left and right columns, then the footer.</p> + <label class="check"><input type="checkbox" name="keep_columns"{{if $t.KeepColumns}} checked{{end}}> Always keep the columns' space, even when a column is empty or hidden <span class="muted">(the main content then has the same width on every page)</span></label> + <p class="actionrow"><button type="submit">Save columns</button></p> + </form> +</div> + +{{template "area" (dict "area" "header" "root" . "hint" "The top of every page. Stack the title, a logo and the menu in the order you like. A banner image can be chosen on the Design tab.")}} +{{template "area" (dict "area" "left" "root" . "hint" "A narrow column beside your posts. Good for the menu, an archive or recent posts.")}} + +<div class="card" id="main"> + <div class="cardhead"><h2>Main content</h2></div> + <p class="muted small">Your posts and pages always show here. You can add your own HTML before and after them — an intro box, a newsletter form, a badge…</p> + <table class="modules"> + {{range $area := .Data.areaOrder}}{{$mods := index $.Data.areas $area}} + <tr><th>{{index $.Data.areaNames $area}}</th> + {{if $mods}}{{$m := index $mods 0}} + <td>{{index $.Data.names $m.Kind}}{{with modsummary $m}} <span class="muted">— {{.}}</span>{{end}}</td> + <td class="nowrap actions"><a href="{{$u}}/modules/{{$m.ID}}/edit">edit</a> <a class="danger" href="{{$u}}/modules/{{$m.ID}}/delete">remove</a></td> + {{else}} + <td class="muted">Nothing extra.</td> + <td class="nowrap actions"><form method="post" action="{{$u}}/areas/{{$area}}/add" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="kind" value="html"><button class="mini">+ Add custom HTML</button></form></td> + {{end}} + </tr>{{end}} + </table> +</div> + +{{template "area" (dict "area" "right" "root" . "hint" "A narrow column on the other side of your posts. Same choices as the left one.")}} +{{template "area" (dict "area" "footer" "root" . "hint" "The bottom of every page: a copyright line, the RSS link, a list of your pages.")}} + +<div class="card" id="menu"> + <h2>Menu</h2> + <p class="muted small">The links the Menu module shows, wherever you place it. Pages and outside links can be mixed in any order. Ticking "Show in menu" on a page does the same as adding it here.</p> + {{if .Data.menu}}<table class="modules"> + {{range .Data.menu}}<tr> + <td class="nowrap order"> + <form method="post" action="{{$u}}/menu/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="dir" value="up"><button class="mini" title="Move up">▲</button></form> + <form method="post" action="{{$u}}/menu/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="dir" value="down"><button class="mini" title="Move down">▼</button></form> + </td> + <td>{{.Text}} {{if .PageID}}<span class="tag">page</span>{{else}}<span class="tag">link</span> <span class="muted small">{{.URL}}</span>{{end}}</td> + <td class="nowrap actions"><form method="post" action="{{$u}}/menu/{{.ID}}/delete" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><button class="mini danger">Remove</button></form></td> + </tr>{{end}} + </table>{{else}}<p class="muted">The menu is empty.</p>{{end}} + <div class="cols addrow"> + <form method="post" action="{{$u}}/menu/add-page" class="add"> + <input type="hidden" name="_csrf" value="{{.CSRF}}"> + <h3>Add a page</h3> + {{if .Data.freePages}}<label>Page<br><select name="page">{{range .Data.freePages}}<option value="{{.ID}}">{{.Title}}</option>{{end}}</select></label> + <button type="submit" class="secondary">Add page</button> + {{else}}<p class="muted small">Every page is already in the menu.</p>{{end}} + </form> + <form method="post" action="{{$u}}/menu/add-link" class="add"> + <input type="hidden" name="_csrf" value="{{.CSRF}}"> + <h3>Add a link</h3> + <label>Text<br><input name="label" maxlength="60" placeholder="My photos"></label> + <label>Address<br><input name="url" maxlength="500" placeholder="https://example.org"></label> + <button type="submit" class="secondary">Add link</button> + </form> + </div> +</div> +{{end}} + +{{/* One area card: on/off switch, its modules in order, and an "add" form. */}} +{{define "area"}}{{$r := .root}}{{$area := .area}}{{$u := print "/b/" $r.Blog.Subdomain "/layout"}}{{$on := $r.Data.theme.AreaOn $area}}{{$mods := index $r.Data.areas $area}} +<div class="card{{if not $on}} area-off{{end}}" id="{{$area}}"> + <div class="cardhead"> + <h2>{{index $r.Data.areaNames $area}}{{if not $on}} <span class="tag off">hidden</span>{{end}}</h2> + <form method="post" action="{{$u}}/areas/{{$area}}/toggle" class="inline switch"><input type="hidden" name="_csrf" value="{{$r.CSRF}}"><button class="mini">{{if $on}}Hide this area{{else}}Show this area{{end}}</button></form> + </div> + <p class="muted small">{{.hint}}</p> + {{if $mods}}<table class="modules"> + {{range $mods}}<tr> + <td class="nowrap order"> + <form method="post" action="{{$u}}/modules/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$r.CSRF}}"><input type="hidden" name="dir" value="up"><button class="mini" title="Move up">▲</button></form> + <form method="post" action="{{$u}}/modules/{{.ID}}/move" class="inline"><input type="hidden" name="_csrf" value="{{$r.CSRF}}"><input type="hidden" name="dir" value="down"><button class="mini" title="Move down">▼</button></form> + </td> + <td>{{index $r.Data.names .Kind}}{{with modsummary .}} <span class="muted">— {{.}}</span>{{end}} + {{if and (eq .Kind "logo") (not $r.Data.theme.Logo)}}<span class="tag off">no logo chosen</span> <span class="muted small">pick one on the <a href="/b/{{$r.Blog.Subdomain}}/design#logo">Design</a> tab</span>{{end}}</td> + <td class="nowrap actions">{{if modsettings .Kind}}<a href="{{$u}}/modules/{{.ID}}/edit">edit</a> {{end}}<a class="danger" href="{{$u}}/modules/{{.ID}}/delete">remove</a></td> + </tr>{{end}} + </table>{{else}}<p class="muted">Nothing here yet{{if ne $area "header"}} — this area is not shown until you add a module{{end}}.</p>{{end}} + <form method="post" action="{{$u}}/areas/{{$area}}/add" class="addmod"> + <input type="hidden" name="_csrf" value="{{$r.CSRF}}"> + <label>Add a module<br><select name="kind">{{range index $r.Data.kinds $area}}<option value="{{.}}">{{index $r.Data.names .}}</option>{{end}}</select></label> + <button type="submit" class="secondary">Add</button> + </form> +</div>{{end}} |
