Skip to content

Conversation

@subtleGradient
Copy link

@subtleGradient subtleGradient commented Sep 19, 2025

Screenshot 2025-09-18 at 8 25 41 PM

This pull request adds experimental WebAssembly (WASM) support to libansilove, enabling ANSI art rendering in browsers and Node.js. It introduces a WASM build target, scripts for building and testing the WASM wrapper, and a browser demo. Additionally, it improves platform compatibility and documents the new workflows and guidelines.

WebAssembly (WASM) Support:

  • Added a new WASM build target in CMakeLists.txt, with conditional logic to build a WASM wrapper (ansilove_wasm) using Emscripten and export key rendering functions for use in web environments. [1] [2] [3]
  • Introduced wasm/ansilove_wasm.c implementing the WASM interface, exposing version and ANSI rendering functions for JavaScript consumers.
  • Added a minimal GD header and implementation for WASM (wasm/gd.h, wasm/minigd.c) to replace the system GD library in the browser context.

Build and Development Environment:

  • Added a Nix flake (flake.nix) and .envrc for reproducible development environments, including all dependencies for native and WASM builds. [1] [2]
  • Provided scripts for building and testing the WASM wrapper (scripts/test-wasm.sh, scripts/test-wasm-node.js), including automated smoke tests and artifact copying for browser demos. [1] [2]

Browser and Node.js Demo:

  • Added an example browser demo (example/wasm/index.html) that loads the WASM module, renders a sample ANSI string, and displays the resulting PNG.

Platform Compatibility and Code Changes:

  • Updated source files (src/clean.c, src/init.c) to avoid using mmap/munmap on WASM builds, ensuring compatibility with Emscripten. [1] [2] [3] [4]

Documentation and Guidelines:

  • Expanded documentation with WASM build/run instructions in README.md and added detailed project guidelines in AGENTS.md. [1] [2]
  • Updated CLAUDE.md to reference new guidelines.

This commit adds the AGENTS.md file, which contains guidelines for contributing to the project. This includes information on project
structure, build and test commands, coding style, testing guidelines, and commit/pull request guidelines.
Introduces a flake.nix file to manage the development environment for libansilove using Nix Flakes. This includes defining packages and a
development shell with necessary build tools and libraries.
This commit introduces the `flake.lock` file, which pins the exact versions of the dependencies used by the Nix flake. This ensures
reproducible builds by locking down the revisions of `nixpkgs`, `flake-utils`, and `nix-systems`.
Updates the flake.nix to use the `gd` package instead of the deprecated `libgd`. Also conditionally adds `gdb` for non-Darwin systems and
`lldb` for Darwin systems.
Include commands for entering the dev shell, building, and checking the flake. Also add a note about updating the flake.lock file.
This commit introduces a WebAssembly target for the ansilove library. It includes: - CMakeLists.txt modifications to conditionally build the
WASM target. - New source files in `wasm/` for the WASM wrapper and a minimal GD implementation. - A Node.js script for smoke testing the
WASM build. - A shell script to automate the WASM build process using `emcmake`. - Updated documentation and `.gitignore` to reflect the new
target.
Introduce two new helper scripts to test the WASM build: - `test-wasm-node.js`: Reruns the WASM smoke test via Node.js, useful for quick
iteration. - `test-wasm-browser.sh`: Launches a local server and drives Safari via JXA to perform an end-to-end smoke test.

This provides more robust testing for the WebAssembly build.
@subtleGradient subtleGradient marked this pull request as ready for review September 19, 2025 00:28
Copilot AI review requested due to automatic review settings September 19, 2025 00:28
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds experimental WebAssembly (WASM) support to libansilove, enabling ANSI art rendering in browsers and Node.js environments. It introduces a complete WASM build target with Emscripten integration, minimal GD library replacement, and comprehensive testing infrastructure.

Key changes:

  • WebAssembly wrapper with JavaScript-accessible API functions for ANSI rendering
  • Minimal GD library implementation using STB image writer for browser compatibility
  • Development environment improvements with Nix flake and comprehensive build/test scripts

Reviewed Changes

Copilot reviewed 17 out of 19 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
CMakeLists.txt Adds WASM build target with Emscripten configuration and exported functions
wasm/ansilove_wasm.c Implements WASM interface with exported rendering functions
wasm/minigd.c Provides minimal GD library replacement using STB image writer
wasm/gd.h Header for minimal GD implementation
wasm/stb_image_write.h Third-party STB library for PNG generation
src/init.c, src/clean.c Platform compatibility changes for WASM builds
scripts/.sh, scripts/.js Build and testing automation scripts
flake.nix, .envrc Nix development environment configuration
example/wasm/index.html Browser demo for WASM functionality
README.md Documentation for WASM build and usage instructions
AGENTS.md, CLAUDE.md Project guidelines and documentation

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.


double srcYOffset = (double)y / (double)dstH;
double syf = srcYOffset * srcH;
int sy = clamp(srcY + (int)(syf + 0.5 - 0.5), srcY, srcY + srcH - 1);
Copy link

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression (sxf + 0.5 - 0.5) and (syf + 0.5 - 0.5) simplifies to just sxf and syf. This appears to be dead code that should be simplified to (int)sxf and (int)syf respectively.

Copilot uses AI. Check for mistakes.

double srcXOffset = (double)x / (double)dstW;
double sxf = srcXOffset * srcW;
int sx = clamp(srcX + (int)(sxf + 0.5 - 0.5), srcX, srcX + srcW - 1);
Copy link

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression (sxf + 0.5 - 0.5) and (syf + 0.5 - 0.5) simplifies to just sxf and syf. This appears to be dead code that should be simplified to (int)sxf and (int)syf respectively.

Copilot uses AI. Check for mistakes.
Comment on lines +391 to +397
if (!im->trueColor) {
int idx = (int)im->pixels[i];
if (idx == im->transparent)
rgba[i * 4 + 3] = 0;
else
rgba[i * 4 + 3] = 255;
}
Copy link

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code block duplicates the palette index lookup from lines 381-386. The idx variable is already calculated above and should be reused instead of recalculating (int)im->pixels[i].

Copilot uses AI. Check for mistakes.
This commit introduces significant changes related to the packaging and documentation of libansilove for npm distribution.

Key changes include:

- **New npm package structure:** A dedicated `npm/libansilove-wasm` directory has been created for the WebAssembly build, including a
TypeScript wrapper, build scripts, and testing utilities. - **Bun integration:** A new `npm/packages/libansilove-demo-bun` package
demonstrates a basic React application built with Bun, showcasing its capabilities. - **Updated documentation:** README files and AGENTS.md
have been updated to reflect the new npm packages, usage instructions, and development workflows. - **Build and verification scripts:** New
scripts for cleaning, compiling, copying WASM artifacts, and verifying the npm distribution have been added. - **Version synchronization:**
A script to automatically synchronize the npm package version with the C header definition is now included. - **`.gitignore` updates:**
Added patterns to ignore common npm build artifacts and dependencies.
Adjust the npm package structure and imports to accommodate a dedicated browser entry point. This change ensures that the WebAssembly module
is correctly resolved and loaded in browser environments.

Additionally, this commit updates the build and testing scripts to reflect the new package structure and includes necessary CMake
configuration for ES6 module support. The `.gitignore` and documentation files have also been updated to align with these changes.
This commit splits the WASM build into two separate targets: one for the browser and one for Node.js.

The browser build uses `-sENVIRONMENT=web,worker` and outputs `libansilove.browser.js` and `libansilove.browser.wasm`. It also updates the
`example/wasm/index.html` to use the new browser-specific output.

The Node.js build uses `-sENVIRONMENT=node` and outputs `libansilove.node.cjs` and `libansilove.node.wasm`.

This separation allows for more targeted builds and avoids potential compatibility issues between different environments. The `.gitignore`
and package files have been updated accordingly.
This commit introduces several changes to improve the WASM build process and integration:

- The browser WASM output is now emitted as an ES module (`.mjs`), aligning with modern JavaScript practices. - A new
`npm/packages/libansilove` directory has been added to encapsulate the npm package for `libansilove`. - A `build-wasm.bun.ts` script within
the new npm package handles the WASM build process using Bun. - The README and example HTML have been updated to reflect the ES module
changes. - `.gitignore` has been updated to include the new `.wasm-build` directory. - `flake.nix` has been updated to include `bun` in the
development environment.
This commit updates the build process to output WebAssembly modules as ESM (`.mjs`). This change simplifies module loading and ensures
better compatibility with modern JavaScript environments.

The `.gitignore` and CMake build system have been updated to reflect the new `.mjs` file extensions for the generated WASM JavaScript
wrappers and Node.js CJS modules. The `libansilove-wasm` npm package has been removed and its functionality integrated into the main
`libansilove` package.
This commit refactors the WebAssembly build process to use Bun scripts. The `CMakeLists.txt` file has been updated to remove the Emscripten
build logic, and the `npm/packages/libansilove` directory now contains the necessary Bun scripts and configurations for building and
packaging the WebAssembly artifacts.

The following changes were made: - Updated `AGENTS.md` and `README.md` to reflect the new build process. - Removed Emscripten-specific CMake
options and targets. - Created new Bun scripts in `npm/packages/libansilove` for building, preparing the package, and verifying the output.
- Updated `scripts/test-wasm.sh` to use Bun and the new build scripts. - Updated `scripts/test-wasm-node.js` to point to the new output
directory.
This commit consolidates the WebAssembly build process and distribution strategy.

The legacy `example/wasm` artifacts and corresponding build/test scripts have been removed. The `npm/packages/libansilove` workspace now
serves as the single source of truth for building, testing, and distributing the WebAssembly runtime.

Key changes: - The `.gitignore` now excludes WASM artefacts. - Documentation in `README.md` and `AGENTS.md` has been updated to reflect the
new commands and workflow. - The `libansilove` npm package exports a unified `load` function that works across environments (browser,
Node.js, Deno, Bun). - The build process (`build-wasm.bun.ts`) now generates `libansilove.js` and `libansilove.wasm` directly into `dist/`.
- The `prepare-package.ts` script copies these artifacts to `dist/`. - The old separate browser/node WASM modules (`.mjs` files) have been
removed in favor of a single `.js` file. - Tests have been updated to use the unified `load` function.
@subtleGradient subtleGradient marked this pull request as draft September 19, 2025 04:05
@subtleGradient
Copy link
Author

working on adding some npm packages in here to make the wasm build usable in the ts/js ecosystem

The `libansilove.js` and `libansilove.wasm` artifacts are now placed within a `generated` subdirectory of `dist`. This change improves
organization and clarity by grouping generated files.

The `package.json` has been updated to reflect the new import paths for these files. The `prepare-package.ts` script now ensures that the
`dist/generated` directory exists and copies the artifacts there.

Additionally, memory management in `src/bindings.ts` has been improved by using `using` for `_malloc` and `freePng`, ensuring resources are
properly cleaned up.
The `RenderInput` type now accepts `ArrayBuffer` in addition to `string` and `Uint8Array`. This allows for more flexible handling of input
data.

Additionally, a new test case for `CL!-AL07.ANS` has been added to ensure compatibility with existing rendering outputs.
Adds `pngjs` to dependencies and updates the `bun.lock` file. Also includes a new `tmp.png` entry to `.gitignore`. Refactors input handling
for the `renderAnsi` function to better support various input types. Introduces a helper function `toUint8Array` for cleaner input
conversion. Enhances test coverage by comparing PNG dimensions and decoded pixel data.
Downgrade @types/pngjs to version 6.0.5 to align with the version specified in bun.lock. This change ensures consistency between the
lockfile and the package.json in the libansilove package.
Move types to their logical groups to improve readability of the exported types.
Move the `load` function and related types and exports to a new `libansilove.ts` file. This improves organization by separating the main
loading logic from the core bindings implementation. The `index.ts` file now serves as a simple re-exporting module.

Also includes minor adjustments to TypeScript types and default values in `bindings.ts` for better clarity and correctness.
The `renderAnsi` function has been updated to accept a `scaleFactor` option, allowing users to upscale the rendered output. Additionally,
specific error classes have been introduced to provide more granular information about potential issues encountered during rendering. This
includes mapping libansilove's internal error codes to distinct JavaScript error types.
Introduce a conditional compilation for Emscripten to allow building the wasm shim with a host clang toolchain. This ensures that the
`EMSCRIPTEN_KEEPALIVE` macro is defined even when not using Emscripten, preventing build errors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant