General Options
Options shared by every UI element. These properties apply to all ui.add*() calls — addText, addImage, addButton, addBitmap, addBar, addLine, addAreaGraph, addHistogram, addRotator, addRoundLine, addShape, addInputBox, and addLayoutBox.
For shared image-processing fields (imageAlpha, imageTint, imageFlip, colorMatrix, etc.) see General Image Options below.
ui.addText({
id: "label",
x: 16, y: 14,
width: 260, height: 28,
fontSize: 14,
fontColor: "rgb(230,230,230)"
});Table of Contents
Layout and Positioning
Unique identifier for the element. While not technically required by the parser, it is needed in practice to update or remove the element later. Creating an element with an id that already exists replaces the previous element.
0Horizontal position in pixels, relative to the top-left corner of the widget window.
0Vertical position in pixels, relative to the top-left corner of the widget window.
0Element width in pixels. When 0 or omitted, the engine sizes the element to fit its content.
0Element height in pixels. When 0 or omitted, the engine sizes the element to fit its content.
0Inner spacing between the element bounds and its rendered content. Accepts three forms:
padding: 10— all four sides equalpadding: [horizontal, vertical]— left/right = first, top/bottom = secondpadding: [left, top, right, bottom]— each side individually
Arrays shorter than 2 are ignored. Arrays of 3 are treated as 2.
padding: 8 // all sides
padding: [12, 4] // left/right 12, top/bottom 4
padding: [8, 4, 8, 4] // explicit per side0Rotation angle in degrees, applied around the element center. When both rotate and transformMatrix are set, transformMatrix takes effect because it is applied last.
Six-element affine transformation matrix [m11, m12, m21, m22, dx, dy]. Enables translation, scaling, rotation, and shearing in a single operation. Requires exactly 6 values — fewer than 6 are silently ignored.
// 45-degree shear
transformMatrix: [1, 0.5, 0, 1, 0, 0]Overrides rotate
When both rotate and transformMatrix are present, transformMatrix is applied last and wins. Use one or the other, not both.
Visibility
trueControls element visibility. false hides the element without removing it. Hidden elements still occupy their position and receive no mouse events. Set show: true to reveal a hidden element.
ui.setElementProperties("panel", { show: false }); // hide
ui.setElementProperties("panel", { show: true }); // show againGrouping and Containers
""Logical group name for batch operations via ui.setElementPropertiesByGroup() and ui.removeElementsByGroup(). Grouping is organizational only and has no effect on rendering or clipping.
Cannot be cleared via setElementProperties
The parser ignores empty strings for group. Passing group: "" does not remove an element from its group.
ui.addText({ id: "title", group: "stats", text: "CPU" });
ui.addBar({ id: "bar", group: "stats", value: 0.5 });
// Update all elements in the group at once
ui.setElementPropertiesByGroup("stats", { show: false });""ID of an existing container element (addLayoutBox). The child element is clipped to the container bounds and moves with it.
The container must already exist when the child is created. An element cannot be its own container. Passing container: "" via setElementProperties has no effect.
Appearance
trueEnables anti-aliased rendering for smoother edges. Disable only when rendering pixel-perfect bitmaps where sub-pixel smoothing would cause blurring.
falseSelects the hit-testing mode for mouse interactions.
false— bounding-box hit testing. Fast and broad.true— pixel-aware hit testing that follows the visible shape of the element.
Only set when explicitly present
Omitting pixelHitTest from an add* or setElementProperties call preserves the element's existing value. Only an explicit true or false changes it.
""Background fill drawn behind all element content. Supports rgb(), rgba(), hex, linearGradient(), and radialGradient(). An empty string draws no background.
backgroundColor: "rgba(30,30,40,0.9)"
backgroundColor: "rgb(20,20,28)"
backgroundColor: "#1e1e2c"0Corner radius in pixels for the background fill. 0 produces square corners.
"none"Draws a decorative border effect around the element.
| Value | Appearance |
|---|---|
"none" | No bevel (default) |
"raised" | Raised button appearance |
"sunken" | Sunken or pressed appearance |
"emboss" | Embossed border |
"pillow" | Pillow or cushion border |
Unrecognized non-empty strings map to no bevel.
0Thickness of the bevel in pixels.
Highlight edge color for the bevel (top and left edges when bevelType is "raised"). Defaults to rgba(255,255,255,200). Only parsed when non-empty. Supports gradients.
Shadow edge color for the bevel (bottom and right edges when bevelType is "raised"). Defaults to rgba(0,0,0,150). Only parsed when non-empty. Supports gradients.
Tooltip
Tooltips appear on hover. tooltipText must be non-empty for any tooltip to display — tooltipTitle or tooltipIcon alone will not show anything.
""Tooltip body text shown on hover. This is the gating condition — all other tooltip properties are ignored when this is empty.
ui.addBar({
id: "cpu",
value: 0.72,
tooltipText: "CPU usage: 72%"
});""Bold title line shown above tooltipText.
"none"Icon displayed next to the tooltip title. Valid values: "none", "info", "warning", "error".
falsetrue renders a cartoon balloon-style tooltip instead of the standard flat style.
0Maximum tooltip width in pixels. Text wraps beyond this limit. A value of 0 (the default) applies a runtime limit of 1000 px.
0Maximum tooltip height in pixels. A value of 0 (the default) applies a runtime limit of 1000 px.
falseDisables tooltip display for this element even when tooltipText is set.
Cursor
Cursor options only take effect when the element has at least one mouse callback registered.
truetrue shows the cursor defined by mouseEventCursorName when hovering over an interactive element. false suppresses cursor changes even when callbacks are registered.
""Name of the cursor to display on hover. When empty, defaults to the hand cursor.
Built-in cursor names: hand, text, help, busy, cross, pen, no, size_all, size_nesw, size_ns, size_nwse, size_we, uparrow, wait
When cursorsDir is set, this name is looked up as a file in that folder.
""Directory containing custom .cur or .ani cursor files. Relative paths are resolved against the widget's script directory. When set, mouseEventCursorName is looked up as a file inside this folder instead of using a built-in cursor.
Mouse Events
All mouse callbacks are optional. Each receives an event object — see Global Variables for the full list of event properties (__clientX, __clientY, __screenX, __screenY, __offsetX, __offsetY, __offsetXPercent, __offsetYPercent).
SIGNATURE
onLeftMouseUp(event): voidFired when the left mouse button is released over the element. This is the standard click handler.
ui.addText({
id: "btn",
text: "Click me",
onLeftMouseUp: (e) => {
ipcRenderer.send("btn-clicked");
}
});SIGNATURE
onLeftMouseDown(event): voidFired when the left mouse button is pressed down over the element.
SIGNATURE
onLeftDoubleClick(event): voidFired on a left-button double-click over the element.
SIGNATURE
onRightMouseUp(event): voidFired when the right mouse button is released over the element.
SIGNATURE
onRightMouseDown(event): voidFired when the right mouse button is pressed down over the element.
SIGNATURE
onRightDoubleClick(event): voidFired on a right-button double-click over the element.
SIGNATURE
onMiddleMouseUp(event): voidFired when the middle mouse button is released over the element.
SIGNATURE
onMiddleMouseDown(event): voidFired when the middle mouse button is pressed down over the element.
SIGNATURE
onMiddleDoubleClick(event): voidFired on a middle-button double-click over the element.
SIGNATURE
onX1MouseUp(event): voidEvents for the X1 (Back) side button.
SIGNATURE
onX2MouseUp(event): voidEvents for the X2 (Forward) side button.
SIGNATURE
onMouseOver(event): voidFired when the cursor enters the element bounds.
onMouseOver: (e) => {
ui.setElementProperties("btn", { backgroundColor: "rgba(255,255,255,0.1)" });
}SIGNATURE
onMouseLeave(event): voidFired when the cursor leaves the element bounds.
onMouseLeave: (e) => {
ui.setElementProperties("btn", { backgroundColor: "" });
}SIGNATURE
onScrollUp(event): voidFired when the mouse wheel is scrolled up over the element.
SIGNATURE
onScrollDown(event): voidFired when the mouse wheel is scrolled down over the element.
SIGNATURE
onScrollLeft(event): voidFired on horizontal scroll left over the element.
SIGNATURE
onScrollRight(event): voidFired on horizontal scroll right over the element.
Drag & Drop
Drag and drop properties control whether an element can be dragged or act as a drop target. Drag callbacks fire when the user holds a mouse button on the element and moves the mouse.
falsetrue makes this element draggable. The user can click and drag the element to move it. The element does not snap or reposition automatically — use the onDrag callback to update the element position or send drag data via IPC.
ui.addShape({
id: "handle",
dragArea: true,
onDrag: (e) => {
ui.setElementProperties("handle", {
x: e.__screenX - e.__offsetX,
y: e.__screenY - e.__offsetY
});
}
});falsetrue registers this element as a drop target. When another element is dragged over it, the drop-related callbacks fire. Use this for file drop zones, reordering lists, or any drag-and-drop interaction.
ui.addShape({
id: "drop-zone",
dropTarget: true,
backgroundColor: "rgba(0,180,255,0.1)",
onDragEnter: () => {
ui.setElementProperties("drop-zone", { backgroundColor: "rgba(0,180,255,0.3)" });
},
onDragLeave: () => {
ui.setElementProperties("drop-zone", { backgroundColor: "rgba(0,180,255,0.1)" });
},
onDrop: (e) => {
console.log("Dropped at:", e.__offsetX, e.__offsetY);
}
});SIGNATURE
onDrop(event): voidFired when the user releases a drag over this drop target element.
SIGNATURE
onDragEnter(event): voidFired when a dragged element enters the bounds of this drop target.
SIGNATURE
onDragOver(event): voidFired continuously while a dragged element is over this drop target.
SIGNATURE
onDragLeave(event): voidFired when a dragged element leaves the bounds of this drop target.
Drag Events (on element being dragged)
Drag callbacks fire when the user holds a mouse button on the element and moves the mouse. They are useful for sliders, handles, and custom drag interactions.
SIGNATURE
onDragStart(event): voidFired once when a drag begins.
onDragStart: (e) => {
console.log("Drag started at", e.__offsetX, e.__offsetY);
}SIGNATURE
onDrag(event): voidFired continuously while the user is dragging. Use e.__offsetXPercent and e.__offsetYPercent for normalized 0–100 position within the element.
onDrag: (e) => {
const value = Math.max(0, Math.min(100, e.__offsetXPercent)) / 100;
ui.setElementProperty("vol-bar", "value", value);
ipcRenderer.send("volume-change", { value });
}SIGNATURE
onDragEnd(event): voidFired once when the mouse button is released after a drag.
onDragEnd: (e) => {
console.log("Drag ended at", e.__offsetXPercent.toFixed(1) + "%");
}Scroll & Overflow
When child elements extend beyond the bounds of a container (or addLayoutBox), scroll and overflow properties control whether scrollbars appear and how content is clipped.
"visible"Shorthand that sets both overflowX and overflowY at once. Case-insensitive.
| Value | Behavior |
|---|---|
"visible" | Content overflows the element bounds and is not clipped (default) |
"hidden" | Content is clipped at the element bounds, no scrollbar |
"scroll" | Content is clipped and scrollbars appear when needed |
"auto" | Scrollbars appear only when content overflows |
"visible"Horizontal overflow mode. Same values as overflow. Overrides overflow for the horizontal axis.
"visible"Vertical overflow mode. Same values as overflow. Overrides overflow for the vertical axis.
0Initial horizontal scroll offset in pixels.
0Initial vertical scroll offset in pixels.
24Number of pixels scrolled per mouse wheel notch.
trueShorthand that sets both showScrollbarX and showScrollbarY at once.
trueShow the horizontal scrollbar when horizontal content overflows.
trueShow the vertical scrollbar when vertical content overflows.
Scrollbar Styling
6Scrollbar thickness in pixels.
-1Scrollbar thickness when hovered. -1 keeps the normal width.
3Corner radius of the scrollbar thumb.
-1Corner radius of the scrollbar track. -1 uses the default.
2Spacing between the scrollbar and the element edge in pixels.
20Minimum thumb length in pixels.
"rgba(255,255,255,100)"Scrollbar thumb color. Supports rgb(), rgba(), and hex.
"rgba(255,255,255,180)"Scrollbar thumb color when hovered.
"rgba(255,255,255,240)"Scrollbar thumb color when actively dragged.
"rgba(0,0,0,0)"Scrollbar track background color.
falseShow arrow buttons at the ends of the scrollbar.
14Size of the scrollbar arrow buttons in pixels.
2Corner radius of the scrollbar arrow buttons.
"rgba(255,255,255,150)"Color of the scrollbar arrow icons.
"rgba(255,255,255,220)"Color of the scrollbar arrow icons when hovered.
"rgba(255,255,255,255)"Color of the scrollbar arrow icons when pressed.
"rgba(0,0,0,0)"Background color of the scrollbar arrow buttons.
"rgba(255,255,255,30)"Background color of the scrollbar arrow buttons when hovered.
// Scrollable container with custom scrollbar
ui.addLayoutBox({
id: "scroll-area",
x: 16, y: 16,
width: 300, height: 200,
overflow: "scroll",
scrollbarWidth: 8,
scrollbarColor: "rgba(255,255,255,80)",
scrollbarHoverColor: "rgba(255,255,255,160)",
scrollbarRadius: 4
});Practical Examples
Hover highlight
ui.addShape({
id: "btn",
shapeType: "rectangle",
x: 16, y: 16,
width: 120, height: 36,
radiusX: 6, radiusY: 6,
fillColor: "rgba(60,120,200,0.8)",
onMouseOver: () => {
ui.setElementProperties("btn", { fillColor: "rgba(80,150,240,0.9)" });
},
onMouseLeave: () => {
ui.setElementProperties("btn", { fillColor: "rgba(60,120,200,0.8)" });
},
onLeftMouseUp: () => {
ipcRenderer.send("btn-action");
}
});Drag slider
ui.addImage({
id: "slider",
path: "./assets/slider-track.png",
x: 16, y: 60,
width: 260, height: 20,
tooltipText: "Drag to adjust volume",
onDrag: (e) => {
const value = Math.max(0, Math.min(100, e.__offsetXPercent)) / 100;
ui.setElementProperty("vol-fill", "value", value);
ipcRenderer.send("set-volume", { value });
}
});Tooltip with title and icon
ui.addText({
id: "cpu-label",
text: "CPU",
x: 16, y: 14,
width: 60, height: 20,
fontSize: 12,
fontColor: "rgb(180,180,180)",
tooltipTitle: "CPU Usage",
tooltipText: "Current processor load as a percentage of total capacity.",
tooltipIcon: "info"
});Grouped elements for batch show/hide
ui.beginUpdate();
ui.addText({ id: "stat-1", group: "stats", text: "CPU: 72%", x: 16, y: 40, fontSize: 13, fontColor: "rgb(200,200,200)" });
ui.addText({ id: "stat-2", group: "stats", text: "RAM: 58%", x: 16, y: 60, fontSize: 13, fontColor: "rgb(200,200,200)" });
ui.addText({ id: "stat-3", group: "stats", text: "Disk: 34%", x: 16, y: 80, fontSize: 13, fontColor: "rgb(200,200,200)" });
ui.endUpdate();
// Toggle all stats at once
ipcRenderer.on("toggle-stats", (event, payload) => {
ui.setElementPropertiesByGroup("stats", { show: payload.visible });
});Image Options
Options shared by all image-based elements: ui.addImage(), ui.addButton(), ui.addBitmap(), and ui.addRotator().
Use these alongside the Element Options above which covers position, visibility, tooltip, mouse events, and more.
Table of Contents
Opacity and Visibility
255Image opacity in the range 0–255. 255 is fully opaque, 0 is fully transparent. Values are clamped to this range.
ui.addImage({
id: "logo",
path: "./assets/logo.png",
x: 16, y: 16,
width: 64, height: 64,
imageAlpha: 180
});Fallback
""Path to an alternative image file shown when the primary image cannot be loaded. Supports local file paths and HTTP/HTTPS URLs. Relative paths are resolved against the widget's script directory.
ui.addImage({
id: "avatar",
path: "./assets/user.png",
fallbackPath: "./assets/default-avatar.png",
x: 16, y: 16,
width: 48, height: 48
});"stretch"How the fallback image (defined by fallbackPath) is scaled when displayed. Operates independently from the main image's preserveAspectRatio.
| Value | Aliases | Behavior |
|---|---|---|
"stretch" | — | Fills bounds exactly, ignoring aspect ratio (default) |
"preserve" | "fit", "contain" | Scales uniformly to fit within bounds, centered |
"crop" | "cover" | Scales uniformly to fill bounds, cropping excess from center |
ui.addImage({
id: "banner",
path: "https://example.com/banner.png",
fallbackPath: "./assets/fallback.png",
fallbackAspectRatio: "preserve",
preserveAspectRatio: "crop",
width: 300, height: 100
});Color Adjustments
falsetrue renders the image without color — all pixels are converted to shades of gray. Useful for representing disabled or inactive states.
ui.addImage({
id: "icon",
path: "./assets/icon.png",
grayscale: true
});""A color overlaid on the image. Accepts rgb(), rgba(), hex, linearGradient(), and radialGradient(). Only applied when the string is non-empty and parses as a valid color.
ui.addImage({
id: "icon",
path: "./assets/icon.png",
imageTint: "rgba(0,180,255,0.5)" // blue tint at 50% opacity
});[]A Direct2D 5x4 color transformation matrix applied to the image. Enables advanced effects: hue rotation, saturation, brightness, contrast, channel swaps, and inversion.
Provide exactly 20 numbers in row-major order. Fewer than 20 values are silently ignored and no matrix is applied.
The matrix layout is:
[ R→R, G→R, B→R, A→R, bias_R,
R→G, G→G, B→G, A→G, bias_G,
R→B, G→B, B→B, A→B, bias_B,
R→A, G→A, B→A, A→A, bias_A ]The identity matrix (no change):
colorMatrix: [
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
]Invert all colors:
ui.addImage({
id: "inverted",
path: "./photo.png",
colorMatrix: [
-1, 0, 0, 0, 1,
0, -1, 0, 0, 1,
0, 0, -1, 0, 1,
0, 0, 0, 1, 0
]
});Desaturate (grayscale via matrix):
colorMatrix: [
0.33, 0.33, 0.33, 0, 0,
0.33, 0.33, 0.33, 0, 0,
0.33, 0.33, 0.33, 0, 0,
0, 0, 0, 1, 0
]TIP
The grayscale property is a simpler alternative to a desaturate matrix when you only need black-and-white rendering.
Flip
"none"Mirrors the image before rendering. Matching is case-insensitive.
| Value | Effect |
|---|---|
"none" | No flip (default) |
"horizontal" | Mirror left to right |
"vertical" | Mirror top to bottom |
"both" | Mirror both horizontally and vertically |
ui.addImage({
id: "arrow-right",
path: "./assets/arrow-left.png",
imageFlip: "horizontal" // reuse the left arrow as a right arrow
});Crop
[]Crops the source image to a rectangular region before rendering. Coordinates are in source image pixels.
Accepted forms:
[x, y, width, height]— crop from the given origin (default: top-left)[x, y, width, height, origin]— crop with an explicit reference corner
The optional fifth element sets the reference corner for the x/y coordinates:
| Value | Reference corner |
|---|---|
0 | Top-left (default) |
1 | Top-right |
2 | Bottom-right |
3 | Bottom-left |
4 | Center |
Values outside the 0–4 range are clamped. An empty or missing array disables cropping.
Not supported on addBitmap or addRotator
imageCrop is accepted but ignored by ui.addBitmap() and ui.addRotator(). It only applies to ui.addImage() and ui.addButton().
// Show only the top-left 64x64 region
ui.addImage({
id: "sprite",
path: "./assets/spritesheet.png",
imageCrop: [0, 0, 64, 64]
});
// Crop 32x32 from the center of the source image
ui.addImage({
id: "center-crop",
path: "./assets/photo.png",
imageCrop: [0, 0, 32, 32, 4] // origin = center
});Orientation
falsetrue applies the rotation/flip specified in the EXIF metadata embedded in the image file. Useful for photos taken on mobile devices that store their orientation in EXIF data rather than in the pixel arrangement.
ui.addImage({
id: "photo",
path: "./assets/photo.jpg",
useExifOrientation: true
});Practical Examples
Grayscale inactive icon that highlights on hover
ui.addImage({
id: "settings-icon",
path: "./assets/settings.png",
x: 16, y: 16,
width: 24, height: 24,
grayscale: true,
onMouseOver: () => {
ui.setElementProperties("settings-icon", { grayscale: false });
},
onMouseLeave: () => {
ui.setElementProperties("settings-icon", { grayscale: true });
}
});Tinted status indicator updated from IPC
ui.addImage({
id: "status-dot",
path: "./assets/circle.png",
x: 8, y: 8,
width: 12, height: 12,
imageTint: "rgba(100,100,100,1)"
});
ipcRenderer.on("status-update", (event, payload) => {
const tint = payload.online
? "rgba(0,200,100,1)"
: "rgba(200,60,60,1)";
ui.setElementProperties("status-dot", { imageTint: tint });
});Sprite sheet crop for different states
// Single sprite sheet with frames at x=0, 64, 128
function setButtonState(state) {
const frameX = state === "normal" ? 0 : state === "hover" ? 64 : 128;
ui.setElementProperties("btn-img", {
imageCrop: [frameX, 0, 64, 32]
});
}
ui.addImage({
id: "btn-img",
path: "./assets/button-states.png",
x: 16, y: 60,
width: 64, height: 32,
imageCrop: [0, 0, 64, 32],
onMouseOver: () => setButtonState("hover"),
onMouseLeave: () => setButtonState("normal"),
onLeftMouseDown: () => setButtonState("pressed"),
onLeftMouseUp: () => setButtonState("hover")
});Fallback for user-provided images
ui.addImage({
id: "wallpaper",
path: path.join(__mainScriptDirPath, "config", "wallpaper.jpg"),
fallbackPath: "./assets/default-wallpaper.jpg",
x: 0, y: 0,
width: 400, height: 300,
preserveAspectRatio: "crop"
});