Skip to content
Draft

wasm #24

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b3ed697
Add AGENTS.md file
subtleGradient Sep 18, 2025
0308363
Add flake.nix for Nix development environment
subtleGradient Sep 18, 2025
cfebe51
Refactor flake.nix for readability
subtleGradient Sep 18, 2025
d64a8ba
Add flake.lock file
subtleGradient Sep 18, 2025
695c512
Refactor: Use gd instead of libgd
subtleGradient Sep 18, 2025
af32bf3
Add Nix and direnv ignores to .gitignore
subtleGradient Sep 18, 2025
3bc0366
Add Nix flake instructions
subtleGradient Sep 18, 2025
efa017d
feat: Add WebAssembly target
subtleGradient Sep 19, 2025
1ea95dc
Add Node.js and Safari helpers for WASM
subtleGradient Sep 19, 2025
8f3e1d4
Update documentation and configuration for npm packages
subtleGradient Sep 19, 2025
fa00943
Refactor wasm package structure
subtleGradient Sep 19, 2025
f5bef98
Add Ansiove component to demo
subtleGradient Sep 19, 2025
dd390eb
Refactor: Split WASM builds for browser and Node.js
subtleGradient Sep 19, 2025
c2a5e89
Refactor WASM build to use ES modules
subtleGradient Sep 19, 2025
2f3839c
Refactor: Migrate WASM output to ESM modules
subtleGradient Sep 19, 2025
cd6568d
Refactor Wasm build to use Bun scripts
subtleGradient Sep 19, 2025
b45fa10
Refactor: Consolidate WASM build and distribution
subtleGradient Sep 19, 2025
6439f3c
Refactor: Move generated WASM and JS to /generated
subtleGradient Sep 19, 2025
a5c97be
feat: Add ArrayBuffer support for render input
subtleGradient Sep 19, 2025
7fed872
Update dependencies and add PNGjs
subtleGradient Sep 19, 2025
900421f
Update pngjs types to match bun.lock
subtleGradient Sep 19, 2025
8eba17d
Reorder type exports for clarity
subtleGradient Sep 19, 2025
a3671a1
Refactor: Move libansilove exports to a dedicated file
subtleGradient Sep 19, 2025
4c18a63
Add `scaleFactor` option and specific error types
subtleGradient Sep 19, 2025
54051b0
Update .gitignore
subtleGradient Sep 19, 2025
0531c3c
Add Emscripten build support
subtleGradient Sep 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use flake
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,24 @@

# build products
build/
build-wasm/
npm/packages/libansilove/.wasm-build/

# Nix
result
result-*/

# direnv
.direnv/

# CMake
CMakeFiles
CMakeCache.txt
Makefile
cmake_install.cmake

# npm package build
node_modules/
npm/packages/*/dist/
tmp.png
wasm/*.o
38 changes: 38 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Repository Guidelines

## Project Structure & Module Organization
libansilove is a C library that converts ANSI and related art files to PNG. Core headers live in `include/`, while the implementation sits in `src/` with `loaders/` containing format-specific decoders and `fonts/` bundling built-in typefaces. Cross-platform fallbacks are under `compat/`. The `example/` directory shows how to invoke the API end-to-end, and `man/` provides installed manual pages. Dedicated fuzzing harnesses reside in `fuzz/`; build them only when running sanitizer-heavy tests.

## Build, Test, and Development Commands
- `cmake -S . -B build -DCMAKE_BUILD_TYPE=Release`: configure the project after installing GD headers and libs.
- `cmake --build build --parallel`: compile shared and static variants of the library.
- `cmake --build build --target install --parallel`: install artifacts into the default prefix.
- `cmake -S fuzz -B fuzz-build`: set up clang-based libFuzzer targets.
- `cmake --build fuzz-build --parallel`: produce fuzz binaries such as `ansi` and `tundra`.
- `nix develop`: enter the flake-backed dev shell with clang, pkg-config, GD, and platform-appropriate debugger preinstalled.
- `nix build`: build the default flake package (shared/static library) without touching your host toolchain.
- `nix flake check`: validate the flake packages and dev shell evaluate cleanly across supported systems.
- `nix develop --command bash -lc 'cd npm/packages/libansilove && bun run build'`: rebuild the WebAssembly artefacts and stage the publishable dist folder.
- `cd npm/packages/libansilove && bun test`: run the smoke test suite (includes wasm build).
- `cd npm/packages/libansilove && bun run verify`: load the packaged output and ensure PNG rendering still works.
- `cd npm/packages/libansilove && npm pack`: inspect the npm tarball after a successful build.

## Coding Style & Naming Conventions
- Target C99 with the default warning set (`-Wall -Wextra -pedantic`).
- Indent with tabs for blocks; align wrapped parameters using spaces as needed, and avoid trailing whitespace.
- Public APIs stay in `include/ansilove.h` and use the `ansilove_*` prefix; internal helpers remain lowercase with underscores and `static` linkage.
- Mirror existing filenames (`loadfile.c`, `savefile.c`) when adding new modules or loaders.

## Testing Guidelines
- There is no unit-test harness; validate behavior with the example app and fuzzers.
- After building, run `build/example/ansilove_example <input.ans>` to confirm PNG output.
- For fuzzing, execute `./fuzz-build/ansi -runs=10000 corpus/` (seed the corpus with representative art files). Investigate sanitizer reports immediately and add reproducer samples.
- Ensure new formats or options ship with updated example inputs or fuzz seeds that exercise the paths.
- If you touch the flake, rerun `nix build` and `nix flake check` and commit the updated `flake.lock` (keep changes reproducible).
- For the wasm target, keep `bun run build` passing inside `npm/packages/libansilove` under `nix develop`; it rebuilds the WebAssembly artefacts every run.
- Safari automation relies on “Allow JavaScript from Apple Events” in Preferences ▸ Advanced. The JXA harness (now in `npm/packages/libansilove-demo-bun/scripts`) should be updated to target the Bun dev server when the demo is rebuilt.

## Commit & Pull Request Guidelines
- Commit messages follow sentence case with concise statements ending in a period (for example, `Update ChangeLog.`).
- Keep functional changes and formatting adjustments in separate commits and ensure files build before pushing.
- Pull requests should summarize the change, call out impacted loaders, and link tracking issues. Note which build or fuzz commands were run, and attach PNG outputs or screenshots when visual diffs help reviewers.
1 change: 1 addition & 0 deletions CLAUDE.md
73 changes: 41 additions & 32 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,24 @@ project(ansilove C)
include(CheckFunctionExists)
include(GNUInstallDirs)

set(IS_EMSCRIPTEN OFF)
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
set(IS_EMSCRIPTEN ON)
endif()

# Check if system has strtonum
check_function_exists(strtonum HAVE_STRTONUM)
check_function_exists(reallocarray HAVE_REALLOCARRAY)

# Additional include directories for compat functions and headers
include_directories("compat" "include" "src")

# GD
find_path(GD_INCLUDE_DIRS gd.h)
find_library(GD_LIBRARIES NAMES gd REQUIRED)
include_directories(${GD_INCLUDE_DIRS})
if(NOT IS_EMSCRIPTEN)
# GD
find_path(GD_INCLUDE_DIRS gd.h)
find_library(GD_LIBRARIES NAMES gd REQUIRED)
include_directories(${GD_INCLUDE_DIRS})
endif()

set(SRC src/clean.c src/drawchar.c src/fonts.c src/error.c src/loadfile.c src/init.c src/output.c src/savefile.c)
set(LOADERS src/loaders/ansi.c src/loaders/artworx.c src/loaders/binary.c src/loaders/icedraw.c src/loaders/pcboard.c src/loaders/tundra.c src/loaders/xbin.c)
Expand All @@ -39,31 +46,33 @@ endif()

add_definitions(-D_GNU_SOURCE -Wall -Wextra -pedantic)

add_library(objlib OBJECT ${SRC} ${LOADERS})
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE ON)

add_library(ansilove SHARED $<TARGET_OBJECTS:objlib>)
add_library(ansilove-static STATIC $<TARGET_OBJECTS:objlib>)

target_link_libraries(ansilove ${GD_LIBRARIES} m)

set_target_properties(ansilove PROPERTIES
VERSION ${LIB_VERSION_STRING} SOVERSION ${LIB_VERSION_MAJOR}
C_VISIBILITY_PRESET hidden)

install(TARGETS ansilove DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(TARGETS ansilove-static DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES include/ansilove.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES man/libansilove.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_clean.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_error.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_init.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_loadfile.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_savefile.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_ansi.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_artworx.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_binary.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_icedraw.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_pcboard.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_tundra.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_xbin.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
if(NOT IS_EMSCRIPTEN)
add_library(objlib OBJECT ${SRC} ${LOADERS})
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE ON)

add_library(ansilove SHARED $<TARGET_OBJECTS:objlib>)
add_library(ansilove-static STATIC $<TARGET_OBJECTS:objlib>)

target_link_libraries(ansilove ${GD_LIBRARIES} m)

set_target_properties(ansilove PROPERTIES
VERSION ${LIB_VERSION_STRING} SOVERSION ${LIB_VERSION_MAJOR}
C_VISIBILITY_PRESET hidden)

install(TARGETS ansilove DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(TARGETS ansilove-static DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES include/ansilove.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES man/libansilove.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_clean.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_error.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_init.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_loadfile.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_savefile.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_ansi.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_artworx.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_binary.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_icedraw.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_pcboard.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_tundra.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
install(FILES man/ansilove_xbin.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3/)
endif()
14 changes: 14 additions & 0 deletions PR_DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

# Add reproducible flake env and WebAssembly target.
## Summary
- keep the dev shell ergonomic by wiring in Emscripten alongside clang/cmake and documenting the workflow for future agents
- compile libansilove to a single universal `libansilove.{js,wasm}` bundle and surface a small TypeScript API around `renderAnsi`
- stand up the `libansilove` npm workspace with Bun-based build scripts, packaging helpers, smoke tests, and publishable dist output
- prune the legacy shell helpers/demo assets in favour of the new universal loader and move the Safari JXA harness into the Bun demo package
- refresh README/AGENTS with the simplified commands for building, testing, and verifying the wasm pipeline

## Testing
- nix flake check
- nix develop --command bash -lc 'cd npm/packages/libansilove && bun run build'
- cd npm/packages/libansilove && bun test
- cd npm/packages/libansilove && bun run verify
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,49 @@ header files.

make install

# WebAssembly

A unified WebAssembly wrapper now lives in `npm/packages/libansilove`. Use the flake-backed
development shell so Emscripten and Bun are on `PATH`:

nix develop --command bash -lc 'cd npm/packages/libansilove && bun run build'

The build performs a fresh `emcc` compile, emits TypeScript shims, and stages the publishable
artefacts under `npm/packages/libansilove/dist/`. Consumers load the module with:

```
import { load } from 'libansilove';

const lib = await load();
const { png } = lib.renderAnsi('Hello from libansilove!\r\n');
```

Overwrite the default `locateFile` if your bundler serves the `.wasm` payload from a custom URL:

```
await load({ locateFile: (path) => new URL(`/static/${path}`, window.location.href).toString() });
```

`bun run verify` executes a smoke test against the packaged output and ensures the PNG pipeline
still works. Use `npm pack` after a build to inspect the final publishing tarball.

# npm package

The `npm/packages/libansilove` workspace contains everything required for publishing. The
important commands:

```
bun install
bun run build
bun test
bun run verify
```

The build step regenerates the WebAssembly artefacts (stored in
`npm/packages/libansilove/generated/`) and copies them into `dist/` alongside the compiled
JavaScript and declaration files.


# Packages

libansilove packages are available for:
Expand Down
78 changes: 78 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 71 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
description = "Development environment for libansilove";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};

outputs = {
self,
nixpkgs,
nixpkgs-unstable,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs {inherit system;};
unstablePkgs = import nixpkgs-unstable { inherit system; };
version = "1.4.2";
in {
packages.default = pkgs.stdenv.mkDerivation {
pname = "libansilove";
inherit version;
src = ./.;
nativeBuildInputs = [
pkgs.cmake
pkgs.pkg-config
];
buildInputs = [
pkgs.gd
];
cmakeFlags = [
"-DCMAKE_BUILD_TYPE=Release"
];
meta = with pkgs.lib; {
description = "Library to convert ANSI and artscene formats to PNG";
homepage = "https://www.ansilove.org";
license = licenses.bsd2;
maintainers = [];
platforms = platforms.unix;
};
};

devShells.default = pkgs.mkShell {
packages =
[
pkgs.cmake
pkgs.ninja
pkgs.pkg-config
pkgs.gd
pkgs.emscripten
unstablePkgs.bun
pkgs.clang
pkgs.clang-tools
]
++ pkgs.lib.optionals (!pkgs.stdenv.isDarwin) [
pkgs.gdb
]
++ pkgs.lib.optionals pkgs.stdenv.isDarwin [
pkgs.lldb
];
shellHook = ''
export CC=${pkgs.clang}/bin/clang
export CXX=${pkgs.clang}/bin/clang++
echo "libansilove dev shell loaded."
'';
};
}
);
}
Loading