Merge changes I2e5db66b,I38376916 * changes: Remove the "JJ VS Code extension at Google" talk as requested Add a venue display page
diff --git a/README.md b/README.md index 7149788..82df772 100644 --- a/README.md +++ b/README.md
@@ -18,3 +18,13 @@ ## Local preview Open `_site/index.html` directly in a browser. + +## Venue display + +`now.html` is a full-screen page for the screens at the venue: it shows the +talk in progress, or a countdown during breaks, and what comes next, from +`_data/schedule.yml`. Open `https://gus26.gerritforge.com/now.html` and click +once to go full screen. If the schedule slips, update `_data/schedule.yml` and +redeploy: open displays pick up the new `schedule.json` within a minute. + +Add `?now=2026-11-09T10:50` (venue time) to preview any moment of the summit.
diff --git a/_data/schedule.yml b/_data/schedule.yml index 3ca57f5..896add5 100644 --- a/_data/schedule.yml +++ b/_data/schedule.yml
@@ -104,29 +104,22 @@ - time: "09:00" title: "Doors open" - time: "09:45" - title: "JJ VS Code extension at Google" - talk: jj-vs-code-extension-at-google - speakers: - - name: Kevin Liao - id: kevin-liao - company: Google - - time: "10:15" title: "Version control and code formatting" talk: version-control-and-code-formatting speakers: - name: Danny Hooper id: danny-hooper company: Google - - time: "10:45" + - time: "10:15" title: "Where does your metadata live?" talk: where-does-your-metadata-live speakers: - name: Patrick Hiesel id: patrick-hiesel company: Google - - time: "11:15" + - time: "10:45" title: "Break" - - time: "11:30" + - time: "11:00" title: "Bridging the Gap: Adding native JJ support to a Git-based SCM" talk: bridging-the-gap-adding-native-jj-support-to-a-git-based-scm speakers: @@ -136,16 +129,16 @@ - name: Emily Shaffer id: emily-shaffer company: Google - - time: "12:00" + - time: "11:30" title: "cloud-jj: Open-Source Commit Cloud for jj" talk: cloud-jj-open-source-commit-cloud-for-jj speakers: - name: Daniel Sperry id: daniel-sperry company: Google - - time: "12:30" + - time: "12:00" title: "Lunch" - - time: "13:30" + - time: "13:00" title: "AI development at scale with Gerrit and Zuul" talk: ai-development-at-scale-with-gerrit-and-zuul speakers: @@ -155,37 +148,37 @@ - name: Luca Milanesio id: luca-milanesio company: GerritForge - - time: "14:15" + - time: "13:45" title: "SHA-256 in JGit: Why, What, and When" talk: sha-256-in-jgit-why-what-and-when speakers: - name: Xing Huang id: xing-huang company: Google - - time: "14:45" + - time: "14:15" title: "GHS: sustaining Git performance under AI workload" talk: ghs-sustaining-git-performance-under-production-workloads speakers: - name: Shane McIntosh id: shane-mcintosh company: University of Waterloo - - time: "15:15" + - time: "14:45" title: "Break" - - time: "15:35" + - time: "15:05" title: "Scaling Gerrit builds with K8s and real-time output sync" talk: scaling-gerrit-builds-with-kubernetes-and-real-time-output-sync speakers: - name: Shameer Erakkath Saidumuhammed id: shameer-erakkath-saidumuhammed company: Hewlett Packard Enterprise - - time: "16:05" + - time: "15:35" title: "How to Stack Changes like a pro" talk: how-to-stack-changes-like-a-pro speakers: - name: Jacek Centkowski id: jacek-centkowski company: GerritForge - - time: "16:20" + - time: "15:50" title: "Q&A with the maintainers" talk: q-a-with-the-maintainers speakers: @@ -206,7 +199,7 @@ - name: Nasser Grainawi - name: Patrick Hiesel id: patrick-hiesel - - time: "17:20" + - time: "16:50" title: "Socials & Networking" - time: "18:00" title: "Door closes"
diff --git a/assets/css/now.css b/assets/css/now.css new file mode 100644 index 0000000..a24ad83 --- /dev/null +++ b/assets/css/now.css
@@ -0,0 +1,278 @@ +/* Full-screen venue display (now.html), sized in rem so it scales with the + screen: 1rem is 20px on a 1920x1080 display. */ +html { font-size: min(1.85vh, 1.04vw); } + +body.now { + height: 100vh; + overflow: hidden; + display: grid; + grid-template-rows: auto 1fr auto; + gap: 1.4rem; + padding: 2rem 3.2rem 1.6rem; + font-size: 1rem; + line-height: 1.3; + cursor: default; +} + +.now-grid { + position: fixed; + inset: 0; + z-index: -1; + background-image: + linear-gradient(rgba(249,115,22,0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(249,115,22,0.05) 1px, transparent 1px); + background-size: 2.8rem 2.8rem; + mask-image: radial-gradient(ellipse 80% 70% at 35% 45%, black 20%, transparent 80%); + -webkit-mask-image: radial-gradient(ellipse 80% 70% at 35% 45%, black 20%, transparent 80%); +} + +/* ── Top bar ── */ +.now-top { + display: flex; + align-items: center; + justify-content: space-between; +} +.now-logo { height: 4.6rem; width: auto; } +.now-clock { + display: flex; + flex-direction: column; + align-items: flex-end; + line-height: 1.1; +} +.now-clock-day { + font-family: "DM Mono", monospace; + font-size: 1rem; + letter-spacing: 0.15em; + text-transform: uppercase; + color: var(--text-muted); +} +.now-clock-time { + font-weight: 800; + font-size: 3.2rem; + letter-spacing: -0.04em; + font-variant-numeric: tabular-nums; +} + +/* ── Main area: stage + Q&A card ── */ +.now-main { + display: grid; + grid-template-columns: 1fr 17rem; + gap: 4rem; + align-items: center; + min-height: 0; +} +.now-stage { + display: flex; + flex-direction: column; + justify-content: center; + gap: 1.3rem; + min-width: 0; +} + +.stage-label { + display: flex; + align-items: center; + gap: 0.8rem; + font-family: "DM Mono", monospace; + font-size: 1.3rem; + letter-spacing: 0.2em; + text-transform: uppercase; + color: var(--accent); +} +.live-dot { + width: 0.9rem; + height: 0.9rem; + border-radius: 50%; + background: var(--accent); + box-shadow: 0 0 0 0 var(--accent-glow); + animation: live-pulse 2s ease-out infinite; +} +@keyframes live-pulse { + from { box-shadow: 0 0 0 0 var(--accent-glow); } + to { box-shadow: 0 0 0 1.2rem transparent; } +} + +.stage-title { + font-weight: 800; + font-size: 3.7rem; + line-height: 1.08; + letter-spacing: -0.03em; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} +.stage-title.pause-title { font-size: 5.4rem; } +.stage-sub { + font-weight: 600; + font-size: 2.3rem; + color: var(--text-muted); +} + +/* ── Speakers ── */ +.speakers { + display: flex; + flex-wrap: wrap; + gap: 1.2rem 2.4rem; +} +.speaker { + display: flex; + align-items: center; + gap: 1rem; +} +.speaker-photo { + width: 4.2rem; + height: 4.2rem; + border-radius: 50%; + object-fit: cover; + border: 2px solid var(--border); + flex-shrink: 0; +} +.speaker-mono { + display: flex; + align-items: center; + justify-content: center; + background: var(--accent-dim); + color: var(--accent); + font-weight: 700; + font-size: 1.5rem; +} +.speaker-text { display: flex; flex-direction: column; } +.speaker-name { font-weight: 700; font-size: 1.7rem; } +.speaker-co { font-size: 1.25rem; color: var(--text-muted); } + +.speakers.many { gap: 0.4rem 1.6rem; } +.speakers.many .speaker-name { font-size: 1.5rem; font-weight: 600; } + +.speakers.small { gap: 0.6rem 1.8rem; } +.speakers.small .speaker-photo { width: 2.4rem; height: 2.4rem; font-size: 1rem; } +.speakers.small .speaker-name { font-size: 1.3rem; } +.speakers.small .speaker-co { font-size: 1.05rem; } +.speakers.small.many .speaker-name { font-size: 1.1rem; } + +/* ── Talk progress ── */ +.progress { + height: 0.5rem; + border-radius: 0.25rem; + background: var(--surface-2); + overflow: hidden; + margin-top: 0.8rem; +} +.progress-bar { + height: 100%; + width: 0; + background: var(--accent); + border-radius: inherit; + transition: width 1s linear; +} +.progress-meta { + display: flex; + justify-content: space-between; + font-family: "DM Mono", monospace; + font-size: 1.1rem; + color: var(--text-muted); + margin-top: -0.8rem; +} +#left { color: var(--text); } + +/* ── Countdown ── */ +.countdown-label { + font-family: "DM Mono", monospace; + font-size: 1.3rem; + letter-spacing: 0.2em; + text-transform: uppercase; + color: var(--text-muted); + margin-bottom: -1.2rem; +} +.countdown { + font-weight: 800; + font-size: 8.5rem; + line-height: 1; + letter-spacing: -0.04em; + font-variant-numeric: tabular-nums; + color: var(--accent); +} + +/* ── Up next ── */ +.next { + margin-top: 0.4rem; + padding: 1.1rem 1.6rem; + background: var(--surface); + border: 1px solid var(--border); + border-left: 0.3rem solid var(--accent); + border-radius: 0.6rem; + display: flex; + flex-direction: column; + gap: 0.5rem; +} +.next-label { + font-family: "DM Mono", monospace; + font-size: 1.05rem; + letter-spacing: 0.15em; + text-transform: uppercase; + color: var(--accent); +} +.next-title { + font-weight: 700; + font-size: 1.8rem; + line-height: 1.15; +} + +/* ── Q&A card ── */ +.qa-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.9rem; + text-align: center; + padding: 1.6rem 1.4rem; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 1rem; +} +.qa-title { + font-weight: 800; + font-size: 1.7rem; + line-height: 1.1; + letter-spacing: -0.02em; +} +.qa-qr { + width: 100%; + padding: 1rem; + background: #fff; + border-radius: 0.6rem; +} +.qa-qr img { display: block; width: 100%; height: auto; } +.qa-note { font-size: 1.15rem; color: var(--text-muted); } +.qa-note strong { color: var(--text); } +.qa-when { + font-family: "DM Mono", monospace; + font-size: 1.05rem; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--accent); +} + +/* ── Footer: host and organiser ── */ +.now-foot { + display: flex; + justify-content: center; + gap: 5rem; + padding: 1rem 0 0; + background: none; + border-top: 1px solid var(--border); +} +.now-org { + display: flex; + align-items: center; + gap: 1.2rem; +} +.now-org-label { + font-family: "DM Mono", monospace; + font-size: 0.95rem; + letter-spacing: 0.15em; + text-transform: uppercase; + color: var(--text-muted); +} +.now-org-logo { height: 2.4rem; width: auto; display: block; } +.now-org-logo.gerritforge-logo { height: 3.6rem; }
diff --git a/assets/img/qa-slido-qr.svg b/assets/img/qa-slido-qr.svg new file mode 100644 index 0000000..214759e --- /dev/null +++ b/assets/img/qa-slido-qr.svg
@@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33 33"><path stroke="#07090f" d="M0 0.5h7m1 0h1m4 0h2m2 0h5m1 0h1m2 0h7m-33 1h1m5 0h1m1 0h3m1 0h2m4 0h1m1 0h5m1 0h1m5 0h1m-33 1h1m1 0h3m1 0h1m4 0h1m5 0h1m2 0h1m2 0h1m2 0h1m1 0h3m1 0h1m-33 1h1m1 0h3m1 0h1m1 0h2m1 0h2m1 0h3m3 0h3m3 0h1m1 0h3m1 0h1m-33 1h1m1 0h3m1 0h1m2 0h1m1 0h1m1 0h2m2 0h1m2 0h2m4 0h1m1 0h3m1 0h1m-33 1h1m5 0h1m3 0h1m2 0h1m4 0h3m3 0h1m1 0h1m5 0h1m-33 1h7m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h7m-25 1h4m2 0h1m2 0h2m1 0h1m-21 1h1m1 0h2m1 0h3m5 0h2m2 0h5m2 0h1m1 0h1m2 0h1m1 0h2m-33 1h1m1 0h1m4 0h4m1 0h1m2 0h2m1 0h4m1 0h1m2 0h2m1 0h4m-31 1h2m2 0h1m4 0h1m2 0h1m1 0h2m3 0h3m1 0h5m1 0h2m-33 1h2m1 0h2m2 0h3m2 0h3m1 0h1m1 0h3m2 0h3m1 0h1m1 0h1m1 0h2m-33 1h1m3 0h1m1 0h1m3 0h1m1 0h1m2 0h1m2 0h1m2 0h4m3 0h2m-29 1h1m1 0h3m1 0h3m6 0h1m1 0h1m2 0h1m1 0h3m1 0h1m1 0h1m1 0h1m-32 1h1m1 0h1m3 0h2m1 0h1m4 0h5m3 0h1m3 0h2m2 0h1m-31 1h1m1 0h1m5 0h1m2 0h1m2 0h1m1 0h1m1 0h9m2 0h2m-31 1h2m1 0h1m1 0h2m3 0h2m2 0h3m1 0h4m1 0h1m1 0h2m1 0h3m-31 1h3m1 0h1m2 0h3m2 0h2m1 0h1m1 0h2m1 0h1m1 0h2m1 0h2m1 0h2m2 0h1m-33 1h2m1 0h2m1 0h2m3 0h2m1 0h2m1 0h1m2 0h3m1 0h1m1 0h3m1 0h2m-31 1h5m1 0h1m3 0h2m1 0h1m4 0h1m2 0h2m1 0h2m1 0h1m2 0h2m-32 1h1m1 0h1m2 0h2m2 0h1m1 0h1m2 0h1m2 0h1m1 0h1m1 0h1m1 0h1m2 0h5m-32 1h4m1 0h1m1 0h1m3 0h1m3 0h1m2 0h1m4 0h2m5 0h1m1 0h1m-31 1h3m1 0h1m1 0h1m1 0h1m2 0h1m2 0h3m2 0h4m1 0h2m3 0h2m-32 1h3m4 0h2m1 0h3m2 0h1m2 0h1m2 0h1m4 0h3m2 0h1m-33 1h1m2 0h1m2 0h4m1 0h1m1 0h1m1 0h1m1 0h2m3 0h1m1 0h6m1 0h2m-25 1h1m2 0h1m4 0h2m1 0h2m3 0h1m3 0h2m1 0h1m-32 1h7m1 0h1m4 0h2m4 0h2m1 0h3m1 0h1m1 0h1m-29 1h1m5 0h1m1 0h2m2 0h1m2 0h2m2 0h3m1 0h2m3 0h4m-32 1h1m1 0h3m1 0h1m3 0h4m3 0h1m1 0h2m2 0h6m1 0h3m-33 1h1m1 0h3m1 0h1m1 0h1m3 0h3m2 0h4m1 0h1m4 0h1m1 0h1m1 0h2m-33 1h1m1 0h3m1 0h1m1 0h3m2 0h2m2 0h1m1 0h1m1 0h2m1 0h4m1 0h1m-30 1h1m5 0h1m4 0h9m6 0h3m3 0h1m-33 1h7m1 0h3m1 0h1m2 0h1m1 0h1m3 0h1m1 0h1m1 0h2m1 0h1"/></svg> \ No newline at end of file
diff --git a/assets/img/speakers/kevin-liao.jpg b/assets/img/speakers/kevin-liao.jpg deleted file mode 100644 index 9661010..0000000 --- a/assets/img/speakers/kevin-liao.jpg +++ /dev/null Binary files differ
diff --git a/now.html b/now.html new file mode 100644 index 0000000..2727b0a --- /dev/null +++ b/now.html
@@ -0,0 +1,241 @@ +--- +layout: null +sitemap: false +title: "Gerrit User Summit 2026 - Now" +--- +{%- comment -%} + Full-screen display for the venue screens: shows the session in progress + and what comes next, from _data/schedule.yml. Open it in a browser and + click once to go full screen. Add ?now=2026-11-09T10:50 (venue time) to + preview any moment of the summit. + + assets/img/qa-slido-qr.svg encodes the Slido event for the maintainers + Q&A: https://app.sli.do/event/5HqT8EqrrawEWt9BQ2C7GE +{%- endcomment -%} +{%- for day in site.data.schedule -%} + {%- for s in day.sessions -%} + {%- if s.talk == "q-a-with-the-maintainers" -%} + {%- assign qa_day = day.day | split: " " | first -%} + {%- assign qa_time = day.date | date: "%Y-%m-%d" | append: " " | append: s.time | date: "%-I:%M %p" -%} + {%- endif -%} + {%- endfor -%} +{%- endfor -%} +{%- assign photos = site.static_files | where_exp: "f", "f.path contains '/assets/img/speakers/'" -%} +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta name="robots" content="noindex"> + <title>{{ page.title }}</title> + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;0,9..40,800;1,9..40,400&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet"> + <link rel="stylesheet" href="{{ site.baseurl }}/assets/css/base.css"> + <link rel="stylesheet" href="{{ site.baseurl }}/assets/css/now.css"> +</head> +<body class="now"> + <div class="now-grid"></div> + + <header class="now-top"> + <img src="{{ site.baseurl }}/assets/img/gus-logo.png" alt="Gerrit User Summit 2026" class="now-logo"> + <div class="now-clock"> + <span class="now-clock-day" id="clock-day"></span> + <span class="now-clock-time" id="clock-time"></span> + </div> + </header> + + <main class="now-main"> + <section class="now-stage" id="stage"></section> + + <aside class="qa-card"> + <div class="qa-title">Ask the maintainers anything</div> + <div class="qa-qr"> + <img src="{{ site.baseurl }}/assets/img/qa-slido-qr.svg" alt="QR code to submit questions on Slido"> + </div> + <div class="qa-note">Scan to send your questions for the<br><strong>Q&A with the maintainers</strong></div> + {%- if qa_day %} + <div class="qa-when">{{ qa_day }} · {{ qa_time }}</div> + {%- endif %} + </aside> + </main> + + <footer class="now-foot"> + <div class="now-org"> + <span class="now-org-label">Hosted by</span> + <img src="{{ site.baseurl }}/assets/img/google-logo.svg" alt="Google" class="now-org-logo google-logo"> + </div> + <div class="now-org"> + <span class="now-org-label">Sponsored by</span> + <img src="{{ site.baseurl }}/assets/img/gerritforge-logo.svg" alt="GerritForge" class="now-org-logo gerritforge-logo"> + </div> + </footer> + + <script> + const VENUE_TZ = 'America/Los_Angeles'; + const VENUE_OFFSET = '-08:00'; // PST, the summit is after the DST change + const SCHEDULE_URL = '{{ site.baseurl }}/schedule.json'; + const PHOTOS = { {%- for f in photos -%}{{ f.basename | jsonify }}: {{ site.baseurl | append: f.path | jsonify }}{%- unless forloop.last -%}, {% endunless -%}{%- endfor -%} }; + + let schedule = {{ site.data.schedule | jsonify }}; + + // ── Clock, optionally shifted by ?now=<venue time> to preview ── + let skew = 0; + const nowParam = new URLSearchParams(location.search).get('now'); + if (nowParam) { + const iso = /(Z|[+-]\d\d:?\d\d)$/i.test(nowParam) ? nowParam : nowParam + VENUE_OFFSET; + const t = Date.parse(iso); + if (!isNaN(t)) skew = t - Date.now(); + } + const now = () => Date.now() + skew; + + // ── Schedule as a flat list of sessions with start instants ── + // The last session of a day ("Doors close") only marks its end. + function timeline(days) { + const out = []; + days.forEach(d => d.sessions.forEach((s, i) => out.push(Object.assign({}, s, { + day: d.day, + start: Date.parse(`${d.date}T${s.time}:00${VENUE_OFFSET}`), + dayEnd: i === d.sessions.length - 1 + })))); + return out; + } + let items = timeline(schedule); + + // ── Formatting ── + const timeFmt = new Intl.DateTimeFormat('en-US', { timeZone: VENUE_TZ, hour: 'numeric', minute: '2-digit' }); + const dayFmt = new Intl.DateTimeFormat('en-US', { timeZone: VENUE_TZ, weekday: 'long', month: 'long', day: 'numeric' }); + const fmtTime = t => timeFmt.format(t); + const pad = n => String(n).padStart(2, '0'); + function fmtDuration(ms) { + const s = Math.max(0, Math.ceil(ms / 1000)); + const d = Math.floor(s / 86400), h = Math.floor(s % 86400 / 3600), m = Math.floor(s % 3600 / 60); + if (d) return `${d}d ${h}h ${pad(m)}m`; + if (h) return `${h}:${pad(m)}:${pad(s % 60)}`; + return `${pad(m)}:${pad(s % 60)}`; + } + const esc = s => String(s).replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); + const initials = name => name.split(/\s+/).map(w => w[0]).slice(0, 2).join(''); + + function speakers(list, small) { + if (!list || !list.length) return ''; + const many = list.length > 3; + return `<div class="speakers${many ? ' many' : ''}${small ? ' small' : ''}">` + list.map(sp => { + const avatar = many ? '' : (PHOTOS[sp.id] + ? `<img class="speaker-photo" src="${esc(PHOTOS[sp.id])}" alt="">` + : `<span class="speaker-photo speaker-mono">${esc(initials(sp.name))}</span>`); + const co = sp.company ? `<span class="speaker-co">${esc(sp.company)}</span>` : ''; + return `<div class="speaker">${avatar}<div class="speaker-text"><span class="speaker-name">${esc(sp.name)}</span>${co}</div></div>`; + }).join('') + '</div>'; + } + + function upNext(s, label) { + return `<div class="next"> + <div class="next-label">${esc(label || 'Up next')} · ${fmtTime(s.start)}</div> + <div class="next-title">${esc(s.title)}</div> + ${speakers(s.speakers, true)} + </div>`; + } + + // ── What is on at instant t ── + function stateAt(t) { + let i = -1; + while (i + 1 < items.length && items[i + 1].start <= t) i++; + if (i === -1) return { kind: 'before', next: items[0], key: 'before' }; + const cur = items[i]; + if (cur.dayEnd) { + return i === items.length - 1 + ? { kind: 'over', key: 'over' } + : { kind: 'overnight', next: items[i + 1], key: 'overnight' + i }; + } + return { kind: cur.talk ? 'talk' : 'pause', cur, next: items[i + 1], key: 'at' + i }; + } + + // First talk at or after session index of s + function firstTalkFrom(s) { + return items.slice(items.indexOf(s)).find(x => x.talk && x.day === s.day); + } + + function render(st) { + switch (st.kind) { + case 'talk': + return `<div class="stage-label"><span class="live-dot"></span>Now</div> + <h1 class="stage-title">${esc(st.cur.title)}</h1> + ${speakers(st.cur.speakers)} + <div class="progress"><div class="progress-bar" id="bar"></div></div> + <div class="progress-meta"><span>${fmtTime(st.cur.start)}</span><span id="left"></span><span>${fmtTime(st.next.start)}</span></div> + ${upNext(st.next)}`; + case 'pause': + return `<h1 class="stage-title pause-title">${esc(st.cur.title)}</h1> + <div class="countdown-label">${st.next.talk ? 'Next talk in' : esc(st.next.title) + ' in'}</div> + <div class="countdown" id="cd"></div> + ${st.next.dayEnd ? '' : upNext(st.next)}`; + case 'before': + case 'overnight': { + const talk = firstTalkFrom(st.next); + return `<div class="stage-label">${st.kind === 'before' ? 'Welcome' : 'That’s a wrap for today'}</div> + <h1 class="stage-title">${esc(st.next.day)}<br><span class="stage-sub">${esc(st.next.title)} at ${fmtTime(st.next.start)}</span></h1> + <div class="countdown-label">Starts in</div> + <div class="countdown" id="cd"></div> + ${talk ? upNext(talk, 'First talk') : ''}`; + } + case 'over': + return `<div class="stage-label">That’s a wrap</div> + <h1 class="stage-title">Thank you for joining the Gerrit User Summit 2026!</h1>`; + } + } + + // ── Tick: rebuild the stage on a new session, update timers every second ── + const stage = document.getElementById('stage'); + let shownKey = null; + function tick() { + const t = now(); + document.getElementById('clock-day').textContent = dayFmt.format(t); + document.getElementById('clock-time').textContent = fmtTime(t); + + const st = stateAt(t); + if (st.key !== shownKey) { + stage.innerHTML = render(st); + stage.dataset.kind = st.kind; + shownKey = st.key; + } + const cd = document.getElementById('cd'); + if (cd) cd.textContent = fmtDuration(st.next.start - t); + const bar = document.getElementById('bar'); + if (bar) { + const span = st.next.start - st.cur.start; + bar.style.width = Math.min(100, (t - st.cur.start) / span * 100) + '%'; + const mins = Math.ceil((st.next.start - t) / 60000); + document.getElementById('left').textContent = mins === 1 ? '1 min left' : `${mins} min left`; + } + } + tick(); + setInterval(tick, 1000); + + // ── Pick up a redeployed schedule without reloading the page ── + setInterval(async () => { + try { + const res = await fetch(SCHEDULE_URL, { cache: 'no-store' }); + if (!res.ok) return; + const fresh = await res.json(); + if (JSON.stringify(fresh) === JSON.stringify(schedule)) return; + schedule = fresh; + items = timeline(schedule); + shownKey = null; + tick(); + } catch (e) { /* offline: keep the schedule we have */ } + }, 60000); + + // ── Kiosk: keep the screen awake, click to go full screen ── + async function keepAwake() { + try { await navigator.wakeLock.request('screen'); } catch (e) { /* unsupported or denied */ } + } + keepAwake(); + document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') keepAwake(); }); + document.addEventListener('click', e => { + if (e.target.closest('a') || document.fullscreenElement) return; + document.documentElement.requestFullscreen().catch(() => {}); + }); + </script> +</body> +</html>
diff --git a/schedule.json b/schedule.json new file mode 100644 index 0000000..e2af761 --- /dev/null +++ b/schedule.json
@@ -0,0 +1,5 @@ +--- +layout: null +sitemap: false +--- +{{ site.data.schedule | jsonify }}
diff --git a/speakers.md b/speakers.md index fed5334..808021d 100644 --- a/speakers.md +++ b/speakers.md
@@ -120,17 +120,6 @@ <div class="speaker-sessions"><span class="label">Talk</span><a href="{{ site.baseurl }}/talks.html#version-control-and-code-formatting">Version control and code formatting</a></div> </div> </article> - <article class="speaker-card" id="kevin-liao"> - <img class="speaker-photo" src="{{ site.baseurl }}/assets/img/speakers/kevin-liao.jpg" alt="Kevin Liao" loading="lazy"> - <div class="speaker-info"> - <h3 class="speaker-name">Kevin Liao</h3> - <p class="speaker-tagline">Software Engineer at Google</p> - <div class="speaker-bio"> - <p>I'm a Software Engineer at Google, passionate about the open-source jj version control system. I'm part of Google's Source team driving its adoption at Google and beyond.</p> - </div> - <div class="speaker-sessions"><span class="label">Talk</span><a href="{{ site.baseurl }}/talks.html#jj-vs-code-extension-at-google">JJ VS Code extension at Google</a></div> - </div> - </article> <article class="speaker-card" id="kaushik-lingarkar"> <img class="speaker-photo" src="{{ site.baseurl }}/assets/img/speakers/kaushik-lingarkar.jpg" alt="Kaushik Lingarkar" loading="lazy"> <div class="speaker-info">
diff --git a/talks.md b/talks.md index 9e3843d..5c9985e 100644 --- a/talks.md +++ b/talks.md
@@ -261,21 +261,9 @@ </div> </article> - <article class="talk-card" id="jj-vs-code-extension-at-google"> - <h3 class="talk-title">JJ VS Code extension at Google</h3> - <p class="talk-meta">Tuesday 10th November, 09:45 AM · <a href="{{ site.baseurl }}/speakers.html#kevin-liao">Kevin Liao</a></p> - <div class="talk-abstract" markdown="1"> - -The JJ team at Google developed a JJ VS Code extension that is used by thousands -of engineers internally. See a demo of its UX and its integration with Piper, -Google's internal source control backend. We are currently in the progress of -open-sourcing it, and we want it to support Gerrit! - -</div> - </article> <article class="talk-card" id="version-control-and-code-formatting"> <h3 class="talk-title">Version control and code formatting</h3> - <p class="talk-meta">Tuesday 10th November, 10:15 AM · <a href="{{ site.baseurl }}/speakers.html#danny-hooper">Danny Hooper</a></p> + <p class="talk-meta">Tuesday 10th November, 09:45 AM · <a href="{{ site.baseurl }}/speakers.html#danny-hooper">Danny Hooper</a></p> <div class="talk-abstract" markdown="1"> There are many approaches to managing indentation and such in a code base over @@ -285,7 +273,7 @@ </article> <article class="talk-card" id="where-does-your-metadata-live"> <h3 class="talk-title">Where does your metadata live?</h3> - <p class="talk-meta">Tuesday 10th November, 10:45 AM · <a href="{{ site.baseurl }}/speakers.html#patrick-hiesel">Patrick Hiesel</a></p> + <p class="talk-meta">Tuesday 10th November, 10:15 AM · <a href="{{ site.baseurl }}/speakers.html#patrick-hiesel">Patrick Hiesel</a></p> <div class="talk-abstract" markdown="1"> Code review comments, approvals, repo permissions and now agent trajectories and @@ -307,7 +295,7 @@ </article> <article class="talk-card" id="bridging-the-gap-adding-native-jj-support-to-a-git-based-scm"> <h3 class="talk-title">Bridging the Gap: Adding native JJ support to a Git-based SCM</h3> - <p class="talk-meta">Tuesday 10th November, 11:30 AM · <a href="{{ site.baseurl }}/speakers.html#daniele-sassoli">Daniele Sassoli</a>, <a href="{{ site.baseurl }}/speakers.html#emily-shaffer">Emily Shaffer</a></p> + <p class="talk-meta">Tuesday 10th November, 11:00 AM · <a href="{{ site.baseurl }}/speakers.html#daniele-sassoli">Daniele Sassoli</a>, <a href="{{ site.baseurl }}/speakers.html#emily-shaffer">Emily Shaffer</a></p> <div class="talk-abstract" markdown="1"> In this talk, we'll walk through the philosophy behind JJ, why it works the way @@ -327,7 +315,7 @@ </article> <article class="talk-card" id="cloud-jj-open-source-commit-cloud-for-jj"> <h3 class="talk-title">cloud-jj: Open-Source Commit Cloud for jj</h3> - <p class="talk-meta">Tuesday 10th November, 12:00 PM · <a href="{{ site.baseurl }}/speakers.html#daniel-sperry">Daniel Sperry</a></p> + <p class="talk-meta">Tuesday 10th November, 11:30 AM · <a href="{{ site.baseurl }}/speakers.html#daniel-sperry">Daniel Sperry</a></p> <div class="talk-abstract" markdown="1"> cloud-jj will bring Google's cloud-backed Commit Cloud server architecture to @@ -364,7 +352,7 @@ </article> <article class="talk-card" id="ai-development-at-scale-with-gerrit-and-zuul"> <h3 class="talk-title">AI Development at Scale with Gerrit and Zuul</h3> - <p class="talk-meta">Tuesday 10th November, 01:30 PM · <a href="{{ site.baseurl }}/speakers.html#luca-milanesio">Luca Milanesio</a>, <a href="{{ site.baseurl }}/speakers.html#james-blair">James Blair</a></p> + <p class="talk-meta">Tuesday 10th November, 01:00 PM · <a href="{{ site.baseurl }}/speakers.html#luca-milanesio">Luca Milanesio</a>, <a href="{{ site.baseurl }}/speakers.html#james-blair">James Blair</a></p> <div class="talk-abstract" markdown="1"> The AI revolutionised the way we write software and introduced a new level of @@ -382,7 +370,7 @@ </article> <article class="talk-card" id="sha-256-in-jgit-why-what-and-when"> <h3 class="talk-title">SHA-256 in JGit: Why, What, and When</h3> - <p class="talk-meta">Tuesday 10th November, 02:15 PM · <a href="{{ site.baseurl }}/speakers.html#xing-huang">Xing Huang</a></p> + <p class="talk-meta">Tuesday 10th November, 01:45 PM · <a href="{{ site.baseurl }}/speakers.html#xing-huang">Xing Huang</a></p> <div class="talk-abstract" markdown="1"> Git has supported SHA-256 repositories since version 2.29, and upstream Git is @@ -402,7 +390,7 @@ </article> <article class="talk-card" id="ghs-sustaining-git-performance-under-production-workloads"> <h3 class="talk-title">GHS: Sustaining Git Performance Under Production Workloads</h3> - <p class="talk-meta">Tuesday 10th November, 02:45 PM · <a href="{{ site.baseurl }}/speakers.html#shane-mcintosh">Shane McIntosh</a></p> + <p class="talk-meta">Tuesday 10th November, 02:15 PM · <a href="{{ site.baseurl }}/speakers.html#shane-mcintosh">Shane McIntosh</a></p> <div class="talk-abstract" markdown="1"> Although decentralized Version Control Systems (VCSs) like Git support several @@ -425,7 +413,7 @@ </article> <article class="talk-card" id="scaling-gerrit-builds-with-kubernetes-and-real-time-output-sync"> <h3 class="talk-title">Scaling Gerrit Builds with Kubernetes and Real-Time Output Sync</h3> - <p class="talk-meta">Tuesday 10th November, 03:35 PM · <a href="{{ site.baseurl }}/speakers.html#shameer-erakkath-saidumuhammed">Shameer Erakkath Saidumuhammed</a></p> + <p class="talk-meta">Tuesday 10th November, 03:05 PM · <a href="{{ site.baseurl }}/speakers.html#shameer-erakkath-saidumuhammed">Shameer Erakkath Saidumuhammed</a></p> <div class="talk-abstract" markdown="1"> As repositories and development teams grow, Gerrit-triggered build and @@ -465,7 +453,7 @@ </article> <article class="talk-card" id="how-to-stack-changes-like-a-pro"> <h3 class="talk-title">How to Stack Changes like a Pro</h3> - <p class="talk-meta">Tuesday 10th November, 04:05 PM · <a href="{{ site.baseurl }}/speakers.html#jacek-centkowski">Jacek Centkowski</a></p> + <p class="talk-meta">Tuesday 10th November, 03:35 PM · <a href="{{ site.baseurl }}/speakers.html#jacek-centkowski">Jacek Centkowski</a></p> <div class="talk-abstract" markdown="1"> Last year the message was: focus the diff. Small, single-concern changes get @@ -485,7 +473,7 @@ </article> <article class="talk-card" id="q-a-with-the-maintainers"> <h3 class="talk-title">Q&A with the maintainers</h3> - <p class="talk-meta">Tuesday 10th November, 04:20 PM</p> + <p class="talk-meta">Tuesday 10th November, 03:50 PM</p> <div class="talk-abstract" markdown="1"> Join us for what has become a staple session of the Gerrit User Summit where the