diff --git a/package-lock.json b/package-lock.json index 50dc020f..3c705d1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,8 @@ "eslint-plugin-prettier": "^3.4.0", "less": "^4.1.1", "prettier": "3.2.5", - "rollup": "^4.43.0" + "rollup": "^4.43.0", + "rollup-plugin-copy": "^3.5.0" } }, "node_modules/@ampproject/remapping": { @@ -2401,6 +2402,27 @@ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true }, + "node_modules/@types/fs-extra": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -2413,6 +2435,23 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.8.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.8.1.tgz", + "integrity": "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.14.0" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -3030,6 +3069,13 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true, + "license": "MIT" + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -3934,6 +3980,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -4187,8 +4248,7 @@ "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true, - "optional": true + "dev": true }, "node_modules/has": { "version": "1.0.3", @@ -4520,6 +4580,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-reference": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", @@ -4700,6 +4770,16 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/less": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", @@ -5526,6 +5606,75 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-copy": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.5.0.tgz", + "integrity": "sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/rollup-plugin-copy/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup-plugin-copy/node_modules/globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-copy/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6172,6 +6321,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "dev": true, + "license": "MIT" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -6212,6 +6368,16 @@ "node": ">=4" } }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", diff --git a/package.json b/package.json index 78b7403f..290df127 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "eslint-plugin-prettier": "^3.4.0", "less": "^4.1.1", "prettier": "3.2.5", - "rollup": "^4.43.0" + "rollup": "^4.43.0", + "rollup-plugin-copy": "^3.5.0" } } diff --git a/rollup.config.mjs b/rollup.config.mjs index f2302cbc..4d2309e3 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -3,6 +3,7 @@ import resolve from "@rollup/plugin-node-resolve"; import rust from "@wasm-tool/rollup-plugin-rust"; import commonjs from '@rollup/plugin-commonjs'; import terser from "@rollup/plugin-terser"; +import copy from "rollup-plugin-copy"; const esmPlugins = [ babel({ @@ -15,7 +16,12 @@ const esmPlugins = [ inlineWasm: true, wasmOptArgs: ["-O4", "--enable-bulk-memory", "--enable-sign-ext"] }), - resolve({ extensions: [".js", ".jsx"] }) + resolve({ extensions: [".js", ".jsx"] }), + copy({ + targets:[ + { src: "src/*.d.ts", dest: "dist/" } + ] + }) ]; const cjsPlugins = [ diff --git a/src/index.d.ts b/src/index.d.ts new file mode 100644 index 00000000..d502c2ae --- /dev/null +++ b/src/index.d.ts @@ -0,0 +1,548 @@ +/** + * @param src the url for fetching the asciicast recording + * @param element the container element to mount the player into + * @param options player options + * @returns the created AsciinemaPlayer instance + */ +export function create( + src: RequestInfo | URL, + element: HTMLElement, + options?: options, +): AsciinemaPlayer; +/** + * @param data the object for fetching data and parsing it to a asciicast recording + * @param element the container element to mount the player into + * @param options player options + * @returns the created AsciinemaPlayer instance + */ +export function create( + data: { + url: RequestInfo | URL; + fetchOpts?: RequestInit; + parser?: "asciicast" | "ttyrec" | "typescript" | ((response: Response) => recording); + }, + element: HTMLElement, + options?: options, +): AsciinemaPlayer; +/** + * @param data the object for providing asciicast recording, either directly or via a function + * @param element the container element to mount the player into + * @param options player options + * @returns the created AsciinemaPlayer instance + */ +export function create( + data: { + data: asciicastProvider; + }, + element: HTMLElement, + options?: options, +): AsciinemaPlayer; + +export type recording = { + cols: number; + rows: number; + events: Array<[number, "o" | "i" | "m" | "r", string]>; +}; + +export type asciicastV1 = { + version: 1; + width: number; + height: number; + duration: number; + command?: string; + title?: string; + env: Record; + stdout: Array<[number, string]>; +}; +type theme = { + fg: string; + bg: string; + palette: string[]; +}; +export type asciicastV2 = [ + { + version: 2; + width: number; + height: number; + timestamp?: number; + duration?: number; + idle_time_limit?: number; + command?: string; + title?: string; + env: Record; + theme?: theme; + }, + ...Array<[number, "o" | "i" | "m" | "r", string]>, +]; +export type asciicastV3 = [ + { + version: 3; + term: { + cols: number; + rows: number; + type: string; + version?: string; + theme?: theme; + }; + timestamp?: number; + duration?: number; + idle_time_limit?: number; + command?: string; + title?: string; + env?: Record; + tags?: string[]; + }, + ...Array<[number, "o" | "i" | "m" | "r" | "x", string]>, +]; + +export type asciicast = asciicastV1 | asciicastV2 | asciicastV3 | string; +export type asciicastProvider = asciicast | (() => Promise) | (() => asciicast); + +/** + * Look and feel of the asciinema player can be configured extensively by passing additional options + * when mounting the player on the page. + * + * @example + * AsciinemaPlayer.create('/demo.cast', document.getElementById('demo'), { + * speed: 2, + * idleTimeLimit: 2, + * }); + * + */ +export interface options { + /** + * Width of player's terminal in columns. + * + * When not set it defaults to **80** (until asciicast gets loaded) + * and to terminal width saved in the asciicast file (after it gets loaded). + * + * It's recommended to set it to the same value as in asciicast file to avoid player + * resizing itself from 80x24 to actual dimensions of the recording when it gets loaded. + * + * @default 80 (until load) / asciicast width (after load) + */ + cols?: number; + + /** + * Height of player's terminal in rows (lines). + * + * When not set it defaults to **24** (until asciicast gets loaded) + * and to terminal height saved in the asciicast file (after it gets loaded). + * + * Same recommendation as for `cols` applies here. + * + * @default 24 (until load) / asciicast height (after load) + */ + rows?: number; + + /** + * Set this option to true if the playback should start automatically. + * + * @default false + */ + autoPlay?: boolean; + + /** + * Set this option to true if the recording should be preloaded on player's initialization. + * + * Tip: Check *Loading a recording* for available options of getting a recording into the player + * in the most suitable way. + * + * @default false + */ + preload?: boolean; + + /** + * Set this option to either true or a number if playback should be looped. + * + * When set to a number (e.g. 3) then the recording will be re-played given number of times + * and stopped after that. + * + * @default false + */ + loop?: boolean | number; + + /** + * Start the playback at a given time. + * + * Supported formats: + * - `123` (number of seconds) + * - `"2:03"` ("mm:ss") + * - `"1:02:03"` ("hh:mm:ss") + * + * @default 0 + * @example + * startAt: "1:23" // start at 1 minute 23 seconds + */ + startAt?: number | `${number}:${number}` | `${number}:${number}:${number}`; + + /** + * Playback speed. The value of 2 means 2x faster. + * + * @default 1 + * @example + * speed: 2 // plays twice as fast + */ + speed?: number; + + /** + * Limit terminal inactivity to a given number of seconds. + * + * For example, when set to 2 any inactivity (pauses) longer than 2 seconds will be "compressed" + * to 2 seconds. + * + * Defaults to: + * - `idle_time_limit` from asciicast header (saved when passing `-i ` to `asciinema rec`) + * - no limit, when it was not specified at the time of recording + * + * Tip: This option makes the playback more pleasant for viewers, + * and it's often better to use `idleTimeLimit` than `speed`. + * + * @default depends on asciicast header / no limit + * @example + * idleTimeLimit: 2 // compress pauses longer than 2s + */ + idleTimeLimit?: number; + + /** + * Terminal color theme. + * + * asciinema CLI 3.0 (and later) captures original terminal theme and embeds it in a recording file. + * This lets the player replicate the exact colors by default. + * + * This option can be used to override the terminal theme. + * See *Terminal themes* for a list of available built-in themes. + * + * If you'd like to configure the player to use the original theme when available, + * falling back to a specific theme, prefix the theme name with `auto/`. + * + * @default "auto/asciinema" (since v3.8), "asciinema" (earlier) + * @example + * theme: "auto/dracula" + */ + theme?: string; + + /** + * Poster (a preview frame) to display until the playback is started. + * + * Supported poster specifications: + * - `npt:1:23` — display recording "frame" at given time using NPT ("Normal Play Time") + * - `data:text/plain,Poster text` — print given text + * + * Using NPT-based poster preloads the recording on player's initialization + * regardless of `preload` option value. + * + * @default blank terminal or, when startAt is specified, screen contents at that time + * @example + * poster: "npt:1:23" + * @example + * poster: "data:text/plain,Hello \x1b[1;32mWorld" + */ + poster?: string; + + /** + * Audio file/stream to play together with the terminal session. + * + * For recorded sessions (asciicast files) the audio position is automatically synced + * with the session playback - pausing/resuming/seeking is reflected in the audio playback. + * + * For live terminal streams audioUrl is expected to be a live audio source - + * either a direct HTTP audio stream (.mp3, .aac, .ogg, etc.) such as Icecast/Shoutcast endpoint, + * or HLS playlist (.m3u8). + * + * Tip: Ensure the audio endpoint allows CORS requests. + * + * Warning: When using audioUrl don't use `autoplay: true` — browsers often require + * explicit user action before audio playback. + * + * @example + * audioUrl: "/demo.mp3" + * @example + * audioUrl: "http://example.com/icecast/stream.ogg" + */ + audioUrl?: string; + + /** + * Selects fitting (sizing) behaviour with regards to player's container element. + * + * Possible values: + * - `"width"` - scale to full width of the container + * - `"height"` - scale to full height of the container (requires fixed height) + * - `"both"` - scale to either full width or height (requires fixed height) + * - `false` / `"none"` - don't scale, use fixed size font + * + * Note: Version 2.x supported only the behaviour of `false`. + * Include `fit: false` to preserve the old sizing behaviour when upgrading from v2 to v3. + * + * @default "width" + * @example + * fit: "width" + */ + fit?: "width" | "height" | "both" | "none" | false; + + /** + * Hide or show user controls (bottom control bar). + * + * Valid values: + * - `true` - always show controls + * - `false` - never show controls + * - `"auto"` - show on mouse movement, hide on inactivity + * + * @default "auto" + * @example + * controls: "auto" + */ + controls?: boolean | "auto"; + + /** + * Defines a list of timeline markers. + * + * Markers set with this option override all markers embedded in asciicast files. + * If this option is not set the player defaults to markers found in the recording file (if any). + * + * @example + * markers: [5.0, 25.0, 66.6, 176.5] + * @example + * markers: [ + * [5.0, "Installation"], + * [25.0, "Configuration"] + * ] + */ + markers?: (number | [number, string])[]; + + /** + * If pauseOnMarkers is set to true, the playback automatically pauses + * on every marker encountered and can be resumed manually. + * + * Useful in live demos or presentations. + * + * @default false + * @example + * pauseOnMarkers: true + */ + pauseOnMarkers?: boolean; + + /** + * Size of the terminal font. + * + * Possible values: + * - any valid CSS font-size value (e.g. `"15px"`) + * - `"small"` + * - `"medium"` + * - `"big"` + * + * Warning: This option is effective only when `fit: false` is specified. + * + * @default "small" + * @example + * terminalFontSize: "15px" + */ + terminalFontSize?: string; + + /** + * Terminal font-family override. + * + * Use any valid CSS font-family value, + * e.g. `'JetBrains Mono', Consolas, Menlo, monospace`. + * + * Note: If you want to use web fonts, see the Fonts section + * for how to load them properly. + * + * @example + * terminalFontFamily: "'JetBrains Mono', monospace" + */ + terminalFontFamily?: string; + + /** + * Terminal line height override. + * + * The value is relative to the font size (like CSS `em` unit). + * Example: `1` means equal to font size; `2` means double spacing. + * + * @default 1.33333333 + * @example + * terminalLineHeight: 1.5 + */ + terminalLineHeight?: number; + + /** + * Set this option to `console`, i.e. `{ logger: console }`, + * or any object implementing console API (.log(), .debug(), .info(), .warn(), .error()) + * to enable logging. + * + * Useful during development or debugging. + * + * @example + * logger: console + */ + logger?: { + log(...args: unknown[]): void; + debug(...args: unknown[]): void; + info(...args: unknown[]): void; + warn(...args: unknown[]): void; + error(...args: unknown[]): void; + }; +} + +/** + * The player object, returned by create function, provides several methods that can be used + * to control the player or obtain information about its state. + */ +interface AsciinemaPlayer { + /** + * Returns the current playback time in seconds. + * + * @example + * player.getCurrentTime(); // => 1.23 + */ + getCurrentTime(): Promise; + + /** + * Returns the length of the recording in seconds, or null if the recording is not loaded yet. + * + * @example + * player.getDuration(); // => 123.45 + */ + getDuration(): Promise; + + /** + * Initiates playback of the recording. If the recording hasn't been preloaded then it's loaded, + * and playback is started. + * + * This function returns a promise which is fulfilled when the playback actually starts. + * If you want to synchronize asciinema player with other elements on the page (e.g. audio element), + * use this promise for coordination, or listen to `play` / `playing` events. + * + * @example + * player.play(); + * @example + * player.play().then(() => { + * console.log(`started! duration: ${player.getDuration()}`); + * }); + */ + play(): Promise; + + /** + * Pauses playback. The playback is paused immediately. + * + * @example + * player.pause(); + */ + pause(): Promise; + + /** + * Changes the playback location to specified time or marker. + * + * `location` can be: + * - time in seconds, as number, e.g. `15` + * - position in percentage, as string, e.g. `'50%'` + * - specific marker by its 0-based index, as `{ marker: i }`, e.g. `{ marker: 3 }` + * - previous marker, as `{ marker: 'prev' }` + * - next marker, as `{ marker: 'next' }` + * + * This function returns a promise which is fulfilled when the location actually changes. + * + * @example + * player.seek(15).then(() => { + * console.log(`current time: ${player.getCurrentTime()}`); + * }); + * @example + * player.seek("50%"); + * @example + * player.seek({ marker: "next" }); + */ + seek(location: SeekLocation): Promise; + + /** + * Adds event listener, binding handler's `this` to the player object. + * See Events for the list of all supported events. + * + * @example + * player.addEventListener("play", function () { + * console.log("play!", this.getCurrentTime()); + * }); + * @example + * player.addEventListener("input", function ({ data }) { + * console.log("input!", JSON.stringify(data)); + * }); + */ + addEventListener( + event: "play" | "playing" | "pause" | "ended", + handler: (this: AsciinemaPlayer) => void, + ): void; + + /** + * Adds event listener for `input` event, dispatched for every keyboard input that was recorded. + * Callback's 1st argument is an object with `data` field, which contains registered input value. + * Usually this is ASCII character representing a key, but may be a control character, + * like `"\r"` (enter), `"\u0001"` (ctrl-a), `"\u0003"` (ctrl-c), etc. + * + * Note: input events are available only for asciicasts recorded with `--stdin` option, + * i.e. `asciinema rec --stdin `. + * + * @example + * player.addEventListener("input", function ({ data }) { + * // play keyboard typing sound or display key presses + * playSound(data); + * }); + */ + addEventListener( + event: "input", + handler: (this: AsciinemaPlayer, detail: InputEventDetail) => void, + ): void; + + /** + * Adds event listener for `marker` event, dispatched for every marker encountered during playback. + * Callback's 1st argument has `index` (0-based), `time` (seconds) and optional `label`. + * Useful for orchestrating timed actions or fine-grained playback control (e.g. loop a section). + * + * @example + * player.addEventListener("marker", function ({ index, time, label }) { + * console.log(`marker! ${index} - ${time} - ${label}`); + * }); + */ + addEventListener( + event: "marker", + handler: (this: AsciinemaPlayer, detail: MarkerEventDetail) => void, + ): void; + + /** + * Use this function to dispose of the player, i.e. to shut it down, release all resources + * and remove it from DOM. + * + * @example + * player.dispose(); + */ + dispose(): void; +} + +/** Seek location union matching the docs. */ +export type SeekLocation = + | number // seconds + | `${number}%` // percentage string, e.g. "50%" + | { marker: number } // go to marker by 0-based index + | { marker: "prev" } // previous marker + | { marker: "next" }; // next marker + +/** Payload for the `input` event. */ +export type InputEventDetail = { + /** + * Registered input value (ASCII or control char). + * + * @example + * "\r" // Enter + * @example + * "\u0003" // Ctrl-C + */ + data: string; +}; + +/** Payload for the `marker` event. */ +export type MarkerEventDetail = { + /** 0-based marker index. */ + index: number; + /** Marker time in seconds. */ + time: number; + /** Optional marker label. */ + label?: string | null; +}; diff --git a/src/ui.d.ts b/src/ui.d.ts new file mode 100644 index 00000000..34c61610 --- /dev/null +++ b/src/ui.d.ts @@ -0,0 +1 @@ +export * from './index' \ No newline at end of file