Fast, lightweight, cross-platform message box - Rust-powered alternative to msgview
npm install @bobfrankston/msgerbash
npm install @bobfrankston/msger
`
For global CLI usage:
`bash
npm install -g @bobfrankston/msger
`
Supported Platforms:
- Windows (x64)
- Linux (x64)
- Linux (ARM64 / Raspberry Pi)
---
Command Line Usage
The msger command-line tool provides a quick way to display message boxes from shell scripts, batch files, or terminal.
Basic Examples
`bash
Simple message
msger -message "Hello, World!"
msger Hello World # shorthand - words become message
With HTML content
msger -html "Welcome!
This is HTML
"
Colored text with inline styles
msger -html "Alert!"
Load a URL (web page or local file)
msger -url "https://example.com"
msger -url "file:///path/to/page.html"
Load URL with hash fragment
msger -url "https://example.com" -hash section1
msger -url "https://example.com" -hash "#intro"
With input field and placeholder
msger -message "Enter your name:" -input "Your name here" -default "John"
Custom buttons
msger -message "Save changes?" -buttons Yes No Cancel
With custom size and position
msger -message "Hello" -size 800,600 -pos 100,100
Always on top with zoom
msger -message "Important!" -ontop -zoom 150
Detached URL browser (stays open after script exits)
msger -url "https://example.com" -detach
Fullscreen mode
msger -fullscreen -url "https://example.com"
Multi-monitor (Windows only - show on second screen)
msger -message "Second screen" -pos 0,100 -screen 1
Auto-close timeout
msger -message "Closing in 5 seconds..." -timeout 5
Open with DevTools for debugging (automatically opens DevTools panel)
msger -url "https://example.com" -dev
See all options
msger --help
`
JSON Configuration Files
You can store message box configuration in a JSON file for reuse. JSON files support comments (JSON5 format).
`bash
Load configuration from file
msger -load config.json
Override specific values from loaded config
msger -load config.json -title "New Title" -size 800,600
Save current command-line options to a file (only saves explicitly specified values)
msger -title "My Dialog" -message "Hello" -buttons Yes No -save my-config.json
Save config without showing the message box (uses -noshow)
msger -title "My Dialog" -message "Hello" -buttons Yes No -save my-config -noshow
Load base config, override, and save the overrides
msger -load base.json -title "Modified" -save modified.json
Save and show (saves config then displays the message box)
msger -title "Test" -message "Saving config..." -save test.json
`
Notes:
- The -save option saves only the explicitly specified command-line arguments, not derived defaults. This keeps config files minimal and allows defaults to evolve.
- Use -noshow with -save to create config files without displaying the message box.
- File extensions: If you don't specify .json, it will be added automatically (e.g., myconfig becomes myconfig.json).
$3
`json
{
"title": "Window Title",
"message": "Plain text message (supports ANSI color codes)",
"html": "HTML content
Rich formatting
",
"url": "https://example.com",
"hash": "section1",
"size": {
"width": 600,
"height": 400
},
"pos": {
"x": 100,
"y": 100,
"screen": 0
},
"buttons": ["Cancel", "OK"],
"defaultValue": "default input text",
"inputPlaceholder": "Enter text here...",
"allowInput": false,
"timeout": 30,
"autoSize": false,
"alwaysOnTop": false,
"fullscreen": false,
"zoom": 100,
"debug": false,
"icon": "path/to/icon.png",
"dev": false
}
`
$3
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| title | string | "Message" | Window title |
| message | string | null | Plain text message (supports ANSI color codes) |
| html | string | null | HTML content to display |
| url | string | null | URL to load (web or file://) |
| hash | string | null | Hash fragment to append to URL (requires url). Leading # is optional. |
| size | object | {"width": 600, "height": 400} | Window size |
| size.width | number | 600 (or 1024 for URLs) | Window width in pixels |
| size.height | number | 400 (or 768 for URLs) | Window height in pixels |
| pos | object | null | Window position |
| pos.x | number | - | X coordinate in pixels |
| pos.y | number | - | Y coordinate in pixels |
| pos.screen | number | null | [Windows only] Screen index (0=primary, 1=second, etc.) |
| buttons | string[] | ["OK"] | Button labels |
| defaultValue | string | null | Default input value |
| inputPlaceholder | string | null | Placeholder text for input field |
| allowInput | boolean | false | Show input field |
| timeout | number | null | Auto-close after N seconds |
| autoSize | boolean | true when no size | Auto-resize window to fit content |
| alwaysOnTop | boolean | false | Keep window on top |
| fullscreen | boolean | false | Start in fullscreen mode |
| zoom | number | 100 | Initial zoom level (100=100%, 150=150%) |
| debug | boolean | false | Return debug info in result |
| icon | string | null | Path to window icon (.png, .ico) |
| dev | boolean | false | Open DevTools automatically (for debugging HTML/URL content) |
Content Priority: Only one content type will be displayed (in order):
1. url - If provided, loads URL (ignores message/html)
2. html - If provided, displays HTML (ignores message)
3. message - Plain text with ANSI color support
Backward Compatibility: The old width and height fields (at the top level) are still supported for backward compatibility, but the size object is preferred. If both are provided, size takes precedence.
$3
Simple dialog:
`json
{
"title": "Confirm",
"message": "Are you sure?",
"buttons": ["Cancel", "OK"]
}
`
Input dialog:
`json
{
"title": "Enter Name",
"message": "Please enter your name:",
"allowInput": true,
"inputPlaceholder": "Your name here",
"defaultValue": "John Doe",
"buttons": ["Cancel", "Submit"]
}
`
HTML content:
`json
{
"title": "Welcome",
"html": "Welcome!
Getting started...
",
"size": {
"width": 700,
"height": 500
}
}
`
Positioned window on second monitor:
`json
{
"message": "Second screen",
"pos": {
"x": 0,
"y": 100,
"screen": 1
}
}
`
CLI Options Reference
`
Usage: msger [options] [message words...]
Options:
-message, --message Plain text message
-title, --title Window title
-html, --html HTML content
-url, --url URL to load (web or file://)
-hash, --hash Hash fragment to append to URL (requires -url). Leading # optional.
-buttons, --buttons Button labels (e.g., -buttons Yes No Cancel)
-ok, --ok Add OK button
-cancel, --cancel Add Cancel button
-input, --input [placeholder] Include input field with optional placeholder text
-default, --default Default value for input field
-size, --size Window size (e.g., -size 800,600)
-pos, --pos Window position (e.g., -pos 100,200)
-screen, --screen [Windows only] Screen index (0=primary, 1=second, etc.)
-zoom, --zoom Zoom level as percentage (e.g., -zoom 150 for 150%)
-timeout, --timeout Auto-close after specified seconds
-ontop, --ontop Keep window always on top
-detach, --detach Launch window independently (parent returns immediately)
-fullscreen, --fullscreen Start window in fullscreen mode (F11 to toggle, Escape to exit)
-debug, --debug Return debug info (HTML, size, autoSize) in result
-v, -version, --version Show version number
-help, -?, --help Show help message
Keyboard Shortcuts:
- Enter: Click default (last) button
- Escape: Dismiss dialog or exit fullscreen
- F11: Toggle fullscreen mode
- F12: Open developer tools (DevTools/Inspect)
- Ctrl+Wheel/Plus/Minus: Zoom in/out
- Ctrl+0: Reset zoom to 100%
Debug Mode:
- Set environment variable MSGER_DEBUG=1 for diagnostic output
- Shows: startup info, keyboard events, IPC messages, button clicks
- Example (PowerShell): $env:MSGER_DEBUG=1; msger "Test"
- Zero performance impact when disabled
Notes:
- If no options provided, all non-option arguments are concatenated as message
- Press ESC to dismiss (returns button: "dismissed")
- Press Enter to click last (default) button
- OK button is green by default
- URLs default to 1024x768 window size
- Window title automatically updates from loaded page's tag when using -url
- Windows auto-size to fit content when size not specified
- Runtime zoom: Ctrl+Wheel, Ctrl+Plus, Ctrl+Minus
`
---
Node.js API Reference
Use msger in your Node.js or TypeScript applications with a simple Promise-based API.
Quick Start
`typescript
import { showMessageBox } from '@bobfrankston/msger';
const result = await showMessageBox({
title: 'Confirm Action',
message: 'Are you sure you want to proceed?',
buttons: ['Cancel', 'OK']
});
console.log('User clicked:', result.button);
if (result.value) {
console.log('User entered:', result.value);
}
`
$3
Close message boxes programmatically from the parent process:
Handle-based API (Recommended):
`typescript
import { showMessageBoxEx } from '@bobfrankston/msger';
const handle = showMessageBoxEx({
message: 'Processing...',
buttons: ['Cancel']
});
console.log('Dialog PID:', handle.pid);
// Close after 5 seconds
setTimeout(() => {
handle.close();
}, 5000);
// Wait for result
const result = await handle.result;
// Result: {button: 'closed', closed: true} if closed programmatically
// Result: {button: 'Cancel'} if user clicked Cancel
`
Standalone close function:
`typescript
import { showMessageBoxEx, closeMessageBox } from '@bobfrankston/msger';
const handle = showMessageBoxEx({ message: 'Working...' });
const pid = handle.pid;
// Close by PID from anywhere
closeMessageBox(pid);
`
Use cases:
- Progress dialogs that auto-close when task completes
- Timeout-based notifications
- Managing multiple dialogs by PID
- Detached dialogs closed remotely
See CLOSE-API.md for complete documentation.
API Functions
$3
Display a message box dialog and wait for user response.
#### MessageBoxOptions
`typescript
interface MessageBoxOptions {
title?: string; // Window title (default: "Message")
message?: string; // Plain text message (optional if url or html provided)
html?: string; // HTML content to display
url?: string; // URL to load (web URL or file:// path)
hash?: string; // Hash fragment to append to URL (requires url). Leading # optional.
size?: { // Window size in pixels
width: number; // Window width (default: 600, or 1024 for URLs)
height: number; // Window height (default: 400, or 768 for URLs)
};
pos?: { // Window position
x: number; // X coordinate in pixels
y: number; // Y coordinate in pixels
screen?: number; // [Windows only] Screen index (0=primary, 1=second, etc.)
};
buttons?: string[]; // Button labels (default: ["OK"])
defaultValue?: string; // Default input value
inputPlaceholder?: string; // Placeholder text (gray hint) for input field
allowInput?: boolean; // Show input field (default: false)
autoSize?: boolean; // Auto-resize window to fit content (default: true when no size specified)
alwaysOnTop?: boolean; // Keep window on top of other windows (default: false)
zoom?: number; // Initial zoom level (100=100%, 150=150%, 50=50%)
timeout?: number; // Auto-close after N seconds
detach?: boolean; // Launch detached from parent process
fullscreen?: boolean; // Start window in fullscreen mode (F11 to toggle)
debug?: boolean; // Return debug info (HTML, size, autoSize) in result
}
`
#### MessageBoxResult
`typescript
interface MessageBoxResult {
button: string; // Label of clicked button
value?: string; // Input value (if allowInput was true)
form?: object; // Form data (if form elements present)
closed?: boolean; // True if closed programmatically via handle.close()
dismissed?: boolean; // True if user pressed Escape
timeout?: boolean; // True if closed due to timeout
debug?: { // Debug info (if debug option was true)
html: string; // Generated HTML content
width: number; // Window width
height: number; // Window height
autoSize: boolean; // Whether auto-sizing is enabled
};
}
`
API Examples
$3
`typescript
const result = await showMessageBox({
title: 'Delete File',
message: 'Are you sure you want to delete this file?',
buttons: ['Cancel', 'Delete']
});
if (result.button === 'Delete') {
// Perform deletion
}
`
$3
`typescript
const result = await showMessageBox({
title: 'Enter Name',
message: 'Please enter your name:',
allowInput: true,
inputPlaceholder: 'Your name here',
defaultValue: 'John Doe',
buttons: ['Cancel', 'OK']
});
if (result.button === 'OK') {
console.log('Name entered:', result.value);
}
`
$3
`typescript
const result = await showMessageBox({
title: 'Welcome',
html:
This supports HTML content.
,
size: { width: 700, height: 500 },
buttons: ['Close']
});
`
$3
Plain text messages automatically support ANSI color escape sequences, which are converted to HTML with proper colors:
`typescript
const result = await showMessageBox({
title: 'Color Test',
message: '\x1b[31mRed text\x1b[0m\n\x1b[32mGreen text\x1b[0m\n\x1b[1m\x1b[34mBold blue\x1b[0m',
buttons: ['OK']
});
`
Supported ANSI codes:
- Colors: Red (31), Green (32), Yellow (33), Blue (34), Magenta (35), Cyan (36), White (37)
- Styles: Bold (1), Underline (4), Reset (0)
- Backgrounds: 40-47 (same color numbers as foreground)
CLI example:
`bash
echo -e "\x1b[31mError:\x1b[0m Something went wrong" | msger
`
$3
`typescript
// Load external website
// Note: Window title automatically updates from page's tag
await showMessageBox({
url: 'https://github.com/BobFrankston/msger',
size: { width: 1024, height: 768 }
});
// Load local HTML file
await showMessageBox({
url: 'file:///path/to/help.html',
size: { width: 800, height: 600 }
});
// Load with DevTools opened automatically (for debugging)
await showMessageBox({
url: 'https://example.com',
dev: true, // Opens DevTools automatically
size: { width: 1024, height: 768 }
});
`
$3
`typescript
// Show notification that auto-closes after 3 seconds
// A translucent countdown indicator appears in the top-right corner
const result = await showMessageBox({
title: 'Notification',
message: 'File saved successfully!',
timeout: 3,
buttons: ['OK']
});
if (result.timeout) {
console.log('Notification auto-closed');
}
`
The countdown timer displays as a subtle yellow badge in the top-right corner, showing the remaining seconds (e.g., "10s", "9s", "8s"...).
$3
`typescript
const result = await showMessageBox({
title: 'User Registration',
html:
,
buttons: ['Cancel', 'Submit']
});
if (result.button === 'Submit' && result.form) {
console.log('Name:', result.form.name);
console.log('Email:', result.form.email);
console.log('Age:', result.form.age);
}
`
$3
`typescript
// Position at specific coordinates
await showMessageBox({
message: 'Positioned window',
pos: { x: 100, y: 100 }
});
// Position on second monitor (Windows only)
await showMessageBox({
message: 'On second screen',
pos: { x: 0, y: 100, screen: 1 }
});
`
$3
`typescript
// Keep window on top with 150% zoom
await showMessageBox({
title: 'Important Alert',
message: 'This window stays on top!',
alwaysOnTop: true,
zoom: 150
});
`
$3
`typescript
// Window automatically sizes to fit content
await showMessageBox({
message: 'Short message',
autoSize: true // default when size not specified
});
`
$3
`typescript
// Launch URL window that stays open after script exits
await showMessageBox({
url: 'https://example.com',
size: { width: 1200, height: 800 },
detach: true,
alwaysOnTop: true
});
// Script continues/exits immediately, window stays open
`
$3
`typescript
// Launch window in fullscreen mode (like F11 in browser)
await showMessageBox({
url: 'https://example.com',
fullscreen: true
});
// Users can press F11 to toggle fullscreen or Escape to exit
// Fullscreen with message
await showMessageBox({
message: 'Full screen presentation',
fullscreen: true,
buttons: ['Close']
});
`
Features
- ✅ Display plain text, HTML content, or load URLs
- ✅ Full HTML/CSS support
- ✅ ANSI color codes (colored terminal output)
- ✅ Customizable buttons
- ✅ Input fields and forms
- ✅ Auto-close with timeout
- ✅ Window positioning and sizing
- ✅ Multi-monitor support (Windows)
- ✅ Always on top mode
- ✅ Zoom control
- ✅ Fullscreen mode
- ✅ TypeScript type definitions
- ✅ Keyboard shortcuts (Enter, Escape, F11, F12)
Why msger?
- Fast - Opens in under 200ms
- Small - Only a few MB installed
- Simple - Easy Promise-based API
- Flexible - Plain text, HTML, or load URLs
- Cross-platform - Works on Windows and Linux
For technical details and performance metrics, see DEVELOPERS.md.
msger vs msgview
This package is the Rust/wry-based implementation. There's also @bobfrankston/msgview - an Electron-based alternative with the same API.
| Aspect | msger (this) | msgview |
|--------|--------------|---------|
| Startup | ~50-200ms | ~2-3 seconds |
| Size | ~5-10MB | ~200MB |
| Technology | Rust + wry (WebView2/webkit2gtk) | Electron |
| Pi/Linux | ❌ Rendering issues | ✅ Perfect |
| Windows/WSL | ✅ Works | ✅ Works |
$3
- Speed is critical (CLI tools, automation)
- Small binary size matters
- Windows or WSL environment
$3
- Running on Raspberry Pi or Linux
- Need reliable cross-platform rendering
- Prefer Electron ecosystem
$3
Both packages will share a common JavaScript API (window.msgapi) for file system access, window manipulation, and bidirectional communication. See TODO.md for details.
For the complete feature comparison and roadmap, see the shared TODO.md which tracks both projects.
---
Additional Information
Important Notes
$3
Icon Behavior Differences:
- Window Title Bar: Shows custom icon from -icon flag or JSON config
- Windows Taskbar: Shows the embedded msger.exe icon (cannot be changed at runtime)
This is a Windows limitation for native applications. The taskbar icon comes from the executable's embedded resource, not the runtime window icon. In contrast, Electron-based msgview can set taskbar icons dynamically.
Workaround: The planned -pin feature with AppUserModelID will allow each pinned shortcut to have its own taskbar icon.
$3
⚠️ msger is designed for displaying trusted, friendly content (local apps, your HTML files). It is NOT a secure sandbox for untrusted/hostile web content. Use -htmlfrom and -url with trusted sources only.
$3
Minimal API - Most functionality uses native browser APIs:
- msger.isAvailable() - Feature detection (returns true in msger)
- msger.close() - Same as window.close() (native API preferred)
Recommendation: Use native browser APIs (localStorage, window.close()`) for compatibility. Code will work in any browser context, not just msger.