UpToDate iteration 2: clear monitor CTA and public GitHub issue intake
This commit is contained in:
1 parent
5425f8f43c
commit
26aa2b289d
3 files changed
+20
-11
No files matched your search
@@ -14,7 +14,7 @@ else if(path.includes('.github/workflows/')){for(const m of text.matchAll(/\buse
|
||||
else {add(path.split('/').pop(),'Detected','stack file');gaps.push(`${path}: detected but full parser unavailable (including yarn/pnpm/Python project lock formats).`);}
|
||||
}catch(e){gaps.push(`${path}: parse failed: ${e.message}`);}}
|
||||
function signal(title,detail,url,date,severity='info'){report.signals.push({title,detail,url,date,severity});}
|
||||
async function scan(){if($('run').disabled)return;let repo;try{repo=parseRepo($('repo').value)}catch(e){$('status').textContent=e.message;return;}$('run').disabled=true;$('demo').disabled=true;$('export').disabled=true;$('results').hidden=true;
|
||||
async function scan(){if($('run').disabled)return;let repo;try{repo=parseRepo($('repo').value)}catch(e){$('status').textContent=e.message;return;}$('run').disabled=true;$('demo').disabled=true;$('export').disabled=true;$('results').hidden=true;$('onboarding').hidden=true;$('public-consent').checked=false;
|
||||
report={repo,checkedAt:new Date().toISOString(),items:[],signals:[],gaps:[],files:[]};const gaps=report.gaps;try{
|
||||
$('status').textContent='Reading repository metadata…';const api=`https://api.github.com/repos/${repo}`;const meta=await json(api);const branch=meta.default_branch;const commit=await json(`${api}/commits/${encodeURIComponent(branch)}`);report.commit=commit.sha;report.branch=branch;
|
||||
const tree=await json(`${api}/git/trees/${commit.sha}?recursive=1`);if(tree.truncated)gaps.push('GitHub tree is truncated; inventory may omit files.');const paths=tree.tree.filter(f=>f.type==='blob'&&/(^|\/)(package\.json|package-lock\.json|yarn\.lock|pnpm-lock\.yaml|requirements[^/]*\.txt|pyproject\.toml|poetry\.lock|uv\.lock|go\.mod|Cargo\.toml|Dockerfile[^/]*|\.nvmrc|\.node-version)$|(^|\/)\.github\/workflows\/[^/]+\.ya?ml$/.test(f.path));if(paths.length>24)gaps.push(`${paths.length} relevant files found; first 24 scanned.`);
|
||||
@@ -29,5 +29,14 @@ const signature=JSON.stringify(report.items.map(i=>[i.name,i.version,i.path]));r
|
||||
$('status').textContent=`Scan complete · ${report.checkedAt} · ${repo}`;
|
||||
}catch(e){gaps.push(`Scan incomplete: ${e.message}. Check repository visibility, connectivity and GitHub's unauthenticated rate limit (usually 60 requests/hour/IP). Retry later; no clean bill of health is implied.`);$('status').textContent='Scan incomplete — see unknowns below.';}
|
||||
render();$('run').disabled=false;$('demo').disabled=false;$('export').disabled=false;}
|
||||
function render(){$('results').hidden=false;$('summary').innerHTML=`<p><span class="badge">${report.files.length?'Live API evidence':'No repository evidence collected'}</span> ${report.items.length} observations · ${report.files.length} files · ${esc(report.checkedAt)}</p><p>${esc(report.comparison||'Incomplete scan; no baseline saved.')} ${report.commit?link(`https://github.com/${report.repo}/tree/${report.commit}`,`Source ${report.commit.slice(0,8)}`):''}</p>`;$('inventory').innerHTML=report.items.map(i=>`<tr><td>${esc(i.name)}<br><small>${esc(i.ecosystem)}</small></td><td>${esc(i.version)}</td><td>${link(i.url,i.path)}</td><td>${esc(i.coverage)}</td></tr>`).join('');$('signals').innerHTML=report.signals.map(s=>`<article class="${s.severity==='alert'?'alert':''}"><strong>${esc(s.title)}</strong><p>${esc(s.detail)}</p><small>${link(s.url,'Source')} · source/checked time: ${esc(s.date)}</small></article>`).join('')||'<p>No verified signals collected. See coverage gaps.</p>';$('gaps').innerHTML=report.gaps.map(g=>`<li>${esc(g)}</li>`).join('');}
|
||||
$('scan').onsubmit=e=>{e.preventDefault();scan();};$('demo').onclick=()=>{$('repo').value='https://github.com/expressjs/express';scan();};$('interval').onchange=()=>{clearInterval(timer);if(+$('interval').value)timer=setInterval(scan,+$('interval').value);};$('export').onclick=()=>{const url=URL.createObjectURL(new Blob([JSON.stringify(report,null,2)],{type:'application/json'}));const a=document.createElement('a');a.href=url;a.download='up-to-date-evidence.json';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);};if(new URLSearchParams(location.search).get('joined')==='1')$('join').innerHTML='<p>Thanks — your interest has been recorded.</p>';
|
||||
function render(){$('results').hidden=false;$('onboarding').hidden=!report.files.length;$('summary').innerHTML=`<p><span class="badge">${report.files.length?'Live API evidence':'No repository evidence collected'}</span> ${report.items.length} observations · ${report.files.length} files · ${esc(report.checkedAt)}</p><p>${esc(report.comparison||'Incomplete scan; no baseline saved.')} ${report.commit?link(`https://github.com/${report.repo}/tree/${report.commit}`,`Source ${report.commit.slice(0,8)}`):''}</p>`;$('inventory').innerHTML=report.items.map(i=>`<tr><td>${esc(i.name)}<br><small>${esc(i.ecosystem)}</small></td><td>${esc(i.version)}</td><td>${link(i.url,i.path)}</td><td>${esc(i.coverage)}</td></tr>`).join('');$('signals').innerHTML=report.signals.map(s=>`<article class="${s.severity==='alert'?'alert':''}"><strong>${esc(s.title)}</strong><p>${esc(s.detail)}</p><small>${link(s.url,'Source')} · source/checked time: ${esc(s.date)}</small></article>`).join('')||'<p>No verified signals collected. See coverage gaps.</p>';$('gaps').innerHTML=report.gaps.map(g=>`<li>${esc(g)}</li>`).join('');}
|
||||
$('scan').onsubmit=e=>{e.preventDefault();scan();};$('demo').onclick=()=>{$('repo').value='https://github.com/expressjs/express';scan();};$('interval').onchange=()=>{clearInterval(timer);if(+$('interval').value)timer=setInterval(scan,+$('interval').value);};$('export').onclick=()=>{const url=URL.createObjectURL(new Blob([JSON.stringify(report,null,2)],{type:'application/json'}));const a=document.createElement('a');a.href=url;a.download='up-to-date-evidence.json';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);};
|
||||
function issueDraft(){
|
||||
if(!report?.files.length)throw new Error('Scan a public repo before requesting monitoring.');
|
||||
const repoUrl='https://github.com/'+report.repo;
|
||||
const request=`UpToDate monitoring request\n\nRepository: ${repoUrl}\nRequested cadence: ${$('cadence').value}\nNotification preference: ${$('notification').value}\n\nFirst scan: ${report.checkedAt}\nSource commit: ${report.commit}\n\nI understand this is an early-access request. Hosted monitoring is NOT active until confirmed on this issue. No credentials or private details are included.`;
|
||||
const query=new URLSearchParams({template:'early-access.yml',title:'Early access: UpToDate — '+report.repo,venture:'up-to-date',plan:'Free / hosted monitoring request',use:request});
|
||||
return 'https://github.com/teahubxyz/time/issues/new?'+query;
|
||||
}
|
||||
$('request').onsubmit=e=>{e.preventDefault();if(!$('public-consent').checked)return;const url=issueDraft();$('request-status').textContent='Opening your draft. Submit it on GitHub to request monitoring; hosted checks remain inactive.';location.assign(url);};
|
||||
document.querySelectorAll('a[href="#scan"]').forEach(a=>a.addEventListener('click',()=>setTimeout(()=>$('repo').focus({preventScroll:true}),300)));
|
||||
@@ -1,8 +1,7 @@
|
||||
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>UpToDate · Stack monitor</title>
|
||||
<style>:root{font-family:Georgia,serif;color:#203b36;background:#f4f2e9}*{box-sizing:border-box}body{margin:0}header,main,footer{max-width:1160px;margin:auto;padding:28px}header{display:flex;justify-content:space-between;border-bottom:1px solid #bbc5b8}a{color:#176e59}h1{font-size:clamp(38px,6vw,70px);font-weight:400;margin:20px 0}h2{font-weight:400}p{line-height:1.6}small,label,button,input,select,.meta,th,td{font-family:Arial,sans-serif}small,.meta{font-size:13px;color:#53645e}.intro{max-width:700px}.scan{display:flex;gap:10px;flex-wrap:wrap;background:#e4e9de;padding:20px;border:1px solid #b8c5b3}input{min-width:180px;flex:1;padding:14px;font-size:16px;border:1px solid #84988d}button,select{padding:14px;border:1px solid #176e59;background:#176e59;color:white;font-size:15px;cursor:pointer}button:disabled{opacity:.5}.secondary{background:transparent;color:#176e59}table{border-collapse:collapse;width:100%;font-size:14px}th,td{text-align:left;padding:14px 10px;border-bottom:1px solid #cbd2c6;vertical-align:top;overflow-wrap:anywhere}th{font-size:12px;text-transform:uppercase}article{padding:18px;border-left:3px solid #95a68c;background:#fffdf6;margin:12px 0}article.alert{border-color:#b6502d}#status{padding:18px 0;white-space:pre-wrap}#results{overflow-x:auto}.badge{font:12px Arial;padding:4px 8px;background:#dce9dc;display:inline-block}details{margin:20px 0}summary{cursor:pointer}#gaps{line-height:1.7}.bar{display:flex;gap:16px;flex-wrap:wrap;align-items:center}footer{border-top:1px solid #bbc5b8}input:focus,button:focus{outline:3px solid #a0bca4}@media(max-width:600px){header,main,footer{padding:18px}th,td{padding:10px 5px}table{min-width:620px}}
|
||||
</style><header><strong>NotPlanned / UpToDate</strong><small>01 — monitor first</small></header><main><div class="intro"><h1>Your stack moves.<br>Keep an eye on it.</h1><p>Inspect a public GitHub repository. See what it runs, what changed upstream, and which exact versions have published advisories. Read the evidence before deciding what to change.</p></div>
|
||||
<form id="scan" class="scan"><label for="repo" style="width:100%">Public GitHub repository</label><input id="repo" required value="https://github.com/expressjs/express" aria-label="Public GitHub repository"><button id="run">Inspect stack →</button><button type="button" id="demo" class="secondary">Try Express</button></form>
|
||||
<div class="bar"><label>Refresh while open <select id="interval"><option value="0">Manual</option><option value="900000">Every 15 minutes</option><option value="3600000">Every hour</option></select></label><button id="export" class="secondary" disabled>Download evidence</button></div><p class="meta">Live, unauthenticated APIs. No tokens, repository writes, or background service. This unlisted link is publicly accessible, not access-controlled.</p><div id="status" role="status" aria-live="polite">Ready. The Express example is a real repository, not sample data.</div><section id="results" hidden><div id="summary"></div><h2>Stack inventory</h2><table><thead><tr><th>Component</th><th>Observed version</th><th>Evidence</th><th>Coverage</th></tr></thead><tbody id="inventory"></tbody></table><h2>Signals & impact</h2><div id="signals"></div><details open><summary>Unknowns & scan boundaries</summary><ul id="gaps"></ul></details></section>
|
||||
<details><summary>How this taste test works</summary><p>We read up to 24 matching files from the default branch, including nested manifests and CI files; check up to 12 dependencies; and keep source links and timestamps. Exact npm lockfile / Python pins can be checked against OSV. Ranges are declarations, not proof of installed versions. Docker tags and Action refs are inventoried; their vulnerability coverage is unknown. Refresh compares the inventory with your previous scan in this tab. Close the tab and monitoring stops.</p></details>
|
||||
<details id="join"><summary>Follow the experiment</summary><p>Free: this browser monitor. Hosted alerts and team monitoring are early-access ideas, with no checkout or paid service yet.</p><form method="post" action="/api/signup"><input type="hidden" name="venture" value="up-to-date"><input type="hidden" name="plan" value="free"><input name="website" hidden tabindex="-1"><label>Email <input name="email" type="email" required></label><button>Request updates</button></form><p class="meta">Email is stored privately by NotPlanned for experiment updates.</p></details>
|
||||
</main><footer><small>From Chai’s idea · <a href="https://teahub.xyz/matcha/teahub/issues/85">Factory issue #85</a> · No autonomous maintenance in v0.</small></footer><script src="app.js"></script></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>UpToDate — Know what your stack needs next</title><link rel="stylesheet" href="style.css"></head><body>
|
||||
<header><a class="brand" href="#">↗ UpToDate<span>by NotPlanned</span></a><a class="button small" href="#scan">Monitor a repo ↗</a></header>
|
||||
<main><section class="hero"><div><p class="eyebrow">LESS NOISE. MORE CONTEXT.</p><h1>Your code stays put.<br><em>The world doesn’t.</em></h1><p class="lede">Catch upstream releases, deprecated dependencies, and published security advisories before they become your next surprise.</p><a class="button" href="#scan">Monitor a repo ↗</a><p class="meta">Free first scan · Public GitHub repos · No signup</p></div><aside class="preview"><div class="preview-top"><span class="dot"></span> YOUR NEXT CHECK-IN <span>01</span></div><h2>A clearer picture<br>of your stack.</h2><div class="preview-row"><b>01</b><div>Know what’s there<small>Dependencies, runtime declarations & CI</small></div><span>↗</span></div><div class="preview-row"><b>02</b><div>See what moved<small>Live releases & exact-version advisories</small></div><span>↗</span></div><div class="preview-row"><b>03</b><div>Make the next call<small>Evidence links, timestamps & coverage gaps</small></div><span>↗</span></div><p class="meta">A guide to what deserves review. Every finding has a source.</p></aside></section>
|
||||
<section class="scan-panel"><div class="section-heading"><div><p class="eyebrow">01 / TAKE A LOOK</p><h2>Start with one repository.</h2></div><span class="badge">No account needed</span></div><form id="scan"><label for="repo">Public GitHub repository</label><div class="input-row"><input id="repo" required placeholder="https://github.com/owner/repository" autocomplete="off" spellcheck="false"><button id="run">Scan my repo ↗</button></div><button type="button" id="demo" class="text-button">Just exploring? Try Express →</button></form><p id="status" role="status" aria-live="polite">Paste a public repository URL to get your first check-in.</p><div class="bar"><label>Refresh in this tab <select id="interval"><option value="0">Manual</option><option value="900000">Every 15 minutes</option><option value="3600000">Every hour</option></select></label><button id="export" class="secondary" disabled>Download evidence ↓</button></div><p class="meta">Monitoring runs only while this tab is open. Close it and checks stop.</p></section>
|
||||
<section id="results" hidden><div id="summary"></div><section id="onboarding" class="onboarding" hidden><div><p class="eyebrow">02 / KEEP THE THREAD</p><h2>Keep this repo monitored.</h2><p>Request hosted monitoring through a GitHub issue. We’ll reply on that issue to confirm availability and next steps.</p><p class="badge">Hosted monitoring: not active</p></div><form id="request"><label for="cadence">Requested cadence</label><select id="cadence"><option>Weekly</option><option>Daily</option><option>Security alerts only</option></select><label for="notification">Notification preference</label><select id="notification"><option>Reply on the GitHub issue</option><option>No notifications; I’ll check the issue</option></select><label class="consent"><input id="public-consent" type="checkbox" required> I understand the repo URL and preferences will appear in a public GitHub issue. No secrets or private details.</label><button>Review request on GitHub ↗</button><p class="meta">GitHub sign-in required. Review and submit there; opening the draft does not activate monitoring.</p><p id="request-status" role="status"></p></form></section><h2>Your check-in</h2><div id="signals"></div><details><summary>Stack inventory</summary><div class="table-wrap"><table><thead><tr><th>Component</th><th>Observed version</th><th>Evidence</th><th>Coverage</th></tr></thead><tbody id="inventory"></tbody></table></div></details><details><summary>Unknowns & scan boundaries</summary><ul id="gaps"></ul></details></section>
|
||||
<section class="explain"><p class="eyebrow">MONITOR FIRST. MAINTAIN WHEN YOU’RE READY.</p><h2>Context before changes.</h2><p>UpToDate reads declared stack files and checks live upstream sources. It never changes your code. Exact npm lockfile versions and Python pins can be checked against OSV; version ranges aren’t proof of what’s installed.</p><details><summary>What this scan covers</summary><p>Up to 24 matching files and 12 unique dependencies on the default branch. Docker and Actions are inventory only. Unsupported parsers, unavailable APIs and rate limits appear as unknowns. This page is public; private repositories and unattended monitoring are unavailable. Hosted monitoring is an early-access request, not an active service.</p></details></section>
|
||||
</main><footer><a class="brand" href="#">↗ UpToDate</a><span>Monitor first. Decide with evidence.</span><a href="https://teahub.xyz/matcha/teahub/issues/85">About this experiment ↗</a></footer><script src="app.js"></script></body></html>
|
||||
@@ -0,0 +1 @@
|
||||
:root{font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:#172f36;background:#f7f8f6;font-synthesis:none}*{box-sizing:border-box}html{scroll-behavior:smooth;scroll-padding-top:24px}body{margin:0}a{color:inherit}header,main,footer{max-width:1180px;margin:auto}header{display:flex;align-items:center;justify-content:space-between;padding:25px 32px;border-bottom:1px solid #dce3df}.brand{font-size:24px;font-weight:750;text-decoration:none;letter-spacing:-1px}.brand span{display:block;font-size:11px;letter-spacing:1px;color:#607570;margin:4px 0 0 25px}main{padding:0 32px}.hero{display:grid;grid-template-columns:1.25fr 1fr;gap:70px;align-items:center;padding:74px 0 68px}.eyebrow{font-size:11px;font-weight:750;letter-spacing:2px;color:#52756a}h1{font-size:clamp(42px,5.3vw,65px);line-height:1.08;letter-spacing:-3px;margin:24px 0}h1 em{font-style:normal;color:#4d796b}h2{font-size:30px;letter-spacing:-1px;line-height:1.2;margin:12px 0 20px}p{line-height:1.65}.lede{font-size:18px;color:#566b6e;max-width:520px;margin-bottom:28px}.button,button{display:inline-block;text-decoration:none;background:#b9ef72;color:#18392d;border:1px solid #addc6b;border-radius:8px;padding:16px 23px;font:650 15px inherit;font-family:inherit;font-size:15px;font-weight:650;cursor:pointer}.small{padding:11px 17px;font-size:13px}.meta,small{font-size:12px;color:#667976;line-height:1.6}.preview{background:#173c37;color:#edf3e9;border-radius:16px;padding:27px 30px;box-shadow:0 18px 45px #183d3712}.preview h2{font-size:32px;margin-top:28px}.preview-top{font-size:10px;letter-spacing:1.5px;display:flex;align-items:center;gap:8px}.preview-top span:last-child{margin-left:auto}.dot{width:7px;height:7px;border-radius:100%;background:#b9ef72}.preview-row{display:flex;align-items:center;gap:16px;border-top:1px solid #ffffff24;padding:19px 0;font-size:14px}.preview-row b{color:#b9ef72;font-size:11px}.preview-row span:last-child{margin-left:auto;color:#b9ef72}.preview small{display:block;color:#b5c9bd;font-size:11px}.preview .meta{color:#b5c9bd;margin-bottom:0}.scan-panel{border:1px solid #d8e2db;border-radius:14px;padding:30px;background:white;scroll-margin-top:20px}.section-heading{display:flex;justify-content:space-between;align-items:center}.badge{display:inline-block;font-size:11px;font-weight:600;padding:7px 10px;border-radius:5px;background:#e8f0e4;color:#385c49}label{font-size:13px;display:block;margin:12px 0 8px}input,select{font:inherit;font-size:16px;background:#fff;border:1px solid #becdc5;border-radius:7px;padding:14px;color:#172f36;max-width:100%;min-width:0}.input-row{display:flex;gap:10px}.input-row input{flex:1}.text-button{background:none;border:0;padding:14px 0;color:#496b5f;font-size:13px}.bar{display:flex;gap:16px;align-items:center;flex-wrap:wrap}.bar label{margin:0;font-size:12px}.bar select{font-size:12px;padding:8px}.secondary{background:transparent;border:1px solid #cad6cf;font-size:12px;padding:9px}#status{font-size:13px;margin:7px 0 18px;overflow-wrap:anywhere}button:disabled{opacity:.5;cursor:wait}#results{margin:35px 0}#summary{font-size:13px;padding:10px 0}.onboarding{display:grid;grid-template-columns:1fr 1fr;gap:45px;padding:30px;background:#edf2e7;border:1px solid #d5e0cd;border-radius:14px;margin:15px 0 35px}.onboarding p{font-size:14px}.onboarding select{width:100%;font-size:14px}.consent{display:flex;align-items:flex-start;gap:9px;font-size:12px;line-height:1.6;margin:18px 0}.consent input{flex:none;margin-top:3px;width:17px;height:17px}.onboarding button{width:100%}.onboarding .meta{font-size:11px}article{background:white;border:1px solid #d8e2db;border-radius:10px;padding:20px;margin:12px 0;font-size:14px}article.alert{border-left:4px solid #b46c31}article p{color:#586b68}article a{color:#39725f}details{border-top:1px solid #dce3df;margin:20px 0;padding-top:18px}summary{cursor:pointer;font-size:14px;font-weight:600}.table-wrap{overflow:auto}table{border-collapse:collapse;width:100%;font-size:12px}th,td{padding:12px 8px;text-align:left;border-bottom:1px solid #dce3df;overflow-wrap:anywhere;vertical-align:top}th{font-size:10px;text-transform:uppercase}#gaps{font-size:13px;line-height:1.8;overflow-wrap:anywhere}.explain{max-width:650px;padding:65px 0 50px}.explain p{font-size:14px;color:#586b68}footer{display:flex;align-items:center;justify-content:space-between;gap:20px;border-top:1px solid #dce3df;padding:25px 32px;font-size:11px;color:#667976}footer .brand{font-size:19px;color:#172f36}:focus-visible{outline:3px solid #6f9f51;outline-offset:3px}[hidden]{display:none!important}@media(max-width:700px){header{padding:20px}.brand{font-size:21px}main{padding:0 20px}.hero{grid-template-columns:1fr;gap:30px;padding:40px 0}h1{letter-spacing:-2px}.preview{padding:24px}.scan-panel{padding:21px}.section-heading{display:block}.section-heading .badge{margin-bottom:15px}.input-row{flex-direction:column}.input-row button{width:100%}.onboarding{grid-template-columns:1fr;gap:8px;padding:22px}h2{font-size:27px}.eLine truncated
|
||||
Reference in new issue
Block a user