feat: bun app foundation, sample data, shared credits/audio, mockup stubs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d52ac63afe
commit
10ce858b31
42 files changed
+839
No files matched your search
@@ -0,0 +1,29 @@
|
||||
import type { Song } from "../data/types";
|
||||
|
||||
export function createAudioSlot(song: Song): HTMLElement {
|
||||
const root = document.createElement("div");
|
||||
root.className = "audio-slot";
|
||||
if (!song.audio) {
|
||||
root.classList.add("audio-slot--empty");
|
||||
root.textContent = "No audio file for this song yet.";
|
||||
return root;
|
||||
}
|
||||
const audio = document.createElement("audio");
|
||||
audio.controls = true;
|
||||
audio.src = song.audio;
|
||||
root.append(audio);
|
||||
for (const part of song.parts) {
|
||||
if (part.startSec === undefined) continue;
|
||||
const btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
btn.className = "audio-slot__seek";
|
||||
btn.dataset.start = String(part.startSec);
|
||||
btn.textContent = part.name;
|
||||
btn.addEventListener("click", () => {
|
||||
audio.currentTime = part.startSec!;
|
||||
void audio.play?.();
|
||||
});
|
||||
root.append(btn);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
.fan-credits { padding: 1rem 1.5rem; font-size: 0.8rem; opacity: 0.75; text-align: center; }
|
||||
.fan-credits p { margin: 0; }
|
||||
.audio-slot { padding: 0.75rem; border: 1px solid currentColor; border-radius: 6px; }
|
||||
.audio-slot--empty { opacity: 0.6; border-style: dashed; }
|
||||
.audio-slot audio { width: 100%; }
|
||||
.audio-slot__seek { margin: 0.25rem 0.25rem 0 0; }
|
||||
.video-cards { display: flex; flex-wrap: wrap; gap: 0.5rem; }
|
||||
.video-card { display: block; padding: 0.5rem 0.75rem; border: 1px solid currentColor; border-radius: 6px; color: inherit; text-decoration: none; }
|
||||
.video-card:hover { opacity: 0.8; }
|
||||
@@ -0,0 +1,13 @@
|
||||
export const FAN_NOTICE =
|
||||
"This is an unofficial fan project, not affiliated with or endorsed by the creator of EPIC: The Musical. " +
|
||||
"With thanks to Jorge Rivera-Herrans, creator of EPIC.";
|
||||
|
||||
export function mountCredits(target?: Element): HTMLElement {
|
||||
const el = (target ?? document.querySelector("[data-fan-credits]") ?? document.body.appendChild(document.createElement("footer"))) as HTMLElement;
|
||||
el.setAttribute("data-fan-credits", "");
|
||||
el.classList.add("fan-credits");
|
||||
const p = document.createElement("p");
|
||||
p.textContent = FAN_NOTICE;
|
||||
el.replaceChildren(p);
|
||||
return el;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { Song } from "../data/types";
|
||||
|
||||
export function createVideoCards(song: Song): HTMLElement {
|
||||
const root = document.createElement("div");
|
||||
root.className = "video-cards";
|
||||
for (const v of song.videos) {
|
||||
const a = document.createElement("a");
|
||||
a.className = "video-card";
|
||||
a.href = v.url;
|
||||
a.target = "_blank";
|
||||
a.rel = "noopener noreferrer";
|
||||
a.textContent = v.title;
|
||||
root.append(a);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
Reference in new issue
Block a user