mirror of
https://github.com/lucas-labs/claude-plugins.git
synced 2026-08-15 20:27:05 -03:00
295 lines
20 KiB
HTML
295 lines
20 KiB
HTML
<!doctype html>
|
||
<!--
|
||
project-artifact template — a self-contained status page for a multi-workstream project.
|
||
Domain-neutral. For software projects (workstreams = PRs), also read swe.md — it has
|
||
the PR-sequence table and per-PR detail HTML fragments to paste in.
|
||
|
||
HOW TO USE
|
||
1. Copy this file to a stable path as <kebab-project-name>.html (the <title> names the
|
||
artifact; the basename is the fallback if <title> is missing), and DELETE this HOW TO
|
||
USE comment block from your copy (don't leave it in the published page).
|
||
2. Fill in the placeholder slots — the HTML comments tagged "FILL:", plus the plain-text
|
||
PROJECT_NAME in <title> and <h1>. Delete the tabs you don't have real content for; if
|
||
you delete one, renumber the remaining tab buttons (1, 2, 3 …).
|
||
3. The <body> below uses TAB MECHANISM B (a tiny `<script>` toggles `.pane` divs) —
|
||
it scales to any number of tabs with zero per-tab CSS, and it's what every real
|
||
page built this way uses. If you want a no-JS page AND have a small fixed tab count, swap in TAB MECHANISM A
|
||
(pure-CSS radio tabs) — the full skeleton for it is in the big comment block right
|
||
after <body>. (Mechanism A needs each tab id added to TWO `:checked ~ …` selector
|
||
lists in the CSS; forget one and the tab silently won't show. That's why B is the
|
||
default here.)
|
||
4. Publish: see SKILL.md ("Publish with the Artifact tool") — you'll also need a
|
||
favicon emoji (keep it the same on every redeploy).
|
||
|
||
The CSS below is the shared house style (light/dark via prefers-color-scheme, CSS
|
||
variables, status pills). Tweak colors, not structure.
|
||
-->
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>PROJECT_NAME — status</title>
|
||
<style>
|
||
:root { --fg:#1a1a1a; --bg:#fdfdfd; --accent:#0a7d4a; --warn:#b45309; --red:#b91c1c; --muted:#666; --border:#ddd; --code-bg:#f5f5f5; }
|
||
@media (prefers-color-scheme: dark) {
|
||
:root { --fg:#e4e4e4; --bg:#1a1a1a; --accent:#4ade80; --warn:#fbbf24; --red:#f87171; --muted:#999; --border:#333; --code-bg:#262626; }
|
||
}
|
||
* { box-sizing:border-box; }
|
||
body { font:15px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; color:var(--fg); background:var(--bg); max-width:980px; margin:1.5em auto; padding:0 1.5em 3em; }
|
||
h1,h2,h3,h4 { font-weight:600; margin-top:1.6em; line-height:1.3; }
|
||
h1 { font-size:1.7em; margin-bottom:.2em; }
|
||
h2 { font-size:1.35em; border-bottom:1px solid var(--border); padding-bottom:.2em; }
|
||
h3 { font-size:1.1em; }
|
||
a { color:var(--accent); }
|
||
code { background:var(--code-bg); padding:.15em .35em; border-radius:3px; font-size:.92em; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; }
|
||
pre { background:var(--code-bg); padding:1em 1.2em; border-radius:6px; overflow-x:auto; font-size:.87em; line-height:1.5; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; }
|
||
pre code { background:none; padding:0; }
|
||
table { border-collapse:collapse; width:100%; margin:.8em 0; font-size:.93em; }
|
||
th,td { border:1px solid var(--border); padding:.45em .7em; vertical-align:top; text-align:left; }
|
||
th { font-weight:600; background:var(--code-bg); }
|
||
ul { padding-left:1.4em; } li { margin:.25em 0; }
|
||
details { margin:.5em 0; } details > summary { cursor:pointer; font-weight:600; padding:.4em 0; }
|
||
hr { border:none; border-top:1px solid var(--border); margin:2em 0; }
|
||
.meta { color:var(--muted); font-size:.85em; }
|
||
.sub { color:var(--muted); font-size:.95em; margin-top:.3em; }
|
||
/* status banner */
|
||
.status { background:color-mix(in srgb, var(--accent) 12%, var(--bg)); border:1px solid var(--accent); border-radius:8px; padding:.9em 1.2em; margin:1.2em 0; }
|
||
.status .badge { display:inline-block; background:var(--accent); color:var(--bg); padding:.1em .6em; border-radius:4px; font-size:.78em; font-weight:600; letter-spacing:.02em; }
|
||
.status p { margin:.5em 0 0; font-size:.92em; }
|
||
/* next-steps strip — sits under the status banner, above the tabs, so it shows on every tab.
|
||
It's a <details open> so the reader can collapse it; the summary keeps the item count visible. */
|
||
.next { border:1px solid var(--warn); border-left:4px solid var(--warn); border-radius:8px; padding:.8em 1.2em; margin:1.2em 0; background:color-mix(in srgb, var(--warn) 8%, var(--bg)); }
|
||
.next > summary { cursor:pointer; font-weight:600; font-size:1.02em; padding:0; }
|
||
.next > summary .meta { font-weight:400; }
|
||
.next ol { margin:.5em 0 .1em 1.3em; padding:0; }
|
||
.next ol li { margin:.3em 0; }
|
||
.next .who { font-weight:600; }
|
||
.next p.none { margin:.5em 0 .1em; font-size:.93em; }
|
||
/* pills — solid fills (text in var(--bg) so contrast clears WCAG AA in both light and dark);
|
||
four distinct hues: accent/done, warn/now, neutral/next, red/warn(blocked) */
|
||
.pill { display:inline-block; font-size:.78em; padding:.1em .55em; border-radius:10px; background:var(--code-bg); color:var(--muted); margin-left:.4em; vertical-align:1px; }
|
||
.pill.done { background:var(--accent); color:var(--bg); } /* done / tested ✓ / verified ✓ */
|
||
.pill.now { background:var(--warn); color:var(--bg); } /* in progress / in review */
|
||
.pill.next { background:var(--code-bg); color:var(--fg); border:1px solid var(--border); } /* next / planned */
|
||
.pill.warn { background:var(--red); color:var(--bg); } /* blocked / ⚠ caveat */
|
||
.callout { border:1px solid var(--border); border-left:3px solid var(--accent); border-radius:4px; padding:.7em 1em; margin:1em 0; font-size:.93em; background:color-mix(in srgb, var(--accent) 5%, var(--bg)); }
|
||
/* ── TAB MECHANISM B (default in the <body> below): JS toggles .pane divs. Scales to
|
||
any tab count; no per-tab CSS. ── */
|
||
.tabbar { display:flex; flex-wrap:wrap; gap:.2em; border-bottom:2px solid var(--border); margin:1.2em 0 1.5em; }
|
||
.tabbar .tab { padding:.55em 1em; cursor:pointer; border:1px solid transparent; border-bottom:none; border-radius:6px 6px 0 0; font:inherit; font-weight:500; font-size:.95em; color:var(--muted); background:none; margin-bottom:-2px; }
|
||
.tabbar .tab:hover { color:var(--fg); }
|
||
.tabbar .tab.active { color:var(--fg); border-color:var(--border); border-bottom:2px solid var(--bg); background:var(--bg); font-weight:600; }
|
||
.pane { display:none; } .pane.active { display:block; }
|
||
/* ── TAB MECHANISM A (no-JS alternative; see the comment block after <body>): pure-CSS
|
||
radio tabs. Each tab id MUST appear in BOTH rule-lists below (all 7 catalog tabs are listed). ── */
|
||
.tabs > input { display:none; }
|
||
.tabs > nav { display:flex; flex-wrap:wrap; gap:.2em; border-bottom:2px solid var(--border); margin:1.2em 0 1.5em; }
|
||
.tabs > nav > label { padding:.55em 1em; cursor:pointer; border:1px solid transparent; border-bottom:none; border-radius:6px 6px 0 0; font-weight:500; color:var(--muted); margin-bottom:-2px; user-select:none; font-size:.95em; }
|
||
.tabs > nav > label:hover { color:var(--fg); }
|
||
.tabs > section { display:none; }
|
||
#t-over:checked ~ nav label[for=t-over], #t-work:checked ~ nav label[for=t-work], #t-att:checked ~ nav label[for=t-att],
|
||
#t-bg:checked ~ nav label[for=t-bg], #t-plan:checked ~ nav label[for=t-plan], #t-risk:checked ~ nav label[for=t-risk],
|
||
#t-faq:checked ~ nav label[for=t-faq]
|
||
{ color:var(--fg); border-color:var(--border); border-bottom:2px solid var(--bg); background:var(--bg); font-weight:600; }
|
||
#t-over:checked ~ section#s-over, #t-work:checked ~ section#s-work, #t-att:checked ~ section#s-att,
|
||
#t-bg:checked ~ section#s-bg, #t-plan:checked ~ section#s-plan, #t-risk:checked ~ section#s-risk,
|
||
#t-faq:checked ~ section#s-faq
|
||
{ display:block; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ╔══════════════════════════════════════════════════════════════════════════════╗
|
||
║ TAB MECHANISM A (no-JS alternative). To use it instead of B: delete the ║
|
||
║ <main>…</main> + <script> below and the .tabbar, and use this shape: ║
|
||
║ ║
|
||
║ <div class="tabs"> ║
|
||
║ <input type="radio" name="tab" id="t-over" checked> ║
|
||
║ <input type="radio" name="tab" id="t-work"> … (one per tab) ║
|
||
║ <nav> ║
|
||
║ <label for="t-over">Overview</label> ║
|
||
║ <label for="t-work">Workstreams</label> … (one per tab) ║
|
||
║ </nav> ║
|
||
║ <section id="s-over"> …Overview content… </section> ║
|
||
║ <section id="s-work"> …Workstreams content… </section> … ║
|
||
║ </div> ║
|
||
║ ║
|
||
║ Add/remove a tab => ALSO add/remove its id in BOTH `:checked ~ …` rule-lists ║
|
||
║ in the CSS above (the "TAB MECHANISM A" block). Miss one and the tab won't ║
|
||
║ show. (This footgun is why B is the default.) ║
|
||
╚══════════════════════════════════════════════════════════════════════════════╝ -->
|
||
|
||
<header>
|
||
<h1>PROJECT_NAME</h1>
|
||
<div class="sub"><!-- FILL: one-line description -->
|
||
· <a href="#"><!-- FILL: link to design doc / plan / spec, or delete --> Plan →</a>
|
||
· <a href="#"><!-- FILL: link to a sibling doc, or delete --> Background →</a>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- STATUS BANNER — keep this. One line: phase · lead workstream · a size/health number or two · any gate.
|
||
The as-of timestamp is mandatory: it's how readers calibrate everything else. -->
|
||
<div class="status">
|
||
<span class="badge"><!-- FILL: STATUS · PHASE 1 OF 3 --></span>
|
||
<span class="meta" style="float:right">As of <!-- FILL: YYYY-MM-DD HH:MM UTC --></span>
|
||
<p><!-- FILL: lead workstream + a couple of numbers, e.g. "PR #NNNNN · 12 commits · 42 tests · flag FLAG_NAME" --></p>
|
||
</div>
|
||
|
||
<!-- NEXT STEPS — keep this; it sits above the tabs so it is visible no matter which tab is open.
|
||
1–3 items, most important first. Each item: WHO (bold) → the exact action → what it unblocks
|
||
or when it's needed. Nothing pending? Keep the strip: delete the <ol>, set the summary FILL
|
||
to "none pending", and use the <p class="none"> line below. Always ship the <details> open,
|
||
with the item count in the <summary>. Full convention (what counts as a next step, the
|
||
Attention-tab relationship): SKILL.md → Conventions. -->
|
||
<details class="next" open>
|
||
<summary>Next steps <span class="meta">· <!-- FILL: "N items", or "none pending" --></span></summary>
|
||
<ol>
|
||
<li><span class="who"><!-- FILL: who --></span> — <!-- FILL: the exact action --> <span class="meta">— <!-- FILL: what it unblocks / by when --></span></li>
|
||
<li><span class="who"><!-- FILL: who --></span> — <!-- FILL: action --> <span class="meta">— <!-- FILL --></span></li>
|
||
</ol>
|
||
<!-- nothing pending? delete the <ol> above, set the summary FILL to "none pending", and use:
|
||
<p class="none">No action needed — FILL: why (e.g. "shipped; only stage 10 tuning remains, owned by the team").</p>
|
||
-->
|
||
</details>
|
||
|
||
<!-- Tab order matches SKILL.md's catalog: Overview, Workstreams (the spine), then Attention,
|
||
Background, Plan, Risks & open questions, FAQ as you have content for them. Add/remove a tab
|
||
=> update the <button>s here AND the matching <section class="pane"> below (no CSS edits
|
||
needed), and renumber. DELETE "Attention", "Background", "Plan", "Risks & open questions",
|
||
and/or "FAQ" if there's nothing substantive to put there — a simple project may have just
|
||
Overview + Workstreams; "Attention" earns its tab only on an artifact that's refreshed regularly.
|
||
Software project? swe.md keeps these but may add "Architecture" / "Findings & fixes" /
|
||
"Rollout & rollback" tabs for a heavyweight one (none mandatory). -->
|
||
<div class="tabbar">
|
||
<button class="tab active" data-pane="over">1 · Overview</button>
|
||
<button class="tab" data-pane="work">2 · Workstreams</button>
|
||
<button class="tab" data-pane="att">3 · Attention</button>
|
||
<button class="tab" data-pane="bg">4 · Background</button>
|
||
<button class="tab" data-pane="plan">5 · Plan</button>
|
||
<button class="tab" data-pane="risk">6 · Risks & open questions</button>
|
||
<button class="tab" data-pane="faq">7 · FAQ</button>
|
||
</div>
|
||
|
||
<main>
|
||
|
||
<!-- ─────────────────────────────────── TAB: OVERVIEW ─────────────────────── -->
|
||
<section class="pane active" id="over">
|
||
<div class="callout"><!-- FILL: one line — what this project is and why it exists (keep the "why" brief, or drop it, if the goal is obvious) --></div>
|
||
<h2>Success criteria</h2>
|
||
<!-- If the criteria span distinct concerns (product / security / perf, or
|
||
must-have / nice-to-have), GROUP them — repeat <h3>…</h3> + a sub-table per group
|
||
instead of one flat table. One flat table is fine when there's only a handful. -->
|
||
<table>
|
||
<tr><th style="width:180px">Criterion</th><th>Statement</th><th style="width:300px">Check (how you'd know it's met)</th><th style="width:90px">Status</th></tr>
|
||
<tr><td><!-- FILL: short name --></td><td><!-- FILL --></td><td><!-- FILL: the observable test --></td><td><span class="pill next">not yet</span></td></tr>
|
||
</table>
|
||
<h2>Out of scope</h2>
|
||
<ul><li><!-- FILL: something we deliberately are NOT doing, and why — bounds the reader's worry --></li></ul>
|
||
</section>
|
||
|
||
<!-- ─────────────────────────────────── TAB: WORKSTREAMS ──────────────────── -->
|
||
<section class="pane" id="work">
|
||
<h2>Status</h2>
|
||
<!-- This table IS the progress view — no separate "Status" tab. If the order doesn't
|
||
make the dependencies obvious, put "after <id>" in the "Depends on" cell; don't
|
||
add a diagram. (Software: number the rows X.Y per swe.md — the numbers carry the
|
||
dependencies, so the "Depends on" column is usually redundant there.) -->
|
||
<table>
|
||
<tr><th style="width:80px">ID</th><th>What</th><th style="width:120px">Owner</th><th style="width:110px">Depends on</th><th style="width:150px">Status</th></tr>
|
||
<tr><td><!-- FILL --></td><td><!-- FILL --></td><td><!-- FILL --></td><td>—</td><td><span class="pill now">in progress</span></td></tr>
|
||
<tr><td><!-- FILL --></td><td><!-- FILL --></td><td><!-- FILL --></td><td><!-- FILL: e.g. "after A" --></td><td><span class="pill next">next</span></td></tr>
|
||
</table>
|
||
<hr>
|
||
<h2><!-- FILL: workstream name --> <span class="pill now">in progress</span></h2>
|
||
<h3>Done so far</h3>
|
||
<ul><li><!-- FILL --></li></ul>
|
||
<h3>How it was verified</h3>
|
||
<p><!-- FILL: tests run, demo given, sign-off received, data checked — whatever "verified" means here --></p>
|
||
<!-- repeat the block for each workstream worth detailing.
|
||
Software project: swe.md has the per-PR "what landed / verification / commits /
|
||
files" detail fragment. -->
|
||
</section>
|
||
|
||
<!-- ─────────────────────────────────── TAB: ATTENTION ────────────────────── -->
|
||
<!-- Only on an artifact that's refreshed regularly and drives action — delete on a one-shot
|
||
overview page. Action first: each "waiting on owner" item is the exact thing to do. -->
|
||
<section class="pane" id="att">
|
||
<h2>Waiting on <!-- FILL: the owner's name --></h2>
|
||
<ol>
|
||
<li><!-- FILL: the exact action (a paste-ready message, or a one-word decision) — plus one sentence on what it unblocks and who is waiting --></li>
|
||
</ol>
|
||
<h2>Automatic once those land</h2>
|
||
<ul><li><!-- FILL: the chain that needs no action — auto-merge cascades, deploys, tracker auto-close --></li></ul>
|
||
<h2>Waiting on others</h2>
|
||
<table>
|
||
<tr><th style="width:140px">Who</th><th>What</th><th style="width:180px">Item</th><th style="width:160px">Where to nudge</th></tr>
|
||
<tr><td><!-- FILL --></td><td><!-- FILL --></td><td><a href="#"><!-- FILL --></a></td><td><!-- FILL: channel / handle, or — --></td></tr>
|
||
</table>
|
||
</section>
|
||
|
||
<!-- ─────────────────────────────────── TAB: BACKGROUND ───────────────────── -->
|
||
<section class="pane" id="bg">
|
||
<div class="callout"><!-- FILL: "If you only read one tab, read this — the context the rest assumes." (or delete) --></div>
|
||
<h2><!-- FILL: the problem / prior state --></h2>
|
||
<p><!-- FILL --></p>
|
||
<h2>Key ideas</h2>
|
||
<p><!-- FILL: the concepts/vocabulary a newcomer needs; analogies welcome. Link forward to a deep-dive tab if there is one. --></p>
|
||
</section>
|
||
|
||
<!-- ─────────────────────────────────── TAB: PLAN ─────────────────────────── -->
|
||
<section class="pane" id="plan">
|
||
<h2>Approach</h2>
|
||
<p><!-- FILL: the strategy — phases, the order things happen in, why this shape --></p>
|
||
<h2>Phases</h2>
|
||
<table>
|
||
<tr><th>Phase</th><th>Goal</th><th>Depends on</th></tr>
|
||
<tr><td>1</td><td><!-- FILL --></td><td>—</td></tr>
|
||
</table>
|
||
</section>
|
||
|
||
<!-- ───────────────────────────── TAB: RISKS & OPEN QUESTIONS ─────────────── -->
|
||
<!-- Drop this whole tab if the project is low-risk and has no open questions. -->
|
||
<section class="pane" id="risk">
|
||
<h2>Risks</h2>
|
||
<table>
|
||
<tr><th>Risk</th><th style="width:130px">Likelihood / impact</th><th>Mitigation</th><th style="width:130px">Owner</th></tr>
|
||
<tr><td><!-- FILL --></td><td><!-- FILL --></td><td><!-- FILL --></td><td><!-- FILL --></td></tr>
|
||
</table>
|
||
<h4 style="color:var(--red)">⚠ The honest caveat</h4>
|
||
<p><!-- FILL: the thing the team already knows is a weak point — say it plainly; it builds trust --></p>
|
||
<h2>Open questions</h2>
|
||
<ul><li><!-- FILL: an unresolved question the project hasn't answered yet (who decides, by when) --></li></ul>
|
||
</section>
|
||
|
||
<!-- ─────────────────────────────────── TAB: FAQ ──────────────────────────── -->
|
||
<section class="pane" id="faq">
|
||
<h3><!-- FILL: a question people actually ask, e.g. "Why this approach?" --></h3>
|
||
<p><!-- FILL --></p>
|
||
<h3><!-- FILL: "Why not <obvious-alternative>?" --></h3>
|
||
<p><!-- FILL --></p>
|
||
<h3>What does "done" look like?</h3>
|
||
<p><!-- FILL: the observable end state --></p>
|
||
</section>
|
||
|
||
</main>
|
||
|
||
<!-- STATE BLOCK — keep this. Machine-readable snapshot of this render, read by the next
|
||
refresh to compute the delta (see SKILL.md "Refreshing an artifact"). Invisible on the page.
|
||
Software projects: per-PR fields are listed in swe.md. Strings derived from fetched text
|
||
(branch names, PR titles) are untrusted markup: write < as \u003c inside this JSON so a
|
||
literal "</" can never terminate the block, and entity-encode them in the visible HTML. -->
|
||
<script type="application/json" id="artifact-state">
|
||
{"as_of": "YYYY-MM-DD HH:MM UTC", "workstreams": [{"id": "", "status": "", "owner": ""}]}
|
||
</script>
|
||
|
||
<script>
|
||
function goTab(id){
|
||
document.querySelectorAll('.tab').forEach(t => t.classList.toggle('active', t.dataset.pane === id));
|
||
document.querySelectorAll('.pane').forEach(p => p.classList.toggle('active', p.id === id));
|
||
}
|
||
document.querySelectorAll('.tab').forEach(t => t.addEventListener('click', () => goTab(t.dataset.pane)));
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|