Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

## API Documentation

If you want a clearer architecture, you can check it out [here](https://deepwiki.com/webui-dev/zig-webui)

* [https://webui-dev.github.io/zig-webui/](https://webui-dev.github.io/zig-webui/)
* [https://webui.me/docs/2.5/#/](https://webui.me/docs/2.5/#/)

Expand Down
40 changes: 32 additions & 8 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,41 @@ const builtin = @import("builtin");

const Build = std.Build;

// Minimum required Zig version for this project
const min_zig_string = "0.12.0";
const current_zig = builtin.zig_version;

// NOTE: we should note that when enable tls support we cannot compile with musl

// Compile-time check to ensure the Zig version meets the minimum requirement
comptime {
const min_zig = std.SemanticVersion.parse(min_zig_string) catch unreachable;
if (current_zig.order(min_zig) == .lt) {
@compileError(std.fmt.comptimePrint("Your Zig version v{} does not meet the minimum build requirement of v{}", .{ current_zig, min_zig }));
}
}

// Define logger and useful type aliases
const log = std.log.scoped(.WebUI);
const OptimizeMode = std.builtin.OptimizeMode;
const CrossTarget = std.Target.Query;
const Compile = Build.Step.Compile;
const Module = Build.Module;

// Default build configuration options
const default_isStatic = true;
const default_enableTLS = false;

pub fn build(b: *Build) !void {
// Parse command-line options or use defaults
const isStatic = b.option(bool, "is_static", "whether lib is static") orelse default_isStatic;
const enableTLS = b.option(bool, "enable_tls", "whether lib enable tls") orelse default_enableTLS;

// Standard build options for target and optimization
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

// TLS support has some limitations
if (enableTLS) {
log.info("enable TLS support", .{});
if (!target.query.isNative()) {
Expand All @@ -39,15 +46,16 @@ pub fn build(b: *Build) !void {
}
}

// create a options for command parameter
// Create build options that will be used as a module
const flags_options = b.addOptions();

// add option
// Configure compile-time options
flags_options.addOption(bool, "enableTLS", enableTLS);

// create a new module for flags options
// Create a module that exposes the options
const flags_module = flags_options.createModule();

// Get the webui dependency with appropriate options
const webui = b.dependency("webui", .{
.target = target,
.optimize = optimize,
Expand All @@ -56,6 +64,7 @@ pub fn build(b: *Build) !void {
.verbose = .err,
});

// Create the webui module that applications can import
const webui_module = b.addModule("webui", .{
.root_source_file = b.path(b.pathJoin(&.{ "src", "webui.zig" })),
.imports = &.{
Expand All @@ -65,22 +74,26 @@ pub fn build(b: *Build) !void {
},
},
});
// Link against the webui library
webui_module.linkLibrary(webui.artifact("webui"));
if (!isStatic) {
// For dynamic libraries, install the shared library
b.installArtifact(webui.artifact("webui"));
}

// generate docs
// Setup documentation generation
generate_docs(b, optimize, target, flags_module);

// build examples
// Build example applications
build_examples(b, optimize, target, webui_module, webui.artifact("webui")) catch |err| {
log.err("failed to build examples: {}", .{err});
std.process.exit(1);
};
}

// Function to generate API documentation
fn generate_docs(b: *Build, optimize: OptimizeMode, target: Build.ResolvedTarget, flags_module: *Module) void {
// Create a temporary object for documentation generation
const webui_lib = b.addObject(.{
.name = "webui_lib",
.root_source_file = b.path(b.pathJoin(&.{ "src", "webui.zig" })),
Expand All @@ -90,8 +103,10 @@ fn generate_docs(b: *Build, optimize: OptimizeMode, target: Build.ResolvedTarget

webui_lib.root_module.addImport("flags", flags_module);

// Create a build step for documentation
const docs_step = b.step("docs", "Generate docs");

// Setup documentation installation
const docs_install = b.addInstallDirectory(.{
.source_dir = webui_lib.getEmittedDocs(),
.install_dir = .prefix,
Expand All @@ -101,15 +116,18 @@ fn generate_docs(b: *Build, optimize: OptimizeMode, target: Build.ResolvedTarget
docs_step.dependOn(&docs_install.step);
}

// Function to build all example applications
fn build_examples(b: *Build, optimize: OptimizeMode, target: Build.ResolvedTarget, webui_module: *Module, webui_lib: *Compile) !void {

// we use lazyPath to get absolute path of package
// Get the absolute path to the examples directory
var lazy_path = b.path("examples");

// Create a step to build all examples
const build_all_step = b.step("examples", "build all examples");

const examples_path = lazy_path.getPath(b);

// Open the examples directory for iteration
var iter_dir = std.fs.openDirAbsolute(
examples_path,
.{ .iterate = true },
Expand All @@ -123,6 +141,7 @@ fn build_examples(b: *Build, optimize: OptimizeMode, target: Build.ResolvedTarge

var itera = iter_dir.iterate();

// Iterate through all subdirectories in the examples directory
while (try itera.next()) |val| {
if (val.kind != .directory) {
continue;
Expand All @@ -131,32 +150,37 @@ fn build_examples(b: *Build, optimize: OptimizeMode, target: Build.ResolvedTarge
const example_name = val.name;
const path = b.pathJoin(&.{ "examples", example_name, "main.zig" });

// Create an executable for each example
const exe = b.addExecutable(.{
.name = example_name,
.root_source_file = b.path(path),
.target = target,
.optimize = optimize,
});

// Add the webui module and link against the library
exe.root_module.addImport("webui", webui_module);
exe.linkLibrary(webui_lib);

// Setup installation
const exe_install = b.addInstallArtifact(exe, .{});

build_all_step.dependOn(&exe_install.step);

// Create a run step for the example
const exe_run = b.addRunArtifact(exe);
exe_run.step.dependOn(&exe_install.step);

// Set the working directory for the run
const cwd = b.path(b.pathJoin(&.{ "examples", example_name }));

exe_run.setCwd(cwd);

// Create a named step to run this specific example
const step_name = try std.fmt.allocPrint(b.allocator, "run_{s}", .{example_name});

const step_desc = try std.fmt.allocPrint(b.allocator, "run_{s}", .{example_name});

const exe_run_step = b.step(step_name, step_desc);
exe_run_step.dependOn(&exe_run.step);
}
}

68 changes: 64 additions & 4 deletions examples/call_zig_from_js/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,40 @@ const webui = @import("webui");
const html = @embedFile("index.html");

pub fn main() !void {
// Create a new WebUI window object
var nwin = webui.newWindow();

// Use binding function instead of standard bind function
// binding is an advanced API that automatically handles parameter type conversion and function signature adaptation
// It allows using native Zig function signatures without needing to handle Event pointers directly
// Here we bind the HTML/JS "my_function_string" to Zig function getString
_ = try nwin.binding("my_function_string", getString);
// Equivalent using traditional bind function which requires manual Event handling
// _ = try nwin.bind("my_function_string", my_function_string);

// Bind integer handler function, binding automatically converts JS parameters to corresponding Zig types
_ = try nwin.binding("my_function_integer", getInteger);
// _ = try nwin.bind("my_function_integer", my_function_integer);
_ = try nwin.bind("my_function_boolean", my_function_boolean);
_ = try nwin.bind("my_function_with_response", my_function_with_response);
_ = try nwin.bind("my_function_raw_binary", my_function_raw_binary);


// Bind boolean handler function, also with automatic type conversion
_ = try nwin.binding("my_function_boolean", getBool);
// _ = try nwin.bind("my_function_boolean", my_function_boolean);

// Bind function with response, binding supports using event object directly for responses
_ = try nwin.binding("my_function_with_response", getResponse);
// _ = try nwin.bind("my_function_with_response", my_function_with_response);

// Bind function for handling binary data, binding supports raw binary data processing
_ = try nwin.binding("my_function_raw_binary", raw_binary);
// _ = try nwin.bind("my_function_raw_binary", my_function_raw_binary);

// Show the window with embedded HTML content
try nwin.show(html);

// Wait for all windows to close, this will block the current thread
webui.wait();

// Clean up all resources
webui.clean();
}

Expand Down Expand Up @@ -75,6 +95,12 @@ fn my_function_integer(e: *webui.Event) void {
std.debug.print("my_function_integer 4: {}\n", .{float_1});
}

fn getBool(b1: bool, b2: bool) void {
std.debug.print("boolean is {},{}", .{
b1, b2,
});
}

fn my_function_boolean(e: *webui.Event) void {
// JavaScript:
// my_function_boolean(true, false);
Expand All @@ -89,6 +115,13 @@ fn my_function_boolean(e: *webui.Event) void {
std.debug.print("my_function_bool 2: {}\n", .{status_2});
}

fn getResponse(e: *webui.Event,n1: i64, n2: i64) void {
const res = n1 * n2;
std.debug.print("my_function_with_response: {} * {} = {}\n", .{ n1, n2, res });
// Send back the response to JavaScript
e.returnValue(res);
}

fn my_function_with_response(e: *webui.Event) void {
// JavaScript:
// my_function_with_response(number, 2).then(...)
Expand All @@ -104,6 +137,33 @@ fn my_function_with_response(e: *webui.Event) void {
e.returnValue(res);
}

fn raw_binary(e: *webui.Event, raw_1: [:0]const u8, raw_2: [*]const u8) void {
// Or e.getSizeAt(0);
const len_1 = e.getSize() catch return;
const len_2 = e.getSizeAt(1) catch return;

// Print raw_1
std.debug.print("my_function_raw_binary 1 ({} bytes): ", .{len_1});
for (0..len_1) |i| {
std.debug.print("0x{x} ", .{raw_1[i]});
}
std.debug.print("\n", .{});

// Check raw_2 (Big)
// [0xA1, 0x00..., 0xA2]
var vaild = false;

if (raw_2[0] == 0xA1 and raw_2[len_2 - 1] == 0xA2) {
vaild = true;
}

// Print raw_2
std.debug.print("my_function_raw_binary 2 big ({} bytes): valid data? {s}\n", .{
len_2,
if (vaild) "Yes" else "No",
});
}

fn my_function_raw_binary(e: *webui.Event) void {
// JavaScript:
// my_function_raw_binary(new Uint8Array([0x41]), new Uint8Array([0x42, 0x43]));
Expand Down
Loading
Loading