From a4994371c0b0fbea9d2802acc22d9cc3977a0de0 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sun, 4 Oct 2026 13:30:41 -0700 Subject: Slides for 2026 REBASE --- docs/2026_REBASE/app.js | 303 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 docs/2026_REBASE/app.js (limited to 'docs/2026_REBASE/app.js') diff --git a/docs/2026_REBASE/app.js b/docs/2026_REBASE/app.js new file mode 100644 index 00000000..c5c6aab0 --- /dev/null +++ b/docs/2026_REBASE/app.js @@ -0,0 +1,303 @@ +(function () { + "use strict"; + const R=window.REBASE, slides=R.slides, $=id=>document.getElementById(id); + const presenter=new URLSearchParams(location.search).has("presenter"); + const state={index:0,step:0,demo:null,view:"example",backend:0,library:null,libraryWide:false,libraryScroll:0,methods:false}; + let held=0,started=null,toastTimeout,channel; + if(presenter) document.body.classList.add("presenter"); + try { channel=new BroadcastChannel("simple-rebase-"+location.pathname); } catch (_) {} + const current=()=>slides[state.index]; + const view=()=>current().views?.find(v=>v.id===state.view); + const steps=()=>view()?.steps??current().steps; + const elapsed=()=>held+(started===null?0:Date.now()-started); + const clock=ms=>Math.floor(ms/60000).toString().padStart(2,"0")+":"+Math.floor(ms/1000%60).toString().padStart(2,"0"); + const starts=i=>slides.slice(0,i).reduce((n,s)=>n+s.minutes,0); + function fit() { + const box=$("viewport").getBoundingClientRect(); + document.documentElement.style.setProperty("--scale",Math.min(box.width/1440,box.height/900)); + } + function readHash() { + const [id,step,tab]=location.hash.slice(1).split("/"); + const index=slides.findIndex(s=>s.id===id); + if(index>=0) { + state.index=index; + state.view=slides[index].views?.some(v=>v.id===tab)?tab:"example"; + state.step=Math.max(0,Math.min(steps(),Number(step)||0)); + state.demo=null; + } + } + function broadcast() { + if(!presenter && channel) channel.postMessage({type:"state",state:{...state},elapsed:elapsed(),running:started!==null}); + } + function render(remote=false) { + const s=current(), count=slides.filter(x=>!x.optional).length; + const v=view(), builds=steps(); + const tabs=s.views?'':""; + const number=s.optional?"BONUS":slides.slice(0,state.index+1).filter(x=>!x.optional).length; + $("slide").className="slide"+(s.still?" still":""); + $("slide").dataset.id=s.id; + $("slide").dataset.step=state.step; + $("slide").dataset.view=state.view; + $("slide").innerHTML='
'+R.escape(s.section)+''+tabs+''+(s.demo&&s.id!=="sccp"?"LIVE GRAPH":s.optional?"OPTIONAL":"REBASE 2026")+'
'+ + '
'+(v?v.body(state):s.body(state))+'
'; + $("slide").querySelectorAll("[data-reveal]").forEach(el=>{ + const hidden=Number(el.dataset.reveal)>state.step; + el.classList.toggle("unrevealed",hidden); + el.setAttribute("aria-hidden",hidden?"true":"false"); + el.inert=hidden; + }); + $("slide").querySelectorAll(".state-card").forEach(card=>{ + const entries=card.querySelectorAll(".state"); + entries.forEach((el,i)=>{ + const active=i===Math.min(state.step,entries.length-1); + el.classList.toggle("active",active); + el.setAttribute("aria-hidden",String(!active)); + }); + }); + $("slide").querySelectorAll("[data-demo]").forEach(button=>{ + const active=button.dataset.demo===(state.demo||s.demo); + button.classList.toggle("active",active); + button.setAttribute("aria-pressed",String(active)); + }); + $("position").textContent=s.optional?"BONUS":number+" / "+count; + $("previous").disabled=state.index===0&&state.step===0; + $("next").disabled=state.index===slides.length-1&&state.step===builds; + $("notes-content").innerHTML=v?v.notes:s.notes; + const source=$("slide").querySelector('[data-library-scroll]'); + if(source) source.scrollTop=state.libraryScroll; + document.title=s.label+" · A Simple Tutorial"; + if(!remote) { + history.replaceState(null,"","#"+s.id+(v?"/"+state.step+"/"+v.id:state.step?"/"+state.step:"")); + broadcast(); + } + tick(); + fit(); + } + function go(id) { + const index=slides.findIndex(s=>s.id===id); + if(index<0) return; + state.index=index;state.step=0;state.demo=null;state.view="example";render(); + } + function move(direction,jump=false) { + const s=current(); + if(!jump && direction>0 && state.step0) {state.step--;render();return;} + let index=state.index+direction; + while(index>=0&&index=slides.length) return; + state.index=index;state.step=direction<0&&!jump?slides[index].steps:0;state.demo=null;state.view="example";render(); + } + function action(name,value,remote=false) { + if(presenter&&!remote&&channel) {channel.postMessage({type:"command",name,value});return;} + if(name==="next") move(1); + else if(name==="previous") move(-1); + else if(name==="next-slide") move(1,true); + else if(name==="previous-slide") move(-1,true); + else if(name==="goto") go(value); + else if(name==="demo") {state.demo=value;render();} + else if(name==="language") { + if(current().id!=="language") return; + if(value==="library-open") {state.library="sys";state.libraryScroll=0;} + else if(value==="library-close") {state.library=null;state.libraryWide=false;state.libraryScroll=0;} + else if(value==="library-width") state.libraryWide=!state.libraryWide; + else if(value==="methods-open" && state.step>=1) state.methods=true; + else if(value==="methods-close") state.methods=false; + else return; + render(); + const focus=value==="library-close"?'[data-language="library-open"]':value==="methods-close"?'[data-language="methods-open"]':value.startsWith("methods")?'[data-language="methods-close"]':'[data-language="library-close"]'; + $("slide").querySelector(focus)?.focus({preventScroll:true}); + } + else if(name==="library-path") { + if(current().id!=="language"||!R.libraryHasPath(value)) return; + state.library=value;state.libraryScroll=0;render(); + $("slide").querySelector('[data-library-scroll],.library-list')?.focus({preventScroll:true}); + } + else if(name==="library-scroll") { + if(current().id!=="language"||!Number.isFinite(value)||value<0) return; + state.libraryScroll=value; + const source=$("slide").querySelector('[data-library-scroll]'); + if(source && Math.abs(source.scrollTop-value)>1) source.scrollTop=value; + broadcast(); + } + else if(name==="backend") { + const index=Number(value); + if(current().id!=="backend"||!Number.isInteger(index)||index<0||index>state.step) return; + state.backend^=1<v.id===value)) return; + state.view=value;state.step=0;render(); + $("slide").querySelector('[data-view="'+value+'"]').focus({preventScroll:true}); + } + else if(name==="cycle-view"&¤t().views) { + const ids=["example",...current().views.map(v=>v.id)]; + action("view",ids[(ids.indexOf(state.view)+1)%ids.length],remote); + } + else if(name==="timer") { + if(started===null) started=Date.now(); + else {held=elapsed();started=null;} + tick();broadcast(); + } else if(name==="reset") {held=0;started=null;tick();broadcast();} + } + function tick() { + const s=current(), time=elapsed(), start=starts(state.index), end=start+s.minutes; + $("timer").textContent=(started===null?(held?"Resume ":"Start timer "):"Pause ")+clock(time); + $("pace").textContent=(presenter&&!channel?"Independent notes · ":"")+ + (s.optional?"Optional 2 min · replaces closing discussion":"Planned "+clock(start*60000)+"–"+clock(end*60000))+ + " · elapsed "+clock(time); + $("pace").classList.toggle("over",!s.optional&&time>end*60000); + } + function toast(message) { + clearTimeout(toastTimeout); + $("toast").textContent=message;$("toast").classList.add("show"); + toastTimeout=setTimeout(()=>$("toast").classList.remove("show"),2600); + } + function openDialog(content) { + $("dialog-content").innerHTML=content; + if(!$("dialog").open) $("dialog").showModal(); + } + async function copy(text,label) { + try { + if(!navigator.clipboard) throw new Error("No clipboard API"); + await navigator.clipboard.writeText(text); + toast(label+" copied"); + } catch (_) { + const field=document.createElement("textarea"); + field.value=text;field.style.cssText="position:fixed;left:-9999px"; + document.body.append(field);field.select(); + let ok=false; + try {ok=document.execCommand("copy");} catch (_) {} + field.remove(); + if(ok) toast(label+" copied"); + else { + openDialog('

Copy '+R.escape(label.toLowerCase())+'

Select and copy the text below.

'); + $("manual-copy").value=text;$("manual-copy").focus();$("manual-copy").select(); + } + } + } + function overview() { + openDialog('

The 30-minute route

'+slides.map(s=> + '').join("")+'
'); + } + function help() { + openDialog('

Presentation controls

'+[ + ["← / →","Previous / next build"],["Space","Next build"],["PgUp / PgDn","Previous / next slide"],["Home / End","Title / closing slide"], + ["L","Cycle example / lattice tabs"],["O","Slide overview (includes optional Chapter 25)"],["N","Toggle notes on this screen"],["P","Open synchronized presenter window"],["T","Start / pause the talk timer"],["F","Fullscreen"],["B","Blank the projection"],["Esc","Close a dialog or unblank"],["?","This help"] + ].map(([key,meaning])=>''+key+''+meaning+'').join("")+ + '

Builds advance only when you ask. The talk timer keeps running while you switch to the graph viewer. For demos: Copy code → switch to viewer → paste → Compile.

'); + } + function toggleNotes() { + if(presenter) return; + $("notes").hidden=!$("notes").hidden; + $("notes-toggle").setAttribute("aria-pressed",String(!$("notes").hidden)); + } + function openPresenter() { + const url=new URL(location.href);url.searchParams.set("presenter","1"); + const child=window.open(url,"simple-rebase-presenter","width=1400,height=850"); + if(!child) toast("Allow popups for presenter mode, or press N for notes."); + } + async function fullscreen() { + try {if(document.fullscreenElement) await document.exitFullscreen();else await document.documentElement.requestFullscreen();} + catch (_) {toast("Use your browser’s fullscreen command.");} + } + function blank() {$("blackout").hidden=!$("blackout").hidden;} + $("previous").addEventListener("click",()=>action("previous")); + $("next").addEventListener("click",()=>action("next")); + $("overview").addEventListener("click",overview); + $("help").addEventListener("click",help); + $("notes-toggle").addEventListener("click",toggleNotes); + $("presenter").addEventListener("click",openPresenter); + $("timer").addEventListener("click",()=>action("timer")); + $("timer-reset").addEventListener("click",()=>action("reset")); + $("fullscreen").addEventListener("click",fullscreen); + $("dialog-close").addEventListener("click",()=>$("dialog").close()); + $("dialog").addEventListener("click",e=>{if(e.target===$("dialog")) $("dialog").close();}); + document.addEventListener("click",e=>{ + const b=e.target.closest("[data-copy],[data-command],[data-graph],[data-goto],[data-demo],button[data-view],button[data-backend],[data-language],[data-library-path],[data-language-copy]"); + if(!b) return; + if(b.dataset.goto) {if($("dialog").open) $("dialog").close();action("goto",b.dataset.goto);} + if(b.dataset.demo) action("demo",b.dataset.demo); + if(b.dataset.backend!==undefined) action("backend",b.dataset.backend); + if(b.dataset.language) action("language",b.dataset.language); + if(b.dataset.libraryPath!==undefined) action("library-path",b.dataset.libraryPath); + if(b.dataset.languageCopy) copy(b.dataset.languageCopy==='methods'?R.library.methods:R.library.files[state.library],b.dataset.languageCopy==='methods'?'Methods example':'Source file'); + if(b.dataset.view) action("view",b.dataset.view); + if(b.dataset.copy) copy(R.demos.find(d=>d.id===b.dataset.copy).code,"Demo code"); + if(b.dataset.command) { + const d=R.demos.find(d=>d.id===b.dataset.command); + copy("make -C "+(d.viewerChapter||d.chapter)+" view","Viewer command"); + } + if(b.dataset.graph) { + const d=R.demos.find(d=>d.id===b.dataset.graph); + openDialog('

'+R.escape(d.title)+'

Captured final compiler graph for '+R.escape(d.title)+'

Captured from '+d.chapter+' after its viewer compilation phases. '+R.escape(d.inputs)+'.

Open full-size graph ↗

'); + } + }); + document.addEventListener("scroll",e=>{ + if(!e.target.matches?.('[data-library-scroll]')) return; + const top=e.target.scrollTop; + if(Math.abs(top-state.libraryScroll)>1) action("library-scroll",top); + },true); + document.addEventListener("keydown",e=>{ + if(e.ctrlKey||e.altKey||e.metaKey||e.repeat) return; + if(e.target.closest("textarea,input,select,[contenteditable=true]")) return; + if(!$("blackout").hidden) { + if(e.key==="b"||e.key==="B"||e.key==="Escape") {$("blackout").hidden=true;e.preventDefault();} + return; + } + if($("dialog").open) return; + if(e.target.closest('.library-text,.library-list') && ['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','PageUp','PageDown','Home','End',' '].includes(e.key)) return; + let handled=true; + switch(e.key) { + case "ArrowRight": action("next");break; + case " ": if(e.target.closest("button,a")) {handled=false;break;} action("next");break; + case "ArrowLeft": action("previous");break; + case "PageDown": action("next-slide");break; + case "PageUp": action("previous-slide");break; + case "Home": action("goto","title");break; + case "End": action("goto","close");break; + case "o": case "O": overview();break; + case "l": case "L": action("cycle-view");break; + case "n": case "N": toggleNotes();break; + case "p": case "P": openPresenter();break; + case "t": case "T": action("timer");break; + case "f": case "F": fullscreen();break; + case "b": case "B": blank();break; + case "?": help();break; + case "Escape": if(!presenter) {$("notes").hidden=true;$("notes-toggle").setAttribute("aria-pressed","false");} break; + default: handled=false; + } + if(handled) e.preventDefault(); + }); + if(channel) channel.onmessage=e=>{ + const m=e.data; + if(m.type==="request"&&!presenter) broadcast(); + if(m.type==="command"&&!presenter) action(m.name,m.value,true); + if(m.type==="state"&&presenter) { + const changed=JSON.stringify(state)!==JSON.stringify(m.state); + const scrollOnly=changed && JSON.stringify({...state,libraryScroll:m.state.libraryScroll})===JSON.stringify(m.state); + Object.assign(state,m.state); + held=m.elapsed;started=m.running?Date.now():null; + if(changed && !scrollOnly) render(true); + else { + const source=$("slide").querySelector('[data-library-scroll]'); + if(scrollOnly && source) source.scrollTop=state.libraryScroll; + tick(); + } + } + }; + window.addEventListener("resize",fit); + window.addEventListener("hashchange",()=>{if(!presenter){readHash();render();}}); + readHash();render(); + if(presenter) {$("notes").hidden=false;if(channel) channel.postMessage({type:"request"});} + setInterval(()=>{tick();if(!presenter&&started!==null) broadcast();},1000); + // Small public surface for rehearsal and offline export tools. + R.go=id=>action("goto",id); + R.state=()=>({...state,elapsed:elapsed(),running:started!==null}); +})(); -- cgit v1.2.3