Redesign CI and config pages to match repo home layout
- Remove README.md eyebrow header from repo home
- Fix ci-inline alignment (baseline instead of center) in repo position token
- Redesign ci/run_list.html with two-column layout (run list + sidebar)
- Redesign ci/run_detail.html with two-column layout (detail + sidebar)
- Redesign config.html to use repo-style section nav instead of table layout
- Add corresponding CSS for new page layouts, remove orphaned ci-heading/ci-table styles

Assisted-by: Owl Alpha via pi
change rvnlmqwzxkpnowmrslsozpuwnrqrsnxu
commit 7cec701d050a92e0b1b27fa744c36b5ab97343a2
author Alpha Chen <alpha@kejadlen.dev>
date
parent ttwlkxpo
diff --git a/quire-server/src/quire/web/auth.rs b/quire-server/src/quire/web/auth.rs
index 82f51cf..a6c08ab 100644
--- a/quire-server/src/quire/web/auth.rs
+++ b/quire-server/src/quire/web/auth.rs
@@ -23,10 +23,9 @@ impl<S: Send + Sync> FromRequestParts<S> for Auth {
 /// `Auth` extractor behaves as if a real user is present.
 #[cfg(feature = "dev")]
 pub async fn inject_dev_user(mut request: axum::extract::Request, next: Next) -> Response {
-    request.headers_mut().insert(
-        "Remote-User",
-        axum::http::HeaderValue::from_static("dev"),
-    );
+    request
+        .headers_mut()
+        .insert("Remote-User", axum::http::HeaderValue::from_static("dev"));
     next.run(request).await
 }
 
diff --git a/quire-server/src/quire/web/templates.rs b/quire-server/src/quire/web/templates.rs
index 2fa1db5..3229bbf 100644
--- a/quire-server/src/quire/web/templates.rs
+++ b/quire-server/src/quire/web/templates.rs
@@ -22,15 +22,31 @@ pub struct SectionLink {
 /// template.
 pub fn nav_sections(repo: &str, active: &str, authed: bool) -> Vec<SectionLink> {
     let mut sections = vec![
-        SectionLink { label: "overview", href: format!("/{repo}"), active: active == "overview" },
-        SectionLink { label: "tree", href: format!("/{repo}/tree"), active: active == "tree" },
-        SectionLink { label: "log", href: format!("/{repo}/log"), active: active == "log" },
+        SectionLink {
+            label: "overview",
+            href: format!("/{repo}"),
+            active: active == "overview",
+        },
+        SectionLink {
+            label: "tree",
+            href: format!("/{repo}/tree"),
+            active: active == "tree",
+        },
+        SectionLink {
+            label: "log",
+            href: format!("/{repo}/log"),
+            active: active == "log",
+        },
         SectionLink {
             label: "bookmarks",
             href: format!("/{repo}/bookmarks"),
             active: active == "bookmarks",
         },
-        SectionLink { label: "tags", href: format!("/{repo}/tags"), active: active == "tags" },
+        SectionLink {
+            label: "tags",
+            href: format!("/{repo}/tags"),
+            active: active == "tags",
+        },
     ];
     if authed {
         sections.push(SectionLink {
diff --git a/quire-server/static/style.css b/quire-server/static/style.css
index 0de745e..6adac15 100644
--- a/quire-server/static/style.css
+++ b/quire-server/static/style.css
@@ -99,33 +99,6 @@ pre { white-space: pre-wrap; word-break: break-word; }
 
 /* CI run list */
 
-.ci-heading {
-  font-family: var(--font-mono);
-  font-size: var(--step-1);
-  font-weight: 600;
-  margin: 0 0 var(--space-s);
-}
-
-.ci-table {
-  width: 100%;
-  border-collapse: collapse;
-  font-family: var(--font-mono);
-  font-size: var(--step--1);
-  line-height: 1.6;
-}
-
-.ci-table thead th {
-  text-align: left;
-  padding: var(--space-3xs) var(--space-2xs);
-  font-weight: 400;
-  color: var(--mutedFaint);
-}
-
-.ci-table thead tr { border-bottom: 1px solid var(--rule2); }
-.ci-table tbody { border-top: 1px solid var(--rule); }
-.ci-table td { padding: var(--space-3xs) var(--space-2xs); }
-.ci-table .empty { padding: var(--space-s); color: var(--muted); }
-
 .ci-status-dot {
   display: inline-block;
   width: 6px;
@@ -146,26 +119,6 @@ pre { white-space: pre-wrap; word-break: break-word; }
   border-bottom: 1px dotted var(--rule2);
 }
 
-/* CI run detail */
-
-.ci-meta {
-  padding: var(--space-s) 0;
-  border-bottom: 1px solid var(--rule);
-}
-
-.ci-meta-primary {
-  font-family: var(--font-mono);
-  font-size: var(--step-0);
-  line-height: 1.6;
-}
-
-.ci-meta-secondary {
-  font-family: var(--font-mono);
-  font-size: var(--step--1);
-  color: var(--mutedFaint);
-  margin-top: var(--space-3xs);
-}
-
 /* CI jobs */
 
 .ci-job {
@@ -306,7 +259,7 @@ html.dark {
 
 .repo-meta-sep { color: var(--rule2); }
 .repo-meta-dot { color: var(--rule2); }
-.ci-inline { display: inline-flex; align-items: center; gap: var(--space-3xs); }
+.ci-inline { display: inline-flex; align-items: baseline; gap: var(--space-3xs); }
 
 .bookmark-glyph { color: var(--mutedFaint); }
 .bookmark-name { color: var(--accent); }
@@ -661,6 +614,89 @@ html.dark {
   color: var(--mutedFaint);
 }
 
+/* ── CI run list (full page) ───────────────────────────────────── */
+
+.ci-run-list {
+  padding: var(--space-l) var(--space-xl);
+  min-width: 0;
+}
+
+.ci-run-row {
+  display: flex;
+  align-items: baseline;
+  gap: var(--space-2xs);
+  font-family: var(--font-mono);
+  font-size: 12.5px;
+  line-height: 2;
+}
+
+.ci-run-row .ci-status-dot {
+  flex-shrink: 0;
+}
+
+.ci-run-branch {
+  color: var(--accent);
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.ci-run-age {
+  color: var(--mutedFaint);
+  white-space: nowrap;
+}
+
+.ci-run-dur {
+  color: var(--muted);
+  white-space: nowrap;
+  margin-left: auto;
+}
+
+/* ── CI run detail ─────────────────────────────────────────────── */
+
+.ci-detail {
+  padding: var(--space-l) var(--space-xl);
+  min-width: 0;
+}
+
+.ci-detail .ci-meta {
+  padding: 0 0 var(--space-s);
+  border-bottom: 1px solid var(--rule);
+  font-family: var(--font-mono);
+  font-size: 12.5px;
+  color: var(--muted);
+}
+
+/* ── Config ────────────────────────────────────────────────────── */
+
+.config-table {
+  padding: var(--space-l) var(--space-xl);
+  min-width: 0;
+}
+
+.config-row {
+  display: flex;
+  align-items: baseline;
+  gap: var(--space-s);
+  font-family: var(--font-mono);
+  font-size: 12.5px;
+  line-height: 2;
+}
+
+.config-key {
+  color: var(--muted);
+  min-width: 200px;
+}
+
+.config-val {
+  color: var(--ink);
+}
+
+.config-val--empty {
+  color: var(--mutedFaint);
+  font-style: italic;
+}
+
 /* ── Arborium syntax highlighting integration ──────────────────── */
 
 /* Keep quire's paper-toned code background; strip any theme-imposed radius. */
diff --git a/quire-server/templates/ci/run_detail.html b/quire-server/templates/ci/run_detail.html
index fdc7797..515dc11 100644
--- a/quire-server/templates/ci/run_detail.html
+++ b/quire-server/templates/ci/run_detail.html
@@ -7,54 +7,99 @@
 {% endblock %}
 
 {% block fullpage %}
+
 <nav class="repo-section-nav">
   {% include "_repo_section_nav.html" %}
+  <span class="repo-position">
+    <span class="ci-commit-link">{{ run.sha_short() }}</span>
+    <span class="repo-meta-dot">·</span>
+    <span>{{ run.branch_short() }}</span>
+    <span class="repo-meta-dot">·</span>
+    <span class="ci-state-label {{ run.state_class() }}"><span class="ci-status-dot {{ run.state_class() }}"></span> {{ run.state() }}</span>
+  </span>
 </nav>
-<main class="page-main">
-<div class="ci-meta">
-  <div class="ci-meta-primary">
-    <span class="c-accent">{{ run.sha_short() }}</span>
-    · {{ run.branch_short() }}
-    · <span class="ci-state-label {{ run.state_class() }}"><span class="ci-status-dot {{ run.state_class() }}"></span> {{ run.state() }}</span>
-  </div>
-  <div class="ci-meta-secondary">
-    {% if run.is_terminal() %}
-    queued <time title="{{ run.queued_iso() }}">{{ run.queued_relative() }}</time>
-    · ran <span title="started {{ run.started_iso() }}\nfinished {{ run.finished_iso() }}">{{ run.duration_display() }}</span>
-    {% elif run.has_started() %}
-    queued <time title="{{ run.queued_iso() }}">{{ run.queued_relative() }}</time>
-    · started <time title="{{ run.started_iso() }}">{{ run.started_display() }}</time>
+
+<div class="repo-body">
+
+  <article class="ci-detail">
+    <div class="ci-meta">
+      {% if run.is_terminal() %}
+      queued <time title="{{ run.queued_iso() }}">{{ run.queued_relative() }}</time>
+      <span class="repo-meta-dot">·</span>
+      ran <span title="started {{ run.started_iso() }}\nfinished {{ run.finished_iso() }}">{{ run.duration_display() }}</span>
+      {% elif run.has_started() %}
+      queued <time title="{{ run.queued_iso() }}">{{ run.queued_relative() }}</time>
+      <span class="repo-meta-dot">·</span>
+      started <time title="{{ run.started_iso() }}">{{ run.started_display() }}</time>
+      {% else %}
+      queued <time title="{{ run.queued_iso() }}">{{ run.queued_relative() }}</time>
+      {% endif %}
+    </div>
+
+    {% for job in jobs %}
+    <div class="ci-job">
+      <div class="ci-job-header">
+        {{ job.job_id }}<span class="repo-meta-dot">·</span>{{ job.duration_display() }}{% if let Some(code) = job.exit_code_filter_nonzero() %}<span class="repo-meta-dot">·</span>exit {{ code }}{% endif %}
+        <span class="repo-meta-dot">·</span>
+        <span class="ci-state-label {{ job.state_class() }}"><span class="ci-status-dot {{ job.state_class() }}"></span> {{ job.state }}</span>
+      </div>
+      {% for sh in job.sh_events %}
+      <div class="ci-sh">
+        <div class="ci-sh-cmd">
+          {{ sh.cmd_display() }}
+          <span class="ci-sh-meta">{{ sh.duration_display() }}{% if sh.exit_code != 0 %} · exit {{ sh.exit_code }}{% endif %}</span>
+        </div>
+        {% if !sh.log_content.is_empty() %}
+        <pre class="ci-sh-log">{{ sh.log_content }}</pre>
+        {% endif %}
+      </div>
+      {% endfor %}
+    </div>
     {% else %}
-    queued <time title="{{ run.queued_iso() }}">{{ run.queued_relative() }}</time>
+    <div class="ci-empty">no jobs recorded</div>
+    {% endfor %}
+
+    {% if !quire_ci_log.is_empty() %}
+    <div class="ci-job">
+      <div class="ci-job-header">quire-ci</div>
+      <pre class="ci-sh-log">{{ quire_ci_log }}</pre>
+    </div>
     {% endif %}
-  </div>
-</div>
+  </article>
 
-{% for job in jobs %}
-<div class="ci-job">
-  <div class="ci-job-header">
-    {{ job.job_id }} · {{ job.duration_display() }}{% if let Some(code) = job.exit_code_filter_nonzero() %} · exit {{ code }}{% endif %}
-    · <span class="ci-state-label {{ job.state_class() }}"><span class="ci-status-dot {{ job.state_class() }}"></span> {{ job.state }}</span>
-  </div>
-  {% for sh in job.sh_events %}
-  <div class="ci-sh">
-    <div class="ci-sh-cmd">
-      {{ sh.cmd_display() }} <span class="ci-sh-meta">{{ sh.duration_display() }}{% if sh.exit_code != 0 %} · exit {{ sh.exit_code }}{% endif %}</span>
+  <aside class="repo-sidebar">
+    {% if !bookmarks.is_empty() %}
+    <div class="side-block">
+      <div class="side-block-title">Bookmarks</div>
+      <div class="bookmark-list">
+        {% for b in bookmarks %}
+        <div class="bookmark-row">
+          <span class="bookmark-kind {% if loop.first %}bookmark-kind--trunk{% endif %}">{% if loop.first %}trunk{% else %}local{% endif %}</span>
+          <a class="bookmark-link" href="/{{ repo }}/log">
+            <span class="bookmark-glyph-sm">※</span>{{ b.name }}
+          </a>
+          <span class="bookmark-age">{{ b.age }}</span>
+        </div>
+        {% endfor %}
+      </div>
     </div>
-    {% if !sh.log_content.is_empty() %}
-    <pre class="ci-sh-log">{{ sh.log_content }}</pre>
     {% endif %}
-  </div>
-  {% endfor %}
-</div>
-{% else %}
-<div class="ci-empty">no jobs recorded</div>
-{% endfor %}
-{% if !quire_ci_log.is_empty() %}
-<div class="ci-job">
-  <div class="ci-job-header">quire-ci</div>
-  <pre class="ci-sh-log">{{ quire_ci_log }}</pre>
+
+    {% if !tags.is_empty() %}
+    <div class="side-block">
+      <div class="side-block-title">Tags</div>
+      <div class="bookmark-list">
+        {% for t in tags %}
+        <div class="bookmark-row">
+          <a class="bookmark-link" href="/{{ repo }}/log">{{ t.name }}</a>
+          <span class="bookmark-age">{{ t.age }}</span>
+        </div>
+        {% endfor %}
+      </div>
+    </div>
+    {% endif %}
+  </aside>
+
 </div>
-{% endif %}
-</main>
+
 {% endblock %}
diff --git a/quire-server/templates/ci/run_list.html b/quire-server/templates/ci/run_list.html
index b3d6226..4189018 100644
--- a/quire-server/templates/ci/run_list.html
+++ b/quire-server/templates/ci/run_list.html
@@ -7,34 +7,60 @@
 {% endblock %}
 
 {% block fullpage %}
+
 <nav class="repo-section-nav">
   {% include "_repo_section_nav.html" %}
 </nav>
-<main class="page-main">
-<h2 class="ci-heading">ci runs</h2>
-<table class="ci-table">
-  <thead>
-    <tr>
-      <th></th>
-      <th>commit</th>
-      <th>ref</th>
-      <th>queued</th>
-      <th>duration</th>
-    </tr>
-  </thead>
-  <tbody>
-  {% for run in runs %}
-    <tr>
-      <td><span class="ci-status-dot {{ run.state_class() }}"></span></td>
-      <td><a href="/{{ repo }}/ci/{{ run.id }}" class="ci-commit-link">{{ run.sha_short() }}</a></td>
-      <td>{{ run.branch_short() }}</td>
-      <td><time title="{{ run.queued_iso() }}">{{ run.queued_relative() }}</time></td>
-      <td>{{ run.duration_display() }}</td>
-    </tr>
-  {% else %}
-    <tr><td colspan="5" class="empty">no runs yet</td></tr>
-  {% endfor %}
-  </tbody>
-</table>
-</main>
+
+<div class="repo-body">
+
+  <article class="ci-run-list">
+    {% for run in runs %}
+    <div class="ci-run-row">
+      <span class="ci-status-dot {{ run.state_class() }}"></span>
+      <a class="ci-commit-link" href="/{{ repo }}/ci/{{ run.id }}">{{ run.sha_short() }}</a>
+      <span class="ci-run-branch">{{ run.branch_short() }}</span>
+      <span class="ci-run-age"><time title="{{ run.queued_iso() }}">{{ run.queued_relative() }}</time></span>
+      <span class="ci-run-dur">{{ run.duration_display() }}</span>
+    </div>
+    {% else %}
+    <p class="ci-empty">no runs yet</p>
+    {% endfor %}
+  </article>
+
+  <aside class="repo-sidebar">
+    {% if !bookmarks.is_empty() %}
+    <div class="side-block">
+      <div class="side-block-title">Bookmarks</div>
+      <div class="bookmark-list">
+        {% for b in bookmarks %}
+        <div class="bookmark-row">
+          <span class="bookmark-kind {% if loop.first %}bookmark-kind--trunk{% endif %}">{% if loop.first %}trunk{% else %}local{% endif %}</span>
+          <a class="bookmark-link" href="/{{ repo }}/log">
+            <span class="bookmark-glyph-sm">※</span>{{ b.name }}
+          </a>
+          <span class="bookmark-age">{{ b.age }}</span>
+        </div>
+        {% endfor %}
+      </div>
+    </div>
+    {% endif %}
+
+    {% if !tags.is_empty() %}
+    <div class="side-block">
+      <div class="side-block-title">Tags</div>
+      <div class="bookmark-list">
+        {% for t in tags %}
+        <div class="bookmark-row">
+          <a class="bookmark-link" href="/{{ repo }}/log">{{ t.name }}</a>
+          <span class="bookmark-age">{{ t.age }}</span>
+        </div>
+        {% endfor %}
+      </div>
+    </div>
+    {% endif %}
+  </aside>
+
+</div>
+
 {% endblock %}
diff --git a/quire-server/templates/config.html b/quire-server/templates/config.html
index a64367d..ceddb6f 100644
--- a/quire-server/templates/config.html
+++ b/quire-server/templates/config.html
@@ -15,37 +15,62 @@
       </svg>
       <span class="nav-wordmark-text">quire</span>
     </a>
-    {% for crumb in crumbs %}
     <span class="sep">/</span>
-    {% if let Some(href) = crumb.href %}
-    <a class="nav-crumb" href="{{ href }}">{{ crumb.label }}</a>
-    {% else %}
-    <span class="nav-crumb">{{ crumb.label }}</span>
-    {% endif %}
-    {% endfor %}
+    <span class="nav-crumb">config</span>
   </div>
 </nav>
 {% endblock %}
 
-{% block content %}
-<h2 class="ci-heading">config</h2>
-<table class="ci-table">
-  <tbody>
-    <tr><th>port</th><td>{{ config.port }}</td></tr>
-    <tr><th>ci.executor</th><td>{{ config.ci.executor }}</td></tr>
+{% block fullpage %}
+
+<nav class="repo-section-nav">
+  <span class="repo-position">config</span>
+</nav>
+
+<div class="repo-body">
+
+  <article class="config-table">
+    <div class="config-row">
+      <span class="config-key">port</span>
+      <span class="config-val">{{ config.port }}</span>
+    </div>
+    <div class="config-row">
+      <span class="config-key">ci.executor</span>
+      <span class="config-val">{{ config.ci.executor }}</span>
+    </div>
     {% if let Some(sentry) = config.sentry %}
-    <tr><th>sentry.dsn</th><td>{{ sentry.dsn }}</td></tr>
+    <div class="config-row">
+      <span class="config-key">sentry.dsn</span>
+      <span class="config-val">{{ sentry.dsn }}</span>
+    </div>
     {% else %}
-    <tr><th>sentry</th><td class="empty">disabled</td></tr>
+    <div class="config-row">
+      <span class="config-key">sentry</span>
+      <span class="config-val config-val--empty">disabled</span>
+    </div>
     {% endif %}
     {% if let Some(token) = config.github.mirror_token %}
-    <tr><th>github.mirror-token</th><td>{{ token }}</td></tr>
+    <div class="config-row">
+      <span class="config-key">github.mirror-token</span>
+      <span class="config-val">{{ token }}</span>
+    </div>
     {% else %}
-    <tr><th>github.mirror-token</th><td class="empty">not set</td></tr>
+    <div class="config-row">
+      <span class="config-key">github.mirror-token</span>
+      <span class="config-val config-val--empty">not set</span>
+    </div>
     {% endif %}
     {% for (key, value) in sorted_secrets() %}
-    <tr><th>secrets.{{ key }}</th><td>{{ value }}</td></tr>
+    <div class="config-row">
+      <span class="config-key">secrets.{{ key }}</span>
+      <span class="config-val">{{ value }}</span>
+    </div>
     {% endfor %}
-  </tbody>
-</table>
+  </article>
+
+  <aside class="repo-sidebar">
+  </aside>
+
+</div>
+
 {% endblock %}
diff --git a/quire-server/templates/repo_home.html b/quire-server/templates/repo_home.html
index aa250ab..9b7fe82 100644
--- a/quire-server/templates/repo_home.html
+++ b/quire-server/templates/repo_home.html
@@ -38,7 +38,6 @@
 
   {# Left column: README #}
   <article class="repo-readme">
-    <div class="readme-eyebrow">README.md</div>
     {% if let Some(html) = readme_html %}
     <div class="readme-content">{{ html|safe }}</div>
     {% else %}