Last active
August 29, 2023 22:12
-
-
Save postspectacular/08098359f75fa703a2cda64b1258a459 to your computer and use it in GitHub Desktop.
#HowToThing #010 — Creating a basic web app with Zig/WebAssembly and the extensible https://thi.ng/wasm-api and its https://thi.ng/wasm-api-dom add-on module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const wasm = @import("wasm-api"); | |
const dom = @import("wasm-api-dom"); | |
// expose thi.ng/wasm-api core API (incl. panic handler & allocation fns) | |
pub usingnamespace wasm; | |
// main entry point | |
export fn start() void { | |
init() catch |e| @panic(@errorName(e)); | |
wasm.printStr("app started"); | |
} | |
// allocator, also exposed & used by JS-side WasmBridge & DOM module | |
// see further comments in: | |
// https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig/lib.zig | |
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | |
pub const WASM_ALLOCATOR = gpa.allocator(); | |
// since various initialization functions can return errors | |
// we're bundling them all in a single function, which is called by start() | |
// and so only needs one code site for error handling | |
fn init() !void { | |
// ...other initializations... | |
// create DOM tree/elements using thi.ng/wasm-api-dom (see readme) | |
_ = dom.createElement(&.{ | |
.tag = "div", | |
.id = "app", | |
// CSS classes from tachyons.io for simplicity | |
.class = "bg-light-blue ma3 pa3", | |
// attach to document.body as parent element | |
.parent = dom.body, | |
// list of child elements | |
.children = dom.children(&.{ | |
.{ .tag = "h1", .class = "ma0", .text = "Hello Zig! 👋" }, | |
.{ | |
.tag = "button", | |
.id = "counter", | |
// initial button label (will be modified by event handler) | |
.text = "Click me!", | |
.attribs = dom.attribs(&.{ | |
// specify click event handler | |
dom.Attrib.event("click", incCounter, null), | |
}), | |
}, | |
}), | |
}); | |
} | |
// counter state | |
var counter: usize = 0; | |
// button event listener to update counter & label text | |
fn incCounter(e: *const dom.Event, _: ?*anyopaque) void { | |
counter += 1; | |
// create new label using temp buffer | |
var buf: [16]u8 = undefined; | |
var label = std.fmt.bufPrintZ(&buf, "Clicks: {d:0>4}", .{counter}) catch return; | |
// apply label to button element | |
dom.setInnerText(e.target, label); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
pub fn build(b: *std.Build) void { | |
// define library build step | |
// use helper build script from hybrid thi.ng/wasm-api package | |
const lib = @import("node_modules/@thi.ng/wasm-api/zig/build.zig").wasmLib(b, .{ | |
.modules = &.{ | |
// declare other hybrid TS/Zig modules and their entry points | |
.{ .name = "wasm-api-dom", .path = "@thi.ng/wasm-api-dom/zig/lib.zig" }, | |
}, | |
// optionally define release mode | |
.optimize = .ReleaseSmall, | |
}); | |
// create lib install step and add it to the top-level install step | |
b.installArtifact(lib); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { Fn0 } from "@thi.ng/api"; | |
import { WasmBridge, type WasmExports } from "@thi.ng/wasm-api"; | |
import { WasmDom, type WasmDomExports } from "@thi.ng/wasm-api-dom"; | |
// (this import assumes we're using ViteJS, adjust as necessary...) | |
import WASM_URL from "./main.wasm?url"; | |
// interface for combining our custom WASM export(s) with those | |
// provided by the thi.ng/wasm-api libraries | |
interface WasmApp extends WasmExports, WasmDomExports { | |
// declare exposed function(s) in WASM module (see main.zig) | |
start: Fn0<void>; | |
} | |
// main app initialization | |
(async () => { | |
// create WASM bridge with extra API module(s) | |
// here we're only adding the DOM module as additional requirement | |
const bridge = new WasmBridge<WasmApp>([new WasmDom()]); | |
// load and instantiate our WASM module | |
await bridge.instantiate(fetch(WASM_URL)); | |
// call WASM start function to kick off | |
bridge.exports.start(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment