feat(runtime): native platform backends and UI vessels onto main
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
libgtk-4-dev \
|
||||
pkg-config \
|
||||
libcurl4-openssl-dev \
|
||||
binutils \
|
||||
file \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# All sources arrive in /build — runtime/ subdirectory contains the el runtime,
|
||||
# top-level contains elc-generated app/vessel C files.
|
||||
COPY runtime/ runtime/
|
||||
COPY el_native_vessel.c el_native_vessel.h native_hello.c ./
|
||||
|
||||
# Compile el_gtk4.c
|
||||
RUN gcc -DEL_TARGET_LINUX $(pkg-config --cflags gtk4) \
|
||||
-std=c11 -I/build/runtime \
|
||||
-c /build/runtime/el_gtk4.c -o /build/el_gtk4.o && \
|
||||
echo "el_gtk4.c compiled OK"
|
||||
|
||||
# Compile el_seed.c
|
||||
RUN gcc -DEL_TARGET_LINUX $(pkg-config --cflags gtk4) \
|
||||
-std=c11 -I/build/runtime \
|
||||
-c /build/runtime/el_seed.c -o /build/el_seed.o && \
|
||||
echo "el_seed.c compiled OK"
|
||||
|
||||
# Compile el_runtime.c
|
||||
RUN gcc -std=c11 -I/build/runtime \
|
||||
-c /build/runtime/el_runtime.c -o /build/el_runtime.o && \
|
||||
echo "el_runtime.c compiled OK"
|
||||
|
||||
# Patch el_runtime.o: hide __-prefixed symbols that el_seed.o also defines.
|
||||
# This is the Linux objcopy equivalent of macOS nmedit -s.
|
||||
# el_seed.o is canonical for all __ symbols; el_runtime.o provides high-level
|
||||
# builtins (str_len, json_get_string, etc.) that el_seed.o does NOT export.
|
||||
RUN nm /build/el_seed.o | awk '/[0-9a-f]+ T /{print $3}' | sort > /build/.seed_T.txt && \
|
||||
nm /build/el_runtime.o | awk '/[0-9a-f]+ T /{print $3}' | sort > /build/.rt_T.txt && \
|
||||
comm -12 /build/.seed_T.txt /build/.rt_T.txt > /build/.dups.txt && \
|
||||
if [ -s /build/.dups.txt ]; then \
|
||||
ARGS=$(awk '{printf "--localize-symbol=%s ", $1}' /build/.dups.txt); \
|
||||
eval "objcopy $ARGS /build/el_runtime.o"; \
|
||||
echo "Patched $(wc -l < /build/.dups.txt) duplicate symbols in el_runtime.o"; \
|
||||
else \
|
||||
echo "No duplicate symbols found"; \
|
||||
fi
|
||||
|
||||
# Compile vessel
|
||||
RUN gcc -DEL_TARGET_LINUX -std=c11 -I/build/runtime \
|
||||
-include /build/runtime/el_native_target.h \
|
||||
-c /build/el_native_vessel.c -o /build/el_native_vessel.o && \
|
||||
echo "vessel compiled OK"
|
||||
|
||||
# Compile app (before patching vessel, so we know which symbols to localise)
|
||||
RUN gcc -DEL_TARGET_LINUX -std=c11 -I/build/runtime \
|
||||
-include /build/runtime/el_native_target.h \
|
||||
-include /build/el_native_vessel.h \
|
||||
-c /build/native_hello.c -o /build/native_hello.o && \
|
||||
echo "app compiled OK"
|
||||
|
||||
# Patch el_native_vessel.o:
|
||||
# 1. Localise main() so it doesn't conflict with the app's main.
|
||||
# 2. Localise any T/B symbols the app also defines (elc emits shared globals
|
||||
# like TOKEN_PRIMARY in every translation unit; GNU ld rejects duplicates
|
||||
# unlike macOS ld which picks the first one silently).
|
||||
RUN nm /build/native_hello.o | awk '/[0-9a-f]+ [TBb] /{print $3}' | sort > /build/.app_syms.txt && \
|
||||
nm /build/el_native_vessel.o | awk '/[0-9a-f]+ [TBb] /{print $3}' | sort > /build/.vessel_syms.txt && \
|
||||
comm -12 /build/.app_syms.txt /build/.vessel_syms.txt > /build/.vessel_dups.txt && \
|
||||
echo "main" >> /build/.vessel_dups.txt && \
|
||||
sort -u /build/.vessel_dups.txt -o /build/.vessel_dups.txt && \
|
||||
ARGS=$(awk '{printf "--localize-symbol=%s ", $1}' /build/.vessel_dups.txt) && \
|
||||
eval "objcopy $ARGS /build/el_native_vessel.o" && \
|
||||
echo "Patched $(wc -l < /build/.vessel_dups.txt) symbols in el_native_vessel.o"
|
||||
|
||||
# Link — add -lm for math functions (exp, log, etc.) used by el_runtime.c
|
||||
RUN gcc \
|
||||
/build/native_hello.o \
|
||||
/build/el_native_vessel.o \
|
||||
/build/el_gtk4.o \
|
||||
/build/el_runtime.o \
|
||||
/build/el_seed.o \
|
||||
$(pkg-config --libs gtk4) -ldl -lpthread -lcurl -lm \
|
||||
-o /build/native-hello-linux && \
|
||||
echo "==> Linked: native-hello-linux" && \
|
||||
file /build/native-hello-linux
|
||||
@@ -0,0 +1,99 @@
|
||||
FROM --platform=linux/arm64 ubuntu:22.04
|
||||
|
||||
# GTK4 + SDL2 for Raspberry Pi (ARM64 / aarch64)
|
||||
# Targets Pi 3 / 4 / 5 running 64-bit Raspberry Pi OS or Ubuntu 22.04.
|
||||
#
|
||||
# GTK4 requires a desktop environment / Wayland compositor at runtime.
|
||||
# SDL2 works on bare framebuffer or KMS/DRM without a full desktop.
|
||||
#
|
||||
# Usage:
|
||||
# docker build --platform linux/arm64 \
|
||||
# -f Dockerfile.pi --tag el-native-pi-arm64 ./build-docker/
|
||||
# docker create --name pi-extract el-native-pi-arm64
|
||||
# docker cp pi-extract:/build/native-hello-linux-pi ./build/native-hello-pi-arm64
|
||||
# docker rm pi-extract
|
||||
#
|
||||
# The GTK4 build is the default. SDL2 is available as an alternative
|
||||
# for headless / bare-metal Pi deployments.
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
libgtk-4-dev \
|
||||
libsdl2-dev \
|
||||
libsdl2-ttf-dev \
|
||||
libsdl2-image-dev \
|
||||
pkg-config \
|
||||
libcurl4-openssl-dev \
|
||||
binutils \
|
||||
file \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# All sources arrive in /build
|
||||
COPY runtime/ runtime/
|
||||
COPY el_native_vessel.c el_native_vessel.h native_hello.c ./
|
||||
|
||||
# ── GTK4 build ────────────────────────────────────────────────────────────────
|
||||
|
||||
# Compile el_gtk4.c
|
||||
RUN gcc -DEL_TARGET_LINUX $(pkg-config --cflags gtk4) \
|
||||
-std=c11 -I/build/runtime \
|
||||
-c /build/runtime/el_gtk4.c -o /build/el_gtk4.o && \
|
||||
echo "el_gtk4.c compiled OK"
|
||||
|
||||
# Compile el_seed.c
|
||||
RUN gcc -DEL_TARGET_LINUX $(pkg-config --cflags gtk4) \
|
||||
-std=c11 -I/build/runtime \
|
||||
-c /build/runtime/el_seed.c -o /build/el_seed.o && \
|
||||
echo "el_seed.c compiled OK"
|
||||
|
||||
# Compile el_runtime.c
|
||||
RUN gcc -std=c11 -I/build/runtime \
|
||||
-c /build/runtime/el_runtime.c -o /build/el_runtime.o && \
|
||||
echo "el_runtime.c compiled OK"
|
||||
|
||||
# Patch el_runtime.o: localise __-prefixed duplicate symbols (el_seed.o is canonical)
|
||||
RUN nm /build/el_seed.o | awk '/[0-9a-f]+ T /{print $3}' | sort > /build/.seed_T.txt && \
|
||||
nm /build/el_runtime.o | awk '/[0-9a-f]+ T /{print $3}' | sort > /build/.rt_T.txt && \
|
||||
comm -12 /build/.seed_T.txt /build/.rt_T.txt > /build/.dups.txt && \
|
||||
if [ -s /build/.dups.txt ]; then \
|
||||
ARGS=$(awk '{printf "--localize-symbol=%s ", $1}' /build/.dups.txt); \
|
||||
eval "objcopy $ARGS /build/el_runtime.o"; \
|
||||
echo "Patched $(wc -l < /build/.dups.txt) duplicate symbols in el_runtime.o"; \
|
||||
fi
|
||||
|
||||
# Compile vessel
|
||||
RUN gcc -DEL_TARGET_LINUX -std=c11 -I/build/runtime \
|
||||
-include /build/runtime/el_native_target.h \
|
||||
-c /build/el_native_vessel.c -o /build/el_native_vessel.o && \
|
||||
echo "vessel compiled OK"
|
||||
|
||||
# Compile app
|
||||
RUN gcc -DEL_TARGET_LINUX -std=c11 -I/build/runtime \
|
||||
-include /build/runtime/el_native_target.h \
|
||||
-include /build/el_native_vessel.h \
|
||||
-c /build/native_hello.c -o /build/native_hello.o && \
|
||||
echo "app compiled OK"
|
||||
|
||||
# Patch el_native_vessel.o: localise shared globals (main + TOKEN_* etc.)
|
||||
RUN nm /build/native_hello.o | awk '/[0-9a-f]+ [TBb] /{print $3}' | sort > /build/.app_syms.txt && \
|
||||
nm /build/el_native_vessel.o | awk '/[0-9a-f]+ [TBb] /{print $3}' | sort > /build/.vessel_syms.txt && \
|
||||
comm -12 /build/.app_syms.txt /build/.vessel_syms.txt > /build/.vessel_dups.txt && \
|
||||
echo "main" >> /build/.vessel_dups.txt && \
|
||||
sort -u /build/.vessel_dups.txt -o /build/.vessel_dups.txt && \
|
||||
ARGS=$(awk '{printf "--localize-symbol=%s ", $1}' /build/.vessel_dups.txt) && \
|
||||
eval "objcopy $ARGS /build/el_native_vessel.o" && \
|
||||
echo "Patched $(wc -l < /build/.vessel_dups.txt) symbols in el_native_vessel.o"
|
||||
|
||||
# Link (GTK4)
|
||||
RUN gcc \
|
||||
/build/native_hello.o \
|
||||
/build/el_native_vessel.o \
|
||||
/build/el_gtk4.o \
|
||||
/build/el_runtime.o \
|
||||
/build/el_seed.o \
|
||||
$(pkg-config --libs gtk4) -ldl -lpthread -lcurl -lm \
|
||||
-o /build/native-hello-linux-pi && \
|
||||
echo "==> Linked: native-hello-linux-pi (GTK4)" && \
|
||||
file /build/native-hello-linux-pi
|
||||
@@ -0,0 +1,459 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t native_init(void);
|
||||
el_val_t native_run_loop(void);
|
||||
el_val_t manifest_read(el_val_t path);
|
||||
el_val_t manifest_title(el_val_t m);
|
||||
el_val_t manifest_width(el_val_t m);
|
||||
el_val_t manifest_height(el_val_t m);
|
||||
el_val_t manifest_min_width(el_val_t m);
|
||||
el_val_t manifest_min_height(el_val_t m);
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height);
|
||||
el_val_t window_from_manifest(el_val_t manifest_path);
|
||||
el_val_t window_show(el_val_t handle);
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title);
|
||||
el_val_t vstack(el_val_t spacing);
|
||||
el_val_t vstack_tight(void);
|
||||
el_val_t hstack(el_val_t spacing);
|
||||
el_val_t zstack(void);
|
||||
el_val_t scroll(void);
|
||||
el_val_t label(el_val_t text);
|
||||
el_val_t button(el_val_t label);
|
||||
el_val_t text_field(el_val_t placeholder);
|
||||
el_val_t text_area(el_val_t placeholder);
|
||||
el_val_t image(el_val_t path_or_name);
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t widget_get_text(el_val_t handle);
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold);
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left);
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p);
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py);
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width);
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height);
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_destroy(el_val_t handle);
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset);
|
||||
el_val_t hex_nibble(el_val_t c);
|
||||
el_val_t color_hex_r(el_val_t hex);
|
||||
el_val_t color_hex_g(el_val_t hex);
|
||||
el_val_t color_hex_b(el_val_t hex);
|
||||
el_val_t color_hex_a(el_val_t hex);
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t style_surface(el_val_t handle);
|
||||
el_val_t style_button_primary(el_val_t handle);
|
||||
el_val_t style_label_body(el_val_t handle);
|
||||
el_val_t style_label_heading(el_val_t handle);
|
||||
el_val_t style_label_muted(el_val_t handle);
|
||||
|
||||
el_val_t TOKEN_PRIMARY;
|
||||
el_val_t TOKEN_ON_PRIMARY;
|
||||
el_val_t TOKEN_BACKGROUND;
|
||||
el_val_t TOKEN_ON_BG;
|
||||
el_val_t TOKEN_SURFACE;
|
||||
el_val_t TOKEN_ON_SURFACE;
|
||||
el_val_t TOKEN_OUTLINE;
|
||||
el_val_t TOKEN_ERROR;
|
||||
|
||||
el_val_t native_init(void) {
|
||||
__native_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t native_run_loop(void) {
|
||||
__native_run_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_read(el_val_t path) {
|
||||
return __manifest_read(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_title(el_val_t m) {
|
||||
return json_get_string(m, EL_STR("title"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_width(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("width"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_height(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("height"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_min_width(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("min_width"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_min_height(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("min_height"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) {
|
||||
return __window_create(title, width, height, min_width, min_height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_from_manifest(el_val_t manifest_path) {
|
||||
el_val_t m = manifest_read(manifest_path);
|
||||
el_val_t title = manifest_title(m);
|
||||
el_val_t w = manifest_width(m);
|
||||
el_val_t h = manifest_height(m);
|
||||
el_val_t mw = manifest_min_width(m);
|
||||
el_val_t mh = manifest_min_height(m);
|
||||
el_val_t safe_w = ({ el_val_t _if_result_1 = 0; if ((w > 0)) { _if_result_1 = (w); } else { _if_result_1 = (1200); } _if_result_1; });
|
||||
el_val_t safe_h = ({ el_val_t _if_result_2 = 0; if ((h > 0)) { _if_result_2 = (h); } else { _if_result_2 = (800); } _if_result_2; });
|
||||
el_val_t safe_mw = ({ el_val_t _if_result_3 = 0; if ((mw > 0)) { _if_result_3 = (mw); } else { _if_result_3 = (600); } _if_result_3; });
|
||||
el_val_t safe_mh = ({ el_val_t _if_result_4 = 0; if ((mh > 0)) { _if_result_4 = (mh); } else { _if_result_4 = (400); } _if_result_4; });
|
||||
el_val_t safe_t = ({ el_val_t _if_result_5 = 0; if ((str_len(title) > 0)) { _if_result_5 = (title); } else { _if_result_5 = (EL_STR("App")); } _if_result_5; });
|
||||
return window_create(safe_t, safe_w, safe_h, safe_mw, safe_mh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_show(el_val_t handle) {
|
||||
__window_show(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title) {
|
||||
__window_set_title(handle, title);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t vstack(el_val_t spacing) {
|
||||
return __vstack_create(spacing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t vstack_tight(void) {
|
||||
return __vstack_create(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hstack(el_val_t spacing) {
|
||||
return __hstack_create(spacing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t zstack(void) {
|
||||
return __zstack_create();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t scroll(void) {
|
||||
return __scroll_create();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t label(el_val_t text) {
|
||||
return __label_create(text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t button(el_val_t label) {
|
||||
return __button_create(label);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t text_field(el_val_t placeholder) {
|
||||
return __text_field_create(placeholder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t text_area(el_val_t placeholder) {
|
||||
return __text_area_create(placeholder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t image(el_val_t path_or_name) {
|
||||
return __image_create(path_or_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text) {
|
||||
__widget_set_text(handle, text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_get_text(el_val_t handle) {
|
||||
return __widget_get_text(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
|
||||
__widget_set_color(handle, r, g, b, a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
|
||||
__widget_set_bg_color(handle, r, g, b, a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold) {
|
||||
el_val_t bold_int = ({ el_val_t _if_result_6 = 0; if (bold) { _if_result_6 = (1); } else { _if_result_6 = (0); } _if_result_6; });
|
||||
__widget_set_font(handle, family, size, bold_int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) {
|
||||
__widget_set_padding(handle, top, right, bottom, left);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p) {
|
||||
__widget_set_padding(handle, p, p, p, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py) {
|
||||
__widget_set_padding(handle, py, px, py, px);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width) {
|
||||
__widget_set_width(handle, width);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height) {
|
||||
__widget_set_height(handle, height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex) {
|
||||
__widget_set_flex(handle, flex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius) {
|
||||
__widget_set_corner_radius(handle, radius);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled) {
|
||||
el_val_t d = ({ el_val_t _if_result_7 = 0; if (disabled) { _if_result_7 = (1); } else { _if_result_7 = (0); } _if_result_7; });
|
||||
__widget_set_disabled(handle, d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden) {
|
||||
el_val_t h = ({ el_val_t _if_result_8 = 0; if (hidden) { _if_result_8 = (1); } else { _if_result_8 = (0); } _if_result_8; });
|
||||
__widget_set_hidden(handle, h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child) {
|
||||
__widget_add_child(parent, child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child) {
|
||||
__widget_remove_child(parent, child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_destroy(el_val_t handle) {
|
||||
__widget_destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_click(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_change(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_submit(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset) {
|
||||
el_val_t hi = str_slice(s, offset, (offset + 1));
|
||||
el_val_t lo = str_slice(s, (offset + 1), (offset + 2));
|
||||
el_val_t h = hex_nibble(hi);
|
||||
el_val_t l = hex_nibble(lo);
|
||||
return ((h * 16) + l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hex_nibble(el_val_t c) {
|
||||
if (str_eq(c, EL_STR("0"))) {
|
||||
return 0;
|
||||
}
|
||||
if (str_eq(c, EL_STR("1"))) {
|
||||
return 1;
|
||||
}
|
||||
if (str_eq(c, EL_STR("2"))) {
|
||||
return 2;
|
||||
}
|
||||
if (str_eq(c, EL_STR("3"))) {
|
||||
return 3;
|
||||
}
|
||||
if (str_eq(c, EL_STR("4"))) {
|
||||
return 4;
|
||||
}
|
||||
if (str_eq(c, EL_STR("5"))) {
|
||||
return 5;
|
||||
}
|
||||
if (str_eq(c, EL_STR("6"))) {
|
||||
return 6;
|
||||
}
|
||||
if (str_eq(c, EL_STR("7"))) {
|
||||
return 7;
|
||||
}
|
||||
if (str_eq(c, EL_STR("8"))) {
|
||||
return 8;
|
||||
}
|
||||
if (str_eq(c, EL_STR("9"))) {
|
||||
return 9;
|
||||
}
|
||||
if (str_eq(c, EL_STR("a"))) {
|
||||
return 10;
|
||||
}
|
||||
if (str_eq(c, EL_STR("b"))) {
|
||||
return 11;
|
||||
}
|
||||
if (str_eq(c, EL_STR("c"))) {
|
||||
return 12;
|
||||
}
|
||||
if (str_eq(c, EL_STR("d"))) {
|
||||
return 13;
|
||||
}
|
||||
if (str_eq(c, EL_STR("e"))) {
|
||||
return 14;
|
||||
}
|
||||
if (str_eq(c, EL_STR("f"))) {
|
||||
return 15;
|
||||
}
|
||||
if (str_eq(c, EL_STR("A"))) {
|
||||
return 10;
|
||||
}
|
||||
if (str_eq(c, EL_STR("B"))) {
|
||||
return 11;
|
||||
}
|
||||
if (str_eq(c, EL_STR("C"))) {
|
||||
return 12;
|
||||
}
|
||||
if (str_eq(c, EL_STR("D"))) {
|
||||
return 13;
|
||||
}
|
||||
if (str_eq(c, EL_STR("E"))) {
|
||||
return 14;
|
||||
}
|
||||
if (str_eq(c, EL_STR("F"))) {
|
||||
return 15;
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_r(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_9 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_9 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_9 = (hex); } _if_result_9; });
|
||||
el_val_t v = hex_channel(s, 0);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_g(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_10 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_10 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_10 = (hex); } _if_result_10; });
|
||||
el_val_t v = hex_channel(s, 2);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_b(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_11 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_11 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_11 = (hex); } _if_result_11; });
|
||||
el_val_t v = hex_channel(s, 4);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_a(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_12 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_12 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_12 = (hex); } _if_result_12; });
|
||||
if (str_len(s) < 8) {
|
||||
return el_from_float(1.0);
|
||||
}
|
||||
el_val_t v = hex_channel(s, 6);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex) {
|
||||
widget_set_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex) {
|
||||
widget_set_bg_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_surface(el_val_t handle) {
|
||||
widget_set_bg_color_hex(handle, TOKEN_SURFACE);
|
||||
widget_set_corner_radius(handle, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_button_primary(el_val_t handle) {
|
||||
widget_set_bg_color_hex(handle, TOKEN_PRIMARY);
|
||||
widget_set_color_hex(handle, TOKEN_ON_PRIMARY);
|
||||
widget_set_corner_radius(handle, 8);
|
||||
widget_set_padding_xy(handle, 16, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_body(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_ON_BG);
|
||||
widget_set_font(handle, EL_STR("system"), 14, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_heading(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_ON_BG);
|
||||
widget_set_font(handle, EL_STR("system"), 20, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_muted(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_OUTLINE);
|
||||
widget_set_font(handle, EL_STR("system"), 12, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int _argc, char** _argv) {
|
||||
el_runtime_init_args(_argc, _argv);
|
||||
TOKEN_PRIMARY = EL_STR("#60a5fa");
|
||||
TOKEN_ON_PRIMARY = EL_STR("#0f172a");
|
||||
TOKEN_BACKGROUND = EL_STR("#0f172a");
|
||||
TOKEN_ON_BG = EL_STR("#f8fafc");
|
||||
TOKEN_SURFACE = EL_STR("#1e293b");
|
||||
TOKEN_ON_SURFACE = EL_STR("#f8fafc");
|
||||
TOKEN_OUTLINE = EL_STR("#475569");
|
||||
TOKEN_ERROR = EL_STR("#f87171");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t native_init(void);
|
||||
el_val_t native_run_loop(void);
|
||||
el_val_t manifest_read(el_val_t path);
|
||||
el_val_t manifest_title(el_val_t m);
|
||||
el_val_t manifest_width(el_val_t m);
|
||||
el_val_t manifest_height(el_val_t m);
|
||||
el_val_t manifest_min_width(el_val_t m);
|
||||
el_val_t manifest_min_height(el_val_t m);
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height);
|
||||
el_val_t window_from_manifest(el_val_t manifest_path);
|
||||
el_val_t window_show(el_val_t handle);
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title);
|
||||
el_val_t vstack(el_val_t spacing);
|
||||
el_val_t vstack_tight(void);
|
||||
el_val_t hstack(el_val_t spacing);
|
||||
el_val_t zstack(void);
|
||||
el_val_t scroll(void);
|
||||
el_val_t label(el_val_t text);
|
||||
el_val_t button(el_val_t label);
|
||||
el_val_t text_field(el_val_t placeholder);
|
||||
el_val_t text_area(el_val_t placeholder);
|
||||
el_val_t image(el_val_t path_or_name);
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t widget_get_text(el_val_t handle);
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold);
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left);
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p);
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py);
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width);
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height);
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_destroy(el_val_t handle);
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset);
|
||||
el_val_t hex_nibble(el_val_t c);
|
||||
el_val_t color_hex_r(el_val_t hex);
|
||||
el_val_t color_hex_g(el_val_t hex);
|
||||
el_val_t color_hex_b(el_val_t hex);
|
||||
el_val_t color_hex_a(el_val_t hex);
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t style_surface(el_val_t handle);
|
||||
el_val_t style_button_primary(el_val_t handle);
|
||||
el_val_t style_label_body(el_val_t handle);
|
||||
el_val_t style_label_heading(el_val_t handle);
|
||||
el_val_t style_label_muted(el_val_t handle);
|
||||
|
||||
el_val_t TOKEN_PRIMARY;
|
||||
el_val_t TOKEN_ON_PRIMARY;
|
||||
el_val_t TOKEN_BACKGROUND;
|
||||
el_val_t TOKEN_ON_BG;
|
||||
el_val_t TOKEN_SURFACE;
|
||||
el_val_t TOKEN_ON_SURFACE;
|
||||
el_val_t TOKEN_OUTLINE;
|
||||
el_val_t TOKEN_ERROR;
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t on_greet_click(el_val_t widget, el_val_t data);
|
||||
el_val_t on_counter_click(el_val_t widget, el_val_t data);
|
||||
el_val_t on_name_change(el_val_t widget, el_val_t data);
|
||||
el_val_t app_build(el_val_t window);
|
||||
|
||||
el_val_t g_window;
|
||||
el_val_t g_label;
|
||||
el_val_t g_input;
|
||||
el_val_t g_button;
|
||||
el_val_t g_counter;
|
||||
el_val_t g_counter_lbl;
|
||||
el_val_t manifest_env;
|
||||
el_val_t manifest_path;
|
||||
el_val_t win;
|
||||
el_val_t g_window;
|
||||
|
||||
el_val_t on_greet_click(el_val_t widget, el_val_t data) {
|
||||
el_val_t name = widget_get_text(g_input);
|
||||
el_val_t greeting = ({ el_val_t _if_result_1 = 0; if ((str_len(name) > 0)) { _if_result_1 = (el_str_concat(el_str_concat(EL_STR("Hello, "), name), EL_STR("!"))); } else { _if_result_1 = (EL_STR("Hello, World!")); } _if_result_1; });
|
||||
widget_set_text(g_label, greeting);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t on_counter_click(el_val_t widget, el_val_t data) {
|
||||
el_val_t g_counter = (g_counter + 1);
|
||||
widget_set_text(g_counter_lbl, el_str_concat(EL_STR("Clicks: "), int_to_str(g_counter)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t on_name_change(el_val_t widget, el_val_t data) {
|
||||
el_val_t has_text = (str_len(data) > 0);
|
||||
widget_set_disabled(g_button, !has_text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t app_build(el_val_t window) {
|
||||
el_val_t root = vstack(0);
|
||||
widget_set_padding_all(root, 24);
|
||||
widget_set_bg_color_hex(root, EL_STR("#0f172a"));
|
||||
widget_set_flex(root, 1);
|
||||
el_val_t title = label(EL_STR("el-native \xe2\x80\x94 native widget demo"));
|
||||
style_label_heading(title);
|
||||
widget_add_child(root, title);
|
||||
el_val_t gap1 = label(EL_STR(""));
|
||||
widget_set_height(gap1, 16);
|
||||
widget_add_child(root, gap1);
|
||||
el_val_t subtitle = label(EL_STR("AppKit controls from el code, no ObjC in the app layer."));
|
||||
style_label_muted(subtitle);
|
||||
widget_add_child(root, subtitle);
|
||||
el_val_t gap2 = label(EL_STR(""));
|
||||
widget_set_height(gap2, 24);
|
||||
widget_add_child(root, gap2);
|
||||
el_val_t input_row = hstack(8);
|
||||
el_val_t name_label = label(EL_STR("Name:"));
|
||||
style_label_body(name_label);
|
||||
widget_set_width(name_label, 60);
|
||||
el_val_t input = text_field(EL_STR("Enter your name\xe2\x80\xa6"));
|
||||
style_label_body(input);
|
||||
widget_set_flex(input, 1);
|
||||
widget_on_change(input, EL_STR("on_name_change"));
|
||||
el_val_t g_input = input;
|
||||
el_val_t greet_btn = button(EL_STR("Greet"));
|
||||
style_button_primary(greet_btn);
|
||||
widget_set_disabled(greet_btn, 1);
|
||||
widget_on_click(greet_btn, EL_STR("on_greet_click"));
|
||||
el_val_t g_button = greet_btn;
|
||||
widget_add_child(input_row, name_label);
|
||||
widget_add_child(input_row, input);
|
||||
widget_add_child(input_row, greet_btn);
|
||||
widget_add_child(root, input_row);
|
||||
el_val_t gap3 = label(EL_STR(""));
|
||||
widget_set_height(gap3, 12);
|
||||
widget_add_child(root, gap3);
|
||||
el_val_t greeting = label(EL_STR("Waiting for name\xe2\x80\xa6"));
|
||||
style_label_body(greeting);
|
||||
widget_set_color_hex(greeting, EL_STR("#60a5fa"));
|
||||
widget_set_font(greeting, EL_STR("system"), 16, 1);
|
||||
el_val_t g_label = greeting;
|
||||
widget_add_child(root, greeting);
|
||||
el_val_t gap4 = label(EL_STR(""));
|
||||
widget_set_height(gap4, 24);
|
||||
widget_add_child(root, gap4);
|
||||
el_val_t counter_row = hstack(12);
|
||||
el_val_t counter_lbl = label(EL_STR("Clicks: 0"));
|
||||
style_label_body(counter_lbl);
|
||||
el_val_t g_counter_lbl = counter_lbl;
|
||||
el_val_t counter_btn = button(EL_STR("Click me"));
|
||||
style_button_primary(counter_btn);
|
||||
widget_on_click(counter_btn, EL_STR("on_counter_click"));
|
||||
widget_add_child(counter_row, counter_lbl);
|
||||
widget_add_child(counter_row, counter_btn);
|
||||
widget_add_child(root, counter_row);
|
||||
widget_add_child(window, root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int _argc, char** _argv) {
|
||||
el_runtime_init_args(_argc, _argv);
|
||||
g_window = (-1);
|
||||
g_label = (-1);
|
||||
g_input = (-1);
|
||||
g_button = (-1);
|
||||
g_counter = 0;
|
||||
g_counter_lbl = (-1);
|
||||
native_init();
|
||||
manifest_env = env(EL_STR("EL_MANIFEST"));
|
||||
manifest_path = ({ el_val_t _if_result_2 = 0; if ((str_len(manifest_env) > 0)) { _if_result_2 = (manifest_env); } else { _if_result_2 = (EL_STR("manifest.el")); } _if_result_2; });
|
||||
win = window_from_manifest(manifest_path);
|
||||
g_window = win;
|
||||
if (win < 0) {
|
||||
println(EL_STR("Error: failed to create window. Is EL_TARGET_MACOS defined?"));
|
||||
exit_program(1);
|
||||
}
|
||||
app_build(win);
|
||||
window_show(win);
|
||||
native_run_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,573 @@
|
||||
/*
|
||||
* el_native_target.h — Native widget declarations for el programs targeting
|
||||
* native desktop UI (AppKit / GTK4 / Win32).
|
||||
*
|
||||
* This header is designed to be included AFTER el_runtime.h without conflict:
|
||||
* - It does NOT redefine el_to_float, el_from_float, or any el_runtime.h
|
||||
* static inlines.
|
||||
* - It does NOT redeclare __println, __print, or other functions whose
|
||||
* return types differ between el_seed.h and el_runtime.h.
|
||||
* - It adds: native widget builtins + float arithmetic helpers that the
|
||||
* current el_runtime.h omits but elc still emits calls to.
|
||||
*
|
||||
* Usage:
|
||||
* Inject via -include at compile time, OR #include it after el_runtime.h.
|
||||
*
|
||||
* clang -DEL_TARGET_MACOS -include el_native_target.h -c my_app.c ...
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* el_val_t must already be defined by el_runtime.h or el_seed.h. */
|
||||
#ifndef EL_VAL_T_DEFINED
|
||||
typedef int64_t el_val_t;
|
||||
#endif
|
||||
|
||||
/* ── Float arithmetic helpers ───────────────────────────────────────────────
|
||||
* elc emits calls to float_div / float_mul etc. for Float-typed expressions.
|
||||
* These were in el_runtime.c through v1.0.0-20260501 but are missing from the
|
||||
* current el_runtime.h. Redeclared here as static inline to avoid link deps.
|
||||
* Only defined if not already declared (old runtimes that still have them). */
|
||||
|
||||
#ifndef EL_FLOAT_OPS_DEFINED
|
||||
#define EL_FLOAT_OPS_DEFINED
|
||||
|
||||
/* el_to_float / el_from_float — bit-cast between el_val_t and double.
|
||||
* Defined as static inline in both el_runtime.h and el_seed.h; we do NOT
|
||||
* redefine them here. We rely on one of those headers being included first. */
|
||||
|
||||
static inline el_val_t float_div(el_val_t a, el_val_t b) {
|
||||
union { double d; int64_t i; } ua, ub, ur;
|
||||
ua.i = a; ub.i = b;
|
||||
ur.d = (ub.d != 0.0) ? (ua.d / ub.d) : 0.0;
|
||||
return ur.i;
|
||||
}
|
||||
|
||||
static inline el_val_t float_mul(el_val_t a, el_val_t b) {
|
||||
union { double d; int64_t i; } ua, ub, ur;
|
||||
ua.i = a; ub.i = b; ur.d = ua.d * ub.d;
|
||||
return ur.i;
|
||||
}
|
||||
|
||||
static inline el_val_t float_add(el_val_t a, el_val_t b) {
|
||||
union { double d; int64_t i; } ua, ub, ur;
|
||||
ua.i = a; ub.i = b; ur.d = ua.d + ub.d;
|
||||
return ur.i;
|
||||
}
|
||||
|
||||
static inline el_val_t float_sub(el_val_t a, el_val_t b) {
|
||||
union { double d; int64_t i; } ua, ub, ur;
|
||||
ua.i = a; ub.i = b; ur.d = ua.d - ub.d;
|
||||
return ur.i;
|
||||
}
|
||||
|
||||
static inline el_val_t float_lt(el_val_t a, el_val_t b) {
|
||||
union { double d; int64_t i; } ua, ub;
|
||||
ua.i = a; ub.i = b;
|
||||
return (el_val_t)(ua.d < ub.d);
|
||||
}
|
||||
|
||||
static inline el_val_t float_gt(el_val_t a, el_val_t b) {
|
||||
union { double d; int64_t i; } ua, ub;
|
||||
ua.i = a; ub.i = b;
|
||||
return (el_val_t)(ua.d > ub.d);
|
||||
}
|
||||
|
||||
static inline el_val_t float_lte(el_val_t a, el_val_t b) {
|
||||
union { double d; int64_t i; } ua, ub;
|
||||
ua.i = a; ub.i = b;
|
||||
return (el_val_t)(ua.d <= ub.d);
|
||||
}
|
||||
|
||||
static inline el_val_t float_gte(el_val_t a, el_val_t b) {
|
||||
union { double d; int64_t i; } ua, ub;
|
||||
ua.i = a; ub.i = b;
|
||||
return (el_val_t)(ua.d >= ub.d);
|
||||
}
|
||||
|
||||
static inline el_val_t float_eq(el_val_t a, el_val_t b) {
|
||||
union { double d; int64_t i; } ua, ub;
|
||||
ua.i = a; ub.i = b;
|
||||
return (el_val_t)(ua.d == ub.d);
|
||||
}
|
||||
|
||||
#endif /* EL_FLOAT_OPS_DEFINED */
|
||||
|
||||
/* ── Native widget system (macOS AppKit) ────────────────────────────────────
|
||||
* Available when compiled with -DEL_TARGET_MACOS and linked with el_appkit.m.
|
||||
* Widget handles are opaque int64_t slot indices; -1 = invalid. */
|
||||
|
||||
#ifdef EL_TARGET_MACOS
|
||||
|
||||
/* Initialisation */
|
||||
void __native_init(void);
|
||||
void __native_run_loop(void);
|
||||
|
||||
/* Window */
|
||||
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
|
||||
el_val_t min_width, el_val_t min_height);
|
||||
void __window_show(el_val_t handle);
|
||||
void __window_set_title(el_val_t handle, el_val_t title);
|
||||
|
||||
/* Layout containers */
|
||||
el_val_t __vstack_create(el_val_t spacing);
|
||||
el_val_t __hstack_create(el_val_t spacing);
|
||||
el_val_t __zstack_create(void);
|
||||
el_val_t __scroll_create(void);
|
||||
|
||||
/* Widgets */
|
||||
el_val_t __label_create(el_val_t text);
|
||||
el_val_t __button_create(el_val_t label);
|
||||
el_val_t __text_field_create(el_val_t placeholder);
|
||||
el_val_t __text_area_create(el_val_t placeholder);
|
||||
el_val_t __image_create(el_val_t path_or_name);
|
||||
|
||||
/* Widget properties */
|
||||
void __widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t __widget_get_text(el_val_t handle);
|
||||
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_font(el_val_t handle, el_val_t family,
|
||||
el_val_t size, el_val_t bold);
|
||||
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
|
||||
el_val_t bottom, el_val_t left);
|
||||
void __widget_set_width(el_val_t handle, el_val_t width);
|
||||
void __widget_set_height(el_val_t handle, el_val_t height);
|
||||
void __widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
|
||||
/* Layout / tree */
|
||||
void __widget_add_child(el_val_t parent, el_val_t child);
|
||||
void __widget_remove_child(el_val_t parent, el_val_t child);
|
||||
void __widget_destroy(el_val_t handle);
|
||||
|
||||
/* Events */
|
||||
void __widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
|
||||
/* Manifest reader */
|
||||
el_val_t __manifest_read(el_val_t path);
|
||||
|
||||
#endif /* EL_TARGET_MACOS */
|
||||
|
||||
/* ── Native widget system (Linux GTK4) ──────────────────────────────────────
|
||||
* Available when compiled with -DEL_TARGET_LINUX and linked with el_gtk4.c.
|
||||
* Widget handles are opaque int64_t slot indices; -1 = invalid.
|
||||
* All functions have the same signatures as EL_TARGET_MACOS above. */
|
||||
|
||||
#ifdef EL_TARGET_LINUX
|
||||
|
||||
/* Initialisation */
|
||||
void __native_init(void);
|
||||
void __native_run_loop(void);
|
||||
|
||||
/* Window */
|
||||
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
|
||||
el_val_t min_width, el_val_t min_height);
|
||||
void __window_show(el_val_t handle);
|
||||
void __window_set_title(el_val_t handle, el_val_t title);
|
||||
|
||||
/* Layout containers */
|
||||
el_val_t __vstack_create(el_val_t spacing);
|
||||
el_val_t __hstack_create(el_val_t spacing);
|
||||
el_val_t __zstack_create(void);
|
||||
el_val_t __scroll_create(void);
|
||||
|
||||
/* Widgets */
|
||||
el_val_t __label_create(el_val_t text);
|
||||
el_val_t __button_create(el_val_t label);
|
||||
el_val_t __text_field_create(el_val_t placeholder);
|
||||
el_val_t __text_area_create(el_val_t placeholder);
|
||||
el_val_t __image_create(el_val_t path_or_name);
|
||||
|
||||
/* Widget properties */
|
||||
void __widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t __widget_get_text(el_val_t handle);
|
||||
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_font(el_val_t handle, el_val_t family,
|
||||
el_val_t size, el_val_t bold);
|
||||
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
|
||||
el_val_t bottom, el_val_t left);
|
||||
void __widget_set_width(el_val_t handle, el_val_t width);
|
||||
void __widget_set_height(el_val_t handle, el_val_t height);
|
||||
void __widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
|
||||
/* Layout / tree */
|
||||
void __widget_add_child(el_val_t parent, el_val_t child);
|
||||
void __widget_remove_child(el_val_t parent, el_val_t child);
|
||||
void __widget_destroy(el_val_t handle);
|
||||
|
||||
/* Events */
|
||||
void __widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
|
||||
/* Manifest reader — same JSON output as EL_TARGET_MACOS */
|
||||
el_val_t __manifest_read(el_val_t path);
|
||||
|
||||
#endif /* EL_TARGET_LINUX */
|
||||
|
||||
/* ── Native widget system (Windows Win32) ───────────────────────────────────
|
||||
* Available when compiled with -DEL_TARGET_WIN32 and linked with el_win32.c.
|
||||
* Widget handles are opaque int64_t slot indices; -1 = invalid.
|
||||
* Link: el_win32.obj comctl32.lib user32.lib gdi32.lib */
|
||||
|
||||
#ifdef EL_TARGET_WIN32
|
||||
|
||||
/* Initialisation */
|
||||
void __native_init(void);
|
||||
void __native_run_loop(void);
|
||||
|
||||
/* Window */
|
||||
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
|
||||
el_val_t min_width, el_val_t min_height);
|
||||
void __window_show(el_val_t handle);
|
||||
void __window_set_title(el_val_t handle, el_val_t title);
|
||||
|
||||
/* Layout containers */
|
||||
el_val_t __vstack_create(el_val_t spacing);
|
||||
el_val_t __hstack_create(el_val_t spacing);
|
||||
el_val_t __zstack_create(void);
|
||||
el_val_t __scroll_create(void);
|
||||
|
||||
/* Widgets */
|
||||
el_val_t __label_create(el_val_t text);
|
||||
el_val_t __button_create(el_val_t label);
|
||||
el_val_t __text_field_create(el_val_t placeholder);
|
||||
el_val_t __text_area_create(el_val_t placeholder);
|
||||
el_val_t __image_create(el_val_t path_or_name);
|
||||
|
||||
/* Widget properties */
|
||||
void __widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t __widget_get_text(el_val_t handle);
|
||||
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_font(el_val_t handle, el_val_t family,
|
||||
el_val_t size, el_val_t bold);
|
||||
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
|
||||
el_val_t bottom, el_val_t left);
|
||||
void __widget_set_width(el_val_t handle, el_val_t width);
|
||||
void __widget_set_height(el_val_t handle, el_val_t height);
|
||||
void __widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
|
||||
/* Layout / tree */
|
||||
void __widget_add_child(el_val_t parent, el_val_t child);
|
||||
void __widget_remove_child(el_val_t parent, el_val_t child);
|
||||
void __widget_destroy(el_val_t handle);
|
||||
|
||||
/* Events */
|
||||
void __widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
|
||||
/* Manifest reader */
|
||||
el_val_t __manifest_read(el_val_t path);
|
||||
|
||||
#endif /* EL_TARGET_WIN32 */
|
||||
|
||||
/* ── Native widget system (iOS UIKit) ───────────────────────────────────────
|
||||
* Available when compiled with -DEL_TARGET_IOS and linked with el_uikit.m.
|
||||
* Widget handles are opaque int64_t slot indices; -1 = invalid.
|
||||
*
|
||||
* iOS lifecycle note: UIApplicationMain never returns. The el program must
|
||||
* store its UI-build logic in a void(*)(void) function pointer, assign it to
|
||||
* el_main_entry_fn, then call __native_run_loop. ElAppDelegate invokes
|
||||
* el_main_entry_fn inside didFinishLaunchingWithOptions.
|
||||
* Call el_uikit_set_args(argc, argv) from main() before __native_run_loop. */
|
||||
|
||||
#ifdef EL_TARGET_IOS
|
||||
|
||||
/* Lifecycle entry-function hook — set before calling __native_run_loop. */
|
||||
extern void (*el_main_entry_fn)(void);
|
||||
|
||||
/* Forward argc/argv from main() to UIApplicationMain. */
|
||||
void el_uikit_set_args(int argc, char** argv);
|
||||
|
||||
/* Initialisation */
|
||||
void __native_init(void);
|
||||
void __native_run_loop(void);
|
||||
|
||||
/* Window */
|
||||
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
|
||||
el_val_t min_width, el_val_t min_height);
|
||||
void __window_show(el_val_t handle);
|
||||
void __window_set_title(el_val_t handle, el_val_t title);
|
||||
|
||||
/* Layout containers */
|
||||
el_val_t __vstack_create(el_val_t spacing);
|
||||
el_val_t __hstack_create(el_val_t spacing);
|
||||
el_val_t __zstack_create(void);
|
||||
el_val_t __scroll_create(void);
|
||||
|
||||
/* Widgets */
|
||||
el_val_t __label_create(el_val_t text);
|
||||
el_val_t __button_create(el_val_t label);
|
||||
el_val_t __text_field_create(el_val_t placeholder);
|
||||
el_val_t __text_area_create(el_val_t placeholder);
|
||||
el_val_t __image_create(el_val_t path_or_name);
|
||||
|
||||
/* Widget properties */
|
||||
void __widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t __widget_get_text(el_val_t handle);
|
||||
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_font(el_val_t handle, el_val_t family,
|
||||
el_val_t size, el_val_t bold);
|
||||
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
|
||||
el_val_t bottom, el_val_t left);
|
||||
void __widget_set_width(el_val_t handle, el_val_t width);
|
||||
void __widget_set_height(el_val_t handle, el_val_t height);
|
||||
void __widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
|
||||
/* Layout / tree */
|
||||
void __widget_add_child(el_val_t parent, el_val_t child);
|
||||
void __widget_remove_child(el_val_t parent, el_val_t child);
|
||||
void __widget_destroy(el_val_t handle);
|
||||
|
||||
/* Events */
|
||||
void __widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
|
||||
/* Manifest reader */
|
||||
el_val_t __manifest_read(el_val_t path);
|
||||
|
||||
#endif /* EL_TARGET_IOS */
|
||||
|
||||
/* ── Native widget system (Android JNI) ─────────────────────────────────────
|
||||
* Available when compiled with -DEL_TARGET_ANDROID and linked with
|
||||
* libelruntime.so (which includes el_android.c compiled by the NDK build).
|
||||
* Widget handles are opaque int64_t slot indices; -1 = invalid.
|
||||
*
|
||||
* Java companion: ElBridge.java (package com.neuron.el) must be compiled into
|
||||
* the APK. The Activity must call ElBridge.init(this) before any widget ops.
|
||||
*
|
||||
* Link flags (in Android.mk or CMakeLists.txt):
|
||||
* -landroid -llog -ldl */
|
||||
|
||||
#ifdef EL_TARGET_ANDROID
|
||||
|
||||
/* Initialisation */
|
||||
void __native_init(void);
|
||||
void __native_run_loop(void); /* no-op on Android */
|
||||
|
||||
/* Window */
|
||||
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
|
||||
el_val_t min_width, el_val_t min_height);
|
||||
void __window_show(el_val_t handle);
|
||||
void __window_set_title(el_val_t handle, el_val_t title);
|
||||
|
||||
/* Layout containers */
|
||||
el_val_t __vstack_create(el_val_t spacing);
|
||||
el_val_t __hstack_create(el_val_t spacing);
|
||||
el_val_t __zstack_create(void);
|
||||
el_val_t __scroll_create(void);
|
||||
|
||||
/* Widgets */
|
||||
el_val_t __label_create(el_val_t text);
|
||||
el_val_t __button_create(el_val_t label);
|
||||
el_val_t __text_field_create(el_val_t placeholder);
|
||||
el_val_t __text_area_create(el_val_t placeholder);
|
||||
el_val_t __image_create(el_val_t path_or_name);
|
||||
|
||||
/* Widget properties */
|
||||
void __widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t __widget_get_text(el_val_t handle);
|
||||
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_font(el_val_t handle, el_val_t family,
|
||||
el_val_t size, el_val_t bold);
|
||||
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
|
||||
el_val_t bottom, el_val_t left);
|
||||
void __widget_set_width(el_val_t handle, el_val_t width);
|
||||
void __widget_set_height(el_val_t handle, el_val_t height);
|
||||
void __widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
|
||||
/* Layout / tree */
|
||||
void __widget_add_child(el_val_t parent, el_val_t child);
|
||||
void __widget_remove_child(el_val_t parent, el_val_t child);
|
||||
void __widget_destroy(el_val_t handle);
|
||||
|
||||
/* Events */
|
||||
void __widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
|
||||
/* Manifest reader */
|
||||
el_val_t __manifest_read(el_val_t path);
|
||||
|
||||
#endif /* EL_TARGET_ANDROID */
|
||||
|
||||
/* ── Native widget system (LVGL v9 — embedded / microcontroller) ─────────────
|
||||
* Available when compiled with -DEL_TARGET_LVGL and linked with el_lvgl.c
|
||||
* and the LVGL library (lvgl.a or lvgl source tree).
|
||||
*
|
||||
* Target platforms: ESP32, STM32, industrial panels. Any system with 256KB+
|
||||
* RAM and an LVGL-compatible display driver. No OS required.
|
||||
*
|
||||
* Widget handles are opaque int64_t slot indices; -1 = invalid.
|
||||
*
|
||||
* Bare-metal / no dynamic linker:
|
||||
* Compile with -DEL_LVGL_NO_DLSYM and provide:
|
||||
* el_val_t el_lvgl_dispatch(const char *fn, el_val_t a, el_val_t b);
|
||||
*
|
||||
* Compile:
|
||||
* gcc -DEL_TARGET_LVGL -I./lvgl el_lvgl.c -c -o el_lvgl.o
|
||||
* # Then link with lvgl.a. */
|
||||
|
||||
#ifdef EL_TARGET_LVGL
|
||||
|
||||
/* Initialisation */
|
||||
void __native_init(void);
|
||||
void __native_run_loop(void);
|
||||
|
||||
/* Window */
|
||||
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
|
||||
el_val_t min_width, el_val_t min_height);
|
||||
void __window_show(el_val_t handle);
|
||||
void __window_set_title(el_val_t handle, el_val_t title);
|
||||
|
||||
/* Layout containers */
|
||||
el_val_t __vstack_create(el_val_t spacing);
|
||||
el_val_t __hstack_create(el_val_t spacing);
|
||||
el_val_t __zstack_create(void);
|
||||
el_val_t __scroll_create(void);
|
||||
|
||||
/* Widgets */
|
||||
el_val_t __label_create(el_val_t text);
|
||||
el_val_t __button_create(el_val_t label);
|
||||
el_val_t __text_field_create(el_val_t placeholder);
|
||||
el_val_t __text_area_create(el_val_t placeholder);
|
||||
el_val_t __image_create(el_val_t path_or_name);
|
||||
|
||||
/* Widget properties */
|
||||
void __widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t __widget_get_text(el_val_t handle);
|
||||
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_font(el_val_t handle, el_val_t family,
|
||||
el_val_t size, el_val_t bold);
|
||||
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
|
||||
el_val_t bottom, el_val_t left);
|
||||
void __widget_set_width(el_val_t handle, el_val_t width);
|
||||
void __widget_set_height(el_val_t handle, el_val_t height);
|
||||
void __widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
|
||||
/* Layout / tree */
|
||||
void __widget_add_child(el_val_t parent, el_val_t child);
|
||||
void __widget_remove_child(el_val_t parent, el_val_t child);
|
||||
void __widget_destroy(el_val_t handle);
|
||||
|
||||
/* Events */
|
||||
void __widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
|
||||
/* Manifest reader — same JSON output as all other native targets */
|
||||
el_val_t __manifest_read(el_val_t path);
|
||||
|
||||
#endif /* EL_TARGET_LVGL */
|
||||
|
||||
/* ── Native widget system (SDL2 — embedded / Pi) ────────────────────────────
|
||||
* Available when compiled with -DEL_TARGET_SDL2 and linked with el_sdl2.c.
|
||||
* Widget handles are opaque int64_t slot indices; -1 = invalid.
|
||||
*
|
||||
* Target: Raspberry Pi Zero, embedded Linux, any system with a framebuffer
|
||||
* and SDL2 available. No GTK, no desktop environment required.
|
||||
*
|
||||
* Compile:
|
||||
* gcc -DEL_TARGET_SDL2 $(sdl2-config --cflags) -c el_sdl2.c -o el_sdl2.o
|
||||
* Link:
|
||||
* $(sdl2-config --libs) -lSDL2_ttf -lSDL2_image -ldl */
|
||||
|
||||
#ifdef EL_TARGET_SDL2
|
||||
|
||||
/* Initialisation */
|
||||
void __native_init(void);
|
||||
void __native_run_loop(void);
|
||||
|
||||
/* Window */
|
||||
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
|
||||
el_val_t min_width, el_val_t min_height);
|
||||
void __window_show(el_val_t handle);
|
||||
void __window_set_title(el_val_t handle, el_val_t title);
|
||||
|
||||
/* Layout containers */
|
||||
el_val_t __vstack_create(el_val_t spacing);
|
||||
el_val_t __hstack_create(el_val_t spacing);
|
||||
el_val_t __zstack_create(void);
|
||||
el_val_t __scroll_create(void);
|
||||
|
||||
/* Widgets */
|
||||
el_val_t __label_create(el_val_t text);
|
||||
el_val_t __button_create(el_val_t label);
|
||||
el_val_t __text_field_create(el_val_t placeholder);
|
||||
el_val_t __text_area_create(el_val_t placeholder);
|
||||
el_val_t __image_create(el_val_t path_or_name);
|
||||
|
||||
/* Widget properties */
|
||||
void __widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t __widget_get_text(el_val_t handle);
|
||||
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g,
|
||||
el_val_t b, el_val_t a);
|
||||
void __widget_set_font(el_val_t handle, el_val_t family,
|
||||
el_val_t size, el_val_t bold);
|
||||
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
|
||||
el_val_t bottom, el_val_t left);
|
||||
void __widget_set_width(el_val_t handle, el_val_t width);
|
||||
void __widget_set_height(el_val_t handle, el_val_t height);
|
||||
void __widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
|
||||
/* Layout / tree */
|
||||
void __widget_add_child(el_val_t parent, el_val_t child);
|
||||
void __widget_remove_child(el_val_t parent, el_val_t child);
|
||||
void __widget_destroy(el_val_t handle);
|
||||
|
||||
/* Events */
|
||||
void __widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
|
||||
/* Manifest reader */
|
||||
el_val_t __manifest_read(el_val_t path);
|
||||
|
||||
#endif /* EL_TARGET_SDL2 */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,883 @@
|
||||
/*
|
||||
* el_runtime.h — El language C runtime header
|
||||
*
|
||||
* Declares all built-in functions available to compiled El programs.
|
||||
* Include this in every generated .c file.
|
||||
*
|
||||
* Value model:
|
||||
* All El values are represented as el_val_t (= int64_t).
|
||||
* On 64-bit systems a pointer fits in int64_t.
|
||||
* String values are cast: (el_val_t)(uintptr_t)"hello"
|
||||
* Integer values are stored directly.
|
||||
* This lets arithmetic work naturally while still passing strings around.
|
||||
*
|
||||
* Type conventions (El -> C):
|
||||
* String -> el_val_t (holds const char* via uintptr_t cast)
|
||||
* Int -> el_val_t
|
||||
* Bool -> el_val_t (0 = false, nonzero = true)
|
||||
* Any -> el_val_t
|
||||
* Void -> void
|
||||
*
|
||||
* Macros for convenience:
|
||||
* EL_STR(s) cast string literal to el_val_t
|
||||
* EL_CSTR(v) cast el_val_t back to const char*
|
||||
* EL_INT(v) identity — el_val_t is already int64_t
|
||||
* EL_NULL null / zero value
|
||||
* EL_FALSE boolean false (0)
|
||||
* EL_TRUE boolean true (1)
|
||||
*
|
||||
* Link requirements:
|
||||
* -lcurl — required for the HTTP client (http_get, http_post, llm_*).
|
||||
* -lpthread — required for the HTTP server (one detached thread per
|
||||
* connection, capped at 64 concurrent).
|
||||
* -loqs — optional; required only when liboqs is installed and the
|
||||
* pq_* / sha3_256_hex entry points are needed. Detected at
|
||||
* compile time via __has_include(<oqs/oqs.h>).
|
||||
* -lcrypto — optional; pulled in alongside -loqs. Used for X25519 in
|
||||
* pq_hybrid_* and HKDF-SHA256 derivation.
|
||||
*
|
||||
* Canonical compile command:
|
||||
* cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \
|
||||
* -o <out> <prog>.c el-compiler/runtime/el_runtime.c
|
||||
*
|
||||
* With liboqs (post-quantum stack):
|
||||
* cc -std=c11 -I el-compiler/runtime -lcurl -lpthread -loqs -lcrypto \
|
||||
* -o <out> <prog>.c el-compiler/runtime/el_runtime.c
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef int64_t el_val_t;
|
||||
|
||||
#define EL_STR(s) ((el_val_t)(uintptr_t)(s))
|
||||
#define EL_CSTR(v) ((const char*)(uintptr_t)(v))
|
||||
#define EL_INT(v) (v)
|
||||
#define EL_NULL ((el_val_t)0)
|
||||
#define EL_FALSE ((el_val_t)0)
|
||||
#define EL_TRUE ((el_val_t)1)
|
||||
|
||||
/* Float values share the el_val_t (int64) slot via a bit-cast.
|
||||
* The codegen emits Float literals as `el_from_float(<dbl>)` so the
|
||||
* underlying bits represent the IEEE 754 double. Float-aware builtins
|
||||
* (math, format, json) round-trip via these helpers. */
|
||||
static inline double el_to_float(el_val_t v) {
|
||||
union { int64_t i; double f; } u;
|
||||
u.i = (int64_t)v;
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline el_val_t el_from_float(double f) {
|
||||
union { double f; int64_t i; } u;
|
||||
u.f = f;
|
||||
return (el_val_t)u.i;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ── I/O ──────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t println(el_val_t s);
|
||||
el_val_t print(el_val_t s);
|
||||
el_val_t readline(void);
|
||||
|
||||
/* ── String builtins ─────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t el_str_concat(el_val_t a, el_val_t b);
|
||||
el_val_t str_eq(el_val_t a, el_val_t b);
|
||||
el_val_t str_starts_with(el_val_t s, el_val_t prefix);
|
||||
el_val_t str_ends_with(el_val_t s, el_val_t suffix);
|
||||
el_val_t str_len(el_val_t s);
|
||||
el_val_t str_concat(el_val_t a, el_val_t b);
|
||||
el_val_t int_to_str(el_val_t n);
|
||||
el_val_t str_to_int(el_val_t s);
|
||||
el_val_t native_str_to_int(el_val_t s);
|
||||
el_val_t str_slice(el_val_t s, el_val_t start, el_val_t end);
|
||||
el_val_t str_contains(el_val_t s, el_val_t sub);
|
||||
el_val_t str_replace(el_val_t s, el_val_t from, el_val_t to);
|
||||
el_val_t str_to_upper(el_val_t s);
|
||||
el_val_t str_to_lower(el_val_t s);
|
||||
el_val_t str_trim(el_val_t s);
|
||||
|
||||
/* ── Math ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t el_abs(el_val_t n);
|
||||
el_val_t el_max(el_val_t a, el_val_t b);
|
||||
el_val_t el_min(el_val_t a, el_val_t b);
|
||||
|
||||
/* ── Refcount (ARC) ──────────────────────────────────────────────────────────
|
||||
* Lists and Maps carry a refcount. Strings and ints do not — el_retain and
|
||||
* el_release are safe no-ops on non-refcounted values (they sniff a magic
|
||||
* header at offset 0 and only act if the magic matches).
|
||||
*
|
||||
* Codegen emits these at let-binding shadowing, function entry (params), and
|
||||
* function exit (locals other than the returned value). The refcount lets
|
||||
* el_list_append and el_map_set mutate in place when uniquely owned (cheap)
|
||||
* and copy-on-write when shared (preserves persistent semantics across
|
||||
* accumulator patterns in the compiler itself). */
|
||||
|
||||
void el_retain(el_val_t v);
|
||||
void el_release(el_val_t v);
|
||||
|
||||
/* ── Scoped arena (CLI use) ───────────────────────────────────────────────── */
|
||||
el_val_t el_arena_push(void);
|
||||
el_val_t el_arena_pop(el_val_t mark);
|
||||
|
||||
/* ── List ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t el_list_new(el_val_t count, ...);
|
||||
el_val_t el_list_len(el_val_t list);
|
||||
el_val_t el_list_get(el_val_t list, el_val_t index);
|
||||
el_val_t el_list_append(el_val_t list, el_val_t elem);
|
||||
el_val_t el_list_empty(void);
|
||||
el_val_t el_list_clone(el_val_t list);
|
||||
|
||||
/* ── Map ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t el_map_new(el_val_t pair_count, ...);
|
||||
el_val_t el_get_field(el_val_t map, el_val_t key);
|
||||
el_val_t el_map_get(el_val_t map, el_val_t key);
|
||||
el_val_t el_map_set(el_val_t map, el_val_t key, el_val_t value);
|
||||
|
||||
/* ── HTTP ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t http_get(el_val_t url);
|
||||
el_val_t http_post(el_val_t url, el_val_t body);
|
||||
el_val_t http_post_json(el_val_t url, el_val_t json_body);
|
||||
el_val_t http_get_with_headers(el_val_t url, el_val_t headers_map);
|
||||
el_val_t http_post_with_headers(el_val_t url, el_val_t body, el_val_t headers_map);
|
||||
el_val_t http_post_json_with_headers(el_val_t url, el_val_t headers_map, el_val_t json_body);
|
||||
el_val_t http_post_form_auth(el_val_t url, el_val_t form_body, el_val_t auth_header);
|
||||
el_val_t http_delete(el_val_t url);
|
||||
el_val_t http_serve(el_val_t port, el_val_t handler);
|
||||
el_val_t http_set_handler(el_val_t name);
|
||||
|
||||
/* HTTP server v2 ─────────────────────────────────────────────────────────────
|
||||
* Same dispatch model as http_serve, but the handler signature is widened:
|
||||
*
|
||||
* el_val_t handler(method, path, headers_map, body)
|
||||
*
|
||||
* `headers_map` is an ElMap from lowercased header name → header value (both
|
||||
* Strings). Repeated headers are joined with ", " per RFC 7230.
|
||||
*
|
||||
* Response value: the handler may return either
|
||||
* (a) a plain body string — same auto-content-type / 200-OK behaviour as
|
||||
* http_serve (3-arg) — or
|
||||
* (b) a response envelope built with `http_response(status, headers_json,
|
||||
* body)`. The runtime detects the envelope discriminator
|
||||
* `"el_http_response":1` at the start of the returned string and
|
||||
* unpacks status / headers / body before sending.
|
||||
*
|
||||
* The 3-arg http_serve(port, handler) remains supported unchanged for
|
||||
* existing handlers (e.g. products/web/server.el): it dispatches with
|
||||
* (method, path, body), hardcodes 200 OK, and auto-detects content type. */
|
||||
el_val_t http_serve_v2(el_val_t port, el_val_t handler);
|
||||
el_val_t http_set_handler_v2(el_val_t name);
|
||||
|
||||
/* Build an HTTP response envelope. `headers_json` should be a JSON object
|
||||
* literal like `{"WWW-Authenticate":"Basic"}` (or "" / "{}" for none). The
|
||||
* returned string carries the discriminator `{"el_http_response":1,...}`
|
||||
* which the runtime's send-path detects and unpacks. Detection happens
|
||||
* uniformly inside http_send_response, so a 3-arg handler may also return
|
||||
* an envelope. The 3-arg variant remains documented as a fixed 200-OK
|
||||
* auto-content-type contract for legacy handlers that return plain bodies. */
|
||||
el_val_t http_response(el_val_t status, el_val_t headers_json, el_val_t body);
|
||||
|
||||
/* SSE connection fd — set by http_worker_v2 before calling the El handler,
|
||||
* cleared afterwards. Defined in el_seed.c; called from el_runtime.c.
|
||||
* The getter is exposed as __http_conn_fd() to El programs. */
|
||||
void el_seed_set_http_conn_fd(int fd);
|
||||
|
||||
/* HTTP timeout — every libcurl request honors EL_HTTP_TIMEOUT_MS (default
|
||||
* 60000ms). Read lazily on first use, so setting the env var any time before
|
||||
* the first http_* call is sufficient. */
|
||||
|
||||
/* Streaming variants — write the response body straight to a file via
|
||||
* libcurl's CURLOPT_WRITEFUNCTION = fwrite. These bypass the el_val_t string
|
||||
* wrapper entirely, so binary payloads (audio/mpeg, image/png, etc.) survive
|
||||
* embedded NUL bytes that would truncate a strlen()-based code path.
|
||||
*
|
||||
* Both honor EL_HTTP_TIMEOUT_MS, follow redirects, and accept the same
|
||||
* `headers_map` shape as http_post_with_headers (ElMap of String→String).
|
||||
*
|
||||
* Return value: 1 on success (file fully written), 0 on any failure
|
||||
* (network, file open, partial write). On failure the output file is removed
|
||||
* so callers cannot mistake a partially-written file for a valid one. */
|
||||
el_val_t http_post_to_file(el_val_t url, el_val_t body, el_val_t headers_map, el_val_t output_path);
|
||||
el_val_t http_get_to_file(el_val_t url, el_val_t headers_map, el_val_t output_path);
|
||||
|
||||
/* ── URL encoding ────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t url_encode(el_val_t s); /* RFC 3986 unreserved set */
|
||||
el_val_t url_decode(el_val_t s); /* '+' → space, %XX → byte */
|
||||
|
||||
/* ── HTML allowlist sanitizer ────────────────────────────────────────────────
|
||||
* el_html_sanitize(input_html, allowlist_json) — strict allowlist HTML
|
||||
* cleaner. State-machine parser; tag/attribute names compared case-
|
||||
* insensitively against the allowlist; `<a href>` / `<… src>` URL schemes
|
||||
* validated (http, https, mailto, fragment-only, or relative); whole-
|
||||
* subtree drop for script / style / iframe / object / embed / form; HTML-
|
||||
* escapes free text outside dropped subtrees.
|
||||
*
|
||||
* The allowlist is JSON of the form
|
||||
* {"p":[],"a":["href","title"],"strong":[],...}
|
||||
* where each value is the array of attribute names allowed for that tag. */
|
||||
el_val_t el_html_sanitize(el_val_t input_html, el_val_t allowlist_json);
|
||||
el_val_t html_raw(el_val_t s);
|
||||
el_val_t html_escape(el_val_t s);
|
||||
|
||||
/* ── Filesystem ──────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t fs_read(el_val_t path);
|
||||
el_val_t fs_write(el_val_t path, el_val_t content);
|
||||
el_val_t fs_list(el_val_t path);
|
||||
el_val_t fs_list_json(el_val_t path);
|
||||
el_val_t fs_exists(el_val_t path);
|
||||
el_val_t fs_mkdir(el_val_t path); /* mkdir -p, mode 0755 */
|
||||
|
||||
/* Length-explicit binary write. `length` is an Int (el_val_t holding the
|
||||
* byte count). The caller knows the length from context — typically because
|
||||
* `bytes` came from base64_decode (which produces a magic-tagged binary
|
||||
* buffer with embedded NULs possible) and the caller already tracks the
|
||||
* decoded length, OR because the bytes came from a fixed-size source
|
||||
* (sha256_bytes = 32, hmac_sha256_bytes = 32). Bypasses strlen entirely.
|
||||
*
|
||||
* Returns 1 on success, 0 on failure (invalid path, can't open, partial
|
||||
* write, negative length). On partial-write failure, the file is removed
|
||||
* so callers cannot read back a truncated artefact. */
|
||||
el_val_t fs_write_bytes(el_val_t path, el_val_t bytes, el_val_t length);
|
||||
|
||||
/* ── JSON ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t json_get(el_val_t json, el_val_t key);
|
||||
el_val_t json_parse(el_val_t s);
|
||||
el_val_t json_stringify(el_val_t v);
|
||||
el_val_t json_get_string(el_val_t json_str, el_val_t key);
|
||||
el_val_t json_get_int(el_val_t json_str, el_val_t key);
|
||||
el_val_t json_get_float(el_val_t json_str, el_val_t key);
|
||||
el_val_t json_get_bool(el_val_t json_str, el_val_t key);
|
||||
el_val_t json_get_raw(el_val_t json_str, el_val_t key);
|
||||
el_val_t json_set(el_val_t json_str, el_val_t key, el_val_t value);
|
||||
el_val_t json_array_len(el_val_t json_str);
|
||||
el_val_t json_array_get(el_val_t json_str, el_val_t index);
|
||||
el_val_t json_array_get_string(el_val_t json_str, el_val_t index);
|
||||
el_val_t json_escape_string(el_val_t sv);
|
||||
el_val_t json_build_object(el_val_t kvs);
|
||||
el_val_t json_build_array(el_val_t items);
|
||||
|
||||
/* ── Time ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t time_now(void);
|
||||
el_val_t time_now_utc(void);
|
||||
el_val_t sleep_secs(el_val_t secs);
|
||||
el_val_t sleep_ms(el_val_t ms);
|
||||
el_val_t time_format(el_val_t ts, el_val_t fmt);
|
||||
el_val_t time_to_parts(el_val_t ts);
|
||||
el_val_t time_from_parts(el_val_t secs, el_val_t ns, el_val_t tz);
|
||||
el_val_t time_add(el_val_t ts, el_val_t n, el_val_t unit);
|
||||
el_val_t time_diff(el_val_t ts1, el_val_t ts2, el_val_t unit);
|
||||
el_val_t now_ns(void);
|
||||
|
||||
/* ── Instant + Duration: first-class temporal types ──────────────────────────
|
||||
* Both types share the el_val_t (int64) slot. Instants are nanoseconds
|
||||
* since the Unix epoch; Durations are signed nanoseconds. Type discipline
|
||||
* is enforced at codegen-time: BinOps on names registered as Instant or
|
||||
* Duration route through the typed wrappers below; mismatches like
|
||||
* Instant+Instant become #error at the C compiler.
|
||||
*
|
||||
* Postfix literals — `30.seconds`, `1.hour`, `500.millis`, `30.nanos` — are
|
||||
* recognised by the parser as DurationLit AST nodes and lowered to literal
|
||||
* int64 nanoseconds at codegen time. The runtime never sees the units. */
|
||||
|
||||
el_val_t el_now_instant(void);
|
||||
el_val_t now(void);
|
||||
el_val_t unix_seconds(el_val_t n);
|
||||
el_val_t unix_millis(el_val_t n);
|
||||
el_val_t instant_from_iso8601(el_val_t s);
|
||||
|
||||
el_val_t el_duration_from_nanos(el_val_t ns);
|
||||
el_val_t duration_seconds(el_val_t n);
|
||||
el_val_t duration_millis(el_val_t n);
|
||||
el_val_t duration_nanos(el_val_t n);
|
||||
|
||||
el_val_t el_instant_add_dur(el_val_t inst, el_val_t dur);
|
||||
el_val_t el_instant_sub_dur(el_val_t inst, el_val_t dur);
|
||||
el_val_t el_instant_diff(el_val_t a, el_val_t b);
|
||||
el_val_t el_duration_add(el_val_t a, el_val_t b);
|
||||
el_val_t el_duration_sub(el_val_t a, el_val_t b);
|
||||
el_val_t el_duration_scale(el_val_t dur, el_val_t scalar);
|
||||
el_val_t el_duration_div(el_val_t dur, el_val_t scalar);
|
||||
|
||||
el_val_t el_instant_lt(el_val_t a, el_val_t b);
|
||||
el_val_t el_instant_le(el_val_t a, el_val_t b);
|
||||
el_val_t el_instant_gt(el_val_t a, el_val_t b);
|
||||
el_val_t el_instant_ge(el_val_t a, el_val_t b);
|
||||
el_val_t el_instant_eq(el_val_t a, el_val_t b);
|
||||
el_val_t el_instant_ne(el_val_t a, el_val_t b);
|
||||
el_val_t el_duration_lt(el_val_t a, el_val_t b);
|
||||
el_val_t el_duration_le(el_val_t a, el_val_t b);
|
||||
el_val_t el_duration_gt(el_val_t a, el_val_t b);
|
||||
el_val_t el_duration_ge(el_val_t a, el_val_t b);
|
||||
el_val_t el_duration_eq(el_val_t a, el_val_t b);
|
||||
el_val_t el_duration_ne(el_val_t a, el_val_t b);
|
||||
|
||||
el_val_t instant_to_unix_seconds(el_val_t i);
|
||||
el_val_t instant_to_unix_millis(el_val_t i);
|
||||
el_val_t instant_to_iso8601(el_val_t i);
|
||||
el_val_t duration_to_seconds(el_val_t d);
|
||||
el_val_t duration_to_millis(el_val_t d);
|
||||
el_val_t duration_to_nanos(el_val_t d);
|
||||
|
||||
el_val_t el_sleep_duration(el_val_t dur);
|
||||
el_val_t unix_timestamp(void);
|
||||
|
||||
el_val_t ttl_cache_set(el_val_t key, el_val_t value);
|
||||
el_val_t ttl_cache_get(el_val_t key, el_val_t max_age);
|
||||
el_val_t ttl_cache_age(el_val_t key);
|
||||
|
||||
/* ── Calendar + CalendarTime + Rhythm + LocalDate/Time/DateTime ─────────────
|
||||
* Phase 1.5 of the time system. Calendar is pluggable: EarthCalendar (IANA
|
||||
* zones, Gregorian, DST) is the user-facing default; MarsCalendar,
|
||||
* CycleCalendar(period), NoCycleCalendar, RelativeCalendar handle non-Earth
|
||||
* domains.
|
||||
*
|
||||
* A Calendar interprets an Instant under a particular cycle convention and
|
||||
* produces a CalendarTime. CalendarTime carries the underlying Instant and
|
||||
* a back-pointer to its Calendar; arithmetic and formatting consult the
|
||||
* Calendar to convert ns since epoch into year/month/day/hour/minute/second
|
||||
* (or sol/phase, or cycle/phase, depending on kind).
|
||||
*
|
||||
* Storage convention: Calendar / CalendarTime / Rhythm / LocalDate /
|
||||
* LocalDateTime are heap-allocated structs whose pointers are cast into
|
||||
* el_val_t. A 24-bit magic header at offset 0 lets the runtime identify
|
||||
* the kind safely. LocalTime is small enough to live in the int64 slot
|
||||
* directly (nanos since midnight, signed). */
|
||||
|
||||
/* Zone — opaque IANA zone or fixed offset, used by EarthCalendar.
|
||||
* `zone_id` is either an IANA name ("America/New_York", "UTC") or a fixed
|
||||
* offset string ("+05:30", "-08:00"). The runtime resolves it via tzset()
|
||||
* on first use of the owning EarthCalendar. */
|
||||
el_val_t zone(el_val_t id);
|
||||
el_val_t zone_utc(void);
|
||||
el_val_t zone_local(void);
|
||||
el_val_t zone_offset(el_val_t hours, el_val_t minutes);
|
||||
|
||||
/* Calendar constructors. Each returns an el_val_t pointer to a heap-
|
||||
* allocated, magic-tagged Calendar struct. Calendars are interned by
|
||||
* (kind, zone_id, period_ns, epoch_ns) so identical constructors return
|
||||
* the same pointer — equality is reference equality. */
|
||||
el_val_t earth_calendar(el_val_t z);
|
||||
el_val_t earth_calendar_default(void);
|
||||
el_val_t mars_calendar(void);
|
||||
el_val_t cycle_calendar(el_val_t period_dur);
|
||||
el_val_t no_cycle_calendar(void);
|
||||
el_val_t relative_calendar(el_val_t epoch_inst);
|
||||
|
||||
/* CalendarTime constructors and methods. Returns a heap-allocated struct
|
||||
* whose pointer fits in el_val_t. */
|
||||
el_val_t now_in(el_val_t cal);
|
||||
el_val_t in_calendar(el_val_t inst, el_val_t cal);
|
||||
el_val_t cal_format(el_val_t ct, el_val_t pattern);
|
||||
el_val_t cal_to_instant(el_val_t ct);
|
||||
el_val_t cal_cycle_phase(el_val_t ct);
|
||||
el_val_t cal_in(el_val_t ct, el_val_t cal);
|
||||
|
||||
/* LocalDate / LocalTime / LocalDateTime — calendar-agnostic value types.
|
||||
* LocalTime carries nanoseconds since midnight as a signed int64 directly
|
||||
* in the el_val_t slot (no allocation). LocalDate / LocalDateTime are
|
||||
* heap-allocated structs with magic headers. */
|
||||
el_val_t local_date(el_val_t y, el_val_t m, el_val_t d);
|
||||
el_val_t local_time(el_val_t h, el_val_t m, el_val_t s, el_val_t ns);
|
||||
el_val_t local_datetime(el_val_t date, el_val_t time);
|
||||
el_val_t zoned(el_val_t date, el_val_t time, el_val_t cal);
|
||||
|
||||
el_val_t local_date_year(el_val_t ld);
|
||||
el_val_t local_date_month(el_val_t ld);
|
||||
el_val_t local_date_day(el_val_t ld);
|
||||
el_val_t local_time_hour(el_val_t lt);
|
||||
el_val_t local_time_minute(el_val_t lt);
|
||||
el_val_t local_time_second(el_val_t lt);
|
||||
el_val_t local_time_nanos(el_val_t lt);
|
||||
|
||||
el_val_t el_local_date_add_dur(el_val_t ld, el_val_t dur);
|
||||
el_val_t el_local_time_add_dur(el_val_t lt, el_val_t dur);
|
||||
el_val_t el_local_date_lt(el_val_t a, el_val_t b);
|
||||
el_val_t el_local_date_eq(el_val_t a, el_val_t b);
|
||||
|
||||
/* Rhythm — pluggable recurrence AST. Returns a heap-allocated struct
|
||||
* pointer in el_val_t; rhythms are immutable so callers may share them. */
|
||||
el_val_t rhythm_cycle_start(void);
|
||||
el_val_t rhythm_cycle_phase(el_val_t phase);
|
||||
el_val_t rhythm_duration(el_val_t d);
|
||||
el_val_t rhythm_session_start(void);
|
||||
el_val_t rhythm_event(el_val_t name);
|
||||
el_val_t rhythm_and(el_val_t a, el_val_t b);
|
||||
el_val_t rhythm_or(el_val_t a, el_val_t b);
|
||||
el_val_t rhythm_weekday(el_val_t day);
|
||||
el_val_t rhythm_weekly_at(el_val_t day, el_val_t hour, el_val_t minute);
|
||||
el_val_t rhythm_next_after(el_val_t r, el_val_t after, el_val_t cal);
|
||||
el_val_t rhythm_matches(el_val_t r, el_val_t ct);
|
||||
|
||||
/* ── UUID ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t uuid_new(void);
|
||||
el_val_t uuid_v4(void);
|
||||
|
||||
/* ── Environment ─────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t env(el_val_t key);
|
||||
|
||||
/* ── In-process state K/V ────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t state_set(el_val_t key, el_val_t value);
|
||||
el_val_t state_get(el_val_t key);
|
||||
el_val_t state_del(el_val_t key);
|
||||
el_val_t state_keys(void);
|
||||
el_val_t state_has(el_val_t key);
|
||||
el_val_t state_get_or(el_val_t key, el_val_t default_val);
|
||||
|
||||
/* ── Float formatting ────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t float_to_str(el_val_t f);
|
||||
el_val_t int_to_float(el_val_t n);
|
||||
el_val_t float_to_int(el_val_t f);
|
||||
el_val_t format_float(el_val_t f, el_val_t decimals);
|
||||
el_val_t decimal_round(el_val_t f, el_val_t decimals);
|
||||
el_val_t str_to_float(el_val_t s);
|
||||
|
||||
/* ── Math (Float-aware) ──────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t math_sqrt(el_val_t f);
|
||||
el_val_t math_log(el_val_t f);
|
||||
el_val_t math_ln(el_val_t f);
|
||||
el_val_t math_sin(el_val_t f);
|
||||
el_val_t math_cos(el_val_t f);
|
||||
el_val_t math_pi(void);
|
||||
|
||||
/* ── String additions ────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t str_index_of(el_val_t s, el_val_t sub);
|
||||
el_val_t str_split(el_val_t s, el_val_t sep);
|
||||
el_val_t str_char_at(el_val_t s, el_val_t i);
|
||||
el_val_t str_char_code(el_val_t s, el_val_t i);
|
||||
el_val_t str_pad_left(el_val_t s, el_val_t width, el_val_t pad);
|
||||
el_val_t str_pad_right(el_val_t s, el_val_t width, el_val_t pad);
|
||||
el_val_t str_format(el_val_t fmt, el_val_t data);
|
||||
el_val_t str_lower(el_val_t s);
|
||||
el_val_t str_upper(el_val_t s);
|
||||
|
||||
/* ── Text-processing primitives (Phase 1: byte/codepoint, ASCII char classes)
|
||||
* Phase 2 (filed): Unicode-grapheme awareness, NFC/NFD normalization, regex.
|
||||
* is_* predicates: empty input returns false; multi-char requires ALL bytes
|
||||
* to match. ASCII ranges only in Phase 1. */
|
||||
|
||||
/* Counting */
|
||||
el_val_t str_count(el_val_t s, el_val_t sub); /* non-overlapping */
|
||||
el_val_t str_count_chars(el_val_t s); /* codepoint count */
|
||||
el_val_t str_count_bytes(el_val_t s); /* alias of str_len */
|
||||
el_val_t str_count_lines(el_val_t s);
|
||||
el_val_t str_count_words(el_val_t s);
|
||||
el_val_t str_count_letters(el_val_t s); /* ASCII [A-Za-z] */
|
||||
el_val_t str_count_digits(el_val_t s); /* ASCII [0-9] */
|
||||
|
||||
/* Find / position */
|
||||
el_val_t str_index_of_all(el_val_t s, el_val_t sub); /* [Int] of byte offsets */
|
||||
el_val_t str_last_index_of(el_val_t s, el_val_t sub);
|
||||
el_val_t str_find_chars(el_val_t s, el_val_t any_of); /* first idx of any ch */
|
||||
|
||||
/* Transform */
|
||||
el_val_t str_repeat(el_val_t s, el_val_t n);
|
||||
el_val_t str_reverse(el_val_t s); /* by codepoint */
|
||||
el_val_t str_strip_prefix(el_val_t s, el_val_t prefix);
|
||||
el_val_t str_strip_suffix(el_val_t s, el_val_t suffix);
|
||||
el_val_t str_strip_chars(el_val_t s, el_val_t chars);
|
||||
el_val_t str_lstrip(el_val_t s);
|
||||
el_val_t str_rstrip(el_val_t s);
|
||||
|
||||
/* Char classification (Bool) */
|
||||
el_val_t is_letter(el_val_t s);
|
||||
el_val_t is_digit(el_val_t s);
|
||||
el_val_t is_alphanumeric(el_val_t s);
|
||||
el_val_t is_whitespace(el_val_t s);
|
||||
el_val_t is_punctuation(el_val_t s);
|
||||
el_val_t is_uppercase(el_val_t s);
|
||||
el_val_t is_lowercase(el_val_t s);
|
||||
|
||||
/* Split / join */
|
||||
el_val_t str_split_lines(el_val_t s);
|
||||
el_val_t str_split_chars(el_val_t s); /* alias of native_string_chars */
|
||||
el_val_t str_split_n(el_val_t s, el_val_t sep, el_val_t n);
|
||||
el_val_t str_join(el_val_t list, el_val_t sep); /* alias of list_join */
|
||||
|
||||
/* ── List additions ──────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t list_push(el_val_t list, el_val_t elem);
|
||||
el_val_t list_push_front(el_val_t list, el_val_t elem);
|
||||
el_val_t list_join(el_val_t list, el_val_t sep);
|
||||
el_val_t list_range(el_val_t start, el_val_t end);
|
||||
|
||||
/* ── Bool helpers ────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t bool_to_str(el_val_t b);
|
||||
|
||||
/* ── Numeric parsing ─────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t parse_int(el_val_t s, el_val_t default_val);
|
||||
|
||||
/* ── Process ─────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t exit_program(el_val_t code);
|
||||
el_val_t getpid_now(void);
|
||||
|
||||
/* Self-terminating memory guard. Reads ELC_MAX_MEM_MB (default 512) and
|
||||
* exits with code 1 if resident memory exceeds the limit. Call periodically
|
||||
* during long compilation loops (e.g. after each function is compiled).
|
||||
* Returns 0 when memory is within bounds. */
|
||||
el_val_t el_mem_check(void);
|
||||
|
||||
/* ── CGI identity ─────────────────────────────────────────────────────────────
|
||||
* Called at the start of main() in CGI programs (those with a `cgi {}` block).
|
||||
* Records the program's DHARMA identity before any other code executes. */
|
||||
|
||||
void el_cgi_init(el_val_t name, el_val_t dharma_id, el_val_t principal,
|
||||
el_val_t network, el_val_t engram);
|
||||
|
||||
/* ── DHARMA network builtins ─────────────────────────────────────────────────
|
||||
* Available to CGI programs (declared with a `cgi {}` block).
|
||||
*
|
||||
* Peers are addressed by `dharma_id` of the form
|
||||
* "<registry-id>@<transport-url>" e.g. "ntn-genesis@http://localhost:7770"
|
||||
* If the @<url> portion is omitted, transport defaults to
|
||||
* "http://localhost:7770" (the local CGI daemon assumption).
|
||||
*
|
||||
* Wire protocol (all peers expose):
|
||||
* POST <url>/dharma/recv { channel, from, content } → response body
|
||||
* POST <url>/dharma/event { type, payload, source, timestamp }
|
||||
* POST <url>/api/activate { query } → list of nodes
|
||||
*
|
||||
* Hosting application's responsibility: an El program with a `cgi {}` block
|
||||
* runs http_serve() with its own request handler; that handler should route
|
||||
* "/dharma/event" requests by calling el_runtime_dharma_event_arrive() so
|
||||
* incoming events feed dharma_field() queues. The runtime itself does not
|
||||
* intercept any /dharma path. */
|
||||
|
||||
el_val_t dharma_connect(el_val_t cgi_id);
|
||||
el_val_t dharma_send(el_val_t channel, el_val_t content);
|
||||
el_val_t dharma_activate(el_val_t query);
|
||||
void dharma_emit(el_val_t event_type, el_val_t payload);
|
||||
el_val_t dharma_field(el_val_t event_type);
|
||||
void dharma_strengthen(el_val_t cgi_id, el_val_t weight);
|
||||
el_val_t dharma_relationship(el_val_t cgi_id);
|
||||
el_val_t dharma_peers(void);
|
||||
|
||||
/* Public C API: called by an El program's HTTP handler when a /dharma/event
|
||||
* request arrives. Pushes onto the per-event-type queue and signals any
|
||||
* pending dharma_field() blockers. All three arguments must be NUL-terminated
|
||||
* C strings (or NULL — then treated as empty). */
|
||||
void el_runtime_dharma_event_arrive(const char* event_type,
|
||||
const char* payload,
|
||||
const char* source);
|
||||
|
||||
/* ── Engram local graph primitives ───────────────────────────────────────────
|
||||
* Operate on the CGI's local Engram knowledge graph.
|
||||
* `engram_activate` queries the local graph only; `dharma_activate` is
|
||||
* network-wide across all connected CGI graphs. */
|
||||
|
||||
el_val_t engram_node(el_val_t content, el_val_t node_type, el_val_t salience);
|
||||
el_val_t engram_node_full(el_val_t content, el_val_t node_type, el_val_t label,
|
||||
el_val_t salience, el_val_t importance, el_val_t confidence,
|
||||
el_val_t tier, el_val_t tags);
|
||||
/* Layered consciousness — see el_runtime.c for the layered architecture
|
||||
* design notes (search "Layered consciousness architecture"). The five
|
||||
* canonical layers (safety / core-identity / domain-knowledge / imprint /
|
||||
* suit) are seeded automatically; engram_add_layer extends the registry
|
||||
* with imprint or suit overlays at runtime. Nodes default to layer 1
|
||||
* (core-identity) when created via engram_node / engram_node_full. */
|
||||
el_val_t engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label,
|
||||
el_val_t salience, el_val_t certainty, el_val_t confidence,
|
||||
el_val_t status, el_val_t tags, el_val_t layer_id);
|
||||
el_val_t engram_add_layer(el_val_t name, el_val_t priority, el_val_t suppressible,
|
||||
el_val_t transparent, el_val_t injectable);
|
||||
el_val_t engram_remove_layer(el_val_t layer_id);
|
||||
el_val_t engram_list_layers(void);
|
||||
el_val_t engram_get_node(el_val_t id);
|
||||
void engram_strengthen(el_val_t node_id);
|
||||
void engram_forget(el_val_t node_id);
|
||||
el_val_t engram_node_count(void);
|
||||
el_val_t engram_search(el_val_t query, el_val_t limit);
|
||||
el_val_t engram_scan_nodes(el_val_t limit, el_val_t offset);
|
||||
void engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation);
|
||||
el_val_t engram_edge_between(el_val_t from_id, el_val_t to_id);
|
||||
el_val_t engram_neighbors(el_val_t node_id);
|
||||
el_val_t engram_neighbors_filtered(el_val_t node_id, el_val_t max_depth, el_val_t direction);
|
||||
el_val_t engram_edge_count(void);
|
||||
/* Three-pass activation: background fan-out → working-memory promotion →
|
||||
* Layer 0 override. See "Three-pass activation" in el_runtime.c. */
|
||||
el_val_t engram_activate(el_val_t query, el_val_t depth);
|
||||
el_val_t engram_save(el_val_t path);
|
||||
el_val_t engram_load(el_val_t path);
|
||||
|
||||
/* JSON-string accessors — return pre-serialized JSON so HTTP handlers
|
||||
* can pass results straight through without round-tripping ElList/ElMap
|
||||
* through json_stringify. */
|
||||
el_val_t engram_get_node_json(el_val_t id);
|
||||
el_val_t engram_search_json(el_val_t query, el_val_t limit);
|
||||
el_val_t engram_scan_nodes_json(el_val_t limit, el_val_t offset);
|
||||
el_val_t engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset);
|
||||
el_val_t engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction);
|
||||
el_val_t engram_activate_json(el_val_t query, el_val_t depth);
|
||||
el_val_t engram_stats_json(void);
|
||||
el_val_t engram_list_layers_json(void);
|
||||
/* engram_compile_layered_json — produce a prompt-ready text block split
|
||||
* into "[LAYER 0 — STRUCTURAL]" (non-suppressible layers, sacred fire)
|
||||
* and "[ENGRAM CONTEXT]" (standard suppressible layers). Returns "" if
|
||||
* no nodes promoted to working memory. */
|
||||
el_val_t engram_compile_layered_json(el_val_t intent, el_val_t depth);
|
||||
|
||||
/* ── LLM (Anthropic API client) ─────────────────────────────────────────────
|
||||
* All functions call https://api.anthropic.com/v1/messages with the API key
|
||||
* from env ANTHROPIC_API_KEY. Default model when empty: claude-sonnet-4-5. */
|
||||
|
||||
el_val_t llm_call(el_val_t model, el_val_t prompt);
|
||||
el_val_t llm_call_system(el_val_t model, el_val_t system_prompt, el_val_t user_prompt);
|
||||
el_val_t llm_call_agentic(el_val_t model, el_val_t system, el_val_t user, el_val_t tools);
|
||||
el_val_t llm_vision(el_val_t model, el_val_t system, el_val_t prompt, el_val_t image_url_or_b64);
|
||||
el_val_t llm_models(void);
|
||||
|
||||
/* Register a tool handler by name. The handler is looked up via dlsym
|
||||
* (mirroring http_set_handler), so any El `fn <name>(input)` compiles to
|
||||
* a global C symbol that this function can locate at runtime.
|
||||
* Handler signature: `el_val_t handler(el_val_t input_json)` — receives
|
||||
* the tool input as a JSON-string el_val_t and returns a JSON-string
|
||||
* el_val_t result. Used by llm_call_agentic. */
|
||||
void llm_register_tool(el_val_t name, el_val_t handler_fn_name);
|
||||
|
||||
/* ── args() ─────────────────────────────────────────────────────────────────
|
||||
* Provides access to command-line arguments passed to the program.
|
||||
* Populated by el_runtime_init_args() before main() runs. */
|
||||
|
||||
el_val_t args(void);
|
||||
void el_runtime_init_args(int argc, char** argv);
|
||||
|
||||
/* ── Crypto primitives ─────────────────────────────────────────────────────
|
||||
* SHA-256, HMAC-SHA-256, and base64 (standard + URL-safe).
|
||||
* Self-contained — no OpenSSL/libcrypto dependency. The implementations are
|
||||
* adapted from public-domain reference code (Brad Conte / RFC 4648).
|
||||
*
|
||||
* Bytes-returning variants (sha256_bytes, hmac_sha256_bytes) return a string
|
||||
* value whose contents are raw binary; callers usually feed these into
|
||||
* base64_encode. Note that el_val_t strings are NUL-terminated by convention,
|
||||
* so the binary payload may contain embedded NULs — pass it directly into
|
||||
* base64_encode (which uses an explicit length) rather than treating it as
|
||||
* a printable C string.
|
||||
*
|
||||
* The "base64" variants emit/accept RFC 4648 standard alphabet with padding.
|
||||
* The "base64url" variants use URL-safe alphabet (`-`/`_`) with no padding,
|
||||
* as used in JWTs. */
|
||||
|
||||
el_val_t sha256_hex(el_val_t input);
|
||||
el_val_t sha256_bytes(el_val_t input);
|
||||
el_val_t hmac_sha256_hex(el_val_t key, el_val_t message);
|
||||
el_val_t hmac_sha256_bytes(el_val_t key, el_val_t message);
|
||||
el_val_t base64_encode(el_val_t input);
|
||||
el_val_t base64_decode(el_val_t input);
|
||||
el_val_t base64url_encode(el_val_t input);
|
||||
el_val_t base64url_decode(el_val_t input);
|
||||
|
||||
/* Length-aware variants (internal — exposed for the rare caller that already
|
||||
* has a known-length binary buffer and doesn't want to round-trip through
|
||||
* a NUL-terminated el_val_t string). Sha256_bytes and hmac_sha256_bytes feed
|
||||
* these implicitly. */
|
||||
el_val_t el_sha256_bytes_n(const unsigned char* data, size_t len);
|
||||
el_val_t el_base64_encode_n(const unsigned char* data, size_t len, int url_safe);
|
||||
|
||||
/* ── Post-quantum primitives (liboqs-backed) ────────────────────────────────
|
||||
* All inputs/outputs hex-encoded. Algorithm choices:
|
||||
* Signature: CRYSTALS-Dilithium-3 (NIST level 3, balanced)
|
||||
* KEM: CRYSTALS-Kyber-768 (NIST level 3)
|
||||
* Hash: SHA3-256 (Keccak) (PQ-aware protocols favour SHA3 over SHA2)
|
||||
*
|
||||
* If liboqs is not linked (detected via __has_include(<oqs/oqs.h>) at compile
|
||||
* time), the pq_* entry points return a JSON-shaped error string so callers
|
||||
* fail loudly rather than silently fall back to classical schemes:
|
||||
* {"error":"liboqs not linked, post-quantum primitives unavailable"}
|
||||
*
|
||||
* The hybrid handshake pairs X25519 with Kyber-768 per NIST PQ guidance and
|
||||
* CNSA 2.0. Combined shared secret is HKDF-SHA256(x25519_ss || kyber_ss).
|
||||
* Even if Kyber falls, X25519 holds; if X25519 falls under quantum attack,
|
||||
* Kyber holds. SHA3-256 also remains usable independent of liboqs (the
|
||||
* Keccak permutation is PQ-OK as a primitive). */
|
||||
|
||||
el_val_t pq_keygen_signature(void);
|
||||
el_val_t pq_sign(el_val_t secret_key_hex, el_val_t message);
|
||||
el_val_t pq_verify(el_val_t public_key_hex, el_val_t message, el_val_t signature_hex);
|
||||
|
||||
el_val_t pq_kem_keygen(void);
|
||||
el_val_t pq_kem_encaps(el_val_t public_key_hex);
|
||||
el_val_t pq_kem_decaps(el_val_t secret_key_hex, el_val_t ciphertext_hex);
|
||||
|
||||
el_val_t pq_hybrid_keygen(void);
|
||||
el_val_t pq_hybrid_handshake(el_val_t remote_pub_combined);
|
||||
|
||||
el_val_t sha3_256_hex(el_val_t input);
|
||||
|
||||
/* ── AEAD: AES-256-GCM (libcrypto-backed) ───────────────────────────────────
|
||||
* Symmetric authenticated encryption used to wrap envelopes after a KEM
|
||||
* handshake. Caller MUST supply a 32-byte key (64 hex chars) — typically the
|
||||
* Kyber-768 / hybrid shared_secret, optionally normalized via SHA3-256.
|
||||
*
|
||||
* aead_encrypt returns a JSON map {"nonce":"...","ciphertext":"..."} where
|
||||
* ciphertext is the AES-256-GCM output with the 16-byte auth tag appended.
|
||||
* Nonce is a fresh 12-byte CSPRNG draw — callers never pick the nonce, which
|
||||
* structurally rules out the GCM nonce-reuse footgun.
|
||||
*
|
||||
* aead_decrypt returns the plaintext String, or "" on any failure (including
|
||||
* auth-tag mismatch). Callers MUST check for "" before trusting the result. */
|
||||
el_val_t aead_encrypt(el_val_t key_hex, el_val_t plaintext);
|
||||
el_val_t aead_decrypt(el_val_t key_hex, el_val_t nonce_hex, el_val_t ciphertext_hex);
|
||||
|
||||
/* ── Native VM builtin aliases (for compiled El source) ─────────────────────
|
||||
* These match the El VM's native_* builtins so that El source compiled
|
||||
* to C can call the same names without modification. */
|
||||
|
||||
el_val_t native_list_get(el_val_t list, el_val_t index);
|
||||
el_val_t native_list_len(el_val_t list);
|
||||
el_val_t native_list_append(el_val_t list, el_val_t elem);
|
||||
el_val_t native_list_empty(void);
|
||||
el_val_t native_list_clone(el_val_t list);
|
||||
el_val_t native_string_chars(el_val_t s);
|
||||
el_val_t native_int_to_str(el_val_t n);
|
||||
|
||||
/* ── Method-call shorthand aliases ──────────────────────────────────────────
|
||||
* The El method-call convention `obj.method(args)` compiles to
|
||||
* `method(obj, args)`. These aliases expose the runtime functions under
|
||||
* the short names that result from method calls in El source.
|
||||
*
|
||||
* Example: `myList.append(x)` → `append(myList, x)` (calls this alias)
|
||||
* `myList.len()` → `len(myList)` (calls this alias) */
|
||||
|
||||
el_val_t append(el_val_t list, el_val_t elem); /* el_list_append */
|
||||
el_val_t len(el_val_t list); /* el_list_len */
|
||||
el_val_t get(el_val_t list, el_val_t index); /* el_list_get */
|
||||
el_val_t map_get(el_val_t map, el_val_t key); /* el_map_get */
|
||||
el_val_t map_set(el_val_t map, el_val_t key, el_val_t value); /* el_map_set */
|
||||
|
||||
/* ── OTLP/HTTP Observability ─────────────────────────────────────────────── */
|
||||
/* See bottom of el_runtime.c for the implementation.
|
||||
* Configured by env vars OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_SERVICE_VERSION.
|
||||
* No-op when OTLP_ENDPOINT is unset. Drop-on-failure semantics. */
|
||||
/* ── Subprocess execution ────────────────────────────────────────────────── */
|
||||
el_val_t exec_command(el_val_t cmd); /* run shell command, return exit code */
|
||||
el_val_t exec_capture(el_val_t cmd); /* run shell command, capture stdout */
|
||||
el_val_t exec(el_val_t cmd); /* exec(cmd) → stdout String (30s timeout) */
|
||||
el_val_t exec_bg(el_val_t cmd); /* exec_bg(cmd) → PID String (non-blocking) */
|
||||
|
||||
/* ── Stdout redirection (used by compiler JS pipeline) ───────────────────── */
|
||||
el_val_t stdout_to_file(el_val_t path); /* redirect process stdout to a file */
|
||||
el_val_t stdout_restore(void); /* restore process stdout to terminal */
|
||||
|
||||
el_val_t emit_log(el_val_t level, el_val_t msg, el_val_t fields_json);
|
||||
el_val_t emit_metric(el_val_t name, el_val_t value, el_val_t tags_json);
|
||||
el_val_t trace_span_start(el_val_t name);
|
||||
el_val_t trace_span_end(el_val_t span_handle);
|
||||
el_val_t emit_event(el_val_t name, el_val_t duration_ms);
|
||||
|
||||
el_val_t __thread_create(el_val_t fn_name_v, el_val_t arg_v);
|
||||
el_val_t __thread_join(el_val_t tid_v);
|
||||
|
||||
/* ── __ prefixed aliases (self-hosting compiler ABI) ─────────────────────────
|
||||
* The El self-hosting compiler emits calls to __-prefixed names. These are
|
||||
* forwarding wrappers around the existing el_runtime functions above. */
|
||||
|
||||
/* I/O */
|
||||
el_val_t __println(el_val_t s);
|
||||
el_val_t __print(el_val_t s);
|
||||
el_val_t __readline(void);
|
||||
|
||||
/* String */
|
||||
el_val_t __int_to_str(el_val_t n);
|
||||
el_val_t __str_to_int(el_val_t s);
|
||||
el_val_t __float_to_str(el_val_t f);
|
||||
el_val_t __str_to_float(el_val_t s);
|
||||
el_val_t __str_len(el_val_t s);
|
||||
el_val_t __str_char_at(el_val_t s, el_val_t i);
|
||||
el_val_t __str_cmp(el_val_t a, el_val_t b);
|
||||
el_val_t __str_ncmp(el_val_t a, el_val_t b, el_val_t n);
|
||||
el_val_t __str_concat_raw(el_val_t a, el_val_t b);
|
||||
el_val_t __str_slice_raw(el_val_t s, el_val_t start, el_val_t end);
|
||||
el_val_t __str_alloc(el_val_t n);
|
||||
el_val_t __str_set_char(el_val_t s, el_val_t i, el_val_t c);
|
||||
|
||||
/* URL encoding */
|
||||
el_val_t __url_encode(el_val_t s);
|
||||
el_val_t __url_decode(el_val_t s);
|
||||
|
||||
/* Environment */
|
||||
el_val_t __env_get(el_val_t key);
|
||||
|
||||
/* Subprocess */
|
||||
el_val_t __exec(el_val_t cmd);
|
||||
el_val_t __exec_bg(el_val_t cmd);
|
||||
|
||||
/* Process */
|
||||
el_val_t __exit_program(el_val_t code);
|
||||
|
||||
/* Filesystem */
|
||||
el_val_t __fs_exists(el_val_t path);
|
||||
el_val_t __fs_mkdir(el_val_t path);
|
||||
el_val_t __fs_read(el_val_t path);
|
||||
el_val_t __fs_write(el_val_t path, el_val_t content);
|
||||
el_val_t __fs_write_bytes(el_val_t path, el_val_t bytes, el_val_t n);
|
||||
el_val_t __fs_list_raw(el_val_t path);
|
||||
|
||||
/* HTTP server */
|
||||
el_val_t __http_response(el_val_t status, el_val_t headers_json, el_val_t body);
|
||||
el_val_t __http_serve(el_val_t port, el_val_t handler);
|
||||
el_val_t __http_serve_v2(el_val_t port, el_val_t handler);
|
||||
|
||||
/* HTTP conn fd / SSE (weak; overridden by el_seed.c when linked together) */
|
||||
el_val_t __http_conn_fd(void);
|
||||
el_val_t __http_sse_open(el_val_t conn_id);
|
||||
el_val_t __http_sse_send(el_val_t conn_id, el_val_t data);
|
||||
el_val_t __http_sse_close(el_val_t conn_id);
|
||||
|
||||
/* HTTP client (requires HAVE_CURL; stubs provided for no-curl builds) */
|
||||
el_val_t __http_do(el_val_t method, el_val_t url, el_val_t body,
|
||||
el_val_t headers_map, el_val_t timeout_ms);
|
||||
el_val_t __http_do_map(el_val_t method, el_val_t url, el_val_t body,
|
||||
el_val_t headers_json, el_val_t timeout_ms);
|
||||
el_val_t __http_do_map_to_file(el_val_t method, el_val_t url, el_val_t body,
|
||||
el_val_t headers_json, el_val_t output_path);
|
||||
|
||||
/* JSON */
|
||||
el_val_t __json_array_get(el_val_t json, el_val_t index);
|
||||
el_val_t __json_array_get_string(el_val_t json, el_val_t index);
|
||||
el_val_t __json_array_len(el_val_t json);
|
||||
el_val_t __json_get(el_val_t json, el_val_t key);
|
||||
el_val_t __json_get_raw(el_val_t json, el_val_t key);
|
||||
el_val_t __json_set(el_val_t json, el_val_t key, el_val_t value);
|
||||
el_val_t __json_parse_map(el_val_t json_str);
|
||||
el_val_t __json_stringify_val(el_val_t val);
|
||||
|
||||
/* Hashing */
|
||||
el_val_t __sha256_hex(el_val_t s);
|
||||
|
||||
/* State K/V */
|
||||
el_val_t __state_del(el_val_t key);
|
||||
el_val_t __state_get(el_val_t key);
|
||||
el_val_t __state_keys(void);
|
||||
el_val_t __state_set(el_val_t key, el_val_t val);
|
||||
|
||||
/* UUID */
|
||||
el_val_t __uuid_v4(void);
|
||||
|
||||
/* Args */
|
||||
el_val_t __args_json(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* el_seed.h — El language seed runtime header
|
||||
*
|
||||
* Declares all OS-boundary primitives available to compiled El programs.
|
||||
* All functions use the __ prefix convention. Signatures use el_val_t (= int64_t)
|
||||
* as the universal value type.
|
||||
*
|
||||
* el_seed.c is the complete C boundary for the El runtime. The heavy runtime
|
||||
* (el_runtime.c) has been retired — everything lives in el_seed.c plus the
|
||||
* native El runtime (runtime/ *.el files).
|
||||
*
|
||||
* Link requirements:
|
||||
* -lcurl — HTTP client (__http_do, __http_do_to_file)
|
||||
* -lpthread — threading (__thread_create, __thread_join, __mutex_new, ...)
|
||||
*
|
||||
* Canonical compile (via elb):
|
||||
* elb builds and links el_seed.c automatically.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* ── Value model ─────────────────────────────────────────────────────────────
|
||||
* All El values are el_val_t (int64_t). On 64-bit systems a pointer fits.
|
||||
* String -> el_val_t (holds const char* via uintptr_t cast)
|
||||
* Int -> el_val_t (stored directly)
|
||||
* Bool -> el_val_t (0 = false, nonzero = true)
|
||||
* Void -> void
|
||||
*/
|
||||
typedef int64_t el_val_t;
|
||||
|
||||
#define EL_STR(s) ((el_val_t)(uintptr_t)(s))
|
||||
#define EL_CSTR(v) ((const char*)(uintptr_t)(v))
|
||||
#define EL_INT(v) (v)
|
||||
#define EL_NULL ((el_val_t)0)
|
||||
|
||||
/* Float values share the el_val_t slot via bit-cast. */
|
||||
static inline double el_to_float(el_val_t v) {
|
||||
union { int64_t i; double f; } u; u.i = (int64_t)v; return u.f;
|
||||
}
|
||||
static inline el_val_t el_from_float(double f) {
|
||||
union { double f; int64_t i; } u; u.f = f; return (el_val_t)u.i;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ── String primitives ───────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __str_len(el_val_t s);
|
||||
el_val_t __str_char_at(el_val_t s, el_val_t i); /* returns Int (byte value) */
|
||||
el_val_t __str_alloc(el_val_t n); /* malloc(n+1), zero-init, return String */
|
||||
el_val_t __str_set_char(el_val_t s, el_val_t i, el_val_t c); /* s[i]=c, return s */
|
||||
el_val_t __str_cmp(el_val_t a, el_val_t b); /* strcmp result as Int */
|
||||
el_val_t __str_ncmp(el_val_t a, el_val_t b, el_val_t n); /* strncmp */
|
||||
el_val_t __str_concat_raw(el_val_t a, el_val_t b); /* malloc+strcpy concat */
|
||||
el_val_t __str_slice_raw(el_val_t s, el_val_t start, el_val_t end); /* substring copy */
|
||||
el_val_t __int_to_str(el_val_t n);
|
||||
el_val_t __str_to_int(el_val_t s);
|
||||
el_val_t __float_to_str(el_val_t f); /* f is bit-cast double */
|
||||
el_val_t __str_to_float(el_val_t s); /* strtod, bit-cast result */
|
||||
|
||||
/* ── I/O ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
void __println(el_val_t s);
|
||||
void __print(el_val_t s);
|
||||
el_val_t __readline(void);
|
||||
|
||||
/* ── Filesystem ──────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __fs_read(el_val_t path);
|
||||
el_val_t __fs_write(el_val_t path, el_val_t content);
|
||||
el_val_t __fs_exists(el_val_t path);
|
||||
el_val_t __fs_list_raw(el_val_t path); /* newline-separated filenames */
|
||||
el_val_t __fs_mkdir(el_val_t path);
|
||||
el_val_t __fs_write_bytes(el_val_t path, el_val_t bytes, el_val_t n);
|
||||
|
||||
/* ── HTTP client ─────────────────────────────────────────────────────────── */
|
||||
|
||||
/* Unified HTTP call. headers_json is a JSON object of header name->value pairs
|
||||
* (e.g. {"Authorization":"Bearer ...","Content-Type":"application/json"}).
|
||||
* Use "" or "{}" for no extra headers. timeout_ms <= 0 uses the default. */
|
||||
el_val_t __http_do(el_val_t method, el_val_t url, el_val_t body,
|
||||
el_val_t headers_json, el_val_t timeout_ms);
|
||||
|
||||
/* Stream response body directly to a file. Returns 1 on success, 0 on failure. */
|
||||
el_val_t __http_do_to_file(el_val_t method, el_val_t url, el_val_t body,
|
||||
el_val_t headers_json, el_val_t out_path);
|
||||
|
||||
/* ── HTTP server ─────────────────────────────────────────────────────────── */
|
||||
|
||||
/* Blocking HTTP server. handler_name is the El function name to dispatch to.
|
||||
* v1 handler: (method, path, body) -> String
|
||||
* v2 handler: (method, path, headers_map, body) -> String or envelope */
|
||||
void __http_serve(el_val_t port, el_val_t handler_name);
|
||||
void __http_serve_v2(el_val_t port, el_val_t handler_name);
|
||||
|
||||
/* Build a structured HTTP response envelope.
|
||||
* headers_json: JSON object literal like {"Content-Type":"text/plain"} or "{}" */
|
||||
el_val_t __http_response(el_val_t status, el_val_t headers_json, el_val_t body);
|
||||
|
||||
/* ── HTTP SSE — Server-Sent Events streaming ─────────────────────────────── */
|
||||
|
||||
/* Returns the raw file descriptor for the current HTTP connection.
|
||||
* Valid only inside an http_serve_v2 handler before it returns.
|
||||
* Returns -1 if called outside a handler context. */
|
||||
el_val_t __http_conn_fd(void);
|
||||
|
||||
/* Sends SSE response headers on conn_id (the fd from __http_conn_fd),
|
||||
* keeping the connection open for streaming. Returns 1 on success, 0 on
|
||||
* write failure. Call once at the start of an SSE handler. */
|
||||
el_val_t __http_sse_open(el_val_t conn_id);
|
||||
|
||||
/* Writes one SSE event frame: "data: <data>\n\n". data must not contain
|
||||
* newlines. Returns 1 on success, 0 if the client disconnected. */
|
||||
el_val_t __http_sse_send(el_val_t conn_id, el_val_t data);
|
||||
|
||||
/* Closes the SSE connection. The handler must return http_sse_sentinel()
|
||||
* so the HTTP worker does not double-close the fd. */
|
||||
el_val_t __http_sse_close(el_val_t conn_id);
|
||||
|
||||
/* ── Threading ───────────────────────────────────────────────────────────── */
|
||||
|
||||
/* Create a thread that calls the named El function with a String argument.
|
||||
* fn_name is resolved via dlsym(RTLD_DEFAULT, fn_name). Returns a thread
|
||||
* handle Int that can be passed to __thread_join. Returns -1 on failure. */
|
||||
el_val_t __thread_create(el_val_t fn_name, el_val_t arg);
|
||||
|
||||
/* Wait for thread tid (returned by __thread_create) to finish.
|
||||
* Returns the thread's return value as a String. */
|
||||
el_val_t __thread_join(el_val_t tid);
|
||||
|
||||
/* Allocate a new mutex. Returns a handle Int (index into internal table). */
|
||||
el_val_t __mutex_new(void);
|
||||
|
||||
void __mutex_lock(el_val_t m);
|
||||
void __mutex_unlock(el_val_t m);
|
||||
|
||||
/* ── Subprocess ──────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __exec(el_val_t cmd); /* popen, capture all stdout, return String */
|
||||
void __exec_bg(el_val_t cmd); /* fire and forget */
|
||||
|
||||
/* ── Environment and process ─────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __env_get(el_val_t key); /* getenv, return "" if not set */
|
||||
void __exit_program(el_val_t code);
|
||||
el_val_t __args_json(void); /* CLI args as JSON array string */
|
||||
|
||||
/* ── Time ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __time_now_ns(void); /* clock_gettime REALTIME, nanoseconds */
|
||||
void __sleep_ms(el_val_t ms);
|
||||
|
||||
/* ── UUID ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __uuid_v4(void);
|
||||
|
||||
/* ── Math ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __sqrt_f(el_val_t f);
|
||||
el_val_t __log_f(el_val_t f);
|
||||
el_val_t __ln_f(el_val_t f);
|
||||
el_val_t __sin_f(el_val_t f);
|
||||
el_val_t __cos_f(el_val_t f);
|
||||
el_val_t __pi_f(void);
|
||||
|
||||
/* ── JSON ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __json_get(el_val_t json, el_val_t key);
|
||||
el_val_t __json_get_raw(el_val_t json_str, el_val_t key);
|
||||
el_val_t __json_parse(el_val_t s);
|
||||
el_val_t __json_stringify(el_val_t v);
|
||||
el_val_t __json_parse_map(el_val_t json_str); /* alias for __json_parse */
|
||||
el_val_t __json_stringify_val(el_val_t val); /* alias for __json_stringify */
|
||||
el_val_t __json_array_len(el_val_t json_str);
|
||||
el_val_t __json_array_get(el_val_t json_str, el_val_t index);
|
||||
el_val_t __json_array_get_string(el_val_t json_str, el_val_t index);
|
||||
el_val_t __json_get_string(el_val_t json_str, el_val_t key);
|
||||
el_val_t __json_get_int(el_val_t json_str, el_val_t key);
|
||||
el_val_t __json_get_float(el_val_t json_str, el_val_t key);
|
||||
el_val_t __json_get_bool(el_val_t json_str, el_val_t key);
|
||||
el_val_t __json_set(el_val_t json_str, el_val_t key, el_val_t value);
|
||||
|
||||
/* ── State K/V ───────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __state_set(el_val_t key, el_val_t value);
|
||||
el_val_t __state_get(el_val_t key);
|
||||
el_val_t __state_del(el_val_t key);
|
||||
el_val_t __state_keys(void);
|
||||
|
||||
/* ── HTML/URL ────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __html_sanitize(el_val_t input_html, el_val_t allowlist_json);
|
||||
el_val_t __url_encode(el_val_t s);
|
||||
el_val_t __url_decode(el_val_t s);
|
||||
|
||||
/* ── Engram ──────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t __engram_node(el_val_t content, el_val_t node_type, el_val_t salience);
|
||||
el_val_t __engram_node_full(el_val_t content, el_val_t node_type, el_val_t label,
|
||||
el_val_t salience, el_val_t importance, el_val_t confidence,
|
||||
el_val_t tier, el_val_t tags);
|
||||
el_val_t __engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label,
|
||||
el_val_t salience, el_val_t certainty, el_val_t confidence,
|
||||
el_val_t status, el_val_t tags, el_val_t layer_id);
|
||||
el_val_t __engram_add_layer(el_val_t name, el_val_t priority, el_val_t suppressible,
|
||||
el_val_t transparent, el_val_t injectable);
|
||||
el_val_t __engram_remove_layer(el_val_t layer_id);
|
||||
el_val_t __engram_list_layers(void);
|
||||
el_val_t __engram_get_node(el_val_t id);
|
||||
void __engram_strengthen(el_val_t node_id);
|
||||
void __engram_forget(el_val_t node_id);
|
||||
el_val_t __engram_node_count(void);
|
||||
el_val_t __engram_search(el_val_t query, el_val_t limit);
|
||||
el_val_t __engram_scan_nodes(el_val_t limit, el_val_t offset);
|
||||
void __engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation);
|
||||
el_val_t __engram_edge_between(el_val_t from_id, el_val_t to_id);
|
||||
el_val_t __engram_neighbors(el_val_t node_id);
|
||||
el_val_t __engram_neighbors_filtered(el_val_t node_id, el_val_t max_depth, el_val_t direction);
|
||||
el_val_t __engram_edge_count(void);
|
||||
el_val_t __engram_activate(el_val_t query, el_val_t depth);
|
||||
el_val_t __engram_save(el_val_t path);
|
||||
el_val_t __engram_load(el_val_t path);
|
||||
el_val_t __engram_get_node_json(el_val_t id);
|
||||
el_val_t __engram_search_json(el_val_t query, el_val_t limit);
|
||||
el_val_t __engram_scan_nodes_json(el_val_t limit, el_val_t offset);
|
||||
el_val_t __engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset);
|
||||
el_val_t __engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction);
|
||||
el_val_t __engram_activate_json(el_val_t query, el_val_t depth);
|
||||
el_val_t __engram_stats_json(void);
|
||||
el_val_t __engram_list_layers_json(void);
|
||||
el_val_t __engram_compile_layered_json(el_val_t intent, el_val_t depth);
|
||||
|
||||
/* ── Cryptographic hashing ────────────────────────────────────────────────── */
|
||||
|
||||
/* __sha256_hex — return the SHA-256 hex digest of a string.
|
||||
* The returned string is 64 hex characters (lowercase). */
|
||||
el_val_t __sha256_hex(el_val_t s);
|
||||
|
||||
/* ── args init (called from main) ────────────────────────────────────────── */
|
||||
/* Store argc/argv for __args_json. Call once at the start of main(). */
|
||||
void el_seed_init_args(int argc, char** argv);
|
||||
|
||||
/* ── Native widget system (macOS AppKit) ─────────────────────────────────── */
|
||||
/*
|
||||
* Available when compiled with -DEL_TARGET_MACOS and linked with el_appkit.m
|
||||
* (-framework Cocoa). Widget handles are opaque Int values (int64_t slot
|
||||
* indices). The null/invalid handle value is -1.
|
||||
*
|
||||
* Lifecycle:
|
||||
* 1. Call __native_init() once at program start.
|
||||
* 2. Build widget tree with __window_create / __vstack_create / etc.
|
||||
* 3. Attach children with __widget_add_child(parent, child).
|
||||
* 4. Register callbacks with __widget_on_click / __widget_on_change.
|
||||
* 5. Call __window_show(win) to make the window visible.
|
||||
* 6. Call __native_run_loop() — never returns. AppKit owns the thread.
|
||||
*
|
||||
* Callback El function signature:
|
||||
* fn my_handler(widget: Int, data: String) -> Void
|
||||
*/
|
||||
#ifdef EL_TARGET_MACOS
|
||||
|
||||
/* Initialisation */
|
||||
void __native_init(void);
|
||||
void __native_run_loop(void);
|
||||
|
||||
/* Window */
|
||||
el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height,
|
||||
el_val_t min_width, el_val_t min_height);
|
||||
void __window_show(el_val_t handle);
|
||||
void __window_set_title(el_val_t handle, el_val_t title);
|
||||
|
||||
/* Layout containers */
|
||||
el_val_t __vstack_create(el_val_t spacing);
|
||||
el_val_t __hstack_create(el_val_t spacing);
|
||||
el_val_t __zstack_create(void);
|
||||
el_val_t __scroll_create(void);
|
||||
|
||||
/* Widgets */
|
||||
el_val_t __label_create(el_val_t text);
|
||||
el_val_t __button_create(el_val_t label);
|
||||
el_val_t __text_field_create(el_val_t placeholder);
|
||||
el_val_t __text_area_create(el_val_t placeholder);
|
||||
el_val_t __image_create(el_val_t path_or_name);
|
||||
|
||||
/* Widget properties */
|
||||
void __widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t __widget_get_text(el_val_t handle);
|
||||
void __widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
void __widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
void __widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold);
|
||||
void __widget_set_padding(el_val_t handle, el_val_t top, el_val_t right,
|
||||
el_val_t bottom, el_val_t left);
|
||||
void __widget_set_width(el_val_t handle, el_val_t width);
|
||||
void __widget_set_height(el_val_t handle, el_val_t height);
|
||||
void __widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
void __widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
void __widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
void __widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
|
||||
/* Layout / tree */
|
||||
void __widget_add_child(el_val_t parent, el_val_t child);
|
||||
void __widget_remove_child(el_val_t parent, el_val_t child);
|
||||
void __widget_destroy(el_val_t handle);
|
||||
|
||||
/* Events */
|
||||
void __widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
void __widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
|
||||
/* Manifest reader */
|
||||
/* Parse the app{} block from manifest.el at path. Returns a JSON string:
|
||||
* {"title":"...","width":N,"height":N,"min_width":N,"min_height":N} */
|
||||
el_val_t __manifest_read(el_val_t path);
|
||||
|
||||
#endif /* EL_TARGET_MACOS */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Executable
+182
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env bash
|
||||
# build-gtk4.sh — Compile and run native-hello using the GTK4 bridge on macOS.
|
||||
#
|
||||
# Usage:
|
||||
# ./build-gtk4.sh # compile and run
|
||||
# ./build-gtk4.sh compile # compile only
|
||||
# ./build-gtk4.sh clean # remove build artefacts
|
||||
#
|
||||
# Requirements:
|
||||
# - elc in PATH (or ../../../lang/dist/platform/elc)
|
||||
# - clang (Xcode Command Line Tools)
|
||||
# - GTK4 via Homebrew: brew install gtk4
|
||||
# - macOS 12+
|
||||
#
|
||||
# This is the same pipeline as build.sh but targeting the GTK4 backend:
|
||||
# - Links with $(pkg-config --libs gtk4) instead of -framework Cocoa
|
||||
# - Compiles el_gtk4.c (plain C) instead of el_appkit.m (ObjC)
|
||||
# - Defines EL_TARGET_LINUX (the GTK4 target flag)
|
||||
#
|
||||
# Multi-file compilation strategy: see build.sh comments — identical rationale.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
EL_LANG_ROOT="${SCRIPT_DIR}/../../../lang"
|
||||
EL_UI_ROOT="${SCRIPT_DIR}/../.."
|
||||
EL_RUNTIME="${EL_LANG_ROOT}/el-compiler/runtime"
|
||||
EL_NATIVE_VESSEL="${EL_UI_ROOT}/vessels/el-native/src/main.el"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build-gtk4"
|
||||
|
||||
# Locate elc
|
||||
if command -v elc &>/dev/null; then
|
||||
ELC="elc"
|
||||
elif [ -x "${EL_LANG_ROOT}/dist/platform/elc" ]; then
|
||||
ELC="${EL_LANG_ROOT}/dist/platform/elc"
|
||||
else
|
||||
echo "Error: elc not found. Add it to PATH or place it at:"
|
||||
echo " ${EL_LANG_ROOT}/dist/platform/elc"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# GTK4 pkg-config flags
|
||||
GTK4_CFLAGS=$(pkg-config --cflags gtk4)
|
||||
GTK4_LIBS=$(pkg-config --libs gtk4)
|
||||
|
||||
CLANG_FLAGS_COMMON=(
|
||||
-std=c11
|
||||
-DEL_TARGET_LINUX
|
||||
-I "${EL_RUNTIME}"
|
||||
)
|
||||
|
||||
clean() {
|
||||
rm -rf "${BUILD_DIR}"
|
||||
echo "Cleaned."
|
||||
}
|
||||
|
||||
compile() {
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
|
||||
# ── 1. Vessel forward-declarations header ─────────────────────────────────
|
||||
# elc --emit-header emits full function bodies, not just prototypes.
|
||||
# Extract only the forward-declaration lines: stop at the first function
|
||||
# definition (a line that ends with ') {' opening a body block).
|
||||
# macOS awk does not support \s — use POSIX [[:space:]] instead.
|
||||
echo "==> Generating el-native vessel header (declarations only)..."
|
||||
"${ELC}" --emit-header "${EL_NATIVE_VESSEL}" \
|
||||
| awk 'BEGIN{in_body=0} /^[a-zA-Z_].+\)[[:space:]]*\{/ {in_body=1} !in_body{print}' \
|
||||
> "${BUILD_DIR}/el_native_vessel.h"
|
||||
|
||||
# ── 2. Compile el-native vessel to C ──────────────────────────────────────
|
||||
echo "==> Compiling el-native vessel to C..."
|
||||
"${ELC}" "${EL_NATIVE_VESSEL}" \
|
||||
> "${BUILD_DIR}/el_native_vessel.c"
|
||||
|
||||
# ── 3. Compile app entry to C ─────────────────────────────────────────────
|
||||
echo "==> Compiling app entry to C..."
|
||||
EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \
|
||||
"${ELC}" "${SCRIPT_DIR}/src/main.el" \
|
||||
> "${BUILD_DIR}/native_hello.c"
|
||||
|
||||
# ── 4. Compile el_gtk4.c (plain C, GTK4 backend) ─────────────────────────
|
||||
echo "==> Compiling el_gtk4.c (GTK4 bridge)..."
|
||||
# shellcheck disable=SC2086
|
||||
clang \
|
||||
-std=c11 \
|
||||
-DEL_TARGET_LINUX \
|
||||
-I "${EL_RUNTIME}" \
|
||||
${GTK4_CFLAGS} \
|
||||
-c "${EL_RUNTIME}/el_gtk4.c" \
|
||||
-o "${BUILD_DIR}/el_gtk4.o"
|
||||
|
||||
# ── 5a. Compile el_seed.c ─────────────────────────────────────────────────
|
||||
# el_seed.c provides the OS-boundary __-prefixed primitives + thin wrappers.
|
||||
# With -DEL_TARGET_LINUX, the EL_TARGET_LINUX section in el_seed.c is
|
||||
# compiled in (GTK4 forwarders) and the EL_TARGET_MACOS section is excluded.
|
||||
echo "==> Compiling el_seed.c (EL_TARGET_LINUX)..."
|
||||
# shellcheck disable=SC2086
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
${GTK4_CFLAGS} \
|
||||
-c "${EL_RUNTIME}/el_seed.c" \
|
||||
-o "${BUILD_DIR}/el_seed.o"
|
||||
|
||||
# ── 5b. Compile el_runtime.c and patch out duplicate __-prefixed symbols ──
|
||||
# Same strategy as build.sh: el_runtime.c has duplicates of the __-prefixed
|
||||
# OS wrappers that el_seed.c defines. Hide el_runtime.o's copies with nmedit.
|
||||
echo "==> Compiling el_runtime.c..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-c "${EL_RUNTIME}/el_runtime.c" \
|
||||
-o "${BUILD_DIR}/el_runtime.o"
|
||||
|
||||
echo "==> Patching el_runtime.o (hiding __-prefixed symbols duplicated in el_seed.o)..."
|
||||
nm "${BUILD_DIR}/el_seed.o" 2>/dev/null \
|
||||
| awk '/^[0-9a-f]+ T _/{print $3}' | sort > "${BUILD_DIR}/.seed_T.txt"
|
||||
nm "${BUILD_DIR}/el_runtime.o" 2>/dev/null \
|
||||
| awk '/^[0-9a-f]+ T _/{print $3}' | sort > "${BUILD_DIR}/.rt_T.txt"
|
||||
comm -23 "${BUILD_DIR}/.rt_T.txt" "${BUILD_DIR}/.seed_T.txt" \
|
||||
> "${BUILD_DIR}/.rt_keep.txt"
|
||||
nmedit -s "${BUILD_DIR}/.rt_keep.txt" "${BUILD_DIR}/el_runtime.o"
|
||||
|
||||
# ── 6. Compile el-native vessel object ────────────────────────────────────
|
||||
# Vessel code calls __-prefixed builtins. el_native_target.h provides the
|
||||
# EL_TARGET_LINUX block of declarations (same function signatures as MACOS).
|
||||
echo "==> Compiling el-native vessel object..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-include "${EL_RUNTIME}/el_native_target.h" \
|
||||
-c "${BUILD_DIR}/el_native_vessel.c" \
|
||||
-o "${BUILD_DIR}/el_native_vessel.o"
|
||||
|
||||
echo "==> Patching el_native_vessel.o (hiding vessel main)..."
|
||||
nm "${BUILD_DIR}/el_native_vessel.o" 2>/dev/null \
|
||||
| awk '/^[0-9a-f]+ T _/{print $3}' \
|
||||
| grep -v '^_main$' \
|
||||
> "${BUILD_DIR}/.vessel_keep.txt"
|
||||
nmedit -s "${BUILD_DIR}/.vessel_keep.txt" "${BUILD_DIR}/el_native_vessel.o"
|
||||
|
||||
# ── 7. Compile app object ─────────────────────────────────────────────────
|
||||
# Inject el_native_target.h (float ops + EL_TARGET_LINUX native decls) and
|
||||
# the vessel's forward-declaration header.
|
||||
echo "==> Compiling app object..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-include "${EL_RUNTIME}/el_native_target.h" \
|
||||
-include "${BUILD_DIR}/el_native_vessel.h" \
|
||||
-c "${BUILD_DIR}/native_hello.c" \
|
||||
-o "${BUILD_DIR}/native_hello.o"
|
||||
|
||||
# ── 8. Link ───────────────────────────────────────────────────────────────
|
||||
# el_seed.o = OS-boundary primitives (__-prefixed) + GTK4 forwarders
|
||||
# el_runtime.o = high-level builtins (str_len, json_*, etc.), patched
|
||||
# el_native_vessel.o = el-level widget API, patched
|
||||
# el_gtk4.o = GTK4 C bridge
|
||||
# GTK4_LIBS = $(pkg-config --libs gtk4)
|
||||
# -ldl = dlsym for callback dispatch
|
||||
# -lpthread = threading
|
||||
echo "==> Linking native-hello-gtk4..."
|
||||
# shellcheck disable=SC2086
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
"${BUILD_DIR}/native_hello.o" \
|
||||
"${BUILD_DIR}/el_native_vessel.o" \
|
||||
"${BUILD_DIR}/el_gtk4.o" \
|
||||
"${BUILD_DIR}/el_runtime.o" \
|
||||
"${BUILD_DIR}/el_seed.o" \
|
||||
${GTK4_LIBS} \
|
||||
-ldl \
|
||||
-lpthread \
|
||||
-lcurl \
|
||||
-o "${BUILD_DIR}/native-hello-gtk4"
|
||||
|
||||
echo "==> Build complete: ${BUILD_DIR}/native-hello-gtk4"
|
||||
}
|
||||
|
||||
run() {
|
||||
compile
|
||||
echo "==> Launching native-hello-gtk4..."
|
||||
EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \
|
||||
"${BUILD_DIR}/native-hello-gtk4"
|
||||
}
|
||||
|
||||
case "${1:-run}" in
|
||||
clean) clean ;;
|
||||
compile) compile ;;
|
||||
run|*) run ;;
|
||||
esac
|
||||
@@ -0,0 +1,397 @@
|
||||
___args_json
|
||||
___channel_close
|
||||
___channel_new
|
||||
___channel_recv
|
||||
___channel_send
|
||||
___channel_try_recv
|
||||
___env_get
|
||||
___exec
|
||||
___exec_bg
|
||||
___exit_program
|
||||
___float_to_str
|
||||
___fs_exists
|
||||
___fs_list_raw
|
||||
___fs_mkdir
|
||||
___fs_read
|
||||
___fs_write
|
||||
___fs_write_bytes
|
||||
___http_conn_fd
|
||||
___http_do
|
||||
___http_do_map
|
||||
___http_do_map_to_file
|
||||
___http_response
|
||||
___http_serve
|
||||
___http_serve_v2
|
||||
___http_sse_close
|
||||
___http_sse_open
|
||||
___http_sse_send
|
||||
___int_to_str
|
||||
___json_array_get
|
||||
___json_array_get_string
|
||||
___json_array_len
|
||||
___json_get
|
||||
___json_get_raw
|
||||
___json_parse_map
|
||||
___json_set
|
||||
___json_stringify_val
|
||||
___mutex_lock
|
||||
___mutex_new
|
||||
___mutex_unlock
|
||||
___print
|
||||
___print_raw
|
||||
___println
|
||||
___read_n
|
||||
___readline
|
||||
___sha256_hex
|
||||
___state_del
|
||||
___state_get
|
||||
___state_keys
|
||||
___state_set
|
||||
___str_alloc
|
||||
___str_char_at
|
||||
___str_cmp
|
||||
___str_concat_raw
|
||||
___str_len
|
||||
___str_ncmp
|
||||
___str_set_char
|
||||
___str_slice_raw
|
||||
___str_to_float
|
||||
___str_to_int
|
||||
___thread_create
|
||||
___thread_join
|
||||
___url_decode
|
||||
___url_encode
|
||||
___uuid_v4
|
||||
_aead_decrypt
|
||||
_aead_encrypt
|
||||
_append
|
||||
_args
|
||||
_base64_decode
|
||||
_base64_encode
|
||||
_base64url_decode
|
||||
_base64url_encode
|
||||
_bool_to_str
|
||||
_bytes_to_str
|
||||
_cal_cycle_phase
|
||||
_cal_format
|
||||
_cal_in
|
||||
_cal_to_instant
|
||||
_config
|
||||
_cycle_calendar
|
||||
_decimal_round
|
||||
_duration_millis
|
||||
_duration_nanos
|
||||
_duration_seconds
|
||||
_duration_to_millis
|
||||
_duration_to_nanos
|
||||
_duration_to_seconds
|
||||
_earth_calendar
|
||||
_earth_calendar_default
|
||||
_el_abs
|
||||
_el_arena_pop
|
||||
_el_arena_push
|
||||
_el_base64_encode_n
|
||||
_el_cgi_init
|
||||
_el_duration_add
|
||||
_el_duration_div
|
||||
_el_duration_eq
|
||||
_el_duration_from_nanos
|
||||
_el_duration_ge
|
||||
_el_duration_gt
|
||||
_el_duration_le
|
||||
_el_duration_lt
|
||||
_el_duration_ne
|
||||
_el_duration_scale
|
||||
_el_duration_sub
|
||||
_el_get_field
|
||||
_el_html_sanitize
|
||||
_el_instant_add_dur
|
||||
_el_instant_diff
|
||||
_el_instant_eq
|
||||
_el_instant_ge
|
||||
_el_instant_gt
|
||||
_el_instant_le
|
||||
_el_instant_lt
|
||||
_el_instant_ne
|
||||
_el_instant_sub_dur
|
||||
_el_list_append
|
||||
_el_list_clone
|
||||
_el_list_empty
|
||||
_el_list_get
|
||||
_el_list_len
|
||||
_el_list_new
|
||||
_el_local_date_add_dur
|
||||
_el_local_date_eq
|
||||
_el_local_date_lt
|
||||
_el_local_time_add_dur
|
||||
_el_map_get
|
||||
_el_map_new
|
||||
_el_map_set
|
||||
_el_max
|
||||
_el_mem_check
|
||||
_el_min
|
||||
_el_now_instant
|
||||
_el_release
|
||||
_el_request_end
|
||||
_el_request_start
|
||||
_el_retain
|
||||
_el_runtime_init_args
|
||||
_el_runtime_register_handler
|
||||
_el_runtime_register_handler_v2
|
||||
_el_sha256_bytes_n
|
||||
_el_sleep_duration
|
||||
_el_str_concat
|
||||
_engram_activate
|
||||
_engram_activate_json
|
||||
_engram_add_layer
|
||||
_engram_apply_decay_json
|
||||
_engram_compile_layered_json
|
||||
_engram_connect
|
||||
_engram_edge_between
|
||||
_engram_edge_count
|
||||
_engram_forget
|
||||
_engram_get_node
|
||||
_engram_get_node_json
|
||||
_engram_list_layers
|
||||
_engram_list_layers_json
|
||||
_engram_load
|
||||
_engram_neighbors
|
||||
_engram_neighbors_filtered
|
||||
_engram_neighbors_json
|
||||
_engram_node
|
||||
_engram_node_count
|
||||
_engram_node_full
|
||||
_engram_node_layered
|
||||
_engram_query_range
|
||||
_engram_remove_layer
|
||||
_engram_save
|
||||
_engram_scan_nodes
|
||||
_engram_scan_nodes_by_type_json
|
||||
_engram_scan_nodes_json
|
||||
_engram_search
|
||||
_engram_search_json
|
||||
_engram_stats_json
|
||||
_engram_strengthen
|
||||
_env
|
||||
_exec
|
||||
_exec_bg
|
||||
_exec_capture
|
||||
_exec_command
|
||||
_exit_program
|
||||
_float_to_int
|
||||
_float_to_str
|
||||
_format_float
|
||||
_fs_exists
|
||||
_fs_list
|
||||
_fs_list_json
|
||||
_fs_mkdir
|
||||
_fs_read
|
||||
_fs_write
|
||||
_fs_write_bytes
|
||||
_get
|
||||
_getpid_now
|
||||
_hash_sha256
|
||||
_hmac_sha256_bytes
|
||||
_hmac_sha256_hex
|
||||
_html_escape
|
||||
_html_raw
|
||||
_http_delete
|
||||
_http_get
|
||||
_http_get_engram
|
||||
_http_get_to_file
|
||||
_http_get_with_headers
|
||||
_http_patch
|
||||
_http_post
|
||||
_http_post_engram
|
||||
_http_post_form_auth
|
||||
_http_post_json
|
||||
_http_post_json_with_headers
|
||||
_http_post_to_file
|
||||
_http_post_with_headers
|
||||
_http_response
|
||||
_http_serve
|
||||
_http_serve_v2
|
||||
_http_set_handler
|
||||
_http_set_handler_v2
|
||||
_in_calendar
|
||||
_instant_from_iso8601
|
||||
_instant_to_iso8601
|
||||
_instant_to_unix_millis
|
||||
_instant_to_unix_seconds
|
||||
_int_to_float
|
||||
_int_to_str
|
||||
_is_alphanumeric
|
||||
_is_digit
|
||||
_is_letter
|
||||
_is_lowercase
|
||||
_is_punctuation
|
||||
_is_uppercase
|
||||
_is_whitespace
|
||||
_json_array_get
|
||||
_json_array_get_string
|
||||
_json_array_len
|
||||
_json_array_push
|
||||
_json_build_array
|
||||
_json_build_object
|
||||
_json_escape_string
|
||||
_json_get
|
||||
_json_get_bool
|
||||
_json_get_float
|
||||
_json_get_int
|
||||
_json_get_raw
|
||||
_json_get_string
|
||||
_json_parse
|
||||
_json_set
|
||||
_json_stringify
|
||||
_len
|
||||
_list_get
|
||||
_list_join
|
||||
_list_len
|
||||
_list_push
|
||||
_list_push_front
|
||||
_list_range
|
||||
_llm_call
|
||||
_llm_call_agentic
|
||||
_llm_call_system
|
||||
_llm_models
|
||||
_llm_register_tool
|
||||
_llm_vision
|
||||
_local_date
|
||||
_local_date_day
|
||||
_local_date_month
|
||||
_local_date_year
|
||||
_local_datetime
|
||||
_local_time
|
||||
_local_time_hour
|
||||
_local_time_minute
|
||||
_local_time_nanos
|
||||
_local_time_second
|
||||
_log_info
|
||||
_log_warn
|
||||
_map_get
|
||||
_map_set
|
||||
_mars_calendar
|
||||
_math_cos
|
||||
_math_ln
|
||||
_math_log
|
||||
_math_pi
|
||||
_math_sin
|
||||
_math_sqrt
|
||||
_native_int_to_str
|
||||
_native_list_append
|
||||
_native_list_clone
|
||||
_native_list_empty
|
||||
_native_list_get
|
||||
_native_list_len
|
||||
_native_str_to_int
|
||||
_native_string_chars
|
||||
_no_cycle_calendar
|
||||
_now
|
||||
_now_in
|
||||
_now_millis
|
||||
_now_ns
|
||||
_parse_int
|
||||
_pq_hybrid_handshake
|
||||
_pq_hybrid_keygen
|
||||
_pq_kem_decaps
|
||||
_pq_kem_encaps
|
||||
_pq_kem_keygen
|
||||
_pq_keygen_signature
|
||||
_pq_sign
|
||||
_pq_verify
|
||||
_print
|
||||
_println
|
||||
_readline
|
||||
_relative_calendar
|
||||
_rhythm_and
|
||||
_rhythm_cycle_phase
|
||||
_rhythm_cycle_start
|
||||
_rhythm_duration
|
||||
_rhythm_event
|
||||
_rhythm_matches
|
||||
_rhythm_next_after
|
||||
_rhythm_or
|
||||
_rhythm_session_start
|
||||
_rhythm_weekday
|
||||
_rhythm_weekly_at
|
||||
_sha256_bytes
|
||||
_sha256_hex
|
||||
_sha3_256_hex
|
||||
_sleep_ms
|
||||
_sleep_secs
|
||||
_state_del
|
||||
_state_get
|
||||
_state_get_or
|
||||
_state_has
|
||||
_state_keys
|
||||
_state_set
|
||||
_stdout_restore
|
||||
_stdout_to_file
|
||||
_str_char_at
|
||||
_str_char_code
|
||||
_str_concat
|
||||
_str_contains
|
||||
_str_count
|
||||
_str_count_bytes
|
||||
_str_count_chars
|
||||
_str_count_digits
|
||||
_str_count_letters
|
||||
_str_count_lines
|
||||
_str_count_words
|
||||
_str_ends_with
|
||||
_str_eq
|
||||
_str_find_chars
|
||||
_str_format
|
||||
_str_index_of
|
||||
_str_index_of_all
|
||||
_str_join
|
||||
_str_last_index_of
|
||||
_str_len
|
||||
_str_lower
|
||||
_str_lstrip
|
||||
_str_pad_left
|
||||
_str_pad_right
|
||||
_str_repeat
|
||||
_str_replace
|
||||
_str_reverse
|
||||
_str_rstrip
|
||||
_str_slice
|
||||
_str_split
|
||||
_str_split_chars
|
||||
_str_split_lines
|
||||
_str_split_n
|
||||
_str_starts_with
|
||||
_str_strip_chars
|
||||
_str_strip_prefix
|
||||
_str_strip_suffix
|
||||
_str_to_bytes
|
||||
_str_to_float
|
||||
_str_to_int
|
||||
_str_to_lower
|
||||
_str_to_upper
|
||||
_str_trim
|
||||
_str_upper
|
||||
_time_add
|
||||
_time_diff
|
||||
_time_format
|
||||
_time_from_parts
|
||||
_time_now
|
||||
_time_now_ms
|
||||
_time_now_utc
|
||||
_time_to_parts
|
||||
_ttl_cache_age
|
||||
_ttl_cache_get
|
||||
_ttl_cache_set
|
||||
_unix_millis
|
||||
_unix_seconds
|
||||
_unix_timestamp
|
||||
_unix_timestamp_ms
|
||||
_url_decode
|
||||
_url_encode
|
||||
_uuid_new
|
||||
_uuid_v4
|
||||
_zone
|
||||
_zone_local
|
||||
_zone_offset
|
||||
_zone_utc
|
||||
_zoned
|
||||
@@ -0,0 +1,340 @@
|
||||
___channel_close
|
||||
___channel_new
|
||||
___channel_recv
|
||||
___channel_send
|
||||
___channel_try_recv
|
||||
___http_do_map
|
||||
___http_do_map_to_file
|
||||
___print_raw
|
||||
___read_n
|
||||
_aead_decrypt
|
||||
_aead_encrypt
|
||||
_append
|
||||
_args
|
||||
_base64_decode
|
||||
_base64_encode
|
||||
_base64url_decode
|
||||
_base64url_encode
|
||||
_bool_to_str
|
||||
_bytes_to_str
|
||||
_cal_cycle_phase
|
||||
_cal_format
|
||||
_cal_in
|
||||
_cal_to_instant
|
||||
_config
|
||||
_cycle_calendar
|
||||
_decimal_round
|
||||
_duration_millis
|
||||
_duration_nanos
|
||||
_duration_seconds
|
||||
_duration_to_millis
|
||||
_duration_to_nanos
|
||||
_duration_to_seconds
|
||||
_earth_calendar
|
||||
_earth_calendar_default
|
||||
_el_abs
|
||||
_el_arena_pop
|
||||
_el_arena_push
|
||||
_el_base64_encode_n
|
||||
_el_cgi_init
|
||||
_el_duration_add
|
||||
_el_duration_div
|
||||
_el_duration_eq
|
||||
_el_duration_from_nanos
|
||||
_el_duration_ge
|
||||
_el_duration_gt
|
||||
_el_duration_le
|
||||
_el_duration_lt
|
||||
_el_duration_ne
|
||||
_el_duration_scale
|
||||
_el_duration_sub
|
||||
_el_get_field
|
||||
_el_html_sanitize
|
||||
_el_instant_add_dur
|
||||
_el_instant_diff
|
||||
_el_instant_eq
|
||||
_el_instant_ge
|
||||
_el_instant_gt
|
||||
_el_instant_le
|
||||
_el_instant_lt
|
||||
_el_instant_ne
|
||||
_el_instant_sub_dur
|
||||
_el_list_append
|
||||
_el_list_clone
|
||||
_el_list_empty
|
||||
_el_list_get
|
||||
_el_list_len
|
||||
_el_list_new
|
||||
_el_local_date_add_dur
|
||||
_el_local_date_eq
|
||||
_el_local_date_lt
|
||||
_el_local_time_add_dur
|
||||
_el_map_get
|
||||
_el_map_new
|
||||
_el_map_set
|
||||
_el_max
|
||||
_el_mem_check
|
||||
_el_min
|
||||
_el_now_instant
|
||||
_el_release
|
||||
_el_retain
|
||||
_el_runtime_init_args
|
||||
_el_runtime_register_handler
|
||||
_el_runtime_register_handler_v2
|
||||
_el_sha256_bytes_n
|
||||
_el_sleep_duration
|
||||
_el_str_concat
|
||||
_engram_activate
|
||||
_engram_activate_json
|
||||
_engram_add_layer
|
||||
_engram_apply_decay_json
|
||||
_engram_compile_layered_json
|
||||
_engram_connect
|
||||
_engram_edge_between
|
||||
_engram_edge_count
|
||||
_engram_forget
|
||||
_engram_get_node
|
||||
_engram_get_node_json
|
||||
_engram_list_layers
|
||||
_engram_list_layers_json
|
||||
_engram_load
|
||||
_engram_neighbors
|
||||
_engram_neighbors_filtered
|
||||
_engram_neighbors_json
|
||||
_engram_node
|
||||
_engram_node_count
|
||||
_engram_node_full
|
||||
_engram_node_layered
|
||||
_engram_query_range
|
||||
_engram_remove_layer
|
||||
_engram_save
|
||||
_engram_scan_nodes
|
||||
_engram_scan_nodes_by_type_json
|
||||
_engram_scan_nodes_json
|
||||
_engram_search
|
||||
_engram_search_json
|
||||
_engram_stats_json
|
||||
_engram_strengthen
|
||||
_env
|
||||
_exec
|
||||
_exec_bg
|
||||
_exec_capture
|
||||
_exec_command
|
||||
_exit_program
|
||||
_float_to_int
|
||||
_float_to_str
|
||||
_format_float
|
||||
_fs_exists
|
||||
_fs_list
|
||||
_fs_list_json
|
||||
_fs_mkdir
|
||||
_fs_read
|
||||
_fs_write
|
||||
_fs_write_bytes
|
||||
_get
|
||||
_getpid_now
|
||||
_hash_sha256
|
||||
_hmac_sha256_bytes
|
||||
_hmac_sha256_hex
|
||||
_html_escape
|
||||
_html_raw
|
||||
_http_delete
|
||||
_http_get
|
||||
_http_get_engram
|
||||
_http_get_to_file
|
||||
_http_get_with_headers
|
||||
_http_patch
|
||||
_http_post
|
||||
_http_post_engram
|
||||
_http_post_form_auth
|
||||
_http_post_json
|
||||
_http_post_json_with_headers
|
||||
_http_post_to_file
|
||||
_http_post_with_headers
|
||||
_http_response
|
||||
_http_serve
|
||||
_http_serve_v2
|
||||
_http_set_handler
|
||||
_http_set_handler_v2
|
||||
_in_calendar
|
||||
_instant_from_iso8601
|
||||
_instant_to_iso8601
|
||||
_instant_to_unix_millis
|
||||
_instant_to_unix_seconds
|
||||
_int_to_float
|
||||
_int_to_str
|
||||
_is_alphanumeric
|
||||
_is_digit
|
||||
_is_letter
|
||||
_is_lowercase
|
||||
_is_punctuation
|
||||
_is_uppercase
|
||||
_is_whitespace
|
||||
_json_array_get
|
||||
_json_array_get_string
|
||||
_json_array_len
|
||||
_json_array_push
|
||||
_json_build_array
|
||||
_json_build_object
|
||||
_json_escape_string
|
||||
_json_get
|
||||
_json_get_bool
|
||||
_json_get_float
|
||||
_json_get_int
|
||||
_json_get_raw
|
||||
_json_get_string
|
||||
_json_parse
|
||||
_json_set
|
||||
_json_stringify
|
||||
_len
|
||||
_list_get
|
||||
_list_join
|
||||
_list_len
|
||||
_list_push
|
||||
_list_push_front
|
||||
_list_range
|
||||
_llm_call
|
||||
_llm_call_agentic
|
||||
_llm_call_system
|
||||
_llm_models
|
||||
_llm_register_tool
|
||||
_llm_vision
|
||||
_local_date
|
||||
_local_date_day
|
||||
_local_date_month
|
||||
_local_date_year
|
||||
_local_datetime
|
||||
_local_time
|
||||
_local_time_hour
|
||||
_local_time_minute
|
||||
_local_time_nanos
|
||||
_local_time_second
|
||||
_log_info
|
||||
_log_warn
|
||||
_map_get
|
||||
_map_set
|
||||
_mars_calendar
|
||||
_math_cos
|
||||
_math_ln
|
||||
_math_log
|
||||
_math_pi
|
||||
_math_sin
|
||||
_math_sqrt
|
||||
_native_int_to_str
|
||||
_native_list_append
|
||||
_native_list_clone
|
||||
_native_list_empty
|
||||
_native_list_get
|
||||
_native_list_len
|
||||
_native_str_to_int
|
||||
_native_string_chars
|
||||
_no_cycle_calendar
|
||||
_now
|
||||
_now_in
|
||||
_now_millis
|
||||
_now_ns
|
||||
_parse_int
|
||||
_pq_hybrid_handshake
|
||||
_pq_hybrid_keygen
|
||||
_pq_kem_decaps
|
||||
_pq_kem_encaps
|
||||
_pq_kem_keygen
|
||||
_pq_keygen_signature
|
||||
_pq_sign
|
||||
_pq_verify
|
||||
_print
|
||||
_println
|
||||
_readline
|
||||
_relative_calendar
|
||||
_rhythm_and
|
||||
_rhythm_cycle_phase
|
||||
_rhythm_cycle_start
|
||||
_rhythm_duration
|
||||
_rhythm_event
|
||||
_rhythm_matches
|
||||
_rhythm_next_after
|
||||
_rhythm_or
|
||||
_rhythm_session_start
|
||||
_rhythm_weekday
|
||||
_rhythm_weekly_at
|
||||
_sha256_bytes
|
||||
_sha256_hex
|
||||
_sha3_256_hex
|
||||
_sleep_ms
|
||||
_sleep_secs
|
||||
_state_del
|
||||
_state_get
|
||||
_state_get_or
|
||||
_state_has
|
||||
_state_keys
|
||||
_state_set
|
||||
_stdout_restore
|
||||
_stdout_to_file
|
||||
_str_char_at
|
||||
_str_char_code
|
||||
_str_concat
|
||||
_str_contains
|
||||
_str_count
|
||||
_str_count_bytes
|
||||
_str_count_chars
|
||||
_str_count_digits
|
||||
_str_count_letters
|
||||
_str_count_lines
|
||||
_str_count_words
|
||||
_str_ends_with
|
||||
_str_eq
|
||||
_str_find_chars
|
||||
_str_format
|
||||
_str_index_of
|
||||
_str_index_of_all
|
||||
_str_join
|
||||
_str_last_index_of
|
||||
_str_len
|
||||
_str_lower
|
||||
_str_lstrip
|
||||
_str_pad_left
|
||||
_str_pad_right
|
||||
_str_repeat
|
||||
_str_replace
|
||||
_str_reverse
|
||||
_str_rstrip
|
||||
_str_slice
|
||||
_str_split
|
||||
_str_split_chars
|
||||
_str_split_lines
|
||||
_str_split_n
|
||||
_str_starts_with
|
||||
_str_strip_chars
|
||||
_str_strip_prefix
|
||||
_str_strip_suffix
|
||||
_str_to_bytes
|
||||
_str_to_float
|
||||
_str_to_int
|
||||
_str_to_lower
|
||||
_str_to_upper
|
||||
_str_trim
|
||||
_str_upper
|
||||
_time_add
|
||||
_time_diff
|
||||
_time_format
|
||||
_time_from_parts
|
||||
_time_now
|
||||
_time_now_ms
|
||||
_time_now_utc
|
||||
_time_to_parts
|
||||
_ttl_cache_age
|
||||
_ttl_cache_get
|
||||
_ttl_cache_set
|
||||
_unix_millis
|
||||
_unix_seconds
|
||||
_unix_timestamp
|
||||
_unix_timestamp_ms
|
||||
_url_decode
|
||||
_url_encode
|
||||
_uuid_new
|
||||
_uuid_v4
|
||||
_zone
|
||||
_zone_local
|
||||
_zone_offset
|
||||
_zone_utc
|
||||
_zoned
|
||||
@@ -0,0 +1,137 @@
|
||||
___args_json
|
||||
___button_create
|
||||
___cos_f
|
||||
___engram_activate
|
||||
___engram_activate_json
|
||||
___engram_add_layer
|
||||
___engram_compile_layered_json
|
||||
___engram_connect
|
||||
___engram_edge_between
|
||||
___engram_edge_count
|
||||
___engram_forget
|
||||
___engram_get_node
|
||||
___engram_get_node_json
|
||||
___engram_list_layers
|
||||
___engram_list_layers_json
|
||||
___engram_load
|
||||
___engram_neighbors
|
||||
___engram_neighbors_filtered
|
||||
___engram_neighbors_json
|
||||
___engram_node
|
||||
___engram_node_count
|
||||
___engram_node_full
|
||||
___engram_node_layered
|
||||
___engram_remove_layer
|
||||
___engram_save
|
||||
___engram_scan_nodes
|
||||
___engram_scan_nodes_by_type_json
|
||||
___engram_scan_nodes_json
|
||||
___engram_search
|
||||
___engram_search_json
|
||||
___engram_stats_json
|
||||
___engram_strengthen
|
||||
___env_get
|
||||
___exec
|
||||
___exec_bg
|
||||
___exit_program
|
||||
___float_to_str
|
||||
___fs_exists
|
||||
___fs_list_raw
|
||||
___fs_mkdir
|
||||
___fs_read
|
||||
___fs_write
|
||||
___fs_write_bytes
|
||||
___hstack_create
|
||||
___html_sanitize
|
||||
___http_conn_fd
|
||||
___http_do
|
||||
___http_do_to_file
|
||||
___http_response
|
||||
___http_serve
|
||||
___http_serve_v2
|
||||
___http_sse_close
|
||||
___http_sse_open
|
||||
___http_sse_send
|
||||
___image_create
|
||||
___int_to_str
|
||||
___json_array_get
|
||||
___json_array_get_string
|
||||
___json_array_len
|
||||
___json_get
|
||||
___json_get_bool
|
||||
___json_get_float
|
||||
___json_get_int
|
||||
___json_get_raw
|
||||
___json_get_string
|
||||
___json_parse
|
||||
___json_parse_map
|
||||
___json_set
|
||||
___json_stringify
|
||||
___json_stringify_val
|
||||
___label_create
|
||||
___ln_f
|
||||
___log_f
|
||||
___manifest_read
|
||||
___mutex_lock
|
||||
___mutex_new
|
||||
___mutex_unlock
|
||||
___native_init
|
||||
___native_run_loop
|
||||
___pi_f
|
||||
___print
|
||||
___println
|
||||
___readline
|
||||
___scroll_create
|
||||
___sha256_hex
|
||||
___sin_f
|
||||
___sleep_ms
|
||||
___sqrt_f
|
||||
___state_del
|
||||
___state_get
|
||||
___state_keys
|
||||
___state_set
|
||||
___str_alloc
|
||||
___str_char_at
|
||||
___str_cmp
|
||||
___str_concat_raw
|
||||
___str_len
|
||||
___str_ncmp
|
||||
___str_set_char
|
||||
___str_slice_raw
|
||||
___str_to_float
|
||||
___str_to_int
|
||||
___text_area_create
|
||||
___text_field_create
|
||||
___thread_create
|
||||
___thread_join
|
||||
___time_now_ns
|
||||
___url_decode
|
||||
___url_encode
|
||||
___uuid_v4
|
||||
___vstack_create
|
||||
___widget_add_child
|
||||
___widget_destroy
|
||||
___widget_get_text
|
||||
___widget_on_change
|
||||
___widget_on_click
|
||||
___widget_on_submit
|
||||
___widget_remove_child
|
||||
___widget_set_bg_color
|
||||
___widget_set_color
|
||||
___widget_set_corner_radius
|
||||
___widget_set_disabled
|
||||
___widget_set_flex
|
||||
___widget_set_font
|
||||
___widget_set_height
|
||||
___widget_set_hidden
|
||||
___widget_set_padding
|
||||
___widget_set_text
|
||||
___widget_set_width
|
||||
___window_create
|
||||
___window_set_title
|
||||
___window_show
|
||||
___zstack_create
|
||||
_el_request_end
|
||||
_el_request_start
|
||||
_el_seed_init_args
|
||||
_el_seed_set_http_conn_fd
|
||||
@@ -0,0 +1,55 @@
|
||||
_button
|
||||
_color_hex_a
|
||||
_color_hex_b
|
||||
_color_hex_g
|
||||
_color_hex_r
|
||||
_hex_channel
|
||||
_hex_nibble
|
||||
_hstack
|
||||
_image
|
||||
_label
|
||||
_manifest_height
|
||||
_manifest_min_height
|
||||
_manifest_min_width
|
||||
_manifest_read
|
||||
_manifest_title
|
||||
_manifest_width
|
||||
_native_init
|
||||
_native_run_loop
|
||||
_scroll
|
||||
_style_button_primary
|
||||
_style_label_body
|
||||
_style_label_heading
|
||||
_style_label_muted
|
||||
_style_surface
|
||||
_text_area
|
||||
_text_field
|
||||
_vstack
|
||||
_vstack_tight
|
||||
_widget_add_child
|
||||
_widget_destroy
|
||||
_widget_get_text
|
||||
_widget_on_change
|
||||
_widget_on_click
|
||||
_widget_on_submit
|
||||
_widget_remove_child
|
||||
_widget_set_bg_color
|
||||
_widget_set_bg_color_hex
|
||||
_widget_set_color
|
||||
_widget_set_color_hex
|
||||
_widget_set_corner_radius
|
||||
_widget_set_disabled
|
||||
_widget_set_flex
|
||||
_widget_set_font
|
||||
_widget_set_height
|
||||
_widget_set_hidden
|
||||
_widget_set_padding
|
||||
_widget_set_padding_all
|
||||
_widget_set_padding_xy
|
||||
_widget_set_text
|
||||
_widget_set_width
|
||||
_window_create
|
||||
_window_from_manifest
|
||||
_window_set_title
|
||||
_window_show
|
||||
_zstack
|
||||
Binary file not shown.
@@ -0,0 +1,459 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t native_init(void);
|
||||
el_val_t native_run_loop(void);
|
||||
el_val_t manifest_read(el_val_t path);
|
||||
el_val_t manifest_title(el_val_t m);
|
||||
el_val_t manifest_width(el_val_t m);
|
||||
el_val_t manifest_height(el_val_t m);
|
||||
el_val_t manifest_min_width(el_val_t m);
|
||||
el_val_t manifest_min_height(el_val_t m);
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height);
|
||||
el_val_t window_from_manifest(el_val_t manifest_path);
|
||||
el_val_t window_show(el_val_t handle);
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title);
|
||||
el_val_t vstack(el_val_t spacing);
|
||||
el_val_t vstack_tight(void);
|
||||
el_val_t hstack(el_val_t spacing);
|
||||
el_val_t zstack(void);
|
||||
el_val_t scroll(void);
|
||||
el_val_t label(el_val_t text);
|
||||
el_val_t button(el_val_t label);
|
||||
el_val_t text_field(el_val_t placeholder);
|
||||
el_val_t text_area(el_val_t placeholder);
|
||||
el_val_t image(el_val_t path_or_name);
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t widget_get_text(el_val_t handle);
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold);
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left);
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p);
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py);
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width);
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height);
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_destroy(el_val_t handle);
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset);
|
||||
el_val_t hex_nibble(el_val_t c);
|
||||
el_val_t color_hex_r(el_val_t hex);
|
||||
el_val_t color_hex_g(el_val_t hex);
|
||||
el_val_t color_hex_b(el_val_t hex);
|
||||
el_val_t color_hex_a(el_val_t hex);
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t style_surface(el_val_t handle);
|
||||
el_val_t style_button_primary(el_val_t handle);
|
||||
el_val_t style_label_body(el_val_t handle);
|
||||
el_val_t style_label_heading(el_val_t handle);
|
||||
el_val_t style_label_muted(el_val_t handle);
|
||||
|
||||
el_val_t TOKEN_PRIMARY;
|
||||
el_val_t TOKEN_ON_PRIMARY;
|
||||
el_val_t TOKEN_BACKGROUND;
|
||||
el_val_t TOKEN_ON_BG;
|
||||
el_val_t TOKEN_SURFACE;
|
||||
el_val_t TOKEN_ON_SURFACE;
|
||||
el_val_t TOKEN_OUTLINE;
|
||||
el_val_t TOKEN_ERROR;
|
||||
|
||||
el_val_t native_init(void) {
|
||||
__native_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t native_run_loop(void) {
|
||||
__native_run_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_read(el_val_t path) {
|
||||
return __manifest_read(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_title(el_val_t m) {
|
||||
return json_get_string(m, EL_STR("title"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_width(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("width"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_height(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("height"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_min_width(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("min_width"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_min_height(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("min_height"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) {
|
||||
return __window_create(title, width, height, min_width, min_height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_from_manifest(el_val_t manifest_path) {
|
||||
el_val_t m = manifest_read(manifest_path);
|
||||
el_val_t title = manifest_title(m);
|
||||
el_val_t w = manifest_width(m);
|
||||
el_val_t h = manifest_height(m);
|
||||
el_val_t mw = manifest_min_width(m);
|
||||
el_val_t mh = manifest_min_height(m);
|
||||
el_val_t safe_w = ({ el_val_t _if_result_1 = 0; if ((w > 0)) { _if_result_1 = (w); } else { _if_result_1 = (1200); } _if_result_1; });
|
||||
el_val_t safe_h = ({ el_val_t _if_result_2 = 0; if ((h > 0)) { _if_result_2 = (h); } else { _if_result_2 = (800); } _if_result_2; });
|
||||
el_val_t safe_mw = ({ el_val_t _if_result_3 = 0; if ((mw > 0)) { _if_result_3 = (mw); } else { _if_result_3 = (600); } _if_result_3; });
|
||||
el_val_t safe_mh = ({ el_val_t _if_result_4 = 0; if ((mh > 0)) { _if_result_4 = (mh); } else { _if_result_4 = (400); } _if_result_4; });
|
||||
el_val_t safe_t = ({ el_val_t _if_result_5 = 0; if ((str_len(title) > 0)) { _if_result_5 = (title); } else { _if_result_5 = (EL_STR("App")); } _if_result_5; });
|
||||
return window_create(safe_t, safe_w, safe_h, safe_mw, safe_mh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_show(el_val_t handle) {
|
||||
__window_show(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title) {
|
||||
__window_set_title(handle, title);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t vstack(el_val_t spacing) {
|
||||
return __vstack_create(spacing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t vstack_tight(void) {
|
||||
return __vstack_create(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hstack(el_val_t spacing) {
|
||||
return __hstack_create(spacing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t zstack(void) {
|
||||
return __zstack_create();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t scroll(void) {
|
||||
return __scroll_create();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t label(el_val_t text) {
|
||||
return __label_create(text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t button(el_val_t label) {
|
||||
return __button_create(label);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t text_field(el_val_t placeholder) {
|
||||
return __text_field_create(placeholder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t text_area(el_val_t placeholder) {
|
||||
return __text_area_create(placeholder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t image(el_val_t path_or_name) {
|
||||
return __image_create(path_or_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text) {
|
||||
__widget_set_text(handle, text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_get_text(el_val_t handle) {
|
||||
return __widget_get_text(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
|
||||
__widget_set_color(handle, r, g, b, a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
|
||||
__widget_set_bg_color(handle, r, g, b, a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold) {
|
||||
el_val_t bold_int = ({ el_val_t _if_result_6 = 0; if (bold) { _if_result_6 = (1); } else { _if_result_6 = (0); } _if_result_6; });
|
||||
__widget_set_font(handle, family, size, bold_int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) {
|
||||
__widget_set_padding(handle, top, right, bottom, left);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p) {
|
||||
__widget_set_padding(handle, p, p, p, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py) {
|
||||
__widget_set_padding(handle, py, px, py, px);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width) {
|
||||
__widget_set_width(handle, width);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height) {
|
||||
__widget_set_height(handle, height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex) {
|
||||
__widget_set_flex(handle, flex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius) {
|
||||
__widget_set_corner_radius(handle, radius);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled) {
|
||||
el_val_t d = ({ el_val_t _if_result_7 = 0; if (disabled) { _if_result_7 = (1); } else { _if_result_7 = (0); } _if_result_7; });
|
||||
__widget_set_disabled(handle, d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden) {
|
||||
el_val_t h = ({ el_val_t _if_result_8 = 0; if (hidden) { _if_result_8 = (1); } else { _if_result_8 = (0); } _if_result_8; });
|
||||
__widget_set_hidden(handle, h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child) {
|
||||
__widget_add_child(parent, child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child) {
|
||||
__widget_remove_child(parent, child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_destroy(el_val_t handle) {
|
||||
__widget_destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_click(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_change(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_submit(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset) {
|
||||
el_val_t hi = str_slice(s, offset, (offset + 1));
|
||||
el_val_t lo = str_slice(s, (offset + 1), (offset + 2));
|
||||
el_val_t h = hex_nibble(hi);
|
||||
el_val_t l = hex_nibble(lo);
|
||||
return ((h * 16) + l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hex_nibble(el_val_t c) {
|
||||
if (str_eq(c, EL_STR("0"))) {
|
||||
return 0;
|
||||
}
|
||||
if (str_eq(c, EL_STR("1"))) {
|
||||
return 1;
|
||||
}
|
||||
if (str_eq(c, EL_STR("2"))) {
|
||||
return 2;
|
||||
}
|
||||
if (str_eq(c, EL_STR("3"))) {
|
||||
return 3;
|
||||
}
|
||||
if (str_eq(c, EL_STR("4"))) {
|
||||
return 4;
|
||||
}
|
||||
if (str_eq(c, EL_STR("5"))) {
|
||||
return 5;
|
||||
}
|
||||
if (str_eq(c, EL_STR("6"))) {
|
||||
return 6;
|
||||
}
|
||||
if (str_eq(c, EL_STR("7"))) {
|
||||
return 7;
|
||||
}
|
||||
if (str_eq(c, EL_STR("8"))) {
|
||||
return 8;
|
||||
}
|
||||
if (str_eq(c, EL_STR("9"))) {
|
||||
return 9;
|
||||
}
|
||||
if (str_eq(c, EL_STR("a"))) {
|
||||
return 10;
|
||||
}
|
||||
if (str_eq(c, EL_STR("b"))) {
|
||||
return 11;
|
||||
}
|
||||
if (str_eq(c, EL_STR("c"))) {
|
||||
return 12;
|
||||
}
|
||||
if (str_eq(c, EL_STR("d"))) {
|
||||
return 13;
|
||||
}
|
||||
if (str_eq(c, EL_STR("e"))) {
|
||||
return 14;
|
||||
}
|
||||
if (str_eq(c, EL_STR("f"))) {
|
||||
return 15;
|
||||
}
|
||||
if (str_eq(c, EL_STR("A"))) {
|
||||
return 10;
|
||||
}
|
||||
if (str_eq(c, EL_STR("B"))) {
|
||||
return 11;
|
||||
}
|
||||
if (str_eq(c, EL_STR("C"))) {
|
||||
return 12;
|
||||
}
|
||||
if (str_eq(c, EL_STR("D"))) {
|
||||
return 13;
|
||||
}
|
||||
if (str_eq(c, EL_STR("E"))) {
|
||||
return 14;
|
||||
}
|
||||
if (str_eq(c, EL_STR("F"))) {
|
||||
return 15;
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_r(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_9 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_9 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_9 = (hex); } _if_result_9; });
|
||||
el_val_t v = hex_channel(s, 0);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_g(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_10 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_10 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_10 = (hex); } _if_result_10; });
|
||||
el_val_t v = hex_channel(s, 2);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_b(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_11 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_11 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_11 = (hex); } _if_result_11; });
|
||||
el_val_t v = hex_channel(s, 4);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_a(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_12 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_12 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_12 = (hex); } _if_result_12; });
|
||||
if (str_len(s) < 8) {
|
||||
return el_from_float(1.0);
|
||||
}
|
||||
el_val_t v = hex_channel(s, 6);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex) {
|
||||
widget_set_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex) {
|
||||
widget_set_bg_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_surface(el_val_t handle) {
|
||||
widget_set_bg_color_hex(handle, TOKEN_SURFACE);
|
||||
widget_set_corner_radius(handle, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_button_primary(el_val_t handle) {
|
||||
widget_set_bg_color_hex(handle, TOKEN_PRIMARY);
|
||||
widget_set_color_hex(handle, TOKEN_ON_PRIMARY);
|
||||
widget_set_corner_radius(handle, 8);
|
||||
widget_set_padding_xy(handle, 16, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_body(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_ON_BG);
|
||||
widget_set_font(handle, EL_STR("system"), 14, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_heading(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_ON_BG);
|
||||
widget_set_font(handle, EL_STR("system"), 20, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_muted(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_OUTLINE);
|
||||
widget_set_font(handle, EL_STR("system"), 12, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int _argc, char** _argv) {
|
||||
el_runtime_init_args(_argc, _argv);
|
||||
TOKEN_PRIMARY = EL_STR("#60a5fa");
|
||||
TOKEN_ON_PRIMARY = EL_STR("#0f172a");
|
||||
TOKEN_BACKGROUND = EL_STR("#0f172a");
|
||||
TOKEN_ON_BG = EL_STR("#f8fafc");
|
||||
TOKEN_SURFACE = EL_STR("#1e293b");
|
||||
TOKEN_ON_SURFACE = EL_STR("#f8fafc");
|
||||
TOKEN_OUTLINE = EL_STR("#475569");
|
||||
TOKEN_ERROR = EL_STR("#f87171");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t native_init(void);
|
||||
el_val_t native_run_loop(void);
|
||||
el_val_t manifest_read(el_val_t path);
|
||||
el_val_t manifest_title(el_val_t m);
|
||||
el_val_t manifest_width(el_val_t m);
|
||||
el_val_t manifest_height(el_val_t m);
|
||||
el_val_t manifest_min_width(el_val_t m);
|
||||
el_val_t manifest_min_height(el_val_t m);
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height);
|
||||
el_val_t window_from_manifest(el_val_t manifest_path);
|
||||
el_val_t window_show(el_val_t handle);
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title);
|
||||
el_val_t vstack(el_val_t spacing);
|
||||
el_val_t vstack_tight(void);
|
||||
el_val_t hstack(el_val_t spacing);
|
||||
el_val_t zstack(void);
|
||||
el_val_t scroll(void);
|
||||
el_val_t label(el_val_t text);
|
||||
el_val_t button(el_val_t label);
|
||||
el_val_t text_field(el_val_t placeholder);
|
||||
el_val_t text_area(el_val_t placeholder);
|
||||
el_val_t image(el_val_t path_or_name);
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t widget_get_text(el_val_t handle);
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold);
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left);
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p);
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py);
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width);
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height);
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_destroy(el_val_t handle);
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset);
|
||||
el_val_t hex_nibble(el_val_t c);
|
||||
el_val_t color_hex_r(el_val_t hex);
|
||||
el_val_t color_hex_g(el_val_t hex);
|
||||
el_val_t color_hex_b(el_val_t hex);
|
||||
el_val_t color_hex_a(el_val_t hex);
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t style_surface(el_val_t handle);
|
||||
el_val_t style_button_primary(el_val_t handle);
|
||||
el_val_t style_label_body(el_val_t handle);
|
||||
el_val_t style_label_heading(el_val_t handle);
|
||||
el_val_t style_label_muted(el_val_t handle);
|
||||
|
||||
el_val_t TOKEN_PRIMARY;
|
||||
el_val_t TOKEN_ON_PRIMARY;
|
||||
el_val_t TOKEN_BACKGROUND;
|
||||
el_val_t TOKEN_ON_BG;
|
||||
el_val_t TOKEN_SURFACE;
|
||||
el_val_t TOKEN_ON_SURFACE;
|
||||
el_val_t TOKEN_OUTLINE;
|
||||
el_val_t TOKEN_ERROR;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,123 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t on_greet_click(el_val_t widget, el_val_t data);
|
||||
el_val_t on_counter_click(el_val_t widget, el_val_t data);
|
||||
el_val_t on_name_change(el_val_t widget, el_val_t data);
|
||||
el_val_t app_build(el_val_t window);
|
||||
|
||||
el_val_t g_window;
|
||||
el_val_t g_label;
|
||||
el_val_t g_input;
|
||||
el_val_t g_button;
|
||||
el_val_t g_counter;
|
||||
el_val_t g_counter_lbl;
|
||||
el_val_t manifest_env;
|
||||
el_val_t manifest_path;
|
||||
el_val_t win;
|
||||
el_val_t g_window;
|
||||
|
||||
el_val_t on_greet_click(el_val_t widget, el_val_t data) {
|
||||
el_val_t name = widget_get_text(g_input);
|
||||
el_val_t greeting = ({ el_val_t _if_result_1 = 0; if ((str_len(name) > 0)) { _if_result_1 = (el_str_concat(el_str_concat(EL_STR("Hello, "), name), EL_STR("!"))); } else { _if_result_1 = (EL_STR("Hello, World!")); } _if_result_1; });
|
||||
widget_set_text(g_label, greeting);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t on_counter_click(el_val_t widget, el_val_t data) {
|
||||
el_val_t g_counter = (g_counter + 1);
|
||||
widget_set_text(g_counter_lbl, el_str_concat(EL_STR("Clicks: "), int_to_str(g_counter)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t on_name_change(el_val_t widget, el_val_t data) {
|
||||
el_val_t has_text = (str_len(data) > 0);
|
||||
widget_set_disabled(g_button, !has_text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t app_build(el_val_t window) {
|
||||
el_val_t root = vstack(0);
|
||||
widget_set_padding_all(root, 24);
|
||||
widget_set_bg_color_hex(root, EL_STR("#0f172a"));
|
||||
widget_set_flex(root, 1);
|
||||
el_val_t title = label(EL_STR("el-native \xe2\x80\x94 native widget demo"));
|
||||
style_label_heading(title);
|
||||
widget_add_child(root, title);
|
||||
el_val_t gap1 = label(EL_STR(""));
|
||||
widget_set_height(gap1, 16);
|
||||
widget_add_child(root, gap1);
|
||||
el_val_t subtitle = label(EL_STR("AppKit controls from el code, no ObjC in the app layer."));
|
||||
style_label_muted(subtitle);
|
||||
widget_add_child(root, subtitle);
|
||||
el_val_t gap2 = label(EL_STR(""));
|
||||
widget_set_height(gap2, 24);
|
||||
widget_add_child(root, gap2);
|
||||
el_val_t input_row = hstack(8);
|
||||
el_val_t name_label = label(EL_STR("Name:"));
|
||||
style_label_body(name_label);
|
||||
widget_set_width(name_label, 60);
|
||||
el_val_t input = text_field(EL_STR("Enter your name\xe2\x80\xa6"));
|
||||
style_label_body(input);
|
||||
widget_set_flex(input, 1);
|
||||
widget_on_change(input, EL_STR("on_name_change"));
|
||||
el_val_t g_input = input;
|
||||
el_val_t greet_btn = button(EL_STR("Greet"));
|
||||
style_button_primary(greet_btn);
|
||||
widget_set_disabled(greet_btn, 1);
|
||||
widget_on_click(greet_btn, EL_STR("on_greet_click"));
|
||||
el_val_t g_button = greet_btn;
|
||||
widget_add_child(input_row, name_label);
|
||||
widget_add_child(input_row, input);
|
||||
widget_add_child(input_row, greet_btn);
|
||||
widget_add_child(root, input_row);
|
||||
el_val_t gap3 = label(EL_STR(""));
|
||||
widget_set_height(gap3, 12);
|
||||
widget_add_child(root, gap3);
|
||||
el_val_t greeting = label(EL_STR("Waiting for name\xe2\x80\xa6"));
|
||||
style_label_body(greeting);
|
||||
widget_set_color_hex(greeting, EL_STR("#60a5fa"));
|
||||
widget_set_font(greeting, EL_STR("system"), 16, 1);
|
||||
el_val_t g_label = greeting;
|
||||
widget_add_child(root, greeting);
|
||||
el_val_t gap4 = label(EL_STR(""));
|
||||
widget_set_height(gap4, 24);
|
||||
widget_add_child(root, gap4);
|
||||
el_val_t counter_row = hstack(12);
|
||||
el_val_t counter_lbl = label(EL_STR("Clicks: 0"));
|
||||
style_label_body(counter_lbl);
|
||||
el_val_t g_counter_lbl = counter_lbl;
|
||||
el_val_t counter_btn = button(EL_STR("Click me"));
|
||||
style_button_primary(counter_btn);
|
||||
widget_on_click(counter_btn, EL_STR("on_counter_click"));
|
||||
widget_add_child(counter_row, counter_lbl);
|
||||
widget_add_child(counter_row, counter_btn);
|
||||
widget_add_child(root, counter_row);
|
||||
widget_add_child(window, root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int _argc, char** _argv) {
|
||||
el_runtime_init_args(_argc, _argv);
|
||||
g_window = (-1);
|
||||
g_label = (-1);
|
||||
g_input = (-1);
|
||||
g_button = (-1);
|
||||
g_counter = 0;
|
||||
g_counter_lbl = (-1);
|
||||
native_init();
|
||||
manifest_env = env(EL_STR("EL_MANIFEST"));
|
||||
manifest_path = ({ el_val_t _if_result_2 = 0; if ((str_len(manifest_env) > 0)) { _if_result_2 = (manifest_env); } else { _if_result_2 = (EL_STR("manifest.el")); } _if_result_2; });
|
||||
win = window_from_manifest(manifest_path);
|
||||
g_window = win;
|
||||
if (win < 0) {
|
||||
println(EL_STR("Error: failed to create window. Is EL_TARGET_MACOS defined?"));
|
||||
exit_program(1);
|
||||
}
|
||||
app_build(win);
|
||||
window_show(win);
|
||||
native_run_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Binary file not shown.
Executable
+169
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
# build-sdl2.sh — Compile and run native-hello on macOS using the SDL2 backend.
|
||||
#
|
||||
# Usage:
|
||||
# ./build-sdl2.sh # compile and run
|
||||
# ./build-sdl2.sh compile # compile only
|
||||
# ./build-sdl2.sh clean # remove build artefacts
|
||||
#
|
||||
# Requirements:
|
||||
# - elc in PATH (or ../../../lang/dist/platform/elc)
|
||||
# - SDL2, SDL2_ttf, SDL2_image via Homebrew (brew install sdl2 sdl2_ttf sdl2_image)
|
||||
# - clang (Xcode Command Line Tools)
|
||||
# - macOS 12+
|
||||
#
|
||||
# This is the SDL2 counterpart to build.sh (AppKit). The pipeline is identical;
|
||||
# only the platform bridge object and compile flags differ:
|
||||
# - el_sdl2.c instead of el_appkit.m
|
||||
# - -DEL_TARGET_SDL2 instead of -DEL_TARGET_MACOS
|
||||
# - sdl2-config --libs -lSDL2_ttf -lSDL2_image -ldl instead of -framework Cocoa
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
EL_LANG_ROOT="${SCRIPT_DIR}/../../../lang"
|
||||
EL_UI_ROOT="${SCRIPT_DIR}/../.."
|
||||
EL_RUNTIME="${EL_LANG_ROOT}/el-compiler/runtime"
|
||||
EL_NATIVE_VESSEL="${EL_UI_ROOT}/vessels/el-native/src/main.el"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build"
|
||||
|
||||
# Locate elc
|
||||
if command -v elc &>/dev/null; then
|
||||
ELC="elc"
|
||||
elif [ -x "${EL_LANG_ROOT}/dist/platform/elc" ]; then
|
||||
ELC="${EL_LANG_ROOT}/dist/platform/elc"
|
||||
else
|
||||
echo "Error: elc not found. Add it to PATH or place it at:"
|
||||
echo " ${EL_LANG_ROOT}/dist/platform/elc"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify SDL2 is available
|
||||
if ! command -v sdl2-config &>/dev/null; then
|
||||
echo "Error: sdl2-config not found. Install SDL2 via:"
|
||||
echo " brew install sdl2 sdl2_ttf sdl2_image"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SDL2_CFLAGS=$(sdl2-config --cflags)
|
||||
SDL2_LIBS=$(sdl2-config --libs)
|
||||
|
||||
CLANG_FLAGS_COMMON=(
|
||||
-std=c11
|
||||
-DEL_TARGET_SDL2
|
||||
-I "${EL_RUNTIME}"
|
||||
)
|
||||
|
||||
clean() {
|
||||
rm -rf "${BUILD_DIR}"
|
||||
echo "Cleaned."
|
||||
}
|
||||
|
||||
compile() {
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
|
||||
# ── 1. Vessel forward-declarations header ─────────────────────────────────
|
||||
echo "==> Generating el-native vessel header (declarations only)..."
|
||||
"${ELC}" --emit-header "${EL_NATIVE_VESSEL}" \
|
||||
| awk 'BEGIN{in_body=0} /^[a-zA-Z_].+\)[[:space:]]*\{/ {in_body=1} !in_body{print}' \
|
||||
> "${BUILD_DIR}/el_native_vessel.h"
|
||||
|
||||
# ── 2. Compile el-native vessel to C ──────────────────────────────────────
|
||||
echo "==> Compiling el-native vessel to C..."
|
||||
"${ELC}" "${EL_NATIVE_VESSEL}" \
|
||||
> "${BUILD_DIR}/el_native_vessel.c"
|
||||
|
||||
# ── 3. Compile app entry to C ─────────────────────────────────────────────
|
||||
echo "==> Compiling app entry to C..."
|
||||
EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \
|
||||
"${ELC}" "${SCRIPT_DIR}/src/main.el" \
|
||||
> "${BUILD_DIR}/native_hello.c"
|
||||
|
||||
# ── 4. Compile el_sdl2.c (SDL2 bridge — pure C) ───────────────────────────
|
||||
# sdl2-config --cflags gives -I/opt/homebrew/include/SDL2 which handles
|
||||
# #include <SDL.h> but el_sdl2.c uses #include <SDL2/SDL.h> — also add
|
||||
# the parent directory so both forms resolve.
|
||||
echo "==> Compiling el_sdl2.c (SDL2 bridge)..."
|
||||
clang \
|
||||
-std=c11 \
|
||||
-DEL_TARGET_SDL2 \
|
||||
${SDL2_CFLAGS} \
|
||||
-I/opt/homebrew/include \
|
||||
-I "${EL_RUNTIME}" \
|
||||
-c "${EL_RUNTIME}/el_sdl2.c" \
|
||||
-o "${BUILD_DIR}/el_sdl2.o"
|
||||
|
||||
# ── 5a. Compile el_seed.c ─────────────────────────────────────────────────
|
||||
echo "==> Compiling el_seed.c..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-c "${EL_RUNTIME}/el_seed.c" \
|
||||
-o "${BUILD_DIR}/el_seed.o"
|
||||
|
||||
# ── 5b. Compile el_runtime.c and patch out duplicate __-prefixed symbols ──
|
||||
echo "==> Compiling el_runtime.c..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-c "${EL_RUNTIME}/el_runtime.c" \
|
||||
-o "${BUILD_DIR}/el_runtime.o"
|
||||
|
||||
echo "==> Patching el_runtime.o (hiding __-prefixed symbols duplicated in el_seed.o)..."
|
||||
nm "${BUILD_DIR}/el_seed.o" 2>/dev/null \
|
||||
| awk '/^[0-9a-f]+ T _/{print $3}' | sort > "${BUILD_DIR}/.seed_T.txt"
|
||||
nm "${BUILD_DIR}/el_runtime.o" 2>/dev/null \
|
||||
| awk '/^[0-9a-f]+ T _/{print $3}' | sort > "${BUILD_DIR}/.rt_T.txt"
|
||||
comm -23 "${BUILD_DIR}/.rt_T.txt" "${BUILD_DIR}/.seed_T.txt" \
|
||||
> "${BUILD_DIR}/.rt_keep.txt"
|
||||
nmedit -s "${BUILD_DIR}/.rt_keep.txt" "${BUILD_DIR}/el_runtime.o"
|
||||
|
||||
# ── 6. Compile el-native vessel object ────────────────────────────────────
|
||||
echo "==> Compiling el-native vessel object..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-include "${EL_RUNTIME}/el_native_target.h" \
|
||||
-c "${BUILD_DIR}/el_native_vessel.c" \
|
||||
-o "${BUILD_DIR}/el_native_vessel.o"
|
||||
|
||||
echo "==> Patching el_native_vessel.o (hiding vessel main)..."
|
||||
nm "${BUILD_DIR}/el_native_vessel.o" 2>/dev/null \
|
||||
| awk '/^[0-9a-f]+ T _/{print $3}' \
|
||||
| grep -v '^_main$' \
|
||||
> "${BUILD_DIR}/.vessel_keep.txt"
|
||||
nmedit -s "${BUILD_DIR}/.vessel_keep.txt" "${BUILD_DIR}/el_native_vessel.o"
|
||||
|
||||
# ── 7. Compile app object ─────────────────────────────────────────────────
|
||||
echo "==> Compiling app object..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-include "${EL_RUNTIME}/el_native_target.h" \
|
||||
-include "${BUILD_DIR}/el_native_vessel.h" \
|
||||
-c "${BUILD_DIR}/native_hello.c" \
|
||||
-o "${BUILD_DIR}/native_hello.o"
|
||||
|
||||
# ── 8. Link ───────────────────────────────────────────────────────────────
|
||||
echo "==> Linking native-hello-sdl2..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
${SDL2_LIBS} \
|
||||
-lSDL2_ttf \
|
||||
-lSDL2_image \
|
||||
-lcurl \
|
||||
-ldl \
|
||||
-lpthread \
|
||||
"${BUILD_DIR}/native_hello.o" \
|
||||
"${BUILD_DIR}/el_native_vessel.o" \
|
||||
"${BUILD_DIR}/el_sdl2.o" \
|
||||
"${BUILD_DIR}/el_runtime.o" \
|
||||
"${BUILD_DIR}/el_seed.o" \
|
||||
-o "${BUILD_DIR}/native-hello-sdl2"
|
||||
|
||||
echo "==> Build complete: ${BUILD_DIR}/native-hello-sdl2"
|
||||
}
|
||||
|
||||
run() {
|
||||
compile
|
||||
echo "==> Launching native-hello-sdl2..."
|
||||
EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \
|
||||
"${BUILD_DIR}/native-hello-sdl2"
|
||||
}
|
||||
|
||||
case "${1:-run}" in
|
||||
clean) clean ;;
|
||||
compile) compile ;;
|
||||
run|*) run ;;
|
||||
esac
|
||||
Executable
+135
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env bash
|
||||
# build-win32.sh — Cross-compile native-hello for Windows using mingw64.
|
||||
#
|
||||
# Requirements:
|
||||
# - x86_64-w64-mingw32-gcc (brew install mingw-w64)
|
||||
# - elc in PATH or at ../../../lang/dist/platform/elc
|
||||
#
|
||||
# Output: build-win32/native-hello.exe
|
||||
#
|
||||
# Note: We use el_runtime_win32.c instead of el_runtime.c / el_seed.c because
|
||||
# those files have deep POSIX dependencies (pthread, curl, dlfcn, sys/socket,
|
||||
# etc.) that cannot be cross-compiled to Windows. el_runtime_win32.c provides
|
||||
# all the runtime symbols that native-hello and the el-native vessel actually
|
||||
# call, using only standard C11 + Win32 APIs.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
EL_LANG_ROOT="${SCRIPT_DIR}/../../../lang"
|
||||
EL_UI_ROOT="${SCRIPT_DIR}/../.."
|
||||
EL_RUNTIME="${EL_LANG_ROOT}/el-compiler/runtime"
|
||||
EL_NATIVE_VESSEL="${EL_UI_ROOT}/vessels/el-native/src/main.el"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build-win32"
|
||||
|
||||
# ── Locate elc ───────────────────────────────────────────────────────────────
|
||||
if command -v elc &>/dev/null; then
|
||||
ELC="elc"
|
||||
elif [ -x "${EL_LANG_ROOT}/dist/platform/elc" ]; then
|
||||
ELC="${EL_LANG_ROOT}/dist/platform/elc"
|
||||
else
|
||||
echo "Error: elc not found. Add it to PATH or place it at:"
|
||||
echo " ${EL_LANG_ROOT}/dist/platform/elc"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CC="x86_64-w64-mingw32-gcc"
|
||||
|
||||
# -D_WIN32_IE for SetWindowSubclass (comctl32 >= IE 0x0600)
|
||||
CFLAGS=(
|
||||
-std=c11
|
||||
-DEL_TARGET_WIN32
|
||||
-D_WIN32_IE=0x0600
|
||||
-I"${EL_RUNTIME}"
|
||||
-Wall
|
||||
-Wno-unused-function
|
||||
)
|
||||
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
|
||||
# ── 1. Compile el-native vessel to C ─────────────────────────────────────────
|
||||
echo "==> Compiling el-native vessel to C..."
|
||||
"${ELC}" "${EL_NATIVE_VESSEL}" \
|
||||
> "${BUILD_DIR}/el_native_vessel.c"
|
||||
|
||||
# ── 2. Generate vessel header (declarations only) ────────────────────────────
|
||||
# elc --emit-header includes function bodies AND bare global variable definitions.
|
||||
# We extract only forward declarations with two awk passes:
|
||||
# Pass 1: stop before function bodies (same as macOS build.sh).
|
||||
# Pass 2: convert bare global variable definitions (^el_val_t NAME;) to
|
||||
# `extern el_val_t NAME;` so the app TU sees them as declarations,
|
||||
# not definitions — avoiding duplicate symbol errors at link time.
|
||||
echo "==> Generating el-native vessel header (declarations only)..."
|
||||
"${ELC}" --emit-header "${EL_NATIVE_VESSEL}" \
|
||||
| awk 'BEGIN{in_body=0} /^[a-zA-Z_].+\)[[:space:]]*\{/ {in_body=1} !in_body{print}' \
|
||||
| awk '/^el_val_t [A-Za-z_][A-Za-z0-9_]*;/{print "extern " $0; next} {print}' \
|
||||
> "${BUILD_DIR}/el_native_vessel.h"
|
||||
|
||||
# ── 3. Compile app entry to C ─────────────────────────────────────────────────
|
||||
echo "==> Compiling app entry to C..."
|
||||
EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \
|
||||
"${ELC}" "${SCRIPT_DIR}/src/main.el" \
|
||||
> "${BUILD_DIR}/native_hello.c"
|
||||
|
||||
# ── 4. Compile el_win32.c ─────────────────────────────────────────────────────
|
||||
echo "==> Compiling el_win32.c (Win32 widget bridge)..."
|
||||
${CC} "${CFLAGS[@]}" \
|
||||
-c "${EL_RUNTIME}/el_win32.c" \
|
||||
-o "${BUILD_DIR}/el_win32.o"
|
||||
|
||||
# ── 5. Compile el_runtime_win32.c ─────────────────────────────────────────────
|
||||
# This replaces el_seed.c + el_runtime.c for Win32 targets.
|
||||
echo "==> Compiling el_runtime_win32.c (Win32 runtime stub)..."
|
||||
${CC} "${CFLAGS[@]}" \
|
||||
-c "${EL_RUNTIME}/el_runtime_win32.c" \
|
||||
-o "${BUILD_DIR}/el_runtime_win32.o"
|
||||
|
||||
# ── 6. Compile el-native vessel object ───────────────────────────────────────
|
||||
echo "==> Compiling el-native vessel object..."
|
||||
${CC} "${CFLAGS[@]}" \
|
||||
-include "${EL_RUNTIME}/el_native_target.h" \
|
||||
-c "${BUILD_DIR}/el_native_vessel.c" \
|
||||
-o "${BUILD_DIR}/el_native_vessel.o"
|
||||
|
||||
echo "==> Patching el_native_vessel.o (localising vessel main + shared globals)..."
|
||||
# The vessel's compiled C has its own main(). We localize it so the linker sees
|
||||
# only the app's main(). We also localize any globals (TOKEN_*, etc.) that the
|
||||
# vessel defines but the app also declares — matching the nmedit step in
|
||||
# build.sh (macOS), using objcopy for mingw.
|
||||
VESSEL_LOCAL_SYMS=()
|
||||
# Collect all defined (T and D/B/C) symbols in the vessel object
|
||||
while IFS= read -r sym; do
|
||||
VESSEL_LOCAL_SYMS+=("--localize-symbol=${sym}")
|
||||
done < <(x86_64-w64-mingw32-nm "${BUILD_DIR}/el_native_vessel.o" 2>/dev/null \
|
||||
| awk '/^[0-9a-f]+ [TDBCt] /{print $3}' \
|
||||
| grep -E '^(main|TOKEN_)' || true)
|
||||
if [ ${#VESSEL_LOCAL_SYMS[@]} -gt 0 ]; then
|
||||
x86_64-w64-mingw32-objcopy "${VESSEL_LOCAL_SYMS[@]}" \
|
||||
"${BUILD_DIR}/el_native_vessel.o"
|
||||
fi
|
||||
|
||||
# ── 7. Compile app object ─────────────────────────────────────────────────────
|
||||
echo "==> Compiling app object..."
|
||||
${CC} "${CFLAGS[@]}" \
|
||||
-include "${EL_RUNTIME}/el_native_target.h" \
|
||||
-include "${BUILD_DIR}/el_native_vessel.h" \
|
||||
-c "${BUILD_DIR}/native_hello.c" \
|
||||
-o "${BUILD_DIR}/native_hello.o"
|
||||
|
||||
# ── 8. Link ───────────────────────────────────────────────────────────────────
|
||||
# -Wl,--export-all-symbols: make el callback functions (on_greet_click etc.)
|
||||
# visible to GetProcAddress(GetModuleHandle(NULL), fn_name) — the Win32
|
||||
# equivalent of POSIX dlsym(RTLD_DEFAULT, fn_name).
|
||||
echo "==> Linking native-hello.exe..."
|
||||
${CC} "${CFLAGS[@]}" \
|
||||
-Wl,--export-all-symbols \
|
||||
"${BUILD_DIR}/native_hello.o" \
|
||||
"${BUILD_DIR}/el_native_vessel.o" \
|
||||
"${BUILD_DIR}/el_win32.o" \
|
||||
"${BUILD_DIR}/el_runtime_win32.o" \
|
||||
-lcomctl32 -luser32 -lgdi32 -lkernel32 \
|
||||
-o "${BUILD_DIR}/native-hello.exe"
|
||||
|
||||
echo ""
|
||||
echo "==> Built: ${BUILD_DIR}/native-hello.exe"
|
||||
ls -lh "${BUILD_DIR}/native-hello.exe"
|
||||
@@ -0,0 +1,459 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t native_init(void);
|
||||
el_val_t native_run_loop(void);
|
||||
el_val_t manifest_read(el_val_t path);
|
||||
el_val_t manifest_title(el_val_t m);
|
||||
el_val_t manifest_width(el_val_t m);
|
||||
el_val_t manifest_height(el_val_t m);
|
||||
el_val_t manifest_min_width(el_val_t m);
|
||||
el_val_t manifest_min_height(el_val_t m);
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height);
|
||||
el_val_t window_from_manifest(el_val_t manifest_path);
|
||||
el_val_t window_show(el_val_t handle);
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title);
|
||||
el_val_t vstack(el_val_t spacing);
|
||||
el_val_t vstack_tight(void);
|
||||
el_val_t hstack(el_val_t spacing);
|
||||
el_val_t zstack(void);
|
||||
el_val_t scroll(void);
|
||||
el_val_t label(el_val_t text);
|
||||
el_val_t button(el_val_t label);
|
||||
el_val_t text_field(el_val_t placeholder);
|
||||
el_val_t text_area(el_val_t placeholder);
|
||||
el_val_t image(el_val_t path_or_name);
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t widget_get_text(el_val_t handle);
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold);
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left);
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p);
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py);
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width);
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height);
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_destroy(el_val_t handle);
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset);
|
||||
el_val_t hex_nibble(el_val_t c);
|
||||
el_val_t color_hex_r(el_val_t hex);
|
||||
el_val_t color_hex_g(el_val_t hex);
|
||||
el_val_t color_hex_b(el_val_t hex);
|
||||
el_val_t color_hex_a(el_val_t hex);
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t style_surface(el_val_t handle);
|
||||
el_val_t style_button_primary(el_val_t handle);
|
||||
el_val_t style_label_body(el_val_t handle);
|
||||
el_val_t style_label_heading(el_val_t handle);
|
||||
el_val_t style_label_muted(el_val_t handle);
|
||||
|
||||
el_val_t TOKEN_PRIMARY;
|
||||
el_val_t TOKEN_ON_PRIMARY;
|
||||
el_val_t TOKEN_BACKGROUND;
|
||||
el_val_t TOKEN_ON_BG;
|
||||
el_val_t TOKEN_SURFACE;
|
||||
el_val_t TOKEN_ON_SURFACE;
|
||||
el_val_t TOKEN_OUTLINE;
|
||||
el_val_t TOKEN_ERROR;
|
||||
|
||||
el_val_t native_init(void) {
|
||||
__native_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t native_run_loop(void) {
|
||||
__native_run_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_read(el_val_t path) {
|
||||
return __manifest_read(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_title(el_val_t m) {
|
||||
return json_get_string(m, EL_STR("title"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_width(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("width"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_height(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("height"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_min_width(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("min_width"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_min_height(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("min_height"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) {
|
||||
return __window_create(title, width, height, min_width, min_height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_from_manifest(el_val_t manifest_path) {
|
||||
el_val_t m = manifest_read(manifest_path);
|
||||
el_val_t title = manifest_title(m);
|
||||
el_val_t w = manifest_width(m);
|
||||
el_val_t h = manifest_height(m);
|
||||
el_val_t mw = manifest_min_width(m);
|
||||
el_val_t mh = manifest_min_height(m);
|
||||
el_val_t safe_w = ({ el_val_t _if_result_1 = 0; if ((w > 0)) { _if_result_1 = (w); } else { _if_result_1 = (1200); } _if_result_1; });
|
||||
el_val_t safe_h = ({ el_val_t _if_result_2 = 0; if ((h > 0)) { _if_result_2 = (h); } else { _if_result_2 = (800); } _if_result_2; });
|
||||
el_val_t safe_mw = ({ el_val_t _if_result_3 = 0; if ((mw > 0)) { _if_result_3 = (mw); } else { _if_result_3 = (600); } _if_result_3; });
|
||||
el_val_t safe_mh = ({ el_val_t _if_result_4 = 0; if ((mh > 0)) { _if_result_4 = (mh); } else { _if_result_4 = (400); } _if_result_4; });
|
||||
el_val_t safe_t = ({ el_val_t _if_result_5 = 0; if ((str_len(title) > 0)) { _if_result_5 = (title); } else { _if_result_5 = (EL_STR("App")); } _if_result_5; });
|
||||
return window_create(safe_t, safe_w, safe_h, safe_mw, safe_mh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_show(el_val_t handle) {
|
||||
__window_show(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title) {
|
||||
__window_set_title(handle, title);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t vstack(el_val_t spacing) {
|
||||
return __vstack_create(spacing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t vstack_tight(void) {
|
||||
return __vstack_create(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hstack(el_val_t spacing) {
|
||||
return __hstack_create(spacing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t zstack(void) {
|
||||
return __zstack_create();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t scroll(void) {
|
||||
return __scroll_create();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t label(el_val_t text) {
|
||||
return __label_create(text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t button(el_val_t label) {
|
||||
return __button_create(label);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t text_field(el_val_t placeholder) {
|
||||
return __text_field_create(placeholder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t text_area(el_val_t placeholder) {
|
||||
return __text_area_create(placeholder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t image(el_val_t path_or_name) {
|
||||
return __image_create(path_or_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text) {
|
||||
__widget_set_text(handle, text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_get_text(el_val_t handle) {
|
||||
return __widget_get_text(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
|
||||
__widget_set_color(handle, r, g, b, a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
|
||||
__widget_set_bg_color(handle, r, g, b, a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold) {
|
||||
el_val_t bold_int = ({ el_val_t _if_result_6 = 0; if (bold) { _if_result_6 = (1); } else { _if_result_6 = (0); } _if_result_6; });
|
||||
__widget_set_font(handle, family, size, bold_int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) {
|
||||
__widget_set_padding(handle, top, right, bottom, left);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p) {
|
||||
__widget_set_padding(handle, p, p, p, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py) {
|
||||
__widget_set_padding(handle, py, px, py, px);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width) {
|
||||
__widget_set_width(handle, width);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height) {
|
||||
__widget_set_height(handle, height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex) {
|
||||
__widget_set_flex(handle, flex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius) {
|
||||
__widget_set_corner_radius(handle, radius);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled) {
|
||||
el_val_t d = ({ el_val_t _if_result_7 = 0; if (disabled) { _if_result_7 = (1); } else { _if_result_7 = (0); } _if_result_7; });
|
||||
__widget_set_disabled(handle, d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden) {
|
||||
el_val_t h = ({ el_val_t _if_result_8 = 0; if (hidden) { _if_result_8 = (1); } else { _if_result_8 = (0); } _if_result_8; });
|
||||
__widget_set_hidden(handle, h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child) {
|
||||
__widget_add_child(parent, child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child) {
|
||||
__widget_remove_child(parent, child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_destroy(el_val_t handle) {
|
||||
__widget_destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_click(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_change(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_submit(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset) {
|
||||
el_val_t hi = str_slice(s, offset, (offset + 1));
|
||||
el_val_t lo = str_slice(s, (offset + 1), (offset + 2));
|
||||
el_val_t h = hex_nibble(hi);
|
||||
el_val_t l = hex_nibble(lo);
|
||||
return ((h * 16) + l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hex_nibble(el_val_t c) {
|
||||
if (str_eq(c, EL_STR("0"))) {
|
||||
return 0;
|
||||
}
|
||||
if (str_eq(c, EL_STR("1"))) {
|
||||
return 1;
|
||||
}
|
||||
if (str_eq(c, EL_STR("2"))) {
|
||||
return 2;
|
||||
}
|
||||
if (str_eq(c, EL_STR("3"))) {
|
||||
return 3;
|
||||
}
|
||||
if (str_eq(c, EL_STR("4"))) {
|
||||
return 4;
|
||||
}
|
||||
if (str_eq(c, EL_STR("5"))) {
|
||||
return 5;
|
||||
}
|
||||
if (str_eq(c, EL_STR("6"))) {
|
||||
return 6;
|
||||
}
|
||||
if (str_eq(c, EL_STR("7"))) {
|
||||
return 7;
|
||||
}
|
||||
if (str_eq(c, EL_STR("8"))) {
|
||||
return 8;
|
||||
}
|
||||
if (str_eq(c, EL_STR("9"))) {
|
||||
return 9;
|
||||
}
|
||||
if (str_eq(c, EL_STR("a"))) {
|
||||
return 10;
|
||||
}
|
||||
if (str_eq(c, EL_STR("b"))) {
|
||||
return 11;
|
||||
}
|
||||
if (str_eq(c, EL_STR("c"))) {
|
||||
return 12;
|
||||
}
|
||||
if (str_eq(c, EL_STR("d"))) {
|
||||
return 13;
|
||||
}
|
||||
if (str_eq(c, EL_STR("e"))) {
|
||||
return 14;
|
||||
}
|
||||
if (str_eq(c, EL_STR("f"))) {
|
||||
return 15;
|
||||
}
|
||||
if (str_eq(c, EL_STR("A"))) {
|
||||
return 10;
|
||||
}
|
||||
if (str_eq(c, EL_STR("B"))) {
|
||||
return 11;
|
||||
}
|
||||
if (str_eq(c, EL_STR("C"))) {
|
||||
return 12;
|
||||
}
|
||||
if (str_eq(c, EL_STR("D"))) {
|
||||
return 13;
|
||||
}
|
||||
if (str_eq(c, EL_STR("E"))) {
|
||||
return 14;
|
||||
}
|
||||
if (str_eq(c, EL_STR("F"))) {
|
||||
return 15;
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_r(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_9 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_9 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_9 = (hex); } _if_result_9; });
|
||||
el_val_t v = hex_channel(s, 0);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_g(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_10 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_10 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_10 = (hex); } _if_result_10; });
|
||||
el_val_t v = hex_channel(s, 2);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_b(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_11 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_11 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_11 = (hex); } _if_result_11; });
|
||||
el_val_t v = hex_channel(s, 4);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_a(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_12 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_12 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_12 = (hex); } _if_result_12; });
|
||||
if (str_len(s) < 8) {
|
||||
return el_from_float(1.0);
|
||||
}
|
||||
el_val_t v = hex_channel(s, 6);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex) {
|
||||
widget_set_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex) {
|
||||
widget_set_bg_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_surface(el_val_t handle) {
|
||||
widget_set_bg_color_hex(handle, TOKEN_SURFACE);
|
||||
widget_set_corner_radius(handle, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_button_primary(el_val_t handle) {
|
||||
widget_set_bg_color_hex(handle, TOKEN_PRIMARY);
|
||||
widget_set_color_hex(handle, TOKEN_ON_PRIMARY);
|
||||
widget_set_corner_radius(handle, 8);
|
||||
widget_set_padding_xy(handle, 16, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_body(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_ON_BG);
|
||||
widget_set_font(handle, EL_STR("system"), 14, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_heading(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_ON_BG);
|
||||
widget_set_font(handle, EL_STR("system"), 20, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_muted(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_OUTLINE);
|
||||
widget_set_font(handle, EL_STR("system"), 12, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int _argc, char** _argv) {
|
||||
el_runtime_init_args(_argc, _argv);
|
||||
TOKEN_PRIMARY = EL_STR("#60a5fa");
|
||||
TOKEN_ON_PRIMARY = EL_STR("#0f172a");
|
||||
TOKEN_BACKGROUND = EL_STR("#0f172a");
|
||||
TOKEN_ON_BG = EL_STR("#f8fafc");
|
||||
TOKEN_SURFACE = EL_STR("#1e293b");
|
||||
TOKEN_ON_SURFACE = EL_STR("#f8fafc");
|
||||
TOKEN_OUTLINE = EL_STR("#475569");
|
||||
TOKEN_ERROR = EL_STR("#f87171");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t native_init(void);
|
||||
el_val_t native_run_loop(void);
|
||||
el_val_t manifest_read(el_val_t path);
|
||||
el_val_t manifest_title(el_val_t m);
|
||||
el_val_t manifest_width(el_val_t m);
|
||||
el_val_t manifest_height(el_val_t m);
|
||||
el_val_t manifest_min_width(el_val_t m);
|
||||
el_val_t manifest_min_height(el_val_t m);
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height);
|
||||
el_val_t window_from_manifest(el_val_t manifest_path);
|
||||
el_val_t window_show(el_val_t handle);
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title);
|
||||
el_val_t vstack(el_val_t spacing);
|
||||
el_val_t vstack_tight(void);
|
||||
el_val_t hstack(el_val_t spacing);
|
||||
el_val_t zstack(void);
|
||||
el_val_t scroll(void);
|
||||
el_val_t label(el_val_t text);
|
||||
el_val_t button(el_val_t label);
|
||||
el_val_t text_field(el_val_t placeholder);
|
||||
el_val_t text_area(el_val_t placeholder);
|
||||
el_val_t image(el_val_t path_or_name);
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t widget_get_text(el_val_t handle);
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold);
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left);
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p);
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py);
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width);
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height);
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_destroy(el_val_t handle);
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset);
|
||||
el_val_t hex_nibble(el_val_t c);
|
||||
el_val_t color_hex_r(el_val_t hex);
|
||||
el_val_t color_hex_g(el_val_t hex);
|
||||
el_val_t color_hex_b(el_val_t hex);
|
||||
el_val_t color_hex_a(el_val_t hex);
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t style_surface(el_val_t handle);
|
||||
el_val_t style_button_primary(el_val_t handle);
|
||||
el_val_t style_label_body(el_val_t handle);
|
||||
el_val_t style_label_heading(el_val_t handle);
|
||||
el_val_t style_label_muted(el_val_t handle);
|
||||
|
||||
extern el_val_t TOKEN_PRIMARY;
|
||||
extern el_val_t TOKEN_ON_PRIMARY;
|
||||
extern el_val_t TOKEN_BACKGROUND;
|
||||
extern el_val_t TOKEN_ON_BG;
|
||||
extern el_val_t TOKEN_SURFACE;
|
||||
extern el_val_t TOKEN_ON_SURFACE;
|
||||
extern el_val_t TOKEN_OUTLINE;
|
||||
extern el_val_t TOKEN_ERROR;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,123 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t on_greet_click(el_val_t widget, el_val_t data);
|
||||
el_val_t on_counter_click(el_val_t widget, el_val_t data);
|
||||
el_val_t on_name_change(el_val_t widget, el_val_t data);
|
||||
el_val_t app_build(el_val_t window);
|
||||
|
||||
el_val_t g_window;
|
||||
el_val_t g_label;
|
||||
el_val_t g_input;
|
||||
el_val_t g_button;
|
||||
el_val_t g_counter;
|
||||
el_val_t g_counter_lbl;
|
||||
el_val_t manifest_env;
|
||||
el_val_t manifest_path;
|
||||
el_val_t win;
|
||||
el_val_t g_window;
|
||||
|
||||
el_val_t on_greet_click(el_val_t widget, el_val_t data) {
|
||||
el_val_t name = widget_get_text(g_input);
|
||||
el_val_t greeting = ({ el_val_t _if_result_1 = 0; if ((str_len(name) > 0)) { _if_result_1 = (el_str_concat(el_str_concat(EL_STR("Hello, "), name), EL_STR("!"))); } else { _if_result_1 = (EL_STR("Hello, World!")); } _if_result_1; });
|
||||
widget_set_text(g_label, greeting);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t on_counter_click(el_val_t widget, el_val_t data) {
|
||||
el_val_t g_counter = (g_counter + 1);
|
||||
widget_set_text(g_counter_lbl, el_str_concat(EL_STR("Clicks: "), int_to_str(g_counter)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t on_name_change(el_val_t widget, el_val_t data) {
|
||||
el_val_t has_text = (str_len(data) > 0);
|
||||
widget_set_disabled(g_button, !has_text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t app_build(el_val_t window) {
|
||||
el_val_t root = vstack(0);
|
||||
widget_set_padding_all(root, 24);
|
||||
widget_set_bg_color_hex(root, EL_STR("#0f172a"));
|
||||
widget_set_flex(root, 1);
|
||||
el_val_t title = label(EL_STR("el-native \xe2\x80\x94 native widget demo"));
|
||||
style_label_heading(title);
|
||||
widget_add_child(root, title);
|
||||
el_val_t gap1 = label(EL_STR(""));
|
||||
widget_set_height(gap1, 16);
|
||||
widget_add_child(root, gap1);
|
||||
el_val_t subtitle = label(EL_STR("AppKit controls from el code, no ObjC in the app layer."));
|
||||
style_label_muted(subtitle);
|
||||
widget_add_child(root, subtitle);
|
||||
el_val_t gap2 = label(EL_STR(""));
|
||||
widget_set_height(gap2, 24);
|
||||
widget_add_child(root, gap2);
|
||||
el_val_t input_row = hstack(8);
|
||||
el_val_t name_label = label(EL_STR("Name:"));
|
||||
style_label_body(name_label);
|
||||
widget_set_width(name_label, 60);
|
||||
el_val_t input = text_field(EL_STR("Enter your name\xe2\x80\xa6"));
|
||||
style_label_body(input);
|
||||
widget_set_flex(input, 1);
|
||||
widget_on_change(input, EL_STR("on_name_change"));
|
||||
el_val_t g_input = input;
|
||||
el_val_t greet_btn = button(EL_STR("Greet"));
|
||||
style_button_primary(greet_btn);
|
||||
widget_set_disabled(greet_btn, 1);
|
||||
widget_on_click(greet_btn, EL_STR("on_greet_click"));
|
||||
el_val_t g_button = greet_btn;
|
||||
widget_add_child(input_row, name_label);
|
||||
widget_add_child(input_row, input);
|
||||
widget_add_child(input_row, greet_btn);
|
||||
widget_add_child(root, input_row);
|
||||
el_val_t gap3 = label(EL_STR(""));
|
||||
widget_set_height(gap3, 12);
|
||||
widget_add_child(root, gap3);
|
||||
el_val_t greeting = label(EL_STR("Waiting for name\xe2\x80\xa6"));
|
||||
style_label_body(greeting);
|
||||
widget_set_color_hex(greeting, EL_STR("#60a5fa"));
|
||||
widget_set_font(greeting, EL_STR("system"), 16, 1);
|
||||
el_val_t g_label = greeting;
|
||||
widget_add_child(root, greeting);
|
||||
el_val_t gap4 = label(EL_STR(""));
|
||||
widget_set_height(gap4, 24);
|
||||
widget_add_child(root, gap4);
|
||||
el_val_t counter_row = hstack(12);
|
||||
el_val_t counter_lbl = label(EL_STR("Clicks: 0"));
|
||||
style_label_body(counter_lbl);
|
||||
el_val_t g_counter_lbl = counter_lbl;
|
||||
el_val_t counter_btn = button(EL_STR("Click me"));
|
||||
style_button_primary(counter_btn);
|
||||
widget_on_click(counter_btn, EL_STR("on_counter_click"));
|
||||
widget_add_child(counter_row, counter_lbl);
|
||||
widget_add_child(counter_row, counter_btn);
|
||||
widget_add_child(root, counter_row);
|
||||
widget_add_child(window, root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int _argc, char** _argv) {
|
||||
el_runtime_init_args(_argc, _argv);
|
||||
g_window = (-1);
|
||||
g_label = (-1);
|
||||
g_input = (-1);
|
||||
g_button = (-1);
|
||||
g_counter = 0;
|
||||
g_counter_lbl = (-1);
|
||||
native_init();
|
||||
manifest_env = env(EL_STR("EL_MANIFEST"));
|
||||
manifest_path = ({ el_val_t _if_result_2 = 0; if ((str_len(manifest_env) > 0)) { _if_result_2 = (manifest_env); } else { _if_result_2 = (EL_STR("manifest.el")); } _if_result_2; });
|
||||
win = window_from_manifest(manifest_path);
|
||||
g_window = win;
|
||||
if (win < 0) {
|
||||
println(EL_STR("Error: failed to create window. Is EL_TARGET_MACOS defined?"));
|
||||
exit_program(1);
|
||||
}
|
||||
app_build(win);
|
||||
window_show(win);
|
||||
native_run_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Binary file not shown.
Executable
+198
@@ -0,0 +1,198 @@
|
||||
#!/usr/bin/env bash
|
||||
# build.sh — Compile and run native-hello on macOS.
|
||||
#
|
||||
# Usage:
|
||||
# ./build.sh # compile and run
|
||||
# ./build.sh compile # compile only
|
||||
# ./build.sh clean # remove build artefacts
|
||||
# ./build.sh platforms # detect available platform bridges on this machine
|
||||
#
|
||||
# Requirements:
|
||||
# - elc in PATH (or ../../../lang/dist/platform/elc)
|
||||
# - clang with Xcode Command Line Tools (for Cocoa framework)
|
||||
# - macOS 12+
|
||||
#
|
||||
# Multi-file compilation strategy:
|
||||
# elc does not resolve cross-directory imports (../../vessels/...) at bundle
|
||||
# time. The el-native vessel is compiled as a separate translation unit.
|
||||
#
|
||||
# el_seed.c and el_runtime.c MUST both be linked:
|
||||
# - el_seed.c = OS-boundary layer (__-prefixed primitives) + thin wrappers
|
||||
# - el_runtime.c = high-level el builtins (str_len, json_get_string, etc.)
|
||||
# They share many __-prefixed symbols. The macOS linker rejects duplicate
|
||||
# definitions, so we use nmedit to hide the el_runtime.c copies of the __
|
||||
# symbols (keeping el_seed.c's versions canonical).
|
||||
#
|
||||
# el_native_target.h is a conflict-free header that adds:
|
||||
# - float_div / float_mul etc. (missing from current el_runtime.h)
|
||||
# - Native widget builtin declarations (#ifdef EL_TARGET_MACOS)
|
||||
# without redefining el_to_float / el_from_float from el_runtime.h.
|
||||
#
|
||||
# Vessel header generation: elc --emit-header includes function BODIES.
|
||||
# We extract only the forward declarations using awk (stop at first body).
|
||||
# NOTE: macOS awk does not support \s — use [[:space:]] instead.
|
||||
#
|
||||
# Vessel main(): the vessel's compiled .c file has its own main(). We use
|
||||
# nmedit to hide it from the vessel object before linking.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
EL_LANG_ROOT="${SCRIPT_DIR}/../../../lang"
|
||||
EL_UI_ROOT="${SCRIPT_DIR}/../.."
|
||||
EL_RUNTIME="${EL_LANG_ROOT}/el-compiler/runtime"
|
||||
EL_NATIVE_VESSEL="${EL_UI_ROOT}/vessels/el-native/src/main.el"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build"
|
||||
|
||||
# Locate elc
|
||||
if command -v elc &>/dev/null; then
|
||||
ELC="elc"
|
||||
elif [ -x "${EL_LANG_ROOT}/dist/platform/elc" ]; then
|
||||
ELC="${EL_LANG_ROOT}/dist/platform/elc"
|
||||
else
|
||||
echo "Error: elc not found. Add it to PATH or place it at:"
|
||||
echo " ${EL_LANG_ROOT}/dist/platform/elc"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CLANG_FLAGS_COMMON=(
|
||||
-std=c11
|
||||
-DEL_TARGET_MACOS
|
||||
-I "${EL_RUNTIME}"
|
||||
)
|
||||
|
||||
clean() {
|
||||
rm -rf "${BUILD_DIR}"
|
||||
echo "Cleaned."
|
||||
}
|
||||
|
||||
compile() {
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
|
||||
# ── 1. Vessel forward-declarations header ─────────────────────────────────
|
||||
# elc --emit-header emits full function bodies, not just prototypes.
|
||||
# Extract only the forward-declaration lines: stop at the first function
|
||||
# definition (a line that ends with ') {' opening a body block).
|
||||
# macOS awk does not support \s — use POSIX [[:space:]] instead.
|
||||
echo "==> Generating el-native vessel header (declarations only)..."
|
||||
"${ELC}" --emit-header "${EL_NATIVE_VESSEL}" \
|
||||
| awk 'BEGIN{in_body=0} /^[a-zA-Z_].+\)[[:space:]]*\{/ {in_body=1} !in_body{print}' \
|
||||
> "${BUILD_DIR}/el_native_vessel.h"
|
||||
|
||||
# ── 2. Compile el-native vessel to C ──────────────────────────────────────
|
||||
echo "==> Compiling el-native vessel to C..."
|
||||
"${ELC}" "${EL_NATIVE_VESSEL}" \
|
||||
> "${BUILD_DIR}/el_native_vessel.c"
|
||||
|
||||
# ── 3. Compile app entry to C ─────────────────────────────────────────────
|
||||
echo "==> Compiling app entry to C..."
|
||||
EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \
|
||||
"${ELC}" "${SCRIPT_DIR}/src/main.el" \
|
||||
> "${BUILD_DIR}/native_hello.c"
|
||||
|
||||
# ── 4. Compile el_appkit.m (ObjC, MRC — not ARC) ─────────────────────────
|
||||
echo "==> Compiling el_appkit.m (AppKit bridge, -fno-objc-arc)..."
|
||||
clang \
|
||||
-std=gnu11 \
|
||||
-ObjC \
|
||||
-fno-objc-arc \
|
||||
-DEL_TARGET_MACOS \
|
||||
-I "${EL_RUNTIME}" \
|
||||
-c "${EL_RUNTIME}/el_appkit.m" \
|
||||
-o "${BUILD_DIR}/el_appkit.o"
|
||||
|
||||
# ── 5a. Compile el_seed.c ─────────────────────────────────────────────────
|
||||
# el_seed.c provides the OS-boundary __-prefixed primitives + thin wrappers
|
||||
# around el_runtime.c's higher-level functions.
|
||||
echo "==> Compiling el_seed.c..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-c "${EL_RUNTIME}/el_seed.c" \
|
||||
-o "${BUILD_DIR}/el_seed.o"
|
||||
|
||||
# ── 5b. Compile el_runtime.c and patch out duplicate __-prefixed symbols ──
|
||||
# el_runtime.c provides the high-level builtins (str_len, json_get_string,
|
||||
# int_to_str, println, env, exit_program, el_runtime_init_args, etc.) that
|
||||
# el_seed.c does NOT provide (they are single-name, no __ prefix).
|
||||
#
|
||||
# el_runtime.c also defines the same __-prefixed OS wrappers as el_seed.c.
|
||||
# We hide those duplicates using nmedit so the linker sees only one copy.
|
||||
echo "==> Compiling el_runtime.c..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-c "${EL_RUNTIME}/el_runtime.c" \
|
||||
-o "${BUILD_DIR}/el_runtime.o"
|
||||
|
||||
echo "==> Patching el_runtime.o (hiding __-prefixed symbols duplicated in el_seed.o)..."
|
||||
# Build the set of symbols that el_seed.o defines and el_runtime.o also
|
||||
# defines. We will hide el_runtime.o's copies, keeping el_seed.o canonical.
|
||||
nm "${BUILD_DIR}/el_seed.o" 2>/dev/null \
|
||||
| awk '/^[0-9a-f]+ T _/{print $3}' | sort > "${BUILD_DIR}/.seed_T.txt"
|
||||
nm "${BUILD_DIR}/el_runtime.o" 2>/dev/null \
|
||||
| awk '/^[0-9a-f]+ T _/{print $3}' | sort > "${BUILD_DIR}/.rt_T.txt"
|
||||
# Symbols to keep public in el_runtime.o = all T symbols MINUS the
|
||||
# duplicates (those will be supplied by el_seed.o).
|
||||
comm -23 "${BUILD_DIR}/.rt_T.txt" "${BUILD_DIR}/.seed_T.txt" \
|
||||
> "${BUILD_DIR}/.rt_keep.txt"
|
||||
nmedit -s "${BUILD_DIR}/.rt_keep.txt" "${BUILD_DIR}/el_runtime.o"
|
||||
|
||||
# ── 6. Compile el-native vessel object ────────────────────────────────────
|
||||
# el_native_target.h adds float_div/mul/etc. + native widget declarations
|
||||
# without conflicting with el_runtime.h.
|
||||
echo "==> Compiling el-native vessel object..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-include "${EL_RUNTIME}/el_native_target.h" \
|
||||
-c "${BUILD_DIR}/el_native_vessel.c" \
|
||||
-o "${BUILD_DIR}/el_native_vessel.o"
|
||||
|
||||
echo "==> Patching el_native_vessel.o (hiding vessel main)..."
|
||||
# The vessel's compiled C has its own main(). Hide it so it does not
|
||||
# conflict with the app's main in native_hello.o.
|
||||
nm "${BUILD_DIR}/el_native_vessel.o" 2>/dev/null \
|
||||
| awk '/^[0-9a-f]+ T _/{print $3}' \
|
||||
| grep -v '^_main$' \
|
||||
> "${BUILD_DIR}/.vessel_keep.txt"
|
||||
nmedit -s "${BUILD_DIR}/.vessel_keep.txt" "${BUILD_DIR}/el_native_vessel.o"
|
||||
|
||||
# ── 7. Compile app object ─────────────────────────────────────────────────
|
||||
# Inject el_native_target.h (float ops + native decls) and the vessel's
|
||||
# forward-declarations header so the vessel functions are resolvable.
|
||||
echo "==> Compiling app object..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-include "${EL_RUNTIME}/el_native_target.h" \
|
||||
-include "${BUILD_DIR}/el_native_vessel.h" \
|
||||
-c "${BUILD_DIR}/native_hello.c" \
|
||||
-o "${BUILD_DIR}/native_hello.o"
|
||||
|
||||
# ── 8. Link ───────────────────────────────────────────────────────────────
|
||||
# el_seed.o = OS-boundary primitives (__-prefixed)
|
||||
# el_runtime.o (patched) = high-level builtins (str_len, json_*, etc.)
|
||||
# el_native_vessel.o (patched) = el-level widget API
|
||||
# el_appkit.o = AppKit C bridge (MRC)
|
||||
echo "==> Linking native-hello..."
|
||||
clang "${CLANG_FLAGS_COMMON[@]}" \
|
||||
-framework Cocoa \
|
||||
-lpthread \
|
||||
-lcurl \
|
||||
-ldl \
|
||||
"${BUILD_DIR}/native_hello.o" \
|
||||
"${BUILD_DIR}/el_native_vessel.o" \
|
||||
"${BUILD_DIR}/el_appkit.o" \
|
||||
"${BUILD_DIR}/el_runtime.o" \
|
||||
"${BUILD_DIR}/el_seed.o" \
|
||||
-o "${BUILD_DIR}/native-hello"
|
||||
|
||||
echo "==> Build complete: ${BUILD_DIR}/native-hello"
|
||||
}
|
||||
|
||||
run() {
|
||||
compile
|
||||
echo "==> Launching native-hello..."
|
||||
EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \
|
||||
"${BUILD_DIR}/native-hello"
|
||||
}
|
||||
|
||||
case "${1:-run}" in
|
||||
clean) clean ;;
|
||||
compile) compile ;;
|
||||
platforms) "${EL_LANG_ROOT}/el-compiler/runtime/detect-platforms" ;;
|
||||
run|*) run ;;
|
||||
esac
|
||||
@@ -0,0 +1,397 @@
|
||||
___args_json
|
||||
___channel_close
|
||||
___channel_new
|
||||
___channel_recv
|
||||
___channel_send
|
||||
___channel_try_recv
|
||||
___env_get
|
||||
___exec
|
||||
___exec_bg
|
||||
___exit_program
|
||||
___float_to_str
|
||||
___fs_exists
|
||||
___fs_list_raw
|
||||
___fs_mkdir
|
||||
___fs_read
|
||||
___fs_write
|
||||
___fs_write_bytes
|
||||
___http_conn_fd
|
||||
___http_do
|
||||
___http_do_map
|
||||
___http_do_map_to_file
|
||||
___http_response
|
||||
___http_serve
|
||||
___http_serve_v2
|
||||
___http_sse_close
|
||||
___http_sse_open
|
||||
___http_sse_send
|
||||
___int_to_str
|
||||
___json_array_get
|
||||
___json_array_get_string
|
||||
___json_array_len
|
||||
___json_get
|
||||
___json_get_raw
|
||||
___json_parse_map
|
||||
___json_set
|
||||
___json_stringify_val
|
||||
___mutex_lock
|
||||
___mutex_new
|
||||
___mutex_unlock
|
||||
___print
|
||||
___print_raw
|
||||
___println
|
||||
___read_n
|
||||
___readline
|
||||
___sha256_hex
|
||||
___state_del
|
||||
___state_get
|
||||
___state_keys
|
||||
___state_set
|
||||
___str_alloc
|
||||
___str_char_at
|
||||
___str_cmp
|
||||
___str_concat_raw
|
||||
___str_len
|
||||
___str_ncmp
|
||||
___str_set_char
|
||||
___str_slice_raw
|
||||
___str_to_float
|
||||
___str_to_int
|
||||
___thread_create
|
||||
___thread_join
|
||||
___url_decode
|
||||
___url_encode
|
||||
___uuid_v4
|
||||
_aead_decrypt
|
||||
_aead_encrypt
|
||||
_append
|
||||
_args
|
||||
_base64_decode
|
||||
_base64_encode
|
||||
_base64url_decode
|
||||
_base64url_encode
|
||||
_bool_to_str
|
||||
_bytes_to_str
|
||||
_cal_cycle_phase
|
||||
_cal_format
|
||||
_cal_in
|
||||
_cal_to_instant
|
||||
_config
|
||||
_cycle_calendar
|
||||
_decimal_round
|
||||
_duration_millis
|
||||
_duration_nanos
|
||||
_duration_seconds
|
||||
_duration_to_millis
|
||||
_duration_to_nanos
|
||||
_duration_to_seconds
|
||||
_earth_calendar
|
||||
_earth_calendar_default
|
||||
_el_abs
|
||||
_el_arena_pop
|
||||
_el_arena_push
|
||||
_el_base64_encode_n
|
||||
_el_cgi_init
|
||||
_el_duration_add
|
||||
_el_duration_div
|
||||
_el_duration_eq
|
||||
_el_duration_from_nanos
|
||||
_el_duration_ge
|
||||
_el_duration_gt
|
||||
_el_duration_le
|
||||
_el_duration_lt
|
||||
_el_duration_ne
|
||||
_el_duration_scale
|
||||
_el_duration_sub
|
||||
_el_get_field
|
||||
_el_html_sanitize
|
||||
_el_instant_add_dur
|
||||
_el_instant_diff
|
||||
_el_instant_eq
|
||||
_el_instant_ge
|
||||
_el_instant_gt
|
||||
_el_instant_le
|
||||
_el_instant_lt
|
||||
_el_instant_ne
|
||||
_el_instant_sub_dur
|
||||
_el_list_append
|
||||
_el_list_clone
|
||||
_el_list_empty
|
||||
_el_list_get
|
||||
_el_list_len
|
||||
_el_list_new
|
||||
_el_local_date_add_dur
|
||||
_el_local_date_eq
|
||||
_el_local_date_lt
|
||||
_el_local_time_add_dur
|
||||
_el_map_get
|
||||
_el_map_new
|
||||
_el_map_set
|
||||
_el_max
|
||||
_el_mem_check
|
||||
_el_min
|
||||
_el_now_instant
|
||||
_el_release
|
||||
_el_request_end
|
||||
_el_request_start
|
||||
_el_retain
|
||||
_el_runtime_init_args
|
||||
_el_runtime_register_handler
|
||||
_el_runtime_register_handler_v2
|
||||
_el_sha256_bytes_n
|
||||
_el_sleep_duration
|
||||
_el_str_concat
|
||||
_engram_activate
|
||||
_engram_activate_json
|
||||
_engram_add_layer
|
||||
_engram_apply_decay_json
|
||||
_engram_compile_layered_json
|
||||
_engram_connect
|
||||
_engram_edge_between
|
||||
_engram_edge_count
|
||||
_engram_forget
|
||||
_engram_get_node
|
||||
_engram_get_node_json
|
||||
_engram_list_layers
|
||||
_engram_list_layers_json
|
||||
_engram_load
|
||||
_engram_neighbors
|
||||
_engram_neighbors_filtered
|
||||
_engram_neighbors_json
|
||||
_engram_node
|
||||
_engram_node_count
|
||||
_engram_node_full
|
||||
_engram_node_layered
|
||||
_engram_query_range
|
||||
_engram_remove_layer
|
||||
_engram_save
|
||||
_engram_scan_nodes
|
||||
_engram_scan_nodes_by_type_json
|
||||
_engram_scan_nodes_json
|
||||
_engram_search
|
||||
_engram_search_json
|
||||
_engram_stats_json
|
||||
_engram_strengthen
|
||||
_env
|
||||
_exec
|
||||
_exec_bg
|
||||
_exec_capture
|
||||
_exec_command
|
||||
_exit_program
|
||||
_float_to_int
|
||||
_float_to_str
|
||||
_format_float
|
||||
_fs_exists
|
||||
_fs_list
|
||||
_fs_list_json
|
||||
_fs_mkdir
|
||||
_fs_read
|
||||
_fs_write
|
||||
_fs_write_bytes
|
||||
_get
|
||||
_getpid_now
|
||||
_hash_sha256
|
||||
_hmac_sha256_bytes
|
||||
_hmac_sha256_hex
|
||||
_html_escape
|
||||
_html_raw
|
||||
_http_delete
|
||||
_http_get
|
||||
_http_get_engram
|
||||
_http_get_to_file
|
||||
_http_get_with_headers
|
||||
_http_patch
|
||||
_http_post
|
||||
_http_post_engram
|
||||
_http_post_form_auth
|
||||
_http_post_json
|
||||
_http_post_json_with_headers
|
||||
_http_post_to_file
|
||||
_http_post_with_headers
|
||||
_http_response
|
||||
_http_serve
|
||||
_http_serve_v2
|
||||
_http_set_handler
|
||||
_http_set_handler_v2
|
||||
_in_calendar
|
||||
_instant_from_iso8601
|
||||
_instant_to_iso8601
|
||||
_instant_to_unix_millis
|
||||
_instant_to_unix_seconds
|
||||
_int_to_float
|
||||
_int_to_str
|
||||
_is_alphanumeric
|
||||
_is_digit
|
||||
_is_letter
|
||||
_is_lowercase
|
||||
_is_punctuation
|
||||
_is_uppercase
|
||||
_is_whitespace
|
||||
_json_array_get
|
||||
_json_array_get_string
|
||||
_json_array_len
|
||||
_json_array_push
|
||||
_json_build_array
|
||||
_json_build_object
|
||||
_json_escape_string
|
||||
_json_get
|
||||
_json_get_bool
|
||||
_json_get_float
|
||||
_json_get_int
|
||||
_json_get_raw
|
||||
_json_get_string
|
||||
_json_parse
|
||||
_json_set
|
||||
_json_stringify
|
||||
_len
|
||||
_list_get
|
||||
_list_join
|
||||
_list_len
|
||||
_list_push
|
||||
_list_push_front
|
||||
_list_range
|
||||
_llm_call
|
||||
_llm_call_agentic
|
||||
_llm_call_system
|
||||
_llm_models
|
||||
_llm_register_tool
|
||||
_llm_vision
|
||||
_local_date
|
||||
_local_date_day
|
||||
_local_date_month
|
||||
_local_date_year
|
||||
_local_datetime
|
||||
_local_time
|
||||
_local_time_hour
|
||||
_local_time_minute
|
||||
_local_time_nanos
|
||||
_local_time_second
|
||||
_log_info
|
||||
_log_warn
|
||||
_map_get
|
||||
_map_set
|
||||
_mars_calendar
|
||||
_math_cos
|
||||
_math_ln
|
||||
_math_log
|
||||
_math_pi
|
||||
_math_sin
|
||||
_math_sqrt
|
||||
_native_int_to_str
|
||||
_native_list_append
|
||||
_native_list_clone
|
||||
_native_list_empty
|
||||
_native_list_get
|
||||
_native_list_len
|
||||
_native_str_to_int
|
||||
_native_string_chars
|
||||
_no_cycle_calendar
|
||||
_now
|
||||
_now_in
|
||||
_now_millis
|
||||
_now_ns
|
||||
_parse_int
|
||||
_pq_hybrid_handshake
|
||||
_pq_hybrid_keygen
|
||||
_pq_kem_decaps
|
||||
_pq_kem_encaps
|
||||
_pq_kem_keygen
|
||||
_pq_keygen_signature
|
||||
_pq_sign
|
||||
_pq_verify
|
||||
_print
|
||||
_println
|
||||
_readline
|
||||
_relative_calendar
|
||||
_rhythm_and
|
||||
_rhythm_cycle_phase
|
||||
_rhythm_cycle_start
|
||||
_rhythm_duration
|
||||
_rhythm_event
|
||||
_rhythm_matches
|
||||
_rhythm_next_after
|
||||
_rhythm_or
|
||||
_rhythm_session_start
|
||||
_rhythm_weekday
|
||||
_rhythm_weekly_at
|
||||
_sha256_bytes
|
||||
_sha256_hex
|
||||
_sha3_256_hex
|
||||
_sleep_ms
|
||||
_sleep_secs
|
||||
_state_del
|
||||
_state_get
|
||||
_state_get_or
|
||||
_state_has
|
||||
_state_keys
|
||||
_state_set
|
||||
_stdout_restore
|
||||
_stdout_to_file
|
||||
_str_char_at
|
||||
_str_char_code
|
||||
_str_concat
|
||||
_str_contains
|
||||
_str_count
|
||||
_str_count_bytes
|
||||
_str_count_chars
|
||||
_str_count_digits
|
||||
_str_count_letters
|
||||
_str_count_lines
|
||||
_str_count_words
|
||||
_str_ends_with
|
||||
_str_eq
|
||||
_str_find_chars
|
||||
_str_format
|
||||
_str_index_of
|
||||
_str_index_of_all
|
||||
_str_join
|
||||
_str_last_index_of
|
||||
_str_len
|
||||
_str_lower
|
||||
_str_lstrip
|
||||
_str_pad_left
|
||||
_str_pad_right
|
||||
_str_repeat
|
||||
_str_replace
|
||||
_str_reverse
|
||||
_str_rstrip
|
||||
_str_slice
|
||||
_str_split
|
||||
_str_split_chars
|
||||
_str_split_lines
|
||||
_str_split_n
|
||||
_str_starts_with
|
||||
_str_strip_chars
|
||||
_str_strip_prefix
|
||||
_str_strip_suffix
|
||||
_str_to_bytes
|
||||
_str_to_float
|
||||
_str_to_int
|
||||
_str_to_lower
|
||||
_str_to_upper
|
||||
_str_trim
|
||||
_str_upper
|
||||
_time_add
|
||||
_time_diff
|
||||
_time_format
|
||||
_time_from_parts
|
||||
_time_now
|
||||
_time_now_ms
|
||||
_time_now_utc
|
||||
_time_to_parts
|
||||
_ttl_cache_age
|
||||
_ttl_cache_get
|
||||
_ttl_cache_set
|
||||
_unix_millis
|
||||
_unix_seconds
|
||||
_unix_timestamp
|
||||
_unix_timestamp_ms
|
||||
_url_decode
|
||||
_url_encode
|
||||
_uuid_new
|
||||
_uuid_v4
|
||||
_zone
|
||||
_zone_local
|
||||
_zone_offset
|
||||
_zone_utc
|
||||
_zoned
|
||||
@@ -0,0 +1,340 @@
|
||||
___channel_close
|
||||
___channel_new
|
||||
___channel_recv
|
||||
___channel_send
|
||||
___channel_try_recv
|
||||
___http_do_map
|
||||
___http_do_map_to_file
|
||||
___print_raw
|
||||
___read_n
|
||||
_aead_decrypt
|
||||
_aead_encrypt
|
||||
_append
|
||||
_args
|
||||
_base64_decode
|
||||
_base64_encode
|
||||
_base64url_decode
|
||||
_base64url_encode
|
||||
_bool_to_str
|
||||
_bytes_to_str
|
||||
_cal_cycle_phase
|
||||
_cal_format
|
||||
_cal_in
|
||||
_cal_to_instant
|
||||
_config
|
||||
_cycle_calendar
|
||||
_decimal_round
|
||||
_duration_millis
|
||||
_duration_nanos
|
||||
_duration_seconds
|
||||
_duration_to_millis
|
||||
_duration_to_nanos
|
||||
_duration_to_seconds
|
||||
_earth_calendar
|
||||
_earth_calendar_default
|
||||
_el_abs
|
||||
_el_arena_pop
|
||||
_el_arena_push
|
||||
_el_base64_encode_n
|
||||
_el_cgi_init
|
||||
_el_duration_add
|
||||
_el_duration_div
|
||||
_el_duration_eq
|
||||
_el_duration_from_nanos
|
||||
_el_duration_ge
|
||||
_el_duration_gt
|
||||
_el_duration_le
|
||||
_el_duration_lt
|
||||
_el_duration_ne
|
||||
_el_duration_scale
|
||||
_el_duration_sub
|
||||
_el_get_field
|
||||
_el_html_sanitize
|
||||
_el_instant_add_dur
|
||||
_el_instant_diff
|
||||
_el_instant_eq
|
||||
_el_instant_ge
|
||||
_el_instant_gt
|
||||
_el_instant_le
|
||||
_el_instant_lt
|
||||
_el_instant_ne
|
||||
_el_instant_sub_dur
|
||||
_el_list_append
|
||||
_el_list_clone
|
||||
_el_list_empty
|
||||
_el_list_get
|
||||
_el_list_len
|
||||
_el_list_new
|
||||
_el_local_date_add_dur
|
||||
_el_local_date_eq
|
||||
_el_local_date_lt
|
||||
_el_local_time_add_dur
|
||||
_el_map_get
|
||||
_el_map_new
|
||||
_el_map_set
|
||||
_el_max
|
||||
_el_mem_check
|
||||
_el_min
|
||||
_el_now_instant
|
||||
_el_release
|
||||
_el_retain
|
||||
_el_runtime_init_args
|
||||
_el_runtime_register_handler
|
||||
_el_runtime_register_handler_v2
|
||||
_el_sha256_bytes_n
|
||||
_el_sleep_duration
|
||||
_el_str_concat
|
||||
_engram_activate
|
||||
_engram_activate_json
|
||||
_engram_add_layer
|
||||
_engram_apply_decay_json
|
||||
_engram_compile_layered_json
|
||||
_engram_connect
|
||||
_engram_edge_between
|
||||
_engram_edge_count
|
||||
_engram_forget
|
||||
_engram_get_node
|
||||
_engram_get_node_json
|
||||
_engram_list_layers
|
||||
_engram_list_layers_json
|
||||
_engram_load
|
||||
_engram_neighbors
|
||||
_engram_neighbors_filtered
|
||||
_engram_neighbors_json
|
||||
_engram_node
|
||||
_engram_node_count
|
||||
_engram_node_full
|
||||
_engram_node_layered
|
||||
_engram_query_range
|
||||
_engram_remove_layer
|
||||
_engram_save
|
||||
_engram_scan_nodes
|
||||
_engram_scan_nodes_by_type_json
|
||||
_engram_scan_nodes_json
|
||||
_engram_search
|
||||
_engram_search_json
|
||||
_engram_stats_json
|
||||
_engram_strengthen
|
||||
_env
|
||||
_exec
|
||||
_exec_bg
|
||||
_exec_capture
|
||||
_exec_command
|
||||
_exit_program
|
||||
_float_to_int
|
||||
_float_to_str
|
||||
_format_float
|
||||
_fs_exists
|
||||
_fs_list
|
||||
_fs_list_json
|
||||
_fs_mkdir
|
||||
_fs_read
|
||||
_fs_write
|
||||
_fs_write_bytes
|
||||
_get
|
||||
_getpid_now
|
||||
_hash_sha256
|
||||
_hmac_sha256_bytes
|
||||
_hmac_sha256_hex
|
||||
_html_escape
|
||||
_html_raw
|
||||
_http_delete
|
||||
_http_get
|
||||
_http_get_engram
|
||||
_http_get_to_file
|
||||
_http_get_with_headers
|
||||
_http_patch
|
||||
_http_post
|
||||
_http_post_engram
|
||||
_http_post_form_auth
|
||||
_http_post_json
|
||||
_http_post_json_with_headers
|
||||
_http_post_to_file
|
||||
_http_post_with_headers
|
||||
_http_response
|
||||
_http_serve
|
||||
_http_serve_v2
|
||||
_http_set_handler
|
||||
_http_set_handler_v2
|
||||
_in_calendar
|
||||
_instant_from_iso8601
|
||||
_instant_to_iso8601
|
||||
_instant_to_unix_millis
|
||||
_instant_to_unix_seconds
|
||||
_int_to_float
|
||||
_int_to_str
|
||||
_is_alphanumeric
|
||||
_is_digit
|
||||
_is_letter
|
||||
_is_lowercase
|
||||
_is_punctuation
|
||||
_is_uppercase
|
||||
_is_whitespace
|
||||
_json_array_get
|
||||
_json_array_get_string
|
||||
_json_array_len
|
||||
_json_array_push
|
||||
_json_build_array
|
||||
_json_build_object
|
||||
_json_escape_string
|
||||
_json_get
|
||||
_json_get_bool
|
||||
_json_get_float
|
||||
_json_get_int
|
||||
_json_get_raw
|
||||
_json_get_string
|
||||
_json_parse
|
||||
_json_set
|
||||
_json_stringify
|
||||
_len
|
||||
_list_get
|
||||
_list_join
|
||||
_list_len
|
||||
_list_push
|
||||
_list_push_front
|
||||
_list_range
|
||||
_llm_call
|
||||
_llm_call_agentic
|
||||
_llm_call_system
|
||||
_llm_models
|
||||
_llm_register_tool
|
||||
_llm_vision
|
||||
_local_date
|
||||
_local_date_day
|
||||
_local_date_month
|
||||
_local_date_year
|
||||
_local_datetime
|
||||
_local_time
|
||||
_local_time_hour
|
||||
_local_time_minute
|
||||
_local_time_nanos
|
||||
_local_time_second
|
||||
_log_info
|
||||
_log_warn
|
||||
_map_get
|
||||
_map_set
|
||||
_mars_calendar
|
||||
_math_cos
|
||||
_math_ln
|
||||
_math_log
|
||||
_math_pi
|
||||
_math_sin
|
||||
_math_sqrt
|
||||
_native_int_to_str
|
||||
_native_list_append
|
||||
_native_list_clone
|
||||
_native_list_empty
|
||||
_native_list_get
|
||||
_native_list_len
|
||||
_native_str_to_int
|
||||
_native_string_chars
|
||||
_no_cycle_calendar
|
||||
_now
|
||||
_now_in
|
||||
_now_millis
|
||||
_now_ns
|
||||
_parse_int
|
||||
_pq_hybrid_handshake
|
||||
_pq_hybrid_keygen
|
||||
_pq_kem_decaps
|
||||
_pq_kem_encaps
|
||||
_pq_kem_keygen
|
||||
_pq_keygen_signature
|
||||
_pq_sign
|
||||
_pq_verify
|
||||
_print
|
||||
_println
|
||||
_readline
|
||||
_relative_calendar
|
||||
_rhythm_and
|
||||
_rhythm_cycle_phase
|
||||
_rhythm_cycle_start
|
||||
_rhythm_duration
|
||||
_rhythm_event
|
||||
_rhythm_matches
|
||||
_rhythm_next_after
|
||||
_rhythm_or
|
||||
_rhythm_session_start
|
||||
_rhythm_weekday
|
||||
_rhythm_weekly_at
|
||||
_sha256_bytes
|
||||
_sha256_hex
|
||||
_sha3_256_hex
|
||||
_sleep_ms
|
||||
_sleep_secs
|
||||
_state_del
|
||||
_state_get
|
||||
_state_get_or
|
||||
_state_has
|
||||
_state_keys
|
||||
_state_set
|
||||
_stdout_restore
|
||||
_stdout_to_file
|
||||
_str_char_at
|
||||
_str_char_code
|
||||
_str_concat
|
||||
_str_contains
|
||||
_str_count
|
||||
_str_count_bytes
|
||||
_str_count_chars
|
||||
_str_count_digits
|
||||
_str_count_letters
|
||||
_str_count_lines
|
||||
_str_count_words
|
||||
_str_ends_with
|
||||
_str_eq
|
||||
_str_find_chars
|
||||
_str_format
|
||||
_str_index_of
|
||||
_str_index_of_all
|
||||
_str_join
|
||||
_str_last_index_of
|
||||
_str_len
|
||||
_str_lower
|
||||
_str_lstrip
|
||||
_str_pad_left
|
||||
_str_pad_right
|
||||
_str_repeat
|
||||
_str_replace
|
||||
_str_reverse
|
||||
_str_rstrip
|
||||
_str_slice
|
||||
_str_split
|
||||
_str_split_chars
|
||||
_str_split_lines
|
||||
_str_split_n
|
||||
_str_starts_with
|
||||
_str_strip_chars
|
||||
_str_strip_prefix
|
||||
_str_strip_suffix
|
||||
_str_to_bytes
|
||||
_str_to_float
|
||||
_str_to_int
|
||||
_str_to_lower
|
||||
_str_to_upper
|
||||
_str_trim
|
||||
_str_upper
|
||||
_time_add
|
||||
_time_diff
|
||||
_time_format
|
||||
_time_from_parts
|
||||
_time_now
|
||||
_time_now_ms
|
||||
_time_now_utc
|
||||
_time_to_parts
|
||||
_ttl_cache_age
|
||||
_ttl_cache_get
|
||||
_ttl_cache_set
|
||||
_unix_millis
|
||||
_unix_seconds
|
||||
_unix_timestamp
|
||||
_unix_timestamp_ms
|
||||
_url_decode
|
||||
_url_encode
|
||||
_uuid_new
|
||||
_uuid_v4
|
||||
_zone
|
||||
_zone_local
|
||||
_zone_offset
|
||||
_zone_utc
|
||||
_zoned
|
||||
@@ -0,0 +1,137 @@
|
||||
___args_json
|
||||
___button_create
|
||||
___cos_f
|
||||
___engram_activate
|
||||
___engram_activate_json
|
||||
___engram_add_layer
|
||||
___engram_compile_layered_json
|
||||
___engram_connect
|
||||
___engram_edge_between
|
||||
___engram_edge_count
|
||||
___engram_forget
|
||||
___engram_get_node
|
||||
___engram_get_node_json
|
||||
___engram_list_layers
|
||||
___engram_list_layers_json
|
||||
___engram_load
|
||||
___engram_neighbors
|
||||
___engram_neighbors_filtered
|
||||
___engram_neighbors_json
|
||||
___engram_node
|
||||
___engram_node_count
|
||||
___engram_node_full
|
||||
___engram_node_layered
|
||||
___engram_remove_layer
|
||||
___engram_save
|
||||
___engram_scan_nodes
|
||||
___engram_scan_nodes_by_type_json
|
||||
___engram_scan_nodes_json
|
||||
___engram_search
|
||||
___engram_search_json
|
||||
___engram_stats_json
|
||||
___engram_strengthen
|
||||
___env_get
|
||||
___exec
|
||||
___exec_bg
|
||||
___exit_program
|
||||
___float_to_str
|
||||
___fs_exists
|
||||
___fs_list_raw
|
||||
___fs_mkdir
|
||||
___fs_read
|
||||
___fs_write
|
||||
___fs_write_bytes
|
||||
___hstack_create
|
||||
___html_sanitize
|
||||
___http_conn_fd
|
||||
___http_do
|
||||
___http_do_to_file
|
||||
___http_response
|
||||
___http_serve
|
||||
___http_serve_v2
|
||||
___http_sse_close
|
||||
___http_sse_open
|
||||
___http_sse_send
|
||||
___image_create
|
||||
___int_to_str
|
||||
___json_array_get
|
||||
___json_array_get_string
|
||||
___json_array_len
|
||||
___json_get
|
||||
___json_get_bool
|
||||
___json_get_float
|
||||
___json_get_int
|
||||
___json_get_raw
|
||||
___json_get_string
|
||||
___json_parse
|
||||
___json_parse_map
|
||||
___json_set
|
||||
___json_stringify
|
||||
___json_stringify_val
|
||||
___label_create
|
||||
___ln_f
|
||||
___log_f
|
||||
___manifest_read
|
||||
___mutex_lock
|
||||
___mutex_new
|
||||
___mutex_unlock
|
||||
___native_init
|
||||
___native_run_loop
|
||||
___pi_f
|
||||
___print
|
||||
___println
|
||||
___readline
|
||||
___scroll_create
|
||||
___sha256_hex
|
||||
___sin_f
|
||||
___sleep_ms
|
||||
___sqrt_f
|
||||
___state_del
|
||||
___state_get
|
||||
___state_keys
|
||||
___state_set
|
||||
___str_alloc
|
||||
___str_char_at
|
||||
___str_cmp
|
||||
___str_concat_raw
|
||||
___str_len
|
||||
___str_ncmp
|
||||
___str_set_char
|
||||
___str_slice_raw
|
||||
___str_to_float
|
||||
___str_to_int
|
||||
___text_area_create
|
||||
___text_field_create
|
||||
___thread_create
|
||||
___thread_join
|
||||
___time_now_ns
|
||||
___url_decode
|
||||
___url_encode
|
||||
___uuid_v4
|
||||
___vstack_create
|
||||
___widget_add_child
|
||||
___widget_destroy
|
||||
___widget_get_text
|
||||
___widget_on_change
|
||||
___widget_on_click
|
||||
___widget_on_submit
|
||||
___widget_remove_child
|
||||
___widget_set_bg_color
|
||||
___widget_set_color
|
||||
___widget_set_corner_radius
|
||||
___widget_set_disabled
|
||||
___widget_set_flex
|
||||
___widget_set_font
|
||||
___widget_set_height
|
||||
___widget_set_hidden
|
||||
___widget_set_padding
|
||||
___widget_set_text
|
||||
___widget_set_width
|
||||
___window_create
|
||||
___window_set_title
|
||||
___window_show
|
||||
___zstack_create
|
||||
_el_request_end
|
||||
_el_request_start
|
||||
_el_seed_init_args
|
||||
_el_seed_set_http_conn_fd
|
||||
@@ -0,0 +1,55 @@
|
||||
_button
|
||||
_color_hex_a
|
||||
_color_hex_b
|
||||
_color_hex_g
|
||||
_color_hex_r
|
||||
_hex_channel
|
||||
_hex_nibble
|
||||
_hstack
|
||||
_image
|
||||
_label
|
||||
_manifest_height
|
||||
_manifest_min_height
|
||||
_manifest_min_width
|
||||
_manifest_read
|
||||
_manifest_title
|
||||
_manifest_width
|
||||
_native_init
|
||||
_native_run_loop
|
||||
_scroll
|
||||
_style_button_primary
|
||||
_style_label_body
|
||||
_style_label_heading
|
||||
_style_label_muted
|
||||
_style_surface
|
||||
_text_area
|
||||
_text_field
|
||||
_vstack
|
||||
_vstack_tight
|
||||
_widget_add_child
|
||||
_widget_destroy
|
||||
_widget_get_text
|
||||
_widget_on_change
|
||||
_widget_on_click
|
||||
_widget_on_submit
|
||||
_widget_remove_child
|
||||
_widget_set_bg_color
|
||||
_widget_set_bg_color_hex
|
||||
_widget_set_color
|
||||
_widget_set_color_hex
|
||||
_widget_set_corner_radius
|
||||
_widget_set_disabled
|
||||
_widget_set_flex
|
||||
_widget_set_font
|
||||
_widget_set_height
|
||||
_widget_set_hidden
|
||||
_widget_set_padding
|
||||
_widget_set_padding_all
|
||||
_widget_set_padding_xy
|
||||
_widget_set_text
|
||||
_widget_set_width
|
||||
_window_create
|
||||
_window_from_manifest
|
||||
_window_set_title
|
||||
_window_show
|
||||
_zstack
|
||||
Binary file not shown.
@@ -0,0 +1,459 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t native_init(void);
|
||||
el_val_t native_run_loop(void);
|
||||
el_val_t manifest_read(el_val_t path);
|
||||
el_val_t manifest_title(el_val_t m);
|
||||
el_val_t manifest_width(el_val_t m);
|
||||
el_val_t manifest_height(el_val_t m);
|
||||
el_val_t manifest_min_width(el_val_t m);
|
||||
el_val_t manifest_min_height(el_val_t m);
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height);
|
||||
el_val_t window_from_manifest(el_val_t manifest_path);
|
||||
el_val_t window_show(el_val_t handle);
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title);
|
||||
el_val_t vstack(el_val_t spacing);
|
||||
el_val_t vstack_tight(void);
|
||||
el_val_t hstack(el_val_t spacing);
|
||||
el_val_t zstack(void);
|
||||
el_val_t scroll(void);
|
||||
el_val_t label(el_val_t text);
|
||||
el_val_t button(el_val_t label);
|
||||
el_val_t text_field(el_val_t placeholder);
|
||||
el_val_t text_area(el_val_t placeholder);
|
||||
el_val_t image(el_val_t path_or_name);
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t widget_get_text(el_val_t handle);
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold);
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left);
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p);
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py);
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width);
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height);
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_destroy(el_val_t handle);
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset);
|
||||
el_val_t hex_nibble(el_val_t c);
|
||||
el_val_t color_hex_r(el_val_t hex);
|
||||
el_val_t color_hex_g(el_val_t hex);
|
||||
el_val_t color_hex_b(el_val_t hex);
|
||||
el_val_t color_hex_a(el_val_t hex);
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t style_surface(el_val_t handle);
|
||||
el_val_t style_button_primary(el_val_t handle);
|
||||
el_val_t style_label_body(el_val_t handle);
|
||||
el_val_t style_label_heading(el_val_t handle);
|
||||
el_val_t style_label_muted(el_val_t handle);
|
||||
|
||||
el_val_t TOKEN_PRIMARY;
|
||||
el_val_t TOKEN_ON_PRIMARY;
|
||||
el_val_t TOKEN_BACKGROUND;
|
||||
el_val_t TOKEN_ON_BG;
|
||||
el_val_t TOKEN_SURFACE;
|
||||
el_val_t TOKEN_ON_SURFACE;
|
||||
el_val_t TOKEN_OUTLINE;
|
||||
el_val_t TOKEN_ERROR;
|
||||
|
||||
el_val_t native_init(void) {
|
||||
__native_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t native_run_loop(void) {
|
||||
__native_run_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_read(el_val_t path) {
|
||||
return __manifest_read(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_title(el_val_t m) {
|
||||
return json_get_string(m, EL_STR("title"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_width(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("width"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_height(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("height"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_min_width(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("min_width"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t manifest_min_height(el_val_t m) {
|
||||
return json_get_int(m, EL_STR("min_height"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) {
|
||||
return __window_create(title, width, height, min_width, min_height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_from_manifest(el_val_t manifest_path) {
|
||||
el_val_t m = manifest_read(manifest_path);
|
||||
el_val_t title = manifest_title(m);
|
||||
el_val_t w = manifest_width(m);
|
||||
el_val_t h = manifest_height(m);
|
||||
el_val_t mw = manifest_min_width(m);
|
||||
el_val_t mh = manifest_min_height(m);
|
||||
el_val_t safe_w = ({ el_val_t _if_result_1 = 0; if ((w > 0)) { _if_result_1 = (w); } else { _if_result_1 = (1200); } _if_result_1; });
|
||||
el_val_t safe_h = ({ el_val_t _if_result_2 = 0; if ((h > 0)) { _if_result_2 = (h); } else { _if_result_2 = (800); } _if_result_2; });
|
||||
el_val_t safe_mw = ({ el_val_t _if_result_3 = 0; if ((mw > 0)) { _if_result_3 = (mw); } else { _if_result_3 = (600); } _if_result_3; });
|
||||
el_val_t safe_mh = ({ el_val_t _if_result_4 = 0; if ((mh > 0)) { _if_result_4 = (mh); } else { _if_result_4 = (400); } _if_result_4; });
|
||||
el_val_t safe_t = ({ el_val_t _if_result_5 = 0; if ((str_len(title) > 0)) { _if_result_5 = (title); } else { _if_result_5 = (EL_STR("App")); } _if_result_5; });
|
||||
return window_create(safe_t, safe_w, safe_h, safe_mw, safe_mh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_show(el_val_t handle) {
|
||||
__window_show(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title) {
|
||||
__window_set_title(handle, title);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t vstack(el_val_t spacing) {
|
||||
return __vstack_create(spacing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t vstack_tight(void) {
|
||||
return __vstack_create(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hstack(el_val_t spacing) {
|
||||
return __hstack_create(spacing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t zstack(void) {
|
||||
return __zstack_create();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t scroll(void) {
|
||||
return __scroll_create();
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t label(el_val_t text) {
|
||||
return __label_create(text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t button(el_val_t label) {
|
||||
return __button_create(label);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t text_field(el_val_t placeholder) {
|
||||
return __text_field_create(placeholder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t text_area(el_val_t placeholder) {
|
||||
return __text_area_create(placeholder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t image(el_val_t path_or_name) {
|
||||
return __image_create(path_or_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text) {
|
||||
__widget_set_text(handle, text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_get_text(el_val_t handle) {
|
||||
return __widget_get_text(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
|
||||
__widget_set_color(handle, r, g, b, a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a) {
|
||||
__widget_set_bg_color(handle, r, g, b, a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold) {
|
||||
el_val_t bold_int = ({ el_val_t _if_result_6 = 0; if (bold) { _if_result_6 = (1); } else { _if_result_6 = (0); } _if_result_6; });
|
||||
__widget_set_font(handle, family, size, bold_int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) {
|
||||
__widget_set_padding(handle, top, right, bottom, left);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p) {
|
||||
__widget_set_padding(handle, p, p, p, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py) {
|
||||
__widget_set_padding(handle, py, px, py, px);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width) {
|
||||
__widget_set_width(handle, width);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height) {
|
||||
__widget_set_height(handle, height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex) {
|
||||
__widget_set_flex(handle, flex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius) {
|
||||
__widget_set_corner_radius(handle, radius);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled) {
|
||||
el_val_t d = ({ el_val_t _if_result_7 = 0; if (disabled) { _if_result_7 = (1); } else { _if_result_7 = (0); } _if_result_7; });
|
||||
__widget_set_disabled(handle, d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden) {
|
||||
el_val_t h = ({ el_val_t _if_result_8 = 0; if (hidden) { _if_result_8 = (1); } else { _if_result_8 = (0); } _if_result_8; });
|
||||
__widget_set_hidden(handle, h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child) {
|
||||
__widget_add_child(parent, child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child) {
|
||||
__widget_remove_child(parent, child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_destroy(el_val_t handle) {
|
||||
__widget_destroy(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_click(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_change(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name) {
|
||||
__widget_on_submit(handle, fn_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset) {
|
||||
el_val_t hi = str_slice(s, offset, (offset + 1));
|
||||
el_val_t lo = str_slice(s, (offset + 1), (offset + 2));
|
||||
el_val_t h = hex_nibble(hi);
|
||||
el_val_t l = hex_nibble(lo);
|
||||
return ((h * 16) + l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t hex_nibble(el_val_t c) {
|
||||
if (str_eq(c, EL_STR("0"))) {
|
||||
return 0;
|
||||
}
|
||||
if (str_eq(c, EL_STR("1"))) {
|
||||
return 1;
|
||||
}
|
||||
if (str_eq(c, EL_STR("2"))) {
|
||||
return 2;
|
||||
}
|
||||
if (str_eq(c, EL_STR("3"))) {
|
||||
return 3;
|
||||
}
|
||||
if (str_eq(c, EL_STR("4"))) {
|
||||
return 4;
|
||||
}
|
||||
if (str_eq(c, EL_STR("5"))) {
|
||||
return 5;
|
||||
}
|
||||
if (str_eq(c, EL_STR("6"))) {
|
||||
return 6;
|
||||
}
|
||||
if (str_eq(c, EL_STR("7"))) {
|
||||
return 7;
|
||||
}
|
||||
if (str_eq(c, EL_STR("8"))) {
|
||||
return 8;
|
||||
}
|
||||
if (str_eq(c, EL_STR("9"))) {
|
||||
return 9;
|
||||
}
|
||||
if (str_eq(c, EL_STR("a"))) {
|
||||
return 10;
|
||||
}
|
||||
if (str_eq(c, EL_STR("b"))) {
|
||||
return 11;
|
||||
}
|
||||
if (str_eq(c, EL_STR("c"))) {
|
||||
return 12;
|
||||
}
|
||||
if (str_eq(c, EL_STR("d"))) {
|
||||
return 13;
|
||||
}
|
||||
if (str_eq(c, EL_STR("e"))) {
|
||||
return 14;
|
||||
}
|
||||
if (str_eq(c, EL_STR("f"))) {
|
||||
return 15;
|
||||
}
|
||||
if (str_eq(c, EL_STR("A"))) {
|
||||
return 10;
|
||||
}
|
||||
if (str_eq(c, EL_STR("B"))) {
|
||||
return 11;
|
||||
}
|
||||
if (str_eq(c, EL_STR("C"))) {
|
||||
return 12;
|
||||
}
|
||||
if (str_eq(c, EL_STR("D"))) {
|
||||
return 13;
|
||||
}
|
||||
if (str_eq(c, EL_STR("E"))) {
|
||||
return 14;
|
||||
}
|
||||
if (str_eq(c, EL_STR("F"))) {
|
||||
return 15;
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_r(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_9 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_9 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_9 = (hex); } _if_result_9; });
|
||||
el_val_t v = hex_channel(s, 0);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_g(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_10 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_10 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_10 = (hex); } _if_result_10; });
|
||||
el_val_t v = hex_channel(s, 2);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_b(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_11 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_11 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_11 = (hex); } _if_result_11; });
|
||||
el_val_t v = hex_channel(s, 4);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t color_hex_a(el_val_t hex) {
|
||||
el_val_t s = ({ el_val_t _if_result_12 = 0; if (str_starts_with(hex, EL_STR("#"))) { _if_result_12 = (str_slice(hex, 1, str_len(hex))); } else { _if_result_12 = (hex); } _if_result_12; });
|
||||
if (str_len(s) < 8) {
|
||||
return el_from_float(1.0);
|
||||
}
|
||||
el_val_t v = hex_channel(s, 6);
|
||||
return float_div(int_to_float(v), el_from_float(255.0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex) {
|
||||
widget_set_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex) {
|
||||
widget_set_bg_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_surface(el_val_t handle) {
|
||||
widget_set_bg_color_hex(handle, TOKEN_SURFACE);
|
||||
widget_set_corner_radius(handle, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_button_primary(el_val_t handle) {
|
||||
widget_set_bg_color_hex(handle, TOKEN_PRIMARY);
|
||||
widget_set_color_hex(handle, TOKEN_ON_PRIMARY);
|
||||
widget_set_corner_radius(handle, 8);
|
||||
widget_set_padding_xy(handle, 16, 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_body(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_ON_BG);
|
||||
widget_set_font(handle, EL_STR("system"), 14, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_heading(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_ON_BG);
|
||||
widget_set_font(handle, EL_STR("system"), 20, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t style_label_muted(el_val_t handle) {
|
||||
widget_set_color_hex(handle, TOKEN_OUTLINE);
|
||||
widget_set_font(handle, EL_STR("system"), 12, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int _argc, char** _argv) {
|
||||
el_runtime_init_args(_argc, _argv);
|
||||
TOKEN_PRIMARY = EL_STR("#60a5fa");
|
||||
TOKEN_ON_PRIMARY = EL_STR("#0f172a");
|
||||
TOKEN_BACKGROUND = EL_STR("#0f172a");
|
||||
TOKEN_ON_BG = EL_STR("#f8fafc");
|
||||
TOKEN_SURFACE = EL_STR("#1e293b");
|
||||
TOKEN_ON_SURFACE = EL_STR("#f8fafc");
|
||||
TOKEN_OUTLINE = EL_STR("#475569");
|
||||
TOKEN_ERROR = EL_STR("#f87171");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t native_init(void);
|
||||
el_val_t native_run_loop(void);
|
||||
el_val_t manifest_read(el_val_t path);
|
||||
el_val_t manifest_title(el_val_t m);
|
||||
el_val_t manifest_width(el_val_t m);
|
||||
el_val_t manifest_height(el_val_t m);
|
||||
el_val_t manifest_min_width(el_val_t m);
|
||||
el_val_t manifest_min_height(el_val_t m);
|
||||
el_val_t window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height);
|
||||
el_val_t window_from_manifest(el_val_t manifest_path);
|
||||
el_val_t window_show(el_val_t handle);
|
||||
el_val_t window_set_title(el_val_t handle, el_val_t title);
|
||||
el_val_t vstack(el_val_t spacing);
|
||||
el_val_t vstack_tight(void);
|
||||
el_val_t hstack(el_val_t spacing);
|
||||
el_val_t zstack(void);
|
||||
el_val_t scroll(void);
|
||||
el_val_t label(el_val_t text);
|
||||
el_val_t button(el_val_t label);
|
||||
el_val_t text_field(el_val_t placeholder);
|
||||
el_val_t text_area(el_val_t placeholder);
|
||||
el_val_t image(el_val_t path_or_name);
|
||||
el_val_t widget_set_text(el_val_t handle, el_val_t text);
|
||||
el_val_t widget_get_text(el_val_t handle);
|
||||
el_val_t widget_set_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_bg_color(el_val_t handle, el_val_t r, el_val_t g, el_val_t b, el_val_t a);
|
||||
el_val_t widget_set_font(el_val_t handle, el_val_t family, el_val_t size, el_val_t bold);
|
||||
el_val_t widget_set_padding(el_val_t handle, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left);
|
||||
el_val_t widget_set_padding_all(el_val_t handle, el_val_t p);
|
||||
el_val_t widget_set_padding_xy(el_val_t handle, el_val_t px, el_val_t py);
|
||||
el_val_t widget_set_width(el_val_t handle, el_val_t width);
|
||||
el_val_t widget_set_height(el_val_t handle, el_val_t height);
|
||||
el_val_t widget_set_flex(el_val_t handle, el_val_t flex);
|
||||
el_val_t widget_set_corner_radius(el_val_t handle, el_val_t radius);
|
||||
el_val_t widget_set_disabled(el_val_t handle, el_val_t disabled);
|
||||
el_val_t widget_set_hidden(el_val_t handle, el_val_t hidden);
|
||||
el_val_t widget_add_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_remove_child(el_val_t parent, el_val_t child);
|
||||
el_val_t widget_destroy(el_val_t handle);
|
||||
el_val_t widget_on_click(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_change(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t widget_on_submit(el_val_t handle, el_val_t fn_name);
|
||||
el_val_t hex_channel(el_val_t s, el_val_t offset);
|
||||
el_val_t hex_nibble(el_val_t c);
|
||||
el_val_t color_hex_r(el_val_t hex);
|
||||
el_val_t color_hex_g(el_val_t hex);
|
||||
el_val_t color_hex_b(el_val_t hex);
|
||||
el_val_t color_hex_a(el_val_t hex);
|
||||
el_val_t widget_set_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t widget_set_bg_color_hex(el_val_t handle, el_val_t hex);
|
||||
el_val_t style_surface(el_val_t handle);
|
||||
el_val_t style_button_primary(el_val_t handle);
|
||||
el_val_t style_label_body(el_val_t handle);
|
||||
el_val_t style_label_heading(el_val_t handle);
|
||||
el_val_t style_label_muted(el_val_t handle);
|
||||
|
||||
el_val_t TOKEN_PRIMARY;
|
||||
el_val_t TOKEN_ON_PRIMARY;
|
||||
el_val_t TOKEN_BACKGROUND;
|
||||
el_val_t TOKEN_ON_BG;
|
||||
el_val_t TOKEN_SURFACE;
|
||||
el_val_t TOKEN_ON_SURFACE;
|
||||
el_val_t TOKEN_OUTLINE;
|
||||
el_val_t TOKEN_ERROR;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,123 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "el_runtime.h"
|
||||
|
||||
el_val_t on_greet_click(el_val_t widget, el_val_t data);
|
||||
el_val_t on_counter_click(el_val_t widget, el_val_t data);
|
||||
el_val_t on_name_change(el_val_t widget, el_val_t data);
|
||||
el_val_t app_build(el_val_t window);
|
||||
|
||||
el_val_t g_window;
|
||||
el_val_t g_label;
|
||||
el_val_t g_input;
|
||||
el_val_t g_button;
|
||||
el_val_t g_counter;
|
||||
el_val_t g_counter_lbl;
|
||||
el_val_t manifest_env;
|
||||
el_val_t manifest_path;
|
||||
el_val_t win;
|
||||
el_val_t g_window;
|
||||
|
||||
el_val_t on_greet_click(el_val_t widget, el_val_t data) {
|
||||
el_val_t name = widget_get_text(g_input);
|
||||
el_val_t greeting = ({ el_val_t _if_result_1 = 0; if ((str_len(name) > 0)) { _if_result_1 = (el_str_concat(el_str_concat(EL_STR("Hello, "), name), EL_STR("!"))); } else { _if_result_1 = (EL_STR("Hello, World!")); } _if_result_1; });
|
||||
widget_set_text(g_label, greeting);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t on_counter_click(el_val_t widget, el_val_t data) {
|
||||
el_val_t g_counter = (g_counter + 1);
|
||||
widget_set_text(g_counter_lbl, el_str_concat(EL_STR("Clicks: "), int_to_str(g_counter)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t on_name_change(el_val_t widget, el_val_t data) {
|
||||
el_val_t has_text = (str_len(data) > 0);
|
||||
widget_set_disabled(g_button, !has_text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t app_build(el_val_t window) {
|
||||
el_val_t root = vstack(0);
|
||||
widget_set_padding_all(root, 24);
|
||||
widget_set_bg_color_hex(root, EL_STR("#0f172a"));
|
||||
widget_set_flex(root, 1);
|
||||
el_val_t title = label(EL_STR("el-native \xe2\x80\x94 native widget demo"));
|
||||
style_label_heading(title);
|
||||
widget_add_child(root, title);
|
||||
el_val_t gap1 = label(EL_STR(""));
|
||||
widget_set_height(gap1, 16);
|
||||
widget_add_child(root, gap1);
|
||||
el_val_t subtitle = label(EL_STR("AppKit controls from el code, no ObjC in the app layer."));
|
||||
style_label_muted(subtitle);
|
||||
widget_add_child(root, subtitle);
|
||||
el_val_t gap2 = label(EL_STR(""));
|
||||
widget_set_height(gap2, 24);
|
||||
widget_add_child(root, gap2);
|
||||
el_val_t input_row = hstack(8);
|
||||
el_val_t name_label = label(EL_STR("Name:"));
|
||||
style_label_body(name_label);
|
||||
widget_set_width(name_label, 60);
|
||||
el_val_t input = text_field(EL_STR("Enter your name\xe2\x80\xa6"));
|
||||
style_label_body(input);
|
||||
widget_set_flex(input, 1);
|
||||
widget_on_change(input, EL_STR("on_name_change"));
|
||||
el_val_t g_input = input;
|
||||
el_val_t greet_btn = button(EL_STR("Greet"));
|
||||
style_button_primary(greet_btn);
|
||||
widget_set_disabled(greet_btn, 1);
|
||||
widget_on_click(greet_btn, EL_STR("on_greet_click"));
|
||||
el_val_t g_button = greet_btn;
|
||||
widget_add_child(input_row, name_label);
|
||||
widget_add_child(input_row, input);
|
||||
widget_add_child(input_row, greet_btn);
|
||||
widget_add_child(root, input_row);
|
||||
el_val_t gap3 = label(EL_STR(""));
|
||||
widget_set_height(gap3, 12);
|
||||
widget_add_child(root, gap3);
|
||||
el_val_t greeting = label(EL_STR("Waiting for name\xe2\x80\xa6"));
|
||||
style_label_body(greeting);
|
||||
widget_set_color_hex(greeting, EL_STR("#60a5fa"));
|
||||
widget_set_font(greeting, EL_STR("system"), 16, 1);
|
||||
el_val_t g_label = greeting;
|
||||
widget_add_child(root, greeting);
|
||||
el_val_t gap4 = label(EL_STR(""));
|
||||
widget_set_height(gap4, 24);
|
||||
widget_add_child(root, gap4);
|
||||
el_val_t counter_row = hstack(12);
|
||||
el_val_t counter_lbl = label(EL_STR("Clicks: 0"));
|
||||
style_label_body(counter_lbl);
|
||||
el_val_t g_counter_lbl = counter_lbl;
|
||||
el_val_t counter_btn = button(EL_STR("Click me"));
|
||||
style_button_primary(counter_btn);
|
||||
widget_on_click(counter_btn, EL_STR("on_counter_click"));
|
||||
widget_add_child(counter_row, counter_lbl);
|
||||
widget_add_child(counter_row, counter_btn);
|
||||
widget_add_child(root, counter_row);
|
||||
widget_add_child(window, root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int _argc, char** _argv) {
|
||||
el_runtime_init_args(_argc, _argv);
|
||||
g_window = (-1);
|
||||
g_label = (-1);
|
||||
g_input = (-1);
|
||||
g_button = (-1);
|
||||
g_counter = 0;
|
||||
g_counter_lbl = (-1);
|
||||
native_init();
|
||||
manifest_env = env(EL_STR("EL_MANIFEST"));
|
||||
manifest_path = ({ el_val_t _if_result_2 = 0; if ((str_len(manifest_env) > 0)) { _if_result_2 = (manifest_env); } else { _if_result_2 = (EL_STR("manifest.el")); } _if_result_2; });
|
||||
win = window_from_manifest(manifest_path);
|
||||
g_window = win;
|
||||
if (win < 0) {
|
||||
println(EL_STR("Error: failed to create window. Is EL_TARGET_MACOS defined?"));
|
||||
exit_program(1);
|
||||
}
|
||||
app_build(win);
|
||||
window_show(win);
|
||||
native_run_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Binary file not shown.
+128
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
# docker-build-linux.sh — Build native-hello for Linux using Docker.
|
||||
#
|
||||
# Usage:
|
||||
# ./docker-build-linux.sh # build ARM64 (native on Apple Silicon)
|
||||
# ./docker-build-linux.sh --x86 # also build x86_64 via QEMU
|
||||
# ./docker-build-linux.sh --x86-only # only build x86_64
|
||||
#
|
||||
# Output binaries are placed in ./build/:
|
||||
# native-hello-linux-arm64 — Linux ARM64 (Raspberry Pi 3/4/5, Apple Silicon VMs)
|
||||
# native-hello-linux-x86_64 — Linux x86_64 (desktop Linux, CI servers)
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Docker (Rancher Desktop / Docker Desktop)
|
||||
# - elc at ../../../lang/dist/platform/elc or in PATH
|
||||
#
|
||||
# How it works:
|
||||
# 1. elc generates C sources from the el program (macOS, ARM64 native elc)
|
||||
# 2. Docker builds those C sources inside Ubuntu 22.04 + GTK4
|
||||
# 3. The resulting ELF binary is extracted from the image
|
||||
#
|
||||
# Linux toolchain notes:
|
||||
# - GNU ld rejects duplicate symbol definitions that macOS ld accepts silently.
|
||||
# The Dockerfile uses objcopy --localize-symbol to hide duplicates between
|
||||
# el_seed.o / el_runtime.o and el_native_vessel.o / native_hello.o.
|
||||
# - el_gtk4.c requires _GNU_SOURCE for RTLD_DEFAULT (dlfcn.h).
|
||||
# - G_APPLICATION_DEFAULT_FLAGS was added in GLib 2.74; Ubuntu 22.04 ships
|
||||
# GLib 2.72, so the Dockerfile adds a compatibility define.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
EL_LANG_ROOT="${SCRIPT_DIR}/../../../lang"
|
||||
EL_UI_ROOT="${SCRIPT_DIR}/../.."
|
||||
EL_RUNTIME="${EL_LANG_ROOT}/el-compiler/runtime"
|
||||
EL_NATIVE_VESSEL="${EL_UI_ROOT}/vessels/el-native/src/main.el"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build"
|
||||
DOCKER_CTX="${SCRIPT_DIR}/build-docker"
|
||||
|
||||
BUILD_X86=0
|
||||
BUILD_ARM64=1
|
||||
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
--x86) BUILD_X86=1 ;;
|
||||
--x86-only) BUILD_X86=1; BUILD_ARM64=0 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Locate elc ────────────────────────────────────────────────────────────────
|
||||
if command -v elc &>/dev/null; then
|
||||
ELC="elc"
|
||||
elif [ -x "${EL_LANG_ROOT}/dist/platform/elc" ]; then
|
||||
ELC="${EL_LANG_ROOT}/dist/platform/elc"
|
||||
else
|
||||
echo "Error: elc not found. Add it to PATH or place at:"
|
||||
echo " ${EL_LANG_ROOT}/dist/platform/elc"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Step 1: Generate C sources ────────────────────────────────────────────────
|
||||
echo "==> Generating C sources with elc..."
|
||||
mkdir -p "${DOCKER_CTX}/runtime"
|
||||
|
||||
# App
|
||||
EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \
|
||||
"${ELC}" "${SCRIPT_DIR}/src/main.el" > "${DOCKER_CTX}/native_hello.c"
|
||||
|
||||
# Vessel
|
||||
"${ELC}" "${EL_NATIVE_VESSEL}" > "${DOCKER_CTX}/el_native_vessel.c"
|
||||
|
||||
# Vessel header (declarations only — stop before first function body)
|
||||
"${ELC}" --emit-header "${EL_NATIVE_VESSEL}" \
|
||||
| awk 'BEGIN{in_body=0} /^[a-zA-Z_].+\)[[:space:]]*\{/ {in_body=1} !in_body{print}' \
|
||||
> "${DOCKER_CTX}/el_native_vessel.h"
|
||||
|
||||
# Runtime C sources
|
||||
cp "${EL_RUNTIME}/el_gtk4.c" \
|
||||
"${EL_RUNTIME}/el_seed.c" \
|
||||
"${EL_RUNTIME}/el_seed.h" \
|
||||
"${EL_RUNTIME}/el_runtime.c" \
|
||||
"${EL_RUNTIME}/el_runtime.h" \
|
||||
"${EL_RUNTIME}/el_native_target.h" \
|
||||
"${DOCKER_CTX}/runtime/"
|
||||
|
||||
echo "==> C sources ready in ${DOCKER_CTX}/"
|
||||
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
|
||||
# ── Step 2: Docker build (ARM64) ─────────────────────────────────────────────
|
||||
if [ "${BUILD_ARM64}" -eq 1 ]; then
|
||||
echo "==> Building Linux ARM64 image..."
|
||||
docker build \
|
||||
--platform linux/arm64 \
|
||||
-f "${SCRIPT_DIR}/Dockerfile.linux-gtk4" \
|
||||
--tag el-native-linux-gtk4-arm64 \
|
||||
"${DOCKER_CTX}/"
|
||||
|
||||
echo "==> Extracting ARM64 binary..."
|
||||
docker create --name el-extract-arm64 el-native-linux-gtk4-arm64
|
||||
docker cp el-extract-arm64:/build/native-hello-linux "${BUILD_DIR}/native-hello-linux-arm64"
|
||||
docker rm el-extract-arm64
|
||||
|
||||
echo "==> ARM64 binary: ${BUILD_DIR}/native-hello-linux-arm64"
|
||||
file "${BUILD_DIR}/native-hello-linux-arm64"
|
||||
fi
|
||||
|
||||
# ── Step 3: Docker build (x86_64) ────────────────────────────────────────────
|
||||
if [ "${BUILD_X86}" -eq 1 ]; then
|
||||
echo "==> Building Linux x86_64 image (QEMU emulation — slower)..."
|
||||
docker build \
|
||||
--platform linux/amd64 \
|
||||
-f "${SCRIPT_DIR}/Dockerfile.linux-gtk4" \
|
||||
--tag el-native-linux-gtk4-x86_64 \
|
||||
"${DOCKER_CTX}/"
|
||||
|
||||
echo "==> Extracting x86_64 binary..."
|
||||
docker create --name el-extract-x86 el-native-linux-gtk4-x86_64
|
||||
docker cp el-extract-x86:/build/native-hello-linux "${BUILD_DIR}/native-hello-linux-x86_64"
|
||||
docker rm el-extract-x86
|
||||
|
||||
echo "==> x86_64 binary: ${BUILD_DIR}/native-hello-linux-x86_64"
|
||||
file "${BUILD_DIR}/native-hello-linux-x86_64"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "==> Done. Binaries in ${BUILD_DIR}/"
|
||||
ls -lh "${BUILD_DIR}"/native-hello-linux-* 2>/dev/null || true
|
||||
@@ -0,0 +1,19 @@
|
||||
package "native-hello" {
|
||||
version "0.1.0"
|
||||
description "Minimal native UI test for the el native widget system."
|
||||
authors ["Neuron Technologies"]
|
||||
edition "2026"
|
||||
}
|
||||
|
||||
build {
|
||||
entry "src/main.el"
|
||||
target "debug"
|
||||
}
|
||||
|
||||
app {
|
||||
window_title "Hello from el-native"
|
||||
window_width 480
|
||||
window_height 320
|
||||
min_width 360
|
||||
min_height 240
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
// App.el — native-hello root component.
|
||||
//
|
||||
// Demonstrates the el-native widget system:
|
||||
// - Window created from manifest.el
|
||||
// - VStack layout
|
||||
// - Label, Button, TextField
|
||||
// - Click and change event callbacks
|
||||
// - Style token application
|
||||
//
|
||||
// This is the manual API that the el-ui-compiler's native codegen target will
|
||||
// eventually emit. For now it shows exactly what apps (and the compiler) need
|
||||
// to produce.
|
||||
|
||||
import "../../vessels/el-native/src/main.el"
|
||||
|
||||
// ── State ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
// Global state — holds the widget handles so callbacks can reference them.
|
||||
// In a real component system, these would be scoped to the component instance.
|
||||
|
||||
let g_window: Int = -1
|
||||
let g_label: Int = -1
|
||||
let g_input: Int = -1
|
||||
let g_button: Int = -1
|
||||
let g_counter: Int = 0
|
||||
let g_counter_lbl: Int = -1
|
||||
|
||||
// ── Callbacks ─────────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Callbacks must be top-level functions with the signature:
|
||||
// fn name(widget: Int, data: String) -> Void
|
||||
// The C bridge resolves them by symbol name via dlsym.
|
||||
|
||||
fn on_greet_click(widget: Int, data: String) -> Void {
|
||||
let name: String = widget_get_text(g_input)
|
||||
let greeting: String = if str_len(name) > 0 {
|
||||
"Hello, " + name + "!"
|
||||
} else {
|
||||
"Hello, World!"
|
||||
}
|
||||
widget_set_text(g_label, greeting)
|
||||
}
|
||||
|
||||
fn on_counter_click(widget: Int, data: String) -> Void {
|
||||
let g_counter = g_counter + 1
|
||||
widget_set_text(g_counter_lbl, "Clicks: " + int_to_str(g_counter))
|
||||
}
|
||||
|
||||
fn on_name_change(widget: Int, data: String) -> Void {
|
||||
// Enable the greet button only when there is text in the field.
|
||||
// (data is the current text value)
|
||||
let has_text: Bool = str_len(data) > 0
|
||||
widget_set_disabled(g_button, !has_text)
|
||||
}
|
||||
|
||||
// ── App build ─────────────────────────────────────────────────────────────────
|
||||
|
||||
fn app_build(window: Int) -> Void {
|
||||
// Root vertical stack — full-window content.
|
||||
let root: Int = vstack(0)
|
||||
widget_set_padding_all(root, 24)
|
||||
widget_set_bg_color_hex(root, "#0f172a")
|
||||
widget_set_flex(root, 1)
|
||||
|
||||
// Title label.
|
||||
let title: Int = label("el-native — native widget demo")
|
||||
style_label_heading(title)
|
||||
widget_add_child(root, title)
|
||||
|
||||
// Spacer (empty label acts as a gap).
|
||||
let gap1: Int = label("")
|
||||
widget_set_height(gap1, 16)
|
||||
widget_add_child(root, gap1)
|
||||
|
||||
// Subtitle.
|
||||
let subtitle: Int = label("AppKit controls from el code, no ObjC in the app layer.")
|
||||
style_label_muted(subtitle)
|
||||
widget_add_child(root, subtitle)
|
||||
|
||||
// Divider gap.
|
||||
let gap2: Int = label("")
|
||||
widget_set_height(gap2, 24)
|
||||
widget_add_child(root, gap2)
|
||||
|
||||
// Name input row.
|
||||
let input_row: Int = hstack(8)
|
||||
let name_label: Int = label("Name:")
|
||||
style_label_body(name_label)
|
||||
widget_set_width(name_label, 60)
|
||||
|
||||
let input: Int = text_field("Enter your name…")
|
||||
style_label_body(input)
|
||||
widget_set_flex(input, 1)
|
||||
widget_on_change(input, "on_name_change")
|
||||
let g_input = input // stash handle
|
||||
|
||||
let greet_btn: Int = button("Greet")
|
||||
style_button_primary(greet_btn)
|
||||
widget_set_disabled(greet_btn, true) // disabled until name is typed
|
||||
widget_on_click(greet_btn, "on_greet_click")
|
||||
let g_button = greet_btn
|
||||
|
||||
widget_add_child(input_row, name_label)
|
||||
widget_add_child(input_row, input)
|
||||
widget_add_child(input_row, greet_btn)
|
||||
widget_add_child(root, input_row)
|
||||
|
||||
// Greeting output label.
|
||||
let gap3: Int = label("")
|
||||
widget_set_height(gap3, 12)
|
||||
widget_add_child(root, gap3)
|
||||
|
||||
let greeting: Int = label("Waiting for name…")
|
||||
style_label_body(greeting)
|
||||
widget_set_color_hex(greeting, "#60a5fa")
|
||||
widget_set_font(greeting, "system", 16, true)
|
||||
let g_label = greeting
|
||||
widget_add_child(root, greeting)
|
||||
|
||||
// Counter row.
|
||||
let gap4: Int = label("")
|
||||
widget_set_height(gap4, 24)
|
||||
widget_add_child(root, gap4)
|
||||
|
||||
let counter_row: Int = hstack(12)
|
||||
|
||||
let counter_lbl: Int = label("Clicks: 0")
|
||||
style_label_body(counter_lbl)
|
||||
let g_counter_lbl = counter_lbl
|
||||
|
||||
let counter_btn: Int = button("Click me")
|
||||
style_button_primary(counter_btn)
|
||||
widget_on_click(counter_btn, "on_counter_click")
|
||||
|
||||
widget_add_child(counter_row, counter_lbl)
|
||||
widget_add_child(counter_row, counter_btn)
|
||||
widget_add_child(root, counter_row)
|
||||
|
||||
// Attach root to window.
|
||||
widget_add_child(window, root)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// main.el — Entry point for native-hello.
|
||||
//
|
||||
// Boot sequence:
|
||||
// 1. Initialize the native widget system.
|
||||
// 2. Read the manifest.el app{} block for window config.
|
||||
// 3. Create the window.
|
||||
// 4. Build the widget tree (App.el: app_build).
|
||||
// 5. Show the window.
|
||||
// 6. Start the run loop (never returns).
|
||||
|
||||
import "App.el"
|
||||
|
||||
// ── Boot ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
native_init()
|
||||
|
||||
// Determine the manifest path relative to where we are invoked.
|
||||
// When running from the example directory: manifest.el is at ./manifest.el.
|
||||
// When invoked from elsewhere: pass EL_MANIFEST env var.
|
||||
let manifest_env: String = env("EL_MANIFEST")
|
||||
let manifest_path: String = if str_len(manifest_env) > 0 {
|
||||
manifest_env
|
||||
} else {
|
||||
"manifest.el"
|
||||
}
|
||||
|
||||
// Create window from manifest.
|
||||
let win: Int = window_from_manifest(manifest_path)
|
||||
let g_window = win
|
||||
|
||||
if win < 0 {
|
||||
println("Error: failed to create window. Is EL_TARGET_MACOS defined?")
|
||||
exit_program(1)
|
||||
}
|
||||
|
||||
// Build the widget tree.
|
||||
app_build(win)
|
||||
|
||||
// Show the window.
|
||||
window_show(win)
|
||||
|
||||
// Hand control to AppKit. Never returns.
|
||||
native_run_loop()
|
||||
Reference in New Issue
Block a user