Point 7 of the page-wording review. The pages no longer show their data structure as headings, and they no longer explain their own conventions. - Level 1 is one screen: claim, status badge, three figures, the technology row, and one sentence carrying one link. Measured in headless Chrome at 1440x900: 1404-1845 px tall before, exactly 900 px (no scroll) after, on all six area pages. - DocPage: no kicker line, no visible level label, no "Evidence for this page" heading or instruction note, no "Drill-downs" list, no "Status <word> - sentence" footer. The artefact table starts at level 2, under the heading "Artefacts"; the history timeline is level 2 only. - Landing page: "Start here" link text gone, "How to read this site" panel gone, the footer's status-label explainer gone. - Content, presentation only, no claim softened: the leading H1 that duplicated the page title removed from all 41 documents; level 1 bodies reduced to the constraint as ordinary prose plus the way in; "**Status: ...**" prefixes removed from the artefact stubs; "## Drill-downs" and "## Period and confidence" removed from the level 2 pages, with the cross-area links moved into the prose. - The six level 1 claims are now sentences with a subject, and use commas instead of middots. Same facts, no new ones. Not in this commit: the 35 level 2 and 3 claims still use the compact CV style. That is a voice decision for Sam, not a wording tidy-up. make validate: 41 documents, 0 violations. go test ./...: pass. (cherry picked from commit 03009f80fc7815ad7719412e125df9297a6e653f)
236 lines
7.9 KiB
Plaintext
236 lines
7.9 KiB
Plaintext
package templates
|
|
|
|
import "strconv"
|
|
|
|
// HomePage is the landing page: the 90-second read.
|
|
templ HomePage(v HomeView) {
|
|
@Layout(v.Layout) {
|
|
<section class="hero">
|
|
<p class="hero-kicker">Melbourne · twenty-five years · AI · platform · edge</p>
|
|
<h1 class="hero-title">{ v.Positioning }</h1>
|
|
<p class="hero-support">{ v.Support }</p>
|
|
<p class="hero-facts">
|
|
<span>{ strconv.Itoa(v.Areas) } areas</span>
|
|
<span>{ strconv.Itoa(v.Pages) } pages</span>
|
|
<span>{ strconv.Itoa(v.Artifacts) } linked artefacts</span>
|
|
<span>no page needs a model to render</span>
|
|
</p>
|
|
@SearchForm("", "hero-search")
|
|
</section>
|
|
|
|
<section class="pillars" aria-label="Six areas">
|
|
for _, c := range v.Cards {
|
|
@PillarCard(c)
|
|
}
|
|
</section>
|
|
}
|
|
}
|
|
|
|
// DocPage renders an L1, L2 or L3 document.
|
|
templ DocPage(v DocView) {
|
|
@Layout(v.Layout) {
|
|
<article class="doc">
|
|
<header class="doc-head">
|
|
<h1 class="doc-title">{ v.Doc.Title }</h1>
|
|
<p class="doc-claim">{ v.Doc.Claim }</p>
|
|
<div class="doc-chips">
|
|
<span class={ ConfidenceClass(v.Doc.Confidence) }>{ v.Doc.Confidence }</span>
|
|
if v.Doc.Period != "" {
|
|
<span class="chip chip-plain">{ v.Doc.Period }</span>
|
|
}
|
|
for _, m := range v.Metrics {
|
|
<span class="chip">
|
|
<strong>{ m.Value }</strong>
|
|
<span>{ m.Label }</span>
|
|
</span>
|
|
}
|
|
</div>
|
|
if len(v.Tech) > 0 {
|
|
<ul class="tech" aria-label="Technology">
|
|
for _, t := range v.Tech {
|
|
<li>{ t }</li>
|
|
}
|
|
</ul>
|
|
}
|
|
</header>
|
|
|
|
<div class="prose">
|
|
@v.Body
|
|
</div>
|
|
|
|
// Level 1 is one screen: the claim, the figures, the technology and the
|
|
// link in the prose. The evidence table starts at level 2, which is the
|
|
// page that argues for the claim.
|
|
if v.Doc.Level >= 2 && (len(v.Evidence) > 0 || len(v.SubPages) > 0) {
|
|
<section class="doc-evidence" aria-labelledby="evidence-heading">
|
|
<h2 id="evidence-heading">Artefacts</h2>
|
|
if len(v.Evidence) > 0 {
|
|
@EvidenceTable(evidenceRows(v.Evidence))
|
|
}
|
|
if len(v.SubPages) > 0 {
|
|
<ul class="drill-list">
|
|
for _, s := range v.SubPages {
|
|
<li>
|
|
<a href={ templ.SafeURL(s.Href) }>{ s.Label }</a>
|
|
<span class="drill-kind">{ s.Kind }</span>
|
|
</li>
|
|
}
|
|
</ul>
|
|
}
|
|
</section>
|
|
}
|
|
|
|
if v.Doc.Level == 2 && len(v.Timeline) > 0 {
|
|
<section class="doc-timeline" aria-labelledby="timeline-heading">
|
|
<h2 id="timeline-heading">In time order</h2>
|
|
@Timeline(v.Timeline)
|
|
</section>
|
|
}
|
|
|
|
if len(v.Backlinks) > 0 {
|
|
<section class="doc-back" aria-labelledby="back-heading">
|
|
<h2 id="back-heading">Referenced from</h2>
|
|
<ul class="drill-list">
|
|
for _, b := range v.Backlinks {
|
|
<li><a href={ templ.SafeURL(b.Href) }>{ b.Label }</a></li>
|
|
}
|
|
</ul>
|
|
</section>
|
|
}
|
|
|
|
<footer class="doc-foot">
|
|
<p>Last verified { v.Doc.LastVerified } · <code>content/{ v.Doc.Path }</code></p>
|
|
</footer>
|
|
</article>
|
|
}
|
|
}
|
|
|
|
// EvidencePage is one page listing every artefact, grouped by area. It is the
|
|
// shortest path from the landing page to any proof.
|
|
templ EvidencePage(v EvidenceView) {
|
|
@Layout(v.Layout) {
|
|
<article class="doc">
|
|
<header class="doc-head">
|
|
<p class="doc-kicker">Index</p>
|
|
<h1>Evidence index</h1>
|
|
<p class="doc-claim">Every artefact this site claims, on one page. { strconv.Itoa(v.Total) } links, each one a file in the repository.</p>
|
|
</header>
|
|
for _, g := range v.Groups {
|
|
<section class="evidence-group">
|
|
<h2><a href={ templ.SafeURL(g.Href) }>{ g.AreaTitle }</a></h2>
|
|
@EvidenceTable(g.Rows)
|
|
</section>
|
|
}
|
|
</article>
|
|
}
|
|
}
|
|
|
|
// SearchPage is the keyword search. The results block is swapped by HTMX on the
|
|
// same page; with JavaScript off the form simply reloads the page.
|
|
templ SearchPage(v SearchView) {
|
|
@Layout(v.Layout) {
|
|
<article class="doc">
|
|
<header class="doc-head">
|
|
<p class="doc-kicker">Keyword search</p>
|
|
<h1>Search { strconv.Itoa(v.Indexed) } pages</h1>
|
|
<p class="doc-claim">Full-text search over page titles and body text. Served from a local index — no model, no network call.</p>
|
|
</header>
|
|
@SearchForm(v.Query, "page-search")
|
|
@SearchResults(v)
|
|
</article>
|
|
}
|
|
}
|
|
|
|
// SearchResults is swapped into #search-results by HTMX.
|
|
templ SearchResults(v SearchView) {
|
|
<div id="search-results" aria-live="polite">
|
|
if v.Query == "" {
|
|
<p class="section-note">Try “zigbee”, “signage”, “routing”, “backups” or “guardrails”.</p>
|
|
} else if v.Failed {
|
|
<p class="search-empty">That query could not be run. Try fewer or simpler words.</p>
|
|
} else if len(v.Results) == 0 {
|
|
<p class="search-empty">No page matches <q>{ v.Query }</q>. The <a href="/evidence/">evidence index</a> lists everything by area.</p>
|
|
} else {
|
|
<p class="search-count">{ strconv.Itoa(len(v.Results)) } results for <q>{ v.Query }</q></p>
|
|
<ol class="results">
|
|
for _, r := range v.Results {
|
|
<li class="result">
|
|
<h2><a href={ templ.SafeURL(r.URL) }>{ r.Title }</a></h2>
|
|
<p class="result-meta">
|
|
<span class="kind">{ r.Area }</span>
|
|
<span class="kind">L{ strconv.Itoa(r.Level) }</span>
|
|
<span class={ ConfidenceClass(r.Confidence) }>{ r.Confidence }</span>
|
|
</p>
|
|
<p class="result-snippet">@SafeSnippet(r.Snippet)</p>
|
|
</li>
|
|
}
|
|
</ol>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
// StreamPage proves the panel can be re-pointed at a stream. The layout and the
|
|
// panel markup are the ones every other page uses; only the binding differs.
|
|
templ StreamPage(v StreamView) {
|
|
@Layout(v.Layout) {
|
|
<article class="doc">
|
|
<header class="doc-head">
|
|
<p class="doc-kicker">Design note · not a product feature</p>
|
|
<h1>The panel takes content from a driver</h1>
|
|
<p class="doc-claim">This page renders the same layout and the same panel as every other page. Its panel is bound to an event stream instead of pre-rendered files, and no layout code changed.</p>
|
|
</header>
|
|
<div class="prose">
|
|
<p>The presentation panel is a content sink. In Mode A it takes pre-rendered fragments over
|
|
HTMX: you click a row in an evidence table and one file is swapped in. On this page the same
|
|
panel is bound to <code>/api/stage/stream</code> and receives { strconv.Itoa(v.Frames) } fragments
|
|
over server-sent events, one after another.</p>
|
|
<p>Fragments are pushed, not requested. Nothing about the panel — its markup, its fragment
|
|
view, its empty state — knows the difference. That is the point: the Phase 5 console
|
|
re-points this panel at retrieval output and the layout does not change.</p>
|
|
<p class="section-note">This demonstration streams the artefacts already built for the site, with
|
|
no model in the loop. It exists to prove the transport, not to pretend an AI is answering.</p>
|
|
</div>
|
|
if len(v.Fragment) > 0 {
|
|
<section class="doc-sub">
|
|
<h2>Fragments in this stream</h2>
|
|
<ul class="drill-list">
|
|
for _, f := range v.Fragment {
|
|
<li>
|
|
<a href={ templ.SafeURL(f.Href) } hx-get={ f.HXGet } hx-target="#stage-content" hx-swap="innerHTML">{ f.Label }</a>
|
|
<span class="drill-kind">{ f.Kind }</span>
|
|
</li>
|
|
}
|
|
</ul>
|
|
</section>
|
|
}
|
|
</article>
|
|
}
|
|
}
|
|
|
|
// NotFoundPage is the 404.
|
|
templ NotFoundPage(v NotFoundView) {
|
|
@Layout(v.Layout) {
|
|
<article class="doc">
|
|
<header class="doc-head">
|
|
<p class="doc-kicker">404</p>
|
|
<h1>That page is not here</h1>
|
|
<p class="doc-claim">The link may be from an older build. The six areas and the evidence index are the reliable ways in.</p>
|
|
</header>
|
|
<ul class="drill-list">
|
|
<li><a href="/">Landing page</a></li>
|
|
<li><a href="/evidence/">Evidence index</a></li>
|
|
<li><a href="/search">Search</a></li>
|
|
</ul>
|
|
</article>
|
|
}
|
|
}
|
|
|
|
func evidenceRows(links []LinkView) []EvidenceRow {
|
|
rows := make([]EvidenceRow, 0, len(links))
|
|
for _, l := range links {
|
|
rows = append(rows, EvidenceRow{Label: l.Label, Href: l.Href, HXGet: l.HXGet, Kind: l.Kind, Confidence: l.Confidence})
|
|
}
|
|
return rows
|
|
}
|
|
|