1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>mailweb ({{account}})</title>
<link rel="alternate" type="text/llm" href="/"
      title="Same information as a compact plain-text rendering for machine and LLM consumption: identical semantics and links, far fewer tokens. Request it with: Accept: text/llm">
<style>
  :root {
    color-scheme: light dark;
    --bg:           #f5f5f5;
    --bg-card:      #ffffff;
    --border:       #dddddd;
    --border-frame: #eeeeee;
    --text:         #111111;
    --text-meta:    #666666;
    --link:         #3781b8;
  }
  @media (prefers-color-scheme: dark) {
    :root {
      --bg:           #1a1a1a;
      --bg-card:      #242424;
      --border:       #3a3a3a;
      --border-frame: #333333;
      --text:         #e8e8e8;
      --text-meta:    #999999;
      --link:         #7ab3e0;
    }
  }
  body    { font-family: sans-serif; margin: 0; padding: 1rem;
            background: var(--bg); color: var(--text); }
  section { background: var(--bg-card); border: 1px solid var(--border);
            border-radius: 4px; margin-bottom: 2rem; padding: 1rem; }
  h1      { font-size: 1.2rem; margin-bottom: 1rem; }
  h2      { margin: 0 0 0.25rem 0; font-size: 1rem; }
  h2 a.subj { color: var(--text); text-decoration: none; }
  h2 a.subj:hover { color: var(--link); text-decoration: underline; }
  .meta   { color: var(--text-meta); font-size: 0.85rem; margin-bottom: 0.75rem; }
  iframe  { width: 100%; border: 1px solid var(--border-frame);
            border-radius: 2px; display: block; }
  nav     { margin-bottom: 1rem; display: flex; gap: 1.5rem; }
  nav a   { color: var(--link); text-decoration: none; font-size: 0.9rem; }
  nav a:hover { text-decoration: underline; }
  .pager  { display: flex; gap: 1.5rem; align-items: baseline;
            margin: 1rem 0; font-size: 0.9rem; }
  .pager a { color: var(--link); text-decoration: none; }
  .pager a:hover { text-decoration: underline; }
  .pager .count { color: var(--text-meta); }
  .unsub-form { margin-top: 0.4rem; }
  button.unsub-btn {
    background: none; border: 1px solid var(--border); border-radius: 3px;
    color: var(--text-meta); font-size: 0.8rem; padding: 0.2rem 0.5rem;
    cursor: pointer;
  }
  button.unsub-btn:hover { color: var(--text); border-color: var(--text-meta); }
{{template "nameStyle"}}
{{template "attachmentStyle"}}
{{template "editorStyle"}}
</style>
<script>
// Resize each iframe to its content height once loaded.
// Safe because allow-same-origin lets us read the iframe DOM, but
// allow-scripts is absent so JS inside the iframe cannot run.
function resizeIframe(el) {
  try {
    var doc = el.contentDocument || el.contentWindow.document;
    el.style.height = doc.documentElement.scrollHeight + "px";
  } catch(e) {}
}
</script>
</head>
<body>
<nav>
  <a href="/contacts">contacts →</a>
  <a href="/forge">forge →</a>
  <a href="/drafts">drafts →</a>
  <a href="{{.LLMViewURL}}" title="Compact plain-text rendering of this page">text view →</a>
</nav>
{{/* The pager is repeated below the listing as well: a page is ten full message
     bodies tall, so a reader who has reached the end of it is nowhere near the
     one at the top. */}}
{{define "pager"}}
{{- if or .Paging.Next .Paging.Prev}}
<div class="pager">
  <span class="count">showing {{.Paging.Range (len .Messages)}} of {{.Paging.Total}}</span>
  {{if .Paging.Prev}}<a href="{{.Paging.Prev}}">← newer</a>{{end}}
  {{if .Paging.Next}}<a href="{{.Paging.Next}}">older ({{.Paging.NextCount}} more) →</a>{{end}}
</div>
{{- end}}
{{end}}
{{if .Random}}
<h1>{{len .Messages}} random messages</h1>
{{else}}
<h1>{{.Paging.Total}} messages{{if .Filter}} — {{.Filter}}{{end}}</h1>
{{template "pager" .}}
{{end}}
{{range .Messages}}
<section>
  {{/* The heading links to the message's own page rather than to /msg/{id},
       which is the fragment framed just below and would open as a body with no
       header block. */}}
  <h2><a class="subj" href="/msg/{{.ID}}/view">{{if .Subject}}{{.Subject}}{{else}}(no subject){{end}}</a></h2>
  <div class="meta">From: {{template "name" .From}} &lt;{{.From.Address}}&gt; &nbsp;|&nbsp; {{.Date.Format "Mon, 02 Jan 2006 15:04"}}{{with .SizeHint}}{{.}}{{end}}</div>
  {{/* Above the body: for mail that carries a calendar, the event usually is
       the message and the text is the covering letter. */}}
  {{template "calendars" (index $.AttachmentsByMsg .ID)}}
  <iframe src="/msg/{{.ID}}"
          sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox"
          loading="lazy"
          onload="resizeIframe(this)"></iframe>
  {{/* Outside the frame: mailweb's own words never render inside a document the
       sender wrote. See templates/attachments.html. */}}
  {{template "attachments" (index $.AttachmentsByMsg .ID)}}
  {{if .ListUnsubscribe}}
  <form class="unsub-form" method="POST" action="/unsubscribe/{{.ID}}">
    <button class="unsub-btn" type="submit" title="Send unsubscribe email to {{.ListUnsubscribe}}">✉ unsubscribe</button>
  </form>
  {{else if .ListUnsubscribeURL}}
  <form class="unsub-form" method="POST" action="/unsubscribe/{{.ID}}">
    <button class="unsub-btn" type="submit"
            onclick="window.open('{{.ListUnsubscribeURL}}', '_blank')"
            title="Open unsubscribe page: {{.ListUnsubscribeURL}}">✉ unsubscribe</button>
  </form>
  {{end}}
  {{/* Replying sits below the message, because it is what you do after
       reading one. Any draft already written against this message is listed
       beside it: without that, replying twice would silently make a second
       draft with neither page mentioning the other. */}}
  {{$drafts := index $.DraftsByMsg .ID}}
  {{range $drafts}}
  <div data-draft-editor="{{.Token}}"></div>
  {{else}}
  {{template "replyButton" .ID}}
  {{end}}
</section>
{{end}}
{{if not .Random}}{{template "pager" .}}{{end}}
{{template "editorScript"}}
</body>
</html>