Components¶
Every <nessie-…> tag is a Web Component built on NessieElement in components/base.js. They render into the light DOM (no shadow DOM), so Bootstrap classes and the theme CSS reach inside them like anywhere else.
class NessieThing extends NessieElement {
setup() { /* once, on connect: add listeners. this.listen(target, type, fn) cleans up on disconnect */ }
render() { return html`…`; } // called by update(); its result replaces innerHTML
afterRender() { /* touch the DOM render() just drew */ }
}
reactive(NessieThing, 'items'); // setting el.items calls update()
define('nessie-thing', NessieThing);
The rules every component follows (the comment at the top of base.js):
- Data in through properties (
el.tracks = […]). Attributes are only for plain settings (numbering="track"). - Requests out through bubbling events (
emit(this, PLAY, {…})). A component never calls the player or another component directly. - Listeners on anything outside itself go through
this.listen(), so they're removed when the element leaves the page.
| Component | What it does |
|---|---|
nessie-sidebar |
Navigation: Playlists, Liked Songs, the lists that span libraries, each library's links, and the folding Other libraries. Takes .libraries and .activePath. |
nessie-track-list |
The song table used almost everywhere. Click to play from a row; ⋮ menu; drag to reorder while editing; draws long lists as you scroll. The song playing is marked as in the queue: a tinted row, a bold title, and the wave icon in place of its number (in a list without numbers, over its cover: .track-art-wave, since PR #56), with aria-current="true". Each row has a heart (td.track-like, from likeButton()): with a mouse it shows on hover unless the song is liked; below 576 px it takes the length column's place. Attributes choose its columns: numbering, hide-album, and show-added. show-added is a playlist's Date added, from each item's added. Like Album, it shows from md up, so a phone's rows never scroll sideways. formatAdded() in lib/format.js writes the date: "Today", "Yesterday" or "3 days ago" in the last week, then the date in the browser's language. The full date and time is the tooltip. |
nessie-lyrics |
The lyrics panel (#lyrics-panel, opened from the player bar or Now Playing). Fetches lyrics only while open. Synced lines light up with the music: the time is counted on between player states with requestAnimationFrame, the current line is kept centred, and clicking a line seeks there. The panel has no backdrop, so the player stays usable. |
likes.js |
Not an element: liked songs (PR #56). Keeps your liked ids (GET /v1/likes at start, again on a likes or reconnect event and on a profile change; a copy in localStorage per profile for offline). Every element with data-like="<track id>" is painted (filled heart, aria-pressed, label) and one document listener toggles it, optimistically, putting it back with a toast if the server refuses. likeButton() for rows, likeMenuItem() for the ⋮ menu, showLike() for the player bar's and Now Playing's heart as songs change. |
song-menu.js |
Not an element: the items of a song's ⋮ menu (Play next, Add to queue, Add to playlist…, Add to / Remove from Liked Songs, Go to album or artist). Used by track-list rows and by the player bar and Now Playing, where playingSongMenu() builds the menu for whatever is playing when it opens. |
nessie-name-picker |
Chosen names as chips with a ×, and an Add button opening a search (.search(term) returns { name, count }). With exclude, the chips read "not Rock". Fires names-change, because its search box's own change events would otherwise be mistaken for it. It redraws only the chips, so the search stays open while you add several. |
nessie-download-button |
Download on album, playlist and mix pages. Takes .source, and shows progress ("Downloading 3 of 12", checked every 1.5 s) until "Downloaded"; tapped again, it offers to remove the download. It isn't drawn without a service worker. |
nessie-recap |
A year's recap as a full-screen story: slides built from GET /v1/stats/recap (slidesFor(), leaving out those with nothing to say), progress segments, 7 s auto-advance (none with reduced motion; held or paused it waits), taps, swipes and arrow keys, a modal dialog keeping focus. It plays along on this device only, with its own Audio: 30 s from the middle of one top song after another, fading between them (SnippetQueue: each song once before any plays again; a slide's own song, like the top artist's, comes in with its slide if it hasn't played). This device is paused and resumed, and it's muted while the server's speakers play. Its outro plays the top songs, saves them as a playlist, or saves the card as a PNG (summaryImage(), a canvas; browsers only). Own palette, the same in every theme. A slide's backdrop fills the screen behind it: the listening time's mosaic() of the year's covers, under a wash of the slide's colour (90% where the text is, 65% at the edges). Discoveries shows faces() of the new artists. Slides centre with auto margins, so a tall one (the summary on a small phone) scrolls from its top. |
nessie-notifications |
The header's bell (PR #51). Drawn once by start(), which main.js calls after sign-in (asking earlier would get a 401 and open the profile picker); after that only the badge and list change, because redrawing would close the open menu. Loads GET /v1/notifications, adds notice events with an id from live, and asks again on ready. Opening it (show.bs.dropdown) marks everything seen; the ones new before opening stay tinted (is-new) until it closes. Clear all clears up to the newest shown, and the menu stays open (data-bs-auto-close="outside"). Shown to admins and canRequest(); hides itself on a 404 (an older server). formatAgo() in lib/format.js writes the times. |
nessie-view-header |
Top of a page: art, eyebrow, <h1>, a details line. Its children become the action buttons. |
nessie-player-bar, nessie-now-playing, nessie-queue |
The player: the bottom bar, the full-screen sheet, and the Up next panel. Each takes .player and listens for its state events. The bar (on desktop) and the sheet (on phones) each have a ⋮ menu for the song playing. |
nessie-media-grid, nessie-artwork |
Cover grids (albums, artists, playlists) and one image with a fallback icon. |
nessie-pager |
Previous and next pages. Fires page. |
nessie-dialog |
Shared modal. formDialog({title, fields, onSubmit}) and confirmations. |
nessie-playlist-picker |
"Add to playlist…". Answers ADD_TO_PLAYLIST events by itself. |
nessie-profile-picker, nessie-profile-menu |
"Who's listening?" with PIN entry, and the header's profile menu. |
nessie-image-picker |
Change cover or photo: choices from the sources, upload, or reset. |
nessie-folder-picker, nessie-scan-progress |
Adding a library and following its scan (Settings → Libraries). |
nessie-description, nessie-empty-state, nessie-toasts |
Wikipedia blurbs with credit, "nothing here" panels, and toast messages. |