davideisinger.com

My personal website
Log | Files | Refs | README

commit e055b0ca7d0ff724792b9a0037b4f295ea5c6997
parent 0ec62439205a3ebe8d12394f0a3e96ee6d66a841
Author: David Eisinger <[email protected]>
Date:   Sun, 11 Oct 2026 09:03:02 -0400

Use new player in posts

Diffstat:
Mtest/navigation.cjs | 41+++++++++++++++++++++++++++++++++++++----
Mthemes/v2/assets/css/style.scss | 2+-
Mthemes/v2/assets/js/player.js | 28++++++++++++++++++++++++++++
Mthemes/v2/layouts/elsewhere/single.html | 4+++-
Mthemes/v2/layouts/journal/single.html | 4+++-
5 files changed, 72 insertions(+), 7 deletions(-)

diff --git a/test/navigation.cjs b/test/navigation.cjs @@ -22,20 +22,20 @@ function write(name, text) { (async () => { fs.cpSync(path.join(repo, 'themes/v2'), path.join(root, 'themes/v2'), { recursive: true }); - write('hugo.toml', 'baseURL="http://localhost/"\ntheme="v2"\ntitle="David Eisinger"\n'); + write('hugo.toml', 'baseURL="http://localhost/"\ntheme="v2"\ntitle="David Eisinger"\n[markup.goldmark.renderer]\nunsafe=true\n'); write('layouts/partials/marky.html', ''); write('content/_index.md', 'A personal website.'); write('content/music/_index.md', '---\ntitle: Music\n---\n'); write('content/about/index.md', '---\ntitle: About\n---\n' + '[Jump to contact](#contact)\n\n' + 'A paragraph for testing scroll restoration.\n\n'.repeat(90) + '## Contact\n\n[Music](/music/)\n\n' + 'More content.\n\n'.repeat(20)); - write('content/journal/example/index.md', '---\ntitle: Example post\ndate: 2026-01-01\ntags: [dispatch]\n---\nHello.'); + write('content/journal/example/index.md', '---\ntitle: Example post\ndate: 2026-01-01\ntags: [dispatch]\n---\nHello.\n\n<audio controls src="/journal/example/Night Moon.mp3"></audio>'); fs.copyFileSync(path.join(repo, 'content/journal/dispatch-44-october-2026/Night Moon.mp3'), path.join(root, 'content/journal/example/Night Moon.mp3')); - write('content/journal/older/index.md', '---\ntitle: Older post\ndate: 2025-01-01\n---\nAn earlier recording.'); + write('content/journal/older/index.md', '---\ntitle: Older post\ndate: 2025-01-01\n---\nAn earlier recording.\n\n<audio controls src="/journal/older/Older Song.mp3"></audio>\n\nAnother copy.\n\n<audio controls src="/journal/older/Older Song.mp3"></audio>'); fs.copyFileSync(path.join(repo, 'content/journal/dispatch-44-october-2026/Night Moon.mp3'), path.join(root, 'content/journal/older/Older Song.mp3')); - write('content/elsewhere/example/index.md', '---\ntitle: Elsewhere example\ndate: 2025-01-01\ncanonical_url: https://example.org/original\n---\nOriginal article.'); + write('content/elsewhere/example/index.md', '---\ntitle: Elsewhere example\ndate: 2025-01-01\ncanonical_url: https://example.org/original\nexclude_music: true\n---\nOriginal article.\n\n<audio controls src="/journal/example/Night Moon.mp3"></audio>'); server = http.createServer((request, response) => { const isFetch = request.headers.accept === 'text/html'; if (isFetch && failNextFetch) { @@ -196,6 +196,29 @@ function write(name, text) { assert.equal(await page.locator('#site-header .track-title').textContent(), 'Night Moon'); assert.equal(await page.locator('#content audio').evaluateAll(players => players.every(audio => audio.paused)), true); + // Post embeds share playback and metadata, including multiple embeds and revisits. + await click('/journal/older/'); + assert.equal(await page.locator('.track-toggle').count(), 2); + assert.equal(await page.locator('#content audio:visible').count(), 0); + await page.getByRole('button', { name: 'Play Older Song', exact: true }).first().click(); + await page.waitForFunction(() => !originalAudio.paused && originalAudio.currentTime > 0.1 + && originalAudio.src.includes('Older%20Song.mp3')); + assert.equal(await page.getByRole('button', { name: 'Pause Older Song', exact: true }).count(), 2); + assert.equal(await page.locator('#site-header .track-source a').textContent(), 'Older post'); + await click('/music/'); + await page.getByRole('button', { name: 'Pause Older Song', exact: true }).waitFor(); + await click('/journal/older/'); + assert.equal(await page.getByRole('button', { name: 'Pause Older Song', exact: true }).count(), 2); + await page.getByRole('button', { name: 'Pause Older Song', exact: true }).last().click(); + assert.equal(await page.evaluate(() => originalAudio.paused), true); + await click('/elsewhere/example/'); + assert.equal(await page.locator('.track-toggle').count(), 0); + assert.equal(await page.locator('#content audio:visible').count(), 1); + await click('/journal/example/'); + await page.getByRole('button', { name: 'Play Night Moon', exact: true }).click(); + await page.getByRole('button', { name: 'Pause Night Moon', exact: true }).waitFor(); + assert.equal(await page.locator('#site-header .track-source a').textContent(), 'Example post'); + // A failed enhanced request must retry with normal document navigation. failNextFetch = true; await click('/about/'); @@ -217,8 +240,18 @@ function write(name, text) { await plain.getByRole('link', { name: 'More music →' }).click(); assert.equal(await plain.locator('#content audio:visible').count(), 2); assert.equal(await plain.locator('.track-toggle').count(), 0); + await plain.goto(origin + '/journal/example/'); + assert.equal(await plain.locator('#content audio:visible').count(), 1); + assert.equal(await plain.locator('.track-toggle').count(), 0); await noJS.close(); + // A direct post load enhances its embeds too. + await page.goto(origin + '/journal/older/'); + assert.equal(await page.locator('.track-toggle').count(), 2); + await page.getByRole('button', { name: 'Play Older Song', exact: true }).first().click(); + await page.getByRole('button', { name: 'Pause Older Song', exact: true }).first().waitFor(); + + await page.setViewportSize({ width: 390, height: 850 }); await page.goto(origin); await click('/about/'); diff --git a/themes/v2/assets/css/style.scss b/themes/v2/assets/css/style.scss @@ -422,7 +422,7 @@ pre { align-items: center; display: flex; gap: 2ch; - padding: 2ch 0; + padding: 1ch 0; > div { flex: 1; diff --git a/themes/v2/assets/js/player.js b/themes/v2/assets/js/player.js @@ -54,7 +54,35 @@ } } + function preparePostTracks() { + document.querySelectorAll('#content [data-post-music]').forEach(post => { + post.querySelectorAll('audio[src]').forEach(audio => { + if (audio.closest('.music-track')) return; + const url = new URL(audio.src); + if (!url.pathname.toLowerCase().endsWith('.mp3')) return; + let name = url.pathname.split('/').pop().slice(0, -4); + try { name = decodeURIComponent(name); } catch { /* Keep malformed filenames readable. */ } + const row = document.createElement('div'); + row.className = 'music-track'; + const details = document.createElement('div'); + const trackTitle = document.createElement('p'); + trackTitle.className = 'track-title'; + trackTitle.textContent = name; + const trackSource = document.createElement('p'); + trackSource.className = 'track-source'; + const link = document.createElement('a'); + link.href = post.dataset.postUrl; + link.textContent = post.dataset.postTitle; + trackSource.append('From ', link); + details.append(trackTitle, trackSource); + audio.before(row); + row.append(audio, details); + }); + }); + } + function enhance() { + preparePostTracks(); controls = []; document.querySelectorAll('#content .music-track').forEach(row => { const audio = row.querySelector('audio'); diff --git a/themes/v2/layouts/elsewhere/single.html b/themes/v2/layouts/elsewhere/single.html @@ -18,7 +18,9 @@ on <a href="{{ .Params.canonical_url }}">{{ $host }}</a> </h3> - {{ .Content }} + <div{{ if ne .Params.exclude_music true }} data-post-music{{ end }} data-post-title="{{ .Title }}" data-post-url="{{ .RelPermalink }}"> + {{ .Content }} + </div> {{- partial "references.html" . -}} {{- partial "backlinks.html" . -}} diff --git a/themes/v2/layouts/journal/single.html b/themes/v2/layouts/journal/single.html @@ -18,7 +18,9 @@ {{ end }} </h3> - {{ .Content }} + <div{{ if ne .Params.exclude_music true }} data-post-music{{ end }} data-post-title="{{ .Title }}" data-post-url="{{ .RelPermalink }}"> + {{ .Content }} + </div> {{- partial "references.html" . -}} {{- partial "backlinks.html" . -}}