### Running Systray Examples
Source: https://github.com/glzr-io/zebar/blob/main/crates/systray-util/README.md
Command-line instructions to run the synchronous and asynchronous (tokio) examples provided with the systray-util library.
```shell
# Run the synchronous example.
cargo run -p systray-util --example sync
# Run the asynchronous (tokio) example.
cargo run -p systray-util --example async
```
--------------------------------
### Install pnpm and Project Dependencies
Source: https://github.com/glzr-io/zebar/blob/main/CONTRIBUTING.md
Install the pnpm package manager globally, then install project dependencies. This is a prerequisite for running the development server.
```shell
# Install pnpm (package manager).
npm i -g pnpm
# Install dependencies.
pnpm i
```
--------------------------------
### Run Development Server
Source: https://github.com/glzr-io/zebar/blob/main/packages/settings-ui/README.md
Starts the application in development mode for active development and testing.
```bash
npm run dev
```
--------------------------------
### Initialize Zebar Providers for GlazeWM
Source: https://github.com/glzr-io/zebar/blob/main/resources/starter/with-glazewm.html
Sets up various Zebar providers including network, GlazeWM, CPU, date, battery, memory, and weather. This is the initial setup for the status bar.
```javascript
import React, { useState, useEffect, } from 'https://esm.sh/react@18?dev';
import { createRoot } from 'https://esm.sh/react-dom@18/client?dev';
import * as zebar from 'https://esm.sh/zebar@3.0';
const providers = zebar.createProviderGroup({
network: { type: 'network' },
glazewm: { type: 'glazewm' },
cpu: { type: 'cpu' },
date: { type: 'date', formatting: 'EEE d MMM t' },
battery: { type: 'battery' },
memory: { type: 'memory' },
weather: { type: 'weather' },
});
createRoot(document.getElementById('root')).render();
```
--------------------------------
### Install Dependencies
Source: https://github.com/glzr-io/zebar/blob/main/packages/settings-ui/README.md
Installs project dependencies using npm, pnpm, or yarn.
```bash
npm install # or pnpm install or yarn install
```
--------------------------------
### Programmatically Start Another Widget
Source: https://context7.com/glzr-io/zebar/llms.txt
Use `startWidget` or `startWidgetPreset` to spawn new widget windows at specified placements or using presets from `zpack.json`. This is useful for triggering popups from a main widget.
```typescript
import { startWidget, startWidgetPreset, type WidgetPlacement } from 'zebar';
// Open a popup at the top-right corner of the primary monitor
const placement: WidgetPlacement = {
anchor: 'top_right',
offsetX: '0',
offsetY: '40px',
width: '300px',
height: '400px',
monitorSelection: { type: 'primary' },
dockToEdge: { enabled: false },
};
await startWidget('notification-popup', placement);
// Or use a named preset defined in zpack.json
await startWidgetPreset('notification-popup', 'compact');
// Open a widget from a different pack
await startWidget('volume-overlay', placement, { packId: 'audio-widgets' });
```
--------------------------------
### Local Zebar NPM Package Configuration Example
Source: https://github.com/glzr-io/zebar/blob/main/CONTRIBUTING.md
An example of a package.json file when using a locally linked zebar NPM package. The 'zebar' dependency points to a local path using a 'link:' prefix.
```json
{
"dependencies": {
"zebar": "link:../../repos/zebar/packages/client-api"
}
}
```
--------------------------------
### Start Zebar in Development Mode
Source: https://github.com/glzr-io/zebar/blob/main/CONTRIBUTING.md
Run the Zebar application in development mode. The configuration will be loaded from ~/.glzr/zebar/.
```shell
# Start in development mode. (config: ~/.glzr/zebar/)
pnpm dev
```
--------------------------------
### Audio Provider for Device Control
Source: https://context7.com/glzr-io/zebar/llms.txt
Utilizes the 'audio' provider to access and control audio devices. This example shows how to get default playback device information, list all playback devices, and set volume or mute state for specific devices. Note: This provider is Windows-only.
```typescript
import { createProvider } from 'zebar';
const audio = createProvider({ type: 'audio' });
audio.onOutput(async output => {
const dev = output.defaultPlaybackDevice;
if (!dev) return;
console.log(`Device: ${dev.name}`);
console.log(`Volume: ${dev.volume}%`);
console.log(`Muted: ${dev.isMuted}`);
console.log(`Type: ${dev.type}`); // 'playback' | 'recording'
// List all playback devices
for (const d of output.playbackDevices) {
console.log(` [${d.isDefaultPlayback ? 'default' : ' '}] ${d.name} — ${d.volume}%`);
}
// Set default playback device volume to 50%
await output.setVolume(50);
// Set volume for a specific device by ID
await output.setVolume(30, { deviceId: dev.deviceId });
// Mute/unmute a specific device
await output.setMute(true, { deviceId: dev.deviceId });
await output.setMute(false);
});
```
--------------------------------
### createProvider
Source: https://context7.com/glzr-io/zebar/llms.txt
Creates a single reactive provider instance for system data. The provider starts fetching data immediately and can be stopped using the `.stop()` method.
```APIDOC
## createProvider
### Description
Creates one provider instance from a typed config object. The generic parameter is inferred from the `type` field, so the returned object is fully typed. The provider starts fetching immediately; call `.stop()` when the widget is unmounted to clean up IPC listeners.
### Method Signature
```typescript
createProvider(config: ProviderConfig): ProviderInstance
```
### Parameters
#### `config` (object) - Required
Configuration object for the provider.
- **type** (string) - Required - The type of system data to provide (e.g., 'cpu', 'memory', 'battery').
- **refreshInterval** (number) - Optional - The interval in milliseconds to refresh the data.
- **formatting** (string) - Optional - Formatting options for specific provider types (e.g., 'date').
### Example
```typescript
import { createProvider } from 'zebar';
// CPU provider — refreshes every 2 seconds
const cpu = createProvider({ type: 'cpu', refreshInterval: 2000 });
cpu.onOutput(output => {
console.log(`CPU usage: ${output.usage.toFixed(1)}%`);
console.log(`Frequency: ${output.frequency} MHz`);
console.log(`Cores: ${output.physicalCoreCount}P / ${output.logicalCoreCount}L`);
console.log(`Vendor: ${output.vendor}`);
});
cpu.onError(err => console.error('CPU provider error:', err));
// Access the latest snapshot synchronously (null until first emission)
console.log(cpu.output?.usage);
// Restart or stop when done
await cpu.restart();
await cpu.stop();
```
```
--------------------------------
### Get OS Name, Version, Hostname, and Uptime
Source: https://context7.com/glzr-io/zebar/llms.txt
The host provider retrieves static and semi-static system identification information, including OS name, version, hostname, and uptime. Configure a refresh interval for periodic updates.
```typescript
import { createProvider } from 'zebar';
const host = createProvider({ type: 'host', refreshInterval: 60000 });
host.onOutput(output => {
console.log(`Hostname: ${output.hostname}`);
console.log(`OS name: ${output.osName}`);
// => 'Darwin' | 'Windows' | 'Debian GNU/Linux' ...
console.log(`OS ver: ${output.osVersion}`);
// => '13.2.1' | '11 22000' | '9'
console.log(`Friendly: ${output.friendlyOsVersion}`);
// => 'macOS 13.2.1' | 'Windows 11 Pro' | 'Linux Debian GNU/Linux 9'
const uptimeSec = output.uptime / 1000;
const hours = Math.floor(uptimeSec / 3600);
const mins = Math.floor((uptimeSec % 3600) / 60);
console.log(`Uptime: ${hours}h ${mins}m`);
});
```
--------------------------------
### Create Keyboard Provider for Active Keyboard Layout
Source: https://context7.com/glzr-io/zebar/llms.txt
Initializes a keyboard provider to get the active keyboard layout tag. This provider is Windows-only and can be used to adapt UI elements based on the user's input method.
```typescript
import { createProvider } from 'zebar';
const keyboard = createProvider({ type: 'keyboard', refreshInterval: 5000 });
keyboard.onOutput(output => {
console.log(`Layout: ${output.layout}`);
// => 'en-US'
document.getElementById('layout')!.textContent = output.layout;
});
```
--------------------------------
### Execute Shell Commands with Zebar
Source: https://context7.com/glzr-io/zebar/llms.txt
Use `shellExec` to run a command and get its output, or `shellSpawn` to stream output from a long-running process. Ensure the `shell` privilege is configured in `zpack.json` for the commands you intend to run. The `encoding` option can be set to 'raw' to receive binary output.
```typescript
import { shellExec, shellSpawn } from 'zebar';
// One-shot command — await completion
const result = await shellExec('git', ['rev-parse', '--short', 'HEAD'], {
cwd: '/home/user/project',
});
console.log('Git hash:', result.stdout.trim());
console.log('Exit code:', result.code);
// Long-running process — stream output
const ping = await shellSpawn('ping', ['127.0.0.1', '-c', '5']);
ping.onStdout(line => {
console.log('stdout:', line);
// => stdout: 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.041 ms
});
ping.onStderr(line => console.error('stderr:', line));
ping.onExit(({ exitCode, signal }) => {
console.log(`Exited with code ${exitCode}`);
});
// Send input to stdin or kill the process
ping.write('some input\n');
ping.kill();
// Binary output example
const raw = await shellExec('ffmpeg', ['-version'], {
encoding: 'raw',
});
console.log('Bytes:', raw.stdout.byteLength);
```
--------------------------------
### Create Network Provider for Interface Info and Traffic
Source: https://context7.com/glzr-io/zebar/llms.txt
Initializes a network provider to get network interface information and real-time traffic statistics. This cross-platform provider returns details about the default interface, gateway, and per-second received/transmitted traffic.
```typescript
import { createProvider } from 'zebar';
const network = createProvider({ type: 'network', refreshInterval: 5000 });
network.onOutput(output => {
const iface = output.defaultInterface;
if (iface) {
console.log(`Interface: ${iface.friendlyName ?? iface.name}`);
console.log(`Type: ${iface.type}`); // 'wifi' | 'ethernet' | ...
console.log(`IPv4: ${iface.ipv4Addresses.join(', ')}`);
console.log(`MAC: ${iface.macAddress}`);
}
const gw = output.defaultGateway;
if (gw) {
console.log(`SSID: ${gw.ssid ?? 'N/A'}`);
console.log(`Signal: ${gw.signalStrength ?? 'N/A'}`);
}
const traffic = output.traffic;
if (traffic) {
console.log(`↓ ${traffic.received.siValue.toFixed(1)} ${traffic.received.siUnit}/s`);
console.log(`↑ ${traffic.transmitted.siValue.toFixed(1)} ${traffic.transmitted.siUnit}/s`);
// => ↓ 1.2 MB/s
// => ↑ 0.3 MB/s
}
});
```
--------------------------------
### Initialize Zebar Providers and Render App
Source: https://github.com/glzr-io/zebar/blob/main/resources/starter/vanilla.html
Sets up Zebar providers for various system metrics and renders the main React application component. Ensure the 'root' element exists in your HTML.
```javascript
import React, { useState, useEffect, } from 'https://esm.sh/react@18?dev';
import { createRoot } from 'https://esm.sh/react-dom@18/client?dev';
import * as zebar from 'https://esm.sh/zebar@3.0';
const providers = zebar.createProviderGroup({
network: { type: 'network' },
cpu: { type: 'cpu' },
date: { type: 'date', formatting: 'EEE d MMM t' },
battery: { type: 'battery' },
memory: { type: 'memory' },
weather: { type: 'weather' },
});
createRoot(document.getElementById('root')).render();
```
--------------------------------
### Publish Widget Pack (Basic)
Source: https://github.com/glzr-io/zebar/blob/main/MARKETPLACE.md
Use this command for a basic publish operation. Ensure your API token is provided.
```bash
zebar publish --token your-api-token
```
--------------------------------
### Initialize Zebar Providers and Render React App
Source: https://github.com/glzr-io/zebar/blob/main/resources/starter/with-komorebi.html
Sets up Zebar providers for various system modules and renders the main React application component. Ensure the 'root' element exists in your HTML.
```javascript
import React, { useState, useEffect, } from 'https://esm.sh/react@18?dev';
import { createRoot } from 'https://esm.sh/react-dom@18/client?dev';
import * as zebar from 'https://esm.sh/zebar@3.0';
const providers = zebar.createProviderGroup({
network: { type: 'network' },
komorebi: { type: 'komorebi' },
cpu: { type: 'cpu' },
date: { type: 'date', formatting: 'EEE d MMM t' },
battery: { type: 'battery' },
memory: { type: 'memory' },
weather: { type: 'weather' },
});
createRoot(document.getElementById('root')).render();
```
--------------------------------
### startWidget / startWidgetPreset
Source: https://context7.com/glzr-io/zebar/llms.txt
Open another widget programmatically. Spawns a widget window at a specified placement (or using a named preset from `zpack.json`). Useful for popup widgets triggered from a main bar.
```APIDOC
## startWidget / startWidgetPreset
### Description
Open another widget programmatically. Spawns a widget window at a specified placement (or using a named preset from `zpack.json`). Useful for popup widgets triggered from a main bar.
### Usage
```typescript
import { startWidget, startWidgetPreset, type WidgetPlacement } from 'zebar';
// Open a popup at the top-right corner of the primary monitor
const placement: WidgetPlacement = {
anchor: 'top_right',
offsetX: '0',
offsetY: '40px',
width: '300px',
height: '400px',
monitorSelection: { type: 'primary' },
dockToEdge: { enabled: false },
};
await startWidget('notification-popup', placement);
// Or use a named preset defined in zpack.json
await startWidgetPreset('notification-popup', 'compact');
// Open a widget from a different pack
await startWidget('volume-overlay', placement, { packId: 'audio-widgets' });
```
```
--------------------------------
### Monitor Battery Status with Zebar
Source: https://context7.com/glzr-io/zebar/llms.txt
Use the battery provider to get real-time charge percentage, health, state, and power consumption. Configure a refresh interval to receive periodic updates.
```typescript
import { createProvider } from 'zebar';
const battery = createProvider({ type: 'battery', refreshInterval: 5000 });
battery.onOutput(output => {
console.log(`Charge: ${output.chargePercent}%`);
console.log(`Health: ${output.healthPercent}%`);
console.log(`State: ${output.state}`);
// => 'charging' | 'discharging' | 'full' | 'empty' | 'unknown'
console.log(`Charging: ${output.isCharging}`);
console.log(`Power: ${output.powerConsumption}W`);
console.log(`Voltage: ${output.voltage}V`);
console.log(`Cycles: ${output.cycleCount}`);
if (output.timeTillEmpty !== null) {
const mins = Math.round(output.timeTillEmpty / 60000);
console.log(`Time till empty: ${mins} min`);
}
if (output.timeTillFull !== null) {
const mins = Math.round(output.timeTillFull / 60000);
console.log(`Time till full: ${mins} min`);
}
});
```
--------------------------------
### Create and Use a Provider Group
Source: https://context7.com/glzr-io/zebar/llms.txt
Shows how to create multiple providers (CPU, memory, battery, date) under named keys using `createProviderGroup`. The `onOutput` callback fires when any provider emits, passing an `outputMap`. Error handling for individual providers is also demonstrated.
```typescript
import { createProviderGroup } from 'zebar';
const group = createProviderGroup({
cpu: { type: 'cpu', refreshInterval: 2000 },
memory: { type: 'memory', refreshInterval: 5000 },
battery: { type: 'battery', refreshInterval: 10000 },
date: { type: 'date', formatting: 'HH:mm:ss', refreshInterval: 1000 },
});
group.onOutput(({ cpu, memory, battery, date }) => {
const cpuPct = cpu?.usage.toFixed(1) ?? '--';
const memPct = memory?.usage.toFixed(1) ?? '--';
const batPct = battery?.chargePercent ?? '--';
const charging = battery?.isCharging ? '⚡' : '🔋';
const timeStr = date?.formatted ?? '--';
document.getElementById('status-bar')!.textContent =
`${timeStr} CPU ${cpuPct}% MEM ${memPct}% ${charging}${batPct}%`;
});
group.onError(errorMap => {
for (const [name, err] of Object.entries(errorMap)) {
if (err) console.error(`[${name}] ${err}`);
}
});
// Access individual raw providers
console.log(group.raw.battery.output?.state); // 'charging' | 'discharging' | ...
// Stop all providers at once
await group.stopAll();
```
--------------------------------
### Build for Production
Source: https://github.com/glzr-io/zebar/blob/main/packages/settings-ui/README.md
Builds the application for production, optimizing for performance and creating minified assets with hashes.
```bash
npm run build
```
--------------------------------
### Create and Use a Single CPU Provider
Source: https://context7.com/glzr-io/zebar/llms.txt
Demonstrates creating a CPU provider that refreshes every 2 seconds. It logs CPU usage, frequency, core count, and vendor. Remember to call `.stop()` when the widget is unmounted.
```typescript
import { createProvider } from 'zebar';
// CPU provider — refreshes every 2 seconds
const cpu = createProvider({ type: 'cpu', refreshInterval: 2000 });
cpu.onOutput(output => {
console.log(`CPU usage: ${output.usage.toFixed(1)}%`);
console.log(`Frequency: ${output.frequency} MHz`);
console.log(`Cores: ${output.physicalCoreCount}P / ${output.logicalCoreCount}L`);
console.log(`Vendor: ${output.vendor}`);
// => CPU usage: 14.3%
// => Frequency: 3600 MHz
// => Cores: 8P / 16L
// => Vendor: GenuineIntel
});
cpu.onError(err => console.error('CPU provider error:', err));
// Access the latest snapshot synchronously (null until first emission)
console.log(cpu.output?.usage);
// Restart or stop when done
await cpu.restart();
await cpu.stop();
```
--------------------------------
### Executing Shell Commands
Source: https://context7.com/glzr-io/zebar/llms.txt
Demonstrates how to execute shell commands from a widget using `shellExec` for one-off commands and `shellSpawn` for long-running processes with streaming output. Requires the `shell` privilege.
```APIDOC
## `shellExec` / `shellSpawn` — Execute shell commands from a widget
`shellExec` runs a command and awaits completion, returning stdout/stderr. `shellSpawn` launches a long-running process and provides callbacks for streaming output. Requires the `shell` privilege in the widget config.
```typescript
import { shellExec, shellSpawn } from 'zebar';
// One-shot command — await completion
const result = await shellExec('git', ['rev-parse', '--short', 'HEAD'], {
cwd: '/home/user/project',
});
console.log('Git hash:', result.stdout.trim());
console.log('Exit code:', result.code);
// Long-running process — stream output
const ping = await shellSpawn('ping', ['127.0.0.1', '-c', '5']);
ping.onStdout(line => {
console.log('stdout:', line);
// => stdout: 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.041 ms
});
ping.onStderr(line => console.error('stderr:', line));
ping.onExit(({ exitCode, signal }) => {
console.log(`Exited with code ${exitCode}`);
});
// Send input to stdin or kill the process
ping.write('some input\n');
ping.kill();
// Binary output example
const raw = await shellExec('ffmpeg', ['-version'], {
encoding: 'raw',
});
console.log('Bytes:', raw.stdout.byteLength);
```
```
--------------------------------
### Systray Provider
Source: https://context7.com/glzr-io/zebar/llms.txt
Exposes all tray icons as ready-to-use image URLs plus interaction event dispatchers. Windows-only.
```APIDOC
## Systray Provider
### Description
Exposes all tray icons as ready-to-use image URLs plus interaction event dispatchers. Windows-only.
### Usage
```typescript
import { createProvider } from 'zebar';
const systray = createProvider({ type: 'systray' });
systray.onOutput(output => {
const container = document.getElementById('tray')!;
container.innerHTML = '';
for (const icon of output.icons) {
const img = document.createElement('img');
img.src = icon.iconUrl; // PNG blob URL — safe to put in
img.title = icon.tooltip;
img.addEventListener('click', () => output.onLeftClick(icon.id));
img.addEventListener('contextmenu', e => {
e.preventDefault();
output.onRightClick(icon.id);
});
img.addEventListener('mouseenter', () => output.onHoverEnter(icon.id));
img.addEventListener('mouseleave', () => output.onHoverLeave(icon.id));
img.addEventListener('dblclick', () => output.onLeftDoubleClick(icon.id));
container.appendChild(img);
}
});
```
```
--------------------------------
### React to GlazeWM Tiling Window Manager Events
Source: https://context7.com/glzr-io/zebar/llms.txt
Connect to GlazeWM's IPC server to receive real-time workspace, monitor, and window state. This provider also allows issuing WM commands like focusing workspaces or changing window states. It is Windows-only.
```typescript
import { createProvider } from 'zebar';
const wm = createProvider({ type: 'glazewm' });
wm.onOutput(async output => {
// Render workspace buttons
for (const ws of output.currentWorkspaces) {
const isFocused = ws.name === output.focusedWorkspace.name;
const isDisplayed = ws.name === output.displayedWorkspace.name;
console.log(`[${ws.name}] focused=${isFocused} displayed=${isDisplayed}`);
}
// Show focused window title
const fc = output.focusedContainer;
if (fc.type === 'window') {
console.log(`Active window: ${fc.title}`);
}
console.log(`Tiling direction: ${output.tilingDirection}`);
console.log(`WM paused: ${output.isPaused}`);
// Issue WM commands
await output.runCommand('focus --workspace 2');
await output.runCommand('set-floating');
await output.runCommand('set-tiling', fc.id);
});
wm.onError(err => console.error('GlazeWM not running:', err));
```
--------------------------------
### Create Komorebi Provider for Tiling Window Manager Info
Source: https://context7.com/glzr-io/zebar/llms.txt
Connects to Komorebi's named pipe IPC to expose workspace and monitor topology. This provider is Windows-only and useful for integrating with tiling window manager features.
```typescript
import { createProvider } from 'zebar';
const km = createProvider({ type: 'komorebi' });
km.onOutput(output => {
const ws = output.displayedWorkspace;
console.log(`Workspace: ${ws.name ?? '(unnamed)'}`);
console.log(`Layout: ${ws.layout}`);
// => 'bsp' | 'vertical_stack' | 'grid' | ...
const tiled = ws.tilingContainers.flatMap(c => c.windows);
for (const win of tiled) {
console.log(` Window: ${win.title} (${win.exe})`);
}
// Monitor info
console.log(`Current monitor: ${output.currentMonitor.name}`);
console.log(`All monitors: ${output.allMonitors.length}`);
console.log(`All workspaces: ${output.allWorkspaces.length}`);
});
km.onError(err => console.error('Komorebi not running:', err));
```
--------------------------------
### Publish Widget Pack with Metadata
Source: https://github.com/glzr-io/zebar/blob/main/MARKETPLACE.md
Publish a widget pack with additional metadata for a richer marketplace listing. This includes specifying the pack configuration file, overriding the version, and providing commit SHA, release notes, and a release URL.
```bash
zebar publish --token your-api-token \
--pack-config ./my-pack/zpack.json \
--version-override 1.2.0 \
--commit-sha abc123def456 \
--release-notes "Fixed performance issues and added new themes." \
--release-url "https://github.com/username/pack-repo/releases/v1.2.0"
```
--------------------------------
### React App with Zebar Providers
Source: https://github.com/glzr-io/zebar/blob/main/resources/templates/widget-templates/react-buildless/index.html
This snippet sets up Zebar providers for various system resources and renders them in a React component. It uses `createProviderGroup` to manage multiple providers and `onOutput` for real-time updates. Ensure React and Zebar are correctly imported.
```javascript
import React, { useState, useEffect, } from 'https://esm.sh/react@18?dev'; import { createRoot } from 'https://esm.sh/react-dom@18/client?dev'; import * as zebar from 'https://esm.sh/zebar@3.0'; const providers = zebar.createProviderGroup({ cpu: { type: 'cpu' }, battery: { type: 'battery' }, memory: { type: 'memory' }, weather: { type: 'weather' }, media: { type: 'media' }, audio: { type: 'audio' }, }); createRoot(document.getElementById('root')).render(); function App() { const [output, setOutput] = useState(providers.outputMap); useEffect(() => { providers.onOutput(() => setOutput(providers.outputMap)); }, []); return (
); }
```
--------------------------------
### Link Local Zebar Client-API Package
Source: https://github.com/glzr-io/zebar/blob/main/CONTRIBUTING.md
Link a local version of the zebar client-api package into your widget project. This is useful for developing the client-api and widgets simultaneously. Adjust the path '../../path/to/zebar/packages/client-api' to point to your local client-api directory.
```shell
cd path/to/your/widget
# If using pnpm:
pnpm add --link ../../path/to/zebar/packages/client-api
# If using npm:
npm install ../../path/to/zebar/packages/client-api
```
--------------------------------
### Rust Systray Event Handling
Source: https://github.com/glzr-io/zebar/blob/main/crates/systray-util/README.md
Demonstrates how to initialize the systray, listen for icon events (add, update, remove) using blocking calls, and send a click action to an icon. Use this for synchronous applications.
```rust
use systray_util::{Systray, SystrayEvent, SystrayIconAction};
fn main() -> systray_util::Result<()> {
let mut systray = Systray::new()?;
// Alternatively use `systray.events().await` to get async events for use
// with a tokio runtime.
while let Some(event) = systray.events_blocking() {
match event {
SystrayEvent::IconAdd(icon) => {
println!("Tray icon added: {:?}", icon);
}
SystrayEvent::IconUpdate(icon) => {
println!("Tray icon updated: {:?}", icon);
}
SystrayEvent::IconRemove(id) => {
println!("Tray icon removed: {:?}", id);
}
}
}
// Send click action to first icon.
if let Some(icon) = systray.icons().first() {
systray.send_action(&icon.stable_id, &SystrayIconAction::LeftClick)?;
}
Ok(())
}
```
--------------------------------
### Zebar Widget Pack Configuration (`zpack.json`)
Source: https://context7.com/glzr-io/zebar/llms.txt
Defines a widget pack, including widget properties like HTML path, window behavior, and security privileges. The `privileges.shellCommands` array explicitly lists programs and their arguments that the widget is allowed to execute.
```json
{
"$schema": "https://raw.githubusercontent.com/glzr-io/zebar/main/resources/zpack-schema.json",
"name": "My Status Bar",
"version": "1.0.0",
"description": "A minimal status bar with system info.",
"tags": ["bar", "minimal"],
"widgets": [
{
"name": "bar",
"htmlPath": "./bar/index.html",
"zOrder": "bottom_most",
"shownInTaskbar": false,
"focused": false,
"resizable": false,
"transparent": true,
"includeFiles": [],
"caching": { "defaultOnlineUrl": null, "defaultMonitorId": null },
"privileges": {
"shellCommands": [
{ "program": "git", "args": [] },
{ "program": "ping", "args": [] }
]
},
"presets": [
{
"name": "default",
"anchor": "top_left",
"offsetX": "0",
"offsetY": "0",
"width": "100%",
"height": "40px",
"monitorSelection": { "type": "all" },
"dockToEdge": { "enabled": true, "edge": "top", "windowMargin": "40px" }
}
]
}
]
}
```
--------------------------------
### Create Media Provider for Media Playback Control
Source: https://context7.com/glzr-io/zebar/llms.txt
Initializes a media provider to control and monitor media playback sessions. This provider is Windows-only and allows interacting with the current media session, including play/pause and skip functions.
```typescript
import { createProvider } from 'zebar';
const media = createProvider({ type: 'media' });
media.onOutput(async output => {
const session = output.currentSession;
if (!session) {
console.log('No active media session');
return;
}
console.log(`Title: ${session.title}`);
console.log(`Artist: ${session.artist}`);
console.log(`Album: ${session.albumTitle}`);
console.log(`Playing: ${session.isPlaying}`);
console.log(`Position: ${session.position.toFixed(0)}s / ${session.endTime.toFixed(0)}s`);
// Playback controls (target current session implicitly)
await output.togglePlayPause();
await output.next();
await output.previous();
// Target a specific session by ID
const spotifySession = output.allSessions.find(s => s.title === 'Spotify');
if (spotifySession) {
await output.play({ sessionId: spotifySession.sessionId });
}
});
```
--------------------------------
### Create IP Provider to Fetch Public IP and Geolocation
Source: https://context7.com/glzr-io/zebar/llms.txt
Initializes an IP provider to fetch the public IP address and approximate geolocation. The provider refreshes hourly by default and is cross-platform. Use this to display user's IP and location.
```typescript
import { createProvider } from 'zebar';
const ip = createProvider({ type: 'ip', refreshInterval: 3600000 });
ip.onOutput(output => {
console.log(`IP: ${output.address}`);
// => '203.0.113.42'
console.log(`City: ${output.approxCity}`);
console.log(`Country: ${output.approxCountry}`);
console.log(`Lat/Lon: ${output.approxLatitude}, ${output.approxLongitude}`);
});
```
--------------------------------
### Disk Information with Space and Filesystem Details
Source: https://context7.com/glzr-io/zebar/llms.txt
The disk provider enumerates mounted disks, providing total and available space in both SI and IEC units. It also includes filesystem type and removable status. Configure a refresh interval for periodic updates.
```typescript
import { createProvider } from 'zebar';
const disk = createProvider({ type: 'disk', refreshInterval: 60000 });
disk.onOutput(output => {
for (const d of output.disks) {
const usedGB = (d.totalSpace.siValue - d.availableSpace.siValue).toFixed(1);
const totalGB = d.totalSpace.siValue.toFixed(1);
const pct = ((1 - d.availableSpace.bytes / d.totalSpace.bytes) * 100).toFixed(1);
console.log(`${d.mountPoint} (${d.driveType})`);
console.log(` FS: ${d.fileSystem}`);
console.log(` Used: ${usedGB} ${d.totalSpace.siUnit} / ${totalGB} ${d.totalSpace.siUnit} (${pct}%)`);
console.log(` Removable: ${d.isRemovable}`);
// => C:\ (SSD)
// => FS: NTFS
// => Used: 312.4 GB / 512.0 GB (61.0%)
// => Removable: false
}
});
```
--------------------------------
### Create Memory Provider for RAM and Swap Usage
Source: https://context7.com/glzr-io/zebar/llms.txt
Initializes a memory provider to fetch RAM and swap usage statistics. This provider is cross-platform and provides memory usage percentage and absolute free/used/total values in bytes.
```typescript
import { createProvider } from 'zebar';
const memory = createProvider({ type: 'memory', refreshInterval: 5000 });
memory.onOutput(output => {
const usedGB = (output.usedMemory / 1e9).toFixed(1);
const totalGB = (output.totalMemory / 1e9).toFixed(1);
console.log(`RAM: ${usedGB}GB / ${totalGB}GB (${output.usage.toFixed(1)}%)`);
if (output.totalSwap > 0) {
const swapGB = (output.usedSwap / 1e9).toFixed(1);
const swapTotalGB = (output.totalSwap / 1e9).toFixed(1);
console.log(`Swap: ${swapGB}GB / ${swapTotalGB}GB`);
}
});
```
--------------------------------
### Clone the Zebar Repository
Source: https://github.com/glzr-io/zebar/blob/main/CONTRIBUTING.md
Clone the Zebar repository to your local machine using Git. Replace 'your-username' with your GitHub username.
```shell
git clone git@github.com:your-username/zebar.git
```
--------------------------------
### Render Windows System Tray Icons with Zebar
Source: https://context7.com/glzr-io/zebar/llms.txt
Use the systray provider to expose tray icons as image URLs and dispatch interaction events. This is a Windows-only feature.
```typescript
import { createProvider } from 'zebar';
const systray = createProvider({ type: 'systray' });
systray.onOutput(output => {
const container = document.getElementById('tray')!;
container.innerHTML = '';
for (const icon of output.icons) {
const img = document.createElement('img');
img.src = icon.iconUrl; // PNG blob URL — safe to put in
img.title = icon.tooltip;
img.addEventListener('click', () => output.onLeftClick(icon.id));
img.addEventListener('contextmenu', e => {
e.preventDefault();
output.onRightClick(icon.id);
});
img.addEventListener('mouseenter', () => output.onHoverEnter(icon.id));
img.addEventListener('mouseleave', () => output.onHoverLeave(icon.id));
img.addEventListener('dblclick', () => output.onLeftDoubleClick(icon.id));
container.appendChild(img);
}
});
```
--------------------------------
### Troubleshoot MacOS Settings UI Outdated Dependencies
Source: https://github.com/glzr-io/zebar/blob/main/CONTRIBUTING.md
Resolve issues with the MacOS settings UI using outdated dependencies by deleting the .vite directory and rerunning the development server.
```shell
1. Delete the `packages/settings-ui/.vite` directory
2. Run `pnpm dev` again
```
--------------------------------
### Widget Pack Configuration (`zpack.json`)
Source: https://context7.com/glzr-io/zebar/llms.txt
Defines the manifest file for a widget pack, specifying widget behavior, placement, caching, and security privileges like shell command execution.
```APIDOC
## `zpack.json` Widget Pack Configuration
The manifest file that defines a widget pack. Each entry in `widgets` maps to an HTML file and controls window behavior, placement presets, caching, and security privileges.
```json
{
"$schema": "https://raw.githubusercontent.com/glzr-io/zebar/main/resources/zpack-schema.json",
"name": "My Status Bar",
"version": "1.0.0",
"description": "A minimal status bar with system info.",
"tags": ["bar", "minimal"],
"widgets": [
{
"name": "bar",
"htmlPath": "./bar/index.html",
"zOrder": "bottom_most",
"shownInTaskbar": false,
"focused": false,
"resizable": false,
"transparent": true,
"includeFiles": [],
"caching": { "defaultOnlineUrl": null, "defaultMonitorId": null },
"privileges": {
"shellCommands": [
{ "program": "git", "args": [] },
{ "program": "ping", "args": [] }
]
},
"presets": [
{
"name": "default",
"anchor": "top_left",
"offsetX": "0",
"offsetY": "0",
"width": "100%",
"height": "40px",
"monitorSelection": { "type": "all" },
"dockToEdge": { "enabled": true, "edge": "top", "windowMargin": "40px" }
}
]
}
]
}
```
```
--------------------------------
### createProviderGroup
Source: https://context7.com/glzr-io/zebar/llms.txt
Composes multiple providers under named keys, allowing for a unified callback when any provider emits. This is useful for updating the UI efficiently.
```APIDOC
## createProviderGroup
### Description
Creates several providers at once under named keys. `onOutput` fires whenever **any** member emits, passing the full `outputMap` so the widget can re-render once even when multiple values change in quick succession.
### Method Signature
```typescript
createProviderGroup(providersConfig: { [key: string]: ProviderConfig }): ProviderGroupInstance
```
### Parameters
#### `providersConfig` (object) - Required
An object where keys are provider names and values are provider configuration objects.
- **cpu** (ProviderConfig) - Configuration for the CPU provider.
- **memory** (ProviderConfig) - Configuration for the memory provider.
- **battery** (ProviderConfig) - Configuration for the battery provider.
- **date** (ProviderConfig) - Configuration for the date provider.
### Example
```typescript
import { createProviderGroup } from 'zebar';
const group = createProviderGroup({
cpu: { type: 'cpu', refreshInterval: 2000 },
memory: { type: 'memory', refreshInterval: 5000 },
battery: { type: 'battery', refreshInterval: 10000 },
date: { type: 'date', formatting: 'HH:mm:ss', refreshInterval: 1000 },
});
group.onOutput(({ cpu, memory, battery, date }) => {
const cpuPct = cpu?.usage.toFixed(1) ?? '--';
const memPct = memory?.usage.toFixed(1) ?? '--';
const batPct = battery?.chargePercent ?? '--';
const charging = battery?.isCharging ? '⚡' : '🔋';
const timeStr = date?.formatted ?? '--';
document.getElementById('status-bar')!.textContent =
`${timeStr} CPU ${cpuPct}% MEM ${memPct}% ${charging}${batPct}%`;
});
group.onError(errorMap => {
for (const [name, err] of Object.entries(errorMap)) {
if (err) console.error(`[${name}] ${err}`);
}
});
// Access individual raw providers
console.log(group.raw.battery.output?.state); // 'charging' | 'discharging' | ...
// Stop all providers at once
await group.stopAll();
```
```
--------------------------------
### Audio Provider
Source: https://context7.com/glzr-io/zebar/llms.txt
Controls and monitors audio devices, including setting volume and mute state. This provider is Windows-only.
```APIDOC
## Audio Provider
### Description
Exposes all playback/recording devices and allows setting volume or mute state per device. Windows-only.
### Usage
Use `createProvider` with `type: 'audio'`.
### Example
```typescript
import { createProvider } from 'zebar';
const audio = createProvider({ type: 'audio' });
audio.onOutput(async output => {
const dev = output.defaultPlaybackDevice;
if (!dev) return;
console.log(`Device: ${dev.name}`);
console.log(`Volume: ${dev.volume}%`);
console.log(`Muted: ${dev.isMuted}`);
console.log(`Type: ${dev.type}`); // 'playback' | 'recording'
// List all playback devices
for (const d of output.playbackDevices) {
console.log(` [${d.isDefaultPlayback ? 'default' : ' '}] ${d.name} — ${d.volume}%`);
}
// Set default playback device volume to 50%
await output.setVolume(50);
// Set volume for a specific device by ID
await output.setVolume(30, { deviceId: dev.deviceId });
// Mute/unmute a specific device
await output.setMute(true, { deviceId: dev.deviceId });
await output.setMute(false);
});
```
```