diff --git a/spec/whistlegraph-admin-spec.mjs b/spec/whistlegraph-admin-spec.mjs index 86061fcc6..15a798974 100644 --- a/spec/whistlegraph-admin-spec.mjs +++ b/spec/whistlegraph-admin-spec.mjs @@ -79,11 +79,20 @@ describe("Whistlegraph Desk", () => { it("recovers TikTok posters without routing archival media through TikTok", () => { const page = readFileSync(new URL("../system/public/whistlegraph.org/index.html", import.meta.url), "utf8"); - expect(page).toContain('const videoPoster=p=>p?.platform==="tiktok"&&p?.id?`/api/whistlegraph-thumbnail?id=${encodeURIComponent(p.id)}`:postThumb(p)'); + expect(page).toContain('const videoPoster=p=>p?.platform==="tiktok"&&p?.id?`https://aesthetic.computer/api/whistlegraph-thumbnail?id=${encodeURIComponent(p.id)}`:postThumb(p)'); + expect(page).toContain('fallback.setAttribute("aria-label","Audio-only; no image")'); expect(page).toContain("D.video.poster=hero?videoPoster(hero)"); expect(page).toContain("D.video.poster=videoPoster(p)"); }); + it("reverses chronological and alphabetical work sorts", () => { + const page = readFileSync(new URL("../system/public/whistlegraph.org/index.html", import.meta.url), "utf8"); + expect(page).toContain('id="reverseSort"'); + expect(page).toContain('new Set(["year","title","author"])'); + expect(page).toContain('sortReversed=!sortReversed'); + expect(page).toContain('sortReversed=false; syncReverseSort()'); + }); + it("supports shareable Desk search and view URLs", () => { const page = readFileSync(new URL("../system/public/whistlegraph.org/admin.html", import.meta.url), "utf8"); expect(page).toContain('query=params.get("q")||""'); diff --git a/system/public/whistlegraph.org/index.html b/system/public/whistlegraph.org/index.html index 10e1ed7b9..34d1bb52b 100644 --- a/system/public/whistlegraph.org/index.html +++ b/system/public/whistlegraph.org/index.html @@ -174,11 +174,12 @@ table.idx{border-collapse:collapse; table-layout:fixed; width:100%; font-size:14px} table.idx td{padding:5px 7px; border-bottom:1px solid var(--hair); vertical-align:middle} tr.row:nth-of-type(odd) td{background:var(--zebra)} /* a warm rung, not a grey one */ - td.th{width:66px} td.th img{width:52px; height:66px; object-fit:cover; border:1px solid var(--line); display:block; background:var(--art)} + td.th{width:78px} td.th img{width:64px; height:80px; object-fit:cover; object-position:center 70%; border:1px solid var(--line); display:block; background:var(--art)} td.cd{width:96px; white-space:nowrap; font-family:var(--mono); font-size:15px; overflow:hidden} .sw{display:inline-block; width:.72em; height:.72em; margin-right:6px; border:1px solid var(--edge); background:var(--c); vertical-align:baseline} .sw.quiet{background:transparent; border-color:var(--dim)} - td.th .sw.noglyph{width:36px; height:46px; margin:0; border:1px solid var(--line)} /* audio-only: no frame */ + .sw.noglyph{display:inline-flex; align-items:center; justify-content:center; color:var(--ink); font-size:22px; line-height:1} + td.th .sw.noglyph{width:64px; height:80px; margin:0; border:1px solid var(--line)} td.cd a{font-weight:normal} /* max-width:0 hands the title cell exactly the space the other three leave — which is what gives its two lines something finite to ellipsize against. */ @@ -204,7 +205,6 @@ footer{margin-top:22px; color:var(--dim); font-size:12px; line-height:1.8} footer a{color:var(--dim)} - .badge{border:1px solid var(--dim); padding:1px 6px; display:inline-block; margin-top:6px} /* detail "page" */ #detail{display:none} @@ -229,8 +229,9 @@ #dPosts .posts .post{display:flex; flex-direction:row; gap:11px; align-items:stretch; border:1px solid var(--line); border-radius:2px; padding:8px 10px; background:var(--paper); cursor:pointer; font-size:11.5px} #dPosts .posts .post:hover{border-color:var(--margin)} #dPosts .posts .post.active{border-color:var(--margin); box-shadow:inset 0 0 0 1px var(--margin)} - #dPosts .post .thm{position:relative; flex:0 0 52px; width:52px; aspect-ratio:9/16; border:1px solid var(--line); background:#000; overflow:hidden} - #dPosts .post .thm img{width:100%; height:100%; object-fit:cover; display:block} + #dPosts .post .thm{position:relative; flex:0 0 64px; width:64px; aspect-ratio:9/16; border:1px solid var(--line); background:#000; overflow:hidden} + #dPosts .post .thm img{width:100%; height:100%; object-fit:cover; object-position:center 70%; display:block} + #dPosts .post .thm .noglyph{width:100%; height:100%; margin:0; border:0; background:var(--art)} #dPosts .post .thm .pl{position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:#fff; font-size:13px; text-shadow:0 1px 3px #000; pointer-events:none} #dPosts .post.active .thm .pl{color:var(--margin)} #dPosts .post .pmeta{flex:1 1 auto; min-width:0; align-self:center; margin-top:0} @@ -266,7 +267,7 @@ table.idx td{padding:4px 4px} td.cd .sw{display:none} td.cd{width:70px; font-size:14px} - td.th{width:48px} td.th img{width:40px; height:50px} + td.th{width:60px} td.th img,td.th .sw.noglyph{width:52px; height:65px} td.yr{width:54px} td.ti > a{font-size:13.5px} td.ti .by{font-size:11px} @@ -329,6 +330,7 @@ +
@@ -529,8 +530,20 @@ let LIVE_CURATION={works:{},posts:{}}; // Auth0-admin edits, stored durably by l const KIND_LABEL={performance:"Performance",talk:"Talk",other:"Other"}; const postThumb=p=>p?.thumb||(p?.platform==="tiktok"&&p?.id?`${IDX}/posts/${p.id}.jpg`:""); const thumbPost=p=>String(p?.thumbPost||p?.id||""); -const videoPoster=p=>p?.platform==="tiktok"&&p?.id?`/api/whistlegraph-thumbnail?id=${encodeURIComponent(p.id)}`:postThumb(p); -document.addEventListener("error",event=>{const img=event.target;if(img?.tagName!=="IMG")return;const id=img.dataset.thumbPost;if(!id||img.dataset.thumbRecovered)return;img.dataset.thumbRecovered="1";img.src=`/api/whistlegraph-thumbnail?id=${encodeURIComponent(id)}`},true); +const videoPoster=p=>p?.platform==="tiktok"&&p?.id?`https://aesthetic.computer/api/whistlegraph-thumbnail?id=${encodeURIComponent(p.id)}`:postThumb(p); +document.addEventListener("error",event=>{ + const img=event.target;if(img?.tagName!=="IMG")return; + const id=img.dataset.thumbPost;if(!id)return; + if(!img.dataset.thumbRecovered){ + img.dataset.thumbRecovered="1"; + img.src=`https://aesthetic.computer/api/whistlegraph-thumbnail?id=${encodeURIComponent(id)}`; + return; + } + const fallback=document.createElement("span"); + fallback.className="sw noglyph"; fallback.textContent="♪"; + fallback.setAttribute("role","img"); fallback.setAttribute("aria-label","Audio-only; no image"); + img.replaceWith(fallback); +},true); const esc=s=>String(s||"").replace(/[&<>"]/g,c=>({"&":"&","<":"<",">":">",'"':"""}[c])); // Generated files remain the reproducible baseline. Small live patches from @@ -599,7 +612,7 @@ function renderPosts(code){ function rowHTML(w){ const thumb=!w.thumb - ? `` + ? `♪` : `