> ## Documentation Index
> Fetch the complete documentation index at: https://globaliptv.misidev.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Components

> Reference for all SceneGraph components in the GlobalTV Roku channel — screens, managers, and overlays.

All UI components are SceneGraph nodes that run on the render thread. Each screen implements `ApplyResponsiveLayout()` so MainScene can relayout it when the display resolution changes.

<AccordionGroup>
  <Accordion title="MainScene">
    **File:** `components/MainScene.*`\
    **Extends:** `Scene`

    ### Purpose

    Root scene and app lifecycle orchestrator. Initializes global state, manages the screen stack, drives the handshake → auth → playlist startup sequence, and handles deep links.

    ### Interface fields

    ```xml theme={null}
    <field id="launchDeepLink"  type="assocarray" onChange="onLaunchDeepLink" />
    <field id="inputDeepLink"   type="assocarray" onChange="onInputDeepLink" />
    <field id="exitRequested"   type="integer"    value="0" />
    ```

    | Field            | Direction | Description                                                                 |
    | ---------------- | --------- | --------------------------------------------------------------------------- |
    | `launchDeepLink` | input     | Cold-start deep link payload (`mediaType`, `contentId`). Set by `main.brs`. |
    | `inputDeepLink`  | input     | Warm-start deep link payload. Set by `main.brs` on input event.             |
    | `exitRequested`  | output    | Incremented when the user confirms exit; observed by `main.brs`.            |

    ### Key children

    ```xml theme={null}
    <Rectangle id="bgRect" width="1920" height="1080" color="0x0F1020FF" />
    ```

    ### Key behaviors

    * On `init()`: calls `GTV_InitGlobalState()`, creates relayout and focus-repair timers, creates the session auth timer (`SESSION_AUTH_CHECK_MS = 420000 ms`), then calls `ShowSplash()`.
    * After splash: checks registry for saved credentials. If found, runs auto-login; otherwise shows `OnboardingScreen`.
    * Screen stack: only one primary screen is active at a time (`m.currentScreen`). `MainScreen` and `PlayerScreen` co-exist as siblings; `MainScreen` is shown in overlay mode when the channel list is requested over the player.
    * Session auth timer fires every 7 minutes while authenticated and re-runs `AuthTask` silently. On failure it classifies the reason and shows the appropriate session-issue dialog.
    * Signals `AppDialogInitiate` / `AppDialogComplete` Roku certification beacons during pre-home dialogs.
    * `signalBeacon("AppLaunchComplete")` is fired by `MainScreen` once the channel grid is visible.
  </Accordion>

  <Accordion title="SplashScreen">
    **File:** `components/SplashScreen/SplashScreen.*`\
    **Extends:** `Group`

    ### Purpose

    Launch splash displayed immediately after app start. Shows the GlobalTV logo and splash image, then fires `splashDone` after `SPLASH_MS` (1 500 ms).

    ### Interface fields

    ```xml theme={null}
    <field id="splashDone" type="boolean" value="false" />
    <function name="ApplyResponsiveLayout" />
    ```

    | Field        | Direction | Description                                                               |
    | ------------ | --------- | ------------------------------------------------------------------------- |
    | `splashDone` | output    | Set to `true` when the timer fires; observed by `MainScene.OnSplashDone`. |

    ### Key children

    ```xml theme={null}
    <Rectangle  id="bg"         color="0x1A1A2EFF" />
    <Poster     id="splashImg"  uri="pkg:/images/splash_hd.png" />
    <Poster     id="logoImg"    uri="pkg:/images/globaltv-logo.png" visible="false" />
    <Label      id="versionLabel" />
    <Timer      id="splashTimer" duration="1.5" repeat="false" />
    ```

    ### Key behaviors

    * Timer duration maps directly to `AppConstants().SPLASH_MS / 1000` (1.5 seconds).
    * `ApplyResponsiveLayout()` repositions children to match the current safe-area layout context.
  </Accordion>

  <Accordion title="OnboardingScreen">
    **File:** `components/OnboardingScreen/OnboardingScreen.*`\
    **Extends:** `Group`

    ### Purpose

    First-run / logged-out screen. Presents the app branding and a prompt to proceed to login. Also handles exit requests.

    ### Interface fields

    ```xml theme={null}
    <field id="goToLogin"    type="boolean" value="false" />
    <field id="requestExit"  type="integer" value="0" />
    ```

    | Field         | Direction | Description                                                                                |
    | ------------- | --------- | ------------------------------------------------------------------------------------------ |
    | `goToLogin`   | output    | Set to `true` when the user presses the login button. Observed by `MainScene.OnGoToLogin`. |
    | `requestExit` | output    | Incremented when the user presses Back to exit. Observed by `MainScene.OnRequestExit`.     |

    ### Key behaviors

    * Signals `AppDialogInitiate("OnboardingScreen")` for Roku certification timing.
    * `ApplyResponsiveLayout()` adapts to 1080p / 720p display modes.
  </Accordion>

  <Accordion title="LoginScreen">
    **File:** `components/LoginScreen/LoginScreen.*`\
    **Extends:** `Group`

    ### Purpose

    On-device credential entry screen. Collects username and password, optionally calls Roku's `getUserData` RFI to pre-fill the Roku account email, authenticates via `AuthTask`, and returns the result to `MainScene`.

    ### Interface fields

    ```xml theme={null}
    <field id="loginSucceeded" type="boolean"   value="false" />
    <field id="loginResult"    type="assocarray" />
    <function name="ApplyResponsiveLayout" />
    ```

    | Field            | Direction | Description                                                                 |
    | ---------------- | --------- | --------------------------------------------------------------------------- |
    | `loginSucceeded` | output    | Set to `true` on successful auth. Observed by `MainScene.OnLoginSucceeded`. |
    | `loginResult`    | output    | AssocArray with `username`, `password`, and optional `rokuEmail`.           |

    ### Key children

    ```xml theme={null}
    <Poster     id="loginBg"    uri="pkg:/images/login_bg_fhd.png" />
    <Label      id="usernameValue" />
    <Label      id="passwordValue" />
    <Label      id="lblError"   visible="false" />
    <Label      id="statusHintLabel" visible="false" />
    <GTV_Spinner id="spinner"  visible="false" />
    ```

    ### Key behaviors

    * Two virtual keyboard fields (username, password) managed on the render thread via standard `roKeyEvent` handling.
    * "Show password" toggle reveals masked password characters.
    * Calls Roku `getUserData` to request the subscriber's Roku email (`rokuEmail`) as part of the auth context.
    * Displays inline `statusHintLabel` during auth to relay `AuthTask.statusMessage` progress text (e.g. "Conectando con servidor local...").
    * Signals `AppDialogInitiate("LoginScreen")` for certification beacon tracking.
  </Accordion>

  <Accordion title="MainScreen">
    **File:** `components/MainScreen/MainScreen.*`\
    **Extends:** `Group`

    ### Purpose

    Channel grid/list. Displays channels organized by category rows. Operates in two modes: full-screen (normal navigation) and overlay mode (transparent panel over the active player).

    ### Interface fields

    ```xml theme={null}
    <field id="channels"         type="array"   onChange="onChannelsChanged" />
    <field id="categories"       type="array" />
    <field id="initialIndex"     type="integer" value="0" />
    <field id="channelSelected"  type="integer" value="-1" />
    <field id="openSettings"     type="boolean" value="false" />
    <field id="overlayMode"      type="boolean" value="false" onChange="onOverlayModeChanged" />
    <field id="closeOverlay"     type="integer" value="0" />
    <field id="requestExit"      type="integer" value="0" />
    <field id="infoMessage"      type="string"  value="" onChange="onInfoMessageChanged" />
    <function name="GiveFocusToRowList" />
    <function name="ReleaseOverlayFocus" />
    <function name="ApplyResponsiveLayout" />
    <function name="SyncFocusToFlatIndex" />
    <function name="RestoreOverlayContext" />
    <function name="HandleOverlayKey" />
    ```

    | Field             | Direction | Description                                                        |
    | ----------------- | --------- | ------------------------------------------------------------------ |
    | `channels`        | input     | Flat array of channel objects from the playlist parser.            |
    | `categories`      | input     | Array of category name strings.                                    |
    | `initialIndex`    | input     | Flat channel index to pre-select on load.                          |
    | `channelSelected` | output    | Flat index of the channel the user activated.                      |
    | `openSettings`    | output    | Set to `true` when the user opens Settings from within the grid.   |
    | `overlayMode`     | input     | `true` = transparent panel over player; `false` = full-screen.     |
    | `closeOverlay`    | output    | Incremented when the user dismisses the overlay (Back key).        |
    | `requestExit`     | output    | Incremented when the user requests exit from the grid.             |
    | `infoMessage`     | input     | Short status string shown in a banner (e.g. "Lista actualizada."). |

    ### Key behaviors

    * Fires `signalBeacon("AppLaunchComplete")` the first time the grid becomes visible, satisfying the Roku certification requirement.
    * In overlay mode the background is set to `COLOR_OVERLAY` (`0x000000CC`) so the live video below remains visible.
    * `SyncFocusToFlatIndex({flatIndex, forceTodos})` scrolls the row list to the channel matching the currently playing index.
    * `RestoreOverlayContext({categoryIndex, rowIndex})` restores the last manually selected row when reopening the overlay.
  </Accordion>

  <Accordion title="PlayerScreen">
    **File:** `components/PlayerScreen/PlayerScreen.*`\
    **Extends:** `Group`

    ### Purpose

    Live stream player. Hosts the `Video` node, manages channel switching, reconnection, freeze detection, ad viewport reduction, and all player-level dialogs (offline, inactive, error).

    ### Interface fields

    ```xml theme={null}
    <field id="channelIndex"   type="integer" value="-1" onChange="onChannelIndexChanged" />
    <field id="requestOverlay" type="integer" value="0" />
    <field id="closeOverlay"   type="integer" value="0" />
    <field id="exitPlayer"     type="boolean" value="false" />
    <field id="openSettings"   type="boolean" value="false" />
    <field id="userInactive"   type="boolean" value="false" />
    <field id="requestExit"    type="integer" value="0" />
    <field id="modalActive"    type="boolean" value="false" />
    <function name="ApplyResponsiveLayout" />
    <function name="FocusActiveModal" />
    <function name="FocusPlaybackNode" />
    <function name="IsAnyModalOpen" />
    <function name="IsOfflineModalOpen" />
    ```

    | Field            | Direction | Description                                                                    |
    | ---------------- | --------- | ------------------------------------------------------------------------------ |
    | `channelIndex`   | input     | Flat index into `m.global.channelList`. Setting this tunes to the channel.     |
    | `requestOverlay` | output    | Incremented when the user presses Up/OK to show the channel list overlay.      |
    | `closeOverlay`   | output    | Incremented when the user closes the overlay from within the player.           |
    | `exitPlayer`     | output    | Set to `true` when the user backs out of the player entirely.                  |
    | `openSettings`   | output    | Set to `true` when the user opens the Settings overlay from the player.        |
    | `userInactive`   | output    | Set to `true` when `AdsPollingTask` signals account inactivity.                |
    | `modalActive`    | output    | `true` while any player-level modal dialog (error, offline, inactive) is open. |

    ### Key behaviors

    * Starts `AdsPollingTask` and `ConnectivityTask` as soon as a channel is loaded.
    * `AdManager` is embedded and observes `adData` / `adsSnapshot` from the polling task.
    * When `AdFormatC` is active, `videoHeightReduction` and `videoOffsetY` fields shrink the `Video` node's viewport to make room for the bottom-banner ad.
    * Freeze detection timer (`FREEZE_CHECK_MS = 10 000 ms`) restarts the stream automatically if no position change is detected.
    * Network recovery: when `ConnectivityTask` reports internet restored after an outage, the player attempts a reconnect.
    * `StopVideo()` is called by `MainScene` before session resets and server changes.
  </Accordion>

  <Accordion title="SettingsScreen">
    **File:** `components/SettingsScreen/SettingsScreen.*`\
    **Extends:** `Group`

    ### Purpose

    Configuration panel. Provides channel refresh, logout, safe-area adjustment, and a technical server management panel.

    ### Interface fields

    ```xml theme={null}
    <field id="closeSettings"              type="boolean" value="false" />
    <field id="logoutRequested"            type="boolean" value="false" />
    <field id="refreshChannelsRequested"   type="integer" value="0" />
    <field id="safeAreaChanged"            type="integer" value="0" />
    <field id="applyServerRequested"       type="boolean" value="false" />
    <field id="applyServerId"              type="string"  value="" />
    <function name="ApplyResponsiveLayout" />
    ```

    | Field                      | Direction | Description                                                                                                 |
    | -------------------------- | --------- | ----------------------------------------------------------------------------------------------------------- |
    | `closeSettings`            | output    | Set to `true` when the user exits Settings.                                                                 |
    | `logoutRequested`          | output    | Set to `true` when the user confirms logout.                                                                |
    | `refreshChannelsRequested` | output    | Incremented each time the user triggers a channel list refresh.                                             |
    | `safeAreaChanged`          | output    | Incremented when the user adjusts the safe-area margin; observed by `MainScene` to trigger layout relayout. |
    | `applyServerRequested`     | output    | Set to `true` when the user selects a server to apply. `applyServerId` carries the profile ID.              |

    ### Key behaviors

    * Server panel shows profiles loaded from `GTV_LoadServerProfiles()`. Each profile can be probed via `ServerProbeTask`.
    * Safe-area margin persists to registry via `GTV_RegSaveSafeMarginPct()` and triggers `OnSettingsSafeAreaChanged` in `MainScene`, which calls `ApplySceneLayout()` and relayouts all active screens.
    * Logout clears saved credentials and cache via `GTV_RegClearCredentials()` / `GTV_RegClearCache()`.
  </Accordion>

  <Accordion title="AdManager">
    **File:** `components/ads/AdManager.*`\
    **Extends:** `Group`

    ### Purpose

    Ad lifecycle manager. Receives ad payloads from `AdsPollingTask`, instantiates the correct format renderer (`AdFormatA`, `AdFormatB`, or `AdFormatC`), manages viewport reduction for format C, and emits metric events.

    ### Interface fields

    ```xml theme={null}
    <field id="adPayload"            type="assocarray" onChange="onAdPayload" />
    <field id="adsSnapshot"          type="assocarray" onChange="onAdsSnapshot" />
    <field id="clearAds"             type="boolean"    value="false" onChange="onClearAds" />
    <field id="trackMetric"          type="assocarray" />
    <field id="allAdsHidden"         type="boolean"    value="false" />
    <field id="videoHeightReduction" type="integer"    value="0" />
    <field id="videoOffsetY"         type="integer"    value="0" />
    <field id="videoViewport"        type="assocarray" onChange="onVideoViewportChanged" />
    <field id="videoBaseViewport"    type="assocarray" onChange="onVideoBaseViewportChanged" />
    ```

    | Field                  | Direction | Description                                                           |
    | ---------------------- | --------- | --------------------------------------------------------------------- |
    | `adPayload`            | input     | Single normalized ad object from `AdsPollingTask.adData`.             |
    | `adsSnapshot`          | input     | Full snapshot object from `AdsPollingTask.adsSnapshot`.               |
    | `clearAds`             | input     | Set to `true` to tear down all active ad renderers.                   |
    | `trackMetric`          | output    | AssocArray metric event written here for `MetricsTask` to consume.    |
    | `allAdsHidden`         | output    | `true` when no ad is currently visible.                               |
    | `videoHeightReduction` | output    | Pixels to shrink the `Video` node height (Format C only).             |
    | `videoOffsetY`         | output    | Y offset for the `Video` node when Format C is active.                |
    | `videoViewport`        | input     | Current video viewport dimensions; used to position format renderers. |
    | `videoBaseViewport`    | input     | Unmodified base viewport; used to restore after Format C teardown.    |

    ### Key behaviors

    * Inspects `adPayload.format.type` to select `AdFormatA`, `AdFormatB`, or `AdFormatC`.
    * Tracks impression start time; on teardown emits an `ad_impression_closed` metric with `visible_ms`.
  </Accordion>

  <Accordion title="AdFormatA / AdFormatB / AdFormatC">
    **Files:** `components/ads/AdFormatA.*`, `AdFormatB.*`, `AdFormatC.*`\
    **Extends:** `Group`

    ### Purpose

    Individual ad format renderers. Each loads a remote media URL into a `Poster` node and fades it in. Format C additionally reports viewport reduction values so `PlayerScreen` can shrink the live video to make room.

    ### Interface fields (all three formats)

    ```xml theme={null}
    <field id="adConfig"             type="assocarray" onChange="onAdConfigChanged" />
    <field id="imageLoaded"          type="boolean"    value="false" />
    <field id="imageError"           type="boolean"    value="false" />
    ```

    ### Additional fields — AdFormatC only

    ```xml theme={null}
    <field id="videoHeightReduction" type="integer" value="0" />
    <field id="videoOffsetY"         type="integer" value="0" />
    ```

    | Field                             | Description                                                                           |
    | --------------------------------- | ------------------------------------------------------------------------------------- |
    | `adConfig`                        | Normalized ad payload from `AdManager`. Contains `media_url`, `format`, `ad_id`, etc. |
    | `imageLoaded`                     | Set to `true` when the `Poster` has finished loading.                                 |
    | `imageError`                      | Set to `true` if the image load fails.                                                |
    | `videoHeightReduction` *(C only)* | Pixels to reduce the player's video height.                                           |
    | `videoOffsetY` *(C only)*         | Y offset to apply to the video node.                                                  |

    ### Key children (all three)

    ```xml theme={null}
    <Poster id="adPoster" loadDisplayMode="scaleToFit" visible="false" />
    <Timer  id="fadeTimer" duration="0.05" repeat="true" />
    ```

    ### Key behaviors

    * **Format A** — Full-screen or large overlay image, fades in over the video.
    * **Format B** — Partial overlay image at a configurable position and size, fades in.
    * **Format C** — Bottom-banner image. On load, writes `videoHeightReduction` and `videoOffsetY` so `PlayerScreen` pushes the video up to avoid overlap.
    * All formats fade in using a 50 ms repeating timer and fade out on teardown.
  </Accordion>

  <Accordion title="Overlays — NumberOverlay & ChannelBanner">
    **Files:** `components/overlays/NumberOverlay.*`, `components/overlays/ChannelBanner.*`\
    **Extends:** `Group`

    ### NumberOverlay

    Displays a channel-number input prompt when the user types digits on the remote. Commits after a 2-second idle timer.

    ```xml theme={null}
    <field id="numberCommitted" type="string" value="" />
    ```

    | Field             | Description                                                                                                    |
    | ----------------- | -------------------------------------------------------------------------------------------------------------- |
    | `numberCommitted` | The full digit string after the commit timer fires; consumed by `PlayerScreen` to tune to that channel number. |

    **Key children:**

    ```xml theme={null}
    <Rectangle id="numBg"     color="0x101B42DD" width="300" height="180" />
    <Rectangle id="numAccent" color="0x2D57C1FF" width="6"   height="180" />
    <Label     id="lblDigits" font="font:LargeBoldSystemFont" />
    <Timer     id="commitTimer" duration="2" repeat="false" />
    ```

    ### ChannelBanner

    Info overlay shown briefly when a channel starts or changes. Displays channel number, logo, name, live status, and category.

    ```xml theme={null}
    <field id="channelData"    type="assocarray" onChange="onChannelDataChanged" />
    <field id="networkOnline"  type="boolean"    value="true" onChange="onNetworkOnlineChanged" />
    <field id="statusText"     type="string"     value=""     onChange="onStatusTextChanged" />
    ```

    | Field           | Description                                                                     |
    | --------------- | ------------------------------------------------------------------------------- |
    | `channelData`   | AssocArray with `name`, `number`, `logo`, `group` from the parsed channel list. |
    | `networkOnline` | When `false`, replaces the "En vivo" status with an offline indicator.          |
    | `statusText`    | Override status string (e.g. reconnecting message).                             |

    **Key children:**

    ```xml theme={null}
    <Rectangle id="bannerBg"     color="0x14234FCC" />
    <Rectangle id="bannerAccent" color="0x2D57C1FF" />
    <Label     id="lblNumber" />
    <Poster    id="chLogo" />
    <Label     id="lblName" />
    <Label     id="lblStatus"   text="En vivo" />
    <Label     id="lblCategory" />
    <Timer     id="hideTimer"  duration="3"    repeat="false" />
    <Timer     id="fadeTimer"  duration="0.05" repeat="true" />
    ```

    The banner auto-hides after `BANNER_MS` (3 000 ms) with a fade-out driven by the `fadeTimer`.
  </Accordion>
</AccordionGroup>
