Members
(static, constant) ESTIMATED_BUTTON_ROW_HEIGHT_PX
First-frame estimate for the button row's total rendered height. Used by anchorDateLabel() before boundingRect() has settled (it reports height=0 during initial render). Observed to land around 130 px for the typical 2-line-label icon button. A later renderTracking callback replaces this with the real measured height once layout completes.
- Default Value
- 130
- Source
(static, constant) ITEM_DETAILS_EXTRAS_PADDING
Gap between bottom of itemInfoRows and top of extras pane when extras are open
- Default Value
- 24
- Source
(static, constant) LOGO_MAX_DISPLAY_HEIGHT
Maximum display height for non-Person primary images — prevents portrait posters from extending above the metadata rows. Person photos are exempt because their info rows are short and don't reach the right edge where the photo sits.
- Default Value
- 336
- Source
(static, constant) LOGO_MAX_DISPLAY_WIDTH
Maximum display width for the logo image — prevents very wide/flat logos from overlapping buttons
- Default Value
- 500
- Source
(static, constant) LOGO_MIN_DISPLAY_HEIGHT
Minimum display height for the logo image — images too small are scaled up (aspect ratio preserved).
- Default Value
- 212
- Source
Methods
(static) SetDefaultAudioTrack(mediaStreams) → {void}
SetDefaultAudioTrack: Auto-select the best audio stream based on user language prefs, storing the result on m.top.selectedAudioStreamIndex. The dropdown trigger label is refreshed separately by applyDropdown() using the same index.
| Name | Type | Description |
|---|---|---|
mediaStreams | object |
- Type:
- void
(static) activateExtras() → {void}
- Type:
- void
(static) anchorDateLabel() → {integer}
anchorDateLabel: Derive the date label's Y position from the button group's position. Returns dateLabelY so callers (e.g. logo positioning) can use it without duplicating the math. Safe to call multiple times — idempotent, and safe to call BEFORE the button group has finished laying out: falls back to the group's fixed translation + ESTIMATED_BUTTON_ROW_HEIGHT_PX so the label never lands off-screen. A later renderTracking callback refines the position once the real rendered height is known.
dateLabelY — the Y coordinate used for the date label
- Type:
- integer
(static) applyDropdown(dropdown, items, selectedId, applicable) → {void}
applyDropdown: Push items + selection into a single TrackDropdown. Hides the slot when the track type isn't applicable (keeps layout order stable for the other two slots).
| Name | Type | Description |
|---|---|---|
dropdown | object | |
items | object | |
selectedId | string | |
applicable | boolean |
- Type:
- void
(static) buildAudioDropdownItems(streams) → {object}
buildAudioDropdownItems: Convert the MediaStreams array into dropdown items for audio. Item id is the stringified server-side stream index, matching the ItemDetails contract that selectedAudioStreamIndex is an integer spanning all stream types.
| Name | Type | Description |
|---|---|---|
streams | object |
- Type:
- object
(static) buildSubtitleDropdownItems(streams) → {object}
buildSubtitleDropdownItems: Convert the MediaStreams array into dropdown items for subtitles.
Two shapes are returned depending on whether the item actually has subtitle streams: No subtitle streams at all → [{ id:"-1", title:"None" }]. Rendered as a static non-interactive slot so the "Subtitles" title row still shows and the layout stays consistent across items. "None" (not "Off") because the user has no choice to make — subtitles simply don't exist on this item. One or more subtitle streams → [{ id:"-1", title:"Off" }, ...tracks]. The "Off" entry lets the user disable subtitles when tracks ARE present. id "-1" is the SubtitleSelection.NONE sentinel used by m.top.selectedSubtitleStreamIndex.
| Name | Type | Description |
|---|---|---|
streams | object |
- Type:
- object
(static) buildVideoDropdownItems(mediaSources) → {object}
buildVideoDropdownItems: Convert MediaSources into dropdown items. "Video" here is the MediaSource, not a stream — it's the user-facing alternate-version selector (e.g. "1080p H264" vs "4K HEVC HDR10").
| Name | Type | Description |
|---|---|---|
mediaSources | object |
- Type:
- object
(static) createDividerNode(dividerId) → {object}
createDividerNode: Create a bullet divider node for separating info items
| Name | Type | Description |
|---|---|---|
dividerId | string | Unique ID for the divider |
Configured divider node
- Type:
- object
(static) createEndsAtNode() → {void}
createEndsAtNode: Build an "Ends at" time label with AM/PM support and store it on m.endsAtNode for dynamic clock updates. Used by types with runTimeTicks-based durations.
- Type:
- void
(static) createInfoLabel(labelId) → {object}
createInfoLabel: Create a bold label node for the info rows
| Name | Type | Description |
|---|---|---|
labelId | string | Unique ID for the label |
Configured LabelPrimaryMedium node
- Type:
- object
(static) createTimeLabel(labelId, timeText, periodText) → {object}
createTimeLabel: Creates a time label with an optional lowercase am/pm suffix. In 24h mode returns a single label; in 12h mode appends the period to the time text.
| Name | Type | Description |
|---|---|---|
labelId | string | |
timeText | string | |
periodText | string |
- Type:
- object
(static) deactivateExtras() → {void}
- Type:
- void
(static) displayDirectorGenreNode(node) → {void}
displayDirectorGenreNode: Add a node to the second info row, prepending a bullet divider if needed
| Name | Type | Description |
|---|---|---|
node | object |
- Type:
- void
(static) displayInfoNode(node) → {void}
displayInfoNode: Add a node to the info group, prepending a bullet divider if needed
| Name | Type | Description |
|---|---|---|
node | object |
- Type:
- void
(static) firstInteractiveDropdown() → {object}
firstInteractiveDropdown: Returns the first visible, interactive TrackDropdown in left-to-right order, or invalid if none exist.
- Type:
- object
(static) focusButtonGroupChild() → {void}
focusButtonGroupChild: Focus the button at the current buttonFocused index directly
- Type:
- void
(static) focusInteractiveDropdown(candidates) → {void}
focusInteractiveDropdown: Walks the given candidate list and focuses the first dropdown that is interactive AND visible. The candidate order encodes both direction and wrap behavior; the currently-focused dropdown is always last so the nav falls back to it if nothing else is interactive (effectively: stay put).
| Name | Type | Description |
|---|---|---|
candidates | object |
- Type:
- void
(static) getButtonIndex(buttonId) → {integer}
getButtonIndex: Find the index of a button by ID in the button group
| Name | Type | Description |
|---|---|---|
buttonId | string | The id of the button to find |
The index of the button, or -1 if not found
- Type:
- integer
(static) getHistory() → {string}
getHistory: Format the air schedule/status string for a Series item Example output: "ABC" or "Fridays at 9:30 PM on NBC"
- Type:
- string
(static) getResumeButtonText(item) → {string}
getResumeButtonText: Return "Resume S{n}E{n}" when season and episode numbers are known, otherwise fall back to plain "Resume".
| Name | Type | Description |
|---|---|---|
item | object | nextUpEpisode JellyfinBaseItem node |
Localised button label
- Type:
- string
(static) getRuntime() → {integer}
- Type:
- integer
(static) getStreamsForSelectedSource(mediaSources) → {object}
getStreamsForSelectedSource: Pull the MediaStreams array from whichever media source matches m.top.selectedVideoStreamId. Falls back to mediaSources[0] when the selected id isn't found (e.g. first load before selection).
| Name | Type | Description |
|---|---|---|
mediaSources | object |
- Type:
- object
(static) init() → {void}
- Source
- Type:
- void
(static) manageResumeButton() → {void}
manageResumeButton: Add or remove Resume button based on playback position (non-Series types)
- Type:
- void
(static) onAudioDropdownFocusExit() → {void}
- Type:
- void
(static) onAudioDropdownSelection() → {void}
Audio dropdown selection: updates selectedAudioStreamIndex and re-seeds the subtitle auto-selection (Smart mode depends on which audio is playing). If the user has already manually overridden the subtitle, we leave it alone.
- Type:
- void
(static) onButtonGrpRendered() → {void}
onButtonGrpRendered: Fires after the render thread settles the button group layout. Re-anchors the date label and repositions the logo. The track dropdown cluster lives inside itemDetails LayoutGroup and is positioned automatically by the layout engine, so it doesn't need to be re-placed here.
- Type:
- void
(static) onClockMinuteChanged() → {void}
onClockMinuteChanged: Dynamically update "Ends At" time when overhang clock minute changes.
- Type:
- void
(static) onDestroy() → {void}
onDestroy: Full teardown releasing all resources before component removal Called automatically by SceneManager.popScene() / clearScenes()
- Type:
- void
(static) onDetailsLoaded() → {void}
Callback when initial item details metadata arrives from the task
- Source
- Type:
- void
(static) onDropdownRequestDown() → {void}
DOWN from any dropdown returns focus to the button group.
- Type:
- void
(static) onDropdownRequestUp() → {void}
Dropdown navigation: UP from any dropdown goes to description when in layout, else is consumed (no-op). parent-level UP handling reused so we don't have three identical handlers.
- Type:
- void
(static) onExtrasTargetTranslationYChanged() → {void}
onExtrasTargetTranslationYChanged: Animate the RowList translation to the new target. Fired early by onKeyEvent in ExtrasRowList (at key press) so the animation runs in parallel with the RowList's floatingFocus animation, and confirmed by onRowItemFocused after focus lands.
- Type:
- void
(static) onFirstEpisodeLoaded() → {void}
onFirstEpisodeLoaded: Start playback of first episode for Series Play button
- Type:
- void
(static) onItemContentChanged() → {void}
- Type:
- void
(static) onItemDetailsRendered() → {void}
- Type:
- void
(static) onItemIdChanged() → {void}
Triggered when CreateItemDetailsGroup sets itemId — kicks off async data load
- Source
- Type:
- void
(static) onKeyEvent(key, press) → {boolean}
| Name | Type | Description |
|---|---|---|
key | string | |
press | boolean |
- Type:
- boolean
(static) onLogoLoadStatusChanged() → {void}
onLogoLoadStatusChanged: Position logo to the right, with its bottom just above the date label. When the date label is hidden, anchors to where the date label would be. Images smaller than LOGO_MIN_DISPLAY_HEIGHT are scaled up while preserving aspect ratio, unless the logo is very wide/flat, in which case LOGO_MAX_DISPLAY_WIDTH takes precedence.
- Type:
- void
(static) onLyricsLoaded() → {void}
onLyricsLoaded: Show lyrics in the description area once the async fetch completes (Audio only)
- Type:
- void
(static) onNextUpEpisodeChanged() → {void}
nextUpEpisodeChanged: For Series — create/remove Resume button based on nextUpEpisode. On first load, replaces the LoadingButton placeholder from setupButtons(). On refresh, inserts/updates/removes the Resume button directly.
- Type:
- void
(static) onPersonHasMediaChanged() → {void}
onPersonHasMediaChanged: Show or hide the Shuffle button once the person extras chain completes. On first load, replaces the LoadingButton placeholder from setupButtons(). On refresh, inserts/removes the Shuffle button directly. Fires via alwaysNotify so it triggers even when personHasMedia stays the same value (e.g. on refresh).
- Type:
- void
(static) onPlaylistContentKindChanged() → {void}
onPlaylistContentKindChanged: Drives the Watched button and item-count label based on the confirmed content kind of the playlist ("video" / "audio" / "mixed" / "unknown"). Fires via alwaysNotify so it triggers even when the kind is unchanged (e.g. on refresh).
Watched button: only shown for "video" (has at least one video-type item). Label: "Tracks" only when positively confirmed "audio" (all items are Audio type). All other kinds — including "unknown" (load failure) and "mixed" — stay "Items".
- Type:
- void
(static) onRefreshDetailsLoaded() → {void}
Callback when refreshed item details metadata arrives
- Type:
- void
(static) onRefreshExtrasData() → {void}
onRefreshExtrasData: Reload all extras rows when the user explicitly presses Refresh.
- Type:
- void
(static) onRefreshItemDetailsData() → {void}
Triggered by refreshItemDetailsData field toggle (return from playback, refresh button). Refreshes silently in the background — no spinner, no input blocking. The UI already has data from the initial load so the user can navigate freely.
- Type:
- void
(static) onRefreshResumeData() → {void}
Triggered by refreshResumeData field toggle (watched toggle on Series). Re-fetches ONLY the next-up/resume episode — no full rebuild, no setupButtons(). The existing onNextUpEpisodeChanged() handles adding/removing/updating the resume button.
- Type:
- void
(static) onScreenHidden() → {void}
- Source
- Type:
- void
(static) onScreenShown() → {void}
onScreenShown: Callback when view is presented on screen
- Source
- Type:
- void
(static) onSeasonSeriesDataLoaded() → {void}
onSeasonSeriesDataLoaded: Update Season info rows and logo with parent series metadata
- Type:
- void
(static) onSeriesResumeLoaded() → {void}
onSeriesResumeLoaded: Set nextUpEpisode from seriesResume task result to show/hide the Resume button
- Type:
- void
(static) onSubtitleDropdownFocusExit() → {void}
- Type:
- void
(static) onSubtitleDropdownSelection() → {void}
Subtitle dropdown selection: marks user override so subsequent audio changes don't clobber the pick, then stores the new index. Action "-1" = Off.
- Type:
- void
(static) onTrackDropdownFocusChanged() → {void}
onTrackDropdownFocusChanged: Observer wired to each TrackDropdown's focusedChild. Remembers the last dropdown that held focus so the UP-from-buttons handler can restore the previous slot on re-entry instead of always defaulting to video.
- Type:
- void
(static) onTrackDropdownMenuOpenChanged() → {void}
onTrackDropdownMenuOpenChanged: Observer wired to each TrackDropdown's menuOpen. Toggles the full-screen dimmer iff ANY dropdown's menu is currently open. The trackCluster (titles + triggers + the open menu itself) renders ABOVE the dimmer in XML sibling order, so only the screen content BEHIND the cluster dims — category titles and trigger labels stay full-brightness as the user's "you are here" cue while picking a track.
- Type:
- void
(static) onTrailerAvailableChanged() → {void}
- Type:
- void
(static) onTrailerCheckDone() → {void}
Callback when trailer availability check completes
- Source
- Type:
- void
(static) onVideoDropdownFocusExit() → {void}
LEFT/RIGHT between dropdowns — each dropdown has its own exit handler so we know which slot we came from and can target the correct neighbor, skipping non-interactive slots.
- Type:
- void
(static) onVideoDropdownSelection() → {void}
Video dropdown selection fires when the user picks a different MediaSource (alternate version). We swap in the new source's streams, re-run audio auto-select, and rebuild the audio/subtitle dropdowns against the new source. Focus stays on the video dropdown.
- Type:
- void
(static) pickTrackDropdownFromButtons() → {object}
pickTrackDropdownFromButtons: Chooses which TrackDropdown to focus when UP is pressed from the button group. Ignores m.lastFocusedDropdown by design — UP-from-buttons should ALWAYS land on the dropdown visually above the currently focused button, not on wherever the user was last time. Each slot spans 2 buttons: slot 1 sits above buttons [0,1], slot 2 above [2,3], slot 3 above [4,5,...]. Widens outward to the nearest interactive slot if the preferred one is static/hidden.
CRITICAL: guards on m.trackCluster.visible — when the item type has no media tracks (Series/Person/BoxSet/etc.), the cluster is hidden but the individual dropdown nodes may still carry stale visible=true / isInteractive=true from a previously-loaded item. Without this guard, focus would move to an invisible stale dropdown when the user presses UP on those item types.
- Type:
- object
(static) pickTrackDropdownFromDescription() → {object}
pickTrackDropdownFromDescription: Chooses which TrackDropdown to focus when DOWN is pressed from the description. Restores m.lastFocusedDropdown if still usable so the user returns to wherever they were before moving up. Falls back to the leftmost interactive dropdown (video → audio → subtitles) for the first-ever entry. Also guards on m.trackCluster.visible to match pickTrackDropdownFromButtons above.
- Type:
- object
(static) populateDescriptionGroup() → {void}
populateDescriptionGroup: Set FocusableOverview text with tagline and overview
- Type:
- void
(static) populateInfoGroup() → {void}
populateInfoGroup: Dispatch to type-specific info row builder
- Type:
- void
(static) populateInfoGroupAudio(item, userSettings) → {void}
populateInfoGroupAudio: Info rows for Audio (song) Row 1: Track N · Disc N (only when disc > 1) · Runtime · Ends At Row 2: Album name · Album artist
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupBoxSet(item, userSettings) → {void}
populateInfoGroupBoxSet: Info rows for BoxSet (movie collection) Row 1: Year · Official Rating · Community Rating · N Movies Row 2: Genres · Studio
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupEpisode(item, userSettings) → {void}
populateInfoGroupEpisode: Info rows for Episode Row 1: Air Date · Official Rating · Runtime · Ends At Row 2: Series name + "S{n}E{n}"
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupMovie(item, userSettings) → {void}
populateInfoGroupMovie: Info rows for Movie, Video, Recording Row 1: Year · Official Rating · Community Rating · Critic Rating · Runtime · Ends At Row 2: Genres · Director(s)
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupMusicAlbum(item, userSettings) → {void}
populateInfoGroupMusicAlbum: Info rows for MusicAlbum Row 1: Year · Runtime · Ends At Row 2: Album Artist · Genres · N Tracks
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupMusicArtist(item) → {void}
populateInfoGroupMusicArtist: Info rows for MusicArtist Row 1: N Albums · N Songs · Year Row 2: Genres
| Name | Type | Description |
|---|---|---|
item | object |
- Type:
- void
(static) populateInfoGroupMusicVideo(item, userSettings) → {void}
populateInfoGroupMusicVideo: Info rows for MusicVideo Row 1: Year · Official Rating · Runtime · Ends At Row 2: Genres · Artist(s)
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupPerson(item) → {void}
populateInfoGroupPerson: Info rows for Person Row 1: {birthDate} [- {deathDate}] · {n} years old Living: Jan 1, 1980 · 45 years old Deceased: Jan 1, 1920 - Dec 31, 1980 · 60 years old Row 2: {n} Movie(s) [· {n} Episode(s)] — omitted entirely if both counts are 0
| Name | Type | Description |
|---|---|---|
item | object |
- Type:
- void
(static) populateInfoGroupPhoto(item) → {void}
populateInfoGroupPhoto: Info rows for Photo Row 1: Resolution ("WxH") · Camera ("Make Model") · Production Year Row 2: Album name (if present)
| Name | Type | Description |
|---|---|---|
item | object |
- Type:
- void
(static) populateInfoGroupPhotoAlbum(item) → {void}
populateInfoGroupPhotoAlbum: Info rows for PhotoAlbum Row 1: Photo count
| Name | Type | Description |
|---|---|---|
item | object |
- Type:
- void
(static) populateInfoGroupPlaylist(item, userSettings) → {void}
populateInfoGroupPlaylist: Info rows for Playlist Row 1: N Items · Runtime · Ends At Row 2: Genres The item count label starts as "Items" and is swapped to "Tracks" by onPlaylistContentKindChanged() once the extras chain confirms the playlist is all-audio.
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupProgram(item, userSettings) → {void}
populateInfoGroupProgram: Info rows for Program (Live TV EPG entry) Row 1: Year · CH X - ChanName · OfficialRating · CommunityRating · S#E# · Premiere/Repeat · Runtime · BroadcastTime · Ends At Row 2: Genres (or category flags)
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupRecording(item, userSettings) → {void}
populateInfoGroupRecording: Info rows for Recording (Live TV recorded content) Row 1: PremiereYear · OfficialRating · CommunityRating · S#E# · Runtime · Ends At · CH X - ChanName · Recorded: Date Row 2: Genres
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupSeason(item, userSettings) → {void}
populateInfoGroupSeason: Info rows for Season Row 1: Year · Official Rating (series) · Avg episode runtime (series) · Ends At Row 2: Series name · Episode count · Studio
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupSeries(item, userSettings) → {void}
populateInfoGroupSeries: Info rows for Series Row 1: Year Range · Official Rating · Community Rating · Runtime · Ends At (only when avg episode runtime is available) Row 2: Genres · Air schedule/Status
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateInfoGroupTvChannel(item, userSettings) → {void}
populateInfoGroupTvChannel: Info rows for TvChannel (Live TV) Displays metadata from the current program (the channel is a vessel for what's airing). Row 1: CH N · OfficialRating · CommunityRating · S#E# · Program Name · Runtime · BroadcastTime · Ends At Row 2: Genres (or category flags)
| Name | Type | Description |
|---|---|---|
item | object | |
userSettings | object |
- Type:
- void
(static) populateTrackDropdowns(mediaSources, allStreams, itemType) → {void}
populateTrackDropdowns: Build the Video/Audio/Subtitle dropdown items from the current media source list and stream list for the given item type, then toggle the cluster + title row visibility. Seeds m.top.selectedSubtitleStreamIndex from findDefaultSubtitleStreamIndex() on first load so the initial trigger label reflects what playback will actually use.
| Name | Type | Description |
|---|---|---|
mediaSources | object | Array of MediaSource objects (may be empty) |
allStreams | object | MediaStreams from the selected source (video/audio/subs mixed) |
itemType | string | The item type string (drives which slots apply) |
- Type:
- void
(static) removeResumeButtonWithFocus(resumeButton) → {void}
removeResumeButtonWithFocus: Remove resume button while preserving focus position
| Name | Type | Description |
|---|---|---|
resumeButton | object |
- Type:
- void
(static) removeResumeLoadingButton() → {void}
removeResumeLoadingButton: Remove the resume LoadingButton placeholder if still present. Called when async data confirms no resume episode exists (nextUpEpisode stays invalid).
- Type:
- void
(static) round(f) → {integer}
| Name | Type | Description |
|---|---|---|
f | float |
- Type:
- integer
(static) setDateAdded(item) → {void}
setDateAdded: Set date added label text and position at bottom right corner
| Name | Type | Description |
|---|---|---|
item | object | JellyfinBaseItem node |
- Type:
- void
(static) setDescriptionInLayout(shouldShow) → {void}
setDescriptionInLayout: Add or remove itemDescription from the itemDetails LayoutGroup. Invisible nodes still take up space in RSG LayoutGroups, so reparenting is required to truly eliminate spacing when there is no description text.
| Name | Type | Description |
|---|---|---|
shouldShow | boolean |
- Type:
- void
(static) setFieldText(field, value) → {void}
| Name | Type | Description |
|---|---|---|
field | dynamic | |
value | dynamic |
- Type:
- void
(static) setItemLogo(item) → {void}
setItemLogo: Set the item logo image URL if available. Fallback chains by type — every chain ends in a typed placeholder via getPlaceholderImagePath() so the logo slot never goes blank, matching the pattern Person and MusicArtist always used:
- Movie/Series/BoxSet: Logo → primaryImageTag (poster, compact size) → placeholder
- Episode/Season/Recording: primaryImageTag → parentLogoImageTag (series logo) → seriesPrimaryImageTag → placeholder
- Video/MusicVideo: primaryImageTag → placeholder
- MusicAlbum / Playlist / Photo / PhotoAlbum / TvChannel: primaryImageTag → placeholder
- Program: primaryImageTag → channel logo → placeholder
- Audio: primaryImageTag → album art → placeholder
- Person / MusicArtist: primaryImageTag (portrait) → placeholder
| Name | Type | Description |
|---|---|---|
item | object | JellyfinBaseItem node |
- Type:
- void
(static) setTracksInLayout(shouldShow) → {void}
setTracksInLayout: Add or remove the 62px itemTracks spacer from the itemDetails LayoutGroup. When a track cluster is applicable to the current item, the spacer stays in the layout and pushes description bottom up to y=701 so the trackCluster sibling can sit at y=713 with 12px breathing pad above and 18px below between the description and the button group. For item types without any media tracks (Series/Person/etc.), the spacer is removed and description bottom drops back to y=775 (matching main's behavior — no cluster, no padding, no regression).
| Name | Type | Description |
|---|---|---|
shouldShow | boolean |
- Type:
- void
(static) setupButtons(item) → {void}
setupButtons: Build the complete button set for the given item type. Clears all existing buttons and adds the correct sync buttons in order. Loading placeholders are added for async buttons (Series Resume, Person Shuffle) so the button row renders at its final width immediately. The async handlers replace or remove the placeholders when data arrives. Called on every itemContentChanged — the button list is rebuilt from scratch each time.
| Name | Type | Description |
|---|---|---|
item | object |
- Type:
- void
(static) triggerTextForSelection(items, selectedId) → {string}
triggerTextForSelection: Look up the currently-selected item's display title.
| Name | Type | Description |
|---|---|---|
items | object | |
selectedId | string |
- Type:
- string
(static) updateFavoriteButton() → {void}
- Type:
- void
(static) updateItemDetailsAnimationTarget() → {void}
updateItemDetailsAnimationTarget: Slide itemDetails so the bottom of itemInfoRows sits just above the extras pane. boundingRect() on a child returns LOCAL coords relative to the parent's translation point, so we use translation[1] (not boundingRect().y) as the screen origin. Called just-in-time from the DOWN key handler to guarantee the layout is fully settled (text wrapping complete) before measuring. Eager calculation via renderTracking races with async text layout, producing stale targets — especially for long descriptions.
- Type:
- void
(static) updateTextGradient() → {void}
- Type:
- void
(static) updateWatchedButton() → {void}
- Type:
- void