|
@@ -90,6 +90,21 @@
|
|
|
.rel-row .grow{flex:1}
|
|
.rel-row .grow{flex:1}
|
|
|
.rel-row label{display:flex;align-items:center;gap:6px;color:var(--mut);font-size:12px;cursor:pointer}
|
|
.rel-row label{display:flex;align-items:center;gap:6px;color:var(--mut);font-size:12px;cursor:pointer}
|
|
|
.rel-row input[type=checkbox]{accent-color:var(--bad)}
|
|
.rel-row input[type=checkbox]{accent-color:var(--bad)}
|
|
|
|
|
+ /* one release = a row plus (optionally) its GitHub release notes, collapsed */
|
|
|
|
|
+ .rel-item{border-top:1px solid #232a3a}
|
|
|
|
|
+ .rel-item:first-child{border-top:0}
|
|
|
|
|
+ .rel-item .rel-row{border-top:0}
|
|
|
|
|
+ .pill.pre{background:#3a2f14;color:var(--warn)}
|
|
|
|
|
+ .rel-notes{margin:0 2px 10px;padding:0}
|
|
|
|
|
+ .rel-notes>summary{cursor:pointer;color:var(--acc2);font-size:12px;list-style:none;padding:2px 0}
|
|
|
|
|
+ .rel-notes>summary::-webkit-details-marker{display:none}
|
|
|
|
|
+ .rel-notes>summary::before{content:"\25B8 ";display:inline-block;transition:transform .1s}
|
|
|
|
|
+ .rel-notes[open]>summary::before{content:"\25BE "}
|
|
|
|
|
+ .rel-notes .body{white-space:pre-wrap;word-break:break-word;color:var(--fg);font-size:12.5px;
|
|
|
|
|
+ background:#151925;border:1px solid #232a3a;border-radius:8px;padding:9px 11px;margin-top:4px;
|
|
|
|
|
+ max-height:280px;overflow:auto}
|
|
|
|
|
+ .rel-notes .body code{background:#0f131c;border-radius:4px;padding:1px 4px;font-size:12px}
|
|
|
|
|
+ .rel-notes .body a{color:var(--acc2)}
|
|
|
/* firmware-tab gate: when the connected puck's firmware predates panel updates, both update cards go
|
|
/* firmware-tab gate: when the connected puck's firmware predates panel updates, both update cards go
|
|
|
inert (pointer-events kills clicks, drops, and the file picker) and the banner explains why */
|
|
inert (pointer-events kills clicks, drops, and the file picker) and the banner explains why */
|
|
|
.fwupcard.gated{opacity:.45;pointer-events:none;user-select:none}
|
|
.fwupcard.gated{opacity:.45;pointer-events:none;user-select:none}
|
|
@@ -2099,6 +2114,21 @@ function flashRelease(rel,factory){
|
|
|
+"automatic reboot. A failed or interrupted transfer leaves the current firmware untouched.",
|
|
+"automatic reboot. A failed or interrupted transfer leaves the current firmware untouched.",
|
|
|
()=>downloadAsset(asset));
|
|
()=>downloadAsset(asset));
|
|
|
}
|
|
}
|
|
|
|
|
+// Release notes come straight from GitHub (rel.body: markdown written by a human in the release UI).
|
|
|
|
|
+// Render a deliberately small subset -- headings, bullets, `code`, **bold**, links, and bare/markdown URLs
|
|
|
|
|
+// -- from ESCAPED text, so nothing in a release body can inject markup into the panel.
|
|
|
|
|
+function relNotesHtml(md){
|
|
|
|
|
+ const esc=t=>t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
|
|
|
|
+ return esc((md||"").replace(/\r\n/g,"\n").trim())
|
|
|
|
|
+ .replace(/`([^`\n]+)`/g,(m,c)=>"<code>"+c+"</code>")
|
|
|
|
|
+ .replace(/\*\*([^*\n]+)\*\*/g,(m,b)=>"<b>"+b+"</b>")
|
|
|
|
|
+ .replace(/^(#{1,6})\s+(.+)$/gm,(m,h,t)=>"<b>"+t+"</b>")
|
|
|
|
|
+ .replace(/^\s*[-*]\s+/gm,"• ")
|
|
|
|
|
+ .replace(/\[([^\]\n]+)\]\((https?:\/\/[^\s)]+)\)/g,
|
|
|
|
|
+ (m,t,u)=>'<a href="'+u+'" target="_blank" rel="noopener">'+t+"</a>")
|
|
|
|
|
+ .replace(/(^|[\s(])(https?:\/\/[^\s<)]+)/g,
|
|
|
|
|
+ (m,pre,u)=>pre+'<a href="'+u+'" target="_blank" rel="noopener">'+u+"</a>");
|
|
|
|
|
+}
|
|
|
function renderReleases(){
|
|
function renderReleases(){
|
|
|
const list=$("#relList");
|
|
const list=$("#relList");
|
|
|
list.textContent="";
|
|
list.textContent="";
|
|
@@ -2107,10 +2137,11 @@ function renderReleases(){
|
|
|
const std=relAsset(rel,false), fac=relAsset(rel,true);
|
|
const std=relAsset(rel,false), fac=relAsset(rel,true);
|
|
|
if(!std && !fac) continue;
|
|
if(!std && !fac) continue;
|
|
|
shown++;
|
|
shown++;
|
|
|
|
|
+ const item=document.createElement("div"); item.className="rel-item";
|
|
|
const row=document.createElement("div"); row.className="rel-row";
|
|
const row=document.createElement("div"); row.className="rel-row";
|
|
|
const ver=document.createElement("span"); ver.className="ver"; ver.textContent=rel.tag_name;
|
|
const ver=document.createElement("span"); ver.className="ver"; ver.textContent=rel.tag_name;
|
|
|
const date=document.createElement("span"); date.className="date";
|
|
const date=document.createElement("span"); date.className="date";
|
|
|
- date.textContent=(rel.published_at||"").slice(0,10)+(rel.prerelease?" · pre":"");
|
|
|
|
|
|
|
+ date.textContent=(rel.published_at||"").slice(0,10);
|
|
|
const name=document.createElement("span"); name.className="grow note"; name.style.margin="0";
|
|
const name=document.createElement("span"); name.className="grow note"; name.style.margin="0";
|
|
|
name.textContent=rel.name && rel.name!==rel.tag_name ? rel.name : "";
|
|
name.textContent=rel.name && rel.name!==rel.tag_name ? rel.name : "";
|
|
|
const lab=document.createElement("label");
|
|
const lab=document.createElement("label");
|
|
@@ -2121,6 +2152,15 @@ function renderReleases(){
|
|
|
const btn=document.createElement("button"); btn.textContent="Flash "+rel.tag_name;
|
|
const btn=document.createElement("button"); btn.textContent="Flash "+rel.tag_name;
|
|
|
btn.onclick=()=>flashRelease(rel,cb.checked);
|
|
btn.onclick=()=>flashRelease(rel,cb.checked);
|
|
|
row.append(ver,date,name);
|
|
row.append(ver,date,name);
|
|
|
|
|
+ // Pre-release marking is read live from the GitHub API on every refresh, so un-marking a release on
|
|
|
|
|
+ // GitHub makes the badge disappear here as soon as the list is reloaded.
|
|
|
|
|
+ if(rel.prerelease){
|
|
|
|
|
+ const pre=document.createElement("span"); pre.className="pill pre";
|
|
|
|
|
+ pre.textContent="pre-release";
|
|
|
|
|
+ pre.title="Marked as a pre-release on GitHub: a test build, not the recommended version. "
|
|
|
|
|
+ +"It flashes like any other release, and the update notice never points at it.";
|
|
|
|
|
+ row.appendChild(pre);
|
|
|
|
|
+ }
|
|
|
if(relPanelUpdatable(rel)===false){
|
|
if(relPanelUpdatable(rel)===false){
|
|
|
const pill=document.createElement("span"); pill.className="pill dn";
|
|
const pill=document.createElement("span"); pill.className="pill dn";
|
|
|
pill.textContent="no panel updates";
|
|
pill.textContent="no panel updates";
|
|
@@ -2129,7 +2169,16 @@ function renderReleases(){
|
|
|
row.appendChild(pill);
|
|
row.appendChild(pill);
|
|
|
}
|
|
}
|
|
|
row.append(lab,btn);
|
|
row.append(lab,btn);
|
|
|
- list.appendChild(row);
|
|
|
|
|
|
|
+ item.appendChild(row);
|
|
|
|
|
+ if((rel.body||"").trim()){
|
|
|
|
|
+ const det=document.createElement("details"); det.className="rel-notes";
|
|
|
|
|
+ const sum=document.createElement("summary"); sum.textContent="release notes";
|
|
|
|
|
+ const body=document.createElement("div"); body.className="body";
|
|
|
|
|
+ body.innerHTML=relNotesHtml(rel.body);
|
|
|
|
|
+ det.append(sum,body);
|
|
|
|
|
+ item.appendChild(det);
|
|
|
|
|
+ }
|
|
|
|
|
+ list.appendChild(item);
|
|
|
}
|
|
}
|
|
|
if(!shown) list.textContent="no releases with OpenPuck .uf2 assets found";
|
|
if(!shown) list.textContent="no releases with OpenPuck .uf2 assets found";
|
|
|
}
|
|
}
|
|
@@ -2139,8 +2188,10 @@ async function loadReleases(force){
|
|
|
list.textContent="loading…";
|
|
list.textContent="loading…";
|
|
|
try{
|
|
try{
|
|
|
const [r,mr]=await Promise.all([
|
|
const [r,mr]=await Promise.all([
|
|
|
|
|
+ // no-store: the pre-release flag and the notes are edited on GitHub after the fact, and a cached
|
|
|
|
|
+ // list would keep showing the stale state after a refresh.
|
|
|
fetch("https://api.github.com/repos/"+REL_REPO+"/releases?per_page=15",
|
|
fetch("https://api.github.com/repos/"+REL_REPO+"/releases?per_page=15",
|
|
|
- {headers:{Accept:"application/vnd.github+json"}}),
|
|
|
|
|
|
|
+ {cache:"no-store",headers:{Accept:"application/vnd.github+json"}}),
|
|
|
fetch("https://raw.githubusercontent.com/"+REL_REPO+"/firmware/manifest.json",{cache:"no-store"})
|
|
fetch("https://raw.githubusercontent.com/"+REL_REPO+"/firmware/manifest.json",{cache:"no-store"})
|
|
|
.catch(()=>null),
|
|
.catch(()=>null),
|
|
|
]);
|
|
]);
|