// The persistent right-side panel: About · Listen & lyrics · Paths · Fan works for the current song. // It follows the store (song, tab, picked motif, timeline, reading view) and writes back to it; the play // button in the header starts the song and opens Listen & lyrics. Collapsing the panel to its slim tab is // local to the panel (it sets data-panel on the surrounding .shell). Ported from 17's panel.ts. import { songById, songs, sagaOf, type AlbumSong } from "../../data/album"; import { contentMounted, libraryReady, realFor } from "../content"; import type { AppState, Store, Tab } from "../state"; import { aboutView } from "./about"; import { fanView } from "./fanworks"; import { createListen } from "./listen"; import { pathsView } from "./paths"; import { readingView } from "./reading"; import { applyPick, esc, fmt, PAUSE_ICON, PLAY_ICON } from "./util"; export const TABS: { id: Tab; label: string }[] = [ { id: "about", label: "About" }, { id: "listen", label: "Listen & lyrics" }, { id: "paths", label: "Paths" }, { id: "fan", label: "Fan works" }, ]; export interface PanelOpts { /** Whether the bottom timeline is hidden (besides `state.timeline === "closed"`), e.g. on small screens. */ timelineHidden: () => boolean; } export function mountPanel(root: HTMLElement, store: Store, opts: PanelOpts): { refresh(): void; destroy(): void } { const reduced = () => typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches; let song: AlbumSong = songById(store.get().songId) ?? songs[0]!; let mounted = contentMounted(); let readAll = false; let destroyed = false; root.classList.add("panel"); root.innerHTML = `
${esc(saga.name)} · ${esc(song.storyTime.label)}
`; q