Bulk-inserts <link rel="icon" type="image/svg+xml" href="/favicon.svg"> before the </head> tag on every silentmode.st page that was missing it. All 36 site HTML pages now advertise the chain-links favicon; browsers show it in tabs and bookmarks across the whole umbrella site. Pages touched (29): apps/, choose/*, deviant/* (7 pages under the parent house), hephaestus/, hephaestus.x/* (9 chain-published mirror pages), nav/ (redirect stub), register.html (redirect stub), sirius/, sirius/register.html, store/ (redirect stub), tlds/. Already had favicon from earlier passes (7): index.html, brand/, tools/, prometheus/, helios/, hermes/, releases/. Line-ending preservation: choose/index.html and sirius/register.html were originally stored with CRLF endings; the plain Python read/write path normalized them to LF and produced a noisy diff. Both files re-serialized as CRLF before the favicon insert to keep the diff to +1 line each. Deploy: rsync-equivalent tar-pipe to /opt/silent-mode/site/, plus per-file scp for the two CRLF-corrected files. Verified live: 7 spot-checked URLs each report exactly one <link rel="icon"> in served HTML.
228 lines
11 KiB
HTML
228 lines
11 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>silentmode/__REPO__ — Hephaestus</title>
|
|
<meta name="description" content="silentmode/__REPO__ on Hephaestus — Silent Mode's decentralized code host.">
|
|
<style>
|
|
:root{
|
|
--bg:#0b0e14; --panel:#141a24; --panel2:#18202c; --line:rgba(255,255,255,.09);
|
|
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d;
|
|
}
|
|
*{box-sizing:border-box}
|
|
body{margin:0;background:radial-gradient(1100px 560px at 50% -12%,#16202e,var(--bg));
|
|
color:var(--ink);font:15px/1.65 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;min-height:100vh;
|
|
display:flex;flex-direction:column}
|
|
a{color:var(--acid);text-decoration:none}
|
|
a:hover{text-decoration:underline}
|
|
.wrap{max-width:900px;margin:0 auto;padding:0 1.2rem;width:100%;flex:1}
|
|
.top{padding:1.6rem 0;color:var(--dim);font-size:13px}
|
|
.top a{color:var(--mut)}
|
|
header.repo{padding:1.6rem 0 1.2rem;border-bottom:1px solid var(--line)}
|
|
h1{margin:0;font-size:1.6rem;font-family:ui-monospace,monospace;font-weight:500}
|
|
h1 .owner{color:var(--mut)} h1 .sep{color:var(--dim)} h1 .name{color:var(--acid)}
|
|
.meta{margin-top:.6rem;color:var(--mut);font-size:13.5px;line-height:1.6}
|
|
.meta span{margin-right:14px}
|
|
.desc{margin-top:.8rem;color:var(--ink);font-size:15px}
|
|
.cta{margin-top:1.2rem;display:flex;gap:8px;flex-wrap:wrap}
|
|
.btn{display:inline-block;padding:9px 18px;border-radius:9px;font-size:13.5px;font-weight:500;
|
|
border:1px solid var(--line);color:var(--ink);background:transparent;transition:filter .1s,background .1s;text-decoration:none}
|
|
.btn.acid{background:var(--acid);color:#0b0e14;font-weight:600;border-color:var(--acid)}
|
|
.btn:hover{background:var(--panel);text-decoration:none}
|
|
.btn.acid:hover{background:var(--acid)}
|
|
section{padding:1.8rem 0;border-bottom:1px solid var(--line)}
|
|
section:last-of-type{border-bottom:none}
|
|
h2{margin:0 0 .8rem;font-size:1rem;color:var(--mut);font-weight:500;text-transform:uppercase;letter-spacing:.05em}
|
|
pre.clone{background:#0e131b;border:1px solid var(--line);border-radius:8px;padding:10px 14px;overflow-x:auto;
|
|
font-family:ui-monospace,monospace;font-size:12.5px;color:var(--ink);margin:0 0 8px}
|
|
.clone-label{color:var(--dim);font-size:11.5px;text-transform:uppercase;letter-spacing:.05em;margin:12px 0 4px}
|
|
#readme{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:20px 24px;
|
|
overflow-x:auto;font-size:14px;line-height:1.7}
|
|
#readme.empty{color:var(--dim);font-style:italic;text-align:center;padding:2rem}
|
|
#readme h1,#readme h2,#readme h3{color:var(--ink);font-weight:600;margin-top:1.4rem;margin-bottom:.5rem;font-family:inherit;text-transform:none;letter-spacing:0}
|
|
#readme h1{font-size:1.5rem;border-bottom:1px solid var(--line);padding-bottom:.4rem}
|
|
#readme h2{font-size:1.2rem}
|
|
#readme h3{font-size:1.05rem;color:var(--acid)}
|
|
#readme p{margin:.6rem 0;color:var(--ink)}
|
|
#readme code{background:#0e131b;border:1px solid var(--line);border-radius:4px;padding:1px 6px;font-family:ui-monospace,monospace;font-size:12.5px}
|
|
#readme pre{background:#0e131b;border:1px solid var(--line);border-radius:8px;padding:12px 14px;overflow-x:auto}
|
|
#readme pre code{background:transparent;border:none;padding:0}
|
|
#readme a{color:var(--acid)}
|
|
#readme ul,#readme ol{padding-left:1.4rem}
|
|
#readme li{margin:.25rem 0}
|
|
#readme blockquote{border-left:3px solid var(--acid);padding-left:12px;margin:.8rem 0;color:var(--mut)}
|
|
.commits{display:grid;gap:8px}
|
|
.commit{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:12px 16px;
|
|
font-size:13.5px;display:grid;grid-template-columns:1fr auto;gap:8px 14px;align-items:baseline}
|
|
.commit .msg{color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
.commit .sha{color:var(--dim);font-family:ui-monospace,monospace;font-size:12px}
|
|
.commit .when{color:var(--dim);font-size:11.5px;text-align:right;white-space:nowrap}
|
|
.commit .author{color:var(--mut);font-size:11.5px;grid-column:1/-1}
|
|
footer{border-top:1px solid var(--line);padding:2rem 1.2rem 3rem;color:var(--dim);font-size:13px;text-align:center;margin-top:2rem}
|
|
footer a{color:var(--mut)} footer a:hover{color:var(--ink)}
|
|
.private-note{background:var(--panel);border:1px dashed var(--line);border-left:3px solid var(--acid);
|
|
border-radius:10px;padding:14px 18px;color:var(--mut);font-size:13.5px;margin:1.2rem 0}
|
|
</style>
|
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
</head>
|
|
<body>
|
|
|
|
<div class="wrap">
|
|
|
|
<div class="top">
|
|
<a href="../../">hephaestus.x</a> / <a href="../">silentmode</a> / <strong>__REPO__</strong>
|
|
</div>
|
|
|
|
<header class="repo">
|
|
<h1><span class="owner">silentmode</span><span class="sep">/</span><span class="name">__REPO__</span></h1>
|
|
<p class="meta" id="meta"><span>loading …</span></p>
|
|
<p class="desc" id="desc"></p>
|
|
<div class="cta">
|
|
<a class="btn acid" href="https://code.silentmode.st/silentmode/__REPO__">Browse full source →</a>
|
|
<a class="btn" href="https://code.silentmode.st/silentmode/__REPO__/issues">Issues</a>
|
|
<a class="btn" href="https://code.silentmode.st/silentmode/__REPO__/releases">Releases</a>
|
|
</div>
|
|
</header>
|
|
|
|
<section>
|
|
<h2>README</h2>
|
|
<div id="readme" class="empty">Loading README from the forge…</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Recent commits</h2>
|
|
<div class="commits" id="commits"><div class="commit"><div class="msg">Loading…</div></div></div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>Clone</h2>
|
|
<div class="clone-label">HTTPS (with a personal access token)</div>
|
|
<pre class="clone">git clone https://<you>:<token>@code.silentmode.st/silentmode/__REPO__.git</pre>
|
|
<div class="clone-label">SSH (with a key registered under your account)</div>
|
|
<pre class="clone">git clone ssh://git@code.silentmode.st:2222/silentmode/__REPO__.git</pre>
|
|
</section>
|
|
|
|
</div>
|
|
|
|
<footer>
|
|
<a href="../../">hephaestus.x</a> · Silent Mode's decentralized code host · <a href="https://code.silentmode.st/">code.silentmode.st</a>
|
|
</footer>
|
|
|
|
<script type="module">
|
|
const REPO = "__REPO__";
|
|
const API = `https://code.silentmode.st/api/v1/repos/silentmode/${REPO}`;
|
|
|
|
function esc(s) {
|
|
return String(s ?? "").replace(/[&<>"']/g, c => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));
|
|
}
|
|
function fmtDate(iso) {
|
|
if (!iso) return "";
|
|
const d = new Date(iso);
|
|
if (isNaN(d)) return "";
|
|
const days = (Date.now() - d.getTime()) / 86400000;
|
|
if (days < 1) return "today";
|
|
if (days < 30) return `${Math.round(days)}d ago`;
|
|
return d.toISOString().slice(0, 10);
|
|
}
|
|
|
|
// Minimal markdown → HTML renderer for the README preview.
|
|
// Not comprehensive — covers headers, paragraphs, lists, links, inline code,
|
|
// fenced code blocks, and blockquotes. For anything richer, users click through
|
|
// to the full forge which has Forgejo's proper markdown renderer.
|
|
function mdToHtml(md) {
|
|
// Escape first, then apply markdown patterns
|
|
let s = esc(md);
|
|
// Fenced code blocks
|
|
s = s.replace(/```(\w+)?\n([\s\S]*?)```/g, (_, lang, code) => `<pre><code>${code.trimEnd()}</code></pre>`);
|
|
// Headers
|
|
s = s.replace(/^### (.+)$/gm, "<h3>$1</h3>");
|
|
s = s.replace(/^## (.+)$/gm, "<h2>$1</h2>");
|
|
s = s.replace(/^# (.+)$/gm, "<h1>$1</h1>");
|
|
// Bold + italic
|
|
s = s.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
|
|
s = s.replace(/\*([^*\n]+)\*/g, "<i>$1</i>");
|
|
// Inline code (leave alone anything inside <pre> — the regex won't match across newlines)
|
|
s = s.replace(/`([^`\n]+)`/g, "<code>$1</code>");
|
|
// Links [text](url)
|
|
s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
|
|
// Unordered lists
|
|
s = s.replace(/(?:^|\n)((?:- .+\n?)+)/g, m => "\n<ul>" + m.trim().split("\n").map(l => `<li>${l.replace(/^-\s+/, "")}</li>`).join("") + "</ul>\n");
|
|
// Blockquotes
|
|
s = s.replace(/^> (.+)$/gm, "<blockquote>$1</blockquote>");
|
|
// Paragraphs: any line-block not already wrapped in a block tag
|
|
s = s.split(/\n{2,}/).map(chunk => {
|
|
if (/^<(h[1-6]|ul|ol|pre|blockquote)/.test(chunk.trim())) return chunk;
|
|
return chunk.trim() ? `<p>${chunk.trim().replace(/\n/g, "<br>")}</p>` : "";
|
|
}).join("\n");
|
|
return s;
|
|
}
|
|
|
|
async function loadRepo() {
|
|
const meta = document.getElementById("meta");
|
|
const desc = document.getElementById("desc");
|
|
const readme = document.getElementById("readme");
|
|
const commits = document.getElementById("commits");
|
|
try {
|
|
const info = await fetch(API).then(r => r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)));
|
|
const parts = [];
|
|
if (info.private) parts.push('<span style="color:var(--dim)">private</span>');
|
|
if (info.default_branch) parts.push(`<span>default branch <code>${esc(info.default_branch)}</code></span>`);
|
|
if (info.size != null) parts.push(`<span>${info.size < 1024 ? info.size + " KB" : (info.size / 1024).toFixed(1) + " MB"}</span>`);
|
|
if (info.updated_at) parts.push(`<span>updated ${fmtDate(info.updated_at)}</span>`);
|
|
meta.innerHTML = parts.join("");
|
|
desc.textContent = info.description || "";
|
|
|
|
// README
|
|
try {
|
|
const branch = info.default_branch || "master";
|
|
const rmRes = await fetch(`${API}/raw/README.md?ref=${encodeURIComponent(branch)}`);
|
|
if (rmRes.ok) {
|
|
const md = await rmRes.text();
|
|
readme.classList.remove("empty");
|
|
readme.innerHTML = mdToHtml(md);
|
|
} else {
|
|
readme.textContent = "(no README.md in this repo)";
|
|
}
|
|
} catch (e) {
|
|
readme.textContent = `(couldn't load README: ${e.message})`;
|
|
}
|
|
|
|
// Recent commits
|
|
try {
|
|
const cRes = await fetch(`${API}/commits?limit=8`);
|
|
if (cRes.ok) {
|
|
const list = await cRes.json();
|
|
commits.innerHTML = list.slice(0, 8).map(c => {
|
|
const msg = (c.commit?.message || "").split("\n")[0];
|
|
const author = c.commit?.author?.name || c.author?.login || "unknown";
|
|
const when = fmtDate(c.commit?.author?.date);
|
|
const sha = (c.sha || "").slice(0, 7);
|
|
return `<a class="commit" href="https://code.silentmode.st/silentmode/${REPO}/commit/${sha}">
|
|
<div class="msg">${esc(msg)}</div>
|
|
<div class="sha">${esc(sha)}</div>
|
|
<div class="author">by ${esc(author)}</div>
|
|
<div class="when">${esc(when)}</div>
|
|
</a>`;
|
|
}).join("");
|
|
}
|
|
} catch (e) {
|
|
commits.innerHTML = `<div class="commit"><div class="msg" style="color:var(--dim)">Couldn't load commits: ${esc(e.message)}</div></div>`;
|
|
}
|
|
} catch (e) {
|
|
// Repo is private (401) or gone (404). Show a helpful message + sign-in CTA.
|
|
meta.innerHTML = '<span style="color:var(--dim)">private repo — sign in to browse</span>';
|
|
readme.innerHTML = `
|
|
<div class="private-note">
|
|
This repository is private. To browse it, <a href="https://code.silentmode.st/user/oauth2/hephaestus-wallet">sign in with your wallet</a>
|
|
on <code>code.silentmode.st</code> — if you've been added as a collaborator or org member,
|
|
you'll be able to read it there.
|
|
</div>`;
|
|
commits.innerHTML = "";
|
|
}
|
|
}
|
|
|
|
loadRepo();
|
|
</script>
|
|
</body>
|
|
</html>
|