Skip to content

codecaine-zz/electrobun_easydialogs

Repository files navigation

Electrobun EasyDialogs

A modern, high-performance desktop dialog library for Bun and Electrobun on macOS. Replace outdated system prompts and heavy UI toolkits with polished, animated HTML5 dialogs powered by a native macOS WebView.

Login Form Screenshot Server Configuration Screenshot

Table of Contents


Architecture

Electrobun compiles and packages a Bun process alongside a native macOS Cocoa shell container.

Layer Role
Bun Process (Main) Manages dialog lifecycle, collects values, executes subprocesses via Bun.spawn, and coordinates RPC transactions
WebView (Renderer) Renders modernized HTML5 layouts via protocol resolution (views://) using low-overhead IPC

Project Structure

electrobun_easydialogs/
├── src/
│   ├── bun/
│   │   ├── index.ts                   # CLI entrypoint & demo runner (re-exports ElectrobunEasyDialogs)
│   │   └── electrobun_easydialogs.ts  # Core ElectrobunEasyDialogs class — the single-file distributable
│   ├── mainview/
│   │   ├── index.html                 # WebView shell
│   │   ├── index.css                  # WebView styles
│   │   ├── index.ts                   # WebView TypeScript entrypoint
│   │   ├── dynamic-dialog.js          # Runtime dialog renderer (compiled)
│   │   └── wrapper-module.js          # UI module (compiled)
│   ├── shared-types.ts                # Shared type definitions
│   └── types.d.ts                     # Ambient declarations
├── icons/                             # Premium Sequoia-style 3D dialog icons
│   ├── success.png
│   ├── warning.png
│   ├── error.png
│   ├── info.png
│   ├── question.png
│   ├── settings.png
│   ├── security.png
│   ├── database.png
│   ├── network.png
│   └── user.png
├── demos/                             # Individual dialog demo scripts
│   ├── calendar_dialog.ts
│   ├── comprehensive_form.ts
│   ├── custom_colors_demo.ts          # Custom color configurations demo
│   ├── entry_dialog.ts
│   ├── error_dialog.ts
│   ├── feedback_form.ts
│   ├── icon_dialog_demo.ts            # Custom and built-in icons demo
│   ├── info_dialog.ts
│   ├── login_form.ts
│   ├── password_dialog.ts
│   ├── profile_form.ts
│   ├── question_dialog.ts
│   ├── scale_dialog.ts
│   ├── secure_html_demo.ts            # Secure HTML dialog and XSS protection demo
│   ├── server_settings_form.ts
│   ├── simple_form.ts
│   ├── theme_toggle_demo.ts           # Light/Dark mode toggle demo
│   └── warning_dialog.ts
├── examples/                          # Integration examples
│   ├── test_vanilla_bun.ts            # Vanilla Bun (outside Electrobun) integration test
│   └── zsh_example.sh                 # Full zsh CLI integration showcase
├── screenshots/                       # Dialog layout screenshots for documentation
│   ├── login_dialog.png
│   └── server_settings.png
├── electrobun_easydialogs             # zsh CLI wrapper script (executable)
├── build.ts                           # Build script (compiles app + assembles dist/)
├── electrobun.config.ts               # Electrobun configuration
├── package.json
├── tsconfig.json
└── bun.lock

Note: build/, dist/, node_modules/, and the root-level electrobun_easydialogs.ts (a build artifact) are excluded from version control via .gitignore.


Getting Started

Prerequisites

  • Bun ≥ 1.0
  • macOS (arm64 or x86_64)

1. Install Dependencies

bun install

2. Start Development (with file-watcher)

bun run dev

This compiles the Electrobun app bundle into ./build and starts a live-reload watcher.

3. Build for Distribution

bun run build

Outputs:

  • dist/electrobun_easydialogs.ts — the self-contained, zero-dependency TypeScript module
  • dist/views/mainview/ — compiled WebView assets
  • dist/electrobun-easydialogs-dev.app — the complete macOS app bundle

Usage Guide

A. TypeScript / Bun Usage

src/bun/electrobun_easydialogs.ts is a self-contained single file with zero local imports — copy it directly into any Bun project.

import ElectrobunEasyDialogs from "./electrobun_easydialogs"; // copy electrobun_easydialogs.ts into your project

const easydialogs = new ElectrobunEasyDialogs();

// Ask a yes/no question
const runBackup = await easydialogs.question("Run a backup database scan?", {
  title: "Database Utility",
  okLabel: "Run Backup",
  cancelLabel: "Skip",
});

if (runBackup) {
  // Spawn a subprocess via Bun
  const proc = Bun.spawn(["tar", "-czf", "backup.tar.gz", "./src"]);
  const output = await new Response(proc.stdout).text();

  // Show output in an info dialog
  await easydialogs.info(`Backup completed!\n\n${output}`);
}

Run the interactive full demo:

bun run demo

Run a specific dialog demo:

EASYDIALOGS_DEMO_FILE=demos/calendar_dialog.ts bun run dev
EASYDIALOGS_DEMO_FILE=demos/comprehensive_form.ts bun run dev
EASYDIALOGS_DEMO_FILE=demos/entry_dialog.ts bun run dev
EASYDIALOGS_DEMO_FILE=demos/password_dialog.ts bun run dev
EASYDIALOGS_DEMO_FILE=demos/scale_dialog.ts bun run dev

Run the vanilla Bun integration test:

bun run test:vanilla
# or directly:
bun run examples/test_vanilla_bun.ts

Force-test the AppleScript (osascript) serverless fallback:

EASYDIALOGS_FORCE_OSASCRIPT=true bun run test:vanilla

B. Shell (zsh) Usage

electrobun_easydialogs in the project root is a native zsh CLI wrapper. It reads dialog type and options from flags, delegates to the compiled Electrobun app, and returns output via stdout / exit codes.

Run the full zsh showcase:

bun run example:zsh
# or directly:
zsh examples/zsh_example.sh

Manual terminal commands:

# Info notification (auto-closes in 5s)
./electrobun_easydialogs --info --title="Build Warning" --text="Compile complete with 2 notices." --timeout=5

# Branching yes/no prompt
./electrobun_easydialogs --question --title="Restart Service?" --text="Apply configuration updates?"
if [ $? -eq 0 ]; then
  echo "Restarting service..."
fi

# Capture text input
USERNAME=$(./electrobun_easydialogs --entry --title="Create Account" --text="Choose a system username:")
echo "Registering: $USERNAME"

# Multi-field form (pipe-separated output)
CONFIG=$(./electrobun_easydialogs --forms --title="Microservice Setup" \
                      --add-entry="Service Name" \
                      --add-password="Admin Token" \
                      --add-combo="Cache Provider" --combo-values="Redis,Memcached,None")
# e.g. output: "my-api|secret_token|Redis"

Dialog API Reference

This reference acts as a complete cheatsheet for all options, method signatures, return types, and CLI flags.

1. Global Common Options

These options apply to all dialog methods (in TypeScript as CommonOptions) and can be specified via corresponding CLI flags/environment variables.

TypeScript Option CLI Flag / Env Var Type Description
title --title string The title displayed in the native dialog title bar.
width (Auto-calculated) number The width of the dialog window in pixels (default: 460 for messages, 560 for forms, 600 for HTML).
height (Auto-calculated) number The height of the dialog window in pixels.
timeout --timeout number Auto-closes the dialog after N seconds (returns null/false/exit status 1).
okLabel --ok-label string Text for the confirmation or submit button (e.g. "OK", "Submit", "Yes").
cancelLabel --cancel-label string Text for the abort button (e.g. "Cancel", "No", "Abort").
extraButton --add-button string Text for an optional third button. Pressing it returns 'extra' / exit code 2 (Forms only).
icon --icon string Built-in icon name ('info', 'warning', 'error', 'danger', 'question', 'none') or absolute path to a custom .png/.svg.
theme --theme 'light' | 'dark' | 'system' Force the dialog rendering theme mode (default: 'system').
colors --colors Record<string, string> Dynamic styling overrides (as a JSON string in CLI, e.g. '{"bg":"#1c1c1e","primary":"#af52de"}').
Supported keys: bg (background), surface, text, primary (accent color), border.
modalHint - boolean Hints/renders the window in a modal state relative to parent or desktop.
attachParent - number Window handle/ID of the parent window to attach the dialog to.

| |---

2. Dialog Methods Cheatsheet

A. Info Notification

Displays an informational alert with a single button.

  • TypeScript Signature:
    async easydialogs.info(message: string, options?: InfoOptions): Promise<void>
  • InfoOptions:
    • noWrap?: boolean — Prevents text wrapping
    • noMarkup?: boolean — Disables parsing basic HTML/markup tags
    • ellipsize?: boolean — Truncates text with ellipses if it exceeds line bounds
    • iconName?: string — Legacy/alternative icon identifier string
  • Examples:
    // TypeScript
    await easydialogs.info("Configuration loaded successfully!", { title: "System Info", icon: "success" });
    # CLI
    ./electrobun_easydialogs --info --title="System Info" --text="Configuration loaded successfully!" --icon="success"

B. Warning Alert

Displays a cautionary alert window.

  • TypeScript Signature:
    async easydialogs.warning(message: string, options?: WarningOptions): Promise<void>
  • WarningOptions: Inherits all InfoOptions.
  • Examples:
    // TypeScript
    await easydialogs.warning("Disk space is exceeding 90%.", { title: "Disk Space Warning" });
    # CLI
    ./electrobun_easydialogs --warning --title="Disk Space Warning" --text="Disk space is exceeding 90%."

C. Error Alert

Displays a critical failure or error block.

  • TypeScript Signature:
    async easydialogs.error(message: string, options?: ErrorOptions): Promise<void>
  • ErrorOptions: Inherits all InfoOptions.
  • Examples:
    // TypeScript
    await easydialogs.error("Database connection refused.", { title: "Database Error" });
    # CLI
    ./electrobun_easydialogs --error --title="Database Error" --text="Database connection refused."

D. Question Confirmation

Displays a branching confirmation prompt with two options.

  • TypeScript Signature:
    async easydialogs.question(message: string, options?: QuestionOptions): Promise<boolean>
  • QuestionOptions:
    • defaultCancel?: boolean — Sets the default focused button to Cancel instead of OK
    • noWrap?: boolean, noMarkup?: boolean, ellipsize?: boolean
  • Returns: Promise<boolean> (true for OK/Yes, false for Cancel/No/dismissal)
  • Examples:
    // TypeScript
    const confirm = await easydialogs.question("Apply pending updates now?", {
      title: "System Update",
      okLabel: "Update",
      cancelLabel: "Later"
    });
    if (confirm) { /* ... */ }
    # CLI (Exit code is 0 for Yes, 1 for No/Cancel)
    ./electrobun_easydialogs --question --title="System Update" --text="Apply updates?" --ok-label="Update" --cancel-label="Later"
    if [ $? -eq 0 ]; then
      echo "Updating..."
    fi

E. Text Entry Input

Captures a single line of freeform text input from the user.

  • TypeScript Signature:
    async easydialogs.entry(message: string, options?: EntryOptions): Promise<string | null>
  • EntryOptions:
    • entryText?: string — Initial text value populated in the input field
    • hideText?: boolean — Masks the input text with bullet points (password mode)
  • Returns: Promise<string | null> (Returns the text string on OK, or null if cancelled/dismissed)
  • Examples:
    // TypeScript
    const username = await easydialogs.entry("Choose a handle:", { title: "Handle Registration", entryText: "anonymous" });
    # CLI (Outputs value to stdout; exit code 1 if cancelled)
    HANDLE=$(./electrobun_easydialogs --entry --title="Handle Registration" --text="Choose a handle:" --value="anonymous")

F. Password Input

Captures sensitive passwords. Can also capture username credentials simultaneously.

  • TypeScript Signature:
    async easydialogs.password(options?: PasswordOptions): Promise<[string, string] | string | null>
  • PasswordOptions:
    • username?: boolean — If set to true, renders separate fields for username and password
  • Returns:
    • If username is true: Promise<[string, string] | null> as [username, password]
    • If username is false: Promise<string | null> as password
  • Examples:
    // TypeScript (Password Only)
    const masterKey = await easydialogs.password({ title: "Decryption Key Required" });
    
    // TypeScript (Credentials Mode)
    const creds = await easydialogs.password({ title: "Server Log In", username: true });
    if (creds) {
      const [user, pass] = creds;
    }
    # CLI (Outputs to stdout; Exit code 1 if cancelled)
    # Password Only:
    PASSWORD=$(./electrobun_easydialogs --password --title="Decryption Key Required")
    
    # Credentials Mode (outputs delimited by separator, e.g., "username|password"):
    CREDS=$(./electrobun_easydialogs --password --title="Server Log In" --username --separator="|")

G. Scale Slider

Selects a numeric value using a slider scale.

  • TypeScript Signature:
    async easydialogs.scale(message: string, options?: ScaleOptions): Promise<number | null>
  • ScaleOptions:
    • value?: number — Default starting value (default: 0)
    • minValue?: number — Minimum value limit (default: 0)
    • maxValue?: number — Maximum value limit (default: 100)
    • step?: number — Numeric interval/increment step
    • hideValue?: boolean — Hides the live number display bubble next to the slider
    • printPartial?: boolean — Prints/outputs partial decimal/float adjustments rather than rounded integers
  • Returns: Promise<number | null>
  • Examples:
    // TypeScript
    const volume = await easydialogs.scale("Set volume level:", { value: 75, minValue: 0, maxValue: 100 });
    # CLI
    VOLUME=$(./electrobun_easydialogs --scale --text="Set volume level:" --value=75 --min-value=0 --max-value=100)

H. Calendar Picker

Selects a date and time from an interactive calendar interface.

  • TypeScript Signature:
    async easydialogs.calendar(message: string, options?: CalendarOptions): Promise<string | null>
  • CalendarOptions:
    • day?: number, month?: number, year?: number — Pre-select date
    • dateFormat?: string — Formatting pattern
  • Returns: Promise<string | null> (ISO-8601 formatted date/time string or custom format)
  • Examples:
    // TypeScript
    const dateStr = await easydialogs.calendar("Select date of birth:", { title: "Onboarding" });
    # CLI
    DOB=$(./electrobun_easydialogs --calendar --text="Select date of birth:")

I. HTML Content

Displays fully featured custom rich-text HTML components with automatic DOM sanitization to block script injection (XSS).

  • TypeScript Signature:
    async easydialogs.html(htmlContent: string, options?: HTMLOptions): Promise<boolean>
  • Returns: Promise<boolean> (true for OK/Submit, false for Cancel/dismiss)
  • Examples:
    // TypeScript
    const isAgreed = await easydialogs.html(`
      <h2>Accept System Policy</h2>
      <p>By proceeding, you agree to follow the <strong>Acceptable Use Guidelines</strong>.</p>
    `, { title: "Security Notice", okLabel: "I Agree", cancelLabel: "Cancel" });
    # CLI (Direct inline markup text)
    ./electrobun_easydialogs --html --html-text="<h3>Warning</h3><p>Production mode</p>" --ok-label="Understand"
    
    # CLI (Load content from a local file)
    ./electrobun_easydialogs --html --html-file="eula.html" --title="Agreement"

J. Multi-Field Forms

Constructs rich forms with mixed input fields (dropdowns, toggles, ratings, text inputs, etc.) in a single dialog.

  • TypeScript Signature:
    async easydialogs.forms(fields: FormField[], options?: FormsOptions): Promise<FormsResult>
  • FormsOptions:
    • text?: string — Form subtitle/body text
    • separator?: string — Delimiter for output values mapping (default: '|')
    • formsDateFormat?: string — Date/time format specifically for forms date inputs
    • showHeader?: boolean — Toggles visibility of the form text/subtitle header
  • FormsResult Output:
    interface FormsResult {
      button: 'ok' | 'cancel' | 'extra';
      values: string[] | null; // Array of collected input strings in the declared field order
    }
  • Examples:
    // TypeScript
    const result = await easydialogs.forms([
      { type: 'entry', label: 'App ID', value: 'com.app.service', required: true },
      { type: 'combo', label: 'Environment', values: ['Dev', 'Staging', 'Prod'], value: 'Dev' },
      { type: 'toggle', label: 'Enable SSL', value: true },
      { type: 'rating', label: 'Rating', value: 3 }
    ], {
      title: "Application Settings",
      text: "Set up the application configuration metadata.",
      extraButton: "Restore Defaults"
    });
    
    if (result.button === 'ok' && result.values) {
      const [appId, env, ssl, rating] = result.values;
    }
    # CLI (Form values are printed to stdout, pipe-separated)
    # Exit code: 0 on Submit (OK), 2 on Extra button press ("Restore Defaults"), 1 on cancel/dismiss
    CONFIG=$(./electrobun_easydialogs --forms --title="App Config" \
                --add-entry="App ID" \
                --add-combo="Environment" --combo-values="Dev,Staging,Prod")

3. Forms Field Types Reference

Inside the fields array passed to the forms() method, you can define different field elements:

Field type Custom TypeScript Field Schema Definition Rendered UI Element
entry { type: 'entry'; label: string; value?: string; required?: boolean } Standard single-line text input
password { type: 'password'; label: string; value?: string; required?: boolean } Masked text input
multiline { type: 'multiline'; label: string; value?: string; required?: boolean } Multi-line textarea
calendar { type: 'calendar'; label: string; value?: string; required?: boolean } Inline date-time picker
list { type: 'list'; label: string; values?: string[]; required?: boolean } Scrollable value list selection
combo { type: 'combo'; label: string; values?: string[]; value?: string; required?: boolean } Dropdown/combobox menu
rating { type: 'rating'; label: string; value?: number | string; required?: boolean } Sequoia 3D 5-star rating selector
slider { type: 'slider'; label: string; value?: number | string; required?: boolean } Dynamic range slider bar
toggle { type: 'toggle'; label: string; value?: boolean | string; required?: boolean } iOS-style toggle switch
HTML5 Native { type: 'text' | 'email' | 'number' | 'tel' | 'url' | 'color' | 'file' | 'checkbox' | 'radio' ...; label: string; value?: any; required?: boolean } Native browser input widgets matching the OS theme.


Built-in Premium Icons

A collection of 10 macOS Sequoia-inspired, glassmorphic 3D icons are included in the icons folder for custom-branded dialogs:

Icon File Recommended Usage
success.png Task completion, successful saves, operation completed
info.png Standard notifications, hints, instructions
warning.png Warnings, caution alerts, non-blocking errors
error.png Blocked operations, failures, exception states
question.png Confirmation dialogues, interactive help prompts
settings.png Setup, configuration panels, preferences
security.png Authentication, permission requests, privacy settings
database.png Database updates, connection statuses, backups
network.png Web requests, sync status, network/API configurations
user.png User logins, profile setup, account registration

To use any icon:

import { join } from 'path';

await easydialogs.info("Configuration loaded!", {
  title: "Settings Utility",
  icon: join(process.cwd(), "icons/settings.png")
});

Execution Modes

The library auto-detects its execution context and selects the best strategy:

Mode Triggered When Behavior
Electrobun Worker Running inside compiled Electrobun app Renders native HTML5 dialogs in a macOS WebView window
Vanilla Bun + Helper App Running via bun run with helper binary present Spawns the compiled app in the background to present the HTML5 dialog
Serverless AppleScript Fallback No Electrobun binary found, or EASYDIALOGS_FORCE_OSASCRIPT=true Falls back to native osascript dialogs — zero TCP ports, zero binaries required

Custom Styling & Themes

Electrobun EasyDialogs supports light/dark mode configuration and dynamic element color overrides.

1. Theme Configuration

Force any dialog to render in Light Mode, Dark Mode, or automatically detect OS theme preferences:

TypeScript:

await easydialogs.info("Dark Mode Dialog", {
  theme: "dark"
});

CLI:

./electrobun_easydialogs --info --text="Force Light Mode" --theme="light"

2. Custom Colors Customize

Override CSS custom properties dynamically (with safety sanitization) to style dialog buttons, text, and background elements.

TypeScript:

await easydialogs.info("Custom colored theme", {
  colors: {
    bg: "#2b0f54",       // Background
    surface: "#3d1b6e",  // Surface containers
    text: "#ff7edb",     // Text color
    primary: "#39ff14",  // Primary buttons/selection color
    border: "#ff7edb"    // Border highlights
  }
});

CLI:

./electrobun_easydialogs --info --text="Vibrant Accent Notification" --colors='{"primary":"#af52de","bg":"#1c1c1e","text":"#f2f2f7"}'

Secure HTML Rendering

Easily display rich formatted HTML in the dialog body. The WebView performs strict recursive DOM sanitization at runtime before rendering to eliminate injection risks (XSS).

TypeScript:

const wantsToProceed = await easydialogs.html(`
  <h2>System Update Available</h2>
  <p>Please review details below:</p>
  <ul>
    <li>Version: <strong>v2.1.0</strong></li>
    <li>Security updates applied</li>
  </ul>
`, {
  title: "Software Center",
  okLabel: "Install Now",
  cancelLabel: "Postpone"
});

CLI:

# Direct inline markup text
./electrobun_easydialogs --html --html-text="<h3>Safe Header</h3><p>Rendered via CLI</p>"

# Render from an HTML file
./electrobun_easydialogs --html --html-file="path/to/page.html"

Note

Active XSS Protection: Unallowed tags like <script>, <iframe>, <object>, <embed>, or <style>, event handlers like onerror / onload, and javascript-protocol URLs (href="javascript:...") are completely stripped and neutralized.

About

A modern, high-performance desktop dialog library for Bun and Electrobun on macOS. Replace outdated system prompts and heavy UI toolkits with polished, animated HTML5 dialogs powered by a native macOS WebView.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors