chore: remove design scaffolding
commit
d53ff40chore: remove design scaffolding
Reviews
No reviews of this commit yet — record a verdict below.
Start a review
design_handoff_git_ents/Git Ents.dc.html
@@ -1,895 +1,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8">
-<meta name="viewport" content="width=device-width, initial-scale=1">
-<script src="./support.js"></script>
-</head>
-<body>
-<x-dc>
-<helmet>
-<link rel="preconnect" href="https://fonts.googleapis.com">
-<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
-<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Lora:wght@500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
-<style>
-:root{
- --color-bg:#faf8f4; --color-surface:#ffffff; --color-text:#2a2518; --color-text-muted:#8a7e6a;
- --color-link:#b07d10; --color-link-hover:#96690a; --color-border:#ede9de; --color-code-bg:#f5f3eb;
- --color-accent:#b07d10; --color-accent-subtle:#b07d100f;
- --shadow-sm:0 1px 3px #0000000d; --shadow-md:0 4px 16px #0000000f;
- --radius-sm:10px; --radius-pill:100px;
- --kw:#9d0006; --fn:#427b58; --ty:#b57614; --str:#79740e; --num:#8f3f71; --cmt:#9c8f74; --prop:#076678;
- --diff-add:#4e9a0622; --diff-rem:#cc241d22;
- --font-body:"DM Sans",system-ui,sans-serif; --font-display:"Lora",Georgia,serif; --font-mono:"IBM Plex Mono",ui-monospace,Menlo,monospace;
-}
-@media (prefers-color-scheme: dark){:root{
- --color-bg:#171510; --color-surface:#211f17; --color-text:#ede8d8; --color-text-muted:#a89e88;
- --color-link:#d4a030; --color-link-hover:#e4b850; --color-border:#383324; --color-code-bg:#211f17;
- --color-accent:#d4a030; --color-accent-subtle:#d4a03012;
- --shadow-sm:0 1px 3px #00000040; --shadow-md:0 4px 16px #0000004d;
- --kw:#fb4934; --fn:#8ec07c; --ty:#fabd2f; --str:#b8bb26; --num:#d3869b; --cmt:#928374; --prop:#83a598;
- --diff-add:#b8bb2620; --diff-rem:#fb493420;
-}}
-*{box-sizing:border-box}
-html,body{margin:0;padding:0}
-body{background:var(--color-bg);color:var(--color-text);font-family:var(--font-body);font-size:17px;line-height:1.7;-webkit-font-smoothing:antialiased;background-image:radial-gradient(58rem 30rem at 50% -10rem, var(--color-accent-subtle), transparent 72%);background-repeat:no-repeat;background-attachment:fixed}
-a{color:inherit;text-decoration:none}
-::selection{background:var(--color-accent-subtle)}
-input,textarea,select,button{font-family:inherit}
-@keyframes spin{to{transform:rotate(360deg)}}
-@keyframes fadein{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}
-::-webkit-scrollbar{height:9px;width:9px}
-::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:100px}
-::-webkit-scrollbar-track{background:transparent}
-</style>
-</helmet>
-
-<div style="min-height:100vh">
-
- <!-- ============ STICKY NAV ============ -->
- <header style="position:sticky;top:0;z-index:50;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);background:color-mix(in srgb, var(--color-bg) 82%, transparent);border-bottom:1px solid var(--color-border)">
- <div style="max-width:78rem;margin:0 auto;padding:0 1.5rem;height:58px;display:flex;align-items:center;gap:18px">
- <a href="#" style="display:flex;align-items:center;gap:9px;font-family:var(--font-mono);font-weight:600;font-size:.95rem;letter-spacing:-.01em;white-space:nowrap;flex:none" style-hover="{ color:'var(--color-accent)' }">
- <svg width="19" height="19" viewBox="0 0 16 16" fill="currentColor" style="color:var(--color-accent)"><path d="M8 1L2 4.2v4.3c0 3.4 2.5 5.6 6 6.5 3.5-.9 6-3.1 6-6.5V4.2L8 1zm0 1.5l4.5 2.4v3.6c0 2.5-1.8 4.2-4.5 5-2.7-.8-4.5-2.5-4.5-5V4.9L8 2.5z"/><circle cx="8" cy="7" r="1.6"/></svg>
- git-ents
- </a>
- <div style="flex:1;max-width:24rem;position:relative;display:flex;align-items:center">
- <svg width="14" height="14" viewBox="0 0 16 16" fill="var(--color-text-muted)" style="position:absolute;left:11px;pointer-events:none"><path d="M10.7 9.6a5 5 0 10-1.1 1.1l3 3 1.1-1.1-3-3zM2.5 6.5a3.5 3.5 0 117 0 3.5 3.5 0 01-7 0z"/></svg>
- <input placeholder="Jump to file or symbol" style="width:100%;padding:7px 11px 7px 32px;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);font-size:.82rem;color:var(--color-text);outline:none" style-focus="{ borderColor:'var(--color-accent)' }"/>
- </div>
- <nav style="display:flex;align-items:center;gap:18px;font-size:.85rem;color:var(--color-text-muted)">
- <a href="#" style-hover="{ color:'var(--color-accent)' }">Explore</a>
- <a href="#" style-hover="{ color:'var(--color-accent)' }">Docs</a>
- <div style="width:30px;height:30px;border-radius:100px;background:var(--color-accent-subtle);border:1px solid var(--color-border);display:flex;align-items:center;justify-content:center;font-family:var(--font-mono);font-size:.72rem;font-weight:600;color:var(--color-accent)">el</div>
- </nav>
- </div>
- </header>
-
- <!-- ============ REPO HEADER ============ -->
- <div style="max-width:78rem;margin:0 auto;padding:0 1.5rem">
- <div style="padding:26px 0 0">
- <div style="display:flex;align-items:flex-start;justify-content:space-between;gap:20px;flex-wrap:wrap">
- <div>
- <div style="display:flex;align-items:center;gap:9px;font-family:var(--font-mono);font-size:1.18rem;letter-spacing:-.01em">
- <svg width="18" height="18" viewBox="0 0 16 16" fill="var(--color-text-muted)"><path d="M2 3.5C2 2.7 2.7 2 3.5 2h3l1.5 1.5h4.5c.8 0 1.5.7 1.5 1.5v7c0 .8-.7 1.5-1.5 1.5h-9C2.7 13.5 2 12.8 2 12V3.5z"/></svg>
- <a href="#" style="color:var(--color-text-muted)" style-hover="{ color:'var(--color-accent)' }">git-ents</a>
- <span style="color:var(--color-border)">/</span>
- <a href="#" style="font-weight:600;color:var(--color-accent)">git-ents</a>
- <span style="display:inline-flex;align-items:center;gap:5px;margin-left:4px;padding:2px 10px;font-size:.7rem;font-weight:500;color:var(--color-accent);background:var(--color-accent-subtle);border:1px solid var(--color-accent);border-radius:var(--radius-pill)">
- <svg width="11" height="11" viewBox="0 0 16 16" fill="currentColor"><path d="M11.5 3.5a2 2 0 10-2.7 1.9v1.3c0 .6-.5 1.1-1.1 1.1H5.6a2.5 2.5 0 00-1.1.3V5.4a2 2 0 10-1.5 0v5.2a2 2 0 101.6 0V9.5c0-.6.5-1 1-1h2.1A2.6 2.6 0 0010.3 6V5.4a2 2 0 001.2-1.9zM3.75 3.5a.75.75 0 110 1.5.75.75 0 010-1.5zm0 9a.75.75 0 110-1.5.75.75 0 010 1.5zm5.75-9a.75.75 0 110 1.5.75.75 0 010-1.5z"/></svg>
- main
- </span>
- <span style="padding:2px 10px;font-size:.7rem;color:var(--color-text-muted);background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-pill)">Public</span>
- </div>
- <p style="margin:11px 0 0;max-width:40rem;color:var(--color-text);font-size:.98rem">A warm, editorial Git forge — serve your repositories as paper-grain HTML. Readable, fast, and quiet, with no client-side JavaScript required.</p>
- <div style="display:flex;flex-wrap:wrap;gap:7px;margin-top:13px">
- <span style="padding:3px 11px;font-family:var(--font-mono);font-size:.72rem;color:var(--color-link);background:var(--color-accent-subtle);border-radius:var(--radius-pill)">rust</span>
- <span style="padding:3px 11px;font-family:var(--font-mono);font-size:.72rem;color:var(--color-link);background:var(--color-accent-subtle);border-radius:var(--radius-pill)">git-forge</span>
- <span style="padding:3px 11px;font-family:var(--font-mono);font-size:.72rem;color:var(--color-link);background:var(--color-accent-subtle);border-radius:var(--radius-pill)">self-hosted</span>
- <span style="padding:3px 11px;font-family:var(--font-mono);font-size:.72rem;color:var(--color-link);background:var(--color-accent-subtle);border-radius:var(--radius-pill)">html-first</span>
- </div>
- </div>
- <div style="display:flex;gap:9px">
- <button style="display:flex;align-items:center;gap:7px;padding:7px 13px;font-size:.8rem;font-weight:500;color:var(--color-text);background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);cursor:pointer;box-shadow:var(--shadow-sm)" style-hover="{ borderColor:'var(--color-accent)' }">
- <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"/><path d="M8 2C4.5 2 1.7 4.4 1 7.9c.7 3.5 3.5 5.9 7 5.9s6.3-2.4 7-5.9C14.3 4.4 11.5 2 8 2zm0 10c-2.6 0-4.9-1.7-5.6-4.1C3.1 5.4 5.4 3.7 8 3.7s4.9 1.7 5.6 4.2C12.9 10.3 10.6 12 8 12z"/></svg>
- Watch
- </button>
- <button style="display:flex;align-items:center;gap:7px;padding:7px 13px;font-size:.8rem;font-weight:500;color:var(--color-text);background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);cursor:pointer;box-shadow:var(--shadow-sm)" style-hover="{ borderColor:'var(--color-accent)' }">
- <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1.5l1.9 3.9 4.3.6-3.1 3 .7 4.3L8 11.3 4.2 13.3l.7-4.3L1.8 6l4.3-.6L8 1.5z"/></svg>
- Star <span style="font-family:var(--font-mono);color:var(--color-text-muted)">128</span>
- </button>
- </div>
- </div>
-
- <!-- TAB BAR -->
- <div style="display:flex;gap:2px;margin-top:22px;border-bottom:1px solid var(--color-border)">
- <sc-for list="{{ tabs }}" as="t" hint-placeholder-count="6">
- <button onClick="{{ t.onClick }}" style="{{ t.style }}" style-hover="{ color:'var(--color-text)' }">
- <span>{{ t.label }}</span>
- <sc-if value="{{ t.showCount }}"><span style="padding:1px 8px;font-family:var(--font-mono);font-size:.68rem;color:var(--color-text-muted);background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-pill)">{{ t.count }}</span></sc-if>
- <sc-if value="{{ t.dot }}"><span style="{{ t.dotStyle }}"></span></sc-if>
- <sc-if value="{{ t.active }}"><span style="position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--color-accent);border-radius:2px"></span></sc-if>
- </button>
- </sc-for>
- </div>
- </div>
-
- <!-- ============ TAB CONTENT ============ -->
- <main style="padding:30px 0 80px">
-
- <!-- ===== OVERVIEW ===== -->
- <sc-if value="{{ isOverview }}" hint-placeholder-val="{{ true }}">
- <div style="display:grid;grid-template-columns:1fr 19rem;gap:34px;align-items:start">
- <!-- README CARD -->
- <article style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);overflow:hidden">
- <div style="display:flex;align-items:center;gap:8px;padding:11px 18px;background:var(--color-code-bg);border-bottom:1px solid var(--color-border);font-family:var(--font-mono);font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted)">
- <svg width="13" height="13" viewBox="0 0 16 16" fill="currentColor"><path d="M3 1.5h6L13 5v9.5H3V1.5zm5.5 1H4v11h8V5.7L8.5 2.5z"/></svg>
- README.md
- </div>
- <div style="padding:40px 48px 52px;max-width:44rem">
- <h1 style="font-family:var(--font-display);font-weight:700;font-size:2.4rem;letter-spacing:-.02em;margin:0 0 6px;line-height:1.15">git‑ents</h1>
- <p style="font-family:var(--font-display);font-style:italic;font-size:1.18rem;color:var(--color-text-muted);margin:0 0 22px">An editorial Git forge for people who love to read their code.</p>
- <div style="display:flex;flex-wrap:wrap;gap:7px;margin-bottom:30px">
- <span style="display:inline-flex;align-items:center;gap:5px;padding:3px 11px;font-family:var(--font-mono);font-size:.7rem;color:var(--fn);background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-pill)"><span style="width:7px;height:7px;border-radius:100px;background:var(--fn)"></span>build passing</span>
- <span style="padding:3px 11px;font-family:var(--font-mono);font-size:.7rem;color:var(--ty);background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-pill)">v0.7.0</span>
- <span style="padding:3px 11px;font-family:var(--font-mono);font-size:.7rem;color:var(--prop);background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-pill)">MIT</span>
- <span style="padding:3px 11px;font-family:var(--font-mono);font-size:.7rem;color:var(--color-text-muted);background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-pill)">rust 1.78+</span>
- </div>
- <p style="margin:0 0 26px">git-ents renders a repository as quiet, server-side HTML — a README that reads like a page, a file tree you can wander, releases as a changelog, and CI defined entirely with the Git hooks you already know. The whole UI is paper-grain and works without a single byte of client JavaScript.</p>
-
- <h2 style="font-family:var(--font-display);font-weight:600;font-size:1.4rem;letter-spacing:-.01em;margin:34px 0 4px;position:relative">Why git‑ents</h2>
- <div style="width:3rem;height:2px;background:var(--color-accent);margin-bottom:16px"></div>
- <ul style="margin:0 0 12px;padding-left:1.2rem">
- <li style="margin-bottom:7px"><strong>Readable by default.</strong> Lora display type, generous measure, warm paper palette — light and dark are equals.</li>
- <li style="margin-bottom:7px"><strong>Hooks, not pipelines.</strong> CI is just <code style="font-family:var(--font-mono);font-size:.85em;background:var(--color-code-bg);padding:1px 5px;border-radius:5px">pre-receive</code> and <code style="font-family:var(--font-mono);font-size:.85em;background:var(--color-code-bg);padding:1px 5px;border-radius:5px">post-receive</code> scripts, with run history and logs.</li>
- <li style="margin-bottom:7px"><strong>No JavaScript.</strong> Every page is fully server-rendered; the browser just paints.</li>
- <li><strong>One binary.</strong> Point it at a directory of bare repos and serve.</li>
- </ul>
-
- <h2 style="font-family:var(--font-display);font-weight:600;font-size:1.4rem;letter-spacing:-.01em;margin:34px 0 4px">Install</h2>
- <div style="width:3rem;height:2px;background:var(--color-accent);margin-bottom:16px"></div>
- <pre style="margin:0 0 20px;padding:15px 18px;background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-sm);overflow-x:auto;font-family:var(--font-mono);font-size:.82rem;line-height:1.7;color:var(--color-text)"><span style="color:var(--color-text-muted)">$</span> cargo install git-ents
-<span style="color:var(--color-text-muted)">$</span> git-ents serve ./repos <span style="color:var(--cmt)"># http://localhost:8420</span></pre>
-
- <h2 style="font-family:var(--font-display);font-weight:600;font-size:1.4rem;letter-spacing:-.01em;margin:34px 0 4px">A taste of the renderer</h2>
- <div style="width:3rem;height:2px;background:var(--color-accent);margin-bottom:16px"></div>
- <pre style="margin:0 0 8px;padding:16px 18px;background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-sm);overflow-x:auto;font-family:var(--font-mono);font-size:.82rem;line-height:1.6"><span style="color:var(--cmt)">// render any blob as warm, highlighted HTML</span>
-<span style="color:var(--kw)">pub fn</span> <span style="color:var(--fn)">render_blob</span>(blob: &<span style="color:var(--ty)">Blob</span>) -> <span style="color:var(--ty)">Markup</span> {
- <span style="color:var(--kw)">let</span> lang = <span style="color:var(--ty)">Language</span>::<span style="color:var(--fn)">guess</span>(&blob.path);
- html! {
- pre.paper { (<span style="color:var(--fn)">highlight</span>(&blob.text, lang)) }
- }
-}</pre>
-
- <h2 style="font-family:var(--font-display);font-weight:600;font-size:1.4rem;letter-spacing:-.01em;margin:34px 0 4px">License</h2>
- <div style="width:3rem;height:2px;background:var(--color-accent);margin-bottom:16px"></div>
- <p style="margin:0">Released under the MIT License. Grow something with it.</p>
- </div>
- </article>
-
- <!-- ASIDE -->
- <aside style="display:flex;flex-direction:column;gap:18px;position:sticky;top:78px">
- <!-- Clone -->
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);overflow:hidden">
- <div style="padding:10px 15px;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted);border-bottom:1px solid var(--color-border)">Clone</div>
- <div style="padding:14px 15px">
- <div style="display:flex;gap:7px">
- <input readonly value="git.ents.dev/git-ents/git-ents.git" style="flex:1;min-width:0;padding:7px 10px;font-family:var(--font-mono);font-size:.74rem;color:var(--color-text);background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:8px;outline:none"/>
- <button onClick="{{ onClone }}" title="Copy clone URL" style="flex:none;width:34px;display:flex;align-items:center;justify-content:center;background:var(--color-surface);border:1px solid var(--color-border);border-radius:8px;cursor:pointer;color:var(--color-text-muted)" style-hover="{ borderColor:'var(--color-accent)', color:'var(--color-accent)' }">
- <sc-if value="{{ cloneCopied }}" hint-placeholder-val="{{ false }}"><svg width="14" height="14" viewBox="0 0 16 16" fill="var(--fn)"><path d="M13.5 4.5l-7 7L3 8l1-1 2.5 2.5 6-6 1 1z"/></svg></sc-if>
- <sc-if value="{{ notCloneCopied }}" hint-placeholder-val="{{ true }}"><svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M4 2h6.5L13 4.5V11H4V2zm1 1v7h7V5.2L9.8 3H5z"/><path d="M3 5H2v9h8v-1H3V5z"/></svg></sc-if>
- </button>
- </div>
- </div>
- </div>
- <!-- About -->
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm)">
- <div style="padding:10px 15px;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted);border-bottom:1px solid var(--color-border)">About</div>
- <div style="padding:14px 15px;font-size:.84rem;color:var(--color-text-muted);display:flex;flex-direction:column;gap:9px">
- <div style="display:flex;align-items:center;gap:8px"><svg width="14" height="14" viewBox="0 0 16 16" fill="var(--ty)"><circle cx="8" cy="8" r="6"/></svg><span>Rust · MIT licensed</span></div>
- <div style="display:flex;align-items:center;gap:8px"><svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1.5l1.9 3.9 4.3.6-3.1 3 .7 4.3L8 11.3 4.2 13.3l.7-4.3L1.8 6l4.3-.6L8 1.5z"/></svg><span>128 stars · 9 forks</span></div>
- <div style="display:flex;align-items:center;gap:8px"><svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M8 3.5V8l3 1.8.5-.8L9 7.5V3.5H8z"/><path d="M8 1.5A6.5 6.5 0 108 14.5 6.5 6.5 0 008 1.5zm0 1.5a5 5 0 110 10 5 5 0 010-10z"/></svg><span>Updated 3 hours ago</span></div>
- </div>
- </div>
- <!-- Releases preview -->
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm)">
- <div style="display:flex;align-items:center;justify-content:space-between;padding:10px 15px;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted);border-bottom:1px solid var(--color-border)"><span>Releases</span><span style="color:var(--color-accent)">4</span></div>
- <div style="padding:14px 15px">
- <div style="display:flex;align-items:center;gap:8px;margin-bottom:3px"><svg width="14" height="14" viewBox="0 0 16 16" fill="var(--color-accent)"><path d="M1.5 8L8 1.5h5.5V7L7 13.5 1.5 8zm10-4a1 1 0 100 2 1 1 0 000-2z"/></svg><span style="font-family:var(--font-mono);font-size:.82rem;font-weight:600;color:var(--color-accent)">v0.7.0</span><span style="padding:1px 7px;font-size:.62rem;color:var(--fn);background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-pill)">Latest</span></div>
- <p style="margin:0;font-size:.78rem;color:var(--color-text-muted)">Paper Trail — released 9 days ago</p>
- </div>
- </div>
- <!-- Languages -->
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm)">
- <div style="padding:10px 15px;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted);border-bottom:1px solid var(--color-border)">Languages</div>
- <div style="padding:14px 15px">
- <div style="display:flex;height:7px;border-radius:100px;overflow:hidden;margin-bottom:11px"><div style="width:86%;background:var(--ty)"></div><div style="width:9%;background:var(--fn)"></div><div style="width:5%;background:var(--prop)"></div></div>
- <div style="display:flex;flex-wrap:wrap;gap:11px;font-size:.76rem;color:var(--color-text-muted)">
- <span style="display:flex;align-items:center;gap:5px"><span style="width:8px;height:8px;border-radius:100px;background:var(--ty)"></span>Rust 86%</span>
- <span style="display:flex;align-items:center;gap:5px"><span style="width:8px;height:8px;border-radius:100px;background:var(--fn)"></span>HTML 9%</span>
- <span style="display:flex;align-items:center;gap:5px"><span style="width:8px;height:8px;border-radius:100px;background:var(--prop)"></span>CSS 5%</span>
- </div>
- </div>
- </div>
- </aside>
- </div>
- </sc-if>
-
- <!-- ===== FILES ===== -->
- <sc-if value="{{ isFiles }}" hint-placeholder-val="{{ false }}">
- <div style="display:grid;grid-template-columns:17rem 1fr;gap:0;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);overflow:hidden;min-height:30rem">
- <!-- TREE -->
- <div style="border-right:1px solid var(--color-border);background:var(--color-bg)">
- <div style="display:flex;align-items:center;gap:7px;padding:11px 15px;border-bottom:1px solid var(--color-border);font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted)">
- <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><path d="M11.5 3.5a2 2 0 10-2.7 1.9v1.3c0 .6-.5 1.1-1.1 1.1H5.6a2.5 2.5 0 00-1.1.3V5.4a2 2 0 10-1.5 0v5.2a2 2 0 101.6 0V9.5c0-.6.5-1 1-1h2.1A2.6 2.6 0 0010.3 6V5.4a2 2 0 001.2-1.9z"/></svg>
- main
- </div>
- <div style="padding:7px 6px">
- <sc-for list="{{ tree }}" as="n1" hint-placeholder-count="7">
- <button onClick="{{ n1.onClick }}" style="{{ n1.style }}" style-hover="{ background:'var(--color-code-bg)' }">
- <sc-if value="{{ n1.isDir }}" hint-placeholder-val="{{ true }}"><span style="{{ n1.chevStyle }}"><svg width="11" height="11" viewBox="0 0 16 16" fill="currentColor"><path d="M6 4l5 4-5 4V4z"/></svg></span><svg width="14" height="14" viewBox="0 0 16 16" fill="var(--color-accent)"><path d="M1.5 4c0-.6.4-1 1-1h3l1.2 1.2H13c.6 0 1 .4 1 1V12c0 .6-.4 1-1 1H2.5c-.6 0-1-.4-1-1V4z"/></svg></sc-if>
- <sc-if value="{{ n1.isFile }}" hint-placeholder-val="{{ false }}"><span style="width:11px;flex:none"></span><svg width="13" height="13" viewBox="0 0 16 16" fill="var(--color-text-muted)"><path d="M3 1.5h6L13 5v9.5H3V1.5zm5.5 1H4v11h8V5.7L8.5 2.5z"/></svg></sc-if>
- <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ n1.name }}</span>
- </button>
- <sc-if value="{{ n1.open }}" hint-placeholder-val="{{ false }}">
- <sc-for list="{{ n1.kids }}" as="n2" hint-placeholder-count="3">
- <button onClick="{{ n2.onClick }}" style="{{ n2.style }}" style-hover="{ background:'var(--color-code-bg)' }">
- <sc-if value="{{ n2.isDir }}" hint-placeholder-val="{{ true }}"><span style="{{ n2.chevStyle }}"><svg width="11" height="11" viewBox="0 0 16 16" fill="currentColor"><path d="M6 4l5 4-5 4V4z"/></svg></span><svg width="14" height="14" viewBox="0 0 16 16" fill="var(--color-accent)"><path d="M1.5 4c0-.6.4-1 1-1h3l1.2 1.2H13c.6 0 1 .4 1 1V12c0 .6-.4 1-1 1H2.5c-.6 0-1-.4-1-1V4z"/></svg></sc-if>
- <sc-if value="{{ n2.isFile }}" hint-placeholder-val="{{ false }}"><span style="width:11px;flex:none"></span><svg width="13" height="13" viewBox="0 0 16 16" fill="var(--color-text-muted)"><path d="M3 1.5h6L13 5v9.5H3V1.5zm5.5 1H4v11h8V5.7L8.5 2.5z"/></svg></sc-if>
- <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ n2.name }}</span>
- </button>
- <sc-if value="{{ n2.open }}" hint-placeholder-val="{{ false }}">
- <sc-for list="{{ n2.kids }}" as="n3" hint-placeholder-count="3">
- <button onClick="{{ n3.onClick }}" style="{{ n3.style }}" style-hover="{ background:'var(--color-code-bg)' }">
- <span style="width:11px;flex:none"></span><svg width="13" height="13" viewBox="0 0 16 16" fill="var(--color-text-muted)"><path d="M3 1.5h6L13 5v9.5H3V1.5zm5.5 1H4v11h8V5.7L8.5 2.5z"/></svg>
- <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ n3.name }}</span>
- </button>
- </sc-for>
- </sc-if>
- </sc-for>
- </sc-if>
- </sc-for>
- </div>
- </div>
- <!-- BLOB -->
- <div style="min-width:0;display:flex;flex-direction:column">
- <div style="display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;background:var(--color-code-bg);border-bottom:1px solid var(--color-border)">
- <div style="font-family:var(--font-mono);font-size:.78rem;color:var(--color-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ currentPath }}</div>
- <div style="display:flex;align-items:center;gap:14px;flex:none">
- <span style="font-family:var(--font-mono);font-size:.7rem;color:var(--color-text-muted)">{{ lineCount }} lines · {{ currentSize }}</span>
- <button onClick="{{ onCopyFile }}" style="display:flex;align-items:center;gap:5px;padding:4px 10px;font-size:.72rem;color:var(--color-text-muted);background:var(--color-surface);border:1px solid var(--color-border);border-radius:7px;cursor:pointer" style-hover="{ borderColor:'var(--color-accent)', color:'var(--color-accent)' }">
- <sc-if value="{{ fileCopied }}" hint-placeholder-val="{{ false }}"><svg width="12" height="12" viewBox="0 0 16 16" fill="var(--fn)"><path d="M13.5 4.5l-7 7L3 8l1-1 2.5 2.5 6-6 1 1z"/></svg>Copied</sc-if>
- <sc-if value="{{ notFileCopied }}" hint-placeholder-val="{{ true }}"><svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><path d="M4 2h6.5L13 4.5V11H4V2zm1 1v7h7V5.2L9.8 3H5z"/><path d="M3 5H2v9h8v-1H3V5z"/></svg>Copy</sc-if>
- </button>
- </div>
- </div>
- <div style="display:grid;grid-template-columns:auto 1fr;overflow:auto;flex:1">
- <div style="padding:14px 0;background:var(--color-code-bg);border-right:1px solid var(--color-border);text-align:right;user-select:none">
- <sc-for list="{{ lines }}" as="ln" hint-placeholder-count="12">
- <div style="padding:0 14px;font-family:var(--font-mono);font-size:.78rem;line-height:1.55;color:var(--color-text-muted);opacity:.6">{{ ln.n }}</div>
- </sc-for>
- </div>
- <div style="padding:14px 0;overflow-x:auto">
- <sc-for list="{{ lines }}" as="ln" hint-placeholder-count="12">
- <div style="padding:0 18px;font-family:var(--font-mono);font-size:.78rem;line-height:1.55;white-space:pre;color:var(--color-text);min-height:1.21em">{{ ln.t }}</div>
- </sc-for>
- </div>
- </div>
- </div>
- </div>
- </sc-if>
-
- <!-- ===== RELEASES ===== -->
- <sc-if value="{{ isReleases }}" hint-placeholder-val="{{ false }}">
- <div style="margin-bottom:24px">
- <h1 style="font-family:var(--font-display);font-weight:700;font-size:1.5rem;letter-spacing:-.01em;margin:0 0 4px">Releases</h1>
- <div style="width:3rem;height:2px;background:var(--color-accent)"></div>
- </div>
- <div style="position:relative;padding-left:30px">
- <div style="position:absolute;left:5px;top:6px;bottom:6px;width:2px;background:var(--color-border)"></div>
- <sc-for list="{{ releases }}" as="r" hint-placeholder-count="4">
- <div style="position:relative;margin-bottom:30px">
- <span style="{{ r.dotStyle }}"></span>
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);overflow:hidden">
- <div style="display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:14px 20px;border-bottom:1px solid var(--color-border)">
- <div style="display:flex;align-items:center;gap:11px">
- <svg width="16" height="16" viewBox="0 0 16 16" fill="var(--color-accent)"><path d="M1.5 8L8 1.5h5.5V7L7 13.5 1.5 8zm10-4a1 1 0 100 2 1 1 0 000-2z"/></svg>
- <span style="font-family:var(--font-mono);font-size:1rem;font-weight:600;color:var(--color-accent)">{{ r.tag }}</span>
- <span style="font-family:var(--font-display);font-size:1.05rem;color:var(--color-text)">{{ r.name }}</span>
- <sc-if value="{{ r.latest }}" hint-placeholder-val="{{ false }}"><span style="padding:2px 9px;font-size:.64rem;font-weight:500;color:var(--fn);background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-pill)">Latest</span></sc-if>
- </div>
- <span style="font-size:.78rem;color:var(--color-text-muted)">{{ r.date }}</span>
- </div>
- <div style="padding:18px 20px">
- <sc-for list="{{ r.sections }}" as="s" hint-placeholder-count="2">
- <div style="margin-bottom:13px">
- <div style="font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:{{ s.color }};margin-bottom:5px">{{ s.label }}</div>
- <ul style="margin:0;padding-left:1.1rem">
- <sc-for list="{{ s.items }}" as="it" hint-placeholder-count="2">
- <li style="font-size:.88rem;margin-bottom:3px">{{ it }}</li>
- </sc-for>
- </ul>
- </div>
- </sc-for>
- <sc-if value="{{ r.hasDiff }}" hint-placeholder-val="{{ false }}">
- <div style="margin:6px 0 13px;border:1px solid var(--color-border);border-radius:8px;overflow:hidden;font-family:var(--font-mono);font-size:.76rem;line-height:1.6">
- <div style="padding:3px 14px;background:var(--diff-add);color:var(--color-text)">+ render release notes with the Lora display face</div>
- <div style="padding:3px 14px;background:var(--diff-rem);color:var(--color-text)">- render release notes with the body sans</div>
- </div>
- </sc-if>
- <div style="display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:14px;padding-top:13px;border-top:1px solid var(--color-border)">
- <sc-for list="{{ r.assets }}" as="a" hint-placeholder-count="2">
- <a href="#" style="display:flex;align-items:center;gap:6px;font-family:var(--font-mono);font-size:.74rem;color:var(--color-link)" style-hover="{ color:'var(--color-link-hover)' }">
- <svg width="13" height="13" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1.5v7.8l2.5-2.5 1 1L8 12.3 4.5 8.8l1-1L8 9.3V1.5H8zM3 13h10v1.2H3V13z"/></svg>
- {{ a.name }} <span style="color:var(--color-text-muted)">{{ a.size }}</span>
- </a>
- </sc-for>
- <span style="display:flex;align-items:center;gap:6px;margin-left:auto;font-family:var(--font-mono);font-size:.72rem;color:var(--color-text-muted)">
- <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><circle cx="5" cy="8" r="2"/><circle cx="11" cy="8" r="2" fill="none" stroke="currentColor" stroke-width="1.4"/></svg>
- {{ r.sha }}
- </span>
- </div>
- </div>
- </div>
- </div>
- </sc-for>
- </div>
- </sc-if>
-
- <!-- ===== HOOKS ===== -->
- <sc-if value="{{ isHooks }}" hint-placeholder-val="{{ false }}">
- <div style="margin-bottom:22px">
- <h1 style="font-family:var(--font-display);font-weight:700;font-size:1.5rem;letter-spacing:-.01em;margin:0 0 4px">Hooks</h1>
- <div style="width:3rem;height:2px;background:var(--color-accent)"></div>
- <p style="margin:12px 0 0;max-width:38rem;font-size:.9rem;color:var(--color-text-muted)">Continuous integration defined as plain Git hooks. Each push runs the stages in <code style="font-family:var(--font-mono);font-size:.85em;background:var(--color-code-bg);padding:1px 5px;border-radius:5px">.gitents/hooks.toml</code> — no YAML, no marketplace.</p>
- </div>
- <div style="display:grid;grid-template-columns:20rem 1fr;gap:20px;align-items:start">
- <!-- run list -->
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);overflow:hidden">
- <div style="padding:10px 15px;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted);border-bottom:1px solid var(--color-border)">Recent runs</div>
- <sc-for list="{{ runs }}" as="run" hint-placeholder-count="4">
- <button onClick="{{ run.onClick }}" style="{{ run.style }}" style-hover="{ background:'var(--color-code-bg)' }">
- <span style="{{ run.iconWrap }}">
- <sc-if value="{{ run.isRunning }}" hint-placeholder-val="{{ false }}"><svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="var(--ty)" stroke-width="1.6" style="animation:spin 1s linear infinite"><path d="M8 2a6 6 0 106 6" stroke-linecap="round"/></svg></sc-if>
- <sc-if value="{{ run.isPassed }}" hint-placeholder-val="{{ true }}"><svg width="13" height="13" viewBox="0 0 16 16" fill="var(--fn)"><path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM7 10.4L4.3 7.7l1-1L7 8.4l3.7-3.7 1 1L7 10.4z"/></svg></sc-if>
- <sc-if value="{{ run.isFailed }}" hint-placeholder-val="{{ false }}"><svg width="13" height="13" viewBox="0 0 16 16" fill="var(--kw)"><path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zm2.5 8.4l-1 1L8 9l-1.5 1.5-1-1L7 8 5.5 6.5l1-1L8 7l1.5-1.5 1 1L9 8l1.5 1.5z"/></svg></sc-if>
- </span>
- <span style="min-width:0;flex:1;text-align:left">
- <span style="display:block;font-size:.82rem;color:var(--color-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ run.subject }}</span>
- <span style="display:block;font-family:var(--font-mono);font-size:.68rem;color:var(--color-text-muted);margin-top:1px">#{{ run.id }} · {{ run.branch }} · {{ run.trigger }}</span>
- </span>
- <span style="flex:none;font-family:var(--font-mono);font-size:.66rem;color:var(--color-text-muted);text-align:right">{{ run.age }}</span>
- </button>
- </sc-for>
- </div>
- <!-- run detail -->
- <div style="display:flex;flex-direction:column;gap:18px;min-width:0">
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);overflow:hidden">
- <div style="display:flex;align-items:center;gap:11px;flex-wrap:wrap;padding:15px 20px;border-bottom:1px solid var(--color-border)">
- <span style="{{ cur.iconWrap }}">
- <sc-if value="{{ cur.isRunning }}" hint-placeholder-val="{{ false }}"><svg width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="var(--ty)" stroke-width="1.6" style="animation:spin 1s linear infinite"><path d="M8 2a6 6 0 106 6" stroke-linecap="round"/></svg></sc-if>
- <sc-if value="{{ cur.isPassed }}" hint-placeholder-val="{{ true }}"><svg width="15" height="15" viewBox="0 0 16 16" fill="var(--fn)"><path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM7 10.4L4.3 7.7l1-1L7 8.4l3.7-3.7 1 1L7 10.4z"/></svg></sc-if>
- <sc-if value="{{ cur.isFailed }}" hint-placeholder-val="{{ false }}"><svg width="15" height="15" viewBox="0 0 16 16" fill="var(--kw)"><path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zm2.5 8.4l-1 1L8 9l-1.5 1.5-1-1L7 8 5.5 6.5l1-1L8 7l1.5-1.5 1 1L9 8l1.5 1.5z"/></svg></sc-if>
- </span>
- <span style="font-family:var(--font-display);font-size:1.1rem;color:var(--color-text)">{{ cur.subject }}</span>
- <span style="font-family:var(--font-mono);font-size:.72rem;color:var(--color-text-muted);margin-left:auto">{{ cur.sha }} · {{ cur.duration }}</span>
- </div>
- <!-- stages -->
- <div style="display:flex;flex-wrap:wrap;gap:8px;padding:15px 20px;border-bottom:1px solid var(--color-border)">
- <sc-for list="{{ cur.stages }}" as="st" hint-placeholder-count="4">
- <div style="display:flex;align-items:center;gap:7px;padding:6px 12px;background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-pill)">
- <span style="width:8px;height:8px;border-radius:100px;background:{{ st.color }}"></span>
- <span style="font-family:var(--font-mono);font-size:.74rem;color:var(--color-text)">{{ st.name }}</span>
- <span style="font-family:var(--font-mono);font-size:.68rem;color:var(--color-text-muted)">{{ st.duration }}</span>
- </div>
- </sc-for>
- </div>
- <!-- log -->
- <div style="background:var(--color-code-bg);padding:14px 0;overflow-x:auto">
- <sc-for list="{{ cur.log }}" as="lg" hint-placeholder-count="6">
- <div style="display:flex;gap:14px;padding:0 18px;font-family:var(--font-mono);font-size:.76rem;line-height:1.7;white-space:pre">
- <span style="color:var(--color-text-muted);opacity:.5;user-select:none;min-width:1.4rem;text-align:right">{{ lg.n }}</span>
- <span style="color:{{ lg.color }}">{{ lg.text }}</span>
- </div>
- </sc-for>
- </div>
- </div>
- <!-- config -->
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);overflow:hidden">
- <div style="display:flex;align-items:center;gap:8px;padding:10px 16px;background:var(--color-code-bg);border-bottom:1px solid var(--color-border);font-family:var(--font-mono);font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted)">
- <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><path d="M3 1.5h6L13 5v9.5H3V1.5zm5.5 1H4v11h8V5.7L8.5 2.5z"/></svg>.gitents/hooks.toml
- </div>
- <div style="padding:14px 0;overflow-x:auto">
- <sc-for list="{{ hooksToml }}" as="hl" hint-placeholder-count="10">
- <div style="padding:0 18px;font-family:var(--font-mono);font-size:.78rem;line-height:1.6;white-space:pre;color:{{ hl.color }};min-height:1.25em">{{ hl.t }}</div>
- </sc-for>
- </div>
- </div>
- </div>
- </div>
- </sc-if>
-
- <!-- ===== ISSUES ===== -->
- <sc-if value="{{ isIssues }}" hint-placeholder-val="{{ false }}">
- <div style="display:flex;align-items:flex-end;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:18px">
- <div>
- <h1 style="font-family:var(--font-display);font-weight:700;font-size:1.5rem;letter-spacing:-.01em;margin:0 0 4px;white-space:nowrap">Bug reports</h1>
- <div style="width:3rem;height:2px;background:var(--color-accent)"></div>
- </div>
- <button style="display:flex;align-items:center;gap:7px;padding:8px 15px;font-size:.82rem;font-weight:600;color:var(--color-bg);background:var(--color-accent);border:none;border-radius:var(--radius-sm);cursor:pointer" style-hover="{ background:'var(--color-link-hover)' }">
- <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M7.25 3.5h1.5v3.75H12.5v1.5H8.75V12.5h-1.5V8.75H3.5v-1.5h3.75V3.5z"/></svg>New issue
- </button>
- </div>
- <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px">
- <div style="flex:1;min-width:14rem;position:relative;display:flex;align-items:center">
- <svg width="14" height="14" viewBox="0 0 16 16" fill="var(--color-text-muted)" style="position:absolute;left:12px;pointer-events:none"><path d="M10.7 9.6a5 5 0 10-1.1 1.1l3 3 1.1-1.1-3-3zM2.5 6.5a3.5 3.5 0 117 0 3.5 3.5 0 01-7 0z"/></svg>
- <input value="{{ iq }}" onInput="{{ onSearch }}" placeholder="Search bug reports" style="width:100%;padding:8px 12px 8px 34px;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);font-size:.84rem;color:var(--color-text);outline:none" style-focus="{ borderColor:'var(--color-accent)' }"/>
- </div>
- <div style="display:flex;gap:6px">
- <sc-for list="{{ labelChips }}" as="lc" hint-placeholder-count="4">
- <button onClick="{{ lc.onClick }}" style="{{ lc.style }}">{{ lc.label }}</button>
- </sc-for>
- </div>
- </div>
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);overflow:hidden">
- <div style="display:flex;gap:20px;padding:11px 18px;border-bottom:1px solid var(--color-border);background:var(--color-code-bg)">
- <sc-for list="{{ issueTabs }}" as="ft" hint-placeholder-count="2">
- <button onClick="{{ ft.onClick }}" style="{{ ft.style }}">
- <sc-if value="{{ ft.isOpen }}" hint-placeholder-val="{{ true }}"><svg width="13" height="13" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zm0 1.5a5 5 0 110 10 5 5 0 010-10zm0 2.5a2.5 2.5 0 100 5 2.5 2.5 0 000-5z"/></svg></sc-if>
- <sc-if value="{{ ft.isClosed }}" hint-placeholder-val="{{ false }}"><svg width="13" height="13" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM7 10.4L4.3 7.7l1-1L7 8.4l3.7-3.7 1 1L7 10.4z"/></svg></sc-if>
- {{ ft.label }} <span style="color:var(--color-text-muted)">{{ ft.count }}</span>
- </button>
- </sc-for>
- </div>
- <sc-for list="{{ issues }}" as="iss" hint-placeholder-count="4">
- <div style="display:flex;align-items:flex-start;gap:12px;padding:14px 18px;border-bottom:1px solid var(--color-border)" style-hover="{ background:'var(--color-code-bg)' }">
- <span style="flex:none;margin-top:2px;color:{{ iss.statusColor }}">
- <sc-if value="{{ iss.isOpen }}" hint-placeholder-val="{{ true }}"><svg width="15" height="15" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zm0 1.5a5 5 0 110 10 5 5 0 010-10zm0 2.5a2.5 2.5 0 100 5 2.5 2.5 0 000-5z"/></svg></sc-if>
- <sc-if value="{{ iss.isClosed }}" hint-placeholder-val="{{ false }}"><svg width="15" height="15" viewBox="0 0 16 16" fill="currentColor"><path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM7 10.4L4.3 7.7l1-1L7 8.4l3.7-3.7 1 1L7 10.4z"/></svg></sc-if>
- </span>
- <div style="min-width:0;flex:1">
- <div style="display:flex;align-items:center;gap:9px;flex-wrap:wrap">
- <a href="#" style="font-size:.96rem;font-weight:500;color:var(--color-text)" style-hover="{ color:'var(--color-accent)' }">{{ iss.title }}</a>
- <sc-for list="{{ iss.labels }}" as="lb" hint-placeholder-count="1">
- <span style="padding:1px 9px;font-family:var(--font-mono);font-size:.66rem;color:{{ lb.color }};background:var(--color-code-bg);border:1px solid var(--color-border);border-radius:var(--radius-pill)">{{ lb.name }}</span>
- </sc-for>
- </div>
- <div style="font-size:.76rem;color:var(--color-text-muted);margin-top:3px">#{{ iss.num }} {{ iss.meta }}</div>
- </div>
- <sc-if value="{{ iss.hasComments }}" hint-placeholder-val="{{ false }}">
- <span style="flex:none;display:flex;align-items:center;gap:5px;font-size:.76rem;color:var(--color-text-muted)">
- <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M2 3c0-.6.4-1 1-1h10c.6 0 1 .4 1 1v7c0 .6-.4 1-1 1H6l-3 2.5V11H3c-.6 0-1-.4-1-1V3z"/></svg>{{ iss.comments }}
- </span>
- </sc-if>
- </div>
- </sc-for>
- <sc-if value="{{ noIssues }}" hint-placeholder-val="{{ false }}">
- <div style="padding:40px;text-align:center;color:var(--color-text-muted);font-size:.88rem">No bug reports match these filters.</div>
- </sc-if>
- </div>
- </sc-if>
-
- <!-- ===== SETTINGS ===== -->
- <sc-if value="{{ isSettings }}" hint-placeholder-val="{{ false }}">
- <div style="max-width:46rem">
- <div style="margin-bottom:22px">
- <h1 style="font-family:var(--font-display);font-weight:700;font-size:1.5rem;letter-spacing:-.01em;margin:0 0 4px">Repository settings</h1>
- <div style="width:3rem;height:2px;background:var(--color-accent)"></div>
- </div>
- <!-- General -->
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);margin-bottom:20px">
- <div style="padding:11px 18px;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted);border-bottom:1px solid var(--color-border)">General</div>
- <div style="padding:20px 18px;display:flex;flex-direction:column;gap:17px">
- <label style="display:block">
- <span style="display:block;font-size:.8rem;font-weight:500;margin-bottom:6px">Repository name</span>
- <input value="git-ents" style="width:100%;padding:9px 12px;font-family:var(--font-mono);font-size:.85rem;color:var(--color-text);background:var(--color-bg);border:1px solid var(--color-border);border-radius:8px;outline:none" style-focus="{ borderColor:'var(--color-accent)' }"/>
- </label>
- <label style="display:block">
- <span style="display:block;font-size:.8rem;font-weight:500;margin-bottom:6px">Description</span>
- <textarea rows="2" style="width:100%;padding:9px 12px;font-size:.85rem;color:var(--color-text);background:var(--color-bg);border:1px solid var(--color-border);border-radius:8px;outline:none;resize:vertical;font-family:var(--font-body)" style-focus="{ borderColor:'var(--color-accent)' }">A warm, editorial Git forge — serve your repositories as paper-grain HTML.</textarea>
- </label>
- <label style="display:block">
- <span style="display:block;font-size:.8rem;font-weight:500;margin-bottom:6px">Default branch</span>
- <select style="width:11rem;padding:9px 12px;font-family:var(--font-mono);font-size:.85rem;color:var(--color-text);background:var(--color-bg);border:1px solid var(--color-border);border-radius:8px;outline:none">
- <option>main</option><option>develop</option><option>trunk</option>
- </select>
- </label>
- </div>
- </div>
- <!-- Features -->
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);margin-bottom:20px">
- <div style="padding:11px 18px;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted);border-bottom:1px solid var(--color-border)">Features</div>
- <div>
- <sc-for list="{{ features }}" as="f" hint-placeholder-count="4">
- <div style="display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 18px;border-bottom:1px solid var(--color-border)">
- <div>
- <div style="font-size:.9rem;font-weight:500">{{ f.label }}</div>
- <div style="font-size:.76rem;color:var(--color-text-muted)">{{ f.desc }}</div>
- </div>
- <button onClick="{{ f.onClick }}" style="{{ f.trackStyle }}"><span style="{{ f.knobStyle }}"></span></button>
- </div>
- </sc-for>
- </div>
- </div>
- <!-- Visibility -->
- <div style="background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);margin-bottom:20px">
- <div style="padding:11px 18px;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:var(--color-text-muted);border-bottom:1px solid var(--color-border)">Visibility</div>
- <div style="padding:14px 18px;display:flex;flex-direction:column;gap:10px">
- <sc-for list="{{ visOptions }}" as="v" hint-placeholder-count="2">
- <button onClick="{{ v.onClick }}" style="{{ v.style }}">
- <span style="{{ v.radioStyle }}"><sc-if value="{{ v.on }}" hint-placeholder-val="{{ true }}"><span style="width:8px;height:8px;border-radius:100px;background:var(--color-accent)"></span></sc-if></span>
- <span style="text-align:left"><span style="display:block;font-size:.86rem;font-weight:500;color:var(--color-text)">{{ v.label }}</span><span style="display:block;font-size:.76rem;color:var(--color-text-muted)">{{ v.desc }}</span></span>
- </button>
- </sc-for>
- </div>
- </div>
- <!-- Danger zone -->
- <div style="background:var(--color-surface);border:1px solid var(--kw);border-radius:var(--radius-sm);box-shadow:var(--shadow-sm);overflow:hidden">
- <div style="padding:11px 18px;font-family:var(--font-mono);font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;color:var(--kw);border-bottom:1px solid var(--color-border)">Danger zone</div>
- <div style="padding:6px 18px">
- <div style="display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 0;border-bottom:1px solid var(--color-border)">
- <div><div style="font-size:.88rem;font-weight:500">Archive this repository</div><div style="font-size:.76rem;color:var(--color-text-muted)">Mark read-only. Hooks stop running.</div></div>
- <button style="padding:6px 14px;font-size:.78rem;font-weight:500;color:var(--kw);background:transparent;border:1px solid var(--kw);border-radius:8px;cursor:pointer" style-hover="{ background:'var(--diff-rem)' }">Archive</button>
- </div>
- <div style="display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 0">
- <div><div style="font-size:.88rem;font-weight:500">Delete this repository</div><div style="font-size:.76rem;color:var(--color-text-muted)">This cannot be undone.</div></div>
- <button style="padding:6px 14px;font-size:.78rem;font-weight:600;color:#fff;background:var(--kw);border:1px solid var(--kw);border-radius:8px;cursor:pointer" style-hover="{ opacity:'.9' }">Delete</button>
- </div>
- </div>
- </div>
- </div>
- </sc-if>
-
- </main>
- </div>
-
- <!-- ============ FOOTER ============ -->
- <footer style="border-top:1px solid var(--color-border);margin-top:20px">
- <div style="max-width:78rem;margin:0 auto;padding:24px 1.5rem;text-align:center;font-family:var(--font-mono);font-size:.72rem;color:var(--color-text-muted)">
- git-ents · served as paper-grain HTML · no JavaScript required
- </div>
- </footer>
-</div>
-</x-dc>
-<script type="text/x-dc" data-dc-script data-props="{"$preview":{"width":"100%"},"defaultTab":{"editor":"enum","options":["overview","files","releases","hooks","issues","settings"],"default":"overview","tsType":"string"},"collapseTree":{"editor":"boolean","default":false,"tsType":"boolean"}}">
-class Component extends DCLogic {
- state = {
- tab: 'overview',
- exp: { 'src': true, 'src/forge': true, 'src/hooks': false, 'templates': false, 'static': false, '.gitents': true },
- file: 'Cargo.toml',
- run: 142,
- itab: 'open',
- ilabel: 'all',
- iq: '',
- copied: '',
- features: { issues: true, releases: true, hooks: true, wiki: false },
- visibility: 'public',
- };
-
- constructor(props) {
- super(props);
- const valid = ['overview', 'files', 'releases', 'hooks', 'issues', 'settings'];
- if (props && valid.includes(props.defaultTab)) this.state.tab = props.defaultTab;
- if (props && props.collapseTree) this.state.exp = { 'src': false, 'src/forge': false, 'src/hooks': false, 'templates': false, 'static': false, '.gitents': false };
- }
-
- componentWillUnmount() { clearTimeout(this._t); }
-
- copy(text, key) {
- try { if (navigator.clipboard) navigator.clipboard.writeText(text); } catch (e) {}
- this.setState({ copied: key });
- clearTimeout(this._t);
- this._t = setTimeout(() => this.setState(s => (s.copied === key ? { copied: '' } : null)), 1400);
- }
-
- renderVals() {
- const accent = 'var(--color-accent)', muted = 'var(--color-text-muted)', text = 'var(--color-text)';
- const tab = this.state.tab;
-
- // ---------- TABS ----------
- const tabBtn = (active) => ({
- position: 'relative', display: 'flex', alignItems: 'center', gap: '8px',
- padding: '10px 14px', background: 'none', border: 'none', cursor: 'pointer',
- fontSize: '.88rem', fontWeight: active ? 600 : 500,
- color: active ? text : muted, fontFamily: 'var(--font-body)',
- });
- const tabDefs = [
- { id: 'overview', label: 'Overview' },
- { id: 'files', label: 'Files' },
- { id: 'releases', label: 'Releases', count: 4 },
- { id: 'hooks', label: 'Hooks', dot: true },
- { id: 'issues', label: 'Issues', count: 3 },
- { id: 'settings', label: 'Settings' },
- ];
- const tabs = tabDefs.map(t => ({
- label: t.label, active: tab === t.id,
- showCount: t.count != null, count: t.count,
- dot: !!t.dot,
- dotStyle: { width: '7px', height: '7px', borderRadius: '100px', background: 'var(--fn)' },
- onClick: () => this.setState({ tab: t.id }),
- style: tabBtn(tab === t.id),
- }));
-
- // ---------- FILE TREE ----------
- const T = [
- { t: 'dir', name: '.gitents', path: '.gitents', children: [
- { t: 'file', name: 'hooks.toml', path: '.gitents/hooks.toml' },
- ]},
- { t: 'dir', name: 'src', path: 'src', children: [
- { t: 'file', name: 'main.rs', path: 'src/main.rs' },
- { t: 'dir', name: 'forge', path: 'src/forge', children: [
- { t: 'file', name: 'mod.rs', path: 'src/forge/mod.rs' },
- { t: 'file', name: 'repo.rs', path: 'src/forge/repo.rs' },
- { t: 'file', name: 'render.rs', path: 'src/forge/render.rs' },
- { t: 'file', name: 'blob.rs', path: 'src/forge/blob.rs' },
- ]},
- { t: 'dir', name: 'hooks', path: 'src/hooks', children: [
- { t: 'file', name: 'mod.rs', path: 'src/hooks/mod.rs' },
- { t: 'file', name: 'runner.rs', path: 'src/hooks/runner.rs' },
- ]},
- ]},
- { t: 'dir', name: 'templates', path: 'templates', children: [
- { t: 'file', name: 'repo.html', path: 'templates/repo.html' },
- { t: 'file', name: 'blob.html', path: 'templates/blob.html' },
- ]},
- { t: 'dir', name: 'static', path: 'static', children: [
- { t: 'file', name: 'paper.css', path: 'static/paper.css' },
- ]},
- { t: 'file', name: 'Cargo.toml', path: 'Cargo.toml' },
- { t: 'file', name: 'LICENSE', path: 'LICENSE' },
- { t: 'file', name: 'README.md', path: 'README.md' },
- ];
- const rowBase = (pad, active) => ({
- display: 'flex', alignItems: 'center', gap: '7px', width: '100%',
- padding: '4px 8px', paddingLeft: pad + 'px', border: 'none',
- background: active ? 'var(--color-accent-subtle)' : 'transparent',
- color: active ? accent : text, cursor: 'pointer', textAlign: 'left',
- fontFamily: 'var(--font-mono)', fontSize: '.79rem', borderRadius: '7px',
- fontWeight: active ? 600 : 400,
- });
- const dec = (n, depth) => {
- const pad = 8 + depth * 15;
- if (n.t === 'dir') {
- const open = !!this.state.exp[n.path];
- return {
- key: n.path, name: n.name, isDir: true, isFile: false, open,
- chevStyle: { display: 'inline-flex', transition: 'transform .15s', transform: open ? 'rotate(90deg)' : 'none', color: muted, flex: 'none' },
- onClick: () => this.setState(s => ({ exp: { ...s.exp, [n.path]: !s.exp[n.path] } })),
- style: rowBase(pad, false),
- kids: (n.children || []).map(c => dec(c, depth + 1)),
- };
- }
- const active = this.state.file === n.path;
- return {
- key: n.path, name: n.name, isDir: false, isFile: true, open: false, active,
- onClick: () => this.setState({ file: n.path }),
- style: rowBase(pad, active), kids: [],
- };
- };
- const tree = T.map(n => dec(n, 0));
-
- // ---------- FILE CONTENTS ----------
- const FILES = {
- 'Cargo.toml': '[package]\nname = "git-ents"\nversion = "0.7.0"\nedition = "2021"\nlicense = "MIT"\ndescription = "A warm, editorial Git forge."\nrepository = "https://git.ents.dev/git-ents/git-ents"\n\n[dependencies]\naxum = "0.7"\nmaud = "0.26"\ngix = "0.63"\nsyntect = "5.2"\ntoml = "0.8"\npulldown-cmark = "0.11"',
- 'README.md': '# git-ents\n\nAn editorial Git forge for people who love to read their code.\n\ngit-ents renders a repository as quiet, server-side HTML. The whole UI is\npaper-grain and works without a single byte of client JavaScript.\n\n## Why git-ents\n\n- Readable by default.\n- Hooks, not pipelines.\n- No JavaScript.\n- One binary.\n\n## Install\n\n cargo install git-ents\n git-ents serve ./repos\n\n## License\n\nMIT.',
- 'LICENSE': 'MIT License\n\nCopyright (c) 2026 The Git Ents\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so.',
- 'src/main.rs': 'use axum::Router;\n\nmod forge;\nmod hooks;\n\n#[tokio::main]\nasync fn main() {\n let app = Router::new()\n .merge(forge::routes())\n .merge(hooks::routes());\n\n let addr = "0.0.0.0:8420";\n println!("git-ents serving on {addr}");\n axum::serve(listen(addr).await, app).await.unwrap();\n}',
- 'src/forge/mod.rs': 'use axum::{Router, routing::get};\n\nmod repo;\nmod render;\nmod blob;\n\npub fn routes() -> Router {\n Router::new()\n .route("/:owner/:repo", get(repo::overview))\n .route("/:owner/:repo/tree/*path", get(blob::tree))\n}',
- 'src/forge/repo.rs': 'use crate::render::Markup;\n\npub struct Repo {\n pub owner: String,\n pub name: String,\n pub head: String,\n}\n\nimpl Repo {\n pub fn open(path: &str) -> Result<Self, Error> {\n let git = gix::open(path)?;\n Ok(Self::from(git))\n }\n}',
- 'src/forge/render.rs': '// turn a markdown README into warm, paper-grain HTML\nuse pulldown_cmark::{Parser, html};\n\npub fn readme(src: &str) -> String {\n let parser = Parser::new(src);\n let mut out = String::new();\n html::push_html(&mut out, parser);\n out\n}',
- 'src/forge/blob.rs': 'use syntect::easy::HighlightLines;\n\npub fn highlight(text: &str, lang: Language) -> Markup {\n let mut hl = HighlightLines::new(lang.syntax(), &THEME);\n for line in text.lines() {\n let spans = hl.highlight_line(line, &SET).unwrap();\n emit(spans);\n }\n}',
- 'src/hooks/mod.rs': 'mod runner;\n\npub use runner::Runner;\n\n// CI is just pre-receive + post-receive scripts\npub fn routes() -> axum::Router {\n axum::Router::new()\n .route("/:owner/:repo/hooks", get(list_runs))\n}',
- 'src/hooks/runner.rs': 'use std::process::Command;\n\npub struct Runner {\n pub stages: Vec<Stage>,\n}\n\nimpl Runner {\n pub fn run(&self, push: &Push) -> RunResult {\n for stage in &self.stages {\n let ok = stage.exec(push)?;\n if !ok { return RunResult::Failed; }\n }\n RunResult::Passed\n }\n}',
- 'templates/repo.html': '<article class="readme">\n {{ readme | safe }}\n</article>\n<aside class="meta">\n <section class="clone">{{ clone_url }}</section>\n</aside>',
- 'templates/blob.html': '<div class="blob">\n <div class="gutter">{{ line_numbers }}</div>\n <pre class="code">{{ highlighted }}</pre>\n</div>',
- 'static/paper.css': ':root {\n --paper: #faf8f4;\n --ink: #2a2518;\n}\nbody {\n background: var(--paper);\n color: var(--ink);\n font-family: "DM Sans", sans-serif;\n}\n.blob { font-family: "IBM Plex Mono", monospace; }',
- };
- const SIZES = {
- 'Cargo.toml': '412 B', 'README.md': '688 B', 'LICENSE': '1.1 KB',
- 'src/main.rs': '386 B', 'src/forge/mod.rs': '298 B', 'src/forge/repo.rs': '274 B',
- 'src/forge/render.rs': '256 B', 'src/forge/blob.rs': '288 B',
- 'src/hooks/mod.rs': '241 B', 'src/hooks/runner.rs': '352 B',
- 'templates/repo.html': '178 B', 'templates/blob.html': '162 B', 'static/paper.css': '224 B',
- };
- const content = FILES[this.state.file] || '';
- const lines = content.split('\n').map((t, i) => ({ n: i + 1, t: t === '' ? ' ' : t }));
-
- // ---------- RELEASES ----------
- const SEC = { added: 'var(--fn)', changed: 'var(--ty)', fixed: 'var(--prop)' };
- const SECLABEL = { added: 'Added', changed: 'Changed', fixed: 'Fixed' };
- const relDefs = [
- { tag: 'v0.7.0', name: 'Paper Trail', date: 'June 12, 2026', sha: 'a1f3c9d', latest: true, hasDiff: true,
- notes: { added: ['Releases timeline rail with the accent dot', 'Per-blob ETag caching'], changed: ['Render release notes with the Lora display face'] },
- assets: [{ name: 'git-ents-0.7.0-x86_64-linux.tar.gz', size: '4.2 MB' }, { name: 'git-ents-0.7.0-aarch64-macos.tar.gz', size: '4.0 MB' }] },
- { tag: 'v0.6.1', name: '', date: 'May 28, 2026', sha: '7c20e8b', latest: false, hasDiff: false,
- notes: { fixed: ['Blob gutter alignment for 5-digit line numbers', 'Dark-mode flash on first paint'] },
- assets: [{ name: 'git-ents-0.6.1-x86_64-linux.tar.gz', size: '4.1 MB' }] },
- { tag: 'v0.6.0', name: 'Hairline', date: 'May 3, 2026', sha: '3e9a112', latest: false, hasDiff: false,
- notes: { added: ['Two-pane file explorer'], changed: ['Hairline borders replace heavy dividers everywhere'] },
- assets: [{ name: 'git-ents-0.6.0-x86_64-linux.tar.gz', size: '4.0 MB' }] },
- { tag: 'v0.5.0', name: 'First Light', date: 'April 10, 2026', sha: 'bd44f0a', latest: false, hasDiff: false,
- notes: { added: ['Auto dark mode via prefers-color-scheme', 'Gruvbox-warm syntax theme'] },
- assets: [{ name: 'git-ents-0.5.0-x86_64-linux.tar.gz', size: '3.8 MB' }] },
- ];
- const releases = relDefs.map(r => ({
- tag: r.tag, name: r.name, date: r.date, sha: r.sha, latest: r.latest, hasDiff: r.hasDiff,
- assets: r.assets,
- dotStyle: {
- position: 'absolute', left: '-30px', top: '16px', width: '12px', height: '12px',
- borderRadius: '100px', background: r.latest ? accent : 'var(--color-surface)',
- border: '2px solid ' + (r.latest ? accent : 'var(--color-border)'), boxShadow: '0 0 0 4px var(--color-bg)',
- },
- sections: Object.keys(r.notes).map(k => ({ label: SECLABEL[k], color: SEC[k], items: r.notes[k] })),
- }));
-
- // ---------- HOOKS ----------
- const LOGCOL = { cmd: 'var(--prop)', ok: 'var(--fn)', err: 'var(--kw)', warn: 'var(--ty)', info: muted };
- const runDefs = [
- { id: 142, status: 'passed', subject: 'Render release notes with the Lora display face', sha: 'a1f3c9d', branch: 'main', trigger: 'push', duration: '1m 42s', age: '3h ago',
- stages: [['lint', 'passed', '6s'], ['build', 'passed', '48s'], ['test', 'passed', '41s'], ['deploy', 'passed', '7s']],
- log: [['cmd', '$ cargo fmt --check && cargo clippy -- -D warnings'], ['ok', 'formatting clean · 0 warnings'], ['cmd', '$ cargo build --release'], ['info', ' Compiling git-ents v0.7.0'], ['ok', ' Finished release [optimized] in 48.2s'], ['cmd', '$ cargo test --all'], ['ok', 'test result: ok. 86 passed; 0 failed'], ['cmd', '$ ./deploy.sh'], ['ok', 'deployed git.ents.dev @ a1f3c9d']] },
- { id: 141, status: 'failed', subject: 'Fix blob gutter alignment for 5-digit line numbers', sha: '7c20e8b', branch: 'pr/gutter', trigger: 'pre-receive', duration: '2m 05s', age: '5h ago',
- stages: [['lint', 'passed', '6s'], ['build', 'passed', '47s'], ['test', 'failed', '1m 12s'], ['deploy', 'skipped', '—']],
- log: [['cmd', '$ cargo test --all'], ['info', 'running 86 tests'], ['err', 'FAILED: blob::gutter::aligns_five_digits'], ['err', ' left: " 1024", right: "1024 "'], ['err', 'test result: FAILED. 85 passed; 1 failed'], ['warn', 'deploy skipped — tests did not pass']] },
- { id: 140, status: 'passed', subject: 'Bump gix to 0.63, syntect to 5.2', sha: '3e9a112', branch: 'main', trigger: 'push', duration: '1m 31s', age: 'yesterday',
- stages: [['lint', 'passed', '5s'], ['build', 'passed', '52s'], ['test', 'passed', '34s'], ['deploy', 'passed', '6s']],
- log: [['cmd', '$ cargo build --release'], ['ok', ' Finished release [optimized] in 52.0s'], ['cmd', '$ cargo test --all'], ['ok', 'test result: ok. 86 passed; 0 failed']] },
- { id: 139, status: 'running', subject: 'Cache rendered markdown per blob oid', sha: 'f02b8c1', branch: 'main', trigger: 'push', duration: '0m 38s', age: 'just now',
- stages: [['lint', 'passed', '6s'], ['build', 'running', '32s'], ['test', 'pending', '—'], ['deploy', 'pending', '—']],
- log: [['cmd', '$ cargo fmt --check && cargo clippy -- -D warnings'], ['ok', 'formatting clean · 0 warnings'], ['cmd', '$ cargo build --release'], ['info', ' Compiling git-ents v0.7.0 ...']] },
- ];
- const stColor = (s) => s === 'passed' ? 'var(--fn)' : s === 'failed' ? 'var(--kw)' : s === 'running' ? 'var(--ty)' : 'var(--color-text-muted)';
- const runIconWrap = { flex: 'none', width: '18px', display: 'flex', alignItems: 'center', justifyContent: 'center' };
- const runs = runDefs.map(r => ({
- id: r.id, subject: r.subject, branch: r.branch, trigger: r.trigger, age: r.age,
- isRunning: r.status === 'running', isPassed: r.status === 'passed', isFailed: r.status === 'failed',
- iconWrap: runIconWrap,
- onClick: () => this.setState({ run: r.id }),
- style: {
- display: 'flex', alignItems: 'center', gap: '10px', width: '100%', padding: '12px 15px',
- border: 'none', borderBottom: '1px solid var(--color-border)', cursor: 'pointer',
- background: this.state.run === r.id ? 'var(--color-accent-subtle)' : 'transparent',
- borderLeft: '2px solid ' + (this.state.run === r.id ? accent : 'transparent'),
- },
- }));
- const curDef = runDefs.find(r => r.id === this.state.run) || runDefs[0];
- const cur = {
- subject: curDef.subject, sha: curDef.sha, duration: curDef.duration,
- isRunning: curDef.status === 'running', isPassed: curDef.status === 'passed', isFailed: curDef.status === 'failed',
- iconWrap: { flex: 'none', width: '20px', display: 'flex', alignItems: 'center', justifyContent: 'center' },
- stages: curDef.stages.map(s => ({ name: s[0], color: stColor(s[1]), duration: s[2] })),
- log: curDef.log.map((l, i) => ({ n: i + 1, color: LOGCOL[l[0]], text: l[1] })),
- };
- const HT = [
- ['# .gitents/hooks — CI defined as Git hooks', 'var(--cmt)'],
- ['', text],
- ['[pre-receive.lint]', 'var(--prop)'],
- ['run = "cargo fmt --check && cargo clippy -- -D warnings"', text],
- ['', text],
- ['[post-receive.build]', 'var(--prop)'],
- ['run = "cargo build --release"', text],
- ['', text],
- ['[post-receive.test]', 'var(--prop)'],
- ['run = "cargo test --all"', text],
- ['needs = ["build"]', text],
- ['', text],
- ['[post-receive.deploy]', 'var(--prop)'],
- ['run = "./deploy.sh"', text],
- ['needs = ["test"]', text],
- ['when = "branch == \'main\'"', text],
- ];
- const hooksToml = HT.map(l => ({ t: l[0] === '' ? ' ' : l[0], color: l[1] }));
-
- // ---------- ISSUES ----------
- const LBLCOL = { bug: 'var(--kw)', enhancement: 'var(--fn)', question: 'var(--prop)', docs: 'var(--ty)' };
- const issueDefs = [
- { num: 58, title: 'Blob gutter misaligns on files past 9,999 lines', state: 'open', labels: ['bug'], meta: 'opened 2 days ago by mira', comments: 4 },
- { num: 57, title: 'Add ETag caching for rendered README blobs', state: 'open', labels: ['enhancement'], meta: 'opened 4 days ago by elm', comments: 2 },
- { num: 55, title: 'How do I configure a post-receive deploy step?', state: 'open', labels: ['question'], meta: 'opened 6 days ago by toml-fan', comments: 7 },
- { num: 54, title: 'Dark mode flashes white on first paint', state: 'closed', labels: ['bug'], meta: 'closed 9 days ago by elm', comments: 3 },
- { num: 52, title: 'Timeline rail for the releases page', state: 'closed', labels: ['enhancement'], meta: 'closed 12 days ago by mira', comments: 1 },
- { num: 49, title: 'Document the hooks.toml schema', state: 'closed', labels: ['docs'], meta: 'closed 18 days ago by elm', comments: 0 },
- ];
- const openCount = issueDefs.filter(i => i.state === 'open').length;
- const closedCount = issueDefs.filter(i => i.state === 'closed').length;
- let filtered = issueDefs.filter(i => i.state === this.state.itab);
- if (this.state.ilabel !== 'all') filtered = filtered.filter(i => i.labels.includes(this.state.ilabel));
- if (this.state.iq.trim()) { const q = this.state.iq.toLowerCase(); filtered = filtered.filter(i => i.title.toLowerCase().includes(q)); }
- const issues = filtered.map(i => ({
- num: i.num, title: i.title, meta: i.meta, comments: i.comments, hasComments: i.comments > 0,
- isOpen: i.state === 'open', isClosed: i.state === 'closed',
- statusColor: i.state === 'open' ? 'var(--fn)' : 'var(--color-text-muted)',
- labels: i.labels.map(l => ({ name: l, color: LBLCOL[l] || muted })),
- }));
- const itabBtn = (active) => ({
- display: 'flex', alignItems: 'center', gap: '7px', background: 'none', border: 'none',
- cursor: 'pointer', fontSize: '.84rem', fontWeight: active ? 600 : 500,
- color: active ? text : muted, fontFamily: 'var(--font-body)', padding: 0,
- });
- const issueTabs = [
- { id: 'open', label: 'Open', count: openCount },
- { id: 'closed', label: 'Closed', count: closedCount },
- ].map(f => ({
- label: f.label, count: f.count, isOpen: f.id === 'open', isClosed: f.id === 'closed',
- onClick: () => this.setState({ itab: f.id }), style: itabBtn(this.state.itab === f.id),
- }));
- const chipStyle = (active) => ({
- padding: '5px 13px', fontSize: '.76rem', fontWeight: 500, cursor: 'pointer',
- borderRadius: 'var(--radius-pill)', fontFamily: 'var(--font-body)',
- color: active ? 'var(--color-bg)' : muted,
- background: active ? accent : 'var(--color-surface)',
- border: '1px solid ' + (active ? accent : 'var(--color-border)'),
- });
- const labelChips = [
- { id: 'all', label: 'All' }, { id: 'bug', label: 'bug' },
- { id: 'enhancement', label: 'enhancement' }, { id: 'question', label: 'question' },
- ].map(c => ({ label: c.label, onClick: () => this.setState({ ilabel: c.id }), style: chipStyle(this.state.ilabel === c.id) }));
-
- // ---------- SETTINGS ----------
- const featDefs = [
- { key: 'issues', label: 'Bug reports', desc: 'Let collaborators file and track issues.' },
- { key: 'releases', label: 'Releases', desc: 'Publish tagged releases with assets.' },
- { key: 'hooks', label: 'Hooks (CI)', desc: 'Run pre-receive and post-receive stages on push.' },
- { key: 'wiki', label: 'Wiki', desc: 'A companion docs space, rendered as paper.' },
- ];
- const features = featDefs.map(f => {
- const on = this.state.features[f.key];
- return {
- label: f.label, desc: f.desc,
- onClick: () => this.setState(s => ({ features: { ...s.features, [f.key]: !s.features[f.key] } })),
- trackStyle: {
- flex: 'none', width: '42px', height: '24px', borderRadius: '100px', cursor: 'pointer',
- border: 'none', padding: 0, position: 'relative', transition: 'background .18s',
- background: on ? accent : 'var(--color-border)',
- },
- knobStyle: {
- position: 'absolute', top: '3px', left: on ? '21px' : '3px', width: '18px', height: '18px',
- borderRadius: '100px', background: '#fff', transition: 'left .18s', boxShadow: '0 1px 2px #00000033',
- },
- };
- });
- const visBtn = (active) => ({
- display: 'flex', alignItems: 'center', gap: '11px', width: '100%', padding: '12px 14px',
- background: active ? 'var(--color-accent-subtle)' : 'var(--color-bg)', cursor: 'pointer',
- border: '1px solid ' + (active ? accent : 'var(--color-border)'), borderRadius: '8px', textAlign: 'left',
- });
- const visOptions = [
- { id: 'public', label: 'Public', desc: 'Anyone can read; the README and tree are served openly.' },
- { id: 'private', label: 'Private', desc: 'Only invited collaborators can clone or browse.' },
- ].map(v => ({
- label: v.label, desc: v.desc, on: this.state.visibility === v.id,
- onClick: () => this.setState({ visibility: v.id }), style: visBtn(this.state.visibility === v.id),
- radioStyle: {
- flex: 'none', width: '16px', height: '16px', borderRadius: '100px',
- border: '2px solid ' + (this.state.visibility === v.id ? accent : 'var(--color-border)'),
- display: 'flex', alignItems: 'center', justifyContent: 'center',
- },
- }));
-
- return {
- tabs,
- isOverview: tab === 'overview', isFiles: tab === 'files', isReleases: tab === 'releases',
- isHooks: tab === 'hooks', isIssues: tab === 'issues', isSettings: tab === 'settings',
- // overview
- onClone: () => this.copy('git.ents.dev/git-ents/git-ents.git', 'clone'),
- cloneCopied: this.state.copied === 'clone', notCloneCopied: this.state.copied !== 'clone',
- // files
- tree, lines, currentPath: this.state.file, lineCount: lines.length, currentSize: SIZES[this.state.file] || '—',
- onCopyFile: () => this.copy(content, 'file'),
- fileCopied: this.state.copied === 'file', notFileCopied: this.state.copied !== 'file',
- // releases
- releases,
- // hooks
- runs, cur, hooksToml,
- // issues
- issues, issueTabs, labelChips, iq: this.state.iq,
- onSearch: (e) => this.setState({ iq: e.target.value }),
- noIssues: issues.length === 0,
- // settings
- features, visOptions,
- };
- }
-}
-</script>
-</body>
-</html>
design_handoff_git_ents/README.md
@@ -1,275 +1,0 @@
-# Handoff: Git Ents — repository web UI
-
-## Overview
-
-A web presence for a self-hosted Git forge ("git-ents").
-It is a single repository page that deliberately centers the things a code editor is *bad* at: a large, editorial rendered README; a wander-able file tree; releases as a changelog; CI defined as Git hooks with run logs; bug reports; and repository configuration.
-
-The visual language is "Warm Ledger" — technical minimalism with warmth: a cream-and-gold paper palette, serif display type (Lora) over a geometric sans (DM Sans), monospace (IBM Plex Mono) for anything code-like, hairline borders, barely-there shadows, and a signature 3rem gold accent-underline under section headers.
-Light and dark are equals and switch automatically with the OS setting.
-
-## About the Design Files
-
-The file in this bundle (`Git Ents.dc.html`) is a **design reference created in HTML** — a working prototype showing intended look and behavior, **not production code to copy directly**.
-It is authored as a "Design Component" (a streaming single-file format) and will not run standalone in a normal app.
-
-The task is to **recreate this design in your target codebase** using its established framework and patterns (React, Vue, Svelte, server-rendered templates, etc.).
-If no front-end environment exists yet, pick the most appropriate one for the project.
-Note: the prototype itself is client-rendered, but the product it depicts is explicitly **server-rendered, no client JS** — if you are building the real forge, prefer a server-side HTML approach (the mock's interactivity, e.g. tab switching, would become separate page routes).
-
-## Fidelity
-
-**High-fidelity.**
-Final colors, typography, spacing, and interactions.
-Recreate the UI faithfully using your codebase's libraries and patterns.
-Exact hex values, font sizes, and measurements are documented below and in the source file.
-
----
-
-## Design Tokens
-
-All styling reads from CSS custom properties.
-Light is the base; dark overrides under `@media (prefers-color-scheme: dark)`.
-**Never hardcode hex in components — read from these.**
-
-### Color
-
-| Token | Light | Dark | Used for |
-|---|---|---|---|
-| `--color-bg` | `#faf8f4` | `#171510` | page background |
-| `--color-surface` | `#ffffff` | `#211f17` | cards, raised panels |
-| `--color-text` | `#2a2518` | `#ede8d8` | body text |
-| `--color-text-muted` | `#8a7e6a` | `#a89e88` | secondary text, metadata |
-| `--color-link` | `#b07d10` | `#d4a030` | links |
-| `--color-link-hover` | `#96690a` | `#e4b850` | link hover |
-| `--color-border` | `#ede9de` | `#383324` | hairlines, card borders |
-| `--color-code-bg` | `#f5f3eb` | `#211f17` | code, card headers, gutters |
-| `--color-accent` | `#b07d10` | `#d4a030` | gold accent: rules, active states |
-| `--color-accent-subtle` | `#b07d100f` | `#d4a03012` | accent tint backgrounds |
-
-Shadows: `--shadow-sm` = `0 1px 3px #0000000d` (light) / `0 1px 3px #00000040` (dark); `--shadow-md` = `0 4px 16px #0000000f` / `0 4px 16px #0000004d`.
-
-Page background has a faint top-center radial glow in the accent color: `radial-gradient(58rem 30rem at 50% -10rem, var(--color-accent-subtle), transparent 72%)`, `background-attachment: fixed`.
-
-### Syntax / categorical palette (Gruvbox-warm) — light / dark
-
-| Token | Light | Dark | Role |
-|---|---|---|---|
-| `--kw` | `#9d0006` | `#fb4934` | keyword / error / danger |
-| `--fn` | `#427b58` | `#8ec07c` | function / success / "passed" |
-| `--ty` | `#b57614` | `#fabd2f` | type / "running" / Rust lang dot |
-| `--str` | `#79740e` | `#b8bb26` | string |
-| `--num` | `#8f3f71` | `#d3869b` | number / constant |
-| `--cmt` | `#9c8f74` | `#928374` | comment |
-| `--prop` | `#076678` | `#83a598` | property / link / TOML section headers |
-
-Diff tints: `--diff-add` = `#4e9a0622` / `#b8bb2620`; `--diff-rem` = `#cc241d22` / `#fb493420`.
-
-### Typography
-
-Loaded from Google Fonts.
-
-- `--font-body`: `"DM Sans", system-ui, sans-serif` — weights 400–700.
- UI + body.
-- `--font-display`: `"Lora", Georgia, serif` — weights 500–700, `letter-spacing: -.01em`.
- Headings (the signature move).
-- `--font-mono`: `"IBM Plex Mono", ui-monospace, Menlo, monospace` — weights 400–600.
- Code, repo/file names, labels, logos, metadata.
-
-Base: `font-size: 17px`, `line-height: 1.7`, antialiased.
-Page section headings ~`1.5rem` Lora 700.
-README h1 `2.4rem` Lora 700.
-Uppercase mono micro-labels at `.7rem` with `.06em` letter-spacing for card headers and eyebrows.
-
-### Shape & spacing
-
-`--radius-sm: 10px` (cards, inputs, buttons); list rows / small controls use `7–8px`; `--radius-pill: 100px` (badges, branch tags, toggles).
-App content max-width `78rem`, side padding `1.5rem`.
-README reading column capped ~`44rem`.
-Generous vertical rhythm; sections separated by hairlines + the accent underline, not heavy dividers.
-
----
-
-## Global Chrome
-
-### Sticky nav (height 58px)
-
-- `position: sticky; top: 0; z-index: 50`.
- Background `color-mix(in srgb, var(--color-bg) 82%, transparent)` with `backdrop-filter: blur(10px)`.
- Bottom hairline.
-- Left: mono wordmark "git-ents" with a small tree/shield glyph in accent; hover → accent; `white-space: nowrap`.
-- Center: search input (`max-width: 24rem`), placeholder "Jump to file or symbol", leading search icon, focus border → accent. (Decorative in mock.)
-- Right: "Explore", "Docs" muted links (hover → accent), and a 30px circular avatar chip showing initials ("el") in accent on `--color-accent-subtle`.
-
-### Repo header band
-
-- Path line (mono, `1.18rem`): folder icon · `git-ents` (muted link) · `/` · `git-ents` (accent, weight 600) · **branch pill** (`main`, accent text on `--color-accent-subtle`, thin accent border, pill, with branch icon) · **`Public` pill** (muted on `--color-code-bg`).
-- Description paragraph (`.98rem`, max ~40rem).
-- Topic chips: `rust`, `git-forge`, `self-hosted`, `html-first` — mono `.72rem`, link color on `--color-accent-subtle`, pill.
-- Right side: `Watch` and `Star 128` buttons — surface bg, hairline border, `--shadow-sm`, hover border → accent, leading icons.
-
-### Tab bar
-
-Row of buttons over a `1px` bottom border.
-Each tab: `10px 14px`, `.88rem`.
-Active tab = `--color-text` + weight 600 + a full-width **2px accent underline** pinned to `bottom: -1px`.
-Idle = `--color-text-muted`, hover → `--color-text`.
-Tabs: `Overview`, `Files`, `Releases` (count pill "4"), `Hooks` (green status dot), `Issues` (count pill "3"), `Settings`.
-Count pills are mono `.68rem`, muted, on `--color-code-bg` with hairline border.
-
-### Footer
-
-Top hairline, centered mono `.72rem` muted: "git-ents · served as paper-grain HTML · no JavaScript required".
-
----
-
-## Screens / Views
-
-### 1. Overview (default tab)
-
-**Purpose:** read the project at a glance — the README is the star.
-**Layout:** CSS grid, `grid-template-columns: 1fr 19rem`, gap `34px`, `align-items: start`.
-
-- **README card** (left): surface, hairline border, `--radius-sm`, `--shadow-sm`, overflow hidden.
- - Header strip: `--color-code-bg`, bottom hairline, mono uppercase `.72rem` muted label "README.md" with a file icon.
- - Body: padding `40px 48px 52px`, `max-width: 44rem`.
- - `h1` "git-ents" — Lora 700, `2.4rem`, `letter-spacing: -.02em`.
- - Italic Lora subtitle in muted (`1.18rem`).
- - Badge row: pills (build passing — with `--fn` dot, `v0.7.0` in `--ty`, `MIT` in `--prop`, `rust 1.78+` muted), mono `.7rem` on `--color-code-bg` with hairline.
- - Body paragraphs; `h2` section headers (Lora 600, `1.4rem`) each followed by a **3rem × 2px accent underline**.
- - Bulleted lists; inline `code` on `--color-code-bg`, `5px` radius.
- - Two code blocks on `--color-code-bg` with hairline + `--radius-sm`: an install block (shell, `$` prompts muted, comments in `--cmt`) and a syntax-highlighted Rust snippet using the syntax palette (`--kw` keywords, `--fn` functions, `--ty` types, `--cmt` comments).
-- **Aside** (right, `position: sticky; top: 78px`, column, gap `18px`): four cards, each with a mono uppercase header strip + hairline:
- - **Clone**: readonly mono input (`git.ents.dev/git-ents/git-ents.git`) on `--color-code-bg` + 34px copy button; on copy → green check for 1.4s.
- - **About**: Rust·MIT (with `--ty` dot), stars/forks, "Updated 3 hours ago".
- - **Releases**: count "4" in accent; latest `v0.7.0` row with tag icon + "Latest" pill + "Paper Trail — released 9 days ago".
- - **Languages**: thin stacked bar (Rust 86% `--ty` / HTML 9% `--fn` / CSS 5% `--prop`) + legend with color dots.
-
-### 2. Files
-
-**Purpose:** browse the repo tree and read files.
-**Layout:** grid `17rem 1fr` inside one bordered, shadowed, rounded card; min-height `30rem`; overflow hidden.
-
-- **Left tree pane:** `--color-bg`, right hairline.
- Header strip = mono "main" with branch icon.
- - Rows: mono `.79rem`, `padding: 4px 8px`, indent `8 + depth*15` px.
- Folders show a chevron (rotates 90° when open, `.15s`) + accent folder icon; files show a spacer + muted file icon.
- Hover bg → `--color-code-bg`.
- **Selected file**: bg `--color-accent-subtle`, text → accent, weight 600.
- - Tree data: `.gitents/hooks.toml`; `src/{main.rs, forge/{mod,repo,render,blob}.rs, hooks/{mod,runner}.rs}`; `templates/{repo,blob}.html`; `static/paper.css`; `Cargo.toml`; `LICENSE`; `README.md`.
- Default-expanded: `.gitents`, `src`, `src/forge`.
- Default selection: `Cargo.toml`.
-- **Right blob pane:** column.
- - Header: `--color-code-bg`, bottom hairline.
- Left = mono current path.
- Right = mono meta ("N lines · SIZE") + a Copy button (→ green "Copied" check for 1.4s).
- - Body: grid `auto 1fr`, scrollable.
- Left = line-number gutter on `--color-code-bg`, right hairline, mono `.78rem`, muted at `opacity .6`, right-aligned.
- Right = code lines, mono `.78rem`, `line-height: 1.55`, `white-space: pre`, horizontal scroll. (Mock renders plain mono per file; real impl should syntax-highlight server-side.)
-
-### 3. Releases
-
-**Purpose:** read the changelog.
-**Layout:** Page header "Releases" (Lora `1.5rem`) + 3rem accent underline.
-Then a **timeline rail**: container `padding-left: 30px` with a `2px` vertical line at `left: 5px` in `--color-border`.
-
-- Each release: a **dot** absolutely positioned at `left: -30px; top: 16px`, 12px, pill, with a `4px` ring in `--color-bg` (`box-shadow`).
- Latest = filled accent; others = surface fill with border ring.
-- Release card (surface, hairline, rounded, shadow):
- - Header row (bottom hairline): tag icon (accent) + mono tag (`v0.7.0`, accent, weight 600) + Lora name ("Paper Trail") + optional "Latest" pill (`--fn`); right-aligned date (muted).
- - Body: note sections grouped **Added / Changed / Fixed**, each with a mono uppercase eyebrow colored `--fn` / `--ty` / `--prop` respectively, then a bullet list (`.88rem`).
- The latest release also shows a 2-line **diff** block (added line on `--diff-add`, removed line on `--diff-rem`, mono `.76rem`).
- - Footer (top hairline): asset download links (mono `.74rem`, link color, download icon, size in muted) + right-aligned mono sha with a commit glyph.
-- Four releases: `v0.7.0 Paper Trail` (latest, June 12 2026), `v0.6.1` (May 28), `v0.6.0 Hairline` (May 3), `v0.5.0 First Light` (Apr 10).
-
-### 4. Hooks
-
-**Purpose:** CI defined as Git hooks — view runs and logs.
-**Layout:** Page header "Hooks" + underline + intro paragraph (notes CI is plain `pre-receive`/`post-receive`, config in `.gitents/hooks.toml`).
-Then grid `20rem 1fr`, gap `20px`, `align-items: start`.
-
-- **Run list** (left card): header "Recent runs".
- Each run is a button row (bottom hairline): a status icon (passed = `--fn` filled check-circle; failed = `--kw` x-circle; running = `--ty` spinner, `@keyframes spin` 1s linear) + subject (truncated) + mono submeta "#id · branch · trigger" + right-aligned age.
- **Selected run**: bg `--color-accent-subtle` + `2px` left accent border.
- Hover → `--color-code-bg`.
-- **Run detail** (right column, gap 18px):
- - Detail card: header (status icon + Lora subject + right-aligned mono "sha · duration").
- **Stages** row (bottom hairline): pills on `--color-code-bg` each = a status dot (`--fn`/`--kw`/`--ty`/muted) + mono stage name + mono duration (`lint`, `build`, `test`, `deploy`).
- **Log viewer**: `--color-code-bg`, mono `.76rem`, `line-height: 1.7`, `white-space: pre`, horizontal scroll.
- Each line = muted line number + text colored by level: cmd→`--prop`, ok→`--fn`, err→`--kw`, warn→`--ty`, info→muted.
- - Config card: mono header "`.gitents/hooks.toml`" on `--color-code-bg`; body renders the TOML with section headers (`[pre-receive.lint]` etc.) in `--prop`, comments in `--cmt`, rest in text color.
-- Four runs (#142 passed, #141 failed, #140 passed, #139 running).
- Default selection: #142.
-
-### 5. Issues ("Bug reports")
-
-**Purpose:** file and track bugs.
-**Layout:** Header row: "Bug reports" (Lora `1.5rem`, `white-space: nowrap`) + underline on the left; "New issue" button on the right (solid accent bg, `--color-bg` text, weight 600, plus icon, hover → `--color-link-hover`).
-
-- **Filter row:** search input (flex-grow, leading icon, `onInput` filters by title, focus border → accent) + **label chips** (`All`, `bug`, `enhancement`, `question`).
- Active chip = solid accent bg + `--color-bg` text; idle = surface + muted + hairline.
- Pill shape, `.76rem`.
-- **List card:** header strip on `--color-code-bg` with **Open / Closed** sub-tabs (each = status icon + label + count; active = text + weight 600).
- Each issue row (bottom hairline, hover → `--color-code-bg`): status icon (open = `--fn` open-issue dot-circle; closed = muted check-circle) + title link (hover → accent) + label pills (mono `.66rem`, colored per label: bug→`--kw`, enhancement→`--fn`, question→`--prop`, docs→`--ty`, on `--color-code-bg` + hairline) + mono submeta "#num opened/closed … by author" + right-aligned comment count with speech icon (hidden if 0).
- Empty state: centered muted "No bug reports match these filters."
-- Six issues (3 open, 3 closed) covering bug/enhancement/question/docs labels.
-
-### 6. Settings ("Repository settings")
-
-**Purpose:** configure the repo.
-**Layout:** `max-width: 46rem`.
-Page header + underline.
-Four stacked cards, each with a mono uppercase header strip + hairline.
-
-- **General:** labeled fields — repo name (mono input), description (textarea, body font, vertical resize), default branch (`select`: main/develop/trunk).
- Inputs: `--color-bg` bg, hairline, `8px` radius, focus border → accent.
-- **Features:** four rows (Bug reports, Releases, Hooks (CI), Wiki), each = label + description + a **toggle switch** (42×24 track, pill; on = accent bg + knob at `left: 21px`; off = `--color-border` + knob at `left: 3px`; 18px white knob, `.18s` transition).
- Defaults: issues/releases/hooks on, wiki off.
-- **Visibility:** two radio rows (Public / Private) — each a button with a 16px radio (filled accent dot when selected), title + description; selected row = `--color-accent-subtle` bg + accent border.
- Default: Public.
-- **Danger zone:** card with a `--kw` border + `--kw` header.
- Two rows: "Archive this repository" (outline `--kw` button, hover bg `--diff-rem`) and "Delete this repository" (solid `--kw` button, white text).
-
----
-
-## Interactions & Behavior
-
-- **Tabs:** clicking a tab swaps the content region; active underline + weight change.
- In a server-rendered build these become separate routes/pages.
-- **File tree:** folder rows toggle expand/collapse (chevron rotates `.15s`); file rows select and load the blob into the right pane.
-- **Run list:** selecting a run swaps the detail card (stages + log + the run's own logs).
- Running runs show an infinite spinner.
-- **Issue filters:** Open/Closed sub-tabs, label chips, and the search box compose (AND) to filter the list live; empty result → empty-state message.
-- **Copy buttons** (clone URL, blob): write to clipboard, then show a green check / "Copied" for ~1.4s before reverting.
-- **Settings toggles & radios:** flip local state with animated knob / radio fill.
-- **Hover motion** is small (≤3px) and quick (`.15s–.22s`); hover states lean on the accent and the subtle tint, rarely on shadow.
- List-row hover tints to `--color-code-bg`.
-- **Dark mode:** automatic via `prefers-color-scheme`; every token has a dark value — no manual toggle.
-
-## State Management
-
-Local UI state only (no data fetching in the mock):
-
-- `tab` — active top-level tab.
-- `exp` — map of expanded folder paths (file tree).
-- `file` — selected file path (blob view).
-- `run` — selected CI run id.
-- `itab` (open|closed), `ilabel` (all|bug|enhancement|question), `iq` (search string) — issue filters.
-- `copied` — transient key marking which copy button is showing its confirmation.
-- `features` — map of feature toggles; `visibility` — public|private.
-
-In a real forge most of this maps to **server routes + query params** (tab/path/run/ filters in the URL) since the product is server-rendered with no client JS; only the copy-to-clipboard affordance strictly needs a sprinkle of JS.
-
-## Assets
-
-- **Fonts:** DM Sans, Lora, IBM Plex Mono (Google Fonts).
-- **Icons:** inline 16px line icons in the Octicon idiom, `fill: currentColor` so they inherit accent/muted from context.
- No emoji in chrome.
- Recreate with your icon set (e.g. Octicons / Lucide) at equivalent weights.
-- No raster images; the avatar is an initials chip.
-
-## Files
-
-- `Git Ents.dc.html` — the full hi-fi prototype (all six tabs, all interactions).
- Open it in a browser to inspect exact markup, inline styles, and the logic that computes the lists/states.
- All design tokens are defined in its `:root` block (and the dark `@media` override) near the top of the file.