UNPKG

@tldraw/utils

Version:

tldraw infinite canvas SDK (private utilities).

1,550 lines (1,455 loc) • 62.3 kB
import { default as isEqual } from 'lodash.isequal'; import { default as isEqualWith } from 'lodash.isequalwith'; import { default as throttle } from 'lodash.throttle'; import { default as uniq } from 'lodash.uniq'; /* Excluded from this release type: annotateError */ /* Excluded from this release type: areArraysShallowEqual */ /* Excluded from this release type: areObjectsShallowEqual */ /* Excluded from this release type: assert_2 */ /* Excluded from this release type: assertExists */ /** * Decorator that binds a method to its class instance (legacy stage-2 TypeScript decorators). * When applied to a class method, ensures `this` always refers to the class instance, * even when the method is called as a callback or event handler. * * @param target - The prototype of the class being decorated * @param propertyKey - The name of the method being decorated * @param descriptor - The property descriptor for the method being decorated * @returns The modified property descriptor with bound method access * @example * ```typescript * class MyClass { * name = 'example'; * * @bind * getName() { * return this.name; * } * } * * const instance = new MyClass(); * const callback = instance.getName; * console.log(callback()); // 'example' (this is properly bound) * ``` * @public */ export declare function bind<T extends (...args: any[]) => any>(target: object, propertyKey: string, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T>; /** * Decorator that binds a method to its class instance (TC39 decorators standard). * When applied to a class method, ensures `this` always refers to the class instance, * even when the method is called as a callback or event handler. * * @param originalMethod - The original method being decorated * @param context - The decorator context containing metadata about the method * @example * ```typescript * class EventHandler { * message = 'Hello World'; * * @bind * handleClick() { * console.log(this.message); * } * } * * const handler = new EventHandler(); * document.addEventListener('click', handler.handleClick); // 'this' is properly bound * ``` * @public */ export declare function bind<This extends object, T extends (...args: any[]) => any>(originalMethod: T, context: ClassMethodDecoratorContext<This, T>): void; /* Excluded from this release type: clearLocalStorage */ /* Excluded from this release type: clearSessionStorage */ /* Excluded from this release type: compact */ /** * Create a debounced version of a function that delays execution until after a specified wait time. * * Debouncing ensures that a function is only executed once after a specified delay, * even if called multiple times in rapid succession. Each new call resets the timer. The debounced * function returns a Promise that resolves with the result of the original function. Includes a * cancel method to prevent execution if needed. * * @param callback - The function to debounce (can be sync or async) * @param wait - The delay in milliseconds before executing the function * @returns A debounced function that returns a Promise and includes a cancel method * * @example * ```ts * // Debounce a search function * const searchAPI = (query: string) => fetch(`/search?q=${query}`) * const debouncedSearch = debounce(searchAPI, 300) * * // Multiple rapid calls will only execute the last one after 300ms * debouncedSearch('react').then(result => console.log(result)) * debouncedSearch('react hooks') // This cancels the previous call * debouncedSearch('react typescript') // Only this will execute * * // Cancel pending execution * debouncedSearch.cancel() * * // With async/await * const saveData = debounce(async (data: any) => { * return await api.save(data) * }, 1000) * * const result = await saveData({name: 'John'}) * ``` * * @public * @see source - https://gist.github.com/ca0v/73a31f57b397606c9813472f7493a940 */ export declare function debounce<T extends unknown[], U>(callback: (...args: T) => PromiseLike<U> | U, wait: number): { (...args: T): Promise<U>; cancel(): void; }; /** * Remove duplicate items from an array. * * Creates a new array with duplicate items removed. Uses strict equality by default, * or a custom equality function if provided. Order of first occurrence is preserved. * * @param input - The array to deduplicate * @param equals - Optional custom equality function to compare items (defaults to strict equality) * @returns A new array with duplicate items removed * * @example * ```ts * dedupe([1, 2, 2, 3, 1]) // [1, 2, 3] * dedupe(['a', 'b', 'a', 'c']) // ['a', 'b', 'c'] * * // With custom equality function * const objects = [{id: 1}, {id: 2}, {id: 1}] * dedupe(objects, (a, b) => a.id === b.id) // [{id: 1}, {id: 2}] * ``` * @public */ export declare function dedupe<T>(input: T[], equals?: (a: any, b: any) => boolean): T[]; /** * Array of supported video MIME types. * * @example * ```ts * import { DEFAULT_SUPPORT_VIDEO_TYPES } from '@tldraw/utils' * * const isVideo = DEFAULT_SUPPORT_VIDEO_TYPES.includes('video/mp4') * console.log(isVideo) // true * ``` * @public */ export declare const DEFAULT_SUPPORT_VIDEO_TYPES: readonly ("video/mp4" | "video/quicktime" | "video/webm")[]; /** * Array of all supported image MIME types, combining static, vector, and animated types. * * @example * ```ts * import { DEFAULT_SUPPORTED_IMAGE_TYPES } from '@tldraw/utils' * * const isSupported = DEFAULT_SUPPORTED_IMAGE_TYPES.includes('image/png') * console.log(isSupported) // true * ``` * @public */ export declare const DEFAULT_SUPPORTED_IMAGE_TYPES: readonly ("image/apng" | "image/avif" | "image/gif" | "image/jpeg" | "image/png" | "image/svg+xml" | "image/webp")[]; /** * Comma-separated string of all supported media MIME types, useful for HTML file input accept attributes. * * @example * ```ts * import { DEFAULT_SUPPORTED_MEDIA_TYPE_LIST } from '@tldraw/utils' * * // Use in HTML file input for media uploads * const input = document.createElement('input') * input.type = 'file' * input.accept = DEFAULT_SUPPORTED_MEDIA_TYPE_LIST * input.addEventListener('change', (e) => { * const files = (e.target as HTMLInputElement).files * if (files) console.log(`Selected ${files.length} file(s)`) * }) * ``` * @public */ export declare const DEFAULT_SUPPORTED_MEDIA_TYPE_LIST: string; /** * Array of all supported media MIME types, combining images and videos. * * @example * ```ts * import { DEFAULT_SUPPORTED_MEDIA_TYPES } from '@tldraw/utils' * * const isMediaFile = DEFAULT_SUPPORTED_MEDIA_TYPES.includes('video/mp4') * console.log(isMediaFile) // true * ``` * @public */ export declare const DEFAULT_SUPPORTED_MEDIA_TYPES: readonly ("image/apng" | "image/avif" | "image/gif" | "image/jpeg" | "image/png" | "image/svg+xml" | "image/webp" | "video/mp4" | "video/quicktime" | "video/webm")[]; /* Excluded from this release type: deleteFromLocalStorage */ /* Excluded from this release type: deleteFromSessionStorage */ /** @public */ export declare interface ErrorAnnotations { tags: Record<string, bigint | boolean | null | number | string | symbol | undefined>; extras: Record<string, unknown>; } /** * Represents a failed result containing an error. * * Interface for the error case of a Result type, containing the error information. * Used in conjunction with OkResult to create a discriminated union for error handling. * * @example * ```ts * const failure: ErrorResult<string> = { ok: false, error: 'Something went wrong' } * if (!failure.ok) { * console.error(failure.error) // 'Something went wrong' * } * ``` * @public */ export declare interface ErrorResult<E> { readonly ok: false; readonly error: E; } /* Excluded from this release type: ExecutionQueue */ /* Excluded from this release type: exhaustiveSwitchError */ /** * Expands a type definition to show its full structure in IDE tooltips and error messages. * This utility type forces TypeScript to resolve and display the complete type structure * instead of showing complex conditional types or intersections as-is. * * @example * ```ts * type User = { name: string } * type WithId = { id: string } * type UserWithId = User & WithId * * // Without Expand, IDE shows: User & WithId * // With Expand, IDE shows: { name: string; id: string } * type ExpandedUserWithId = Expand<UserWithId> * * // Useful for complex intersections * type ComplexType = Expand<BaseType & Mixin1 & Mixin2> * ``` * * @public */ export declare type Expand<T> = T extends infer O ? { [K in keyof O]: O[K]; } : never; /* Excluded from this release type: fetch_2 */ /** * Utility class providing helper methods for file and blob operations. * * FileHelpers contains static methods for common file operations including * URL fetching, format conversion, and MIME type manipulation. All methods work with * web APIs like fetch, FileReader, and Blob/File objects. * * @example * ```ts * // Fetch and convert a remote image to data URL * const dataUrl = await FileHelpers.urlToDataUrl('https://example.com/image.png') * * // Convert user-selected file to text * const text = await FileHelpers.blobToText(userFile) * * // Change file MIME type * const newFile = FileHelpers.rewriteMimeType(originalFile, 'application/json') * ``` * * @public */ export declare class FileHelpers { /** * Converts a URL to an ArrayBuffer by fetching the resource. * * Fetches the resource at the given URL and returns its content as an ArrayBuffer. * This is useful for loading binary data like images, videos, or other file types. * * @param url - The URL of the file to fetch * @returns Promise that resolves to the file content as an ArrayBuffer * @example * ```ts * const buffer = await FileHelpers.urlToArrayBuffer('https://example.com/image.png') * console.log(buffer.byteLength) // Size of the file in bytes * ``` * @public */ static urlToArrayBuffer(url: string): Promise<ArrayBuffer>; /** * Converts a URL to a Blob by fetching the resource. * * Fetches the resource at the given URL and returns its content as a Blob object. * Blobs are useful for handling file data in web applications. * * @param url - The URL of the file to fetch * @returns Promise that resolves to the file content as a Blob * @example * ```ts * const blob = await FileHelpers.urlToBlob('https://example.com/document.pdf') * console.log(blob.type) // 'application/pdf' * console.log(blob.size) // Size in bytes * ``` * @public */ static urlToBlob(url: string): Promise<Blob>; /** * Converts a URL to a data URL by fetching the resource. * * Fetches the resource at the given URL and converts it to a base64-encoded data URL. * If the URL is already a data URL, it returns the URL unchanged. This is useful for embedding * resources directly in HTML or CSS. * * @param url - The URL of the file to convert, or an existing data URL * @returns Promise that resolves to a data URL string * @example * ```ts * const dataUrl = await FileHelpers.urlToDataUrl('https://example.com/image.jpg') * // Returns: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA...' * * const existing = await FileHelpers.urlToDataUrl('data:text/plain;base64,SGVsbG8=') * // Returns the same data URL unchanged * ``` * @public */ static urlToDataUrl(url: string): Promise<string>; /** * Convert a Blob to a base64 encoded data URL. * * Converts a Blob object to a base64-encoded data URL using the FileReader API. * This is useful for displaying images or embedding file content directly in HTML. * * @param file - The Blob object to convert * @returns Promise that resolves to a base64-encoded data URL string * @example * ```ts * const blob = new Blob(['Hello World'], { type: 'text/plain' }) * const dataUrl = await FileHelpers.blobToDataUrl(blob) * // Returns: 'data:text/plain;base64,SGVsbG8gV29ybGQ=' * * // With an image file * const imageDataUrl = await FileHelpers.blobToDataUrl(myImageFile) * // Can be used directly in img src attribute * ``` * @public */ static blobToDataUrl(file: Blob): Promise<string>; /** * Convert a Blob to a unicode text string. * * Reads the content of a Blob object as a UTF-8 text string using the FileReader API. * This is useful for reading text files or extracting text content from blobs. * * @param file - The Blob object to convert to text * @returns Promise that resolves to the text content as a string * @example * ```ts * const textBlob = new Blob(['Hello World'], { type: 'text/plain' }) * const text = await FileHelpers.blobToText(textBlob) * console.log(text) // 'Hello World' * * // With a text file from user input * const content = await FileHelpers.blobToText(myTextFile) * console.log(content) // File content as string * ``` * @public */ static blobToText(file: Blob): Promise<string>; /** * Creates a new Blob or File with a different MIME type. * * Creates a copy of the given Blob or File with a new MIME type while preserving * all other properties. If the current MIME type already matches the new one, returns the * original object unchanged. For File objects, preserves the filename. * * @param blob - The Blob or File object to modify * @param newMimeType - The new MIME type to assign * @returns A new Blob or File with the updated MIME type * @example * ```ts * // Change a generic blob to a specific image type * const blob = new Blob([imageData]) * const imageBlob = FileHelpers.rewriteMimeType(blob, 'image/png') * * // Change a file's MIME type while preserving filename * const file = new File([data], 'document.txt', { type: 'text/plain' }) * const jsonFile = FileHelpers.rewriteMimeType(file, 'application/json') * console.log(jsonFile.name) // 'document.txt' (preserved) * console.log(jsonFile.type) // 'application/json' (updated) * ``` * @public */ static rewriteMimeType(blob: Blob, newMimeType: string): Blob; static rewriteMimeType(blob: File, newMimeType: string): File; } /* Excluded from this release type: filterEntries */ /* Excluded from this release type: fpsThrottle */ /* Excluded from this release type: getChangedKeys */ /* Excluded from this release type: getErrorAnnotations */ /** * Get the first item from an iterable Set or Map. * * @param value - The iterable Set or Map to get the first item from * @returns The first value from the Set or Map * @example * ```ts * const A = getFirstFromIterable(new Set([1, 2, 3])) // 1 * const B = getFirstFromIterable( * new Map([ * ['a', 1], * ['b', 2], * ]) * ) // 1 * ``` * @public */ export declare function getFirstFromIterable<T = unknown>(set: Map<any, T> | Set<T>): T; /* Excluded from this release type: getFromLocalStorage */ /* Excluded from this release type: getFromSessionStorage */ /** * Hash an ArrayBuffer using the FNV-1a algorithm. * * Generates a deterministic hash value for binary data stored in an ArrayBuffer. * Processes the buffer byte by byte using the same hashing algorithm as getHashForString. * Useful for creating consistent identifiers for binary data like images or files. * * @param buffer - The ArrayBuffer containing binary data to hash * @returns A string representation of the 32-bit hash value * @example * ```ts * // Hash some binary data * const data = new Uint8Array([1, 2, 3, 4, 5]) * const hash = getHashForBuffer(data.buffer) * console.log(hash) // '123456789' * * // Hash image file data * const fileBuffer = await file.arrayBuffer() * const fileHash = getHashForBuffer(fileBuffer) * console.log(fileHash) // Unique hash for the file * ``` * @public */ export declare function getHashForBuffer(buffer: ArrayBuffer): string; /** * Hash an object by converting it to JSON and then hashing the resulting string. * * Converts the object to a JSON string using JSON.stringify and then applies the same * hashing algorithm as getHashForString. Useful for creating consistent hash values * for objects, though the hash depends on JSON serialization order. * * @param obj - The object to hash (any JSON-serializable value) * @returns A string representation of the 32-bit hash value * @example * ```ts * const hash1 = getHashForObject({ name: 'John', age: 30 }) * const hash2 = getHashForObject({ name: 'John', age: 30 }) * console.log(hash1 === hash2) // true * * // Arrays work too * const arrayHash = getHashForObject([1, 2, 3, 'hello']) * console.log(arrayHash) // '-123456789' * ``` * @public */ export declare function getHashForObject(obj: any): string; /** * Hash a string using the FNV-1a algorithm. * * Generates a deterministic hash value for a given string using a variant of the FNV-1a * (Fowler-Noll-Vo) algorithm. The hash is returned as a string representation of a 32-bit integer. * * @param string - The input string to hash * @returns A string representation of the 32-bit hash value * @example * ```ts * const hash = getHashForString('hello world') * console.log(hash) // '-862545276' * * // Same input always produces same hash * const hash2 = getHashForString('hello world') * console.log(hash === hash2) // true * ``` * @public */ export declare function getHashForString(string: string): string; /** * Get the index above a given index. * @param below - The index below. * @returns An IndexKey value above the given index. * @example * ```ts * const index = getIndexAbove('a0' as IndexKey) * console.log(index) // 'a1' * ``` * @public */ export declare function getIndexAbove(below?: IndexKey | null | undefined): IndexKey; /** * Get the index below a given index. * @param above - The index above. * @returns An IndexKey value below the given index. * @example * ```ts * const index = getIndexBelow('a2' as IndexKey) * console.log(index) // 'a1' * ``` * @public */ export declare function getIndexBelow(above?: IndexKey | null | undefined): IndexKey; /** * Get the index between two indices. * @param below - The index below. * @param above - The index above. * @returns A single IndexKey value between below and above. * @example * ```ts * const index = getIndexBetween('a0' as IndexKey, 'a2' as IndexKey) * console.log(index) // 'a1' * ``` * @public */ export declare function getIndexBetween(below: IndexKey | null | undefined, above: IndexKey | null | undefined): IndexKey; /** * Get n number of indices, starting at an index. * @param n - The number of indices to get. * @param start - The index to start at. * @returns An array containing the start index plus n additional IndexKey values. * @example * ```ts * const indices = getIndices(3, 'a1' as IndexKey) * console.log(indices) // ['a1', 'a2', 'a3', 'a4'] * ``` * @public */ export declare function getIndices(n: number, start?: IndexKey): IndexKey[]; /** * Get a number of indices above an index. * @param below - The index below. * @param n - The number of indices to get. * @returns An array of n IndexKey values above the given index. * @example * ```ts * const indices = getIndicesAbove('a0' as IndexKey, 3) * console.log(indices) // ['a1', 'a2', 'a3'] * ``` * @public */ export declare function getIndicesAbove(below: IndexKey | null | undefined, n: number): IndexKey[]; /** * Get a number of indices below an index. * @param above - The index above. * @param n - The number of indices to get. * @returns An array of n IndexKey values below the given index. * @example * ```ts * const indices = getIndicesBelow('a2' as IndexKey, 2) * console.log(indices) // ['a1', 'a0V'] * ``` * @public */ export declare function getIndicesBelow(above: IndexKey | null | undefined, n: number): IndexKey[]; /** * Get a number of indices between two indices. * @param below - The index below. * @param above - The index above. * @param n - The number of indices to get. * @returns An array of n IndexKey values between below and above. * @example * ```ts * const indices = getIndicesBetween('a0' as IndexKey, 'a2' as IndexKey, 2) * console.log(indices) // ['a0V', 'a1'] * ``` * @public */ export declare function getIndicesBetween(below: IndexKey | null | undefined, above: IndexKey | null | undefined, n: number): IndexKey[]; /* Excluded from this release type: getOwnProperty */ /* Excluded from this release type: groupBy */ /* Excluded from this release type: hasOwnProperty */ /* Excluded from this release type: Image_2 */ /** * A string made up of an integer part followed by a fraction part. The fraction point consists of * zero or more digits with no trailing zeros. Based on * {@link https://observablehq.com/@dgreensp/implementing-fractional-indexing}. * * @public */ export declare type IndexKey = string & { __brand: 'indexKey'; }; /** * Inverse lerp between two values. Given a value `t` in the range [a, b], returns a number between * 0 and 1. * * @param a - The start value of the range * @param b - The end value of the range * @param t - The value within the range [a, b] * @returns The normalized position (0-1) of t within the range [a, b] * @example * ```ts * const position = invLerp(0, 100, 25) // 0.25 * const normalized = invLerp(10, 20, 15) // 0.5 * ``` * @public */ export declare function invLerp(a: number, b: number, t: number): number; /** * Get whether a value is not undefined. * * @param value - The value to check. * @returns True if the value is not undefined, with proper type narrowing. * @example * ```ts * const maybeString: string | undefined = getValue() * * if (isDefined(maybeString)) { * // TypeScript knows maybeString is string, not undefined * console.log(maybeString.toUpperCase()) * } * * // Filter undefined values from arrays * const values = [1, undefined, 2, undefined, 3] * const definedValues = values.filter(isDefined) // [1, 2, 3] * ``` * @public */ export declare function isDefined<T>(value: T): value is typeof value extends undefined ? never : T; export { isEqual } /* Excluded from this release type: isEqualAllowingForFloatingPointErrors */ export { isEqualWith } /* Excluded from this release type: isNativeStructuredClone */ /** * Get whether a value is not null. * * @param value - The value to check. * @returns True if the value is not null, with proper type narrowing. * @example * ```ts * const maybeString: string | null = getValue() * * if (isNonNull(maybeString)) { * // TypeScript knows maybeString is string, not null * console.log(maybeString.length) * } * * // Filter null values from arrays * const values = ["a", null, "b", null, "c"] * const nonNullValues = values.filter(isNonNull) // ["a", "b", "c"] * ``` * @public */ export declare function isNonNull<T>(value: T): value is typeof value extends null ? never : T; /** * Get whether a value is not nullish (not null and not undefined). * * @param value - The value to check. * @returns True if the value is neither null nor undefined, with proper type narrowing. * @example * ```ts * const maybeString: string | null | undefined = getValue() * * if (isNonNullish(maybeString)) { * // TypeScript knows maybeString is string, not null or undefined * console.log(maybeString.charAt(0)) * } * * // Filter nullish values from arrays * const values = ["hello", null, "world", undefined, "!"] * const cleanValues = values.filter(isNonNullish) // ["hello", "world", "!"] * ``` * @public */ export declare function isNonNullish<T>(value: T): value is typeof value extends undefined ? never : typeof value extends null ? never : T; /** * A type representing a JSON array containing any valid JSON values. * Arrays can contain mixed types of JSON values including nested arrays and objects. * * @example * ```ts * const jsonArray: JsonArray = [ * "text", * 123, * true, * { nested: "object" }, * [1, 2, 3] * ] * ``` * * @public */ export declare type JsonArray = JsonValue[]; /** * A type representing a JSON object with string keys and JSON values. * Object values can be undefined to handle optional properties safely. * * @example * ```ts * const jsonObject: JsonObject = { * required: "value", * optional: undefined, * nested: { * deep: "property" * }, * array: [1, 2, 3] * } * ``` * * @public */ export declare interface JsonObject { [key: string]: JsonValue | undefined; } /** * A type representing JSON primitive values: boolean, null, string, or number. * These are the atomic values that can appear in JSON data. * * @example * ```ts * const primitives: JsonPrimitive[] = [ * true, * null, * "hello", * 42 * ] * ``` * * @public */ export declare type JsonPrimitive = boolean | null | number | string; /** * A type that represents any valid JSON value. This includes primitives (boolean, null, string, number), * arrays of JSON values, and objects with string keys and JSON values. * * @example * ```ts * const jsonData: JsonValue = { * name: "Alice", * age: 30, * active: true, * tags: ["user", "premium"], * metadata: null * } * ``` * * @public */ export declare type JsonValue = JsonArray | JsonObject | JsonPrimitive; /* Excluded from this release type: last */ /** * Linear interpolate between two values. * * @param a - The start value * @param b - The end value * @param t - The interpolation factor (0-1) * @returns The interpolated value * @example * ```ts * const halfway = lerp(0, 100, 0.5) // 50 * const quarter = lerp(10, 20, 0.25) // 12.5 * ``` * @public */ export declare function lerp(a: number, b: number, t: number): number; /** * Applies a string transformation algorithm that rearranges and modifies characters. * * Performs a series of character manipulations on the input string including * character repositioning through splicing operations and numeric character transformations. * This appears to be a custom encoding/obfuscation function. * * @param str - The input string to transform * @returns The transformed string after applying all manipulations * @example * ```ts * const result = lns('hello123') * console.log(result) // Transformed string (exact output depends on algorithm) * * // Can be used for simple string obfuscation * const obfuscated = lns('sensitive-data') * console.log(obfuscated) // Obfuscated version * ``` * @public */ export declare function lns(str: string): string; /** * Automatically makes properties optional if their type includes `undefined`. * This transforms properties like `prop: string | undefined` to `prop?: string | undefined`, * making the API more ergonomic by not requiring explicit undefined assignments. * * @example * ```ts * interface RawConfig { * name: string * theme: string | undefined * debug: boolean | undefined * version: number * } * * type Config = MakeUndefinedOptional<RawConfig> * // Result: { * // name: string * // theme?: string | undefined // now optional * // debug?: boolean | undefined // now optional * // version: number * // } * * const config: Config = { * name: 'MyApp', * version: 1 * // theme and debug can be omitted instead of explicitly set to undefined * } * ``` * * @public */ export declare type MakeUndefinedOptional<T extends object> = Expand<{ [P in { [K in keyof T]: undefined extends T[K] ? never : K; }[keyof T]]: T[P]; } & { [P in { [K in keyof T]: undefined extends T[K] ? K : never; }[keyof T]]?: T[P]; }>; /* Excluded from this release type: mapObjectMapValues */ /* Excluded from this release type: maxBy */ /* Excluded from this release type: measureAverageDuration */ /* Excluded from this release type: measureCbDuration */ /* Excluded from this release type: measureDuration */ /** * Helpers for media * * @public */ export declare class MediaHelpers { /** * Load a video element from a URL with cross-origin support. * * @param src - The URL of the video to load * @returns Promise that resolves to the loaded HTMLVideoElement * @example * ```ts * const video = await MediaHelpers.loadVideo('https://example.com/video.mp4') * console.log(`Video dimensions: ${video.videoWidth}x${video.videoHeight}`) * ``` * @public */ static loadVideo(src: string): Promise<HTMLVideoElement>; /** * Extract a frame from a video element as a data URL. * * @param video - The HTMLVideoElement to extract frame from * @param time - The time in seconds to extract the frame from (default: 0) * @returns Promise that resolves to a data URL of the video frame * @example * ```ts * const video = await MediaHelpers.loadVideo('https://example.com/video.mp4') * const frameDataUrl = await MediaHelpers.getVideoFrameAsDataUrl(video, 5.0) * // Use frameDataUrl as image thumbnail * const img = document.createElement('img') * img.src = frameDataUrl * ``` * @public */ static getVideoFrameAsDataUrl(video: HTMLVideoElement, time?: number): Promise<string>; /** * Load an image from a URL and get its dimensions along with the image element. * * @param src - The URL of the image to load * @returns Promise that resolves to an object with width, height, and the image element * @example * ```ts * const { w, h, image } = await MediaHelpers.getImageAndDimensions('https://example.com/image.png') * console.log(`Image size: ${w}x${h}`) * // Image is ready to use * document.body.appendChild(image) * ``` * @public */ static getImageAndDimensions(src: string): Promise<{ h: number; image: HTMLImageElement; w: number; }>; /** * Get the size of a video blob * * @param blob - A Blob containing the video * @returns Promise that resolves to an object with width and height properties * @example * ```ts * const file = new File([...], 'video.mp4', { type: 'video/mp4' }) * const { w, h } = await MediaHelpers.getVideoSize(file) * console.log(`Video dimensions: ${w}x${h}`) * ``` * @public */ static getVideoSize(blob: Blob): Promise<{ h: number; w: number; }>; /** * Get the size of an image blob * * @param blob - A Blob containing the image * @returns Promise that resolves to an object with width and height properties * @example * ```ts * const file = new File([...], 'image.png', { type: 'image/png' }) * const { w, h } = await MediaHelpers.getImageSize(file) * console.log(`Image dimensions: ${w}x${h}`) * ``` * @public */ static getImageSize(blob: Blob): Promise<{ h: number; w: number; }>; /** * Check if a media file blob contains animation data. * * @param file - The Blob to check for animation * @returns Promise that resolves to true if the file is animated, false otherwise * @example * ```ts * const file = new File([...], 'animation.gif', { type: 'image/gif' }) * const animated = await MediaHelpers.isAnimated(file) * console.log(animated ? 'Animated' : 'Static') * ``` * @public */ static isAnimated(file: Blob): Promise<boolean>; /** * Check if a MIME type represents an animated image format. * * @param mimeType - The MIME type to check * @returns True if the MIME type is an animated image format, false otherwise * @example * ```ts * const isAnimated = MediaHelpers.isAnimatedImageType('image/gif') * console.log(isAnimated) // true * ``` * @public */ static isAnimatedImageType(mimeType: null | string): boolean; /** * Check if a MIME type represents a static (non-animated) image format. * * @param mimeType - The MIME type to check * @returns True if the MIME type is a static image format, false otherwise * @example * ```ts * const isStatic = MediaHelpers.isStaticImageType('image/jpeg') * console.log(isStatic) // true * ``` * @public */ static isStaticImageType(mimeType: null | string): boolean; /** * Check if a MIME type represents a vector image format. * * @param mimeType - The MIME type to check * @returns True if the MIME type is a vector image format, false otherwise * @example * ```ts * const isVector = MediaHelpers.isVectorImageType('image/svg+xml') * console.log(isVector) // true * ``` * @public */ static isVectorImageType(mimeType: null | string): boolean; /** * Check if a MIME type represents any supported image format (static, animated, or vector). * * @param mimeType - The MIME type to check * @returns True if the MIME type is a supported image format, false otherwise * @example * ```ts * const isImage = MediaHelpers.isImageType('image/png') * console.log(isImage) // true * ``` * @public */ static isImageType(mimeType: string): boolean; /** * Utility function to create an object URL from a blob, execute a function with it, and automatically clean it up. * * @param blob - The Blob to create an object URL for * @param fn - Function to execute with the object URL * @returns Promise that resolves to the result of the function * @example * ```ts * const result = await MediaHelpers.usingObjectURL(imageBlob, async (url) => { * const { w, h } = await MediaHelpers.getImageAndDimensions(url) * return { width: w, height: h } * }) * // Object URL is automatically revoked after function completes * console.log(`Image dimensions: ${result.width}x${result.height}`) * ``` * @public */ static usingObjectURL<T>(blob: Blob, fn: (url: string) => Promise<T>): Promise<T>; } /* Excluded from this release type: mergeArraysAndReplaceDefaults */ /* Excluded from this release type: minBy */ /* Excluded from this release type: mockUniqueId */ /** * Modulate a value between two ranges. * * @example * * ```ts * const A = modulate(0, [0, 1], [0, 100]) * ``` * * @param value - The interpolation value. * @param rangeA - From [low, high] * @param rangeB - To [low, high] * @param clamp - Whether to clamp the the result to [low, high] * @public */ export declare function modulate(value: number, rangeA: number[], rangeB: number[], clamp?: boolean): number; /* Excluded from this release type: noop */ /* Excluded from this release type: objectMapEntries */ /* Excluded from this release type: objectMapEntriesIterable */ /* Excluded from this release type: objectMapFromEntries */ /* Excluded from this release type: objectMapKeys */ /* Excluded from this release type: objectMapValues */ /** * Represents a successful result containing a value. * * Interface for the success case of a Result type, containing the computed value. * Used in conjunction with ErrorResult to create a discriminated union for error handling. * * @example * ```ts * const success: OkResult<string> = { ok: true, value: 'Hello World' } * if (success.ok) { * console.log(success.value) // 'Hello World' * } * ``` * @public */ export declare interface OkResult<T> { readonly ok: true; readonly value: T; } /* Excluded from this release type: omit */ /* Excluded from this release type: omitFromStackTrace */ /* Excluded from this release type: partition */ /** * A utility class for measuring and tracking frame rate performance during operations. * Provides visual feedback in the browser console with color-coded FPS indicators. * * @example * ```ts * const tracker = new PerformanceTracker() * * tracker.start('render') * renderShapes() * tracker.stop() // Logs performance info to console * * // Check if tracking is active * if (tracker.isStarted()) { * console.log('Still tracking performance') * } * ``` * * @public */ export declare class PerformanceTracker { private startTime; private name; private frames; private started; private frame; /** * Records animation frames to calculate frame rate. * Called automatically during performance tracking. */ recordFrame: () => void; /** * Starts performance tracking for a named operation. * * @param name - A descriptive name for the operation being tracked * * @example * ```ts * tracker.start('canvas-render') * // ... perform rendering operations * tracker.stop() * ``` */ start(name: string): void; /** * Stops performance tracking and logs results to the console. * * Displays the operation name, frame rate, and uses color coding: * - Green background: \> 55 FPS (good performance) * - Yellow background: 30-55 FPS (moderate performance) * - Red background: \< 30 FPS (poor performance) * * @example * ```ts * tracker.start('interaction') * handleUserInteraction() * tracker.stop() // Logs: "Perf Interaction 60 fps" * ``` */ stop(): void; /** * Checks whether performance tracking is currently active. * * @returns True if tracking is in progress, false otherwise * * @example * ```ts * if (!tracker.isStarted()) { * tracker.start('new-operation') * } * ``` */ isStarted(): boolean; } /** * Utility class for reading and manipulating PNG image files. * Provides methods for parsing PNG chunks, validating PNG format, and modifying PNG metadata. * * @example * ```ts * // Validate PNG file from blob * const blob = new Blob([pngData], { type: 'image/png' }) * const view = new DataView(await blob.arrayBuffer()) * const isPng = PngHelpers.isPng(view, 0) * * // Parse PNG metadata for image processing * const chunks = PngHelpers.readChunks(view) * const physChunk = PngHelpers.findChunk(view, 'pHYs') * * // Create high-DPI PNG for export * const highDpiBlob = PngHelpers.setPhysChunk(view, 2, { type: 'image/png' }) * ``` * * @public */ export declare class PngHelpers { /** * Checks if binary data at the specified offset contains a valid PNG file signature. * Validates the 8-byte PNG signature: 89 50 4E 47 0D 0A 1A 0A. * * @param view - DataView containing the binary data to check * @param offset - Byte offset where the PNG signature should start * @returns True if the data contains a valid PNG signature, false otherwise * * @example * ```ts * // Validate PNG from file upload * const file = event.target.files[0] * const buffer = await file.arrayBuffer() * const view = new DataView(buffer) * * if (PngHelpers.isPng(view, 0)) { * console.log('Valid PNG file detected') * // Process PNG file... * } else { * console.error('Not a valid PNG file') * } * ``` */ static isPng(view: DataView, offset: number): boolean; /** * Reads the 4-character chunk type identifier from a PNG chunk header. * * @param view - DataView containing the PNG data * @param offset - Byte offset of the chunk type field (after length field) * @returns 4-character string representing the chunk type (e.g., 'IHDR', 'IDAT', 'IEND') * * @example * ```ts * // Read chunk type from PNG header (after 8-byte signature) * const chunkType = PngHelpers.getChunkType(dataView, 8) * console.log(chunkType) // 'IHDR' (Image Header) * * // Read chunk type at a specific position during parsing * let offset = 8 // Skip PNG signature * const chunkLength = dataView.getUint32(offset) * const type = PngHelpers.getChunkType(dataView, offset + 4) * ``` */ static getChunkType(view: DataView, offset: number): string; /** * Parses all chunks in a PNG file and returns their metadata. * Skips duplicate IDAT chunks but includes all other chunk types. * * @param view - DataView containing the complete PNG file data * @param offset - Starting byte offset (defaults to 0) * @returns Record mapping chunk types to their metadata (start position, data offset, and size) * @throws Error if the data is not a valid PNG file * * @example * ```ts * // Parse PNG structure for metadata extraction * const view = new DataView(await blob.arrayBuffer()) * const chunks = PngHelpers.readChunks(view) * * // Check for specific chunks * const ihdrChunk = chunks['IHDR'] * const physChunk = chunks['pHYs'] * * if (physChunk) { * console.log(`Found pixel density info at byte ${physChunk.start}`) * } else { * console.log('No pixel density information found') * } * ``` */ static readChunks(view: DataView, offset?: number): Record<string, { dataOffset: number; size: number; start: number; }>; /** * Parses the pHYs (physical pixel dimensions) chunk data. * Reads pixels per unit for X and Y axes, and the unit specifier. * * @param view - DataView containing the PNG data * @param offset - Byte offset of the pHYs chunk data * @returns Object with ppux (pixels per unit X), ppuy (pixels per unit Y), and unit specifier * * @example * ```ts * // Extract pixel density information for DPI calculation * const physChunk = PngHelpers.findChunk(dataView, 'pHYs') * if (physChunk) { * const physData = PngHelpers.parsePhys(dataView, physChunk.dataOffset) * * if (physData.unit === 1) { // meters * const dpiX = Math.round(physData.ppux * 0.0254) * const dpiY = Math.round(physData.ppuy * 0.0254) * console.log(`DPI: ${dpiX} x ${dpiY}`) * } * } * ``` */ static parsePhys(view: DataView, offset: number): { ppux: number; ppuy: number; unit: number; }; /** * Finds a specific chunk type in the PNG file and returns its metadata. * * @param view - DataView containing the PNG file data * @param type - 4-character chunk type to search for (e.g., 'pHYs', 'IDAT') * @returns Chunk metadata object if found, undefined otherwise * * @example * ```ts * // Look for pixel density information in PNG * const physChunk = PngHelpers.findChunk(dataView, 'pHYs') * if (physChunk) { * const physData = PngHelpers.parsePhys(dataView, physChunk.dataOffset) * console.log(`Found pHYs chunk with ${physData.ppux} x ${physData.ppuy} pixels per unit`) * } * * // Check for text metadata * const textChunk = PngHelpers.findChunk(dataView, 'tEXt') * if (textChunk) { * console.log(`Found text metadata at byte ${textChunk.start}`) * } * ``` */ static findChunk(view: DataView, type: string): { dataOffset: number; size: number; start: number; }; /** * Adds or replaces a pHYs chunk in a PNG file to set pixel density for high-DPI displays. * The method determines insertion point by prioritizing IDAT chunk position over existing pHYs, * creates a properly formatted pHYs chunk with CRC validation, and returns a new Blob. * * @param view - DataView containing the original PNG file data * @param dpr - Device pixel ratio multiplier (defaults to 1) * @param options - Optional Blob constructor options for MIME type and other properties * @returns New Blob containing the PNG with updated pixel density information * * @example * ```ts * // Export PNG with proper pixel density for high-DPI displays * const canvas = document.createElement('canvas') * const ctx = canvas.getContext('2d') * // ... draw content to canvas ... * * canvas.toBlob(async (blob) => { * if (blob) { * const view = new DataView(await blob.arrayBuffer()) * // Create 2x DPI version for Retina displays * const highDpiBlob = PngHelpers.setPhysChunk(view, 2, { type: 'image/png' }) * // Download or use the blob... * } * }, 'image/png') * ``` */ static setPhysChunk(view: DataView, dpr?: number, options?: BlobPropertyBag): Blob; } /* Excluded from this release type: promiseWithResolve */ /** * Makes all properties in a type and all nested properties optional recursively. * This is useful for creating partial update objects where you only want to specify * some deeply nested properties while leaving others unchanged. * * @example * ```ts * interface User { * name: string * settings: { * theme: string * notifications: { * email: boolean * push: boolean * } * } * } * * type PartialUser = RecursivePartial<User> * // Result: { * // name?: string * // settings?: { * // theme?: string * // notifications?: { * // email?: boolean * // push?: boolean * // } * // } * // } * * const update: PartialUser = { * settings: { * notifications: { * email: false * } * } * } * ``` * * @public */ export declare type RecursivePartial<T> = { [P in keyof T]?: RecursivePartial<T[P]>; }; /* Excluded from this release type: registerTldrawLibraryVersion */ /* Excluded from this release type: Required_2 */ /* Excluded from this release type: restoreUniqueId */ /** * A discriminated union type for handling success and error cases. * * Represents either a successful result with a value or a failed result with an error. * This pattern provides type-safe error handling without throwing exceptions. The 'ok' property * serves as the discriminant for type narrowing. * * @example * ```ts * function divide(a: number, b: number): Result<number, string> { * if (b === 0) { * return Result.err('Division by zero') * } * return Result.ok(a / b) * } * * const result = divide(10, 2) * if (result.ok) { * console.log(`Result: ${result.value}`) // Result: 5 * } else { * console.error(`Error: ${result.error}`) * } * ``` * @public */ export declare type Result<T, E> = ErrorResult<E> | OkResult<T>; /** * Utility object for creating Result instances. * * Provides factory methods for creating OkResult and ErrorResult instances. * This is the preferred way to construct Result values for consistent structure. * * @example * ```ts * // Create success result * const success = Result.ok(42) * // success: OkResult<number> = { ok: true, value: 42 } * * // Create error result * const failure = Result.err('Invalid input') * // failure: ErrorResult<string> = { ok: false, error: 'Invalid input' } * ``` * @public */ export declare const Result: { /** * Create a failed result containing an error. * * @param error - The error value to wrap * @returns An ErrorResult containing the error */ err<E>(error: E): ErrorResult<E>; /** * Create a successful result containing a value. * * @param value - The success value to wrap * @returns An OkResult containing the value */ ok<T>(value: T): OkResult<T>; }; /* Excluded from this release type: retry */ /** * Seeded random number generator, using [xorshift](https://en.wikipedia.org/wiki/Xorshift). The * result will always be between -1 and 1. * * Adapted from [seedrandom](https://github.com/davidbau/seedrandom). * * @param seed - The seed string for deterministic random generation (defaults to empty string) * @returns A function that will return a random number between -1 and 1 each time it is called * @example * ```ts * const random = rng('my-seed') * const num1 = random() // Always the same for this seed * const num2 = random() // Next number in sequence * * // Different seed produces different sequence * const otherRandom = rng('other-seed') * const different = otherRandom() // Different value * ``` * @public */ export declare function rng(seed?: string): () => number; /** * Rotate the contents of an array by a specified offset. * * Creates a new array with elements shifted to the left by the specified number of positions. * Both positive and negative offsets result in left shifts (elements move left, with elements * from the front wrapping to the back). * * @param arr - The array to rotate * @param offset - The number of positions to shift left (both positive and negative values shift left) * @returns A new array with elements shifted left by the specified offset * * @example * ```ts * rotateArray([1, 2, 3, 4], 1) // [2, 3, 4, 1] * rotateArray([1, 2, 3, 4], -1) // [2, 3, 4, 1] * rotateArray(['a', 'b', 'c'], 2) // ['c', 'a', 'b'] * ``` * @public */ export declare function rotateArray<T>(arr: T[], offset: number): T[]; /** * Safely parses a URL string without throwing exceptions on invalid input. * Returns a URL object for valid URLs or undefined for invalid ones. * * @param url - The URL string to parse * @param baseUrl - Optional base URL to resolve relative URLs against * @returns A URL object if parsing succeeds, undefined if it fails * * @example * ```ts * // Valid absolute URL * const url1 = safeParseUrl('https://example.com') * if (url1) { * console.log(`Valid URL: ${url1.href}`) // "Valid URL: