I figured out how to set up a file watcher from your browser to zig

const std =

@import

("std");

// Add Emscripten extern functions

pub extern fn emscripten_run_script(script: [*:0]const u8) void;

pub extern fn emscripten_async_run_script(script: [*:0]const u8, millis: i32) void;

pub extern fn emscripten_run_script_int(script: [*:0]const u8) i32;

pub extern fn emscripten_run_script_string(script: [*:0]const u8) [*:0]const u8;

// Error codes enum

pub const FileWatcherError = error{

FileNotFound,

PermissionDenied,

ConnectionLost,

ReadError,

UserCancelled,

NotConnected,

InvalidFile,

Unknown,

};

// Callback function pointer types

pub const FileChangeCallback = *const fn (content: []const u8) void;

pub const FileErrorCallback = *const fn (err: FileWatcherError) void;

// Global state struct

const State = struct {

change_fn: ?FileChangeCallback,

error_fn: ?FileErrorCallback,

const Self =

@This

();

fn init() Self {

return .{

.change_fn = null,

.error_fn = null,

};

}

};

// Single global instance

var state = State.init();

// C callback implementations

export fn on_file_changed(content: [*:0]const u8) callconv(.C) void {

if (state.change_fn) |callback| {

const content_slice = std.mem.span(content);

callback(content_slice);

}

}

export fn on_file_error(error_code: c_int) callconv(.C) void {

if (state.error_fn) |callback| {

const err = switch (error_code) {

1 => FileWatcherError.FileNotFound,

2 => FileWatcherError.PermissionDenied,

3 => FileWatcherError.ConnectionLost,

4 => FileWatcherError.ReadError,

5 => FileWatcherError.UserCancelled,

6 => FileWatcherError.NotConnected,

7 => FileWatcherError.InvalidFile,

else => FileWatcherError.Unknown,

};

callback(err);

}

}

// The JavaScript code as a string

const js_setup =

\\if (!Module.fileWatcher) {

\\ const watchFile = function(opts) {

\\

\\ const onChange = opts.onChange || function() {};

\\ const onError = opts.onError || function() {};

\\ const interval = opts.interval || 1000;

\\

\\ const ERROR = {

\\ FILE_NOT_FOUND: 1,

\\ PERMISSION_DENIED: 2,

\\ CONNECTION_LOST: 3,

\\ READ_ERROR: 4,

\\ USER_CANCELLED: 5,

\\ NOT_CONNECTED: 6,

\\ INVALID_FILE: 7,

\\ UNKNOWN: 999

\\ };

\\ let fileHandle = null;

\\ let previousContent = null;

\\ let watchInterval = null;

\\

\\ async function readFile() {

\\ try {

\\ if (!fileHandle) throw { code: ERROR.NOT_CONNECTED };

\\ const file = await fileHandle.getFile();

\\ const content = await file.text();

\\ if (content !== previousContent) {

\\ previousContent = content;

\\ onChange(content);

\\ }

\\ } catch (error) {

\\ console.error('bridge error', error);

\\ onError({ code: error.code || http://ERROR.READ_ERROR });

\\ }

\\ }

\\

\\ function startWatching() {

\\ watchInterval = setInterval(readFile, interval);

\\ }

\\

\\ function stopWatching() {

\\ if (watchInterval) {

\\ clearInterval(watchInterval);

\\ watchInterval = null;

\\ }

\\ }

\\

\\ return {

\\ async connect() {

\\ try {

\\ [fileHandle] = await window.showOpenFilePicker();

\\ const file = await fileHandle.getFile();

\\ previousContent = await file.text();

\\ startWatching();

\\ return file;

\\ } catch (error) {

\\ onError({ code: http://error.name === 'AbortError' ? ERROR.USER_CANCELLED : ERROR.UNKNOWN });

\\ return null;

\\ }

\\ },

\\ disconnect() {

\\ fileHandle = null;

\\ previousContent = null;

\\ stopWatching();

\\ }

\\ };

\\ };

\\

\\ Module.fileWatcher = watchFile({

\\ onChange: function(content) {

\\ if (typeof Module._on_file_changed !== 'function') {

\\ console.error('on_file_changed is not available');

\\ return;

\\ }

\\ var lengthBytes = lengthBytesUTF8(content) + 1;

\\ var ptr = _malloc(lengthBytes);

\\ stringToUTF8(content, ptr, lengthBytes);

\\ Module._on_file_changed(ptr);

\\ _free(ptr);

\\ },

\\ onError: function(error) {

\\ if (typeof Module._on_file_error !== 'function') {

\\ console.error('on_file_error is not available');

\\ return;

\\ }

\\ Module._on_file_error(error.code);

\\ }

\\ });

\\}

;

// Public API

pub const FileWatcher = struct {

pub fn init(

change_cb: FileChangeCallback,

error_cb: FileErrorCallback,

) void {

state.change_fn = change_cb;

state.error_fn = error_cb;

emscripten_run_script(js_setup);

}

pub fn connect() void {

emscripten_run_script("if (Module.fileWatcher) Module.fileWatcher.connect();");

}

pub fn disconnect() void {

emscripten_run_script("if (Module.fileWatcher) Module.fileWatcher.disconnect();");

}

};

Previous
Previous

My 4 rules for writing (about technical things).

Next
Next

Design for AI