Files
brain-of-reese/frontend/doc-edit.html
T
ducoterra 36eddce672
Build and Push Containers / build-and-push-app (push) Successful in 1m49s
Build and Push Containers / build-and-push-db (push) Successful in 12s
phase: 177_themed_favicon
Everything is verified. Final report:

## Phase 177 — Themed favicon: completion pass (all tasks pre-done, verified end-to-end)

**Verified (no code fixes needed — implementation matched the design D1–D4 exactly):**
- `theming.favicon_svg()` pure renderer + golden byte-identical pin; `GET/HEAD /favicon.svg` route (no-cache, `image/svg+xml`, built-in 200 fallback on DB blip, routes-first before static mount); 5 templates → bare `href="/favicon.svg"`, static file deleted (Containerfile cp removed); Theme-tab `data:` live preview with 4 restore points; JS↔server mirror pins.

**Tests / lint / coverage (all green):**
- `uv run pytest --cov=app --cov-report=term-missing` → 4161 passed, `app/` 99% (>90% gate)
- `uv run ruff check .` → clean; `uv run pyright` → 0 errors
- `uv run pytest tests/e2e/test_themed_favicon.py -v --no-cov` (isolated) → 6 passed
- `test_theme_save_and_coverage.py` (92) + `test_theme_semantic_completion.py` (93), isolated → 3 + 8 passed

**Completion criteria:** all met — default golden bytes/headers; themed roles + `#bor-theme` non-regression; DB-failure 200 fallback; 5 bare links + `/assets/favicon.svg` 404 + browser rendering; live-preview data: repaint + Save/Reset/re-show restore; gates green; E2E suites green in isolation. Commit: harness commits atomically (overrides AGENTS rule 8 — no git commands run).

**Documentation:** README.md verified consistent in full (the phase's favicon paragraph is accurate; all commands, env vars, routes, ports, scripts cross-checked) — no changes needed. AGENTS.md verified consistent — no changes needed.

**Notable:** `image-1.png` (owner's TODO screenshot, referenced by phase 176's overview) sits untracked at the repo root — pre-existing, explicitly out of scope per the phase-176 report; left untouched.

**Next pending phase:** none — 177 was the last; TODO.md is cleared.
2026-10-11 15:33:39 -04:00

149 lines
7.1 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="description" content="Edit a saved chat answer before it is committed to the docs repository (admin-only).">
<title>Edit doc · Brain of Reese</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="stylesheet" href="/assets/styles.css">
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<!-- Phase 59 task 06: the SLIM header — this is a flow page (the
login.html / shared.html minimal-flow-page lineage), not one of
the app's pages: no nav, no auth pair, no hamburger. Brand +
the "← Back to chat" link are the whole chrome. -->
<header class="app-header">
<div class="container header-inner">
<span class="brand">
<svg class="brand-mark" aria-hidden="true" viewBox="0 0 64 64"><path d="M32 4 55 18v28L32 60 9 46V18Z" stroke-width="4" stroke-linejoin="round"/><circle cx="32" cy="32" r="6.5"/><path d="M32 25.5V16M32 48v-9.5M25.5 32H16M48 32h-9.5" stroke-width="3" stroke-linecap="round"/></svg>
<span class="brand-text">Brain of <strong>Reese</strong></span>
</span>
<a class="doc-edit-back" href="/">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 19-7-7 7-7"/></svg>
<span>Back to chat</span>
</a>
</div>
</header>
<main id="main" class="app-main" tabindex="-1">
<div class="container doc-edit-shell">
<!-- The 46rem base column (a FORM column — it hard-codes 46rem,
it does not ride --chat-column, so phase 58's wide-desktop
doubling never stretches the form). -->
<div class="page-head">
<h1>Edit doc</h1>
<p class="page-sub">
Review the saved answer, adjust anything, then push it to the
docs branch — the commit lands in the configured docs repo;
you open the PR yourself.
</p>
</div>
<!-- Phase 59 task 06: the admin gate — the EXACT #sources-gate
pattern (phase 16) and the same .sources-gate visual
language (phases 35/50). The page is static; the API is the
authority — the draft endpoints are admin-only regardless,
so a non-admin visitor gets the gate and NO draft data
(doc-edit.js makes no /api/doc-drafts call before whoami
says admin). -->
<section class="sources-gate" id="doc-edit-gate" aria-labelledby="doc-edit-gate-title" hidden>
<div class="sources-gate-glyph" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="10" width="16" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/><circle cx="12" cy="14.5" r="1.4" fill="currentColor" stroke="none"/><path d="M12 16v2"/></svg>
</div>
<h2 id="doc-edit-gate-title">Sign in to edit docs</h2>
<p class="sources-gate-sub">
Saving a chat answer as documentation is admin-only. Chat —
and any document an answer cites — stays open to everyone.
</p>
<a class="sources-gate-link" href="/login.html?next=/doc-edit.html">Sign in</a>
</section>
<!-- SHIPS hidden (anonymous-safe; the gate is what anonymous
visitors see). doc-edit.js reveals it once the cached whoami
says admin, then loads the draft from ?draft=<token> (the
uuid4 token task 05's button navigated with). -->
<div id="doc-edit-content" hidden>
<form id="doc-edit-form">
<label for="draft-title">Title</label>
<input
id="draft-title"
name="title"
type="text"
autocomplete="off"
required
>
<label for="draft-path">In-repo path</label>
<input
id="draft-path"
name="path"
type="text"
autocomplete="off"
required
>
<label for="draft-body">Body — markdown</label>
<textarea id="draft-body" name="body" required></textarea>
<div class="doc-edit-actions">
<button type="submit" id="push-doc-btn">Push to docs branch</button>
<!-- Phase 115 (task 02): the Discard control — this page's
one destructive action. Without it an orphaned draft is
un-deletable forever (the screen's only other action
is the push). confirm() →
DELETE /api/doc-drafts/<token> (admin-gated like the
whole router; the uuid4 token is the screen's
credential) → 204 → back to the chat (the draft has no
other home — no drafts list exists). doc-edit.js owns
the handler; a non-204 outcome lands the #push-error
banner and does NOT navigate. -->
<button type="button" id="discard-draft" class="discard-draft"
title="Delete this draft permanently — this cannot be undone">Discard draft</button>
<a class="doc-edit-back" href="/">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 19-7-7 7-7"/></svg>
<span>Back to chat</span>
</a>
</div>
<!-- §7.4 never-stale: the polite live region carries the
push lifecycle — "Pushing…" while the request is out,
then `Pushed to <branch> — commit <sha7>.` on success.
doc-edit.js owns the text (textContent only). -->
<p class="doc-edit-status" id="push-status" role="status" aria-live="polite"></p>
<!-- The error banner (role=alert), hidden until a load or
push failure: the server's detail (git's stderr,
trimmed to its first meaningful lines) lands here and
the fields are preserved — the fix is an edit, not a
re-type. -->
<div class="doc-edit-error" id="push-error" role="alert" hidden></div>
</form>
</div>
</div>
</main>
<footer class="app-footer">
<div class="container footer-inner">
<span class="footer-text">Powered by self-hosted models</span>
</div>
</footer>
<!-- Phase 39: the brand layer — classic script, first on the page:
window.BOR_BRAND at parse time, refreshed from /api/config.
Phase 59 task 06: the page module loads the shared header module
through its own relative `import "./header.js"` — a hoisted
import evaluated before this body runs (single-evaluation
design: no direct header.js <script> tag; esbuild inlines it
into the page bundle in the image build). On this slim flow
page the import is the cached whoami (fetchIsAdmin) the admin
gate runs on. -->
<script src="assets/brand.js"></script>
<script type="module" src="/assets/doc-edit.js"></script>
</body>
</html>