diff --git a/.changeset/empty-owls-add.md b/.changeset/empty-owls-add.md new file mode 100644 index 00000000..427b35bc --- /dev/null +++ b/.changeset/empty-owls-add.md @@ -0,0 +1,5 @@ +--- +'@lottiefiles/dotlottie-web': minor +--- + +refactor: 💡 dotlottie-rs wasm bindings integration diff --git a/.changeset/little-dingos-happen.md b/.changeset/little-dingos-happen.md new file mode 100644 index 00000000..0e581a7b --- /dev/null +++ b/.changeset/little-dingos-happen.md @@ -0,0 +1,5 @@ +--- +'@lottiefiles/dotlottie-web': minor +--- + +feat: 🎸 emit `render` event when a new frame is rendered diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 00000000..08834c89 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,11 @@ +{ + "mode": "pre", + "tag": "beta", + "initialVersions": { + "@lottiefiles/dotlottie-react": "0.2.3", + "@lottiefiles/dotlottie-vue": "0.1.4", + "@lottiefiles/dotlottie-wc": "0.0.7", + "@lottiefiles/dotlottie-web": "0.12.3" + }, + "changesets": [] +} diff --git a/.eslintignore b/.eslintignore index 74ca1491..53075f17 100644 --- a/.eslintignore +++ b/.eslintignore @@ -42,4 +42,4 @@ next-env.d.ts # Ignore renderer releases releases/ -renderer.js +dotlottie-player.js diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 08a955c7..0ff3d1da 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,6 +3,7 @@ on: push: branches: - 'main' + - 'beta' pull_request: jobs: diff --git a/.size-limit.cjs b/.size-limit.cjs index 03ad2044..2d18729f 100644 --- a/.size-limit.cjs +++ b/.size-limit.cjs @@ -6,7 +6,7 @@ module.exports = [ }, { name: '@lottiefiles/dotlottie-web WASM', - path: 'packages/web/dist/wasm/*.wasm', + path: 'packages/web/dist/*.wasm', modifyWebpackConfig: (config) => { config.experiments = { asyncWebAssembly: true, diff --git a/apps/dotlottie-vue-example/package.json b/apps/dotlottie-vue-example/package.json index 69e86aab..44fd5ce3 100644 --- a/apps/dotlottie-vue-example/package.json +++ b/apps/dotlottie-vue-example/package.json @@ -1,6 +1,5 @@ { - "name": "dotlottie-vue-example", - "version": "0.0.0", + "name": "@lottiefiles/dotlottie-vue-example", "type": "module", "private": true, "scripts": { diff --git a/apps/dotlottie-wc-example/package.json b/apps/dotlottie-wc-example/package.json index 3ed1e2a7..ebe169aa 100644 --- a/apps/dotlottie-wc-example/package.json +++ b/apps/dotlottie-wc-example/package.json @@ -1,6 +1,5 @@ { - "name": "dotlottie-wc-example", - "version": "0.0.0", + "name": "@lottiefiles/dotlottie-wc-example", "type": "module", "private": true, "scripts": { diff --git a/apps/dotlottie-web-example/src/main.ts b/apps/dotlottie-web-example/src/main.ts index 62821520..1061a87b 100644 --- a/apps/dotlottie-web-example/src/main.ts +++ b/apps/dotlottie-web-example/src/main.ts @@ -8,7 +8,7 @@ import './styles.css'; import type { Mode } from '@lottiefiles/dotlottie-web'; import { DotLottie } from '@lottiefiles/dotlottie-web'; -import wasmUrl from '../../../packages/web/dist/wasm/renderer.wasm?url'; +import wasmUrl from '../../../packages/web/dist/dotlottie-player.wasm?url'; const app = document.getElementById('app') as HTMLDivElement; @@ -63,7 +63,7 @@ app.innerHTML = ` @@ -103,9 +103,11 @@ allCanvas.forEach((canvas) => { loop: true, autoplay: true, backgroundColor, - useFrameInterpolation: false, + // useFrameInterpolation: false, }); + dotLottie.addEventListener('loadError', console.error); + window.addEventListener('resize', () => { dotLottie.resize(); }); @@ -127,9 +129,11 @@ fetch('/hamster.lottie') segments: [10, 90], speed: 1, backgroundColor: '#800080ff', - useFrameInterpolation: false, + // useFrameInterpolation: false, }); + dotLottie.addEventListener('loadError', console.error); + const playPauseButton = document.getElementById('playPause') as HTMLButtonElement; const stopButton = document.getElementById('stop') as HTMLButtonElement; const currentFrameSpan = document.getElementById('current-frame') as HTMLSpanElement; @@ -196,7 +200,7 @@ fetch('/hamster.lottie') src: 'https://lottie.host/f315768c-a29b-41fd-b5a8-a1c1dfb36cd2/CRiiNg8fqQ.lottie', loop: true, autoplay: true, - mode: 'bounce-reverse', + mode: 'reverse-bounce', renderConfig: { devicePixelRatio: 0.2, }, diff --git a/apps/dotlottie-web-node-example/index.ts b/apps/dotlottie-web-node-example/index.ts index 0625d984..26ec49e4 100644 --- a/apps/dotlottie-web-node-example/index.ts +++ b/apps/dotlottie-web-node-example/index.ts @@ -12,7 +12,7 @@ import GIFEncoder from 'gif-encoder'; import minimist from 'minimist'; const wasmBase64 = fs - .readFileSync('./node_modules/@lottiefiles/dotlottie-web/dist/wasm/renderer.wasm') + .readFileSync('./node_modules/@lottiefiles/dotlottie-web/dist/dotlottie-player.wasm') .toString('base64'); const wasmDataUri = `data:application/octet-stream;base64,${wasmBase64}`; @@ -120,7 +120,7 @@ dotLottie.addEventListener('load', () => { dotLottie.addEventListener('frame', (event) => { const frame = ctx.getImageData(0, 0, args.width, args.height).data; - if (event.currentFrame >= dotLottie.totalFrames - 1) { + if (event.currentFrame >= dotLottie.totalFrames) { console.log('Finished recording GIF'); gif.finish(); } else { diff --git a/packages/react/README.md b/packages/react/README.md index 1bdeda3f..2915eee6 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -78,7 +78,7 @@ The `DotLottieReactProps` extends the `HTMLCanvasElement` Props and accepts all | `src` | string | | undefined | URL to the animation data (`.json` or `.lottie`). | | | `speed` | number | | 1 | Animation playback speed. 1 is regular speed. | | | `data` | string \| ArrayBuffer | | undefined | Animation data provided either as a Lottie JSON string or as an ArrayBuffer for .lottie animations. | | -| `mode` | string | | "forward" | Animation play mode. Accepts "forward", "reverse", "bounce", "bounce-reverse". | | +| `mode` | string | | "forward" | Animation play mode. Accepts "forward", "reverse", "bounce", "reverse-bounce". | | | `backgroundColor` | string | | undefined | Background color of the canvas. Accepts 6-digit or 8-digit hex color string (e.g., "#000000", "#000000FF"), | | | `segments` | \[number, number] | | \[0, totalFrames - 1] | Animation segments. Accepts an array of two numbers, where the first number is the start frame and the second number is the end frame. | | | `renderConfig` | RenderConfig | | `{}` | Configuration for rendering the animation. | | diff --git a/packages/vue/README.md b/packages/vue/README.md index 013f2950..273fec84 100644 --- a/packages/vue/README.md +++ b/packages/vue/README.md @@ -69,7 +69,7 @@ import { DotLottieVue } from '@lottiefiles/dotlottie-vue' | `src` | string | | undefined | URL to the animation data (`.json` or `.lottie`). | | `speed` | number | | 1 | Animation playback speed. 1 is regular speed. | | `data` | string \| ArrayBuffer | | undefined | Animation data provided either as a Lottie JSON string or as an ArrayBuffer for .lottie animations. | -| `mode` | string | | "forward" | Animation play mode. Accepts "forward", "reverse", "bounce", "bounce-reverse". | +| `mode` | string | | "forward" | Animation play mode. Accepts "forward", "reverse", "bounce", "reverse-bounce". | | `backgroundColor` | string | | undefined | Background color of the canvas. Accepts 6-digit or 8-digit hex color string (e.g., "#000000", "#000000FF"), | | `segments` | \[number, number] | | \[0, totalFrames - 1] | Animation segments. Accepts an array of two numbers, where the first number is the start frame and the second number is the end frame. | | `renderConfig` | RenderConfig | | `{}` | Configuration for rendering the animation. | diff --git a/packages/web/.dockerignore b/packages/web/.dockerignore deleted file mode 100644 index 3c3629e6..00000000 --- a/packages/web/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/packages/web/Dockerfile b/packages/web/Dockerfile deleted file mode 100644 index a06c8103..00000000 --- a/packages/web/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM emscripten/emsdk:latest - -WORKDIR /package - -RUN apt-get update \ - && apt-get install -y meson ninja-build git bash \ - && rm -rf /var/lib/apt/lists/* - -COPY . . - -CMD ["/bin/bash", "./build_wasm.sh", "/emsdk/"] diff --git a/packages/web/README.md b/packages/web/README.md index bf901dd2..01fe6f2b 100644 --- a/packages/web/README.md +++ b/packages/web/README.md @@ -121,7 +121,7 @@ The `DotLottie` constructor accepts a config object with the following propertie | `src` | string | | undefined | URL to the animation data (`.json` or `.lottie`). | | `speed` | number | | 1 | Animation playback speed. 1 is regular speed. | | `data` | string \| ArrayBuffer | | undefined | Animation data provided either as a Lottie JSON string or as an ArrayBuffer for .lottie animations. | -| `mode` | string | | "forward" | Animation play mode. Accepts "forward", "reverse", "bounce", "bounce-reverse". | +| `mode` | string | | "forward" | Animation play mode. Accepts "forward", "reverse", "bounce", "reverse-bounce". | | `backgroundColor` | string | | undefined | Background color of the canvas. Accepts 6-digit or 8-digit hex color string (e.g., "#000000", "#000000FF"), | | `segments` | \[number, number] | | \[0, totalFrames - 1] | Animation segments. Accepts an array of two numbers, where the first number is the start frame and the second number is the end frame. | | `renderConfig` | [RenderConfig](#renderconfig) | | `{}` | Configuration for rendering the animation. | @@ -197,19 +197,20 @@ The `DotLottie` class exposes the following static methods: The `DotLottie` instance emits the following events that can be listened to via the `addEventListener` method: -| Event | Description | Event Parameter (Type and Fields) | -| ----------- | ----------------------------------------------------------------------- | ---------------------------------------------------- | -| `load` | Emitted when the animation is loaded. | `LoadEvent { type: 'load' }` | -| `loadError` | Emitted when there's an error loading the animation. | `LoadErrorEvent { type: 'loadError', error: Error }` | -| `play` | Emitted when the animation starts playing. | `PlayEvent { type: 'play' }` | -| `pause` | Emitted when the animation is paused. | `PauseEvent { type: 'pause' }` | -| `stop` | Emitted when the animation is stopped. | `StopEvent { type: 'stop' }` | -| `loop` | Emitted when the animation completes a loop. | `LoopEvent { type: 'loop', loopCount: number }` | -| `complete` | Emitted when the animation completes. | `CompleteEvent { type: 'complete' }` | -| `frame` | Emitted when the animation reaches a new frame. | `FrameEvent { type: 'frame', currentFrame: number }` | -| `destroy` | Emitted when the animation is destroyed. | `DestroyEvent { type: 'destroy' }` | -| `freeze` | Emitted when the animation is freezed and the animation loop stops. | `FreezeEvent { type: 'freeze' }` | -| `unfreeze` | Emitted when the animation is unfreezed and the animation loop resumes. | `UnfreezeEvent { type: 'unfreeze' }` | +| Event | Description | Event Parameter (Type and Fields) | +| ----------- | ----------------------------------------------------------------------- | ------------------------------------------------------ | +| `load` | Emitted when the animation is loaded. | `LoadEvent { type: 'load' }` | +| `loadError` | Emitted when there's an error loading the animation. | `LoadErrorEvent { type: 'loadError', error: Error }` | +| `play` | Emitted when the animation starts playing. | `PlayEvent { type: 'play' }` | +| `pause` | Emitted when the animation is paused. | `PauseEvent { type: 'pause' }` | +| `stop` | Emitted when the animation is stopped. | `StopEvent { type: 'stop' }` | +| `loop` | Emitted when the animation completes a loop. | `LoopEvent { type: 'loop', loopCount: number }` | +| `complete` | Emitted when the animation completes. | `CompleteEvent { type: 'complete' }` | +| `frame` | Emitted when the animation reaches a new frame. | `FrameEvent { type: 'frame', currentFrame: number }` | +| `destroy` | Emitted when the animation is destroyed. | `DestroyEvent { type: 'destroy' }` | +| `freeze` | Emitted when the animation is freezed and the animation loop stops. | `FreezeEvent { type: 'freeze' }` | +| `unfreeze` | Emitted when the animation is unfreezed and the animation loop resumes. | `UnfreezeEvent { type: 'unfreeze' }` | +| `render` | Emitted when a new frame is rendered to the canvas. | `RenderEvent { type: 'render', currentFrame: number }` | ## Development diff --git a/packages/web/build_wasm.sh b/packages/web/build_wasm.sh deleted file mode 100755 index 52847351..00000000 --- a/packages/web/build_wasm.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/bash - -# Store positional arguments in descriptive variables for clarity -SCRIPT_NAME="$0" -EMSDK_PATH="$1" -THORVG_TAG_OR_BRANCH="${2:-v0.12.x}" - -# Ensure we always execute from the directory the script resides in -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$DIR" - -# Ensure the user provides the EMSDK path as an argument to the script -#if [ "$#" -lt 1 ]; then -if [ -z "$EMSDK_PATH" ]; then - echo " - Oops! It seems you forgot to provide the emsdk path argument. - - Please install emsdk and then specify its path when running this script. - For emsdk installation guidance, visit: https://emscripten.org/docs/getting_started/downloads.html - - Usage: $SCRIPT_NAME [optional_thorvg_tag_or_branch] - ex. -> $SCRIPT_NAME /home/user/emsdk/ tags/v0.11.1 - " - exit 1 -fi - -# Function for checking command status and printing an error -check_command_success() { - if [ $? -ne 0 ]; then - echo "$1" - exit 1 - fi -} - -# Check if essential tools are available in the system -command -v git > /dev/null 2>&1 || { - echo "Error: git is not installed. Please ensure you have git installed and try again." - exit 1 -} -command -v meson > /dev/null 2>&1 || { - echo "Error: meson is not installed. Please install meson and try again." - exit 1 -} -command -v ninja > /dev/null 2>&1 || { - echo "Error: ninja is not installed. Please install ninja and try again." - exit 1 -} - -# Clone the thorvg repository if it's not already present -if [ ! -d "$DIR/thorvg" ]; then - git clone https://github.com/thorvg/thorvg.git - check_command_success "Error cloning the repository" -fi - -# Remove the build_wasm directory inside thorvg if it exists -if [ -d "$DIR/thorvg/build_wasm" ]; then - rm -r "$DIR/thorvg/build_wasm" -fi - -# Change directory to thorvg to run the wasm_build.sh script -cd "$DIR/thorvg" -git fetch origin -# git checkout $THORVG_TAG_OR_BRANCH -git checkout "$THORVG_TAG_OR_BRANCH" -check_command_success "Error switching to the desired tag/branch: $THORVG_TAG_OR_BRANCH" - -sed "s|EMSDK:|$EMSDK_PATH|g" cross/wasm_x86_i686.txt > /tmp/.wasm_cross.txt -meson -Db_lto=true -Ddefault_library=static -Dstatic=true -Dthreads=false -Dloaders="lottie, png, jpg" --cross-file /tmp/.wasm_cross.txt build_wasm -ninja -C build_wasm/ - -# Change back to the parent directory -cd "$DIR" - -# Create a unique temporary file to hold the cross file after substitution -TMPFILE=$(mktemp /tmp/wasm_cross.XXXXXX) - -# Ensure the temp file is always cleaned up on exit -trap 'rm -f "$TMPFILE"' EXIT - -# Substitute the EMSDK path in the wasm_cross.txt file and save to the temporary file -sed "s|EMSDK:|$EMSDK_PATH|g" "$DIR/wasm_cross.txt" > "$TMPFILE" -check_command_success "Error substituting EMSDK path in wasm_cross.txt" - -# Setup meson build with the cross file -meson --cross-file "$TMPFILE" src/wasm -check_command_success "Error setting up meson" - -# Build using ninja -ninja -C src/wasm/ -check_command_success "Error during ninja build" - -# Remove all non .js, .ts and .wasm files -find "$DIR/src/wasm" -type f ! \( -name "*.js" -o -name "*.wasm" -o -name "*.ts" \) -delete - -# Remove unwanted directories -rm -rf "$DIR/src/wasm/meson-info" "$DIR/src/wasm/meson-logs" "$DIR/src/wasm/meson-private" "$DIR/src/wasm/renderer.js.p" diff --git a/packages/web/cpp/renderer.cpp b/packages/web/cpp/renderer.cpp deleted file mode 100644 index 6bac1bd0..00000000 --- a/packages/web/cpp/renderer.cpp +++ /dev/null @@ -1,271 +0,0 @@ -#include -#include - -using namespace emscripten; -using namespace std; -using namespace tvg; - -static const char *NoError = "None"; - -class __attribute__((visibility("default"))) Renderer -{ - -public: - ~Renderer() - { - free(buffer); - Initializer::term(CanvasEngine::Sw); - } - - static unique_ptr create() - { - return unique_ptr(new Renderer()); - } - - string error() - { - return errorMsg; - } - - bool load(string data, int width, int height) - { - - errorMsg = NoError; - - if (!canvas) - { - errorMsg = "Invalid canvas"; - return false; - } - - if (data.empty()) - { - errorMsg = "Invalid data"; - return false; - } - - canvas->clear(true); - - animation = Animation::gen(); - - if (animation->picture()->load(data.c_str(), data.size(), "lottie", false) != Result::Success) - { - errorMsg = "load() fail"; - return false; - } - - animation->picture()->size(&psize[0], &psize[1]); - - /* need to reset size to calculate scale in Picture.size internally before calling resize() */ - this->width = 0; - this->height = 0; - - resize(width, height); - - // create background shape - auto background = Shape::gen(); - this->background = background.get(); - - background->appendRect(0, 0, psize[0], psize[1]); - uint8_t r = (bgColor >> 24) & 0xFF; - uint8_t g = (bgColor >> 16) & 0xFF; - uint8_t b = (bgColor >> 8) & 0xFF; - uint8_t a = bgColor & 0xFF; - background->fill(r, g, b, a); - canvas->push(std::move(background)); - - std::unique_ptr picturePaint(animation->picture()); - - if (canvas->push(std::move(picturePaint)) != Result::Success) - { - errorMsg = "push() fail"; - return false; - } - - updated = true; - - return true; - } - - bool update() - { - if (!updated) - return true; - - errorMsg = NoError; - - if (canvas->update() != Result::Success) - { - errorMsg = "update() fail"; - return false; - } - - return true; - } - - val render() - { - errorMsg = NoError; - - if (!canvas || !animation) - { - errorMsg = "Invalid canvas or animation"; - return val(typed_memory_view(0, nullptr)); - } - - if (!updated) - { - - return val(typed_memory_view(width * height * 4, buffer)); - } - - if (canvas->draw() != Result::Success) - { - errorMsg = "draw() fail"; - return val(typed_memory_view(0, nullptr)); - } - - canvas->sync(); - - updated = false; - - return val(typed_memory_view(width * height * 4, buffer)); - } - - val size() - { - return val(typed_memory_view(2, psize)); - } - - val duration() - { - if (!canvas || !animation) - return val(0); - return val(animation->duration()); - } - - val totalFrames() - { - if (!canvas || !animation) - return val(0); - return val(animation->totalFrame()); - } - - bool frame(float no) - { - if (!canvas || !animation) - return false; - if (animation->frame(no) == Result::Success) - { - - updated = true; - - return true; - } - - return false; - } - - void setBgColor(uint32_t color) - { - if (!canvas) - return; - - if (color == bgColor) - return; - - this->bgColor = color; - - if (background) - { - uint8_t r = (bgColor >> 24) & 0xFF; - uint8_t g = (bgColor >> 16) & 0xFF; - uint8_t b = (bgColor >> 8) & 0xFF; - uint8_t a = bgColor & 0xFF; - - background->fill(r, g, b, a); - } - - updated = true; - } - - void resize(int width, int height) - { - if (!canvas || !animation) - return; - if (this->width == width && this->height == height) - return; - - this->width = width; - this->height = height; - - free(buffer); - buffer = (uint8_t *)malloc(width * height * sizeof(uint32_t)); - canvas->target((uint32_t *)buffer, width, width, height, SwCanvas::ABGR8888S); - - float scale; - float shiftX = 0.0f, shiftY = 0.0f; - if (psize[0] > psize[1]) - { - scale = width / psize[0]; - shiftY = (height - psize[1] * scale) * 0.5f; - } - else - { - scale = height / psize[1]; - shiftX = (width - psize[0] * scale) * 0.5f; - } - animation->picture()->scale(scale); - animation->picture()->translate(shiftX, shiftY); - - updated = true; - } - -private: - explicit Renderer() - { - errorMsg = NoError; - - if (Initializer::init(CanvasEngine::Sw, 0) != Result::Success) - { - errorMsg = "init() fail"; - return; - } - - canvas = SwCanvas::gen(); - if (!canvas) - errorMsg = "Invalid canvas"; - - animation = Animation::gen(); - if (!animation) - errorMsg = "Invalid animation"; - } - -private: - string errorMsg; - unique_ptr canvas = nullptr; - unique_ptr animation = nullptr; - Shape *background = nullptr; - uint8_t *buffer = nullptr; - uint32_t width = 0; - uint32_t height = 0; - uint32_t bgColor = 0x00000000; - float psize[2]; // picture size - bool updated = false; -}; - -EMSCRIPTEN_BINDINGS(Renderer) -{ - class_("Renderer") - .constructor(&Renderer::create) - .function("error", &Renderer::error, allow_raw_pointers()) - .function("load", &Renderer::load) - .function("update", &Renderer::update) - .function("resize", &Renderer::resize) - .function("render", &Renderer::render) - .function("size", &Renderer::size) - .function("duration", &Renderer::duration) - .function("totalFrames", &Renderer::totalFrames) - .function("frame", &Renderer::frame) - .function("setBgColor", &Renderer::setBgColor); -} \ No newline at end of file diff --git a/packages/web/docker_build_wasm.sh b/packages/web/docker_build_wasm.sh deleted file mode 100644 index 34d3a13c..00000000 --- a/packages/web/docker_build_wasm.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker --log-level=debug build -t $0 . && docker run --rm -v $(pwd):/package $0 diff --git a/packages/web/meson.build b/packages/web/meson.build deleted file mode 100644 index a5ada8eb..00000000 --- a/packages/web/meson.build +++ /dev/null @@ -1,20 +0,0 @@ -project('Renderer', 'cpp') - -cc = meson.get_compiler('cpp') - -if cc.get_id() == 'emscripten' - thorvg_wasm_lib_path = join_paths(meson.current_source_dir(), 'thorvg', 'build_wasm', 'src') - thorvg_wasm_lib = cc.find_library('thorvg', dirs: thorvg_wasm_lib_path) - - headers = include_directories('thorvg/inc') - - thorvg_wasm_dep = declare_dependency(dependencies: thorvg_wasm_lib, include_directories: headers) - - executable('renderer', - 'cpp/renderer.cpp', - include_directories: headers, - dependencies: [thorvg_wasm_dep], - ) -else - message('The compiler is not Emscripten.') -endif diff --git a/packages/web/package.json b/packages/web/package.json index 2ad3b3be..a25601bc 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -18,21 +18,9 @@ "engines": { "node": ">=18" }, - "main": "dist/index.js", - "exports": { - ".": "./dist/index.js", - "./*": "./dist/*.js", - "./package.json": "./package.json" - }, + "main": "dist/index.cjs", + "module": "dist/index.js", "types": "dist/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "dist/*", - "dist/index.d.ts" - ] - } - }, "files": [ "dist" ], @@ -59,9 +47,6 @@ "test:watch": "vitest", "type-check": "tsc --noEmit" }, - "dependencies": { - "@dotlottie/dotlottie-js": "^0.6.2" - }, "devDependencies": { "@types/node": "^20.10.5", "@vitest/browser": "^1.1.0", diff --git a/packages/web/src/wasm/renderer.js b/packages/web/src/core/dotlottie-player.js similarity index 65% rename from packages/web/src/wasm/renderer.js rename to packages/web/src/core/dotlottie-player.js index 2acb419d..b19bd948 100644 --- a/packages/web/src/wasm/renderer.js +++ b/packages/web/src/core/dotlottie-player.js @@ -1,4 +1,4 @@ -var createRendererModule = (() => { +var createDotLottiePlayerModule = (() => { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; return function (moduleArg = {}) { @@ -226,7 +226,7 @@ var createRendererModule = (() => { var wasmBinaryFile; - wasmBinaryFile = 'renderer.wasm'; + wasmBinaryFile = 'DotLottiePlayer.wasm'; if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile); @@ -297,10 +297,10 @@ var createRendererModule = (() => { }; /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) { wasmExports = instance.exports; - wasmMemory = wasmExports['I']; + wasmMemory = wasmExports['Aa']; updateMemoryViews(); - wasmTable = wasmExports['N']; - addOnInit(wasmExports['J']); + wasmTable = wasmExports['Da']; + addOnInit(wasmExports['Ba']); removeRunDependency('wasm-instantiate'); return wasmExports; } @@ -400,6 +400,129 @@ var createRendererModule = (() => { ); }; + var exceptionCaught = []; + + var uncaughtExceptionCount = 0; + + var ___cxa_begin_catch = (ptr) => { + var info = new ExceptionInfo(ptr); + if (!info.get_caught()) { + info.set_caught(true); + uncaughtExceptionCount--; + } + info.set_rethrown(false); + exceptionCaught.push(info); + ___cxa_increment_exception_refcount(info.excPtr); + return info.get_exception_ptr(); + }; + + var exceptionLast = 0; + + var ___cxa_end_catch = () => { + _setThrew(0, 0); + var info = exceptionCaught.pop(); + ___cxa_decrement_exception_refcount(info.excPtr); + exceptionLast = 0; + }; + + /** @constructor */ function ExceptionInfo(excPtr) { + this.excPtr = excPtr; + this.ptr = excPtr - 24; + this.set_type = function (type) { + HEAPU32[(this.ptr + 4) >> 2] = type; + }; + this.get_type = function () { + return HEAPU32[(this.ptr + 4) >> 2]; + }; + this.set_destructor = function (destructor) { + HEAPU32[(this.ptr + 8) >> 2] = destructor; + }; + this.get_destructor = function () { + return HEAPU32[(this.ptr + 8) >> 2]; + }; + this.set_caught = function (caught) { + caught = caught ? 1 : 0; + HEAP8[(this.ptr + 12) >> 0] = caught; + }; + this.get_caught = function () { + return HEAP8[(this.ptr + 12) >> 0] != 0; + }; + this.set_rethrown = function (rethrown) { + rethrown = rethrown ? 1 : 0; + HEAP8[(this.ptr + 13) >> 0] = rethrown; + }; + this.get_rethrown = function () { + return HEAP8[(this.ptr + 13) >> 0] != 0; + }; + this.init = function (type, destructor) { + this.set_adjusted_ptr(0); + this.set_type(type); + this.set_destructor(destructor); + }; + this.set_adjusted_ptr = function (adjustedPtr) { + HEAPU32[(this.ptr + 16) >> 2] = adjustedPtr; + }; + this.get_adjusted_ptr = function () { + return HEAPU32[(this.ptr + 16) >> 2]; + }; + this.get_exception_ptr = function () { + var isPointer = ___cxa_is_pointer_type(this.get_type()); + if (isPointer) { + return HEAPU32[this.excPtr >> 2]; + } + var adjusted = this.get_adjusted_ptr(); + if (adjusted !== 0) return adjusted; + return this.excPtr; + }; + } + + var ___resumeException = (ptr) => { + if (!exceptionLast) { + exceptionLast = ptr; + } + throw exceptionLast; + }; + + var findMatchingCatch = (args) => { + var thrown = exceptionLast; + if (!thrown) { + setTempRet0(0); + return 0; + } + var info = new ExceptionInfo(thrown); + info.set_adjusted_ptr(thrown); + var thrownType = info.get_type(); + if (!thrownType) { + setTempRet0(0); + return thrown; + } + for (var arg in args) { + var caughtType = args[arg]; + if (caughtType === 0 || caughtType === thrownType) { + break; + } + var adjusted_ptr_addr = info.ptr + 16; + if (___cxa_can_catch(caughtType, thrownType, adjusted_ptr_addr)) { + setTempRet0(caughtType); + return thrown; + } + } + setTempRet0(thrownType); + return thrown; + }; + + var ___cxa_find_matching_catch_2 = () => findMatchingCatch([]); + + var ___cxa_find_matching_catch_4 = (arg0, arg1) => findMatchingCatch([arg0, arg1]); + + var ___cxa_throw = (ptr, type, destructor) => { + var info = new ExceptionInfo(ptr); + info.init(type, destructor); + exceptionLast = ptr; + uncaughtExceptionCount++; + throw exceptionLast; + }; + var SYSCALLS = { varargs: undefined, get() { @@ -421,6 +544,62 @@ var createRendererModule = (() => { return 0; } + var lengthBytesUTF8 = (str) => { + var len = 0; + for (var i = 0; i < str.length; ++i) { + var c = str.charCodeAt(i); + if (c <= 127) { + len++; + } else if (c <= 2047) { + len += 2; + } else if (c >= 55296 && c <= 57343) { + len += 4; + ++i; + } else { + len += 3; + } + } + return len; + }; + + var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { + if (!(maxBytesToWrite > 0)) return 0; + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; + for (var i = 0; i < str.length; ++i) { + var u = str.charCodeAt(i); + if (u >= 55296 && u <= 57343) { + var u1 = str.charCodeAt(++i); + u = (65536 + ((u & 1023) << 10)) | (u1 & 1023); + } + if (u <= 127) { + if (outIdx >= endIdx) break; + heap[outIdx++] = u; + } else if (u <= 2047) { + if (outIdx + 1 >= endIdx) break; + heap[outIdx++] = 192 | (u >> 6); + heap[outIdx++] = 128 | (u & 63); + } else if (u <= 65535) { + if (outIdx + 2 >= endIdx) break; + heap[outIdx++] = 224 | (u >> 12); + heap[outIdx++] = 128 | ((u >> 6) & 63); + heap[outIdx++] = 128 | (u & 63); + } else { + if (outIdx + 3 >= endIdx) break; + heap[outIdx++] = 240 | (u >> 18); + heap[outIdx++] = 128 | ((u >> 12) & 63); + heap[outIdx++] = 128 | ((u >> 6) & 63); + heap[outIdx++] = 128 | (u & 63); + } + } + heap[outIdx] = 0; + return outIdx - startIdx; + }; + + var stringToUTF8 = (str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); + + var ___syscall_getcwd = (buf, size) => {}; + function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs; return 0; @@ -430,26 +609,19 @@ var createRendererModule = (() => { SYSCALLS.varargs = varargs; } - var __embind_register_bigint = (primitiveType, name, size, minRange, maxRange) => {}; + var structRegistrations = {}; - var embind_init_charCodes = () => { - var codes = new Array(256); - for (var i = 0; i < 256; ++i) { - codes[i] = String.fromCharCode(i); + var runDestructors = (destructors) => { + while (destructors.length) { + var ptr = destructors.pop(); + var del = destructors.pop(); + del(ptr); } - embind_charCodes = codes; }; - var embind_charCodes; - - var readLatin1String = (ptr) => { - var ret = ''; - var c = ptr; - while (HEAPU8[c]) { - ret += embind_charCodes[HEAPU8[c++]]; - } - return ret; - }; + /** @suppress {globalThis} */ function simpleReadValueFromPointer(pointer) { + return this['fromWireType'](HEAP32[pointer >> 2]); + } var awaitingDependencies = {}; @@ -457,12 +629,6 @@ var createRendererModule = (() => { var typeDependencies = {}; - var BindingError; - - var throwBindingError = (message) => { - throw new BindingError(message); - }; - var InternalError; var throwInternalError = (message) => { @@ -507,6 +673,95 @@ var createRendererModule = (() => { } }; + var __embind_finalize_value_object = (structType) => { + var reg = structRegistrations[structType]; + delete structRegistrations[structType]; + var rawConstructor = reg.rawConstructor; + var rawDestructor = reg.rawDestructor; + var fieldRecords = reg.fields; + var fieldTypes = fieldRecords + .map((field) => field.getterReturnType) + .concat(fieldRecords.map((field) => field.setterArgumentType)); + whenDependentTypesAreResolved([structType], fieldTypes, (fieldTypes) => { + var fields = {}; + fieldRecords.forEach((field, i) => { + var fieldName = field.fieldName; + var getterReturnType = fieldTypes[i]; + var getter = field.getter; + var getterContext = field.getterContext; + var setterArgumentType = fieldTypes[i + fieldRecords.length]; + var setter = field.setter; + var setterContext = field.setterContext; + fields[fieldName] = { + read: (ptr) => getterReturnType['fromWireType'](getter(getterContext, ptr)), + write: (ptr, o) => { + var destructors = []; + setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, o)); + runDestructors(destructors); + }, + }; + }); + return [ + { + name: reg.name, + fromWireType: (ptr) => { + var rv = {}; + for (var i in fields) { + rv[i] = fields[i].read(ptr); + } + rawDestructor(ptr); + return rv; + }, + toWireType: (destructors, o) => { + for (var fieldName in fields) { + if (!(fieldName in o)) { + throw new TypeError(`Missing field: "${fieldName}"`); + } + } + var ptr = rawConstructor(); + for (fieldName in fields) { + fields[fieldName].write(ptr, o[fieldName]); + } + if (destructors !== null) { + destructors.push(rawDestructor, ptr); + } + return ptr; + }, + argPackAdvance: GenericWireTypeSize, + readValueFromPointer: simpleReadValueFromPointer, + destructorFunction: rawDestructor, + }, + ]; + }); + }; + + var __embind_register_bigint = (primitiveType, name, size, minRange, maxRange) => {}; + + var embind_init_charCodes = () => { + var codes = new Array(256); + for (var i = 0; i < 256; ++i) { + codes[i] = String.fromCharCode(i); + } + embind_charCodes = codes; + }; + + var embind_charCodes; + + var readLatin1String = (ptr) => { + var ret = ''; + var c = ptr; + while (HEAPU8[c]) { + ret += embind_charCodes[HEAPU8[c++]]; + } + return ret; + }; + + var BindingError; + + var throwBindingError = (message) => { + throw new BindingError(message); + }; + /** @param {Object=} options */ function sharedRegisterType(rawType, registeredInstance, options = {}) { var name = registeredInstance.name; if (!rawType) { @@ -1321,13 +1576,14 @@ var createRendererModule = (() => { return array; }; - var runDestructors = (destructors) => { - while (destructors.length) { - var ptr = destructors.pop(); - var del = destructors.pop(); - del(ptr); + function usesDestructorStack(argTypes) { + for (var i = 1; i < argTypes.length; ++i) { + if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) { + return true; + } } - }; + return false; + } function usesDestructorStack(argTypes) { for (var i = 1; i < argTypes.length; ++i) { @@ -1598,24 +1854,97 @@ var createRendererModule = (() => { }, }; - /** @suppress {globalThis} */ function simpleReadValueFromPointer(pointer) { - return this['fromWireType'](HEAP32[pointer >> 2]); - } + var __embind_register_emval = (rawType, name) => { + name = readLatin1String(name); + registerType(rawType, { + name: name, + fromWireType: (handle) => { + var rv = Emval.toValue(handle); + __emval_decref(handle); + return rv; + }, + toWireType: (destructors, value) => Emval.toHandle(value), + argPackAdvance: GenericWireTypeSize, + readValueFromPointer: simpleReadValueFromPointer, + destructorFunction: null, + }); + }; - var EmValType = { - name: 'emscripten::val', - fromWireType: (handle) => { - var rv = Emval.toValue(handle); - __emval_decref(handle); - return rv; - }, - toWireType: (destructors, value) => Emval.toHandle(value), - argPackAdvance: GenericWireTypeSize, - readValueFromPointer: simpleReadValueFromPointer, - destructorFunction: null, + var enumReadValueFromPointer = (name, width, signed) => { + switch (width) { + case 1: + return signed + ? function (pointer) { + return this['fromWireType'](HEAP8[pointer >> 0]); + } + : function (pointer) { + return this['fromWireType'](HEAPU8[pointer >> 0]); + }; + + case 2: + return signed + ? function (pointer) { + return this['fromWireType'](HEAP16[pointer >> 1]); + } + : function (pointer) { + return this['fromWireType'](HEAPU16[pointer >> 1]); + }; + + case 4: + return signed + ? function (pointer) { + return this['fromWireType'](HEAP32[pointer >> 2]); + } + : function (pointer) { + return this['fromWireType'](HEAPU32[pointer >> 2]); + }; + + default: + throw new TypeError(`invalid integer width (${width}): ${name}`); + } + }; + + /** @suppress {globalThis} */ var __embind_register_enum = (rawType, name, size, isSigned) => { + name = readLatin1String(name); + function ctor() {} + ctor.values = {}; + registerType(rawType, { + name: name, + constructor: ctor, + fromWireType: function (c) { + return this.constructor.values[c]; + }, + toWireType: (destructors, c) => c.value, + argPackAdvance: GenericWireTypeSize, + readValueFromPointer: enumReadValueFromPointer(name, size, isSigned), + destructorFunction: null, + }); + exposePublicSymbol(name, ctor); + }; + + var requireRegisteredType = (rawType, humanName) => { + var impl = registeredTypes[rawType]; + if (undefined === impl) { + throwBindingError(humanName + ' has unknown type ' + getTypeName(rawType)); + } + return impl; }; - var __embind_register_emval = (rawType) => registerType(rawType, EmValType); + var __embind_register_enum_value = (rawEnumType, name, enumValue) => { + var enumType = requireRegisteredType(rawEnumType, 'enum'); + name = readLatin1String(name); + var Enum = enumType.constructor; + var Value = Object.create(enumType.constructor.prototype, { + value: { + value: enumValue, + }, + constructor: { + value: createNamedFunction(`${enumType.name}_${name}`, function () {}), + }, + }); + Enum.values[enumValue] = Value; + Enum[name] = Value; + }; var embindRepr = (v) => { if (v === null) { @@ -1740,58 +2069,42 @@ var createRendererModule = (() => { ); }; - var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { - if (!(maxBytesToWrite > 0)) return 0; - var startIdx = outIdx; - var endIdx = outIdx + maxBytesToWrite - 1; - for (var i = 0; i < str.length; ++i) { - var u = str.charCodeAt(i); - if (u >= 55296 && u <= 57343) { - var u1 = str.charCodeAt(++i); - u = (65536 + ((u & 1023) << 10)) | (u1 & 1023); - } - if (u <= 127) { - if (outIdx >= endIdx) break; - heap[outIdx++] = u; - } else if (u <= 2047) { - if (outIdx + 1 >= endIdx) break; - heap[outIdx++] = 192 | (u >> 6); - heap[outIdx++] = 128 | (u & 63); - } else if (u <= 65535) { - if (outIdx + 2 >= endIdx) break; - heap[outIdx++] = 224 | (u >> 12); - heap[outIdx++] = 128 | ((u >> 6) & 63); - heap[outIdx++] = 128 | (u & 63); - } else { - if (outIdx + 3 >= endIdx) break; - heap[outIdx++] = 240 | (u >> 18); - heap[outIdx++] = 128 | ((u >> 12) & 63); - heap[outIdx++] = 128 | ((u >> 6) & 63); - heap[outIdx++] = 128 | (u & 63); - } - } - heap[outIdx] = 0; - return outIdx - startIdx; - }; - - var stringToUTF8 = (str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); - - var lengthBytesUTF8 = (str) => { - var len = 0; - for (var i = 0; i < str.length; ++i) { - var c = str.charCodeAt(i); - if (c <= 127) { - len++; - } else if (c <= 2047) { - len += 2; - } else if (c >= 55296 && c <= 57343) { - len += 4; - ++i; - } else { - len += 3; - } - } - return len; + var __embind_register_smart_ptr = ( + rawType, + rawPointeeType, + name, + sharingPolicy, + getPointeeSignature, + rawGetPointee, + constructorSignature, + rawConstructor, + shareSignature, + rawShare, + destructorSignature, + rawDestructor, + ) => { + name = readLatin1String(name); + rawGetPointee = embind__requireFunction(getPointeeSignature, rawGetPointee); + rawConstructor = embind__requireFunction(constructorSignature, rawConstructor); + rawShare = embind__requireFunction(shareSignature, rawShare); + rawDestructor = embind__requireFunction(destructorSignature, rawDestructor); + whenDependentTypesAreResolved([rawType], [rawPointeeType], function (pointeeType) { + pointeeType = pointeeType[0]; + var registeredPointer = new RegisteredPointer( + name, + pointeeType.registeredClass, + false, + false, + true, + pointeeType, + sharingPolicy, + rawGetPointee, + rawConstructor, + rawShare, + rawDestructor, + ); + return [registeredPointer]; + }); }; var __embind_register_std_string = (rawType, name) => { @@ -2026,6 +2339,45 @@ var createRendererModule = (() => { }); }; + var __embind_register_value_object = ( + rawType, + name, + constructorSignature, + rawConstructor, + destructorSignature, + rawDestructor, + ) => { + structRegistrations[rawType] = { + name: readLatin1String(name), + rawConstructor: embind__requireFunction(constructorSignature, rawConstructor), + rawDestructor: embind__requireFunction(destructorSignature, rawDestructor), + fields: [], + }; + }; + + var __embind_register_value_object_field = ( + structType, + fieldName, + getterReturnType, + getterSignature, + getter, + getterContext, + setterArgumentType, + setterSignature, + setter, + setterContext, + ) => { + structRegistrations[structType].fields.push({ + fieldName: readLatin1String(fieldName), + getterReturnType: getterReturnType, + getter: embind__requireFunction(getterSignature, getter), + getterContext: getterContext, + setterArgumentType: setterArgumentType, + setter: embind__requireFunction(setterSignature, setter), + setterContext: setterContext, + }); + }; + var __embind_register_void = (rawType, name) => { name = readLatin1String(name); registerType(rawType, { @@ -2041,18 +2393,70 @@ var createRendererModule = (() => { throw Infinity; }; + var emval_methodCallers = []; + + var __emval_call = (caller, handle, destructorsRef, args) => { + caller = emval_methodCallers[caller]; + handle = Emval.toValue(handle); + return caller(null, handle, destructorsRef, args); + }; + + var emval_addMethodCaller = (caller) => { + var id = emval_methodCallers.length; + emval_methodCallers.push(caller); + return id; + }; + + var emval_lookupTypes = (argCount, argTypes) => { + var a = new Array(argCount); + for (var i = 0; i < argCount; ++i) { + a[i] = requireRegisteredType(HEAPU32[(argTypes + i * 4) >> 2], 'parameter ' + i); + } + return a; + }; + + var reflectConstruct = Reflect.construct; + + var emval_returnValue = (returnType, destructorsRef, handle) => { + var destructors = []; + var result = returnType['toWireType'](destructors, handle); + if (destructors.length) { + HEAPU32[destructorsRef >> 2] = Emval.toHandle(destructors); + } + return result; + }; + + var __emval_get_method_caller = (argCount, argTypes, kind) => { + var types = emval_lookupTypes(argCount, argTypes); + var retType = types.shift(); + argCount--; + var argN = new Array(argCount); + var invokerFunction = (obj, func, destructorsRef, args) => { + var offset = 0; + for (var i = 0; i < argCount; ++i) { + argN[i] = types[i]['readValueFromPointer'](args + offset); + offset += types[i]['argPackAdvance']; + } + var rv = kind === /* CONSTRUCTOR */ 1 ? reflectConstruct(func, argN) : func.apply(obj, argN); + for (var i = 0; i < argCount; ++i) { + types[i].deleteObject?.(argN[i]); + } + return emval_returnValue(retType, destructorsRef, rv); + }; + var functionName = `methodCaller<(${types.map((t) => t.name).join(', ')}) => ${retType.name}>`; + return emval_addMethodCaller(createNamedFunction(functionName, invokerFunction)); + }; + var __emval_incref = (handle) => { if (handle > 4) { emval_handles.get(handle).refcount += 1; } }; - var requireRegisteredType = (rawType, humanName) => { - var impl = registeredTypes[rawType]; - if (undefined === impl) { - throwBindingError(humanName + ' has unknown type ' + getTypeName(rawType)); - } - return impl; + var __emval_run_destructors = (handle) => { + var destructors = Emval.toValue(handle); + runDestructors(destructors); + __emval_decref(handle); }; var __emval_take_value = (type, arg) => { @@ -2065,6 +2469,10 @@ var createRendererModule = (() => { abort(''); }; + var _emscripten_get_now; + + _emscripten_get_now = () => performance.now(); + var _emscripten_memcpy_js = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num); var getHeapMax = () => 2147483648; @@ -2099,6 +2507,64 @@ var createRendererModule = (() => { return false; }; + var ENV = {}; + + var getExecutableName = () => thisProgram || './this.program'; + + var getEnvStrings = () => { + if (!getEnvStrings.strings) { + var lang = + ((typeof navigator == 'object' && navigator.languages && navigator.languages[0]) || 'C').replace('-', '_') + + '.UTF-8'; + var env = { + USER: 'web_user', + LOGNAME: 'web_user', + PATH: '/', + PWD: '/', + HOME: '/home/web_user', + LANG: lang, + _: getExecutableName(), + }; + for (var x in ENV) { + if (ENV[x] === undefined) delete env[x]; + else env[x] = ENV[x]; + } + var strings = []; + for (var x in env) { + strings.push(`${x}=${env[x]}`); + } + getEnvStrings.strings = strings; + } + return getEnvStrings.strings; + }; + + var stringToAscii = (str, buffer) => { + for (var i = 0; i < str.length; ++i) { + HEAP8[buffer++ >> 0] = str.charCodeAt(i); + } + HEAP8[buffer >> 0] = 0; + }; + + var _environ_get = (__environ, environ_buf) => { + var bufSize = 0; + getEnvStrings().forEach((string, i) => { + var ptr = environ_buf + bufSize; + HEAPU32[(__environ + i * 4) >> 2] = ptr; + stringToAscii(string, ptr); + bufSize += string.length + 1; + }); + return 0; + }; + + var _environ_sizes_get = (penviron_count, penviron_buf_size) => { + var strings = getEnvStrings(); + HEAPU32[penviron_count >> 2] = strings.length; + var bufSize = 0; + strings.forEach((string) => (bufSize += string.length + 1)); + HEAPU32[penviron_buf_size >> 2] = bufSize; + return 0; + }; + var _fd_close = (fd) => 52; var _fd_read = (fd, iov, iovcnt, pnum) => 52; @@ -2138,15 +2604,285 @@ var createRendererModule = (() => { return 0; }; - embind_init_charCodes(); + var initRandomFill = () => { + if (typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function') { + return (view) => crypto.getRandomValues(view); + } else abort('initRandomDevice'); + }; - BindingError = Module['BindingError'] = class BindingError extends Error { - constructor(message) { - super(message); - this.name = 'BindingError'; + var randomFill = (view) => (randomFill = initRandomFill())(view); + + var _getentropy = (buffer, size) => { + randomFill(HEAPU8.subarray(buffer, buffer + size)); + return 0; + }; + + var _llvm_eh_typeid_for = (type) => type; + + var isLeapYear = (year) => year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); + + var arraySum = (array, index) => { + var sum = 0; + for (var i = 0; i <= index; sum += array[i++]) {} + return sum; + }; + + var MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + var MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + var addDays = (date, days) => { + var newDate = new Date(date.getTime()); + while (days > 0) { + var leap = isLeapYear(newDate.getFullYear()); + var currentMonth = newDate.getMonth(); + var daysInCurrentMonth = (leap ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR)[currentMonth]; + if (days > daysInCurrentMonth - newDate.getDate()) { + days -= daysInCurrentMonth - newDate.getDate() + 1; + newDate.setDate(1); + if (currentMonth < 11) { + newDate.setMonth(currentMonth + 1); + } else { + newDate.setMonth(0); + newDate.setFullYear(newDate.getFullYear() + 1); + } + } else { + newDate.setDate(newDate.getDate() + days); + return newDate; + } } + return newDate; }; + /** @type {function(string, boolean=, number=)} */ function intArrayFromString(stringy, dontAddNull, length) { + var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1; + var u8array = new Array(len); + var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); + if (dontAddNull) u8array.length = numBytesWritten; + return u8array; + } + + var writeArrayToMemory = (array, buffer) => { + HEAP8.set(array, buffer); + }; + + var _strftime = (s, maxsize, format, tm) => { + var tm_zone = HEAPU32[(tm + 40) >> 2]; + var date = { + tm_sec: HEAP32[tm >> 2], + tm_min: HEAP32[(tm + 4) >> 2], + tm_hour: HEAP32[(tm + 8) >> 2], + tm_mday: HEAP32[(tm + 12) >> 2], + tm_mon: HEAP32[(tm + 16) >> 2], + tm_year: HEAP32[(tm + 20) >> 2], + tm_wday: HEAP32[(tm + 24) >> 2], + tm_yday: HEAP32[(tm + 28) >> 2], + tm_isdst: HEAP32[(tm + 32) >> 2], + tm_gmtoff: HEAP32[(tm + 36) >> 2], + tm_zone: tm_zone ? UTF8ToString(tm_zone) : '', + }; + var pattern = UTF8ToString(format); + var EXPANSION_RULES_1 = { + '%c': '%a %b %d %H:%M:%S %Y', + '%D': '%m/%d/%y', + '%F': '%Y-%m-%d', + '%h': '%b', + '%r': '%I:%M:%S %p', + '%R': '%H:%M', + '%T': '%H:%M:%S', + '%x': '%m/%d/%y', + '%X': '%H:%M:%S', + '%Ec': '%c', + '%EC': '%C', + '%Ex': '%m/%d/%y', + '%EX': '%H:%M:%S', + '%Ey': '%y', + '%EY': '%Y', + '%Od': '%d', + '%Oe': '%e', + '%OH': '%H', + '%OI': '%I', + '%Om': '%m', + '%OM': '%M', + '%OS': '%S', + '%Ou': '%u', + '%OU': '%U', + '%OV': '%V', + '%Ow': '%w', + '%OW': '%W', + '%Oy': '%y', + }; + for (var rule in EXPANSION_RULES_1) { + pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]); + } + var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + var MONTHS = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', + ]; + function leadingSomething(value, digits, character) { + var str = typeof value == 'number' ? value.toString() : value || ''; + while (str.length < digits) { + str = character[0] + str; + } + return str; + } + function leadingNulls(value, digits) { + return leadingSomething(value, digits, '0'); + } + function compareByDay(date1, date2) { + function sgn(value) { + return value < 0 ? -1 : value > 0 ? 1 : 0; + } + var compare; + if ((compare = sgn(date1.getFullYear() - date2.getFullYear())) === 0) { + if ((compare = sgn(date1.getMonth() - date2.getMonth())) === 0) { + compare = sgn(date1.getDate() - date2.getDate()); + } + } + return compare; + } + function getFirstWeekStartDate(janFourth) { + switch (janFourth.getDay()) { + case 0: + return new Date(janFourth.getFullYear() - 1, 11, 29); + + case 1: + return janFourth; + + case 2: + return new Date(janFourth.getFullYear(), 0, 3); + + case 3: + return new Date(janFourth.getFullYear(), 0, 2); + + case 4: + return new Date(janFourth.getFullYear(), 0, 1); + + case 5: + return new Date(janFourth.getFullYear() - 1, 11, 31); + + case 6: + return new Date(janFourth.getFullYear() - 1, 11, 30); + } + } + function getWeekBasedYear(date) { + var thisDate = addDays(new Date(date.tm_year + 1900, 0, 1), date.tm_yday); + var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4); + var janFourthNextYear = new Date(thisDate.getFullYear() + 1, 0, 4); + var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); + var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); + if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { + if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { + return thisDate.getFullYear() + 1; + } + return thisDate.getFullYear(); + } + return thisDate.getFullYear() - 1; + } + var EXPANSION_RULES_2 = { + '%a': (date) => WEEKDAYS[date.tm_wday].substring(0, 3), + '%A': (date) => WEEKDAYS[date.tm_wday], + '%b': (date) => MONTHS[date.tm_mon].substring(0, 3), + '%B': (date) => MONTHS[date.tm_mon], + '%C': (date) => { + var year = date.tm_year + 1900; + return leadingNulls((year / 100) | 0, 2); + }, + '%d': (date) => leadingNulls(date.tm_mday, 2), + '%e': (date) => leadingSomething(date.tm_mday, 2, ' '), + '%g': (date) => getWeekBasedYear(date).toString().substring(2), + '%G': (date) => getWeekBasedYear(date), + '%H': (date) => leadingNulls(date.tm_hour, 2), + '%I': (date) => { + var twelveHour = date.tm_hour; + if (twelveHour == 0) twelveHour = 12; + else if (twelveHour > 12) twelveHour -= 12; + return leadingNulls(twelveHour, 2); + }, + '%j': (date) => + leadingNulls( + date.tm_mday + + arraySum(isLeapYear(date.tm_year + 1900) ? MONTH_DAYS_LEAP : MONTH_DAYS_REGULAR, date.tm_mon - 1), + 3, + ), + '%m': (date) => leadingNulls(date.tm_mon + 1, 2), + '%M': (date) => leadingNulls(date.tm_min, 2), + '%n': () => '\n', + '%p': (date) => { + if (date.tm_hour >= 0 && date.tm_hour < 12) { + return 'AM'; + } + return 'PM'; + }, + '%S': (date) => leadingNulls(date.tm_sec, 2), + '%t': () => '\t', + '%u': (date) => date.tm_wday || 7, + '%U': (date) => { + var days = date.tm_yday + 7 - date.tm_wday; + return leadingNulls(Math.floor(days / 7), 2); + }, + '%V': (date) => { + var val = Math.floor((date.tm_yday + 7 - ((date.tm_wday + 6) % 7)) / 7); + if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { + val++; + } + if (!val) { + val = 52; + var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7; + if (dec31 == 4 || (dec31 == 5 && isLeapYear((date.tm_year % 400) - 1))) { + val++; + } + } else if (val == 53) { + var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7; + if (jan1 != 4 && (jan1 != 3 || !isLeapYear(date.tm_year))) val = 1; + } + return leadingNulls(val, 2); + }, + '%w': (date) => date.tm_wday, + '%W': (date) => { + var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7); + return leadingNulls(Math.floor(days / 7), 2); + }, + '%y': (date) => (date.tm_year + 1900).toString().substring(2), + '%Y': (date) => date.tm_year + 1900, + '%z': (date) => { + var off = date.tm_gmtoff; + var ahead = off >= 0; + off = Math.abs(off) / 60; + off = (off / 60) * 100 + (off % 60); + return (ahead ? '+' : '-') + String('0000' + off).slice(-4); + }, + '%Z': (date) => date.tm_zone, + '%%': () => '%', + }; + pattern = pattern.replace(/%%/g, '\0\0'); + for (var rule in EXPANSION_RULES_2) { + if (pattern.includes(rule)) { + pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date)); + } + } + pattern = pattern.replace(/\0\0/g, '%'); + var bytes = intArrayFromString(pattern, false); + if (bytes.length > maxsize) { + return 0; + } + writeArrayToMemory(bytes, s); + return bytes.length - 1; + }; + + var _strftime_l = (s, maxsize, format, tm, loc) => _strftime(s, maxsize, format, tm); + InternalError = Module['InternalError'] = class InternalError extends Error { constructor(message) { super(message); @@ -2154,6 +2890,15 @@ var createRendererModule = (() => { } }; + embind_init_charCodes(); + + BindingError = Module['BindingError'] = class BindingError extends Error { + constructor(message) { + super(message); + this.name = 'BindingError'; + } + }; + init_ClassHandle(); init_embind(); @@ -2165,68 +2910,169 @@ var createRendererModule = (() => { init_emval(); var wasmImports = { - /** @export */ b: ___assert_fail, - /** @export */ n: ___syscall_fcntl64, - /** @export */ D: ___syscall_ioctl, - /** @export */ E: ___syscall_openat, - /** @export */ w: __embind_register_bigint, - /** @export */ q: __embind_register_bool, - /** @export */ H: __embind_register_class, - /** @export */ G: __embind_register_class_constructor, - /** @export */ e: __embind_register_class_function, - /** @export */ F: __embind_register_emval, - /** @export */ o: __embind_register_float, - /** @export */ f: __embind_register_integer, - /** @export */ d: __embind_register_memory_view, - /** @export */ p: __embind_register_std_string, - /** @export */ k: __embind_register_std_wstring, - /** @export */ r: __embind_register_void, - /** @export */ y: __emscripten_throw_longjmp, - /** @export */ s: __emval_decref, - /** @export */ t: __emval_incref, - /** @export */ g: __emval_take_value, - /** @export */ h: _abort, - /** @export */ A: _emscripten_memcpy_js, - /** @export */ z: _emscripten_resize_heap, - /** @export */ m: _fd_close, - /** @export */ C: _fd_read, - /** @export */ v: _fd_seek, - /** @export */ B: _fd_write, - /** @export */ j: invoke_ii, - /** @export */ i: invoke_iiii, - /** @export */ l: invoke_iiiiii, - /** @export */ a: invoke_vi, + /** @export */ m: ___assert_fail, + /** @export */ qa: ___cxa_begin_catch, + /** @export */ pa: ___cxa_end_catch, + /** @export */ a: ___cxa_find_matching_catch_2, + /** @export */ f: ___cxa_find_matching_catch_4, + /** @export */ v: ___cxa_throw, + /** @export */ d: ___resumeException, + /** @export */ I: ___syscall_fcntl64, + /** @export */ ea: ___syscall_getcwd, + /** @export */ ga: ___syscall_ioctl, + /** @export */ ha: ___syscall_openat, + /** @export */ za: __embind_finalize_value_object, + /** @export */ T: __embind_register_bigint, + /** @export */ na: __embind_register_bool, + /** @export */ P: __embind_register_class, + /** @export */ O: __embind_register_class_constructor, + /** @export */ q: __embind_register_class_function, + /** @export */ la: __embind_register_emval, + /** @export */ $: __embind_register_enum, + /** @export */ z: __embind_register_enum_value, + /** @export */ K: __embind_register_float, + /** @export */ s: __embind_register_integer, + /** @export */ p: __embind_register_memory_view, + /** @export */ ya: __embind_register_smart_ptr, + /** @export */ L: __embind_register_std_string, + /** @export */ F: __embind_register_std_wstring, + /** @export */ Q: __embind_register_value_object, + /** @export */ y: __embind_register_value_object_field, + /** @export */ oa: __embind_register_void, + /** @export */ ba: __emscripten_throw_longjmp, + /** @export */ xa: __emval_call, + /** @export */ ma: __emval_decref, + /** @export */ wa: __emval_get_method_caller, + /** @export */ G: __emval_incref, + /** @export */ va: __emval_run_destructors, + /** @export */ E: __emval_take_value, + /** @export */ N: _abort, + /** @export */ w: _emscripten_get_now, + /** @export */ ia: _emscripten_memcpy_js, + /** @export */ da: _emscripten_resize_heap, + /** @export */ ja: _environ_get, + /** @export */ ka: _environ_sizes_get, + /** @export */ J: _fd_close, + /** @export */ fa: _fd_read, + /** @export */ S: _fd_seek, + /** @export */ D: _fd_write, + /** @export */ ra: _getentropy, + /** @export */ u: invoke_d, + /** @export */ B: invoke_fi, + /** @export */ H: invoke_i, + /** @export */ h: invoke_ii, + /** @export */ A: invoke_iif, + /** @export */ g: invoke_iii, + /** @export */ k: invoke_iiii, + /** @export */ o: invoke_iiiii, + /** @export */ r: invoke_iiiiii, + /** @export */ ua: invoke_iiiiiii, + /** @export */ M: invoke_iiiiiiii, + /** @export */ aa: invoke_ji, + /** @export */ Y: invoke_jii, + /** @export */ l: invoke_v, + /** @export */ b: invoke_vi, + /** @export */ t: invoke_vid, + /** @export */ x: invoke_vif, /** @export */ c: invoke_vii, - /** @export */ x: invoke_viiii, - /** @export */ u: invoke_viiij, + /** @export */ ta: invoke_viif, + /** @export */ sa: invoke_viiff, + /** @export */ e: invoke_viii, + /** @export */ n: invoke_viiii, + /** @export */ i: invoke_viiiii, + /** @export */ C: invoke_viiiiii, + /** @export */ R: invoke_viiij, + /** @export */ V: invoke_viiiji, + /** @export */ W: invoke_viij, + /** @export */ U: invoke_viiji, + /** @export */ X: invoke_viijj, + /** @export */ Z: invoke_vij, + /** @export */ _: invoke_vijiji, + /** @export */ j: _llvm_eh_typeid_for, + /** @export */ ca: _strftime_l, }; var wasmExports = createWasm(); - var ___wasm_call_ctors = () => (___wasm_call_ctors = wasmExports['J'])(); + var ___wasm_call_ctors = () => (___wasm_call_ctors = wasmExports['Ba'])(); + + var _malloc = (a0) => (_malloc = wasmExports['Ca'])(a0); - var _free = (a0) => (_free = wasmExports['K'])(a0); + var _free = (a0) => (_free = wasmExports['Ea'])(a0); - var _malloc = (a0) => (_malloc = wasmExports['L'])(a0); + var stackSave = () => (stackSave = wasmExports['P'])(); - var ___getTypeName = (a0) => (___getTypeName = wasmExports['M'])(a0); + var ___getTypeName = (a0) => (___getTypeName = wasmExports['Fa'])(a0); - var _setThrew = (a0, a1) => (_setThrew = wasmExports['O'])(a0, a1); + var _htonl = (a0) => (_htonl = wasmExports['htonl'])(a0); - var stackSave = () => (stackSave = wasmExports['P'])(); + var _htons = (a0) => (_htons = wasmExports['htons'])(a0); + + var _ntohs = (a0) => (_ntohs = wasmExports['ntohs'])(a0); + + var _setThrew = (a0, a1) => (_setThrew = wasmExports['Ga'])(a0, a1); + + var setTempRet0 = (a0) => (setTempRet0 = wasmExports['Ha'])(a0); + + var stackSave = () => (stackSave = wasmExports['Ia'])(); + + var stackRestore = (a0) => (stackRestore = wasmExports['Ja'])(a0); + + var ___cxa_decrement_exception_refcount = (a0) => (___cxa_decrement_exception_refcount = wasmExports['Ka'])(a0); + + var ___cxa_increment_exception_refcount = (a0) => (___cxa_increment_exception_refcount = wasmExports['La'])(a0); + + var ___cxa_can_catch = (a0, a1, a2) => (___cxa_can_catch = wasmExports['Ma'])(a0, a1, a2); + + var ___cxa_is_pointer_type = (a0) => (___cxa_is_pointer_type = wasmExports['Na'])(a0); + + var dynCall_iijj = (Module['dynCall_iijj'] = (a0, a1, a2, a3, a4, a5) => + (dynCall_iijj = Module['dynCall_iijj'] = wasmExports['Oa'])(a0, a1, a2, a3, a4, a5)); + + var dynCall_vijj = (Module['dynCall_vijj'] = (a0, a1, a2, a3, a4, a5) => + (dynCall_vijj = Module['dynCall_vijj'] = wasmExports['Pa'])(a0, a1, a2, a3, a4, a5)); + + var dynCall_ji = (Module['dynCall_ji'] = (a0, a1) => + (dynCall_ji = Module['dynCall_ji'] = wasmExports['Qa'])(a0, a1)); + + var dynCall_vijiji = (Module['dynCall_vijiji'] = (a0, a1, a2, a3, a4, a5, a6, a7) => + (dynCall_vijiji = Module['dynCall_vijiji'] = wasmExports['Ra'])(a0, a1, a2, a3, a4, a5, a6, a7)); + + var dynCall_vij = (Module['dynCall_vij'] = (a0, a1, a2, a3) => + (dynCall_vij = Module['dynCall_vij'] = wasmExports['Sa'])(a0, a1, a2, a3)); - var stackRestore = (a0) => (stackRestore = wasmExports['Q'])(a0); + var dynCall_jii = (Module['dynCall_jii'] = (a0, a1, a2) => + (dynCall_jii = Module['dynCall_jii'] = wasmExports['Ta'])(a0, a1, a2)); - var ___cxa_increment_exception_refcount = (a0) => - (___cxa_increment_exception_refcount = wasmExports['__cxa_increment_exception_refcount'])(a0); + var dynCall_viijj = (Module['dynCall_viijj'] = (a0, a1, a2, a3, a4, a5, a6) => + (dynCall_viijj = Module['dynCall_viijj'] = wasmExports['Ua'])(a0, a1, a2, a3, a4, a5, a6)); - var ___cxa_is_pointer_type = (a0) => (___cxa_is_pointer_type = wasmExports['__cxa_is_pointer_type'])(a0); + var dynCall_viij = (Module['dynCall_viij'] = (a0, a1, a2, a3, a4) => + (dynCall_viij = Module['dynCall_viij'] = wasmExports['Va'])(a0, a1, a2, a3, a4)); + + var dynCall_viiiji = (Module['dynCall_viiiji'] = (a0, a1, a2, a3, a4, a5, a6) => + (dynCall_viiiji = Module['dynCall_viiiji'] = wasmExports['Wa'])(a0, a1, a2, a3, a4, a5, a6)); + + var dynCall_viiji = (Module['dynCall_viiji'] = (a0, a1, a2, a3, a4, a5) => + (dynCall_viiji = Module['dynCall_viiji'] = wasmExports['Xa'])(a0, a1, a2, a3, a4, a5)); var dynCall_viiij = (Module['dynCall_viiij'] = (a0, a1, a2, a3, a4, a5) => - (dynCall_viiij = Module['dynCall_viiij'] = wasmExports['R'])(a0, a1, a2, a3, a4, a5)); + (dynCall_viiij = Module['dynCall_viiij'] = wasmExports['Ya'])(a0, a1, a2, a3, a4, a5)); var dynCall_jiji = (Module['dynCall_jiji'] = (a0, a1, a2, a3, a4) => - (dynCall_jiji = Module['dynCall_jiji'] = wasmExports['S'])(a0, a1, a2, a3, a4)); + (dynCall_jiji = Module['dynCall_jiji'] = wasmExports['Za'])(a0, a1, a2, a3, a4)); + + var dynCall_viijii = (Module['dynCall_viijii'] = (a0, a1, a2, a3, a4, a5, a6) => + (dynCall_viijii = Module['dynCall_viijii'] = wasmExports['_a'])(a0, a1, a2, a3, a4, a5, a6)); + + var dynCall_iiiiij = (Module['dynCall_iiiiij'] = (a0, a1, a2, a3, a4, a5, a6) => + (dynCall_iiiiij = Module['dynCall_iiiiij'] = wasmExports['$a'])(a0, a1, a2, a3, a4, a5, a6)); + + var dynCall_iiiiijj = (Module['dynCall_iiiiijj'] = (a0, a1, a2, a3, a4, a5, a6, a7, a8) => + (dynCall_iiiiijj = Module['dynCall_iiiiijj'] = wasmExports['ab'])(a0, a1, a2, a3, a4, a5, a6, a7, a8)); + + var dynCall_iiiiiijj = (Module['dynCall_iiiiiijj'] = (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) => + (dynCall_iiiiiijj = Module['dynCall_iiiiiijj'] = wasmExports['bb'])(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)); function invoke_vi(index, a1) { var sp = stackSave(); @@ -2250,10 +3096,10 @@ var createRendererModule = (() => { } } - function invoke_ii(index, a1) { + function invoke_viii(index, a1, a2, a3) { var sp = stackSave(); try { - return getWasmTableEntry(index)(a1); + getWasmTableEntry(index)(a1, a2, a3); } catch (e) { stackRestore(sp); if (e !== e + 0) throw e; @@ -2272,6 +3118,83 @@ var createRendererModule = (() => { } } + function invoke_ii(index, a1) { + var sp = stackSave(); + try { + return getWasmTableEntry(index)(a1); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_viiiii(index, a1, a2, a3, a4, a5) { + var sp = stackSave(); + try { + getWasmTableEntry(index)(a1, a2, a3, a4, a5); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_i(index) { + var sp = stackSave(); + try { + return getWasmTableEntry(index)(); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_v(index) { + var sp = stackSave(); + try { + getWasmTableEntry(index)(); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_viiiiii(index, a1, a2, a3, a4, a5, a6) { + var sp = stackSave(); + try { + getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_fi(index, a1) { + var sp = stackSave(); + try { + return getWasmTableEntry(index)(a1); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_iif(index, a1, a2) { + var sp = stackSave(); + try { + return getWasmTableEntry(index)(a1, a2); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + function invoke_iiiiii(index, a1, a2, a3, a4, a5) { var sp = stackSave(); try { @@ -2283,6 +3206,50 @@ var createRendererModule = (() => { } } + function invoke_iiiii(index, a1, a2, a3, a4) { + var sp = stackSave(); + try { + return getWasmTableEntry(index)(a1, a2, a3, a4); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_iii(index, a1, a2) { + var sp = stackSave(); + try { + return getWasmTableEntry(index)(a1, a2); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_d(index) { + var sp = stackSave(); + try { + return getWasmTableEntry(index)(); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_vid(index, a1, a2) { + var sp = stackSave(); + try { + getWasmTableEntry(index)(a1, a2); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + function invoke_viiii(index, a1, a2, a3, a4) { var sp = stackSave(); try { @@ -2294,6 +3261,149 @@ var createRendererModule = (() => { } } + function invoke_vif(index, a1, a2) { + var sp = stackSave(); + try { + getWasmTableEntry(index)(a1, a2); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_iiiiiii(index, a1, a2, a3, a4, a5, a6) { + var sp = stackSave(); + try { + return getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_viif(index, a1, a2, a3) { + var sp = stackSave(); + try { + getWasmTableEntry(index)(a1, a2, a3); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_viiff(index, a1, a2, a3, a4) { + var sp = stackSave(); + try { + getWasmTableEntry(index)(a1, a2, a3, a4); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_iiiiiiii(index, a1, a2, a3, a4, a5, a6, a7) { + var sp = stackSave(); + try { + return getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6, a7); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_ji(index, a1) { + var sp = stackSave(); + try { + return dynCall_ji(index, a1); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_vijiji(index, a1, a2, a3, a4, a5, a6, a7) { + var sp = stackSave(); + try { + dynCall_vijiji(index, a1, a2, a3, a4, a5, a6, a7); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_vij(index, a1, a2, a3) { + var sp = stackSave(); + try { + dynCall_vij(index, a1, a2, a3); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_jii(index, a1, a2) { + var sp = stackSave(); + try { + return dynCall_jii(index, a1, a2); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_viijj(index, a1, a2, a3, a4, a5, a6) { + var sp = stackSave(); + try { + dynCall_viijj(index, a1, a2, a3, a4, a5, a6); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_viij(index, a1, a2, a3, a4) { + var sp = stackSave(); + try { + dynCall_viij(index, a1, a2, a3, a4); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_viiiji(index, a1, a2, a3, a4, a5, a6) { + var sp = stackSave(); + try { + dynCall_viiiji(index, a1, a2, a3, a4, a5, a6); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + + function invoke_viiji(index, a1, a2, a3, a4, a5) { + var sp = stackSave(); + try { + dynCall_viiji(index, a1, a2, a3, a4, a5); + } catch (e) { + stackRestore(sp); + if (e !== e + 0) throw e; + _setThrew(1, 0); + } + } + function invoke_viiij(index, a1, a2, a3, a4, a5) { var sp = stackSave(); try { @@ -2355,4 +3465,4 @@ var createRendererModule = (() => { return moduleArg.ready; }; })(); -export default createRendererModule; +export default createDotLottiePlayerModule; diff --git a/packages/web/src/core/dotlottie-player.types.ts b/packages/web/src/core/dotlottie-player.types.ts new file mode 100644 index 00000000..cfcc0ab6 --- /dev/null +++ b/packages/web/src/core/dotlottie-player.types.ts @@ -0,0 +1,75 @@ +/** + * Copyright 2024 Design Barn Inc. + */ + +export interface VectorFloat { + delete(): void; + get(_0: number): unknown; + // eslint-disable-next-line @typescript-eslint/naming-convention + push_back(_0: number): void; + resize(_0: number, _1: number): void; + set(_0: number, _1: number): boolean; + size(): number; +} + +export interface ModeValue { + value: T; +} +export type Mode = ModeValue<1> | ModeValue<2> | ModeValue<3> | ModeValue<4>; + +export interface DotLottiePlayer { + buffer(): unknown; + clear(): void; + config(): Config; + currentFrame(): number; + delete(): void; + duration(): number; + isComplete(): boolean; + isLoaded(): boolean; + isPaused(): boolean; + isPlaying(): boolean; + isStopped(): boolean; + loadAnimation(_0: ArrayBuffer | Uint8Array | Uint8ClampedArray | Int8Array | string, _1: number, _2: number): boolean; + loadAnimationData( + _0: ArrayBuffer | Uint8Array | Uint8ClampedArray | Int8Array | string, + _1: number, + _2: number, + ): boolean; + loadAnimationPath( + _0: ArrayBuffer | Uint8Array | Uint8ClampedArray | Int8Array | string, + _1: number, + _2: number, + ): boolean; + loadDotLottieData( + _0: ArrayBuffer | Uint8Array | Uint8ClampedArray | Int8Array | string, + _1: number, + _2: number, + ): boolean; + loopCount(): number; + manifestString(): string; + pause(): boolean; + play(): boolean; + render(): boolean; + requestFrame(): number; + resize(_0: number, _1: number): boolean; + setConfig(_0: Config): void; + setFrame(_0: number): boolean; + stop(): boolean; + totalFrames(): number; +} + +export interface Config { + autoplay: boolean; + backgroundColor: number; + loopAnimation: boolean; + mode: Mode; + segments: VectorFloat; + speed: number; + useFrameInterpolation: boolean; +} + +export interface MainModule { + DotLottiePlayer: { new (_0: Config): DotLottiePlayer }; + Mode: { Bounce: ModeValue<3>; Forward: ModeValue<1>; Reverse: ModeValue<2>; ReverseBounce: ModeValue<4> }; + VectorFloat: { new (): VectorFloat }; +} diff --git a/packages/web/src/core/dotlottie-player.wasm b/packages/web/src/core/dotlottie-player.wasm new file mode 100755 index 00000000..4bd00f39 Binary files /dev/null and b/packages/web/src/core/dotlottie-player.wasm differ diff --git a/packages/web/src/renderer-loader.ts b/packages/web/src/core/dotlottie-wasm-loader.ts similarity index 71% rename from packages/web/src/renderer-loader.ts rename to packages/web/src/core/dotlottie-wasm-loader.ts index 502f5591..a497d419 100644 --- a/packages/web/src/renderer-loader.ts +++ b/packages/web/src/core/dotlottie-wasm-loader.ts @@ -1,30 +1,26 @@ /** - * Copyright 2023 Design Barn Inc. + * Copyright 2024 Design Barn Inc. */ -import pkg from '../package.json'; +import pkg from '../../package.json'; -import type { Module } from './wasm'; -import { createRendererModule } from './wasm'; +import createDotLottiePlayerModule from './dotlottie-player'; +import type { MainModule } from './dotlottie-player.types'; -/** - * RendererLoader is a utility class for loading WebAssembly modules. - * It provides methods to load modules with a primary URL and a backup URL. - */ // eslint-disable-next-line @typescript-eslint/no-extraneous-class -export class RendererLoader { +export class DotLottieWasmLoader { // eslint-disable-next-line @typescript-eslint/naming-convention - private static _ModulePromise: Promise | null = null; + private static _ModulePromise: Promise | null = null; // URL for the WASM file, constructed using package information - private static _wasmURL = `https://cdn.jsdelivr.net/npm/${pkg.name}@${pkg.version}/dist/wasm/renderer.wasm`; + private static _wasmURL = `https://cdn.jsdelivr.net/npm/${pkg.name}@${pkg.version}/dist/dotlottie-player.wasm`; private constructor() { throw new Error('RendererLoader is a static class and cannot be instantiated.'); } - private static async _tryLoad(url: string): Promise { - const module = await createRendererModule({ locateFile: () => url }); + private static async _tryLoad(url: string): Promise { + const module = await createDotLottiePlayerModule({ locateFile: () => url }); return module; } @@ -34,10 +30,10 @@ export class RendererLoader { * Throws an error if both URLs fail to load the module. * @returns Promise - A promise that resolves to the loaded module. */ - private static async _loadWithBackup(): Promise { + private static async _loadWithBackup(): Promise { if (!this._ModulePromise) { - this._ModulePromise = this._tryLoad(this._wasmURL).catch(async (initialError): Promise => { - const backupUrl = `https://unpkg.com/${pkg.name}@${pkg.version}/dist/wasm/renderer.wasm`; + this._ModulePromise = this._tryLoad(this._wasmURL).catch(async (initialError): Promise => { + const backupUrl = `https://unpkg.com/${pkg.name}@${pkg.version}/dist/dotlottie-player.wasm`; console.warn(`Trying backup URL for WASM loading: ${backupUrl}`); try { @@ -61,7 +57,7 @@ export class RendererLoader { * Utilizes a primary and backup URL for robustness. * @returns Promise - A promise that resolves to the loaded module. */ - public static async load(): Promise { + public static async load(): Promise { return this._loadWithBackup(); } diff --git a/packages/web/src/core/index.ts b/packages/web/src/core/index.ts new file mode 100644 index 00000000..6423cf4e --- /dev/null +++ b/packages/web/src/core/index.ts @@ -0,0 +1,6 @@ +/** + * Copyright 2024 Design Barn Inc. + */ + +export type * from './dotlottie-player.types'; +export * from './dotlottie-wasm-loader'; diff --git a/packages/web/src/dotlottie.ts b/packages/web/src/dotlottie.ts index 81fe682b..29df673e 100644 --- a/packages/web/src/dotlottie.ts +++ b/packages/web/src/dotlottie.ts @@ -1,902 +1,478 @@ /** - * Copyright 2023 Design Barn Inc. + * Copyright 2024 Design Barn Inc. */ -/* eslint-disable promise/prefer-await-to-then */ -/* eslint-disable @typescript-eslint/unbound-method */ - import { AnimationFrameManager } from './animation-frame-manager'; -import { IS_BROWSER, MS_TO_SEC_FACTOR, DEFAULT_BG_COLOR } from './constants'; +import { IS_BROWSER } from './constants'; +import type { DotLottiePlayer, MainModule, Mode as CoreMode, VectorFloat } from './core'; +import { DotLottieWasmLoader } from './core'; import type { EventListener, EventType } from './event-manager'; import { EventManager } from './event-manager'; -import { RendererLoader } from './renderer-loader'; -import { getAnimationJSONFromDotLottie, loadAnimationJSONFromURL, hexStringToRGBAInt } from './utils'; -import type { Renderer } from './wasm'; - -export type Mode = 'forward' | 'reverse' | 'bounce' | 'bounce-reverse'; - -type PlaybackState = 'playing' | 'paused' | 'stopped'; interface RenderConfig { - /** - * The device pixel ratio to use when resizing the canvas. - * - * Default is window.devicePixelRatio or 1. - */ devicePixelRatio?: number; } +export type Mode = 'forward' | 'reverse' | 'bounce' | 'reverse-bounce'; + export interface Config { - /** - * Boolean indicating if the animation should start playing automatically. - */ autoplay?: boolean; - /** - * Animation canvas background color. - * - * Default is #00000000. - */ backgroundColor?: string; - /** - * The canvas element to render the animation on. - */ canvas: HTMLCanvasElement; - /** - * The animation data. - * string: The JSON string of the animation data. - * ArrayBuffer: The ArrayBuffer of the .lottie file. - * - * If the data is an ArrayBuffer, the JSON string will be extracted from the .lottie file. - */ data?: string | ArrayBuffer; - /** - * Boolean indicating if the animation should loop. - */ loop?: boolean; - /** - * The playback mode of the animation. - * - * forward: The animation will play from start to end. - * reverse: The animation will play from end to start. - * bounce: The animation will play from start to end and then from end to start. - * bounce-reverse: The animation will play from end to start and then from start to end. - * - */ mode?: Mode; - /** - * The render configuration. - */ renderConfig?: RenderConfig; - /** - * The frame boundaries of the animation. - * - * The animation will only play between the given start and end frames. - * - * e.g. [0, 10] will play the first 10 frames of the animation only. - * - */ segments?: [number, number]; - /** - * The speed of the animation. - */ speed?: number; - /** - * The source URL of the animation. - * - * If the data is provided, the src will be ignored. - */ src?: string; - /** - * - * If true, it will update on every requestAnimationFrame with intermediate values. - * If false, it will respect the original AE fps. - * - * Default is true. - */ useFrameInterpolation?: boolean; } -export class DotLottie { - private readonly _canvas: HTMLCanvasElement | OffscreenCanvas; - - private _context: CanvasRenderingContext2D | null; - - private readonly _eventManager = new EventManager(); - - private _renderer: Renderer | null = null; - - private _beginTime = 0; - - private _elapsedTime = 0; - - private _totalFrames = 0; - - private _loop = false; - - private _speed = 1; +const createCoreMode = (mode: Mode, module: MainModule): CoreMode => { + if (mode === 'reverse') { + return module.Mode.Reverse; + } else if (mode === 'bounce') { + return module.Mode.Bounce; + } else if (mode === 'reverse-bounce') { + return module.Mode.ReverseBounce; + } else { + return module.Mode.Forward; + } +}; - private _currentFrame = 0; +const createCoreSegments = (segments: number[], module: MainModule): VectorFloat => { + const coreSegments = new module.VectorFloat(); - private _duration = 0; + if (segments.length !== 2) return coreSegments; - private _loopCount = 0; + coreSegments.push_back(segments[0] as number); + coreSegments.push_back(segments[1] as number); - private _autoplay = false; + return coreSegments; +}; - private _mode: Mode = 'forward'; +export class DotLottie { + private readonly _canvas: HTMLCanvasElement; - private _direction = 1; + private _context: CanvasRenderingContext2D | null; - private _bounceCount = 0; + private readonly _eventManager: EventManager; - private _animationFrameId?: number | null = null; + private _animationFrameId: number | null = null; - private _segments: [number, number] | null = null; + private readonly _frameManager: AnimationFrameManager; - private _playbackState: PlaybackState = 'stopped'; + private _dotLottieCore: DotLottiePlayer | null = null; - private _backgroundColor: string = DEFAULT_BG_COLOR; + private _wasmModule: MainModule | null = null; private _renderConfig: RenderConfig = {}; - private _isFrozen = false; + private _isFrozen: boolean = false; - private _isLoaded = false; - - private readonly _animationFrameManager = new AnimationFrameManager(); - - private _useFrameInterpolation = true; - - private _imageData: ImageData | null = null; + private _backgroundColor: string | null = null; public constructor(config: Config) { - this._animationLoop = this._animationLoop.bind(this); - this._canvas = config.canvas; this._context = this._canvas.getContext('2d'); - this._loop = config.loop ?? false; - this._speed = config.speed ?? 1; - this._autoplay = config.autoplay ?? false; - this._mode = config.mode ?? 'forward'; - this._segments = config.segments ?? null; - this._backgroundColor = config.backgroundColor ?? DEFAULT_BG_COLOR; + this._eventManager = new EventManager(); + this._frameManager = new AnimationFrameManager(); this._renderConfig = config.renderConfig ?? {}; - this._useFrameInterpolation = config.useFrameInterpolation ?? true; - this._direction = this._mode.includes('reverse') ? -1 : 1; - RendererLoader.load() + DotLottieWasmLoader.load() .then((module) => { - this._renderer = new module.Renderer(); + this._wasmModule = module; + + this._dotLottieCore = new module.DotLottiePlayer({ + autoplay: config.autoplay ?? false, + backgroundColor: 0, + loopAnimation: config.loop ?? false, + mode: createCoreMode(config.mode ?? 'forward', module), + segments: createCoreSegments(config.segments ?? [], module), + speed: config.speed ?? 1, + useFrameInterpolation: config.useFrameInterpolation ?? true, + }); - this.setBackgroundColor(this._backgroundColor); + if (config.data) { + this._loadFromData(config.data); + } else if (config.src) { + this._loadFromSrc(config.src); + } - if (config.src) { - this._loadAnimationFromURL(config.src); - } else if (config.data) { - this._initializeAnimationFromData(config.data); + if (config.backgroundColor) { + this.setBackgroundColor(config.backgroundColor); } }) .catch((error) => { this._eventManager.dispatch({ type: 'loadError', - error: error as Error, + error: new Error(`Failed to load wasm module: ${error}`), }); }); } - // #region Getters and Setters - - public get renderConfig(): RenderConfig { - return this._renderConfig; - } - - public get canvas(): HTMLCanvasElement | OffscreenCanvas { - return this._canvas; - } - - public get mode(): Mode { - return this._mode; - } - - /** - * Gets the autoplay status of the animation. - * - * @returns The autoplay status of the animation. - */ - public get autoplay(): boolean { - return this._autoplay; - } - - /** - * Gets the background color of the canvas. - * - * @returns The background color of the canvas. - */ - public get backgroundColor(): string { - return this._backgroundColor; - } - - /** - * Gets the current direction of the animation. - * - * @returns The current direction of the animation. - */ - public get direction(): number { - return this._direction; - } - - /** - * Gets the current frame number. - * - * @returns The current frame number. - */ - public get currentFrame(): number { - return this._currentFrame; - } - - /** - * Gets the duration of the animation in seconds. - * - * @returns The duration of the animation in seconds. - */ - public get duration(): number { - return this._duration; - } - - /** - * Gets the number of frames in the animation. - * - * @returns The number of frames in the animation. - */ - public get totalFrames(): number { - return this._totalFrames; - } - - /** - * Gets the loop status of the animation. - * - * @returns The loop status of the animation. - */ - public get loop(): boolean { - return this._loop; - } - - /** - * Gets the speed of the animation. - * - * @returns The speed of the animation. - */ - public get speed(): number { - return this._speed; - } - - /** - * Gets the loop count of the animation. - * - * @returns The loop count of the animation. - */ - public get loopCount(): number { - return this._loopCount; - } - - /** - * Gets the segments of the animation if any are set. - * Default is 0 to total frames. but if segments are set, it will be the start and end frames. - * - */ - public get segments(): [number, number] | null { - return this._segments; - } + private _loadFromSrc(src: string): void { + async function load(): Promise { + const response = await fetch(src); - public get isPlaying(): boolean { - return this._playbackState === 'playing'; - } + if (!response.ok) { + throw new Error( + `Failed to fetch the animation data from URL: ${src}. ${response.status}: ${response.statusText}`, + ); + } - public get isPaused(): boolean { - return this._playbackState === 'paused'; - } + const contentType = response.headers.get('content-type'); - public get isStopped(): boolean { - return this._playbackState === 'stopped'; - } + let data: string | ArrayBuffer; - public get isLoaded(): boolean { - return this._isLoaded; - } + if (contentType?.includes('application/json')) { + data = await response.text(); + } else { + data = await response.arrayBuffer(); + } - public get isFrozen(): boolean { - return this._isFrozen; - } + return data; + } - public get useFrameInterpolation(): boolean { - return this._useFrameInterpolation; - } - - // #endregion Getters and Setters - - // #region Private Methods - /** - * Loads and initializes the animation from a given URL. - * - * @public - * @param animationURL - The source URL of the animation. - */ - private _loadAnimationFromURL(animationURL: string): void { - loadAnimationJSONFromURL(animationURL) - .then((animationJSON) => { - this._initializeAnimationFromData(animationJSON); + load() + .then((data) => { + this._loadFromData(data); }) .catch((error) => { this._eventManager.dispatch({ type: 'loadError', - error: error as Error, + error: new Error(`Failed to load animation data from URL: ${src}. ${error}`), }); }); } - /** - * Initializes the animation from the given data. - * - * @public - * @param data - The animation data. - */ - private _initializeAnimationFromData(data: string | ArrayBuffer): void { - const loadAnimation = (animationData: string): void => { - try { - if (this._renderer?.load(animationData, this._canvas.width, this._canvas.height)) { - this._setupAnimationDetails(); - this._isLoaded = true; - this._eventManager.dispatch({ type: 'load' }); - - if (IS_BROWSER) { - this.resize(); - } + private _loadFromData(data: string | ArrayBuffer): void { + if (this._dotLottieCore === null) return; - // render the first frame of the animation - this._currentFrame = this._mode.includes('reverse') - ? this._getEffectiveEndFrame() - : this._getEffectiveStartFrame(); + try { + const width = this._canvas.width; + const height = this._canvas.height; - this._eventManager.dispatch({ - type: 'frame', - currentFrame: this._currentFrame, - }); - this._renderer.frame(this._currentFrame); - this._render(); + let loaded = false; - if (this._autoplay) { - this.play(); - } - } else { - this._eventManager.dispatch({ - type: 'loadError', - error: new Error('Error encountered while initializing animation from data.'), - }); - } - } catch (error) { + // clear the buffer + this._dotLottieCore.clear(); + + if (typeof data === 'string') { + loaded = this._dotLottieCore.loadAnimationData(data, width, height); + } else if (data instanceof ArrayBuffer) { + loaded = this._dotLottieCore.loadDotLottieData(data, width, height); + } else { this._eventManager.dispatch({ type: 'loadError', - error: error as Error, - }); - } - }; - - if (typeof data === 'string') { - loadAnimation(data); - } else if (data instanceof ArrayBuffer) { - getAnimationJSONFromDotLottie(data) - .then(loadAnimation) - .catch((error) => { - this._eventManager.dispatch({ - type: 'loadError', - error: error as Error, - }); + error: new Error('Unsupported data type for animation data. Expected a string or ArrayBuffer.'), }); - } else { - console.error('Unsupported data type for animation data. Expected a string or ArrayBuffer.'); - } - } - - /** - * Sets up animation details like total frames and duration. - */ - private _setupAnimationDetails(): void { - if (this._renderer) { - this._totalFrames = this._renderer.totalFrames(); - this._duration = this._renderer.duration(); - this._segments = [ - Math.max(0, this._segments?.[0] ?? 0), - Math.min(this._totalFrames - 1, this.segments?.[1] ?? this._totalFrames - 1), - ]; - } - } - - /** - * Renders the animation frame on the canvas. - */ - private _render(): void { - if (!this._context || !this._renderer) { - return; - } - - const width = this._canvas.width; - const height = this._canvas.height; - if (width <= 0 || height <= 0) return; - - this._renderer.resize(width, height); - - if (this._renderer.update()) { - const buffer = this._renderer.render(); - - if (this._imageData?.data.length !== buffer.length) { - this._imageData = this._context.createImageData(width, height); + return; } - this._imageData.data.set(buffer); - - this._context.putImageData(this._imageData, 0, 0); - } - } - - /** - * Updates the current frame and animation state. - * @returns Boolean indicating if the frame was updated. - */ - private _update(): boolean { - // animation is not loaded yet - if (!this._isLoaded || this._duration === 0 || this._totalFrames === 0) return false; - - const effectiveStartFrame = this._getEffectiveStartFrame(); - const effectiveEndFrame = this._getEffectiveEndFrame(); - const effectiveDuration = this._getEffectiveDuration(); - const frameDuration = effectiveDuration / (effectiveEndFrame - effectiveStartFrame); - - this._elapsedTime = (Date.now() / MS_TO_SEC_FACTOR - this._beginTime) * this._speed; - const frameProgress = this._elapsedTime / frameDuration; - - let currentRawFrame = 0; - - if (this._mode === 'forward') { - currentRawFrame = effectiveStartFrame + frameProgress; - } else if (this._mode === 'reverse') { - currentRawFrame = effectiveEndFrame - frameProgress; - } else { - // handle bounce or bounce-reverse mode - const isForward = this._direction === 1; - - currentRawFrame = isForward ? effectiveStartFrame + frameProgress : effectiveEndFrame - frameProgress; - } - - if (!this._useFrameInterpolation) { - currentRawFrame = Math.round(currentRawFrame); - } - - // update current frame only if it's different - if (this._currentFrame !== currentRawFrame) { - this._currentFrame = currentRawFrame; - // ensure the current frame is within the effective range - this._currentFrame = Math.max(effectiveStartFrame, Math.min(this._currentFrame, effectiveEndFrame)); + if (loaded) { + this._eventManager.dispatch({ type: 'load' }); - let shouldUpdate = false; + if (IS_BROWSER) { + this.resize(); + } - if (this._renderer?.frame(this._currentFrame)) { this._eventManager.dispatch({ type: 'frame', - currentFrame: this._currentFrame, + currentFrame: this._dotLottieCore.currentFrame(), }); - shouldUpdate = true; - } + this._render(); - // check if the animation should loop or complete - if ( - (this._mode === 'forward' && this._currentFrame >= effectiveEndFrame) || - (this._mode === 'reverse' && this._currentFrame <= effectiveStartFrame) - ) { - this._handleLoopOrCompletion(); - } else if ( - this._mode.includes('bounce') && - (this._currentFrame <= effectiveStartFrame || this._currentFrame >= effectiveEndFrame) - ) { - // change the direction if the animation reaches the start or end frame - this._direction *= -1; - - // increment the bounce cycle count, 2 cycles means 1 loop - this._bounceCount += 1; - - if (this._bounceCount % 2 === 0) { - this._bounceCount = 0; - this._handleLoopOrCompletion(); - } else { - this._beginTime = Date.now() / MS_TO_SEC_FACTOR; + if (this._dotLottieCore.config().autoplay) { + this._dotLottieCore.play(); + if (this._dotLottieCore.isPlaying()) { + this._eventManager.dispatch({ type: 'play' }); + this._animationFrameId = this._frameManager.requestAnimationFrame(this._draw.bind(this)); + } else { + console.error('something went wrong, the animation was suppose to autoplay'); + } } + } else { + this._eventManager.dispatch({ + type: 'loadError', + error: new Error('Failed to load animation data'), + }); } - - return shouldUpdate; + } catch (error) { + this._eventManager.dispatch({ + type: 'loadError', + error: error as Error, + }); } - - return false; } - /** - * Handles the loop or completion logic for the animation. - */ - private _handleLoopOrCompletion(): void { - if (this._loop) { - this._loopCount += 1; - this._eventManager.dispatch({ type: 'loop', loopCount: this._loopCount }); - this._beginTime = Date.now() / MS_TO_SEC_FACTOR; - } else { - this._playbackState = 'stopped'; - this._eventManager.dispatch({ type: 'complete' }); - this._stopAnimationLoop(); - } + public get renderConfig(): RenderConfig { + return this._renderConfig; } - /** - * Loop that handles the animation playback. - */ - private _animationLoop(): void { - const updated = this._update(); + public get segments(): [number, number] | undefined { + const segments = this._dotLottieCore?.config().segments; - if (updated) { - this._render(); + if (segments && segments.size() === 2) { + return [segments.get(0) as number, segments.get(1) as number]; } - // check if the animation is still playing before requesting the next frame - if (this.isPlaying) { - this._animationFrameId = this._animationFrameManager.requestAnimationFrame(this._animationLoop); - } + return undefined; } - /** - * Stops the animation loop. - * - * This is used to ensure that the animation loop is only stopped once. - */ - private _stopAnimationLoop(): void { - if (this._animationFrameId) { - this._animationFrameManager.cancelAnimationFrame(this._animationFrameId); - this._animationFrameId = null; - } + public get loop(): boolean { + return this._dotLottieCore?.config().loopAnimation ?? false; } - /** - * Starts the animation loop. - * - * This is used to ensure that the animation loop is only started once. - */ - private _startAnimationLoop(): void { - if (!this._animationFrameId) { - this._animationFrameId = this._animationFrameManager.requestAnimationFrame(this._animationLoop); + public get mode(): Mode { + const mode = this._dotLottieCore?.config().mode; + + if (mode === this._wasmModule?.Mode.Reverse) { + return 'reverse'; + } else if (mode === this._wasmModule?.Mode.Bounce) { + return 'bounce'; + } else if (mode === this._wasmModule?.Mode.ReverseBounce) { + return 'reverse-bounce'; + } else { + return 'forward'; } } - private _getEffectiveStartFrame(): number { - return this._segments ? this._segments[0] : 0; + public get isFrozen(): boolean { + return this._isFrozen; } - private _getEffectiveEndFrame(): number { - return this._segments ? this._segments[1] : this._totalFrames - 1; + public get backgroundColor(): string { + return this._backgroundColor ?? ''; } - private _getEffectiveTotalFrames(): number { - return this._segments ? this._segments[1] - this._segments[0] : this._totalFrames; + public get autoplay(): boolean { + return this._dotLottieCore?.config().autoplay ?? false; } - private _getEffectiveDuration(): number { - return this._segments - ? this._duration * ((this._segments[1] - this._segments[0]) / this._totalFrames) - : this._duration; + public get useFrameInterpolation(): boolean { + return this._dotLottieCore?.config().useFrameInterpolation ?? false; } - /** - * Synchronizes the animation timing based on the current frame, direction, and speed settings. - * This method calculates the appropriate begin time for the animation loop, ensuring that the - * animation's playback is consistent with the specified parameters. - * - * @param frame - The current frame number from which the animation timing will be synchronized. - * This frame number is used to calculate the correct position in the animation timeline. - * - * Usage: - * - This function should be called whenever there is a change in the frame, speed, or direction - * of the animation to maintain the correct timing. - * - It is used internally in methods like `play`, `setFrame`, and `setMode` to ensure that the - * animation's playback remains smooth and accurate. - */ - private _synchronizeAnimationTiming(frame: number): void { - const effectiveDuration = this._getEffectiveDuration(); - const effectiveTotalFrames = this._getEffectiveTotalFrames(); - const frameDuration = effectiveDuration / effectiveTotalFrames; - let frameTime = (frame - this._getEffectiveStartFrame()) * frameDuration; - - if (this._direction === -1) { - frameTime = effectiveDuration - frameTime; - } - - this._beginTime = Date.now() / MS_TO_SEC_FACTOR - frameTime / this._speed; + public get speed(): number { + return this._dotLottieCore?.config().speed ?? 0; } - // #endregion - - // #region Public Methods - - /** - * Starts the animation playback. - */ - public play(): void { - if (!this._isLoaded || this.isPlaying) return; - - const effectiveStartFrame = this._getEffectiveStartFrame(); - const effectiveEndFrame = this._getEffectiveEndFrame(); + public get isLoaded(): boolean { + return this._dotLottieCore?.isLoaded() ?? false; + } - // reset begin time and loop count if starting from the beginning - // eslint-disable-next-line no-negated-condition - if (this._playbackState !== 'paused') { - this._currentFrame = - this._mode === 'reverse' || this._mode === 'bounce-reverse' ? effectiveEndFrame : effectiveStartFrame; - this._beginTime = Date.now() / MS_TO_SEC_FACTOR; - } else { - this._synchronizeAnimationTiming(this._currentFrame); - } + public get isPlaying(): boolean { + return this._dotLottieCore?.isPlaying() ?? false; + } - this._playbackState = 'playing'; + public get isPaused(): boolean { + return this._dotLottieCore?.isPaused() ?? false; + } - // auto unfreeze if the animation on play - if (this._isFrozen) { - this._isFrozen = false; + public get isStopped(): boolean { + return this._dotLottieCore?.isStopped() ?? false; + } - this._eventManager.dispatch({ type: 'unfreeze' }); - } + public get currentFrame(): number { + return this._dotLottieCore?.currentFrame() ?? 0; + } - this._eventManager.dispatch({ - type: 'play', - }); + public get loopCount(): number { + return this._dotLottieCore?.loopCount() ?? 0; + } - this._animationFrameId = this._animationFrameManager.requestAnimationFrame(this._animationLoop); + public get totalFrames(): number { + return this._dotLottieCore?.totalFrames() ?? 0; } - /** - * Stops the animation playback and resets the current frame. - */ - public stop(): void { - if (!this._isLoaded || this.isStopped) return; + public get duration(): number { + return this._dotLottieCore?.duration() ?? 0; + } - this._stopAnimationLoop(); - this._playbackState = 'stopped'; - this._bounceCount = 0; + public load(config: Omit): void { + if (this._dotLottieCore === null || this._wasmModule === null) return; + + this._dotLottieCore.setConfig({ + autoplay: config.autoplay ?? false, + backgroundColor: 0, + loopAnimation: config.loop ?? false, + mode: createCoreMode(config.mode ?? 'forward', this._wasmModule), + segments: createCoreSegments(config.segments ?? [], this._wasmModule), + speed: config.speed ?? 1, + useFrameInterpolation: config.useFrameInterpolation ?? true, + }); - if (this._mode === 'reverse' || this._mode === 'bounce-reverse') { - this._currentFrame = this._getEffectiveEndFrame(); - this._direction = -1; - } else { - this._currentFrame = this._getEffectiveStartFrame(); - this._direction = 1; + if (config.data) { + this._loadFromData(config.data); + } else if (config.src) { + this._loadFromSrc(config.src); } - this.setFrame(this._currentFrame); - this._render(); - - this._eventManager.dispatch({ - type: 'stop', - }); + this.setBackgroundColor(config.backgroundColor ?? ''); } - /** - * Pauses the animation playback. - */ - public pause(): void { - if (!this._isLoaded || !this.isPlaying) return; + private _render(): boolean { + if (this._dotLottieCore === null || this._context === null) return false; - this._stopAnimationLoop(); + const rendered = this._dotLottieCore.render(); - this._playbackState = 'paused'; + if (rendered) { + const buffer = this._dotLottieCore.buffer() as Uint8ClampedArray; - this._eventManager.dispatch({ - type: 'pause', - }); - } + const imageData = this._context.createImageData(this._canvas.width, this._canvas.height); - /** - * Sets the speed for animation playback. - * @param speed - Speed multiplier for playback. - */ - public setSpeed(speed: number): void { - if (!this._isLoaded || speed <= 0 || speed >= Number.MAX_SAFE_INTEGER) return; + imageData.data.set(buffer); - if (this._speed === speed) return; + this._context.putImageData(imageData, 0, 0); - if (this.isPlaying) { - // recalculate the begin time based on the new speed to maintain the current position - const currentTime = Date.now() / MS_TO_SEC_FACTOR; + this._eventManager.dispatch({ + type: 'render', + currentFrame: this._dotLottieCore.currentFrame(), + }); - this._beginTime = currentTime - this._elapsedTime / speed; + return true; } - this._speed = speed; + return false; } - /** - * Sets the loop state for animation playback. - * @param loop - Boolean indicating if the animation should loop. - */ - public setLoop(loop: boolean): void { - if (!this._isLoaded) return; + private _draw(): void { + if (this._dotLottieCore === null || this._context === null || !this._dotLottieCore.isPlaying()) return; - this._loop = loop; - } - - /** - * Sets the current frame of the animation. - * @param frame - Frame number to set. - */ - public setFrame(frame: number): void { - if (!this._isLoaded) return; + const nextFrame = this._dotLottieCore.requestFrame(); - const effectiveStartFrame = this._getEffectiveStartFrame(); - const effectiveEndFrame = this._getEffectiveEndFrame(); + const updated = this._dotLottieCore.setFrame(nextFrame); - // validate the frame number within the effective frame range - if (frame < effectiveStartFrame || frame > effectiveEndFrame) { - console.error( - `Invalid frame number: ${frame}. It should be between ${effectiveStartFrame} and ${effectiveEndFrame}.`, - ); + if (updated) { + this._eventManager.dispatch({ + type: 'frame', + currentFrame: this._dotLottieCore.currentFrame(), + }); - return; + const rendered = this._render(); + + if (rendered) { + // handle loop or complete + if (this._dotLottieCore.isComplete()) { + if (this._dotLottieCore.config().loopAnimation) { + this._eventManager.dispatch({ + type: 'loop', + loopCount: this._dotLottieCore.loopCount(), + }); + } else { + this._eventManager.dispatch({ type: 'complete' }); + } + } + } } - this._currentFrame = frame; + this._animationFrameId = this._frameManager.requestAnimationFrame(this._draw.bind(this)); + } - if (this.isPlaying) { - this._synchronizeAnimationTiming(frame); - } + public play(): void { + if (this._dotLottieCore === null) return; - if (this._renderer?.frame(this._currentFrame)) { - this._eventManager.dispatch({ - type: 'frame', - currentFrame: this._currentFrame, - }); - this._render(); + const ok = this._dotLottieCore.play(); + + this._isFrozen = false; + + if (ok) { + this._eventManager.dispatch({ type: 'play' }); + this._animationFrameId = this._frameManager.requestAnimationFrame(this._draw.bind(this)); } } - /** - * - * Sets the playback mode of the animation. - * - */ - public setMode(mode: Mode): void { - if (!this._isLoaded || this._mode === mode) { - return; - } + public pause(): void { + if (this._dotLottieCore === null) return; - this._mode = mode; - this._bounceCount = 0; - this._direction = mode.includes('reverse') ? -1 : 1; + const ok = this._dotLottieCore.pause(); - if (this.isPlaying) { - this._synchronizeAnimationTiming(this._currentFrame); + if (ok) { + this._eventManager.dispatch({ type: 'pause' }); } } - public load(config: Omit): void { - if (!this._renderer || !this._context) { - return; - } + public stop(): void { + if (this._dotLottieCore === null) return; - if (!config.src && !config.data) { - console.error('Either "src" or "data" must be provided.'); + const ok = this._dotLottieCore.stop(); - return; - } + if (ok) { + this._eventManager.dispatch({ type: 'frame', currentFrame: this._dotLottieCore.currentFrame() }); - this._stopAnimationLoop(); - this._playbackState = 'stopped'; - this._isLoaded = false; - - this._loop = config.loop ?? false; - this._speed = config.speed ?? 1; - this._autoplay = config.autoplay ?? false; - this._mode = config.mode ?? 'forward'; - this._segments = config.segments ?? null; - this._loopCount = 0; - this._bounceCount = 0; - this._direction = this._mode.includes('reverse') ? -1 : 1; - this._renderConfig = config.renderConfig ?? {}; - this._useFrameInterpolation = config.useFrameInterpolation ?? true; + this._render(); - const effectiveStartFrame = this._getEffectiveStartFrame(); - const effectiveEndFrame = this._getEffectiveEndFrame(); + this._eventManager.dispatch({ type: 'stop' }); + } + } - this._currentFrame = - this._mode === 'reverse' || this._mode === 'bounce-reverse' ? effectiveEndFrame : effectiveStartFrame; + public setFrame(frame: number): void { + if (this._dotLottieCore === null) return; - this._beginTime = 0; - this._totalFrames = 0; - this._duration = 0; - this._backgroundColor = config.backgroundColor ?? DEFAULT_BG_COLOR; + if (frame < 0 || frame > this._dotLottieCore.totalFrames()) return; - this.setBackgroundColor(this._backgroundColor); + const ok = this._dotLottieCore.setFrame(frame); - this._context.clearRect(0, 0, this._canvas.width, this._canvas.height); + if (ok) { + this._eventManager.dispatch({ type: 'frame', currentFrame: this._dotLottieCore.currentFrame() }); - if (config.src) { - this._loadAnimationFromURL(config.src); - } else if (config.data) { - this._initializeAnimationFromData(config.data); + this._render(); } } - public setSegments(startFrame: number, endFrame: number): void { - if (!this._isLoaded) return; + public setSpeed(speed: number): void { + if (this._dotLottieCore === null) return; + + this._dotLottieCore.setConfig({ + ...this._dotLottieCore.config(), + speed, + }); + } - // Validate the frame range - if (startFrame < 0 || endFrame >= this._totalFrames || startFrame > endFrame) { - console.error('Invalid frame range.'); + public setBackgroundColor(color: string): void { + if (this._dotLottieCore === null) return; - return; + if (this._canvas instanceof HTMLCanvasElement) { + this._canvas.style.backgroundColor = color; } - this._segments = [startFrame, endFrame]; + this._backgroundColor = color; + } + + public setLoop(loop: boolean): void { + if (this._dotLottieCore === null) return; - if (this._currentFrame < startFrame || this._currentFrame > endFrame) { - this._currentFrame = this._direction === 1 ? startFrame : endFrame; + this._dotLottieCore.setConfig({ + ...this._dotLottieCore.config(), + loopAnimation: loop, + }); + } - // render the current frame - if (this._renderer?.frame(this._currentFrame)) { - this._render(); - this._eventManager.dispatch({ - type: 'frame', - currentFrame: this._currentFrame, - }); - } - } + public setUseFrameInterpolation(useFrameInterpolation: boolean): void { + if (this._dotLottieCore === null) return; - // If playing, adjust the animation timing - if (this.isPlaying) { - this._synchronizeAnimationTiming(this._currentFrame); - } + this._dotLottieCore.setConfig({ + ...this._dotLottieCore.config(), + useFrameInterpolation, + }); } - /** - * Registers an event listener for a specific event type. - * - * @param type - The type of the event to listen for. - * @param listener - The callback function to be called when the event is dispatched. - */ public addEventListener(type: T, listener: EventListener): void { this._eventManager.addEventListener(type, listener); } - /** - * Removes an event listener for a specific event type. - * - * @param type - The type of the event to listen for. - * @param listener - The callback function to be called when the event is dispatched. - */ public removeEventListener(type: T, listener?: EventListener): void { this._eventManager.removeEventListener(type, listener); } - /** - * Sets the source URL of the WASM file to load. - * @param url - The URL of the WASM file to load. - */ - public static setWasmUrl(url: string): void { - RendererLoader.setWasmUrl(url); - } - - /** - * Destroys the DotLottie instance. - * - */ public destroy(): void { - this._stopAnimationLoop(); - this._playbackState = 'stopped'; - + this._dotLottieCore?.delete(); + this._dotLottieCore = null; this._context = null; - this._renderer = null; this._eventManager.dispatch({ type: 'destroy', @@ -905,98 +481,67 @@ export class DotLottie { this._eventManager.removeAllEventListeners(); } - /** - * Freezes the animation by stopping the animation loop. - * - */ public freeze(): void { - if (this._isFrozen) return; + if (this._animationFrameId === null) return; - this._stopAnimationLoop(); + this._frameManager.cancelAnimationFrame(this._animationFrameId); + this._animationFrameId = null; this._isFrozen = true; - this._eventManager.dispatch({ - type: 'freeze', - }); + this._eventManager.dispatch({ type: 'freeze' }); } - /** - * Unfreezes the animation by resuming the animation loop. - * - */ public unfreeze(): void { - if (!this.isFrozen) return; + if (this._animationFrameId !== null) return; - this._isFrozen = false; - this._eventManager.dispatch({ type: 'unfreeze' }); + this._animationFrameId = this._frameManager.requestAnimationFrame(this._draw.bind(this)); - // resume the animation loop only if the playback state is 'playing' - if (this.isPlaying) { - this._synchronizeAnimationTiming(this._currentFrame); - this._startAnimationLoop(); - } - } - - /** - * Sets the background color of the canvas. - * - * @param color - The background color of the canvas. - */ - public setBackgroundColor(color: string): void { - this._backgroundColor = color; - - const rgbaInt = hexStringToRGBAInt(color); + this._isFrozen = false; - if (IS_BROWSER && this._canvas instanceof HTMLCanvasElement) { - this._canvas.style.backgroundColor = color; - } else { - this._renderer?.setBgColor(rgbaInt); - } + this._eventManager.dispatch({ type: 'unfreeze' }); } - /** - * Adjusts the canvas size to match the size of its bounding box, considering the device's pixel ratio. - * This method ensures that the canvas is correctly scaled for high-density displays, maintaining - * the clarity and quality of the rendered animation. - * - * Call this method whenever the size of the canvas element changes (e.g., due to window resizing, - * orientation changes, or dynamic layout updates) to ensure that the canvas is always properly scaled. - * - */ public resize(): void { - if (!IS_BROWSER) { - console.warn( - ` - Resize operation failed: The canvas cannot be resized in a Node.js or Web Worker environment. - In these environments, you must manually set the canvas size. - `, - ); - - return; - } + if (!IS_BROWSER) return; - if (!(this._canvas instanceof HTMLCanvasElement)) { - return; - } + const dpr = this._renderConfig.devicePixelRatio || window.devicePixelRatio || 1; const { height: clientHeight, width: clientWidth } = this._canvas.getBoundingClientRect(); - const dpr = this._renderConfig.devicePixelRatio || window.devicePixelRatio || 1; this._canvas.width = clientWidth * dpr; this._canvas.height = clientHeight * dpr; - // resize the renderer and render the current frame - this._render(); + const ok = this._dotLottieCore?.resize(this._canvas.width, this._canvas.height); + + if (ok) { + this._render(); + } } - public setUseFrameInterpolation(useFrameInterpolation: boolean): void { - this._useFrameInterpolation = useFrameInterpolation; + public setSegments(startFrame: number, endFrame: number): void { + if (this._dotLottieCore === null || this._wasmModule === null) return; + + this._dotLottieCore.setConfig({ + ...this._dotLottieCore.config(), + segments: createCoreSegments([startFrame, endFrame], this._wasmModule), + }); + } + + public setMode(mode: Mode): void { + if (this._dotLottieCore === null || this._wasmModule === null) return; + + this._dotLottieCore.setConfig({ + ...this._dotLottieCore.config(), + mode: createCoreMode(mode, this._wasmModule), + }); } public setRenderConfig(config: RenderConfig): void { this._renderConfig = config; } - // #endregion + public static setWasmUrl(url: string): void { + DotLottieWasmLoader.setWasmUrl(url); + } } diff --git a/packages/web/src/event-manager.ts b/packages/web/src/event-manager.ts index 69517b26..3260aefa 100644 --- a/packages/web/src/event-manager.ts +++ b/packages/web/src/event-manager.ts @@ -16,7 +16,8 @@ export type EventType = | 'stop' | 'destroy' | 'freeze' - | 'unfreeze'; + | 'unfreeze' + | 'render'; /** * Maps an event type string to its respective event interface. @@ -43,6 +44,8 @@ type EventByType = T extends 'complete' ? FreezeEvent : T extends 'unfreeze' ? UnfreezeEvent + : T extends 'render' + ? RenderEvent : never; /** @@ -52,6 +55,11 @@ export interface BaseEvent { type: EventType; } +export interface RenderEvent extends BaseEvent { + currentFrame: number; + type: 'render'; +} + export interface FreezeEvent extends BaseEvent { type: 'freeze'; } diff --git a/packages/web/src/index.ts b/packages/web/src/index.ts index 156a3730..f8578936 100644 --- a/packages/web/src/index.ts +++ b/packages/web/src/index.ts @@ -3,6 +3,4 @@ */ export * from './dotlottie'; -export * from './event-manager'; -export * from './utils'; -export * from './renderer-loader'; +export type * from './event-manager'; diff --git a/packages/web/src/utils.ts b/packages/web/src/utils.ts index a4aafd78..5a9fac97 100644 --- a/packages/web/src/utils.ts +++ b/packages/web/src/utils.ts @@ -2,48 +2,6 @@ * Copyright 2023 Design Barn Inc. */ -import { getAnimation, getManifest, loadFromArrayBuffer } from '@dotlottie/dotlottie-js'; - -export async function getAnimationJSONFromDotLottie(dotLottieBuffer: ArrayBuffer): Promise { - const loadedDotLottieFile = await loadFromArrayBuffer(dotLottieBuffer); - const manifest = await getManifest(loadedDotLottieFile); - const activeAnimationId = manifest?.activeAnimationId || manifest?.animations[0]?.id || ''; - - if (!activeAnimationId) { - throw new Error('Unable to determine the active animation ID from the .lottie manifest.'); - } - const animationData = await getAnimation(loadedDotLottieFile, activeAnimationId, { inlineAssets: true }); - - return JSON.stringify(animationData); -} - -export async function loadAnimationJSONFromURL(animationURL: string): Promise { - try { - const response = await fetch(animationURL); - - if (!response.ok) { - throw new Error( - `Failed to fetch the animation data from URL: ${animationURL}. ${response.status}: ${response.statusText}`, - ); - } - - const contentType = response.headers.get('content-type'); - let animationJSON: string; - - if (contentType?.includes('application/json')) { - animationJSON = await response.text(); - } else { - const arrayBuffer = await response.arrayBuffer(); - - animationJSON = await getAnimationJSONFromDotLottie(arrayBuffer); - } - - return animationJSON; - } catch (error) { - throw new Error(`Failed to load animation data from URL: ${animationURL}. ${error}`); - } -} - export function isHexColor(color: string): boolean { return /^#([\da-f]{6}|[\da-f]{8})$/iu.test(color); } diff --git a/packages/web/src/wasm/index.ts b/packages/web/src/wasm/index.ts deleted file mode 100644 index cf0f99ac..00000000 --- a/packages/web/src/wasm/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright 2023 Design Barn Inc. - */ - -export { default as createRendererModule } from './renderer'; - -// Interface describing the Renderer class -export interface Renderer { - duration(): number; - error(): string; - frame(no: number): boolean; - load(data: string, width: number, height: number): boolean; - render(): Uint8ClampedArray; - resize(width: number, height: number): void; - setBgColor(color: number): void; - size(): Float32Array; - totalFrames(): number; - update(): boolean; -} - -// Interface representing the module structure with Renderer class -export interface Module { - Renderer: new () => Renderer; -} diff --git a/packages/web/src/wasm/renderer.wasm b/packages/web/src/wasm/renderer.wasm deleted file mode 100755 index 708ab60d..00000000 Binary files a/packages/web/src/wasm/renderer.wasm and /dev/null differ diff --git a/packages/web/tests/dotlottie.test.ts b/packages/web/tests/dotlottie.test.ts index dad39858..48726983 100644 --- a/packages/web/tests/dotlottie.test.ts +++ b/packages/web/tests/dotlottie.test.ts @@ -26,7 +26,7 @@ afterEach(() => { }); describe('play', () => { - test.skip('unfreeze the animation on play()', async () => { + test('unfreeze the animation on play()', async () => { const onLoad = vi.fn(); const onPlay = vi.fn(); @@ -112,7 +112,7 @@ describe('play', () => { mode: 'reverse', }, { - mode: 'bounce-reverse', + mode: 'reverse-bounce', }, { mode: 'bounce', @@ -123,7 +123,7 @@ describe('play', () => { speed: 2, }, { - mode: 'bounce-reverse', + mode: 'reverse-bounce', segments: [5, 25], speed: 2, }, @@ -165,8 +165,7 @@ describe('play', () => { expect(onLoad).toHaveBeenCalledTimes(1); }); - const expectedDirection = config.mode === 'reverse' || config.mode === 'bounce-reverse' ? -1 : 1; - const totalFrames = (config.segments?.[1] ?? dotLottie.totalFrames - 1) - (config.segments?.[0] ?? 0); + const totalFrames = (config.segments?.[1] ?? dotLottie.totalFrames) - (config.segments?.[0] ?? 0); const expectedDuration = ((config.mode?.includes('bounce') ? 2 : 1) * @@ -175,27 +174,26 @@ describe('play', () => { dotLottie.speed; const expectedEndFrame = - config.mode === 'reverse' || config.mode === 'bounce-reverse' + config.mode === 'reverse' || config.mode === 'reverse-bounce' ? config.segments?.[0] ?? 0 - : config.segments?.[1] ?? dotLottie.totalFrames - 1; + : config.segments?.[1] ?? dotLottie.totalFrames; const expectedStartFrame = - config.mode === 'reverse' || config.mode === 'bounce-reverse' - ? config.segments?.[1] ?? dotLottie.totalFrames - 1 + config.mode === 'reverse' || config.mode === 'reverse-bounce' + ? config.segments?.[1] ?? dotLottie.totalFrames : config.segments?.[0] ?? 0; const startFrame = - config.mode === 'reverse' || config.mode === 'bounce-reverse' - ? dotLottie.segments?.[1] - : dotLottie.segments?.[0]; + config.mode === 'reverse' || config.mode === 'reverse-bounce' + ? dotLottie.segments?.[1] ?? dotLottie.totalFrames + : dotLottie.segments?.[0] ?? 0; const endFrame = - config.mode === 'reverse' || config.mode === 'bounce-reverse' - ? dotLottie.segments?.[0] - : dotLottie.segments?.[1]; + config.mode === 'reverse' || config.mode === 'reverse-bounce' + ? dotLottie.segments?.[0] ?? 0 + : dotLottie.segments?.[1] ?? dotLottie.totalFrames; expect(startFrame).toBe(expectedStartFrame); expect(endFrame).toBe(expectedEndFrame); - expect(dotLottie.direction).toBe(expectedDirection); expect(dotLottie.isLoaded).toBe(true); await vi.waitFor(() => { @@ -263,8 +261,7 @@ describe('play', () => { expect(onLoad).toHaveBeenCalledTimes(1); }); - const expectedDirection = config.mode === 'reverse' || config.mode === 'bounce-reverse' ? -1 : 1; - const totalFrames = (config.segments?.[1] ?? dotLottie.totalFrames - 1) - (config.segments?.[0] ?? 0); + const totalFrames = (config.segments?.[1] ?? dotLottie.totalFrames) - (config.segments?.[0] ?? 0); const expectedDuration = ((config.mode?.includes('bounce') ? 2 : 1) * @@ -273,31 +270,29 @@ describe('play', () => { dotLottie.speed; const expectedEndFrame = - config.mode === 'reverse' || config.mode === 'bounce-reverse' + config.mode === 'reverse' || config.mode === 'reverse-bounce' ? config.segments?.[0] ?? 0 - : config.segments?.[1] ?? dotLottie.totalFrames - 1; + : config.segments?.[1] ?? dotLottie.totalFrames; const expectedStartFrame = - config.mode === 'reverse' || config.mode === 'bounce-reverse' - ? config.segments?.[1] ?? dotLottie.totalFrames - 1 + config.mode === 'reverse' || config.mode === 'reverse-bounce' + ? config.segments?.[1] ?? dotLottie.totalFrames : config.segments?.[0] ?? 0; const startFrame = - config.mode === 'reverse' || config.mode === 'bounce-reverse' - ? dotLottie.segments?.[1] - : dotLottie.segments?.[0]; + config.mode === 'reverse' || config.mode === 'reverse-bounce' + ? dotLottie.segments?.[1] ?? dotLottie.totalFrames + : dotLottie.segments?.[0] ?? 0; const endFrame = - config.mode === 'reverse' || config.mode === 'bounce-reverse' - ? dotLottie.segments?.[0] - : dotLottie.segments?.[1]; + config.mode === 'reverse' || config.mode === 'reverse-bounce' + ? dotLottie.segments?.[0] ?? 0 + : dotLottie.segments?.[1] ?? dotLottie.totalFrames; expect(startFrame).toBe(expectedStartFrame); expect(endFrame).toBe(expectedEndFrame); - expect(dotLottie.direction).toBe(expectedDirection); expect(dotLottie.isLoaded).toBe(true); await vi.waitFor(() => { - expect(onLoad).toHaveBeenCalledTimes(1); expect(onPlay).toHaveBeenCalledTimes(1); }); @@ -366,7 +361,7 @@ describe('play', () => { expect(onLoad).toHaveBeenCalledTimes(1); }); - const totalFrames = (config.segments?.[1] ?? dotLottie.totalFrames - 1) - (config.segments?.[0] ?? 0); + const totalFrames = (config.segments?.[1] ?? dotLottie.totalFrames) - (config.segments?.[0] ?? 0); const expectedDuration = ((config.mode?.includes('bounce') ? 2 : 1) * @@ -439,9 +434,13 @@ describe('play', () => { timeout: dotLottie.duration * 1000 * 2, }); - const startFrame = dotLottie.mode.includes('reverse') ? dotLottie.segments?.[1] : dotLottie.segments?.[0]; - const endFrame = dotLottie.mode.includes('reverse') ? dotLottie.segments?.[0] : dotLottie.segments?.[1]; - const totalFrames = (config.segments?.[1] ?? dotLottie.totalFrames - 1) - (config.segments?.[0] ?? 0); + const startFrame = dotLottie.mode.includes('reverse') + ? dotLottie.segments?.[1] ?? dotLottie.totalFrames + : dotLottie.segments?.[0] ?? 0; + const endFrame = dotLottie.mode.includes('reverse') + ? dotLottie.segments?.[0] ?? 0 + : dotLottie.segments?.[1] ?? dotLottie.totalFrames; + const totalFrames = (config.segments?.[1] ?? dotLottie.totalFrames) - (config.segments?.[0] ?? 0); expect(onFrame).toHaveBeenNthCalledWith(1, { type: 'frame', @@ -650,7 +649,6 @@ describe('load', () => { expect(dotLottie.autoplay).toBe(true); expect(dotLottie.mode).toBe('reverse'); expect(dotLottie.backgroundColor).toBe('#ff00ff'); - expect(dotLottie.direction).toBe(-1); }); test('loads a new animation data via load() method', async () => { @@ -687,9 +685,7 @@ describe('load', () => { expect(dotLottie.autoplay).toBe(true); expect(dotLottie.mode).toBe('reverse'); expect(dotLottie.backgroundColor).toBe('#000000'); - expect(dotLottie.direction).toBe(-1); expect(dotLottie.useFrameInterpolation).toBe(true); - expect(dotLottie.canvas).toBe(canvas); }); test('emit loadError event when loading invalid lottie animation data', async () => { @@ -727,16 +723,21 @@ describe('load', () => { }); test('log error when loading invalid animation data of invalid type', async () => { - const error = vi.spyOn(console, 'error'); + const onLoadError = vi.fn(); dotLottie = new DotLottie({ canvas, data: 1 as unknown as string, }); - await vi.waitFor(() => expect(error).toHaveBeenCalledTimes(1)); + dotLottie.addEventListener('loadError', onLoadError); - expect(error).toHaveBeenCalledWith('Unsupported data type for animation data. Expected a string or ArrayBuffer.'); + await vi.waitFor(() => expect(onLoadError).toHaveBeenCalledTimes(1)); + + expect(onLoadError).toHaveBeenCalledWith({ + type: 'loadError', + error: new Error('Unsupported data type for animation data. Expected a string or ArrayBuffer.'), + }); }); test('emit loadError when fail to load wasm', async () => { @@ -838,7 +839,7 @@ describe('stop', () => { mode: 'reverse', }, { - mode: 'bounce-reverse', + mode: 'reverse-bounce', }, { mode: 'bounce', @@ -849,7 +850,7 @@ describe('stop', () => { speed: 2, }, { - mode: 'bounce-reverse', + mode: 'reverse-bounce', segments: [5, 25], speed: 2, }, @@ -884,11 +885,11 @@ describe('stop', () => { expect(onPlay).toHaveBeenCalledTimes(1); }); - const totalFrames = (config.segments?.[1] ?? dotLottie.totalFrames - 1) - (config.segments?.[0] ?? 0); + const totalFrames = (config.segments?.[1] ?? dotLottie.totalFrames) - (config.segments?.[0] ?? 0); const startFrame = - config.mode === 'reverse' || config.mode === 'bounce-reverse' - ? config.segments?.[1] ?? dotLottie.totalFrames - 1 + config.mode === 'reverse' || config.mode === 'reverse-bounce' + ? config.segments?.[1] ?? dotLottie.totalFrames : config.segments?.[0] ?? 0; expect(onFrame).toHaveBeenNthCalledWith(1, { @@ -948,7 +949,7 @@ describe('setMode', () => { expect(dotLottie.mode).toBe('forward'); }); - test.each(['reverse', 'bounce', 'bounce-reverse'])('setMode(%s)', async (mode) => { + test.each(['reverse', 'bounce', 'reverse-bounce'])('setMode(%s)', async (mode) => { const onPlay = vi.fn(); const onFrame = vi.fn(); @@ -968,8 +969,6 @@ describe('setMode', () => { dotLottie.setMode(mode); expect(dotLottie.mode).toBe(mode); - - expect(dotLottie.direction).toBe(dotLottie.mode.includes('reverse') ? -1 : 1); }); }); diff --git a/packages/web/tests/setup.ts b/packages/web/tests/setup.ts index 32cbed79..9bd22a0b 100644 --- a/packages/web/tests/setup.ts +++ b/packages/web/tests/setup.ts @@ -4,4 +4,4 @@ import { DotLottie } from '../src'; -DotLottie.setWasmUrl('src/wasm/renderer.wasm'); +DotLottie.setWasmUrl('src/core/dotlottie-player.wasm'); diff --git a/packages/web/tsup.config.cjs b/packages/web/tsup.config.cjs index 6b27b48e..0af25cfc 100644 --- a/packages/web/tsup.config.cjs +++ b/packages/web/tsup.config.cjs @@ -14,17 +14,17 @@ module.exports = defineConfig({ treeshake: true, clean: true, dts: true, - minify: false, - sourcemap: false, - entry: ['./src/**/*.ts', './src/**/*.js'], - format: ['esm'], + minify: true, + sourcemap: true, + entry: ['./src/index.ts'], + format: ['esm', 'cjs'], platform: 'neutral', target: ['es2020', 'node18'], tsconfig: 'tsconfig.build.json', onSuccess: () => { fs.copyFileSync( - path.resolve(__dirname, 'src/wasm/renderer.wasm'), - path.resolve(__dirname, 'dist/wasm/renderer.wasm'), + path.resolve(__dirname, 'src/core/dotlottie-player.wasm'), + path.resolve(__dirname, 'dist/dotlottie-player.wasm'), ); }, }); diff --git a/packages/web/vitest.config.ts b/packages/web/vitest.config.ts index d620ea48..8693dda1 100644 --- a/packages/web/vitest.config.ts +++ b/packages/web/vitest.config.ts @@ -16,10 +16,10 @@ export default defineConfig({ include: ['src/**/*.ts'], reporter: ['json', 'json-summary', 'text-summary', 'lcov'], thresholds: { - statements: 84, - branches: 76, - functions: 86, - lines: 85, + statements: 83, + branches: 75, + functions: 84, + lines: 86, }, }, testTimeout: 10000, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8dc0d3f0..d1407ed5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -282,10 +282,6 @@ importers: version: 5.0.4 packages/web: - dependencies: - '@dotlottie/dotlottie-js': - specifier: ^0.6.2 - version: 0.6.2 devDependencies: '@types/node': specifier: ^20.10.5 @@ -958,25 +954,6 @@ packages: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@dotlottie/dotlottie-js@0.6.2: - resolution: {integrity: sha512-3UGG60r7Dz868KvPYIBhuO6HPX2M5CpcXulXilLbQZ91eWKkF+amoJSdkSfdLtWjjcwu/jSgeErAercxC2StBw==} - engines: {node: '>=18.0.0'} - dependencies: - browser-image-hash: 0.0.5 - fflate: 0.8.1 - sharp: 0.33.2 - sharp-phash: 2.1.0(sharp@0.33.2) - valibot: 0.13.1 - dev: false - - /@emnapi/runtime@0.45.0: - resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==} - requiresBuild: true - dependencies: - tslib: 2.6.2 - dev: false - optional: true - /@endemolshinegroup/cosmiconfig-typescript-loader@3.0.2(cosmiconfig@7.0.1)(typescript@5.0.4): resolution: {integrity: sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==} engines: {node: '>=10.0.0'} @@ -1787,194 +1764,6 @@ packages: resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} dev: true - /@img/sharp-darwin-arm64@0.33.2: - resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==} - engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.0.1 - dev: false - optional: true - - /@img/sharp-darwin-x64@0.33.2: - resolution: {integrity: sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==} - engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [x64] - os: [darwin] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.0.1 - dev: false - optional: true - - /@img/sharp-libvips-darwin-arm64@1.0.1: - resolution: {integrity: sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==} - engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-darwin-x64@1.0.1: - resolution: {integrity: sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==} - engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linux-arm64@1.0.1: - resolution: {integrity: sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==} - engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linux-arm@1.0.1: - resolution: {integrity: sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==} - engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linux-s390x@1.0.1: - resolution: {integrity: sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==} - engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linux-x64@1.0.1: - resolution: {integrity: sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==} - engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linuxmusl-arm64@1.0.1: - resolution: {integrity: sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==} - engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-libvips-linuxmusl-x64@1.0.1: - resolution: {integrity: sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==} - engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-linux-arm64@0.33.2: - resolution: {integrity: sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==} - engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.0.1 - dev: false - optional: true - - /@img/sharp-linux-arm@0.33.2: - resolution: {integrity: sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==} - engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [arm] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.0.1 - dev: false - optional: true - - /@img/sharp-linux-s390x@0.33.2: - resolution: {integrity: sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==} - engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [s390x] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.0.1 - dev: false - optional: true - - /@img/sharp-linux-x64@0.33.2: - resolution: {integrity: sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==} - engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.0.1 - dev: false - optional: true - - /@img/sharp-linuxmusl-arm64@0.33.2: - resolution: {integrity: sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==} - engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 - dev: false - optional: true - - /@img/sharp-linuxmusl-x64@0.33.2: - resolution: {integrity: sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==} - engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.0.1 - dev: false - optional: true - - /@img/sharp-wasm32@0.33.2: - resolution: {integrity: sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [wasm32] - requiresBuild: true - dependencies: - '@emnapi/runtime': 0.45.0 - dev: false - optional: true - - /@img/sharp-win32-ia32@0.33.2: - resolution: {integrity: sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: false - optional: true - - /@img/sharp-win32-x64@0.33.2: - resolution: {integrity: sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: false - optional: true - /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -2550,30 +2339,6 @@ packages: resolution: {integrity: sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==} dev: true - /@rgba-image/common@0.1.13: - resolution: {integrity: sha512-AnOBmBpjSgcymTuVhTGy+RB4FfmEQqR2GeJY3d3xfvR9fl3HfhzwgVqopuh3bKSAT6KRpJr7wNmug0qr3oI7bA==} - dev: false - - /@rgba-image/copy@0.1.3: - resolution: {integrity: sha512-fscJhpp8YtVELGIwQsv1Pj6BEN4PEWAlMJ6a/HWzYxzVr3y/dut4BUrqeWRKiKeRXAGqaV6QxkBxAgYMQYZEvw==} - dependencies: - '@rgba-image/common': 0.1.13 - dev: false - - /@rgba-image/create-image@0.1.1: - resolution: {integrity: sha512-ndExUNyi9Ooa/OZqiJS53vYrQ48FX7MDmMrEslDxhsorDsXpeKI9w689r4AYhT9CF9KZlBe8SmI++3BwSvvwAQ==} - dependencies: - '@rgba-image/common': 0.1.13 - dev: false - - /@rgba-image/lanczos@0.1.1: - resolution: {integrity: sha512-MSGGU7BZmEbg1xHtNp+StARoN7R38zJnFgSEvSzB710nXsHGEaJt//z2VnPfRQTtKSKUXEnp95JSuqDlXTBrYA==} - dependencies: - '@rgba-image/common': 0.1.13 - '@rgba-image/copy': 0.1.3 - '@rgba-image/create-image': 0.1.1 - dev: false - /@rollup/rollup-android-arm-eabi@4.6.1: resolution: {integrity: sha512-0WQ0ouLejaUCRsL93GD4uft3rOmB8qoQMU05Kb8CmMtMBe7XUDLAltxVZI1q6byNqEtU7N1ZX1Vw5lIpgulLQA==} cpu: [arm] @@ -4540,14 +4305,6 @@ packages: wcwidth: 1.0.1 dev: true - /browser-image-hash@0.0.5: - resolution: {integrity: sha512-j+rsA1L3vL8k8ji4pFPFAOU/wN/hegwk1eoMshFk3OtjzEzdDrT9Dz94OkLc43NhWGck2a9t5eQQok6zjJSPHQ==} - dependencies: - '@rgba-image/lanczos': 0.1.1 - decimal.js: 10.4.3 - wasm-imagemagick: 1.2.8 - dev: false - /browserslist@4.22.1: resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -4919,6 +4676,7 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 + dev: true /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} @@ -4926,27 +4684,13 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - /color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - dev: false + dev: true /color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true dev: true - /color@4.2.3: - resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} - engines: {node: '>=12.5.0'} - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - dev: false - /colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true @@ -5858,10 +5602,6 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true - /decimal.js@10.4.3: - resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - dev: false - /decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} dependencies: @@ -5977,11 +5717,6 @@ packages: engines: {node: '>=8'} dev: true - /detect-libc@2.0.2: - resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} - engines: {node: '>=8'} - dev: false - /devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} dependencies: @@ -6203,12 +5938,6 @@ packages: is-arrayish: 0.2.1 dev: true - /error-stack-parser@2.1.4: - resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - dependencies: - stackframe: 1.3.4 - dev: false - /es-abstract@1.22.3: resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} engines: {node: '>= 0.4'} @@ -7304,10 +7033,6 @@ packages: web-streams-polyfill: 3.2.1 dev: true - /fflate@0.8.1: - resolution: {integrity: sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ==} - dev: false - /figgy-pudding@3.5.2: resolution: {integrity: sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==} dev: true @@ -8253,10 +7978,6 @@ packages: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true - /is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false - /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: @@ -9090,6 +8811,7 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 + dev: true /lru-cache@7.18.3: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} @@ -10763,6 +10485,7 @@ packages: /p-map@2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} + dev: true /p-map@4.0.0: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} @@ -12407,6 +12130,7 @@ packages: hasBin: true dependencies: lru-cache: 6.0.0 + dev: true /serialize-error@11.0.3: resolution: {integrity: sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==} @@ -12448,45 +12172,6 @@ packages: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} dev: true - /sharp-phash@2.1.0(sharp@0.33.2): - resolution: {integrity: sha512-9JYWr4tiKpjRA5Mi0qHn6LP2evS+GjdRVGjDFOSnO761m5Pavkpm83SyzauO2Ntt7znVqTn7J3XTUwHjRPAonw==} - engines: {node: '>= 10'} - peerDependencies: - sharp: '>= 0.25.4' - dependencies: - sharp: 0.33.2 - dev: false - - /sharp@0.33.2: - resolution: {integrity: sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==} - engines: {libvips: '>=8.15.1', node: ^18.17.0 || ^20.3.0 || >=21.0.0} - requiresBuild: true - dependencies: - color: 4.2.3 - detect-libc: 2.0.2 - semver: 7.5.4 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.2 - '@img/sharp-darwin-x64': 0.33.2 - '@img/sharp-libvips-darwin-arm64': 1.0.1 - '@img/sharp-libvips-darwin-x64': 1.0.1 - '@img/sharp-libvips-linux-arm': 1.0.1 - '@img/sharp-libvips-linux-arm64': 1.0.1 - '@img/sharp-libvips-linux-s390x': 1.0.1 - '@img/sharp-libvips-linux-x64': 1.0.1 - '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 - '@img/sharp-libvips-linuxmusl-x64': 1.0.1 - '@img/sharp-linux-arm': 0.33.2 - '@img/sharp-linux-arm64': 0.33.2 - '@img/sharp-linux-s390x': 0.33.2 - '@img/sharp-linux-x64': 0.33.2 - '@img/sharp-linuxmusl-arm64': 0.33.2 - '@img/sharp-linuxmusl-x64': 0.33.2 - '@img/sharp-wasm32': 0.33.2 - '@img/sharp-win32-ia32': 0.33.2 - '@img/sharp-win32-x64': 0.33.2 - dev: false - /shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -12546,12 +12231,6 @@ packages: - supports-color dev: true - /simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - dependencies: - is-arrayish: 0.3.2 - dev: false - /sirv@2.0.3: resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} engines: {node: '>= 10'} @@ -12673,11 +12352,6 @@ packages: source-map: 0.6.1 dev: true - /source-map@0.5.6: - resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} - engines: {node: '>=0.10.0'} - dev: false - /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -12768,35 +12442,10 @@ packages: deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' dev: true - /stack-generator@2.0.10: - resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} - dependencies: - stackframe: 1.3.4 - dev: false - /stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true - /stackframe@1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - dev: false - - /stacktrace-gps@3.1.2: - resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} - dependencies: - source-map: 0.5.6 - stackframe: 1.3.4 - dev: false - - /stacktrace-js@2.0.2: - resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} - dependencies: - error-stack-parser: 2.1.4 - stack-generator: 2.0.10 - stacktrace-gps: 3.1.2 - dev: false - /std-env@3.6.0: resolution: {integrity: sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==} dev: true @@ -13396,6 +13045,7 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: true /tsup@8.0.1(ts-node@10.9.1)(typescript@5.0.4): resolution: {integrity: sha512-hvW7gUSG96j53ZTSlT4j/KL0q1Q2l6TqGBFc6/mu/L46IoNWqLLUzLRLP1R8Q7xrJTmkDxxDoojV5uCVs1sVOg==} @@ -14095,10 +13745,6 @@ packages: resolution: {integrity: sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==} dev: true - /valibot@0.13.1: - resolution: {integrity: sha512-SG2W1RHqE2LShl3p6tyERt6I+G6PQa9ZFVfkyNKXz01HBzL+tBeH5kXw/5AQeAzPJSjI3djVGBl1CyozA1kyBQ==} - dev: false - /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -14526,13 +14172,6 @@ packages: resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==} dev: true - /wasm-imagemagick@1.2.8: - resolution: {integrity: sha512-V7u80n7g+iAoV7sYgQKGSdG59J6/aSMGO0DDK0zxKnwOGjmVXyjP0yU4tX4cMrfC0t/Wk3I8TX7cmdbFQOYHpg==} - dependencies: - p-map: 2.1.0 - stacktrace-js: 2.0.2 - dev: false - /watchpack@2.4.0: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} engines: {node: '>=10.13.0'} @@ -14904,6 +14543,7 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}