///|
/// Window size hints
pub(all) enum SizeHint {
  /// Width and height are default size.
  None = 0
  /// Width and height are minimum bounds.
  Min = 1
  /// Width and height are maximum bounds.
  Max = 2
  /// Window size can not be changed by a user.
  Fixed = 3
}

///|
/// An instance of a webview window (managed via IPC).
///
/// Example:
///
/// ```moonbit nocheck
/// let webview = @webview.WebView::new_managed(task_group)
/// webview.set_title("My WebView")
/// webview.set_size(800, 600, @webview.SizeHint::None)
/// webview.set_html("

Hello, World!

") /// webview.terminate() /// ``` struct WebView[X] { handle : WebView_t window_id : Int task_group : @async.TaskGroup[X] } // ── Internal logging ────────────────────────────────────────────────────────── // // log_level controls verbosity of internal [bridge] / [plugin] traces: // 0 silent (default) // 1 info key lifecycle events (command registered, dispatch invoked, // response sent) // 2 verbose full payloads and handler results // // Set automatically by WebView::new_managed(debug=1). Can also be adjusted at // runtime via set_log_level(). ///| let webview_log_level : Ref[Int] = { val: 0 } ///| /// Set the internal log verbosity. /// /// - `0` — silent (default) /// - `1` — info: lifecycle events (registered commands, invocations, responses) /// - `2` — verbose: full request/response payloads fn set_log_level(level : Int) -> Unit { webview_log_level.val = level } ///| /// Emit a log line if `level` ≤ the current log level. /// The prefix is always included so grep filtering works without extra tooling. fn log(level : Int, msg : String) -> Unit { if level <= webview_log_level.val { println(msg) } } ///| /// Creates a managed webview window via native WindowManager (IPC-capable). /// /// Window lifecycle operations (`terminate/set_title/set_size/` /// `navigate/set_html/eval/init`) will be routed through `wm_*` APIs. fn[X] WebView::new_managed( task_group : @async.TaskGroup[X], title? : String = "MoonBit WebView", url? : String = "", width? : Int = 800, height? : Int = 600, debug? : Int = 0, devtools? : Bool = false, frameless? : Bool = false, resizable? : Bool = true, closeable? : Bool = true, always_on_top? : Bool = false, transparent? : Bool = false, title_bar_style? : TitleBarStyle = Default, title_bar_overlay? : Bool = false, traffic_light_position? : (Int, Int) = (-1, -1), ) -> WebView[X] { let create_debug = if devtools && debug <= 0 { 1 } else { debug } let window_id = wm_create_window( @encoding/utf8.encode(title), @encoding/utf8.encode(url), width, height, -1, -1, 0, create_debug, -1, ) guard window_id > 0 else { abort("WebView::new_managed failed: wm_create_window") } let handle = wm_get_handle(window_id) if create_debug > 0 { set_log_level(create_debug) } if devtools { ignore(wm_set_devtools(window_id, 1)) } ignore( wm_set_window_customization( window_id, if frameless { 1 } else { 0 }, if resizable { 1 } else { 0 }, if closeable { 1 } else { 0 }, if always_on_top { 1 } else { 0 }, if transparent { 1 } else { 0 }, title_bar_style, if title_bar_overlay { 1 } else { 0 }, ), ) if traffic_light_position.0 >= 0 && traffic_light_position.1 >= 0 { ignore( wm_set_traffic_light_position( window_id, traffic_light_position.0, traffic_light_position.1, ), ) } { handle, window_id, task_group } } ///| /// Destroys the webview and closes the window. /// Safe to call from a background thread. pub fn[X] WebView::destroy(self : WebView[X]) -> Unit { ignore(wm_destroy_window(self.window_id)) } ///| /// Stops the main event loop. /// Safe to call from a background thread. pub fn[X] WebView::terminate(self : WebView[X]) -> Unit { ignore(wm_terminate_window(self.window_id)) } ///| /// Updates the title of the native window. pub fn[X] WebView::set_title(self : WebView[X], title : String) -> Unit { ignore(wm_set_title(self.window_id, @encoding/utf8.encode(title))) } ///| /// Loads HTML content into the webview. /// /// Example: /// ```moonbit nocheck /// let webview = @webview.WebView::new_managed(task_group) /// webview.set_html("

Hello, World!

") /// webview.terminate() /// ``` pub fn[X] WebView::set_html(self : WebView[X], html : String) -> Unit { ignore(wm_set_html(self.window_id, @encoding/utf8.encode(html))) } ///| /// Register a custom scheme backed by a local root directory for this webview. pub fn[X] WebView::set_custom_protocol( self : WebView[X], scheme : String, root_dir : String, ) -> Unit { ignore( webview_set_custom_protocol( self.handle, @encoding/utf8.encode(scheme), @encoding/utf8.encode(root_dir), ), ) } ///| /// Navigates the webview to the given URL. URL may be a data URI. /// /// Example: /// ```moonbit nocheck /// let webview = @webview.WebView::new_managed(task_group) /// webview.navigate("https://www.example.com") /// webview.navigate("data:text/html,

Hello

") /// webview.terminate() /// ``` pub fn[X] WebView::navigate(self : WebView[X], url : String) -> Unit { ignore(wm_navigate(self.window_id, @encoding/utf8.encode(url))) } ///| /// Navigates one step back in browser history. pub fn[X] WebView::back(self : WebView[X]) -> Unit { self.eval("history.back();") } ///| /// Navigates one step forward in browser history. pub fn[X] WebView::forward(self : WebView[X]) -> Unit { self.eval("history.forward();") } ///| /// Navigates to a specific entry in browser history. /// /// - `delta < 0`: backward /// - `delta > 0`: forward /// - `delta = 0`: reload current entry pub fn[X] WebView::go(self : WebView[X], delta : Int) -> Unit { self.eval("history.go(\{delta});") } ///| /// Reloads the current page using standard browser cache policy. pub fn[X] WebView::reload(self : WebView[X]) -> Unit { self.eval("window.location.reload();") } ///| /// Reloads the current page and requests a revalidation from server. pub fn[X] WebView::reload_force(self : WebView[X]) -> Unit { self.eval("window.location.reload(true);") } ///| /// Returns the raw `WebView_t` handle. pub fn[X] WebView::get_handle(self : WebView[X]) -> WebView_t { self.handle } ///| /// Returns the managed window id. fn[X] WebView::window_id(self : WebView[X]) -> Int { self.window_id } ///| /// Evaluates arbitrary JavaScript code. fn[X] WebView::eval(self : WebView[X], js : String) -> Unit { ignore(wm_eval_js(self.window_id, @encoding/utf8.encode(js))) } ///| /// Injects JavaScript code executed immediately on every page load, /// before `window.onload`. fn[X] WebView::init(self : WebView[X], js : String) -> Unit { ignore(wm_init_js(self.window_id, @encoding/utf8.encode(js))) } ///| /// Updates the size of the native window. /// /// Remarks: /// - `SizeHint::Max` is not supported with GTK 4. /// - GTK 4 can only set a default size early in the window lifecycle. pub fn[X] WebView::set_size( self : WebView[X], width : Int, height : Int, hints : SizeHint, ) -> Unit { ignore(wm_set_size(self.window_id, width, height, hints)) } ///| /// Applies native custom-window style flags at runtime. pub fn[X] WebView::set_window_customization( self : WebView[X], frameless : Bool, resizable : Bool, closeable : Bool, always_on_top : Bool, transparent : Bool, title_bar_style : TitleBarStyle, title_bar_overlay : Bool, ) -> Unit { ignore( wm_set_window_customization( self.window_id, if frameless { 1 } else { 0 }, if resizable { 1 } else { 0 }, if closeable { 1 } else { 0 }, if always_on_top { 1 } else { 0 }, if transparent { 1 } else { 0 }, title_bar_style, if title_bar_overlay { 1 } else { 0 }, ), ) } ///| /// Set macOS traffic-light buttons position. pub fn[X] WebView::set_traffic_light_position( self : WebView[X], x : Int, y : Int, ) -> Unit { ignore(wm_set_traffic_light_position(self.window_id, x, y)) } ///| /// Injects CSS helper classes for Electron-like drag regions. /// /// - `.lepus-drag`: draggable zone (`-webkit-app-region: drag`) /// - `.lepus-titlebar`: titlebar draggable zone (`-webkit-app-region: drag`) /// - `[data-lepus-drag="true"]`: draggable zone (`-webkit-app-region: drag`) /// - `.lepus-no-drag`: interactive zone (`-webkit-app-region: no-drag`) pub fn[X] WebView::enable_custom_titlebar_support(self : WebView[X]) -> Unit { self.init( ( #|(() => { #| if (window.__LEPUS_DRAG_STYLE__) return; #| const style = document.createElement('style'); #| style.textContent = [ #| '.lepus-drag { -webkit-app-region: drag; app-region: drag; user-select: none; }', #| '.lepus-titlebar { -webkit-app-region: drag; app-region: drag; user-select: none; }', #| '[data-lepus-drag=\"true\"] { -webkit-app-region: drag; app-region: drag; user-select: none; }', #| '.lepus-no-drag { -webkit-app-region: no-drag; app-region: no-drag; }', #| ].join(''); #| document.documentElement.appendChild(style); #| window.__LEPUS_DRAG_STYLE__ = true; #|})(); ), ) } ///| /// Injects CSS/JS that keeps page background transparent. pub fn[X] WebView::enable_transparent_background_support( self : WebView[X], ) -> Unit { self.init( ( #|(() => { #| if (window.__LEPUS_TRANSPARENT_BG__) return; #| const style = document.createElement('style'); #| style.textContent = [ #| 'html, body { background: transparent !important; }', #| ].join(''); #| document.documentElement.appendChild(style); #| const apply = () => { #| document.documentElement.style.background = 'transparent'; #| if (document.body) document.body.style.background = 'transparent'; #| }; #| apply(); #| if (document.readyState === 'loading') { #| document.addEventListener('DOMContentLoaded', apply, { once: true }); #| } #| window.__LEPUS_TRANSPARENT_BG__ = true; #|})(); ), ) } ///| /// Minimize native window. pub fn[X] WebView::minimize(self : WebView[X]) -> Unit { ignore(wm_minimize_window(self.window_id)) } ///| /// Maximize native window. pub fn[X] WebView::maximize(self : WebView[X]) -> Unit { ignore(wm_maximize_window(self.window_id)) } ///| /// Restore native window from maximized state. pub fn[X] WebView::unmaximize(self : WebView[X]) -> Unit { ignore(wm_unmaximize_window(self.window_id)) } ///| /// Toggle native maximize state. pub fn[X] WebView::toggle_maximize(self : WebView[X]) -> Unit { ignore(wm_toggle_maximize_window(self.window_id)) } ///| /// Set native fullscreen state. pub fn[X] WebView::set_fullscreen(self : WebView[X], fullscreen : Bool) -> Unit { ignore( wm_set_fullscreen_window(self.window_id, if fullscreen { 1 } else { 0 }), ) } ///| /// Toggle native fullscreen state. pub fn[X] WebView::toggle_fullscreen(self : WebView[X]) -> Unit { ignore(wm_toggle_fullscreen_window(self.window_id)) } ///| /// Start native window drag/move gesture. pub fn[X] WebView::start_drag(self : WebView[X]) -> Unit { ignore(wm_start_drag_window(self.window_id)) } ///| /// Close native window. pub fn[X] WebView::close(self : WebView[X]) -> Unit { ignore(wm_close_window(self.window_id)) } ///| /// Enable or disable developer tools integration at runtime. pub fn[X] WebView::set_devtools(self : WebView[X], enabled : Bool) -> Unit { ignore(wm_set_devtools(self.window_id, if enabled { 1 } else { 0 })) } ///| /// Runs the main event loop until terminated, then destroys the webview. fn[X] WebView::run(self : WebView[X]) -> Unit { ignore(wm_run_window(self.window_id)) self.destroy() }