989 lines
46 KiB
Zig
989 lines
46 KiB
Zig
const features = [_]Feature{
|
|
.{ .icon_fn = icons.Lightning, .title = "Fast Performance", .description = "Significantly faster at SSR than many other frameworks. Optimized for speed and low latency." },
|
|
// .{ .icon_fn = icons.Shield, .title = "Deterministic Safety", .description = "Built on Zig's safety features. Zero undefined behavior at runtime. Predictable and reliable." },
|
|
.{ .icon_fn = icons.Code, .title = "Familiar Syntax", .description = "Familiar plain HTML-style markup, with full access to Zig's control flow." },
|
|
// .{ .icon_fn = icons.Globe, .title = "Edge First", .description = "Native support for WASI and Edge runtimes. Deploy to Cloudflare Workers or Vercel Edge with zero config." },
|
|
// .{ .icon_fn = icons.Package, .title = "Zero Dependencies", .description = "Self-contained framework. No node_modules, no external runtimes. Just one small binary." },
|
|
.{ .icon_fn = icons.Folder, .title = "File System Routing", .description = "Just files in folders to create routes." },
|
|
.{ .icon_fn = icons.Server, .title = "API Routes", .description = "Create API endpoints by adding route.zig files to your project." },
|
|
.{ .icon_fn = icons.Monitor, .title = "Hybrid Rendering", .description = "Client-side rendering for interactive experiences when you need it." },
|
|
.{ .icon_fn = icons.Flow, .title = "Control Flow", .description = "if/else, for/while, and switch all work as expected. It's just Zig syntax." },
|
|
.{ .icon_fn = icons.Wrench, .title = "Developer Tooling", .description = "CLI, hot reload, and editor extensions for the best development experience." },
|
|
.{ .icon_fn = icons.Rocket, .title = "Deploy Anywhere", .description = "Standalone binaries, Cloudflare, Vercel, or any edge runtime host. Build once, run anywhere." },
|
|
.{ .icon_fn = icons.Layers, .title = "Predictable Memory", .description = "Explicit memory management of Zig, no hidden allocations." },
|
|
};
|
|
|
|
const deployments = [_]Deployment{
|
|
.{ .icon_fn = icons.Terminal, .title = "Standalone", .description = "Deploy as a self-contained binary on Linux, macOS, Windows andy many more platforms that Zig supports." },
|
|
.{ .icon_fn = icons.Globe, .title = "Edge Anywhere", .description = "Deploy to the edge via WASI. Native bindings for Cloudflare and Vercel." },
|
|
.{ .icon_fn = icons.Layers, .title = "Static", .description = "Generate a statically pre-rendered site at build time. Deploy to GitHub Pages, Netlify, S3, or any CDN." },
|
|
.{ .icon_fn = icons.Package, .title = "Cross-Platform", .description = "Build for any target from any host. Easily cross-compile optimized binaries for x86_64, aarch64, and more." },
|
|
};
|
|
|
|
const benchmark_cwv_rows = [_]BenchmarkRow{
|
|
.{ .label = "Ziex", .value = 98, .display = "98%", .url = "" },
|
|
.{ .label = "Next.js", .value = 44, .display = "44%", .url = "https://nextjs.org" },
|
|
.{ .label = "Nuxt", .value = 42, .display = "42%", .url = "https://nuxt.com" },
|
|
.{ .label = "Leptos", .value = 27, .display = "27%", .url = "https://leptos.dev" },
|
|
};
|
|
|
|
// Import benchmark data from auto-generated file
|
|
const bench_data = @import("bench.zon");
|
|
const benchfig = @import("benchfig.zon");
|
|
const bench_run = bench_data.run;
|
|
const bench_results = bench_data.results;
|
|
const bench_len = @typeInfo(@TypeOf(bench_results)).@"struct".field_types.len;
|
|
|
|
// Helper function to format large numbers (e.g., 25153 -> "25k")
|
|
fn formatLargeNumber(n: f64) []const u8 {
|
|
if (n >= 1000) {
|
|
const k = n / 1000;
|
|
if (k >= 10) {
|
|
return std.fmt.comptimePrint("{d}k", .{@as(u32, @intFromFloat(k))});
|
|
} else {
|
|
return std.fmt.comptimePrint("{d:.1}k", .{k});
|
|
}
|
|
}
|
|
return std.fmt.comptimePrint("{d}", .{@as(u32, @intFromFloat(n))});
|
|
}
|
|
|
|
// Helper function to format memory with 1 decimal place (e.g., 9.8 MB)
|
|
fn formatMemory(mb: f64) []const u8 {
|
|
return std.fmt.comptimePrint("{d:.1} MB", .{mb});
|
|
}
|
|
|
|
// Helper function to format requests per second with comma separator
|
|
fn formatRequests(n: f64) []const u8 {
|
|
const int_val = @as(u32, @intFromFloat(n));
|
|
if (int_val >= 10000) {
|
|
const thousands = int_val / 1000;
|
|
const remainder = int_val % 1000;
|
|
return std.fmt.comptimePrint("{d},{d:0>3}", .{ thousands, remainder });
|
|
}
|
|
if (int_val >= 1000) {
|
|
const thousands = int_val / 1000;
|
|
const remainder = int_val % 1000;
|
|
return std.fmt.comptimePrint("{d},{d}", .{ thousands, remainder });
|
|
}
|
|
return std.fmt.comptimePrint("{d}", .{int_val});
|
|
}
|
|
|
|
// Helper function to format latency in milliseconds
|
|
fn formatLatency(ms: f64) []const u8 {
|
|
return std.fmt.comptimePrint("{d:.2} ms", .{ms});
|
|
}
|
|
|
|
// Helper function to format build time in seconds
|
|
fn formatBuildTime(s: f64) []const u8 {
|
|
const int_s = @as(u32, @intFromFloat(s));
|
|
if (int_s >= 60) {
|
|
const mins = int_s / 60;
|
|
const secs = int_s % 60;
|
|
return std.fmt.comptimePrint("{d}m {d}s", .{ mins, secs });
|
|
}
|
|
return std.fmt.comptimePrint("{d}s", .{int_s});
|
|
}
|
|
|
|
// Helper function to format image size (e.g., "18 MB", "1.0 GB")
|
|
fn formatImageSize(mb: f64) []const u8 {
|
|
if (mb >= 1000) {
|
|
const gb = mb / 1000;
|
|
return std.fmt.comptimePrint("{d:.1} GB", .{gb});
|
|
}
|
|
return std.fmt.comptimePrint("{d} MB", .{@as(u32, @intFromFloat(mb))});
|
|
}
|
|
|
|
// Helper function to format cold start time in milliseconds
|
|
fn formatColdStart(ms: f64) []const u8 {
|
|
return std.fmt.comptimePrint("{d} ms", .{@as(u32, @intFromFloat(ms))});
|
|
}
|
|
|
|
// Helper function to format CPU peak percentage
|
|
fn formatCpuPeak(pct: f64) []const u8 {
|
|
return std.fmt.comptimePrint("{d:.1}%", .{pct});
|
|
}
|
|
|
|
// Helper function to format binary size in MB
|
|
fn formatBinarySize(mb: f64) []const u8 {
|
|
if (mb == 0) return "N/A";
|
|
return std.fmt.comptimePrint("{d:.1} MB", .{mb});
|
|
}
|
|
|
|
// Map framework id to its website URL
|
|
fn getFrameworkUrl(comptime id: []const u8) []const u8 {
|
|
if (std.mem.eql(u8, id, "jetzig")) return "https://jetzig.dev";
|
|
if (std.mem.eql(u8, id, "leptos")) return "https://leptos.dev";
|
|
if (std.mem.eql(u8, id, "solidjs")) return "https://start.solidjs.com";
|
|
if (std.mem.eql(u8, id, "dioxus")) return "https://dioxuslabs.com";
|
|
if (std.mem.eql(u8, id, "nextjs")) return "https://nextjs.org";
|
|
return "";
|
|
}
|
|
|
|
fn sortRows(rows: []BenchmarkRow, comptime lower_is_better: bool) void {
|
|
std.sort.insertion(BenchmarkRow, rows, {}, struct {
|
|
fn lessThan(_: void, a: BenchmarkRow, b: BenchmarkRow) bool {
|
|
return if (lower_is_better) a.value < b.value else a.value > b.value;
|
|
}
|
|
}.lessThan);
|
|
}
|
|
|
|
fn buildSsrRows() [bench_len]BenchmarkRow {
|
|
@setEvalBranchQuota(10000);
|
|
var rows: [bench_len]BenchmarkRow = undefined;
|
|
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
|
|
inline for (field_names, 0..) |field_name, i| {
|
|
const d = @field(bench_results, field_name);
|
|
rows[i] = .{
|
|
.label = d.label,
|
|
.value = d.requests_per_sec,
|
|
.display = formatLargeNumber(d.requests_per_sec),
|
|
.url = getFrameworkUrl(d.id),
|
|
.tooltip_metrics = &[_]TooltipMetric{
|
|
.{ .label = "Framework", .value = d.label },
|
|
.{ .label = "Requests/sec", .value = formatRequests(d.requests_per_sec) },
|
|
.{ .label = "P50 Latency", .value = formatLatency(d.p50_latency_ms) },
|
|
.{ .label = "P99 Latency", .value = formatLatency(d.p99_latency_ms) },
|
|
},
|
|
};
|
|
}
|
|
sortRows(&rows, false);
|
|
return rows;
|
|
}
|
|
|
|
fn buildMemoryRows() [bench_len]BenchmarkRow {
|
|
@setEvalBranchQuota(10000);
|
|
var rows: [bench_len]BenchmarkRow = undefined;
|
|
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
|
|
inline for (field_names, 0..) |field_name, i| {
|
|
const d = @field(bench_results, field_name);
|
|
rows[i] = .{
|
|
.label = d.label,
|
|
.value = d.peak_memory_mb,
|
|
.display = formatMemory(d.peak_memory_mb),
|
|
.url = getFrameworkUrl(d.id),
|
|
.tooltip_metrics = &[_]TooltipMetric{
|
|
.{ .label = "Framework", .value = d.label },
|
|
.{ .label = "Idle Memory", .value = formatMemory(d.idle_memory_mb) },
|
|
.{ .label = "Peak Memory", .value = formatMemory(d.peak_memory_mb) },
|
|
},
|
|
};
|
|
}
|
|
sortRows(&rows, true);
|
|
return rows;
|
|
}
|
|
|
|
fn buildBuildTimeRows() [bench_len]BenchmarkRow {
|
|
@setEvalBranchQuota(10000);
|
|
var rows: [bench_len]BenchmarkRow = undefined;
|
|
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
|
|
inline for (field_names, 0..) |field_name, i| {
|
|
const d = @field(bench_results, field_name);
|
|
rows[i] = .{
|
|
.label = d.label,
|
|
.value = d.build_time_s,
|
|
.display = formatBuildTime(d.build_time_s),
|
|
.url = getFrameworkUrl(d.id),
|
|
.tooltip_metrics = &[_]TooltipMetric{
|
|
.{ .label = "Framework", .value = d.label },
|
|
.{ .label = "Build Time", .value = formatBuildTime(d.build_time_s) },
|
|
},
|
|
};
|
|
}
|
|
sortRows(&rows, true);
|
|
return rows;
|
|
}
|
|
|
|
fn buildColdStartRows() [bench_len]BenchmarkRow {
|
|
@setEvalBranchQuota(10000);
|
|
var rows: [bench_len]BenchmarkRow = undefined;
|
|
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
|
|
inline for (field_names, 0..) |field_name, i| {
|
|
const d = @field(bench_results, field_name);
|
|
rows[i] = .{
|
|
.label = d.label,
|
|
.value = d.cold_start_ms,
|
|
.display = formatColdStart(d.cold_start_ms),
|
|
.url = getFrameworkUrl(d.id),
|
|
.tooltip_metrics = &[_]TooltipMetric{
|
|
.{ .label = "Framework", .value = d.label },
|
|
.{ .label = "Cold Start", .value = formatColdStart(d.cold_start_ms) },
|
|
.{ .label = "Image Size", .value = formatImageSize(d.image_mb) },
|
|
},
|
|
};
|
|
}
|
|
sortRows(&rows, true);
|
|
return rows;
|
|
}
|
|
|
|
fn buildImageSizeRows() [bench_len]BenchmarkRow {
|
|
@setEvalBranchQuota(10000);
|
|
var rows: [bench_len]BenchmarkRow = undefined;
|
|
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
|
|
inline for (field_names, 0..) |field_name, i| {
|
|
const d = @field(bench_results, field_name);
|
|
rows[i] = .{
|
|
.label = d.label,
|
|
.value = d.image_mb,
|
|
.display = formatImageSize(d.image_mb),
|
|
.url = getFrameworkUrl(d.id),
|
|
.tooltip_metrics = &[_]TooltipMetric{
|
|
.{ .label = "Framework", .value = d.label },
|
|
.{ .label = "Image Size", .value = formatImageSize(d.image_mb) },
|
|
.{ .label = "Binary Size", .value = formatBinarySize(d.binary_mb) },
|
|
},
|
|
};
|
|
}
|
|
sortRows(&rows, true);
|
|
return rows;
|
|
}
|
|
|
|
fn buildCpuUsageRows() [bench_len]BenchmarkRow {
|
|
@setEvalBranchQuota(10000);
|
|
var rows: [bench_len]BenchmarkRow = undefined;
|
|
const field_names = @typeInfo(@TypeOf(bench_results)).@"struct".field_names;
|
|
inline for (field_names, 0..) |field_name, i| {
|
|
const d = @field(bench_results, field_name);
|
|
rows[i] = .{
|
|
.label = d.label,
|
|
.value = d.cpu_avg_pct,
|
|
.display = formatCpuPeak(d.cpu_avg_pct),
|
|
.url = getFrameworkUrl(d.id),
|
|
.tooltip_metrics = &[_]TooltipMetric{
|
|
.{ .label = "Framework", .value = d.label },
|
|
.{ .label = "Avg CPU", .value = formatCpuPeak(d.cpu_avg_pct) },
|
|
.{ .label = "Peak CPU", .value = formatCpuPeak(d.cpu_peak_pct) },
|
|
.{ .label = "Requests/sec", .value = formatRequests(d.requests_per_sec) },
|
|
},
|
|
};
|
|
}
|
|
sortRows(&rows, true);
|
|
return rows;
|
|
}
|
|
|
|
const benchmark_ssr_rows = buildSsrRows();
|
|
const benchmark_memory_rows = buildMemoryRows();
|
|
const benchmark_build_time_rows = buildBuildTimeRows();
|
|
const benchmark_cold_start_rows = buildColdStartRows();
|
|
const benchmark_image_size_rows = buildImageSizeRows();
|
|
const benchmark_cpu_usage_rows = buildCpuUsageRows();
|
|
|
|
const benchmark_tabs = [_]BenchmarkTab{
|
|
// .{
|
|
// .id = "bench-tab-cwv",
|
|
// .label = "Core Web Vitals",
|
|
// .metric = "% of real-world sites with good Core Web Vitals",
|
|
// .footnote = "Full report. Benchmarked against Next.js and Leptos using real-world Core Web Vitals.",
|
|
// .rows = benchmark_cwv_rows[0..],
|
|
// .checked = true,
|
|
// },
|
|
.{
|
|
.id = "bench-tab-ssr",
|
|
.label = "SSR",
|
|
.metric = "Requests per second (higher is better)",
|
|
.footnote = std.fmt.comptimePrint(
|
|
"Dockerized SSR endpoint hit with {d} requests at {d} concurrent connections, averaged over {d} runs.",
|
|
.{
|
|
benchfig.requests,
|
|
benchfig.concurrency,
|
|
benchfig.runs,
|
|
},
|
|
),
|
|
.rows = benchmark_ssr_rows[0..],
|
|
.checked = true,
|
|
},
|
|
.{
|
|
.id = "bench-tab-cpu",
|
|
.label = "CPU",
|
|
.metric = "Average CPU usage during load (lower is better)",
|
|
.footnote = "Average CPU utilization during the load test via cgroup cpu.stat. Hover for peak CPU.",
|
|
.rows = benchmark_cpu_usage_rows[0..],
|
|
.checked = false,
|
|
.lower_is_better = true,
|
|
},
|
|
.{
|
|
.id = "bench-tab-memory",
|
|
.label = "Memory",
|
|
.metric = "Peak memory under load (lower is better)",
|
|
.footnote = "Peak container RSS during load test. Lower is better. Hover for idle memory.",
|
|
.rows = benchmark_memory_rows[0..],
|
|
.checked = false,
|
|
.lower_is_better = true,
|
|
},
|
|
.{
|
|
.id = "bench-tab-cold-start",
|
|
.label = "Cold Start",
|
|
.metric = "Cold start time in milliseconds (lower is better)",
|
|
.footnote = "Time for the container to start and become ready to serve requests. Lower is better.",
|
|
.rows = benchmark_cold_start_rows[0..],
|
|
.checked = false,
|
|
.lower_is_better = true,
|
|
},
|
|
.{
|
|
.id = "bench-tab-image-size",
|
|
.label = "Size",
|
|
.metric = "Docker image size (lower is better)",
|
|
.footnote = "Size of the final Docker image. Smaller images are faster to deploy and use less storage.",
|
|
.rows = benchmark_image_size_rows[0..],
|
|
.checked = false,
|
|
.lower_is_better = true,
|
|
},
|
|
// TODO: currently it is hard to auto calc build time from inside docker build
|
|
// .{
|
|
// .id = "bench-tab-build",
|
|
// .label = "Build Time",
|
|
// .metric = "Docker image build time in seconds (lower is better)",
|
|
// .footnote = "Time to build each framework's Docker image from scratch on the host machine. Lower is better.",
|
|
// .rows = benchmark_build_time_rows[0..],
|
|
// .checked = false,
|
|
// .lower_is_better = true,
|
|
// },
|
|
|
|
};
|
|
|
|
pub fn Page(ctx: zx.PageContext) zx.Component {
|
|
const allocator = ctx.arena;
|
|
const current_year = blk: {
|
|
const timestamp: i64 = @intCast(@divFloor(std.Io.Timestamp.now(zx.io(), .real).nanoseconds, std.time.ns_per_s));
|
|
const seconds_per_year = 365.25 * 24 * 60 * 60;
|
|
const years_since_epoch = @as(i64, @intFromFloat(@as(f64, @floatFromInt(timestamp)) / seconds_per_year));
|
|
break :blk 1970 + years_since_epoch;
|
|
};
|
|
|
|
// const overview_code = @embedFile("./examples/overview.zx");
|
|
const feature_examples = @embedFile("./examples/feature_examples.zx");
|
|
|
|
const control_tabs = [_]CodeTab{
|
|
.{ .id = "control-tab-if", .label = "If", .section = "Control Flow: if", .code = "", .checked = true },
|
|
.{ .id = "control-tab-if-opt", .label = "If Optional", .section = "Control Flow: if optional capture", .code = "", .checked = false },
|
|
.{ .id = "control-tab-if-err", .label = "If Error", .section = "Control Flow: if error capture", .code = "", .checked = false },
|
|
.{ .id = "control-tab-while", .label = "While", .section = "Control Flow: while", .code = "", .checked = false },
|
|
.{ .id = "control-tab-while-opt", .label = "While Optional", .section = "Control Flow: while optional capture", .code = "", .checked = false },
|
|
.{ .id = "control-tab-while-err", .label = "While Error", .section = "Control Flow: while error capture", .code = "", .checked = false },
|
|
};
|
|
|
|
const caching_tabs = [_]CodeTab{
|
|
.{ .id = "caching-tab-component", .label = "Component", .section = "Caching: component", .code = "", .checked = true },
|
|
.{ .id = "caching-tab-page", .label = "Page", .section = "Caching: page", .code = "", .checked = false },
|
|
};
|
|
|
|
const fs_tabs = [_]CodeTab{
|
|
.{ .id = "fs-tab-page", .label = "Page", .section = "File System Routing: page", .code = "", .checked = true },
|
|
.{ .id = "fs-tab-layout", .label = "Layout", .section = "File System Routing: layout", .code = "", .checked = false },
|
|
};
|
|
|
|
const component_tabs = [_]CodeTab{
|
|
.{ .id = "component-tab-basics", .label = "Basics", .section = "Component: basics", .code = "", .checked = true },
|
|
.{ .id = "component-tab-frag", .label = "Fragment", .section = "Component: fragment", .code = "", .checked = false },
|
|
.{ .id = "component-tab-props", .label = "Props", .section = "Component: props", .code = "", .checked = false },
|
|
.{ .id = "component-tab-attr", .label = "Attributes", .section = "Component: attr", .code = "", .checked = false },
|
|
};
|
|
|
|
return (
|
|
<div @allocator={allocator}>
|
|
<nav class="nav">
|
|
<div class="nav-content">
|
|
<a href="/" class="nav-logo">Ziex</a>
|
|
<input type="checkbox" id="nav-toggle" class="nav-toggle" />
|
|
<label for="nav-toggle" class="nav-hamburger" aria-label="Toggle menu">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</label>
|
|
<div class="nav-right">
|
|
<ul class="nav-links">
|
|
<li><a href="/learn" class="nav-link">Docs</a></li>
|
|
// <li><a href="/docs" class="nav-link"><span class="nav-link-full">Reference</span><span class="nav-link-short">Ref</span></a></li>
|
|
<li><a href="/playground" class="nav-link">Playground</a></li>
|
|
</ul>
|
|
<div class="nav-social" role="group" aria-label="Community links">
|
|
<a href="/r/discord" target="_blank" rel="noopener noreferrer" class="nav-social-link" aria-label="Discord">
|
|
<DiscordIcon allocator={allocator} />
|
|
<span class="nav-social-text">Discord</span>
|
|
</a>
|
|
<span class="nav-social-divider" aria-hidden="true"></span>
|
|
<a href={zx.info.repository} target="_blank" rel="noopener noreferrer" class="nav-social-link" aria-label="GitHub">
|
|
<GitHubIcon allocator={allocator} />
|
|
<span class="nav-social-text">GitHub</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
<main>
|
|
<section class="hero">
|
|
<div class="hero-visuals">
|
|
<div class="hero-visuals-grid"></div>
|
|
<div class="hero-visuals-dots"></div>
|
|
<div class="hero-visuals-scanline"></div>
|
|
</div>
|
|
<div class="hero-inner">
|
|
<div class="hero-content">
|
|
<Version allocator={allocator} />
|
|
<div class="hero-title-wrapper">
|
|
<h1 class="hero-title-main">Ziex</h1>
|
|
<div class="hero-title-accent">/</div>
|
|
</div>
|
|
<p class="hero-subtitle">
|
|
The full-stack web framework for Zig.
|
|
</p>
|
|
<p class="hero-description">
|
|
Compile-time safety, deterministic performance, absolute simplicity, and a delightful developer experience.
|
|
</p>
|
|
<div class="hero-install">
|
|
<InstallCLI allocator={allocator} show_init={true} />
|
|
</div>
|
|
<div class="hero-cta">
|
|
<a href="/learn" class="btn btn-primary">Get Started</a>
|
|
<a href="/playground" class="btn btn-secondary">Playground</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="hero-mascot">
|
|
<div class="hero-mascot-glow"></div>
|
|
// <div class="hero-mascot-glow hero-mascot-glow--inner"></div>
|
|
<img src="/assets/branding/compressed/ziex-ziguana.webp" alt="Ziguana Mascot" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<section class="features">
|
|
<div class="features-header">
|
|
<h2 class="section-title">Features</h2>
|
|
<p class="section-subtitle">
|
|
Everything you need to build fast, safe, and deployable web applications.
|
|
</p>
|
|
</div>
|
|
<div class="features-grid">
|
|
{for (features) |f| (<FeatureCard icon_fn={f.icon_fn} title={f.title} description={f.description} />)}
|
|
</div>
|
|
</section>
|
|
<BenchmarksSection eyebrow="" title="Performance" subtitle=`<a href='{zx.info.repository}/tree/main/bench' target='_blank' class='bench-label-link'>View benchmark →</a> <br/> Each framework runs in a Docker container limited to 2 CPU cores and 2 GB RAM.` tabs={benchmark_tabs[0..]} run_url={bench_run.url} run_date={bench_run.date} />
|
|
// <ArchitectureSection allocator={allocator} />
|
|
<DeploymentSection allocator={allocator} />
|
|
// <TestingSection allocator={allocator} />
|
|
// <TechnicalSection allocator={allocator} />
|
|
<section class="feature-examples">
|
|
<div class="feature-examples-header">
|
|
<h2 class="section-title">Code Examples</h2>
|
|
<p class="section-subtitle">
|
|
Short, focused snippets that show how each feature feels in practice.
|
|
</p>
|
|
</div>
|
|
<div class="feature-examples-grid">
|
|
<FeatureExample title="Control Flow" wide={true} child={(<CodeWindowTabs id="control-flow-tabs" name="control-flow" title="" tabs={control_tabs[0..]} video_url="" source={feature_examples} />)} />
|
|
<FeatureExample title="Caching" wide={false} child={(<CodeWindowTabs id="caching-tabs" name="caching" title="" tabs={caching_tabs[0..]} video_url="" source={feature_examples} />)} />
|
|
<FeatureExample title="File System Routing" wide={false} child={(<CodeWindowTabs id="fs-routing-tabs" name="fs-routing" title="" tabs={fs_tabs[0..]} video_url="" source={feature_examples} />)} />
|
|
<FeatureExample title="Components" wide={false} child={(<CodeWindowTabs id="components-tabs" name="components" title="" tabs={component_tabs[0..]} video_url="" source={feature_examples} />)} />
|
|
<FeatureExample title="Dynamic Path" wide={false} child={(<CodeWindow title="pages/[id]/page.zx" section="Dynamic Path" source={feature_examples} code="" />)} />
|
|
<FeatureExample title="API Route" wide={false} child={(<CodeWindow title="route.zig" section="API Route" source={feature_examples} code="" />)} />
|
|
<FeatureExample title="WebSocket" wide={false} child={(<CodeWindow title="routes/ws/route.zig" section="WebSocket" source={feature_examples} code="" />)} />
|
|
<FeatureExample title="Client-side Rendering" wide={true} child={(<CodePreviewExample title="counter.zx" section="Client-side Rendering" source={feature_examples} code="" preview={(<Examples.Client />)} />)} />
|
|
</div>
|
|
</section>
|
|
// <section class="code-section">
|
|
// <div class="code-container">
|
|
// <div class="code-header">
|
|
// <h2 class="section-title">Familiar Syntax</h2>
|
|
// <p class="section-subtitle">
|
|
// Familiar JSX like syntax or just like HTML with having access to Zig's control flow.
|
|
// </p>
|
|
// </div>
|
|
// <CodeWindow allocator={allocator} title="example.zx" section="" source="" code={overview_code} />
|
|
// </div>
|
|
// </section>
|
|
</main>
|
|
<footer class="footer">
|
|
<div class="footer-runner">
|
|
<div class="footer-runner-wrap">
|
|
<div class="footer-runner-bubble">catch me!</div>
|
|
<img class="footer-runner-mascot" src="/assets/mascot.svg" alt="Running Ziguana" />
|
|
</div>
|
|
</div>
|
|
<p>Copyright © {current_year} {" ziex.dev"}</p>
|
|
</footer>
|
|
<script src=`/assets/clipboard.js?v={zx.info.version}` defer="true"></script>
|
|
<script src=`/assets/runner.js?v={zx.info.version}` defer="true"></script>
|
|
<script src=`/assets/home.js?v={zx.info.version}` defer="true"></script>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const Deployment = struct { icon_fn: *const fn (zx.Allocator) zx.Component, title: []const u8, description: []const u8 };
|
|
const Feature = struct { icon_fn: *const fn (zx.Allocator) zx.Component, title: []const u8, description: []const u8 };
|
|
fn FeatureCard(allocator: zx.Allocator, props: Feature) zx.Component {
|
|
const IconComponent = props.icon_fn(allocator);
|
|
return (
|
|
<div @allocator={allocator} class="feature-card">
|
|
<div class="feature-icon">{IconComponent}</div>
|
|
<h3 class="feature-title">{props.title}</h3>
|
|
<p class="feature-description">{props.description}</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const TooltipMetric = struct {
|
|
label: []const u8,
|
|
value: []const u8,
|
|
};
|
|
|
|
const BenchmarkRow = struct {
|
|
label: []const u8,
|
|
value: f64,
|
|
display: []const u8,
|
|
url: []const u8,
|
|
tooltip_metrics: []const TooltipMetric = &[_]TooltipMetric{},
|
|
};
|
|
|
|
const BenchmarkRowViewProps = struct {
|
|
row: BenchmarkRow,
|
|
normalized_value: f64,
|
|
};
|
|
|
|
const BenchmarkTab = struct {
|
|
id: []const u8,
|
|
label: []const u8,
|
|
metric: []const u8,
|
|
footnote: []const u8,
|
|
rows: []const BenchmarkRow,
|
|
checked: bool,
|
|
lower_is_better: bool = false,
|
|
};
|
|
|
|
const BenchmarksSectionProps = struct {
|
|
eyebrow: []const u8,
|
|
title: []const u8,
|
|
subtitle: []const u8,
|
|
tabs: []const BenchmarkTab,
|
|
run_url: []const u8 = "",
|
|
run_date: []const u8 = "",
|
|
};
|
|
|
|
fn buildBenchTabStyles(allocator: zx.Allocator, container_id: []const u8, tabs: []const BenchmarkTab) []const u8 {
|
|
var out = std.array_list.Managed(u8).init(allocator);
|
|
defer out.deinit();
|
|
|
|
for (tabs) |tab| {
|
|
out.appendSlice("#") catch unreachable;
|
|
out.appendSlice(container_id) catch unreachable;
|
|
out.appendSlice(":has(#") catch unreachable;
|
|
out.appendSlice(tab.id) catch unreachable;
|
|
out.appendSlice(":checked) .benchmarks-panel[data-tab=\"") catch unreachable;
|
|
out.appendSlice(tab.id) catch unreachable;
|
|
out.appendSlice("\"] { display: block; }\n") catch unreachable;
|
|
|
|
out.appendSlice("#") catch unreachable;
|
|
out.appendSlice(container_id) catch unreachable;
|
|
out.appendSlice(":has(#") catch unreachable;
|
|
out.appendSlice(tab.id) catch unreachable;
|
|
out.appendSlice(":checked) label[for=\"") catch unreachable;
|
|
out.appendSlice(tab.id) catch unreachable;
|
|
out.appendSlice("\"] { color: #0c0f14; background: #4ade80; border-color: transparent; box-shadow: 0 8px 20px rgba(74, 222, 128, 0.25); }\n") catch unreachable;
|
|
}
|
|
|
|
return allocator.dupe(u8, out.items) catch unreachable;
|
|
}
|
|
|
|
fn BenchmarkRowView(allocator: zx.Allocator, props: BenchmarkRowViewProps) zx.Component {
|
|
const style_value = std.fmt.allocPrint(allocator, "--value: {d:.3};", .{props.normalized_value}) catch unreachable;
|
|
return (
|
|
<div @allocator={allocator} class="bench-row" style={style_value} data-self={if (props.row.url.len == 0) "true" else "false"}>
|
|
<div class="bench-label">
|
|
{if (props.row.url.len > 0) (
|
|
<a href={props.row.url} target="_blank" rel="noopener noreferrer" class="bench-label-link">{props.row.label}</a>
|
|
) else (
|
|
<span>{props.row.label}</span>
|
|
)}
|
|
</div>
|
|
<div class="bench-bar"><span class="bench-bar-fill"></span></div>
|
|
<div class="bench-value">{props.row.display}</div>
|
|
<div class="bench-tooltip">
|
|
{for (props.row.tooltip_metrics) |metric| (
|
|
<div class="bench-tooltip-row">
|
|
<span class="bench-tooltip-label">{metric.label}</span>
|
|
<span class="bench-tooltip-value">{metric.value}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
fn normalizeValue(value: f64, rows: []const BenchmarkRow) f64 {
|
|
if (rows.len == 0) return 0;
|
|
|
|
var max_value = rows[0].value;
|
|
for (rows[1..]) |row| {
|
|
if (row.value > max_value) max_value = row.value;
|
|
}
|
|
|
|
if (max_value == 0) return 0;
|
|
return value / max_value;
|
|
}
|
|
|
|
fn BenchmarksPanel(allocator: zx.Allocator, tab: BenchmarkTab) zx.Component {
|
|
const panel_id = std.fmt.allocPrint(allocator, "panel-{s}", .{tab.id}) catch unreachable;
|
|
const tab_id = std.fmt.allocPrint(allocator, "tab-{s}", .{tab.id}) catch unreachable;
|
|
return (
|
|
<div @allocator={allocator} class="benchmarks-panel" data-tab={tab.id} data-lower-is-better={if (tab.lower_is_better) "true" else "false"} role="tabpanel" id={panel_id} aria-labelledby={tab_id}>
|
|
// <div class="benchmarks-metric">{tab.metric}</div>
|
|
<div class="benchmarks-chart">
|
|
{for (tab.rows) |row| (
|
|
<BenchmarkRowView row={row} normalized_value={normalizeValue(row.value, tab.rows)} />
|
|
)}
|
|
</div>
|
|
<div class="benchmarks-footnote">{tab.footnote}</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
fn BenchmarksSection(allocator: zx.Allocator, props: BenchmarksSectionProps) zx.Component {
|
|
const tab_styles = buildBenchTabStyles(allocator, "benchmarks-tabs", props.tabs);
|
|
return (
|
|
<section @allocator={allocator} class="benchmarks">
|
|
<div class="benchmarks-header">
|
|
<span class="benchmarks-eyebrow">{props.eyebrow}</span>
|
|
<h2 class="section-title">{props.title}</h2>
|
|
<p class="section-subtitle" @escaping={.none}>{props.subtitle}</p>
|
|
</div>
|
|
<div id="benchmarks-tabs" class="benchmarks-card">
|
|
<style @escaping={.none}>{tab_styles}</style>
|
|
<div class="benchmarks-tabs" role="tablist" aria-label="Benchmark categories">
|
|
{for (props.tabs) |tab| (
|
|
<>
|
|
{if (tab.checked) (
|
|
<input type="radio" id={tab.id} name="bench-tabs" class="benchmarks-tab-radio" checked="true" aria-hidden="true" tabindex="-1" />
|
|
) else (
|
|
<input type="radio" id={tab.id} name="bench-tabs" class="benchmarks-tab-radio" aria-hidden="true" tabindex="-1" />
|
|
)}
|
|
<label for={tab.id} class="benchmarks-tab-label" role="tab" id={std.fmt.allocPrint(allocator, "tab-{s}", .{tab.id}) catch unreachable} aria-selected={if (tab.checked) "true" else "false"} aria-controls={std.fmt.allocPrint(allocator, "panel-{s}", .{tab.id}) catch unreachable} tabindex={if (tab.checked) "0" else "-1"}>{tab.label}</label>
|
|
</>
|
|
)}
|
|
</div>
|
|
<div class="benchmarks-panels">
|
|
{for (props.tabs) |tab| (BenchmarksPanel(allocator, tab))}
|
|
</div>
|
|
{if (props.run_url.len > 0) (
|
|
<div class="benchmarks-run-meta">
|
|
<span class="benchmarks-run-date">Last benchmarked on {props.run_date}</span>
|
|
<a href={props.run_url} target="_blank" rel="noopener noreferrer" class="benchmarks-run-link">View CI run →</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
const DeploymentSectionProps = struct {
|
|
allocator: zx.Allocator,
|
|
};
|
|
|
|
fn DeploymentSection(allocator: zx.Allocator, _: DeploymentSectionProps) zx.Component {
|
|
return (
|
|
<section @allocator={allocator} class="deployment">
|
|
<div class="deployment-header">
|
|
<h2 class="section-title">Deploy Anywhere.</h2>
|
|
<p class="section-subtitle">
|
|
Ziex compiles to a single, statically linked binary or a WASI module, making it compatible with every major cloud provider and edge network.
|
|
</p>
|
|
</div>
|
|
<div class="deployment-grid">
|
|
{for (deployments) |d| (
|
|
<div class="deployment-card">
|
|
<div class="deployment-card-icon">{d.icon_fn(allocator)}</div>
|
|
<h3 class="deployment-card-title">{d.title}</h3>
|
|
<p class="deployment-card-text">{d.description}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
fn ArchitectureSection(allocator: zx.Allocator, _: struct {}) zx.Component {
|
|
return (
|
|
<section @allocator={allocator} class="architecture">
|
|
<div class="architecture-header">
|
|
<h2 class="section-title">Zero Overhead Pipeline</h2>
|
|
<p class="section-subtitle">
|
|
A request in Ziex travels through a streamlined pipeline written in native Zig code, bypassing the heavy overhead of interpreted runtimes.
|
|
</p>
|
|
</div>
|
|
<div class="architecture-diagram">
|
|
<div class="architecture-node">
|
|
<div class="architecture-node-icon">{icons.Monitor(allocator)}</div>
|
|
<span class="architecture-node-label">Browser</span>
|
|
</div>
|
|
<div class="architecture-connector"></div>
|
|
<div class="architecture-node">
|
|
<div class="architecture-node-icon">{icons.Server(allocator)}</div>
|
|
<span class="architecture-node-label">Ziex Edge</span>
|
|
</div>
|
|
<div class="architecture-connector"></div>
|
|
<div class="architecture-node">
|
|
<div class="architecture-node-icon">{icons.Flow(allocator)}</div>
|
|
<span class="architecture-node-label">Zig Router</span>
|
|
</div>
|
|
<div class="architecture-connector"></div>
|
|
<div class="architecture-node">
|
|
<div class="architecture-node-icon">{icons.Lightning(allocator)}</div>
|
|
<span class="architecture-node-label">Handler</span>
|
|
</div>
|
|
<div class="architecture-connector"></div>
|
|
<div class="architecture-node">
|
|
<div class="architecture-node-icon">{icons.Layers(allocator)}</div>
|
|
<span class="architecture-node-label">Response</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
fn TechnicalSection(allocator: zx.Allocator, _: struct {}) zx.Component {
|
|
return (
|
|
<section @allocator={allocator} class="technical">
|
|
<div class="technical-grid">
|
|
<div class="technical-card">
|
|
<span class="technical-card-eyebrow">Reliability</span>
|
|
<h3 class="technical-card-title">Statically Checked Components</h3>
|
|
<p class="technical-card-text">
|
|
Every tag, attribute, and component link is verified at compile-time. There are no runtime string-matching errors or missing props in production.
|
|
</p>
|
|
</div>
|
|
<div class="technical-card">
|
|
<span class="technical-card-eyebrow">Efficiency</span>
|
|
<h3 class="technical-card-title">Deterministic Allocation</h3>
|
|
<p class="technical-card-text">
|
|
Ziex uses arena allocators for every request. Memory is allocated once and cleared instantly when the request ends. Zero garbage collection, zero memory leaks.
|
|
</p>
|
|
</div>
|
|
<div class="technical-card">
|
|
<span class="technical-card-eyebrow">Resilience</span>
|
|
<h3 class="technical-card-title">WASI Isolation</h3>
|
|
<p class="technical-card-text">
|
|
Deploy your application inside a WASI sandbox. Hardware-level isolation ensures your app is secure and portable across any edge infrastructure.
|
|
</p>
|
|
</div>
|
|
<div class="technical-card">
|
|
<span class="technical-card-eyebrow">Speed</span>
|
|
<h3 class="technical-card-title">Native Binary Size</h3>
|
|
<p class="technical-card-text">
|
|
A typical Ziex application is less than 5MB. No node_modules, no shared libraries. Just a high-performance binary ready to serve.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
fn TestingSection(allocator: zx.Allocator, _: struct {}) zx.Component {
|
|
return (
|
|
<section @allocator={allocator} class="testing">
|
|
<div class="testing-header">
|
|
<h2 class="section-title">Deterministic Simulation Testing</h2>
|
|
<p class="section-subtitle">
|
|
Ziex components are tested using deterministic simulation. We simulate thousands of edge cases, network failures, and race conditions in milliseconds.
|
|
</p>
|
|
</div>
|
|
<div class="testing-flex">
|
|
<div class="clock-panel">
|
|
<div class="clock-wrapper">
|
|
<div class="clock-hand clock-hand--slow"></div>
|
|
</div>
|
|
<span class="clock-label">Local Time</span>
|
|
<span class="clock-time">Real World</span>
|
|
</div>
|
|
<div class="clock-panel">
|
|
<div class="clock-wrapper">
|
|
<div class="clock-hand clock-hand--fast"></div>
|
|
</div>
|
|
<span class="clock-label">Simulated Time</span>
|
|
<span class="clock-time">10,000x Speed</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
const FeatureExampleProps = struct {
|
|
title: []const u8,
|
|
child: zx.Component,
|
|
wide: bool,
|
|
};
|
|
|
|
fn FeatureExample(allocator: zx.Allocator, props: FeatureExampleProps) zx.Component {
|
|
const class_name = if (props.wide) "feature-example feature-example--wide" else "feature-example";
|
|
return (
|
|
<div @allocator={allocator} class={class_name}>
|
|
<div class="feature-example-title">{props.title}</div>
|
|
{props.child}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const CodeWindowProps = struct {
|
|
title: []const u8,
|
|
code: []const u8,
|
|
section: []const u8,
|
|
source: []const u8,
|
|
};
|
|
|
|
const CodeTab = struct {
|
|
id: []const u8,
|
|
label: []const u8,
|
|
section: []const u8,
|
|
code: []const u8,
|
|
checked: bool,
|
|
};
|
|
|
|
fn resolveCode(allocator: zx.Allocator, source: []const u8, section: []const u8, fallback: []const u8) []const u8 {
|
|
const raw = if (section.len > 0 and source.len > 0)
|
|
util.extractSection(allocator, source, section)
|
|
else
|
|
fallback;
|
|
return util.highlightZx(allocator, raw) catch raw;
|
|
}
|
|
|
|
fn CodeWindow(allocator: zx.Allocator, props: CodeWindowProps) zx.Component {
|
|
const highlighted = resolveCode(allocator, props.source, props.section, props.code);
|
|
return (
|
|
<div @allocator={allocator} class="code-example code-example--compact">
|
|
<div class="code-example-header">
|
|
<div class="code-example-dots">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</div>
|
|
<div class="code-example-title">{props.title}</div>
|
|
</div>
|
|
<pre><code class="hljs" @escaping={.none}>{highlighted}</code></pre>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const CodeWindowTabsProps = struct {
|
|
id: []const u8,
|
|
name: []const u8,
|
|
title: []const u8,
|
|
tabs: []const CodeTab,
|
|
video_url: []const u8,
|
|
source: []const u8,
|
|
};
|
|
|
|
const CodePreviewExampleProps = struct {
|
|
title: []const u8,
|
|
section: []const u8,
|
|
source: []const u8,
|
|
code: []const u8,
|
|
preview: zx.Component,
|
|
};
|
|
|
|
fn CodePreviewExample(allocator: zx.Allocator, props: CodePreviewExampleProps) zx.Component {
|
|
const highlighted = resolveCode(allocator, props.source, props.section, props.code);
|
|
return (
|
|
<div @allocator={allocator} class="code-preview-example code-example code-example--compact" data-code-container-type="preview">
|
|
<div class="code-example-header">
|
|
<div class="code-example-dots">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</div>
|
|
// <div class="code-example-actions">
|
|
// <button class="code-example-open-playground" data-code-section={props.section} data-code-source="preview" data-tooltip="Open in playground" aria-label="Open in playground">
|
|
// ▶
|
|
// </button>
|
|
// </div>
|
|
<div class="code-example-title">{props.title}</div>
|
|
</div>
|
|
<div class="code-preview-body">
|
|
<div class="code-preview-code">
|
|
<pre><code class="hljs" @escaping={.none}>{highlighted}</code></pre>
|
|
</div>
|
|
<div class="code-preview-pane">
|
|
<div class="code-preview-pane-header">Preview</div>
|
|
<div class="code-preview-pane-content">
|
|
{props.preview}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
fn buildTabStyles(allocator: zx.Allocator, container_id: []const u8, tabs: []const CodeTab) []const u8 {
|
|
var out = std.array_list.Managed(u8).init(allocator);
|
|
defer out.deinit();
|
|
|
|
for (tabs) |tab| {
|
|
out.appendSlice("#") catch unreachable;
|
|
out.appendSlice(container_id) catch unreachable;
|
|
out.appendSlice(":has(#") catch unreachable;
|
|
out.appendSlice(tab.id) catch unreachable;
|
|
out.appendSlice(":checked) .code-example-panel[data-tab=\"") catch unreachable;
|
|
out.appendSlice(tab.id) catch unreachable;
|
|
out.appendSlice("\"] { display: block; }\n") catch unreachable;
|
|
}
|
|
|
|
return allocator.dupe(u8, out.items) catch unreachable;
|
|
}
|
|
|
|
fn CodeWindowTabs(allocator: zx.Allocator, props: CodeWindowTabsProps) zx.Component {
|
|
const tab_styles = buildTabStyles(allocator, props.id, props.tabs);
|
|
|
|
return (
|
|
<div @allocator={allocator} id={props.id} class="code-example code-example--tabs code-example--compact">
|
|
<style @escaping={.none}>{tab_styles}</style>
|
|
<div class="code-example-header code-example-header--tabs">
|
|
<div class="code-example-dots">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</div>
|
|
<div class="code-example-tabs">
|
|
{for (props.tabs) |tab| (
|
|
<>
|
|
{if (tab.checked) (
|
|
<input type="radio" id={tab.id} name={props.name} class="code-example-tab-radio" checked="true" aria-label={tab.label} />
|
|
) else (
|
|
<input type="radio" id={tab.id} name={props.name} class="code-example-tab-radio" aria-label={tab.label} />
|
|
)}
|
|
<label for={tab.id} class="code-example-tab-label">{tab.label}</label>
|
|
</>
|
|
)}
|
|
</div>
|
|
{if (props.video_url.len > 0) (
|
|
<a class="code-example-video" href={props.video_url} target="_blank" rel="noopener noreferrer" aria-label="Play video">
|
|
▶
|
|
</a>
|
|
)}
|
|
<div class="code-example-actions">
|
|
<button class="code-example-open-playground" data-code-container-id={props.id} data-tooltip="Open in playground" aria-label="Open in playground">
|
|
▶
|
|
</button>
|
|
</div>
|
|
{if (props.title.len > 0) (<div class="code-example-title">{props.title}</div>)}
|
|
</div>
|
|
<div class="code-example-panels">
|
|
{for (props.tabs) |tab| (renderTabPanel(allocator, props.source, tab))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
fn renderTabPanel(allocator: zx.Allocator, source: []const u8, tab: CodeTab) zx.Component {
|
|
const highlighted = resolveCode(allocator, source, tab.section, tab.code);
|
|
return (
|
|
<div @allocator={allocator} class="code-example-panel" data-tab={tab.id} data-section={tab.section}>
|
|
<pre><code class="hljs" @escaping={.none}>{highlighted}</code></pre>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const std = @import("std");
|
|
const zx = @import("zx");
|
|
const util = @import("./reference/util.zig");
|
|
const icons = @import("./components/icons.zx");
|
|
const GitHubIcon = icons.GitHub;
|
|
const DiscordIcon = icons.Discord;
|
|
const InstallCLI = @import("./components/install_guide.zx").InstallCLI;
|
|
const Version = @import("./components/version.zx").Version;
|
|
const Examples = @import("./examples/feature_examples.zx");
|
|
const root = @import("root");
|