release: 0.4.0: Domain-admin groundwork, send-log auth, packaging fixes
The project's mark is now in use rather than only on file. The README opens Added: - The project's mark is now in use rather than only on file. The README opens Changed: - panel: the sign-in and setup pages are now a column the width of their own - panel: the three monitoring pages — Deliveries, Mail queue, System log — are Fixed: - panel: Deliveries now shows the subject as text rather than as its MIME - panel: table cells may now break inside a word, so no single long value can - panel: the Applications list on a domain page no longer comes apart. It was a Full detail: CHANGELOG.md (0.4.0).
This commit is contained in:
@@ -98,6 +98,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
// a failing form.
|
||||
s.render(w, http.StatusOK, "login", map[string]any{
|
||||
"Title": "SelfPost — Sign in",
|
||||
"Active": "login",
|
||||
"SetupHint": true,
|
||||
})
|
||||
return
|
||||
@@ -115,9 +116,13 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *Server) renderLogin(w http.ResponseWriter, status int, formErr string) {
|
||||
// Active names the page for the layout even though no navigation is drawn
|
||||
// here: it is what puts page-login on <main>, which the stylesheet uses to
|
||||
// give the signed-out pages a column the width of their own card.
|
||||
s.render(w, status, "login", map[string]any{
|
||||
"Title": "SelfPost — Sign in",
|
||||
"Error": formErr,
|
||||
"Title": "SelfPost — Sign in",
|
||||
"Active": "login",
|
||||
"Error": formErr,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -42,9 +42,10 @@ func (s *Server) handleSetup(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (s *Server) renderSetupForm(w http.ResponseWriter, status int, token, formErr string) {
|
||||
s.render(w, status, "setup", map[string]any{
|
||||
"Title": "SelfPost — Create administrator",
|
||||
"Token": token,
|
||||
"Error": formErr,
|
||||
"Title": "SelfPost — Create administrator",
|
||||
"Active": "setup",
|
||||
"Token": token,
|
||||
"Error": formErr,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="SelfPost">
|
||||
<!-- SelfPost · https://codeberg.org/mix/selfpost · AGPL-3.0
|
||||
Wordmark set in IBM Plex Sans (OFL) and converted to outlines: the
|
||||
file needs no font installed to render as drawn. -->
|
||||
<path d="M7.80 5.00 L13.00 5.00 A2.8 2.8 0 0 0 18.60 5.00 L23.80 5.00 A2.8 2.8 0 0 0 29.40 5.00 L34.60 5.00 A2.8 2.8 0 0 0 40.20 5.00 L45.40 5.00 A2.8 2.8 0 0 0 51.00 5.00 L56.20 5.00 A2.8 2.8 0 0 0 59.00 7.80 L59.00 13.00 A2.8 2.8 0 0 0 59.00 18.60 L59.00 23.80 A2.8 2.8 0 0 0 59.00 29.40 L59.00 34.60 A2.8 2.8 0 0 0 59.00 40.20 L59.00 45.40 A2.8 2.8 0 0 0 59.00 51.00 L59.00 56.20 A2.8 2.8 0 0 0 56.20 59.00 L51.00 59.00 A2.8 2.8 0 0 0 45.40 59.00 L40.20 59.00 A2.8 2.8 0 0 0 34.60 59.00 L29.40 59.00 A2.8 2.8 0 0 0 23.80 59.00 L18.60 59.00 A2.8 2.8 0 0 0 13.00 59.00 L7.80 59.00 A2.8 2.8 0 0 0 5.00 56.20 L5.00 51.00 A2.8 2.8 0 0 0 5.00 45.40 L5.00 40.20 A2.8 2.8 0 0 0 5.00 34.60 L5.00 29.40 A2.8 2.8 0 0 0 5.00 23.80 L5.00 18.60 A2.8 2.8 0 0 0 5.00 13.00 L5.00 7.80 A2.8 2.8 0 0 0 7.80 5.00 Z" fill="#F3EDE1" stroke="#12161C" stroke-width="0.8" stroke-linejoin="round" />
|
||||
<rect x="11" y="11" width="42" height="42" fill="#7A3B2E" />
|
||||
<path d="M24.71 43.31Q22.47 43.31 21.01 42.47Q19.56 41.62 18.54 40.3L19.27 39.72Q20.23 40.97 21.49 41.71Q22.76 42.45 24.73 42.45Q26.99 42.45 28.23 41.39Q29.46 40.32 29.46 38.32Q29.46 36.94 28.94 36.15Q28.42 35.36 27.51 34.94Q26.6 34.52 25.49 34.29L23.74 33.95Q21.95 33.61 20.92 32.93Q19.9 32.24 19.44 31.29Q18.99 30.34 18.99 29.22Q18.99 27.63 19.69 26.59Q20.39 25.55 21.66 25.05Q22.94 24.54 24.63 24.54Q26.66 24.54 28.01 25.24Q29.36 25.94 30.14 27.11L29.41 27.66Q28.63 26.54 27.46 25.97Q26.29 25.4 24.58 25.4Q22.42 25.4 21.18 26.33Q19.95 27.27 19.95 29.19Q19.95 30.47 20.48 31.23Q21.01 32 21.92 32.42Q22.83 32.83 23.95 33.07L25.69 33.41Q27.54 33.74 28.57 34.46Q29.59 35.17 30 36.15Q30.4 37.12 30.4 38.24Q30.4 39.91 29.71 41.02Q29.02 42.14 27.73 42.73Q26.45 43.31 24.71 43.31Z M36.45 43H33.01V24.85H41.18Q42.87 24.85 44.06 25.55Q45.26 26.26 45.91 27.52Q46.56 28.78 46.56 30.47Q46.56 32.16 45.91 33.42Q45.26 34.68 44.06 35.38Q42.87 36.08 41.18 36.08H36.45ZM36.45 27.84V33.12H40.87Q41.54 33.12 42.01 32.87Q42.48 32.63 42.74 32.17Q43 31.72 43 31.07V29.87Q43 29.19 42.74 28.75Q42.48 28.31 42.01 28.08Q41.54 27.84 40.87 27.84Z" fill="#F6EDE2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 100" width="220" height="100" role="img" aria-label="SelfPost">
|
||||
<!-- SelfPost · https://codeberg.org/mix/selfpost · AGPL-3.0
|
||||
Wordmark set in IBM Plex Sans (OFL) and converted to outlines: the
|
||||
file needs no font installed to render as drawn. -->
|
||||
<path d="M10.20 8.00 L12.83 8.00 A2.2 2.2 0 0 0 17.23 8.00 L19.87 8.00 A2.2 2.2 0 0 0 24.27 8.00 L26.90 8.00 A2.2 2.2 0 0 0 31.30 8.00 L33.94 8.00 A2.2 2.2 0 0 0 38.34 8.00 L40.97 8.00 A2.2 2.2 0 0 0 45.37 8.00 L48.01 8.00 A2.2 2.2 0 0 0 52.41 8.00 L55.04 8.00 A2.2 2.2 0 0 0 59.44 8.00 L62.08 8.00 A2.2 2.2 0 0 0 66.48 8.00 L69.11 8.00 A2.2 2.2 0 0 0 73.51 8.00 L76.14 8.00 A2.2 2.2 0 0 0 80.54 8.00 L83.18 8.00 A2.2 2.2 0 0 0 87.58 8.00 L90.21 8.00 A2.2 2.2 0 0 0 94.61 8.00 L97.25 8.00 A2.2 2.2 0 0 0 101.65 8.00 L104.28 8.00 A2.2 2.2 0 0 0 108.68 8.00 L111.32 8.00 A2.2 2.2 0 0 0 115.72 8.00 L118.35 8.00 A2.2 2.2 0 0 0 122.75 8.00 L125.39 8.00 A2.2 2.2 0 0 0 129.79 8.00 L132.42 8.00 A2.2 2.2 0 0 0 136.82 8.00 L139.46 8.00 A2.2 2.2 0 0 0 143.86 8.00 L146.49 8.00 A2.2 2.2 0 0 0 150.89 8.00 L153.52 8.00 A2.2 2.2 0 0 0 157.92 8.00 L160.56 8.00 A2.2 2.2 0 0 0 164.96 8.00 L167.59 8.00 A2.2 2.2 0 0 0 171.99 8.00 L174.63 8.00 A2.2 2.2 0 0 0 179.03 8.00 L181.66 8.00 A2.2 2.2 0 0 0 186.06 8.00 L188.70 8.00 A2.2 2.2 0 0 0 193.10 8.00 L195.73 8.00 A2.2 2.2 0 0 0 200.13 8.00 L202.77 8.00 A2.2 2.2 0 0 0 207.17 8.00 L209.80 8.00 A2.2 2.2 0 0 0 212.00 10.20 L212.00 12.80 A2.2 2.2 0 0 0 212.00 17.20 L212.00 19.80 A2.2 2.2 0 0 0 212.00 24.20 L212.00 26.80 A2.2 2.2 0 0 0 212.00 31.20 L212.00 33.80 A2.2 2.2 0 0 0 212.00 38.20 L212.00 40.80 A2.2 2.2 0 0 0 212.00 45.20 L212.00 47.80 A2.2 2.2 0 0 0 212.00 52.20 L212.00 54.80 A2.2 2.2 0 0 0 212.00 59.20 L212.00 61.80 A2.2 2.2 0 0 0 212.00 66.20 L212.00 68.80 A2.2 2.2 0 0 0 212.00 73.20 L212.00 75.80 A2.2 2.2 0 0 0 212.00 80.20 L212.00 82.80 A2.2 2.2 0 0 0 212.00 87.20 L212.00 89.80 A2.2 2.2 0 0 0 209.80 92.00 L207.17 92.00 A2.2 2.2 0 0 0 202.77 92.00 L200.13 92.00 A2.2 2.2 0 0 0 195.73 92.00 L193.10 92.00 A2.2 2.2 0 0 0 188.70 92.00 L186.06 92.00 A2.2 2.2 0 0 0 181.66 92.00 L179.03 92.00 A2.2 2.2 0 0 0 174.63 92.00 L171.99 92.00 A2.2 2.2 0 0 0 167.59 92.00 L164.96 92.00 A2.2 2.2 0 0 0 160.56 92.00 L157.92 92.00 A2.2 2.2 0 0 0 153.52 92.00 L150.89 92.00 A2.2 2.2 0 0 0 146.49 92.00 L143.86 92.00 A2.2 2.2 0 0 0 139.46 92.00 L136.82 92.00 A2.2 2.2 0 0 0 132.42 92.00 L129.79 92.00 A2.2 2.2 0 0 0 125.39 92.00 L122.75 92.00 A2.2 2.2 0 0 0 118.35 92.00 L115.72 92.00 A2.2 2.2 0 0 0 111.32 92.00 L108.68 92.00 A2.2 2.2 0 0 0 104.28 92.00 L101.65 92.00 A2.2 2.2 0 0 0 97.25 92.00 L94.61 92.00 A2.2 2.2 0 0 0 90.21 92.00 L87.58 92.00 A2.2 2.2 0 0 0 83.18 92.00 L80.54 92.00 A2.2 2.2 0 0 0 76.14 92.00 L73.51 92.00 A2.2 2.2 0 0 0 69.11 92.00 L66.48 92.00 A2.2 2.2 0 0 0 62.08 92.00 L59.44 92.00 A2.2 2.2 0 0 0 55.04 92.00 L52.41 92.00 A2.2 2.2 0 0 0 48.01 92.00 L45.37 92.00 A2.2 2.2 0 0 0 40.97 92.00 L38.34 92.00 A2.2 2.2 0 0 0 33.94 92.00 L31.30 92.00 A2.2 2.2 0 0 0 26.90 92.00 L24.27 92.00 A2.2 2.2 0 0 0 19.87 92.00 L17.23 92.00 A2.2 2.2 0 0 0 12.83 92.00 L10.20 92.00 A2.2 2.2 0 0 0 8.00 89.80 L8.00 87.20 A2.2 2.2 0 0 0 8.00 82.80 L8.00 80.20 A2.2 2.2 0 0 0 8.00 75.80 L8.00 73.20 A2.2 2.2 0 0 0 8.00 68.80 L8.00 66.20 A2.2 2.2 0 0 0 8.00 61.80 L8.00 59.20 A2.2 2.2 0 0 0 8.00 54.80 L8.00 52.20 A2.2 2.2 0 0 0 8.00 47.80 L8.00 45.20 A2.2 2.2 0 0 0 8.00 40.80 L8.00 38.20 A2.2 2.2 0 0 0 8.00 33.80 L8.00 31.20 A2.2 2.2 0 0 0 8.00 26.80 L8.00 24.20 A2.2 2.2 0 0 0 8.00 19.80 L8.00 17.20 A2.2 2.2 0 0 0 8.00 12.80 L8.00 10.20 A2.2 2.2 0 0 0 10.20 8.00 Z" fill="#F3EDE1" stroke="#12161C" stroke-width="0.7" stroke-linejoin="round" />
|
||||
<rect x="15" y="15" width="190" height="70" fill="#7A3B2E" />
|
||||
<path d="M61.06 66.41Q58.14 66.41 56.23 65.3Q54.33 64.2 53 62.46L53.96 61.72Q55.21 63.35 56.86 64.32Q58.51 65.29 61.1 65.29Q64.05 65.29 65.67 63.89Q67.28 62.5 67.28 59.88Q67.28 58.08 66.6 57.04Q65.92 56 64.73 55.46Q63.54 54.92 62.08 54.61L59.8 54.17Q57.46 53.73 56.12 52.83Q54.77 51.92 54.18 50.68Q53.58 49.44 53.58 47.98Q53.58 45.91 54.5 44.55Q55.42 43.19 57.08 42.52Q58.75 41.86 60.96 41.86Q63.61 41.86 65.38 42.78Q67.15 43.7 68.17 45.23L67.22 45.94Q66.2 44.48 64.67 43.73Q63.14 42.98 60.89 42.98Q58.07 42.98 56.46 44.21Q54.84 45.43 54.84 47.95Q54.84 49.61 55.54 50.61Q56.23 51.62 57.42 52.16Q58.61 52.71 60.08 53.01L62.35 53.45Q64.77 53.9 66.11 54.83Q67.45 55.77 67.98 57.04Q68.51 58.32 68.51 59.78Q68.51 61.95 67.61 63.42Q66.71 64.88 65.02 65.64Q63.34 66.41 61.06 66.41Z M78.56 66.41Q76.28 66.41 74.64 65.35Q72.99 64.3 72.09 62.29Q71.18 60.29 71.18 57.36Q71.18 54.44 72.07 52.42Q72.95 50.39 74.57 49.36Q76.18 48.32 78.46 48.32Q80.57 48.32 82.17 49.37Q83.76 50.43 84.67 52.35Q85.57 54.27 85.57 56.89V57.43H72.44V58.35Q72.44 61.51 74.06 63.43Q75.67 65.35 78.6 65.35Q80.64 65.35 82.08 64.35Q83.53 63.35 84.27 61.41L85.19 61.92Q84.44 63.96 82.74 65.18Q81.04 66.41 78.56 66.41ZM78.46 49.34Q76.56 49.34 75.21 50.21Q73.87 51.07 73.16 52.66Q72.44 54.24 72.44 56.34V56.48H84.27V56.28Q84.27 54.17 83.54 52.62Q82.81 51.07 81.5 50.21Q80.19 49.34 78.46 49.34Z M93.72 66H91.17Q90.42 66 90.06 65.63Q89.7 65.25 89.7 64.57V40.84H90.89V64.95H93.72Z M97.92 66V49.78H94.83V48.73H97.92V42.57Q97.92 41.76 98.33 41.3Q98.74 40.84 99.59 40.84H102.99V41.89H99.11V48.73H102.99V49.78H99.11V66Z M109.88 66H105.39V42.27H116.07Q118.28 42.27 119.84 43.19Q121.41 44.1 122.26 45.75Q123.11 47.4 123.11 49.61Q123.11 51.82 122.26 53.47Q121.41 55.12 119.84 56.04Q118.28 56.96 116.07 56.96H109.88ZM109.88 46.18V53.08H115.66Q116.55 53.08 117.16 52.76Q117.77 52.43 118.11 51.84Q118.45 51.24 118.45 50.39V48.83Q118.45 47.95 118.11 47.37Q117.77 46.79 117.16 46.48Q116.55 46.18 115.66 46.18Z M132.75 66.41Q130.24 66.41 128.38 65.27Q126.53 64.13 125.53 62.04Q124.53 59.95 124.53 57.09Q124.53 54.24 125.53 52.18Q126.53 50.12 128.38 48.98Q130.24 47.84 132.75 47.84Q135.3 47.84 137.16 48.98Q139.01 50.12 140.01 52.18Q141.02 54.24 141.02 57.09Q141.02 59.95 140.01 62.04Q139.01 64.13 137.16 65.27Q135.3 66.41 132.75 66.41ZM132.75 62.91Q134.49 62.91 135.47 61.83Q136.46 60.76 136.46 58.76V55.46Q136.46 53.45 135.47 52.4Q134.49 51.35 132.75 51.35Q131.09 51.35 130.08 52.4Q129.08 53.45 129.08 55.46V58.76Q129.08 60.76 130.08 61.83Q131.09 62.91 132.75 62.91Z M149.47 66.41Q146.92 66.41 145.19 65.54Q143.45 64.67 142.13 63.14L144.78 60.56Q145.77 61.68 146.94 62.33Q148.11 62.97 149.64 62.97Q151.21 62.97 151.9 62.43Q152.6 61.89 152.6 60.93Q152.6 60.15 152.11 59.69Q151.61 59.23 150.42 59.06L148.66 58.83Q146.75 58.59 145.43 57.94Q144.1 57.3 143.42 56.17Q142.74 55.05 142.74 53.45Q142.74 50.87 144.59 49.36Q146.45 47.84 149.47 47.84Q151.21 47.84 152.45 48.17Q153.69 48.49 154.64 49.12Q155.59 49.75 156.41 50.63L153.86 53.18Q153.08 52.37 151.99 51.82Q150.9 51.28 149.61 51.28Q148.25 51.28 147.58 51.77Q146.92 52.26 146.92 53.11Q146.92 54 147.45 54.46Q147.98 54.92 149.23 55.12L151.04 55.36Q153.96 55.77 155.37 57.08Q156.78 58.38 156.78 60.59Q156.78 62.29 155.88 63.62Q154.98 64.95 153.33 65.68Q151.68 66.41 149.47 66.41Z M168.09 66H164.97Q162.72 66 161.55 64.83Q160.38 63.65 160.38 61.48V51.69H157.76V48.25H159.12Q160.1 48.25 160.46 47.79Q160.82 47.33 160.82 46.42V43.39H164.73V48.25H168.37V51.69H164.73V62.53H168.09Z" fill="#F6EDE2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.1 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
@@ -17,6 +17,13 @@ body {
|
||||
}
|
||||
main { max-width: 42rem; margin: 0 auto; }
|
||||
h1 { font-size: 1.4rem; margin: 0 0 1rem; }
|
||||
/* The full mark, on the two pages that have no navigation bar to carry the
|
||||
compact one. It takes the column's width so its edges line up with the card
|
||||
below it, capped at that column's own 24rem; height stays automatic because
|
||||
the stamp's proportions are part of the mark and it is never scaled unevenly.
|
||||
The subtitle stops resolving below 280px, which a 320px viewport still clears
|
||||
once the body's padding is taken off. */
|
||||
.mark { display: block; width: 100%; max-width: 24rem; height: auto; margin-bottom: 1.4rem; }
|
||||
.card {
|
||||
background: #fff; border: 1px solid #e2e5e9; border-radius: 10px;
|
||||
padding: 1.5rem; margin: 0 auto;
|
||||
@@ -50,14 +57,47 @@ button:hover, a.btn:hover { background: #1d4ed8; }
|
||||
form's block layout; its button is styled like any other. */
|
||||
form.inline { display: inline; margin: 0; }
|
||||
main { max-width: 48rem; }
|
||||
/* 48rem is a reading measure: right for the forms and prose that make up most
|
||||
of the panel, and the width the navigation bar's two rows were sized for. The
|
||||
three monitoring pages hold data instead — seven columns of send-log, and raw
|
||||
mail.log lines that are long by nature — and at that width the send-log's
|
||||
Subject and Status were fighting over the last inch while the log pages wrapped
|
||||
every second line. They get a wider measure; every other page keeps the narrow
|
||||
one. The class comes from the layout, which stamps the page name onto <main>. */
|
||||
main.page-deliveries, main.page-mail_queue, main.page-system_log { max-width: 64rem; }
|
||||
/* The signed-out pages are a single card and nothing else, and .card.narrow
|
||||
centres itself inside whatever holds it — so at the panel's usual width the
|
||||
card floated in the middle while the mark and the heading stayed at the far
|
||||
left, three alignments on a page with four elements. Narrowing the column to
|
||||
the card's own width makes the three line up and puts the block as a whole in
|
||||
the middle of the page. */
|
||||
main.page-login, main.page-setup { max-width: 24rem; }
|
||||
.card + .card { margin-top: 1.2rem; }
|
||||
.flash { background: #ecfdf3; border: 1px solid #abefc6; color: #067647; padding: 0.7rem 1rem; border-radius: 8px; margin-bottom: 1.2rem; }
|
||||
@media (prefers-color-scheme: dark) { .flash { background: #0d2818 !important; border-color: #1a5336 !important; color: #75d99b !important; } }
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th, td { text-align: left; padding: 0.5rem 0.4rem; border-bottom: 1px solid #e2e5e9; }
|
||||
/* A table column is at least as wide as the longest unbreakable run inside it,
|
||||
and the panel's tables are full of runs with nothing to break on: email
|
||||
addresses, domains, queue ids. One 40-character recipient was enough to widen
|
||||
the send-log past its card and hang Status over the edge. Cells may break mid
|
||||
word, so a column can always be squeezed to the width available. */
|
||||
th, td { text-align: left; padding: 0.5rem 0.4rem; border-bottom: 1px solid #e2e5e9; overflow-wrap: anywhere; }
|
||||
/* The exception: a timestamp broken across two lines is unreadable, and it is
|
||||
short enough to never be the reason a row does not fit. */
|
||||
td.time { white-space: nowrap; }
|
||||
@media (prefers-color-scheme: dark) { th, td { border-color: #2b3138 !important; } }
|
||||
th { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.03em; color: #6b7280; }
|
||||
td.actions { text-align: right; }
|
||||
/* Subject is the one cell whose text we do not control. Breaking mid word (the
|
||||
rule above) keeps it inside the card, but a long subject would do it by
|
||||
growing the row several lines tall, which buries the rows around it. So the
|
||||
subject is clipped to one line instead, with the whole of it in the tooltip.
|
||||
The clamp sits on an inner block box rather than the cell because max-width
|
||||
on a <td> is only advisory in the automatic table layout. */
|
||||
td.subject span {
|
||||
display: block; max-width: 18rem;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.code { display: block; white-space: pre-wrap; word-break: break-all; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 0.85rem; background: #f0f2f4; border: 1px solid #e2e5e9; border-radius: 6px; padding: 0.7rem 0.8rem; margin: 0.3rem 0 0; }
|
||||
@media (prefers-color-scheme: dark) { .code { background: #14171a !important; border-color: #2b3138 !important; } }
|
||||
@@ -77,12 +117,28 @@ textarea { resize: vertical; }
|
||||
}
|
||||
button.danger, a.danger { background: #b42318; }
|
||||
button.danger:hover, a.danger:hover { background: #912018; }
|
||||
td.actions form.inline, td.actions details { margin: 0.4rem 0 0 0.5rem; }
|
||||
/* The disclosure toggle is an action too, so it is drawn as a button (see the
|
||||
compact rule below); the marker is dropped because the pressed background
|
||||
already shows the open state. */
|
||||
td.actions summary { display: inline-block; list-style: none; cursor: pointer; }
|
||||
td.actions summary::-webkit-details-marker { display: none; }
|
||||
.actions summary { display: inline-block; list-style: none; cursor: pointer; }
|
||||
.actions summary::-webkit-details-marker { display: none; }
|
||||
/* Applications are a list of blocks, not table rows. As a table it fell apart:
|
||||
four columns of which the last held six controls — two of them <details>
|
||||
panels with textareas — never fit the panel's 48rem. The controls wrapped
|
||||
into a staircase, .code on the login cell grew into a slab as tall as the
|
||||
row, and the two text cells sat on the baseline halfway down it. One block
|
||||
per application gives the identity a line of its own and the controls a row
|
||||
of their own, at the width they actually need. */
|
||||
.apps { list-style: none; margin: 1.2rem 0 0; padding: 0; }
|
||||
.app { padding: 0.9rem 0; border-top: 1px solid #e2e5e9; }
|
||||
.app:last-child { padding-bottom: 0; }
|
||||
@media (prefers-color-scheme: dark) { .app { border-color: #2b3138 !important; } }
|
||||
.app-login { margin: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 600; }
|
||||
.app-addr { margin: 0.15rem 0 0; word-break: break-all; }
|
||||
.app .actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.7rem; }
|
||||
/* An open panel claims a row to itself: its textareas and number inputs want
|
||||
the block's full width, not the width of the summary that opened them. */
|
||||
.app .actions > details[open] { flex: 1 0 100%; }
|
||||
details form { margin-top: 0.6rem; }
|
||||
.credential { border-color: #f5c518; background: #fffbeb; }
|
||||
@media (prefers-color-scheme: dark) { .credential { background: #2a2408 !important; border-color: #6b5a10 !important; } }
|
||||
@@ -107,9 +163,21 @@ details form { margin-top: 0.6rem; }
|
||||
}
|
||||
@media (prefers-color-scheme: dark) { .nav { border-color: #2b3138 !important; } }
|
||||
.nav .links, .nav .session { display: flex; flex-wrap: wrap; gap: 0.2rem 0.9rem; align-items: center; }
|
||||
/* Pushed to the far edge so the two rows read as two blocks rather than as one
|
||||
/* The top row: mark at one edge, session block at the other. Splitting them to
|
||||
the edges is what makes the two rows read as two blocks rather than as one
|
||||
ragged list that happened to wrap. */
|
||||
.nav .top { display: flex; align-items: center; justify-content: space-between; gap: 0.9rem; }
|
||||
.nav .session { justify-content: flex-end; }
|
||||
/* The mark is a link, but not one of the bar's entries: it takes none of the
|
||||
padding and rounding the entry rule below applies, so its own edge lines up
|
||||
with the page rather than sitting half a step inside it. The stamp carries
|
||||
about 4px of field inside the file at this size, which is what puts it level
|
||||
with the icons of the row underneath. */
|
||||
.nav .brand { padding: 0; }
|
||||
/* width/height are on the element too, so the row reserves the space before the
|
||||
SVG has loaded; these keep the ratio if the box is ever squeezed. The stamp's
|
||||
proportions are part of the mark — it is never scaled unevenly. */
|
||||
.nav .brand img { display: block; width: 110px; height: auto; }
|
||||
/* Each entry pairs an icon with its label, so the entry itself is a flex row
|
||||
rather than a run of text — that is also why the bar centres its items
|
||||
instead of aligning them on the text baseline. Account is included: it is a
|
||||
@@ -152,30 +220,32 @@ details form { margin-top: 0.6rem; }
|
||||
.code-row .code { flex: 1; min-width: 0; }
|
||||
/* Compact outlined button: same affordance as the filled one but quiet enough
|
||||
that several can sit together without shouting — the Copy buttons beside a
|
||||
value, the per-application actions in a table row. Sign out overrides this
|
||||
with .danger below since signing out is a deliberate, singular action. */
|
||||
button.copy, td.actions button, td.actions summary, td.actions a.danger, .nav button {
|
||||
value, the controls of a table row or of an application block. Sign out
|
||||
overrides this with .danger below since signing out is a deliberate,
|
||||
singular action. .actions is the shared hook: a cell that holds controls, or
|
||||
the control row of an application. */
|
||||
button.copy, .actions button, .actions summary, .actions a.danger, .nav button {
|
||||
margin: 0; padding: 0.45rem 0.7rem; font-size: 0.8rem; font-weight: 600;
|
||||
border-radius: 6px; white-space: nowrap;
|
||||
background: #eef1f5; color: #2563eb; border: 1px solid #cfd4da;
|
||||
}
|
||||
button.copy:hover, td.actions button:hover, td.actions summary:hover,
|
||||
td.actions a.danger:hover, .nav button:hover { background: #e2e7ee; }
|
||||
button.copy:hover, .actions button:hover, .actions summary:hover,
|
||||
.actions a.danger:hover, .nav button:hover { background: #e2e7ee; }
|
||||
button.copy { flex: none; margin-top: 0.3rem; }
|
||||
td.actions details[open] > summary { background: #dde3ec; }
|
||||
td.actions button.danger, td.actions a.danger, .nav button.danger {
|
||||
.actions details[open] > summary { background: #dde3ec; }
|
||||
.actions button.danger, .actions a.danger, .nav button.danger {
|
||||
color: #b42318; background: #fef3f2; border-color: #fecdca;
|
||||
}
|
||||
td.actions button.danger:hover, td.actions a.danger:hover, .nav button.danger:hover { background: #fee4e2; }
|
||||
.actions button.danger:hover, .actions a.danger:hover, .nav button.danger:hover { background: #fee4e2; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
button.copy, td.actions button, td.actions summary, td.actions a.danger, .nav button {
|
||||
button.copy, .actions button, .actions summary, .actions a.danger, .nav button {
|
||||
background: #22262b !important; border-color: #2b3138 !important;
|
||||
}
|
||||
button.copy:hover, td.actions button:hover, td.actions summary:hover,
|
||||
td.actions a.danger:hover, .nav button:hover { background: #2b3138 !important; }
|
||||
td.actions details[open] > summary { background: #313841 !important; }
|
||||
td.actions button.danger, td.actions a.danger, .nav button.danger {
|
||||
button.copy:hover, .actions button:hover, .actions summary:hover,
|
||||
.actions a.danger:hover, .nav button:hover { background: #2b3138 !important; }
|
||||
.actions details[open] > summary { background: #313841 !important; }
|
||||
.actions button.danger, .actions a.danger, .nav button.danger {
|
||||
color: #f5a29b !important; background: #2d1211 !important; border-color: #6b201a !important;
|
||||
}
|
||||
td.actions button.danger:hover, td.actions a.danger:hover, .nav button.danger:hover { background: #3d1a18 !important; }
|
||||
.actions button.danger:hover, .actions a.danger:hover, .nav button.danger:hover { background: #3d1a18 !important; }
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
<tbody>
|
||||
{{range .Rows}}
|
||||
<tr>
|
||||
<td class="muted">{{.CreatedAt.Format "2006-01-02 15:04:05"}}</td>
|
||||
<td class="time muted">{{.CreatedAt.Format "2006-01-02 15:04:05"}}</td>
|
||||
<td>{{.Domain}}</td>
|
||||
<td>{{.AppLogin}}</td>
|
||||
<td>{{.From}}</td>
|
||||
<td>{{.To}}</td>
|
||||
<td>{{.Subject}}</td>
|
||||
<td class="subject"><span title="{{.Subject}}">{{.Subject}}</span></td>
|
||||
<td>{{.Status}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
||||
@@ -180,21 +180,15 @@
|
||||
domain (<em>wildcard</em>) or only from a fixed list of addresses.</p>
|
||||
|
||||
{{if .Apps}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Login</th><th>Mode</th><th>Addresses</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Apps}}
|
||||
<tr>
|
||||
<td class="code">{{.Login}}</td>
|
||||
<td>{{if eq .AddressMode $.Wildcard}}Any address (@{{$.Domain.Name}}){{else}}List{{end}}</td>
|
||||
<td class="muted">
|
||||
{{if eq .AddressMode $.Wildcard}}*@{{$.Domain.Name}}{{else}}
|
||||
{{range $i, $a := .Addresses}}{{if $i}}, {{end}}{{$a}}{{end}}
|
||||
{{end}}
|
||||
</td>
|
||||
<td class="actions">
|
||||
<ul class="apps">
|
||||
{{range .Apps}}
|
||||
<li class="app">
|
||||
<p class="app-login">{{.Login}}</p>
|
||||
<p class="app-addr muted">
|
||||
{{if eq .AddressMode $.Wildcard}}Any address of the domain — *@{{$.Domain.Name}}
|
||||
{{else}}Fixed list — {{range $i, $a := .Addresses}}{{if $i}}, {{end}}{{$a}}{{end}}{{end}}
|
||||
</p>
|
||||
<div class="actions">
|
||||
<details>
|
||||
<summary>Edit mode</summary>
|
||||
<form method="post" action="/applications/{{.ID}}/mode">
|
||||
@@ -238,11 +232,10 @@
|
||||
data-confirm="Delete application {{.Login}}? Its credentials stop working immediately.">
|
||||
<button type="submit" class="danger">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<p class="muted">No applications yet. Add one above to get started.</p>
|
||||
{{end}}
|
||||
|
||||
@@ -4,7 +4,13 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{.Title}}</title>
|
||||
<link rel="icon" href="/static/favicon.png" type="image/png">
|
||||
{{/* The tab icon is the stamp's small-size variant — the initials rather than
|
||||
the two-line wordmark, which stops resolving below 32px (the thresholds are
|
||||
recorded in docs/assets/selfpost-proof.html). The PNG is declared first and
|
||||
the SVG second: a browser takes the last icon whose type it understands, so
|
||||
vector wins wherever it is supported and the raster catches the rest. */}}
|
||||
<link rel="icon" href="/static/favicon.png" sizes="32x32">
|
||||
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="/static/panel.css">
|
||||
{{/* Unless told otherwise, htmx injects a stylesheet element of its own into
|
||||
the head for the request-indicator classes. The panel uses no
|
||||
@@ -16,7 +22,12 @@
|
||||
<script src="/static/panel.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
{{/* The page name travels onto <main> as a class so the stylesheet can size a
|
||||
page to what it holds — the monitoring pages are wider than the panel's
|
||||
form pages (see main.page-* in panel.css). It is .Active, already carried
|
||||
for the navigation, rather than a second field every handler would have to
|
||||
remember to set. */}}
|
||||
<main class="page-{{.Active}}">
|
||||
{{if .User}}{{template "nav" .}}{{end}}
|
||||
{{template "content" .}}
|
||||
{{/* The running version, on every authenticated page: it is what a backup
|
||||
@@ -35,15 +46,20 @@
|
||||
.User and get no nav. */}}
|
||||
{{define "nav"}}
|
||||
<nav class="nav">
|
||||
{{/* The session block is first here because it is drawn first: it is the top
|
||||
row of the bar (see .nav in panel.css), and putting it first in the
|
||||
document keeps the tab order matching the visual one. */}}
|
||||
<div class="session">
|
||||
<span class="muted">{{.User}}</span>
|
||||
{{if eq .Active "account"}}<span aria-current="page">{{template "icon-account"}}Account</span>{{else}}<a href="/account">{{template "icon-account"}}Account</a>{{end}}
|
||||
<form class="inline" method="post" action="/logout">
|
||||
<button type="submit" class="danger">{{template "icon-sign-out"}}Sign out</button>
|
||||
</form>
|
||||
{{/* The top row is first here because it is drawn first (see .nav in
|
||||
panel.css), which keeps the tab order matching the visual one. It holds
|
||||
the mark on the left and the session block on the right; the page entries
|
||||
follow underneath. The mark goes to /status, the page the panel opens on —
|
||||
named outright rather than as "/", which is only a redirect to it. */}}
|
||||
<div class="top">
|
||||
<a class="brand" href="/status"><img src="/static/logo-compact.svg" width="110" height="50" alt="SelfPost"></a>
|
||||
<div class="session">
|
||||
<span class="muted">{{.User}}</span>
|
||||
{{if eq .Active "account"}}<span aria-current="page">{{template "icon-account"}}Account</span>{{else}}<a href="/account">{{template "icon-account"}}Account</a>{{end}}
|
||||
<form class="inline" method="post" action="/logout">
|
||||
<button type="submit" class="danger">{{template "icon-sign-out"}}Sign out</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="links">
|
||||
{{if eq .Active "status"}}<span aria-current="page">{{template "icon-status"}}Status</span>{{else}}<a href="/status">{{template "icon-status"}}Status</a>{{end}}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
{{define "content"}}
|
||||
{{/* The full mark, on the two pages that carry no navigation: with no bar above
|
||||
it, this is the only thing telling an administrator which service just asked
|
||||
them for a password. */}}
|
||||
<img class="mark" src="/static/logo.svg" width="330" height="150" alt="SelfPost">
|
||||
<h1>Sign in</h1>
|
||||
<div class="card narrow">
|
||||
{{if .SetupHint}}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{{define "content"}}
|
||||
<img class="mark" src="/static/logo.svg" width="330" height="150" alt="SelfPost">
|
||||
<h1>Create administrator</h1>
|
||||
<div class="card narrow">
|
||||
<p class="muted">This one-time link creates the single panel administrator.
|
||||
|
||||
Reference in New Issue
Block a user