BonitoBonito

Api​#

Public Functions​#

Bonito.AbstractConnectionIndicatorType
julia
AbstractConnectionIndicator

Abstract type for connection indicators. Custom indicators should subtype this. Implementations should define jsrender(session::Session, indicator::T) where T is the subtype.

source
Bonito.AbstractPasswordStoreType
julia
AbstractPasswordStore

Abstract interface for password storage and authentication.

Required Methods

  • get_user(store, username::String)::Union{User, Nothing}: Retrieve user by username

Provided Methods

  • authenticate(store, username::String, password::String)::Bool: Verify credentials (implemented via get_user)

Implementation Guide

Subtypes only need to implement get_user(). The authenticate() method will automatically:

  1. Call get_user() to retrieve the user

  2. Verify the password using the User's authenticate method

  3. Return true if credentials match, false otherwise

For custom authentication logic (e.g., LDAP), you can override authenticate().

source
Bonito.AppType
julia
App(callback_or_dom; title="Bonito App", loading_page=nothing)
App((session, request) -> DOM.div(...))
App((session::Session) -> DOM.div(...))
App((request::HTTP.Request) -> DOM.div(...))
App(() -> DOM.div(...))
App(DOM.div(...))

Keywords

  • title::String: Browser tab title (default: "Bonito App")

  • indicator::Union{Nothing, AbstractConnectionIndicator}: Connection status indicator (default: nothing)

  • loading_page: A component (e.g. LoadingPage()) to display while the app handler runs. When set, the app DOM is wrapped in an Observable that initially shows the loading page, then asynchronously replaces it with the real content once the handler completes (default: nothing).

Usage

julia
using Bonito
app = App() do
    return DOM.div(DOM.h1("hello world"), js"""console.log('hello world')""")
end

Loading Page

julia
app = App(; loading_page=LoadingPage(text="Initializing...")) do session
    data = expensive_setup()
    return DOM.div(data)
end

If you depend on global observable, make sure to bind it to the session. This is pretty important, since every time you display the app, listeners will get registered to it, that will just continue staying there until your Julia process gets closed. bind_global prevents that by binding the observable to the life cycle of the session and cleaning up the state after the app isn't displayed anymore. If you serve the App via a Server, be aware, that those globals will be shared with everyone visiting the page, so possibly by many users concurrently.

julia
global some_observable = Observable("global hello world")
App() do session::Session
    bound_global = bind_global(session, some_observable)
    return DOM.div(bound_global)
end
source
Bonito.AssetType
julia
Asset(path_or_url; name=nothing, es6module=false, check_isfile=false, bundle_dir=nothing, mediatype=:inferred)

Represent an asset (JavaScript, CSS, image, etc.) that can be included in a Bonito DOM.

Arguments

  • path_or_url: Local file path or URL to the asset

  • name: Optional name for the asset. For JS assets, this becomes the global variable name when loaded. Defaults to the filename without extension for JS files (e.g., "ace.js" → "ace")

  • es6module: Whether this is an ES6 module that needs bundling (default: false)

  • check_isfile: Verify that local files exist (default: false)

  • bundle_dir: Directory for bundled ES6 modules (default: inferred)

  • mediatype: Media type symbol (:js, :css, :png, etc.). Auto-detected from extension if not specified

JavaScript Asset Loading

Non-module scripts (es6module=false)

For non-module JavaScript assets, interpolating the asset in JS code creates a Promise that resolves with the global object:

julia
ace_asset = Asset("https://cdn.jsdelivr.net/gh/ajaxorg/ace-builds/src-min/ace.js")
# name defaults to "ace" (inferred from filename)

js"""
    $(ace_asset).then(ace => {
        // ace object is now available
        const editor = ace.edit(element);
    });
"""

To override the global name:

julia
Asset("https://example.com/mylibrary.js"; name="MyLib")

ES6 modules (es6module=true)

For ES6 modules, use the ES6Module(path) constructor which automatically sets es6module=true and bundles the module with its dependencies. ES6 modules also support the .then() syntax:

julia
mod = ES6Module("path/to/module.js")  # name defaults to "module"

js"""
    $(mod).then(exports => {
        // ES6 module exports are now available
        exports.someFunction();
    });
"""

Fields

  • name::Union{Nothing, String}: Asset name (used as global variable name for JS assets)

  • es6module::Bool: Whether this is an ES6 module

  • media_type::Symbol: Type of asset (:js, :css, :png, etc.)

  • online_path::String: URL if asset is hosted online

  • local_path::Union{String, Path}: Local file system path

  • bundle_file::Union{String, Path}: Path to bundled file (ES6 modules only)

  • bundle_data::Vector{UInt8}: Bundled file contents (ES6 modules only)

  • content_hash::RefValue{String}: Hash of bundled content (ES6 modules only)

See Also

  • ES6Module(path): Convenience constructor for ES6 modules with automatic bundling

source
Bonito.ChoicesBoxType
julia
ChoicesBox(options; initial_value="", choicejsparams=ChoicesJSParams(...), attributes...)

A combo box widget using the Choices.js library that allows both text input and selection from predefined options. Users can either select from the dropdown list or type their own custom values.

Fields

  • options::Observable{Vector{String}}: Available dropdown options

  • value::Observable{String}: Current selected or typed value

  • choicejsparams::ChoicesJSParams: Choices.js configuration parameters

  • attributes::Dict{Symbol, Any}: DOM attributes applied to the element

    ChoicesBox(options; initial_value="", choicejsparams=ChoicesJSParams(...), attributes...)

Constructor for creating a ChoicesBox widget.

Arguments

  • options: Vector or Observable of string options for the dropdown

  • initial_value="": Initial selected value

  • choicejsparams=ChoicesJSParams(searchPlaceholderValue="Type here..."): Choices.js configuration

  • attributes...: Additional DOM attributes

Returns

  • ChoicesBox: A configured combo box widget instance

Example

julia
App() do
    # Create a combo box with some sample options
    fruits = ["Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape"]

    # Configure Choices.js parameters
    params = ChoicesJSParams(
        searchPlaceholderValue="Type to search fruits...",
        searchEnabled=true,
        shouldSort=true,
        searchResultLimit=5
    )
    combobox = ChoicesBox(fruits;
        initial_value="Apple",
        choicejsparams=params
    )

    # Display selected value
    selected_display = map(combobox.value) do value
        isnothing(value) ? "No selection" : "Selected: $value"
    end

    # Handle value changes
    on(combobox.value) do value
        @info "ComboBox value changed to: $value"
    end

    return DOM.div(
        DOM.h2("Choices.js ComboBox Example"),
        DOM.p("This combo box allows you to select from predefined options or type your own:"),
        combobox,
        DOM.p(selected_display, style="margin-top: 20px; font-weight: bold;")
    )
end

source
Bonito.ChoicesJSParamsType
julia
ChoicesJSParams(; kwargs...)

Wrapper struct for parameters to the ChoicesJS Choices constructor.

Parameters include search functionality, rendering options, and behavior settings for the Choices.js library. See the official documentation for complete parameter reference: https://github.com/Choices-js/Choices/blob/main/README.md

Fields

  • addItems::Bool: Allow adding of items (default: true)

  • itemSelectText::String: Text shown when hovering over selectable items

  • placeholder::Bool: Show placeholder text (default: true)

  • placeholderValue::String: Placeholder text to display

  • removeItemButton::Bool: Show remove button on items (default: false)

  • renderChoiceLimit::Int: Limit choices rendered (-1 for no limit)

  • searchEnabled::Bool: Enable search functionality (default: true)

  • searchPlaceholderValue::String: Search input placeholder text

  • searchResultLimit::Int: Limit search results (default: 4)

  • shouldSort::Bool: Sort choices alphabetically (default: true)

source
Bonito.CodeEditorMethod
julia
CodeEditor(language::String; initial_source="", theme="chrome", editor_options...)

Defaults for editor_options:

julia
(
    autoScrollEditorIntoView = true,
    copyWithEmptySelection = true,
    wrapBehavioursEnabled = true,
    useSoftTabs = true,
    enableMultiselect = true,
    showLineNumbers = false,
    fontSize = 16,
    wrap = 80,
    mergeUndoDeltas = "always"
)

The content of the editor (as a string) is updated in editor.onchange::Observable.

source
Bonito.ConnectionIndicatorType
julia
ConnectionIndicator <: AbstractConnectionIndicator

Default connection indicator showing connection status as an LED-like circle. See the full documentation with ?ConnectionIndicator after loading Bonito.

source
Bonito.DropdownType
julia
Dropdown(options; index=1, option_to_string=string, style=Styles(), dom_attributes...)

A simple Dropdown, which can be styled via the style::Styles attribute, style=nothing turns off the default Bonito styling.

Example

julia
App() do
    style = Styles(
        CSS("font-weight" => "500"),
        CSS(":hover", "background-color" => "silver"),
        CSS(":focus", "box-shadow" => "rgba(0, 0, 0, 0.5) 0px 0px 5px"),
    )
    dropdown = Dropdown(["a", "b", "c"]; index=2, style=style)
    on(dropdown.value) do value
        @info value
    end
    return dropdown
end
source
Bonito.FolderServerType
julia
FolderServer(folder::String)

A simple file server that serves files from the specified folder. Example with a static site generated with Bonito:

julia
# Build a static site
app = App(()-> DOM.div("Hello World"));
routes = Routes(
    "/" => app
    # Add more routes as needed
)
export_static("build", routes)
# Serve the static site from the build folder
server = Bonito.Server("0.0.0.0", 8982)
route!(server, r".*" => FolderServer("build"))
source
Bonito.KeyedListMethod
julia
KeyedList(items::Observable{Vector{T}}; key = hash) -> KeyedList

T must have a Bonito.jsrender(::Session, ::T) method. key(item) derives the stable identity used for diffing. Default hash works for widgets held by stable instance across rebuilds (cache them in a Dict keyed by their natural id and re-emit the same instances each render).

source
Bonito.LoadingPageType
julia
LoadingPage(; text="Loading Bonito App", spinner=RippleSpinner(), style=Styles())

A customizable loading page component that displays a centered spinner and text. This is used as the default loading_content for App to show while the app is initializing.

Arguments

  • text::String: The text to display below the spinner (default: "Loading Bonito App")

  • spinner: The spinner component to display (default: RippleSpinner())

  • style::Styles: Custom styles for the container (default: Styles())

Example

julia
app = App() do
    return DOM.div("Hello World")
end

# With custom loading page
app = App(; loading_page=LoadingPage(text="Please wait...", spinner=RippleSpinner(width=80))) do session
    return DOM.div("Hello World")
end
source
Bonito.NoServerType

We don't serve files and include anything directly as raw bytes. Interpolating the same asset many times, will only upload the file to JS one time though.

source
Bonito.ProtectedRouteType
julia
ProtectedRoute{T, PS <: AbstractPasswordStore}

A wrapper that adds HTTP Basic Authentication to any type that implements apply_handler. Can wrap Bonito.App, FolderServer, or any other handler type.

Security Notes

  • REQUIRES HTTPS: HTTP Basic Auth sends credentials with every request. Without HTTPS, credentials are transmitted in plaintext (Base64 is NOT encryption).

  • Experimental: This is a simple implementation for basic use cases with no security guarantees.

  • Rate Limiting: Built-in protection against brute force attacks (max 5 attempts per IP per minute).

  • PBKDF2 Password Hashing: Uses PBKDF2-HMAC-SHA256 with 10,000 iterations and random salt.

  • Extensible: Use custom AbstractPasswordStore implementations for multi-user or database-backed auth.

Fields

  • handler::T: The wrapped handler (e.g., Bonito.App, FolderServer, etc.)

  • password_store::PS: Password store implementing AbstractPasswordStore interface

  • realm::String: Authentication realm name (default: "Protected Area")

  • failed_attempts::Dict{String, Vector{Float64}}: IP -> timestamps of failed attempts

  • max_attempts::Int: Maximum failed attempts allowed (default: 5)

  • lockout_window::Float64: Time window in seconds for rate limiting (default: 60.0)

  • auth_required_handler: Handler for 401 responses (default: built-in HTML page)

  • rate_limited_handler: Handler for 429 responses (default: built-in HTML page)

Example

julia
# Create app
app = App() do
    return DOM.h1("Hello World")
end

# Create password store
store = SingleUser("admin", "secret123")

# Create protected route
protected_app = ProtectedRoute(app, store)

# With custom error pages
auth_page = App() do
    return DOM.div(
        DOM.h1("Authentication Required"),
        DOM.p("Please log in to access this resource")
    )
end

rate_limit_page = App() do
    return DOM.div(
        DOM.h1("Too Many Attempts"),
        DOM.p("Please wait before trying again")
    )
end

protected_app = ProtectedRoute(app, store;
                               auth_required_handler=auth_page,
                               rate_limited_handler=rate_limit_page)

# Add to server (MUST use HTTPS in production!)
server = Bonito.Server("0.0.0.0", 8443)  # Use SSL/TLS
route!(server, "/" => protected_app)
source
Bonito.RichTextType
julia
RichText(data::Vector{UInt8})
RichText(str::String)

A type wrapping bytes or a string that may contain ANSI escape codes. When rendered in Bonito, ANSI codes are converted to styled HTML via ANSIColoredPrinters.

source
Bonito.SessionType

A web session — the root of a session tree, or a subsession attached to a parent. The parent_or_root field encodes which: a RootSession{Con} for a root, a parent Session{Con} for a subsession (isroot(s) discriminates).

Subsessions are intentionally lightweight: they share the root's connection, inbox, locks, and msgpack scratch via the parent chain. They allocate only their own per-render state (assetserver handle, onclose, session_objects, imports, stylesheets, etc.).

source
Bonito.SessionMethod
julia
Session(parent_session::Session; kwargs...) -> Session

Create a sub-session attached to parent_session. Sub-sessions reuse the root's connection, inbox, locks, and msgpack scratch via the parent chain; only their per-render state (assetserver handle, onclose, session_objects, imports, stylesheets) is allocated fresh. No inbox task is spawned for subs.

source
Bonito.SessionMethod
julia
Session(connection::FrontendConnection=default_connection(); kwargs...) -> Session

Construct a new root session. Allocates a fresh RootSession (holding the connection, inbox, locks, msgpack scratch, etc.), spawns the inbox-reader task, and returns the wrapping Session.

source
Bonito.SingleUserType
julia
SingleUser <: AbstractPasswordStore

Simple password store for single-user authentication.

Fields

  • user::User: The single user

source
Bonito.SingleUserMethod
julia
SingleUser(username::String, password::String; iterations::Int=10_000)

Create a single-user password store with automatic password hashing.

Example

julia
store = SingleUser("admin", "secret123")
authenticated = authenticate(store, "admin", "secret123")  # true
source
Bonito.StylableSliderMethod
julia
StylableSlider(
    range::AbstractVector;
    value=first(range),
    slider_height=15,
    thumb_width=slider_height,
    thumb_height=slider_height,
    track_height=slider_height / 2,
    track_active_height=track_height + 2,
    backgroundcolor="transparent",
    track_color="#eee",
    track_active_color="#ddd",
    thumb_color="#fff",
    style::Styles=Styles(),
    track_style::Styles=Styles(),
    thumb_style::Styles=Styles(),
    track_active_style::Styles=Styles(),
)

Creates a Stylable Slider, where the basic attributes are easily custimizable via keyword arguments, while the more advanced details can be styled via the style, track_style, thumb_style and track_active_style arguments with the whole might of CSS. This does not use <input type="range"> but is a custom implementation using <div>s javascript, since it is not easily possible to style the native slider in a cross-browser way. For using pure HTML sliders, use Bonito.Slider.

Example

julia
App() do
    Bonito.StylableSlider(
        1:10;
        value=5,
        slider_height=20,
        track_color="lightblue",
        track_active_color="#F0F8FF",
        thumb_color="#fff",
        style=Styles(
            CSS("hover", "background-color" => "lightgray"),
            CSS("border-radius" => "0px"),
        ),
        track_style=Styles(
            "border-radius" => "3px",
            "border" => "1px solid black",
        ),
        thumb_style=Styles(
            "border-radius" => "3px",
            "border" => "1px solid black",
        ),
    )
end
source
Bonito.StylesType
julia
Styles(css::CSS...)

Creates a Styles object, which represents a Set of CSS objects. You can insert the Styles object into a DOM node, and it will be rendered as a <style> node. If you assign it directly to DOM.div(style=Style(...)), the styling will be applied to the specific div. Note, that per Session, each unique css object in all Styles across the session will only be rendered once. This makes it easy to create Styling inside of components, while not worrying about creating lots of Style nodes on the page. There are a two more convenience constructors to make Styles a bit easier to use:

julia
Styles(pairs::Pair...) = Styles(CSS(pairs...))
Styles(priority::Styles, defaults...) = merge(Styles(defaults...), priority)

For styling components, it's recommended, to always allow user to merge in customizations of a Style, like this:

julia
function MyComponent(; style=Styles())
    return DOM.div(style=Styles(style, "color" => "red"))
end

All Bonito components are stylable this way.

Info

Why not Hyperscript.Style? While the scoped styling via Hyperscript.Style is great, it makes it harder to create stylable components, since it doesn't allow the deduplication of CSS objects across the session. It's also significantly slower, since it's not as specialized on the deduplication and the camelcase keyword to css attribute conversion is pretty costly. That's also why CSS uses pairs of strings instead of keyword arguments.

source
Bonito.TerminalOutputType
julia
TerminalOutput(; style=Styles())

A widget for displaying terminal output with ANSI color support. Supports two modes:

  1. Static: Render a RichText value directly.

  2. Streaming: Push new content incrementally via the content observable.

Use push!(widget, bytes) or push!(widget, str) to append content, and empty!(widget) to clear.

Example

julia
# Static rendering
output = TerminalOutput()
push!(output, "\e[32mHello\e[0m \e[31mWorld\e[0m")

# Or render directly from RichText
jsrender(session, RichText("\e[1mbold text\e[0m"))
source
Bonito.UserType
julia
User

Represents a user with authentication credentials.

Fields

  • username::String: The username

  • password_hash::Vector{UInt8}: PBKDF2 derived key

  • salt::Vector{UInt8}: Random salt for password hashing

  • iterations::Int: PBKDF2 iteration count

  • metadata::Dict{String, Any}: Optional user metadata (roles, permissions, etc.)

source
Bonito.UserMethod
julia
User(username::String, password::String; iterations::Int=10_000, metadata::Dict{String, Any}=Dict{String, Any}())

Create a new user with automatic password hashing using PBKDF2.

source
Bonito.WebSocketConnectionMethod
julia
handles a new websocket connection to a session
source
WidgetsBase.ButtonType
julia
Button(name; style=Styles(), dom_attributes...)

A simple button, which can be styled a style::Styles. Set kwarg style=nothing to turn off the default Bonito styling.

Example

julia
App() do
    style = Styles(
        CSS("font-weight" => "500"),
        CSS(":hover", "background-color" => "silver"),
        CSS(":focus", "box-shadow" => "rgba(0, 0, 0, 0.5) 0px 0px 5px"),
    )
    button = Button("Click me"; style=style)
    on(button.value) do click::Bool
        @info "Button clicked!"
    end
    return button
end
source
WidgetsBase.CheckboxType
julia
Checkbox(default_value; style=Styles(), dom_attributes...)

A simple Checkbox, which can be styled via the style::Styles attribute.

source
WidgetsBase.NumberInputType
julia
NumberInput(default_value; style=Styles(), dom_attributes...)

A simple NumberInput, which can be styled via the style::Styles attribute, style=nothing turns off the default Bonito styling.

Example

julia
App() do
    style = Styles(
        CSS("font-weight" => "500"),
        CSS(":hover", "background-color" => "silver"),
        CSS(":focus", "box-shadow" => "rgba(0, 0, 0, 0.5) 0px 0px 5px"),
    )
    numberinput = NumberInput(0.0; style=style)
    on(numberinput.value) do value::Float64
        @info value
    end
    return numberinput
end
source
WidgetsBase.TextFieldType
julia
TextField(default_text; style=Styles(), dom_attributes...)

A simple TextField, which can be styled via the style::Styles attribute, style=nothing turns off the default Bonito styling.

Example

julia
App() do
    style = Styles(
        CSS("font-weight" => "500"),
        CSS(":hover", "background-color" => "silver"),
        CSS(":focus", "box-shadow" => "rgba(0, 0, 0, 0.5) 0px 0px 5px"),
    )
    textfield = TextField("write something"; style=style)
    on(textfield.value) do text::String
        @info text
    end
    return textfield
end
source
Bonito.CardMethod
julia
Card(
    content;
    style::Styles=Styles(),
    backgroundcolor=RGBA(1, 1, 1, 0.2),
    shadow_size="0 4px 8px",
    padding="12px",
    margin="2px",
    shadow_color=RGBA(0, 0, 0.2, 0.2),
    width="auto",
    height="auto",
    border_radius="10px",
    div_attributes...,
)

A Card is a container with a shadow and rounded corners. It is a good way to group elements together and make them stand out from the background. One can easily style them via the above keyword arguments or via the style argument with any CSS attribute.

Example

julia
    App() do
        Card(
            DOM.h1("This is a card");
            width="200px",
            height="200px",
            backgroundcolor="white",
            shadow_size="0 0 10px",
            shadow_color="blue",
            padding="20px",
            margin="20px",
            border_radius="20px",
            style = Styles(
                CSS("hover", "background-color" => "lightgray")
            )
        )
    end
source
Bonito.CenteredMethod
julia
Centered(content; style=Styles(), grid_attributes...)

Creates an element where the content is centered via Grid.

source
Bonito.ColMethod
julia
Col(elems...; grid_attributes...)

Places objects in a column, based on Grid.

source
Bonito.DocumenterBonitoMethod
julia
DocumenterBonito(; kwargs...)

Documenter.jl output format that renders a documentation site with Bonito, styled after VitePress. Pass it to the format keyword of Documenter.makedocs:

julia
using Documenter, Bonito
makedocs(
    sitename = "MyPackage",
    format = Bonito.DocumenterBonito(; repo = "github.com/me/MyPackage.jl"),
    pages = [...],
)

The implementation lives in the BonitoDocumenterExt package extension and is only available once Documenter is loaded. See the extension for the full list of keyword arguments.

source
Bonito.ES6ModuleMethod
julia
ES6Module(path)

Create an ES6 module asset that will be bundled using Deno.

ES6 modules are automatically bundled with their dependencies when first loaded. Interpolating an ES6Module in JavaScript code returns a Promise that resolves to the module's exports.

Example

julia
THREE = ES6Module("https://unpkg.com/three@0.136.0/build/three.js")

js"""
$(THREE).then(module => {
    // Use the module
    const scene = new module.Scene();
})
"""

Rebundling

Bonito tracks the timestamp of the main module file and will automatically rebundle if it detects changes. However, changes to imported/included files (e.g., Session.js imported by Bonito.js) are not tracked.

To force a rebundle when you've modified an included file, delete the bundle file:

julia
mod = ES6Module("path/to/module.js")
rm(mod.bundle_file)  # Bonito will rebundle on next use

For Bonito's internal JavaScript:

julia
rm(Bonito.BonitoLib.bundle_file)
source
Bonito.GridMethod
julia
Grid(
    elems...;
    gap="10px",
    width="100%",
    height="100%",
    # All below Attributes are set to the default CSS values:
    columns="none",
    rows="none",
    areas="none",
    justify_content="normal",
    justify_items="legacy",
    align_content="normal",
    align_items="legacy",
    style::Styles=Styles(),
    div_attributes...,
)

A Grid is a container that lays out its children in a grid, based on the powerful css display: grid property.

source
Bonito.LabeledMethod
julia
Labeled(object, label; label_style=Styles(), attributes...)

A Labeled container with a simople layout to put a label next to an object.

julia
App() do
    label_style = Styles(
        "color" => "white",
        "padding" => "3px",
        "font-size" => "1.5rem",
        "text-shadow" => "0px 0px 10px black, 1px 1px 3px black")
    slider = StylableSlider(1:10)
    Card(Labeled(slider, slider.value; label_style=label_style, width="auto"); backgroundcolor="gray")
end
source
Bonito.PageMethod
julia
Page(;
    offline=false, exportable=true,
    connection::Union{Nothing, FrontendConnection}=nothing,
    server_config...
)

A Page can be used for resetting the Bonito state in a multi page display outputs, like it's the case for Pluto/IJulia/Documenter. For Documenter, the page needs to be set to exportable=true, offline=true, but doesn't need to, since Page defaults to the most common parameters for known Packages. Exportable has the effect of inlining all data & js dependencies, so that everything can be loaded in a single HTML object. offline=true will make the Page not even try to connect to a running Julia process, which makes sense for the kind of static export we do in Documenter. For convenience, one can also pass additional server configurations, which will directly get put into configure_server!(;server_config...). Have a look at the docs for configure_server! to see the parameters.

source
Bonito.RowMethod
julia
Row(elems...; grid_attributes...)

Places objects in a row, based on Grid.

source
Bonito.ansi_to_htmlMethod
julia
ansi_to_html(bytes::AbstractVector{UInt8}) -> String

Convert raw bytes containing ANSI escape codes to an HTML string using ANSIColoredPrinters.HTMLPrinter.

source
Bonito.append_html!Method
julia
append_html!(widget::TerminalOutput, html::String)

Append pre-converted HTML directly to the widget's content. Use this when ANSI-to-HTML conversion has already been performed (e.g. by ansi_to_html).

source
Bonito.bonito_parserMethod
julia
bonito_parser(; kwargs...)

Create a CommonMark parser with all extensions enabled (math, tables, admonitions, footnotes, strikethrough, raw HTML).

julia
bonito_parser(source::String; kwargs...)

Parse a markdown string with the full-featured parser.

source
Bonito.cleanup_globalsMethod
julia
cleanup_globals()

Cleans up global state (servers, sessions, tasks) for precompilation compatibility. On Julia 1.11+, this is called automatically via atexit (which runs before serialization). On Julia 1.10, this must be called manually after precompilation workloads.

source
Bonito.commonmark_to_domMethod
julia
commonmark_to_dom(ast::CommonMark.Node; replacements=Dict())

Walk a CommonMark AST and produce Bonito DOM elements.

Uses a stack-based approach:

  • On entering a container node: push a new children collector

  • On exiting a container node: pop children, create DOM element, push to parent

  • For leaf nodes: create element immediately, push to current collector

replacements is a Dict{Type, Function} mapping CommonMark node types (e.g. CommonMark.CodeBlock, CommonMark.Image) to functions that receive the CommonMark.Node and return either a DOM element or nothing (to use default).

source
Bonito.configure_server!Method
julia
configure_server!(;
        listen_url::String=SERVER_CONFIGURATION.listen_url[],
        listen_port::Integer=SERVER_CONFIGURATION.listen_port[],
        forwarded_port::Integer=listen_port,
        proxy_url=nothing,
        content_delivery_url=nothing
    )

Configures the parameters for the automatically started server.

julia
Parameters:

* listen_url=SERVER_CONFIGURATION.listen_url[]
    The address the server listens to.
    must be 0.0.0.0, 127.0.0.1, ::, ::1, or localhost.
    If not set differently by an ENV variable, will default to 127.0.0.1

* listen_port::Integer=SERVER_CONFIGURATION.listen_port[],
    The Port to which the default server listens to
    If not set differently by an ENV variable, will default to 9384

* forwarded_port::Integer=listen_port,
    if port gets forwarded to some other port, set it here!

* proxy_url=nothing
    The url from which the server is reachable, used to declare resources in Bonitos HTML and to establish a websocket connection.
    Setting it to `""` or `nothing` will use the url the server listens to.
    So, if `listen_url="127.0.0.1"`, this will default to http://localhost:forwarded_port (same as `local_url(server, "")`).
    You can also set this to `"."` to use relative urls, e.g. for accessing the webpage on a local network, or when serving it online with your own server.
    This is the preferred option for serving a whole website via Bonito, where you dont know in advanced where the page will be served.
    If it's more complicated, e.g. when the HTML is served on a different url from the url to proxy through to the Bonito server,
     a full URL needs to set, e.g. `proxy_url=https://bonito.makie.org`.
source
Bonito.evaljsMethod
julia
evaljs(session::Session, jss::JSCode)

Evaluate a javascript script in session.

source
Bonito.evaljs_valueMethod
julia
evaljs_value(session::Session, js::JSCode)

Evals js code and returns the jsonified value. Blocks until value is returned. May block indefinitely, when called with a session that doesn't have a connection to the browser.

source
Bonito.export_staticMethod
julia
export_static(html_file::Union{IO, String}, app::App)
export_static(folder::String, routes::Routes)

Exports the app defined by app with all its assets a single HTML file. Or exports all routes defined by routes to folder.

source
Bonito.get_metadataFunction
julia
get_metadata(session, key::Symbol)
get_metadata(session, key::Symbol, default)

Get metadata from the root session. Returns nothing or the provided default if key doesn't exist. Metadata is stored on the root session and shared across all child sessions.

source
Bonito.has_ansi_codesMethod
julia
has_ansi_codes(str::AbstractString) -> Bool

Check whether a string contains ANSI escape sequences.

source
Bonito.interactive_serverFunction
julia
interactive_server(f, paths, modules=[]; url="127.0.0.1", port=8081, all=true)

Revise base server that will serve a static side based on Bonito and will update on any code change!

Usage:

julia
using Revise, Website
using Website.Bonito

# Start the interactive server and develop your website!
routes, task, server = interactive_server(Website.asset_paths()) do
    return Routes(
        "/" => App(index, title="Makie"),
        "/team" => App(team, title="Team"),
        "/contact" => App(contact, title="Contact"),
        "/support" => App(support, title="Support")
    )
end

# Once everything looks good, export the static site
dir = joinpath(@__DIR__, "docs")
# only delete the bonito generated files
rm(joinpath(dir, "bonito"); recursive=true, force=true)
Bonito.export_static(dir, routes)

For the complete code, visit the Makie website repository which is using Bonito: MakieOrg/Website

source
Bonito.linkjsMethod
julia
linkjs(session::Session, a::Observable, b::Observable)

for an open session, link a and b on the javascript side. This will also Link the observables in Julia, but only as long as the session is active.

source
Bonito.onjsMethod
julia
onjs(session::Session, obs::Observable, func::JSCode)

Register a javascript function with session, that get's called when obs gets a new value. If the observable gets updated from the JS side, the calling of func will be triggered entirely in javascript, without any communication with the Julia session.

source
Bonito.rebundle!Method
julia
rebundle!(asset::Asset)

Programmatically drop asset's cached bundle so the next request re-bundles from source. You rarely need to call this: bundling is automatic.

For an ES6Module(...), Bonito writes a <name>.bundled.js next to the source and serves it. On every request it re-bundles when the bundle is missing or older than the source (see needs_bundling). So the normal dev loop is just:

  • Edit the .js source → the bundle's mtime is now stale → it re-bundles on the next page load. Nothing else to do.

  • Delete the <name>.bundled.js file (e.g. in js_dependencies/) → it is regenerated from source on the next load. This is the simplest way to force a fresh bundle, e.g. after pulling changes or when a bundle looks corrupt.

rebundle! does the same thing in code — it removes the on-disk bundle (asset.bundle_file) and the in-memory cached bytes (asset.bundle_data) under the asset's bundle lock — for the case where you can't (or don't want to) touch the filesystem, e.g. invalidating a bundle from a running session:

julia
const ChartLib = Bonito.ES6Module("chart.js")
# … programmatically regenerate without editing/deleting files …
Bonito.rebundle!(ChartLib)   # next page reload picks up the new source

No-op for non-ES6 assets (they have no bundle to drop).

source
Bonito.set_metadata!Method
julia
set_metadata!(session, key::Symbol, value)

Set metadata on the root session. Returns the value. Metadata is stored on the root session and shared across all child sessions.

source

Private Functions​#

Bonito.AbstractWebsocketConnectionType

Websocket based connection type

source
Bonito.CachedEntryType
julia
CachedEntry(object, owners)

Value stored in root.session_objects for objects that have been serialized to JS and cached for this connection. owners is the set of session ids (root + any sub) that have registered the key.

Lifetime rule: the entry survives as long as owners (filtered to sessions still present in the live tree via get_session(root, id)) is non-empty. Sub-close decrements its id; root-close drops the whole cache. The filter step makes this self-healing — if a session vanished without a clean close, its dead id gets pruned and the entry can still be reclaimed.

INVARIANT: every read or mutation of an entry's owners field happens under root.deletion_lock (the same lock that gates all session_objects access). Plain Set is fine because the lock is the synchronization boundary; per-entry locking would just be overhead.

source
Bonito.CleanupPolicyType
julia
abstract type CleanupPolicy end

You can create a custom cleanup policy by subclassing this type. Implementing the should_cleanup and allow_soft_close methods is required. You can also implement set_cleanup_time!if it makes sense for your policy.

julia
function should_cleanup(policy::MyCleanupPolicy, session::Session)

function allow_soft_close(policy::MyCleanupPolicy)

function set_cleanup_time!(policy::MyCleanupPolicy, time_in_hrs::Real)

This is quite low level, and you implementaiton should probably start by copying DefaultCleanupPolicy.

source
Bonito.CommonMarkDOMType
julia
CommonMarkDOM

Wrapper around a DOM element produced by commonmark_to_dom. This is returned by string_to_markdown when CommonMark replacements are used, and jsrender dispatches on it to render the DOM.

source
Bonito.DefaultCleanupPolicyType
julia
mutable struct DefaultCleanupPolicy <: CleanupPolicy
    session_open_wait_time=30
    cleanup_time=0.0
end

This is the default cleanup policy. It closes sessions after session_open_wait_time seconds (default 30) if the browser didn't connect back to the displayed session. It also closes sessions after cleanup_time hours (default 0) if the session closes cleanly, indicating that the browser may reconnect if a tab is later restored. It returns true for allowsoftclose(...) when cleanup_time is non-zero.

source
Bonito.DualWebsocketMethod
julia
handles a new websocket connection to a session
source
Bonito.FrontendConnectionType

Inteface for FrontendConnection

julia
struct MyConnection <: FrontendConnection
end

Needs to have a constructor with 0 arguments:

julia
MyConnection()

Needs to overload Base.write for sending binary data

julia
Base.write(connection::MyConnection, bytes::AbstractVector{UInt8})

Needs to implement isopen to indicate status of connection

julia
Base.isopen(c::MyConnection)

Setup connection will be called before rendering any dom with session. The return value will be inserted into the DOM of the rendered App and can be used to do the JS part of opening the connection.

julia
Bonito.setup_connection(session::Session{IJuliaConnection})::Union{JSCode, Nothing}

One can overload use_parent_session, to turn on rendering dom objects inside sub-sessions while keeping one parent session managing the connection alive. This is handy for IJulia/Pluto, since the parent session just needs to be initialized one time and can stay active and globally store objects used multiple times across doms.

Julia
Bonito.use_parent_session(::Session{MyConnection}) = false/false
source
Bonito.JSCodeType

Javascript code that supports interpolation of Julia Objects. Construction of JSCode via string macro:

julia
jsc = js"console.log($(some_julia_variable))"

This will decompose into:

julia
jsc.source == [JSString("console.log("), some_julia_variable, JSString(""")]
source
Bonito.JSExceptionMethod

Creates a Julia exception from data passed to us by the frondend!

source
Bonito.JSStringType

The string part of JSCode.

source
Bonito.JSUpdateObservableType

Functor to update JS part when an observable changes. We make this a Functor, so we can clearly identify it and don't sent any updates, if the JS side requires to update an Observable (so we don't get an endless update cycle)

source
Bonito.PackIOPoolType
julia
PackIOPool

Per-tree pool of SessionIO buffers (on RootSession). Usually holds one IO reused for every message; re-entrant/concurrent packs check out their own. Bounds idle scratch memory by concurrency (≈1), not by sub-session count.

source
Bonito.RootSessionType

Root-only session state. One RootSession exists per real connection (per session tree). Holds the OS-level connection, the receive inbox, deletion lock, msgpack scratch IO, and other resources that subsessions share via their parent chain rather than duplicating per-sub.

A subsession's "where do I belong" is its parent Session; a root's is this RootSession. The two cases are mutually exclusive — encoded by the Session.parent_or_root::Union{RootSession{Con}, Session{Con}} field.

source
Bonito.SessionIOType
julia
SessionIO <: IO

Reusable scratch IO for streaming nested msgpack Extension payloads. Checked out of a PackIOPool for one top-level message, so only one task writes to it at a time (no per-IO lock / in_use guard needed).

  • output — destination IOBuffer (set per top-level pack).

  • scratches — per-depth reusable IOBuffers; reset (capacity preserved) via reset_for_reuse!.

  • depth — nesting depth; 0output, >=1scratches[depth].

source
Bonito.TableType

A simple wrapper for types that conform to the Tables.jl Table interface, which gets rendered nicely!

source
Bonito.TrackingOnlyType
julia
TrackingOnly(key::String)

Represents a cache entry that only needs to be tracked in a session but already exists in the global cache. When deserialized in JS, it self-registers the key to the session's tracked objects without adding anything to the global cache.

source
Base.isreadyMethod
julia
isready(session::Session; throw::Bool=true) -> Bool

true once the frontend has connected (connection_ready flipped) and the underlying connection is still open. false once the session has closed.

If session.init_error[] is set (frontend init failed, or rendered_dom recorded a render-time error), the default behavior is to consume + throw that exception so the caller surfaces the real cause instead of seeing a silent closed session. Pass throw=false to opt out — used by sites that only care about the connection state (logging via show_session, the heartbeat ping, _send's queue-or-write decision, close's own evaljs guard, etc.). Opting out is explicit: any caller asking "is this session ready for usage" without that annotation cannot accidentally ignore a recorded failure.

source
Base.parentMethod
julia
parent(s::Session) -> Union{Session, Nothing}

Parent session, or nothing for roots. Matches the old session.parent semantics so callers don't need to change.

source
Bonito.LabelMethod
julia
Label(value; style=Styles(), attributes...)

A Label is a simple text element, with a bold font and a font size of 1rem.

source
Bonito.add_cached!Method
julia
add_cached!(create_cached_object::Function, session::Session, message_cache::AbstractDict{String, Any}, key::String)

Checks if key is already cached by the session or it's root session (we skip any child session between root -> this session). If not cached already, we call create_cached_object to create a serialized form of the object corresponding to key and cache it. We return nothing if already cached, or the serialized object if not cached. We also handle the part of adding things to the message_cache from the serialization context.

source
Bonito.authenticateMethod
julia
authenticate(store::AbstractPasswordStore, username::String, password::String) -> Bool

Authenticate user credentials against the password store. Default implementation retrieves the user via get_user() and verifies the password. Override this method only if you need custom authentication logic.

source
Bonito.authenticateMethod
julia
authenticate(user::User, password::String) -> Bool

Authenticate a user with the provided password.

source
Bonito.bundle_data_snapshotMethod
julia
bundle_data_snapshot(asset::Asset) -> Vector{UInt8}

Return a copy of the asset's current bundle bytes taken under the per-asset bundle lock, so a concurrent bundle! can't tear the vector out from under a serving HTTP task. Callers serve the returned copy.

source
Bonito.dependency_pathMethod
julia
dependency_path(paths...)

Path to serve downloaded dependencies

source
Bonito.export_standaloneMethod
julia
export_standaloneexport_standalone(
    app::App, folder::String;
    clear_folder=false, write_index_html=true,
    absolute_urls=false, content_delivery_url="file://" * folder * "/",
    single_html=false)

Exports the app defined by app::Application with all its assets to folder. Will write the main html out into folder/index.html. Overwrites all existing files! If this gets served behind a proxy, set absolute_urls=true and set content_delivery_url to your proxy url. If clear_folder=true all files in folder will get deleted before exporting again! single_html=true will write out a single html instead of writing out JS depencies as separate files.

source
Bonito.generate_state_keyMethod
julia
generate_state_key(values)

Generate a consistent key for state values that works identically in Julia and JavaScript. Handles Float64 values specially to ensure consistent string representation.

source
Bonito.get_userMethod
julia
get_user(store::AbstractPasswordStore, username::String) -> Union{User, Nothing}

Retrieve a user by username from the password store. Must be implemented by all AbstractPasswordStore subtypes.

source
Bonito.getextensionMethod
julia
getextension(path)

Get the file extension of the path. The extension is defined to be the bit after the last dot, excluding any query string.

Examples

julia
julia> Bonito.getextension("foo.bar.js")
"js"
julia> Bonito.getextension("https://my-cdn.net/foo.bar.css?version=1")
"css"

Taken from WebIO.jl

source
Bonito.handle_render_errorMethod
julia
handle_render_error(f, session::Session) -> Node

Run f() and return its (Node) result. If f throws, log the exception, record it via record_session_error! (so isready(session) / wait_for_ready surface the real cause and any indicator on the app picks it up reactively), and return an error-HTML Node so the caller's downstream emit path (HTTP response, show(io, dom), the UpdateSession message in update_session_dom!) doesn't have to know whether the render succeeded.

The single error boundary for app rendering. Sites above this one don't need their own try/catch — rendered_dom always returns a Node.

source
Bonito.is_current_socketMethod
julia
is_current_socket(handler, websocket) -> Bool

True iff websocket is still the socket handler is bound to. Used by the WS connection callback's finally so a stale loop (an old, half-open socket still parked in safe_read when the browser reconnected and installed a new socket) does NOT tear down the session now owned by the new socket.

source
Bonito.is_onlineMethod
julia
is_online(path)

Determine whether or not the specified path is a local filesystem path (and not a remote resource that is hosted on, for example, a CDN).

source
Bonito.is_path_containedMethod
julia
is_path_contained(root::AbstractString, path::AbstractString) -> Bool

Return true if path resolves to root itself or a location strictly inside root. Both arguments must already be absolute. Uses a normalized path-segment comparison so that sibling directories sharing a name prefix (/data/site-backup vs /data/site) are NOT considered contained — a bug a naive startswith/occursin check would have.

source
Bonito.isrootMethod
julia
isroot(s::Session) -> Bool

True if s is the root of its session tree (its parent_or_root is the RootSession, not a parent Session).

source
Bonito.jsrenderMethod
julia
jsrender([::Session], x::Any)

Internal render method to create a valid dom. Registers used observables with a session And makes sure the dom only contains valid elements. Overload jsrender(::YourType) To enable putting YourType into a dom element/div. You can also overload it to take a session as first argument, to register messages with the current web session (e.g. via onjs).

source
Bonito.on_document_loadMethod
julia
on_document_load(session::Session, js::JSCode)

executes javascript after document is loaded

source
Bonito.onloadMethod
julia
onload(session::Session, node::Node, func::JSCode)

calls javascript func with node, once node has been displayed.

source
Bonito.pack_extension!Method
julia
pack_extension!(f, s::SessionIO, tag::Int8)

Open a nested-extension scope: pushes a fresh scratch IOBuffer onto s's stack, runs f() (which writes the payload via pack(s, ...) / write(s, ...)), then emits [ext_header(tag, payload_size), payload_bytes] to the layer below.

No try/finally — on error, release_pack_io! resets the SessionIO before it returns to the pool.

source
Bonito.page_htmlMethod
julia
page_html(session::Session, html_body)

Embeds the html_body in a standalone html document!

source
Bonito.process_messageMethod
julia
process_message(session::Session, bytes::AbstractVector{UInt8})

Handles the incoming websocket messages from the frontend. Messages are expected to be gzip compressed and packed via MsgPack.

source
Bonito.record_session_error!Method
julia
record_session_error!(session::Session, err::Exception)

Record err as the session's most-recent failure. Sets session.init_error[] (which isready(session) re-throws by default, surfacing the cause to any waiter) and — if this session has a current_app with a ConnectionIndicator — pushes the error into indicator.error[]. That observable is rendered via map(render_error, indicator.error) into the indicator's DOM, so a connected browser sees the cause in the page without any custom protocol message.

source
Bonito.record_statesMethod
julia
record_states(session::Session, dom::Hyperscript.Node)

Records widget states and their UI updates for offline/static HTML export. This function captures how the UI changes in response to widget interactions, allowing exported HTML to remain interactive without a Julia backend.

How it works

Each widget's states are recorded independently:

  • The function finds all widgets in the DOM that implement the widget interface

  • For each widget, it records the UI updates triggered by each possible state

  • The resulting state map is embedded in the exported HTML

Widget Interface

To make a widget recordable, implement these methods:

julia
is_widget(::YourWidget) = true                    # Marks the type as a recordable widget
value_range(w::YourWidget) = [...]                 # Returns all possible states
to_watch(w::YourWidget) = w.observable             # Returns the observable to monitor

Limitations

Experimental Feature

  • Large file sizes: Recording all states can significantly increase HTML size

  • Independent states only: Widgets are recorded independently. Computed observables that depend on multiple widgets won't update correctly in the exported HTML

  • Performance: Not optimized for large numbers of widgets or states

Example

julia
# This will work - independent widgets
s = Slider(1:10)
c = Checkbox(true)
record_states(session, DOM.div(s, c))

# This won't fully work - dependent computed observable
combined = map((s,c) -> "Slider: $s, Checkbox: $c", s.value, c.value)
record_states(session, DOM.div(s, c, combined))  # combined won't update
source
Bonito.register_asset_server!Method
julia
register_asset_server!(condition::Function, ::Type{<: AbstractAssetServer})

Registers a new asset server type. condition is a function that should return nothing, if the asset server type shouldn't be used, and an initialized asset server object, if the conditions are right. E.g. The Bonito.NoServer be used inside an IJulia notebook so it's registered like this:

julia
register_asset_server!(NoServer) do
    if isdefined(Main, :IJulia)
        return NoServer()
    end
    return nothing
end

The last asset server registered takes priority, so if you register a new connection last in your Package, and always return it, You will overwrite the connection type for any other package. If you want to force usage temporary, try:

julia
force_asset_server(YourAssetServer) do
    ...
end
# which is the same as:
force_asset_server!(YourAssetServer)
...
force_asset_server!()
source
Bonito.register_connection!Method
julia
register_connection!(condition::Function, ::Type{<: FrontendConnection})

Registers a new Connection type.

condition is a function that should return nothing, if the connection type shouldn't be used, and an initialized Connection, if the conditions are right. E.g. The IJulia connection should only be used inside an IJulia notebook so it's registered like this:

julia
register_connection!(IJuliaConnection) do
    if isdefined(Main, :IJulia)
        return IJuliaConnection()
    end
    return nothing
end

The last connection registered take priority, so if you register a new connection last in your Package, and always return it, You will overwrite the connection type for any other package. If you want to force usage temporary, try:

julia
force_connection(YourConnectionType) do
    ...
end
# which is the same as:
force_connection!(YourConnectionType)
...
force_connection!()
source
Bonito.render_errorMethod
julia
render_error(err) -> Node

Render a server-side error for display inside a ConnectionIndicator. Default shows nothing for nothing and a banner with the error type + message for an Exception. Override for richer per-app rendering by defining a method on your own indicator type or wrapping a ConnectionIndicator and supplying a different mapping function.

source
Bonito.render_proxiedMethod
julia
render_proxied(app, prefix; compression, driver, asset_server) -> ProxyRender

Render app into a fresh proxied root Session (id == prefix). Produces the DOM fragment as HTML and ships the session's init messages as one binary via the ProxyAssetServer (so the host serves it). No init <script> is inlined — the host drives init_session itself (so it works even when the host mounts the fragment via innerHTML, where inline scripts don't execute).

driver is the transport: proxy_send(driver, bytes) relays a frame to the browser. asset_server is a ProxyAssetServer built over the same driver (so its 0→1 / 1→0 transitions dispatch proxy_asset_add / proxy_asset_remove).

source
Bonito.replace_expressionsMethod
julia
replace_expressions(markdown, context)

Replaces all expressions inside markdown savely, by only supporting getindex/getfield expression that will index into context

source
Bonito.root_dataMethod
julia
root_data(s::Session) -> RootSession

The RootSession shared by everything in s's session tree. Convenience for accessing root-only state (connection, inbox, locks, ...).

source
Bonito.root_sessionMethod
julia
root_session(s::Session) -> Session

The root Session of s's session tree. Returns s itself if s is a root.

source
Bonito.route_to_remoteMethod
julia
route_to_remote(session, data) -> Bool

If data belongs to a registered proxied (worker) session — recognised purely by its id being the worker's namespace prefix (the worker's root session id) or starting with prefix/ (its observables, subsessions, dom nodes) — forward the decoded frame to that worker via proxy_forward(driver, data) and return true. The server holds no mirror of the worker's sessions/objects; this namespace match is the whole routing table. Returns false for the server's own frames (whose ids never carry a worker prefix), which then fall through to normal local handling.

source
Bonito.run_connection_loopMethod
julia
runs the main connection loop for the websocket
source
Bonito.serve_workloadMethod
julia
serve_workload(app::App; updates = Pair{Observable, Any}[])

Precompile-workload helper: serves app on a loopback server and exercises the full serving stack the way a real browser would — page request, one asset request, and a websocket client speaking the frontend protocol (session announce, which runs init_session and flushes the queued init messages through the websocket write path, plus observable updates through the inbox/process_message path).

Intended for PrecompileTools.@compile_workload blocks of packages building on Bonito (e.g. WGLMakie), so the serve path is compiled into their pkgimage with all their own types and method extensions loaded — without this, the first display at runtime pays several seconds of inference for the listener/stream-handler/websocket-decoder task bodies.

updates are observable => payload pairs sent as frontend observable updates (the observables must be registered with the served session, i.e. rendered into the app). When empty, the first registered Observable{Int} gets an update instead, if any.

source
Bonito.set_cleanup_policy!Method
julia
set_cleanup_policy!(policy::CleanupPolicy)

You can set a custom cleanup policy by calling this function.

source
Bonito.set_cleanup_time!Method
julia
set_cleanup_time!(time_in_hrs::Real)

Sets the time that sessions remain open after the browser tab is closed. This allows reconnecting to the same session. Only works for Websocket connection inside VSCode right now, and will display the same App again from first display. State that isn't stored in Observables inside that app is lost.

source
Bonito.setup_websocket_connection_jsMethod
julia
returns the javascript snippet to setup the connection
source
Bonito.stop_cleanup_task!Method
julia
stop_cleanup_task!(server::Server)

Stop and forget server's 1-Hz cleanup task. Without this, the task runs forever and the SERVER_CLEANUP_TASKS entry keeps a strong ref chain (Dict → Server → routes → sessions) alive, defeating GC for every server ever created. close(server) should call this. Idempotent / safe to call on a server that never had a cleanup task.

source
Bonito.string_to_markdownFunction
julia
string_to_markdown(source::String; eval_julia_code=false)

Replaces all interpolation expressions inside markdown savely, by only supporting getindex/getfield expression that will index into context. You can eval Julia code blocks by setting eval_julia_code to a Module, into which the code gets evaluated!

source
Bonito.update_nocycle!Function

Update the value of an observable, without echoing the change back to the frontend session that originated it.

origin is the session whose browser just pushed this value. We must skip ONLY that session's JSUpdateObservable (so the value doesn't bounce back to the tab that sent it), but still fire every OTHER session's updater — when two browser sessions share one observable, session B has to see A's update. Skipping all JSUpdateObservables (the old behavior) silently desynced them. All non-JSUpdateObservable listeners (user on/map) always fire.

source
Sockets.sendMethod
julia
send(session::Session; attributes...)

Send values to the frontend via MsgPack for now

source
Bonito.HTTPServer.EWindowMethod
julia
EWindow(args...; app=nothing, options=Dict{String, Any}(), electron_args=default_electron_args())

Create an Electron window via ElectronCall. If app is provided, reuse that Application instead of creating a new one (avoids multiple Electron processes).

source
Bonito.HTTPServer.ServerType

HTTP server with websocket & http routes

source
Bonito.HTTPServer.ServerMethod

Server( dom, url::String, port::Int; verbose = -1 )

Creates an application that manages the global server state!

source
Base.waitMethod
julia
wait(server::Server)

Wait on the server task, i.e. block execution by bringing the server event loop to the foreground.

source
Bonito.HTTPServer.browser_displayMethod
julia
browser_display()

Forces Bonito.App to be displayed in a browser window that gets opened.

source
Bonito.HTTPServer.local_urlMethod
julia
local_url(server::Server, url)

The local url to reach the server, on the server

source
Bonito.HTTPServer.online_urlMethod
julia
online_url(server::Server, url)

The url to connect to the server from the internet. Needs to have server.proxy_url set to the IP or dns route of the server

source
Bonito.HTTPServer.tryrunMethod
julia
tryrun(cmd::Cmd)

Try to run a command. Return true if cmd runs and is successful (exits with a code of 0). Return false otherwise.

source