/* * Copyright 2023 Comcast Cable Communications Management, LLC * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 */ // blits file type reference /// import {type ShaderEffect as RendererShaderEffect, type RendererMainSettings, type FrameCounter} from '@lightningjs/renderer' import { CanvasShaderType } from '@lightningjs/renderer/canvas'; import { WebGlShaderType } from '@lightningjs/renderer/webgl'; declare module '@lightningjs/blits' { type RendererShaderEffect = import('@lightningjs/renderer').ShaderEffect type WebGlCoreShader = import('@lightningjs/renderer').WebGlCoreShader type RendererMainSettings = import('@lightningjs/renderer').RendererMainSettings export interface AnnouncerUtteranceOptions { /** * Language code (BCP 47 format, e.g., 'en-US', 'fr-FR') * * @default 'en-US' */ lang?: string, /** * Voice pitch (0 to 2, where 1 is normal) * * @default 1 */ pitch?: number, /** * Speech rate (0.1 to 10, where 1 is normal) * * @default 1 */ rate?: number, /** * Voice to use (obtained from `speechSynthesis.getVoices()`) * * @default null */ voice?: SpeechSynthesisVoice | null, /** * Volume level (0 to 1, where 1 is full volume) * * @default 1 */ volume?: number, /** * Whether to enable utterance keep-alive (prevents pausing on some platforms) * * @default undefined */ enableUtteranceKeepAlive?: boolean } export interface AnnouncerDriverOptions extends AnnouncerUtteranceOptions { /** * Message to be spoken by the platform announcer driver. */ message: string | number, /** * Internal announcement id used by the Blits announcer queue. */ id: number, } export interface AnnouncerDriver { /** * Speak one queued announcement. */ speak(options: AnnouncerDriverOptions): Promise, /** * Stop the active platform announcement, when supported. */ cancel(): void, } export interface AnnouncerUtterance extends Promise { /** * Removes a specific message from the announcement queue, * to make sure it isn't spoke out. * * Does not interupt the message when it's already being announced. */ cancel() /** * Interrupts a specific message as it is being spoken out by the Text to Speech * engine. */ stop() } export interface Announcer { /** * Instruct the Announcer to speak a message. Will add the message * to the end of announcement queue by default * * When a message is added with politeness set to `assertive` the message * will be added to the beginning of the queue * * @param message - The message to be spoken * @param politeness - Politeness level ('off', 'polite', or 'assertive') * @param options - Optional utterance options (rate, pitch, lang, voice, volume) */ speak(message: string | number, politeness?: 'off' | 'polite' | 'assertive', options?: AnnouncerUtteranceOptions): AnnouncerUtterance; /** * Instruct the Announcer to speak a message with 'polite' politeness level. * Will add the message to the end of announcement queue. * * @param message - The message to be spoken * @param options - Optional utterance options (rate, pitch, lang, voice, volume) */ polite(message: string | number, options?: AnnouncerUtteranceOptions): AnnouncerUtterance; /** * Instruct the Announcer to speak a message with 'assertive' politeness level. * Will add the message to the beginning of announcement queue. * * @param message - The message to be spoken * @param options - Optional utterance options (rate, pitch, lang, voice, volume) */ assertive(message: string | number, options?: AnnouncerUtteranceOptions): AnnouncerUtterance; /** * Configure global default utterance options that will be applied to all * subsequent announcements unless overridden by per-call options. * * @param options - Default utterance options (rate, pitch, lang, voice, volume) */ configure(options?: AnnouncerUtteranceOptions): void; /** * Instruct the Announcer to add a pause of a certain duration (in ms). Will add this pause * to the end of announcement queue * */ pause(delay: number): AnnouncerUtterance; /** * Interupts and instantly stops any running text to speech utterance * */ stop(): void; /** * Clears out the announcement queue of messages. */ clear(): void; /** * Enables the announcer. */ enable(): void; /** * Disables the announcer. Any messages passed in the announcer.speak() message * will not be added to the queue */ disable(): void; /** * Toggles the announcer based on the passed toggle value (Boolean) */ toggle(toggle: Boolean): void; } export interface Hooks { /** * Fires when the Component is being instantiated * At this moment child elements will not be available yet */ init?: () => void; /** * Fires when the Component is fully initialized and ready for interaction. */ ready?: () => void; /** * Triggers when the Component receives focus. * * This event can fire multiple times during the component's lifecycle */ focus?: () => void; /** * Triggers when the Component loses focus. * * This event can fire multiple times during the component's lifecycle */ unfocus?: () => void; /** * Fires when the Component is being destroyed and removed. */ destroy?: () => void; hover?: () => void; unhover?: () => void; /** * Fires upon each frame start (allowing you to tap directly into the renderloop) * * Note: This hook will fire continuously, multiple times per second! */ frameTick?: (data: {time: number, delta: number}) => void; /** * Fires when the component enters the viewport _margin_ and is attached to the render tree * * This event can fire multiple times during the component's lifecycle */ attach?: () => void; /** * Fires when the component leaves the viewport _margin_ and is detached from the render tree * * This event can fire multiple times during the component's lifecycle */ detach?: () => void; /** * Fires when the component enters the visible viewport * * This event can fire multiple times during the component's lifecycle */ enter?: () => void; /** * Fires when the component leaves the visible viewport * * This event can fire multiple times during the component's lifecycle */ exit?: () => void; /** * Fires when the renderer is done rendering and enters an idle state (idle = true) * and when the renderer starts rendering again and leaves the idle state (idle = false) * * @param idle - boolean to indicate whether the renderer is in idle state or not * * Note: This event can fire multiple times */ idle?: (idle: boolean) => void; /** * Fires at a predefined interval and reports the current FPS value * * Note: This event fire multiple times */ fpsUpdate?: (fps: number, frameCounter: FrameCounter) => void; } export interface Input { [key: string]: ((event: KeyboardEvent) => unknown) | undefined, /** * Catch all input function * * Will be invoked when there is no dedicated function for a certain key */ // @ts-ignore any?: (event: KeyboardEvent) => void, /** * Intercept key presses on the root Application component before being handled * by the currently focused component. * * Only when a KeyboardEvent (the original one, or a modified one) is returned from the * intercept function, the Input event is passed on to the Component with focus. * * The intercept function can be asynchronous. * * Note: the intercept input handler is only available on the Root App component (i.e. Blits.Application) */ intercept?: (event: KeyboardEvent) => KeyboardEvent | Promise | any } export interface Log { /** * Log an info message */ info(...args): typeof console.info /** * Log an error message */ error(...args): typeof console.error /** * Log a warning */ warn(...args): typeof console.warn /** * Log a debug message */ debug(...args): typeof console.debug } export interface RouteData { [key: string]: any } /** * Router Options that can be used at the same time. */ interface ConcurrentRouteOpts { /** * Whether the page navigation should be added to the history stack * used when navigating back using `this.$router.back()` * * @default true */ inHistory?: boolean, passFocus?: boolean, } /** * Route Options that can't be true at the same time */ type MutualExclusiveRouteOpts = { /** * Whether the router should reuse the current page component instance (when matching with the Component * specified for the route that we're routing to). * * @default true */ reuseComponent?: true, /** * Whether the page should be kept alive when navigating away. Can be useful * for a homepage where the state should be fully retained when navigating back * from a details page * * @default false */ keepAlive?: false } | { /** * Whether the router should reuse the current page component instance (when matching with the Component * specified for the route that we're routing to). * * @default true */ reuseComponent?: false, /** * Whether the page should be kept alive when navigating away. Can be useful * for a homepage where the state should be fully retained when navigating back * from a details page * * @default false */ keepAlive?: true } export type RouteOptions = ConcurrentRouteOpts & MutualExclusiveRouteOpts; export interface Router { /** * Navigate to a different location * * @param {string} */ to(location: string, data?: RouteData, options?: RouteOptions): boolean; /** * Get a router scoped to a named RouterView * * @param {string} */ get(name: string): Router; /** * Navigate to the previous location */ back(): boolean; /** * Enable or disable RouterView history navigation on Back input */ backNavigation: boolean; /** * Get the current route read-only */ readonly currentRoute: Route; /** * Get the list of all routes */ readonly routes: Route[]; /** * Get navigating state */ readonly navigating: boolean; /** * Reactive router state */ state: { /** * Path of the current route * * Can be used in: * - a template as `$$router.state.path` * - inside business logic as `this.$router.state.path` * - as a watcher as `$router.state.path(v) {}` */ readonly path: string /** * Whether or not the router is currently in the process of navigating * between pages * * Can be used in: * - a template as `$$router.state.navihating` * - inside business logic as `this.$router.state.navigating` * - as a watcher as `$router.state.navigating(v) {}` */ readonly navigating: boolean } } // Extension point for app- and plugin-specific fields on the component `this`. // Add your own properties (e.g., `$telemetry`, `componentName`) via TypeScript // module augmentation in your app, without changing core types. // Note: `ComponentBase` extends this interface, so augmented fields appear in all // hooks, methods, input, computed, and watch. export interface CustomComponentProperties { // Empty by design: extend in your app via TypeScript module augmentation. } export interface ComponentBase extends CustomComponentProperties { /** * Indicates whether the component currently has focus * * @returns Boolean */ readonly $hasFocus: boolean, /** * Parent component instance. Undefined for the root Application component (destroyed case not modeled). */ readonly $parent: ComponentBase | undefined, /** * Human-readable identifier for this component instance (e.g. "MyComponent_0") */ readonly $componentId: string, /** * Indicates whether the component currently is hovered * * @returns Boolean */ $isHovered: boolean, /** * Listen to events emitted by other components */ $listen: { (event: string, callback: (args: any) => void, priority?: number): void; } /** * Remove an event listener previously registered with $listen */ $unlisten: { (event: string): void; } /** * Emit events that other components can listen to * @param name - name of the event to be emitted * @param data - optional data to be passed along * @param byReference - whether or not to pass the data by reference. * The default behaviour is passing the data object by reference (`true`). * When explicitely passing `false` the object will be recursively cloned * and cleaned from any potential reactivity before emitting */ $emit(name: string, data?: any, byReference?: boolean): void; /** * Remove all listeners for this component from all events */ $clearListeners: { (): void; } /** * Set a timeout that is automatically cleaned upon component destroy */ $setTimeout: (callback: (args: any) => void, ms?: number | undefined) => ReturnType /** * Clear a timeout */ $clearTimeout: (id: ReturnType) => void /** * Debounce a function execution, preventing memory leaks and function re-allocation * @param name - Unique identifier for this debounce instance (unique per component instance) * @param callback - Function to debounce * @param ms - Delay in milliseconds * @param args - Arguments to pass to the callback */ $debounce: (name: string, callback: (...args: any[]) => void, ms?: number, ...args: any[]) => ReturnType /** * Clear a specific debounce by name * @param name - The name of the debounce to clear */ $clearDebounce: (name: string) => void /** * Clear all debounces registered on the component (automatically called on component destroy) */ $clearDebounces: () => void /** * Defer a callback to the next tick (setTimeout 0), automatically cleaned upon component destroy * @param callback - Function to execute on the next tick * @param args - Arguments to pass to the callback */ $nextTick: (callback: (...args: any[]) => void, ...args: any[]) => ReturnType /** * Set an interval that is automatically cleaned upon component destroy */ $setInterval: (callback: (args: any) => void, ms?: number | undefined) => ReturnType /** * Clear a interval */ $clearInterval: (id: ReturnType) => void /** * Log to the console with prettier output and configurable debug levels in Settings */ $log: Log /** * Set focus to the Component, optionally pass a KeyboardEvent for instant event bubbling */ $focus: (event?: KeyboardEvent) => void /** * Handle a keyboard event on this component without changing focus * @param event - The keyboard event to handle * @returns Returns true if this component or a parent component handled the event, false otherwise */ $input: (event: KeyboardEvent) => boolean /** * Select a child Element or Component by ref * * Elements and Components in the template can have an optional ref argument. * Returns an Element Instance or Component Instance. * Useful for passing on the focus to a Child component. * * @example * ```js * const menu = this.$select('Menu') * if(menu) { * menu.$focus() * } * ``` */ $select: (ref: string) => ComponentBase /** * Announcer methods for screen reader support */ $announcer: Announcer /** * Triggers a forced update on state variables. */ $trigger: (key: string) => void /** * Router instance */ $router: Router /** * Creates a reactive object. Changes to properties on the returned object * will automatically trigger re-renders in any component that accesses them. * * Pre-configured with the app's reactivity mode and global scope. * Primarily intended for use inside custom plugins. */ $reactive: >(target: T) => T /** * Dynamically set the size of a component holder node */ $size: (dimensions: { /** * Component width */ w: number, /** * Component height */ h: number }) => void } /** * Context type for the root Application: no parent. */ export interface ApplicationBase extends Omit { readonly $parent: undefined } /** * Context type for Components (Blits.Component): always has a parent when mounted. Destroyed case is not modeled. */ export interface ChildComponentBase extends Omit { readonly $parent: ComponentBase } /** * Prop object */ export type PropObject = { /** * Name of the prop */ key: string, /** * Whether the prop is required to be passed */ required?: boolean, /** * Default value for the prop when omited */ default?: any, /** * Cast the value of the prop * * @example * ```js * { * cast: Number, // casts to a number * cast: (v) => v.toUpperCase() // casts to uppercase * } * ``` */ cast?: () => any }; export type Props = Record type InferProp = T extends (...args: any[]) => any ? ReturnType : T type InferProps> = { // Since InferProps is no longer used for `Computed` props, // then we do not need to infer the return type of a function-typed prop in `Props`. [K in keyof T]: T[K] } type Computed any>> = { [K in keyof T]: Readonly> } export type ComponentContext< P extends Record, S, M, C extends Record any> = Record, > = ThisType< Readonly> & S & M & Computed & ChildComponentBase > export type ApplicationContext< P extends Record, S, M, C extends Record any> = Record, > = ThisType> & S & M & Computed & ApplicationBase> export interface ComponentConfig< P extends Props = {}, S, M, C extends Record any> = Record, W, > { components?: { [key: string]: ComponentFactory } /** * XML-based template string of the Component * * @example * ```xml * * *