adventure: give a finished run a report worth sharing
The liveblog answers "what is happening" — it is capped, it scrolls, and six hours after a run ends it is gone, because the adventurer page is about now. Nothing answered the question asked afterwards, usually by somebody who wasn't watching: what WAS that run. So a dispatch announcing a clear or a death was a paragraph about an outcome with no way back to what produced it. The report is that way back. The whole log uncapped, the numbers rolled up, and the single worst hit the party took pulled out of the middle where it otherwise reads as one line among forty. It is stable for a fortnight, which is what makes it a thing worth linking from a dispatch and worth sending to somebody. It is assembled from the same beats through the same renderer as the liveblog. A report that told a different story from the log it was built out of would be the more convincing of the two and the less true. The summary is the exception and the only prose on the channel: gogobee's model reads the finished run back and says what it was about, which is a judgement no template makes. It rides a summary beat rather than its own endpoint, so it inherits the whole channel — idempotent, retried, impossible to attach to a run that doesn't exist — and it passes the same class of guard a dispatch lede does before it reaches a public page. Visibility is the adventurer page's rule exactly, and that matters more here than anywhere: the report outlives the log by a fortnight and is linked from a public dispatch, so it is the surface most likely to still be reachable after somebody opts out. Coming off the board closes it, including through links minted days earlier. Claude-Session: https://claude.ai/code/session_012bxpQQJDjC1mTtLN3VVtBQ
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
{{define "title"}}{{.Name}} in {{.Zone}} — {{.SiteTitle}}{{end}}
|
||||
|
||||
{{define "main"}}
|
||||
<article class="mt-2 mb-10 max-w-3xl mx-auto">
|
||||
<nav class="mb-4 flex items-center gap-4">
|
||||
<a href="/adventure" class="inline-flex items-center gap-1.5 text-sm font-semibold text-[color:var(--ink)]/60 hover:text-[color:var(--ink)] transition">
|
||||
<span aria-hidden="true">←</span> All dispatches
|
||||
</a>
|
||||
{{if .WhoURL}}
|
||||
<a href="{{.WhoURL}}" class="inline-flex items-center gap-1.5 text-sm font-semibold text-[color:var(--ink)]/60 hover:text-[color:var(--ink)] transition">
|
||||
{{.Name}}'s sheet <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
{{end}}
|
||||
</nav>
|
||||
|
||||
<header class="rounded-3xl bg-theme-adventure text-white p-6 sm:p-10 shadow-pete relative overflow-hidden">
|
||||
<div class="absolute -top-6 -right-6 text-[12rem] opacity-20 select-none" aria-hidden="true">{{.Emoji}}</div>
|
||||
<div class="relative">
|
||||
<p class="text-sm uppercase tracking-[0.2em] opacity-80">{{.Emoji}} {{.Verdict}}</p>
|
||||
<h1 class="font-display text-3xl sm:text-4xl font-bold mt-2 leading-tight">{{.Name}} in {{.Zone}}</h1>
|
||||
<p class="mt-4 text-xs uppercase tracking-wider opacity-75">
|
||||
{{if .When}}{{.When}}{{end}}{{if .Level}} · level {{.Level}}{{end}}{{if .Elapsed}} · {{.Elapsed}} down there{{end}}{{if .Rooms}} · {{.Rooms}}{{end}}
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{if .Summary}}
|
||||
<!-- The one paragraph on this page nobody in the realm wrote down as it
|
||||
happened: the game's own model reading the finished run back. Guarded at
|
||||
ingest, and absent entirely when the model is off — which is why it sits
|
||||
above the numbers rather than instead of them. -->
|
||||
<section class="mt-6 rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 sm:p-8 shadow-pete">
|
||||
<p class="text-lg leading-relaxed text-[color:var(--ink)]/90 whitespace-pre-line">{{.Summary}}</p>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
{{if .Stats}}
|
||||
<section class="mt-6 grid grid-cols-2 sm:grid-cols-3 gap-2">
|
||||
{{range .Stats}}
|
||||
<div class="rounded-2xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 px-2 py-3 text-center shadow-pete">
|
||||
<div class="font-display text-2xl font-bold leading-none tabular-nums">{{.Value}}</div>
|
||||
<div class="text-[10px] uppercase tracking-wider text-[color:var(--ink)]/50 mt-1.5">{{.Label}}</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
{{if .Turning}}
|
||||
<!-- The worst single thing that happened, pulled out of the middle of the log
|
||||
where it would otherwise read as one line among forty. -->
|
||||
<section class="mt-6 rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
|
||||
<h2 class="font-display text-xl font-bold mb-3">Where it turned</h2>
|
||||
<p class="flex items-baseline gap-2.5">
|
||||
<span aria-hidden="true">{{.Turning.Emoji}}</span>
|
||||
<span class="flex-1 font-semibold">{{.Turning.Text}}</span>
|
||||
<span class="runlog-meta">{{if .Turning.Room}}{{.Turning.Room}} · {{end}}{{.Turning.When}}</span>
|
||||
</p>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<section class="mt-6 rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
|
||||
<div class="flex items-baseline justify-between mb-4 gap-3">
|
||||
<h2 class="font-display text-xl font-bold">Room by room</h2>
|
||||
{{if .Live}}<span class="text-sm text-[color:var(--ink)]/50">still under way</span>{{end}}
|
||||
</div>
|
||||
<ol class="runlog runlog-full">
|
||||
{{range .Lines}}
|
||||
<li class="runlog-line{{if .Hurt}} runlog-hurt{{end}}{{if .Good}} runlog-good{{end}}">
|
||||
<span class="runlog-emoji" aria-hidden="true">{{.Emoji}}</span>
|
||||
<span class="runlog-text">{{.Text}}</span>
|
||||
<span class="runlog-meta">{{if .Room}}{{.Room}} · {{end}}{{.When}}</span>
|
||||
</li>
|
||||
{{end}}
|
||||
</ol>
|
||||
{{if .Truncated}}
|
||||
<p class="mt-4 text-xs text-[color:var(--ink)]/45">Only the last {{len .Lines}} moments of this run are shown — it beat out more than the report keeps.</p>
|
||||
{{end}}
|
||||
<p class="mt-8 pt-6 border-t border-[color:var(--ink)]/10 text-sm italic text-theme-adventure font-semibold">
|
||||
Reporting from the realm, this is Pete.
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
{{end}}
|
||||
@@ -19,6 +19,13 @@
|
||||
|
||||
<div class="mt-8 rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 sm:p-8 shadow-pete">
|
||||
<p class="text-lg leading-relaxed text-[color:var(--ink)]/90 whitespace-pre-line">{{.Body}}</p>
|
||||
{{if .RunReportURL}}
|
||||
<!-- The way back to what actually happened. This dispatch is the ending of an
|
||||
expedition; the report is the expedition. -->
|
||||
<a href="{{.RunReportURL}}" class="mt-6 inline-flex items-center gap-2 rounded-2xl bg-theme-adventure text-white px-4 py-2.5 text-sm font-semibold shadow-pete hover:opacity-90 transition">
|
||||
<span aria-hidden="true">📜</span> Read the run, room by room <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
{{end}}
|
||||
<p class="mt-8 pt-6 border-t border-[color:var(--ink)]/10 text-sm italic text-theme-adventure font-semibold">
|
||||
Reporting from the realm, this is Pete.
|
||||
</p>
|
||||
|
||||
@@ -186,6 +186,13 @@
|
||||
</li>
|
||||
{{end}}
|
||||
</ol>
|
||||
<!-- Only once the run is over: while it's still walking, this column IS the
|
||||
report, and a link to a second copy of it is just a way to lose the
|
||||
reader. Revealed in place when a run ends under an open tab. -->
|
||||
<a id="who-runlog-report" href="{{.RunLog.ReportURL}}"
|
||||
class="mt-4 inline-flex items-center gap-1.5 text-sm font-semibold text-theme-adventure hover:opacity-80 transition{{if not .RunLog.ReportURL}} hidden{{end}}">
|
||||
<span aria-hidden="true">📜</span> The full report <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
{{if .HasHistory}}
|
||||
@@ -483,6 +490,19 @@
|
||||
});
|
||||
list.replaceChildren(frag);
|
||||
if (pinned) list.scrollTop = list.scrollHeight;
|
||||
|
||||
// The run can end between two polls, which is exactly the moment the report
|
||||
// becomes worth offering. href is set from the payload rather than built here
|
||||
// so the page never invents a URL for a run Pete won't serve.
|
||||
var report = document.getElementById('who-runlog-report');
|
||||
if (report) {
|
||||
if (log.ReportURL) {
|
||||
report.href = log.ReportURL;
|
||||
report.classList.remove('hidden');
|
||||
} else {
|
||||
report.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
(function () {
|
||||
var list = document.getElementById('who-runlog');
|
||||
|
||||
Reference in New Issue
Block a user