app
Adom Screensaver
Public Made by Adomby adom
Turn your laptop into an always-on vibe coding server: branded screensaver + native keep-awake (no PowerToys) + honest, hardware-aware power management.
← Commit history
Release 0.8.0: honor Dim-after (#57) + brightness-after-saver setting + deprecation filter (#461) + #365 resume/dock backlight restore
3 files changed
+40−13
dist/win/adom-stayawake.exe⋯ 1 unchanged line ⋯
dist/win/billboard.html+5−3@@ -430,8 +430,9 @@ function footText(s, i, n){ const right = (status.power||"") + (status.off>=0 ? " · display turns off in "+mmss(status.off) : "") + ka; const seen = " · shown " + getCount(s.slug) + "×"; // lifetime view count (proves the deprioritizer is working) // the priority score that ordered this slide (higher = shown sooner), with its breakdown so it's transparent- var fS = freshScore(s.updated), nS = noveltyScore(s.slug);- const score = " · priority " + Math.round(s._score != null ? s._score : (fS + nS)) + " (fresh " + fS + " + new " + nS + ")";+ var fS = freshScore(s.updated), nS = noveltyScore(s.slug), aS = (s.scoreAdj|0);+ const adjStr = aS ? (aS > 0 ? " + admin " + aS : " − admin " + Math.abs(aS)) : "";+ const score = " · priority " + Math.round(s._score != null ? s._score : (fS + nS + aS)) + " (fresh " + fS + " + new " + nS + adjStr + ")"; const as = (me && me.name) ? " · viewing as " + me.name : ""; return s.type + " " + (i+1) + " / " + n + seen + score + as + " " + right; }@@ -612,7 +613,8 @@ function freshScore(updated){ if(d < 7) return 100; if(d < 30) return 60; if(d < 90) return 35; if(d < 365) return 15; return 5; } function noveltyScore(slug){ return Math.max(0, 60 - 20*(counts[slug]||0)); }-function slideScore(s){ return freshScore(s.updated) + noveltyScore(s.slug) + Math.random()*12; }+// + the admin curation nudge (s.scoreAdj, e.g. -50 to sink a WIP repo like the adom-usb pages out of rotation).+function slideScore(s){ return freshScore(s.updated) + noveltyScore(s.slug) + (s.scoreAdj|0) + Math.random()*12; } // Order the rotation by score, highest first, so new + never-seen drops lead and stale repeats fall back. function shuffleSlides(){ slides.forEach(function(s){ s._score = slideScore(s); });
dist/win/config.html+35−10@@ -166,7 +166,7 @@ .cscore .big { font-size:21px; font-weight:700; color:var(--tealbri); font-variant-numeric:tabular-nums; line-height:1; } .cscore .brk { font-size:10px; color:var(--lo); margin-top:5px; line-height:1.5; } .cscore .brk .f { color:#7cc6ff; font-weight:600; } .cscore .brk .n { color:#5dd879; font-weight:600; }- .cscore .brk .j { color:var(--dim); }+ .cscore .brk .adj { color:#f0c548; font-weight:700; } .cscore .brk .j { color:var(--dim); } .cdlbtn { font-family:inherit; font-size:10.5px; font-weight:600; color:var(--tealbri); background:#0f141b; border:1px solid var(--line); border-radius:6px; padding:6px 11px; cursor:pointer; margin-top:8px; white-space:nowrap; } .cdlbtn:hover { border-color:var(--teal); } .cdlbtn:disabled { opacity:.45; cursor:default; }@@ -245,7 +245,7 @@ <div class="trow"> <div> <div class="tlab"><span class="step">5</span> Dim the screen after</div>- <div class="twhy" id="restWhy">After the screensaver starts, the billboards play for this long, then it rests: plugged in it dims the <b>backlight to near-black</b> (the laptop stays <b>awake</b> so your /remote-control sessions keep running), on battery it powers the panel off to save battery. The dim plus slow drift are what prevent burn-in.</div>+ <div class="twhy" id="restWhy">After the screensaver starts, the billboards play for this long, then it rests: plugged in it dims the <b>backlight to near-black</b> (the laptop stays <b>awake</b> so your /remote-control sessions keep running), on battery it powers the panel off to save battery. The dim plus slow drift are what prevent burn-in. <b>Never</b> keeps the billboards up while plugged in (on battery the panel still powers off after 8 minutes to protect the battery); burn-in protection then relies on the rotation and slow drift alone.</div> </div> <select id="restSel"> <option value="60">1 min</option><option value="120">2 min</option><option value="180">3 min</option>@@ -264,6 +264,22 @@ <label class="switch"><input type="checkbox" id="compToggle"><span class="track"></span></label> </div> </div>++ <div class="tune" style="margin-top:16px">+ <div class="trow" style="border-top:none">+ <div>+ <div class="tlab">Screen brightness after the saver exits</div>+ <div class="twhy">Normally the panel returns to your usual brightness when the saver ends. For an <b>always-on, remotely-accessed display</b> (e.g. a conference-room laptop you wake over RustDesk), have it <b>hold dim</b> instead — the remote view isn't affected by the physical backlight, so a dim panel <b>saves the OLED</b> while you still see everything.</div>+ </div>+ <select id="wakeBriSel">+ <option value="-1">Restore to normal</option>+ <option value="10">Hold at 10%</option>+ <option value="20">Hold at 20%</option>+ <option value="35">Hold at 35%</option>+ <option value="50">Hold at 50%</option>+ </select>+ </div>+ </div> </div><!-- /tab-cascade --> <!-- ============ TAB: POWER (keep the machine awake & reachable) ============ -->@@ -347,9 +363,10 @@ <div class="panel" id="tab-cache"> <div class="hw" style="font-size:11.5px;color:var(--lo);line-height:1.5;padding:13px 0 2px"> A live look at the saver's on-disk billboard cache: every Wiki page it has fetched, the <b>hero image it- stored</b>, the page's title & description, and the <b>priority score</b> it computes to order the- rotation (higher = shown sooner). Hit <b>Download current from Wiki</b> on any row to pull that page's- hero <b>right now</b> and compare it side-by-side with what's cached, so you can spot a stale hero.+ stored</b>, the page's title & description, and the <b>full priority score</b> it computes to order the+ rotation (higher = shown sooner) — including any <b>admin score adjustment</b> set on the wiki repo.+ Hit <b>⤓ Load current Wiki hero</b> on any row to pull that page's live hero <b>right now</b> and compare it+ side-by-side with what's cached, so you can spot a stale hero. </div> <div class="cachebar"> <span class="cnt" id="cacheCnt">Reading cache…</span>@@ -574,6 +591,7 @@ // on battery we DO let the display sleep, so the old monNever (AC && DC) read OFF even when AC was correct. var nv = document.getElementById("neverToggle"); if (nv) nv.checked = !!data.monNeverAc; var ct = document.getElementById("compToggle"); if (ct) ct.checked = !!data.showComponents;+ if (data.wakeBrightness != null){ var wb = document.getElementById("wakeBriSel"); if (wb){ var wv = String(data.wakeBrightness); wb.value = wv; if (wb.value !== wv){ var o=document.createElement("option"); o.value=wv; o.textContent=(wv==="-1"?"Restore to normal":"Hold at "+wv+"%"); wb.appendChild(o); wb.value=wv; } } } updateNeverWarn(); if (applied){@@ -604,6 +622,7 @@ document.getElementById("close").addEventListener("click", function(){ send("close"); }); document.getElementById("hkToggle").addEventListener("change", function(){ send(this.checked ? "hotkey:on" : "hotkey:off"); }); (function(){ var ct=document.getElementById("compToggle"); if(ct) ct.addEventListener("change", function(){ send(this.checked ? "components:on" : "components:off"); }); })();+ (function(){ var wb=document.getElementById("wakeBriSel"); if(wb) wb.addEventListener("change", function(){ send("wakebri:" + this.value); }); })(); // Pick the matching <option>; if a live value isn't in the list, add it so the control shows reality. function setSel(sel, val){@@ -627,7 +646,9 @@ ? "it dims the <b>backlight to near-black</b>" + (doze ? " (the machine stays <b>awake</b> so your /remote-control sessions keep running)" : "") : "it dims the picture to near-black" ) + (desktop ? "" : ", and on battery it powers the panel off to save battery")- + ". The dim plus slow drift are what prevent burn-in. A few minutes is good.";+ + ". The dim plus slow drift are what prevent burn-in. A few minutes is good. "+ + "<b>Never</b> keeps the billboards up" + (desktop ? "" : " while plugged in (on battery the panel still powers off after 8 minutes to protect the battery)")+ + "; burn-in protection then relies on the rotation and slow drift alone."; } var pd = document.getElementById("previewdimwhy"); if (pd){@@ -711,7 +732,7 @@ function renderCache(){ var cnt = document.getElementById("cacheCnt"), list = document.getElementById("cacheList"); var sortBy = (document.getElementById("cacheSort") || {}).value || "score";- var rows = cacheData.map(function(s){ var f=freshScoreC(s.updated), n=noveltyC(s.slug); return {s:s, f:f, n:n, score:f+n}; });+ var rows = cacheData.map(function(s){ var f=freshScoreC(s.updated), n=noveltyC(s.slug), a=(s.scoreAdj|0); return {s:s, f:f, n:n, a:a, score:f+n+a}; }); if (sortBy==="score") rows.sort(function(a,b){ return b.score-a.score; }); else if (sortBy==="slug") rows.sort(function(a,b){ return (a.s.slug||"").localeCompare(b.s.slug||""); }); else if (sortBy==="fresh") rows.sort(function(a,b){ return b.f-a.f; });@@ -721,6 +742,9 @@ for (var i=0;i<rows.length;i++){ var R = rows[i], s = R.s, media = s.media||"", src = s.src||"", typ = (s.type||"").toLowerCase(); var remoteOnly = /^https?:/i.test(media); // media is a streamed URL (video / un-cached), not a local file+ // The live-hero button ALWAYS works: prefer the manifest's resolved remote URL (src); if an older entry+ // lacks it, derive the standard wiki blob path so the button is never dead.+ var liveUrl = src || ("https://wiki.adom.inc/blob/" + encodeURIComponent(typ||"app") + "/" + encodeURIComponent(s.slug) + "/screenshots/hero.png"); var thumb = media ? '<img class="cthumb" referrerpolicy="no-referrer" src="' + escA(media) + '" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'flex\'">' + '<div class="cthumb miss" style="display:none">no file</div>'@@ -735,12 +759,13 @@ + '<div class="ctitle">' + esc(s.title || "(no title)") + '</div>' + '<div class="cbrief">' + esc(s.brief || "") + '</div>' + '<div class="cfile">' + fileLine + ' · updated ' + esc(freshLabelC(s.updated)) + (s.vis && s.vis!=="public" ? ' · <span style="color:#f0c548">' + esc(s.vis) + '</span>' : '') + '</div>'- + '<button class="cdlbtn"' + (src ? ' data-src="' + escA(src) + '" data-media="' + escA(media) + '"' : ' disabled title="no remote URL stored for this entry"') + '>'- + (src ? 'Download current from Wiki ↓' : 'no remote URL') + '</button>'+ + '<button class="cdlbtn" data-src="' + escA(liveUrl) + '" data-media="' + escA(media) + '">⤓ Load current Wiki hero</button>' + '<div class="ccmp"></div>' + '</div>' + '<div class="cscore"><div class="big">' + R.score + '</div>'- + '<div class="brk"><span class="f">fresh ' + R.f + '</span> + <span class="n">new ' + R.n + '</span><br><span class="j">+ jitter 0–12</span></div></div>'+ + '<div class="brk"><span class="f">fresh ' + R.f + '</span> + <span class="n">new ' + R.n + '</span>'+ + (R.a ? '<br><span class="adj">' + (R.a > 0 ? '+ admin ' + R.a : '− admin ' + Math.abs(R.a)) + '</span>' : '')+ + '<br><span class="j">+ jitter 0–12</span></div></div>' + '</div>'; } if (list) list.innerHTML = html || '<div class="hw" style="padding:18px 0;color:var(--lo)">Cache is empty.</div>';