Widgets
The web and desktop clients share one widget system. Everything on screen is a widget: vitals, group frames, effects, the target bar, chat, your prompt, cooldowns, score. Dock them, float them, resize them, collapse them. Make the screen yours.
- Layout mode lives behind the arrange button in the client menu. Drag a widget onto a side rail to dock it or drop it anywhere to float it. Hold Alt for free placement. Chat can also dock to any edge of the terminal. Just drag it there.
- ⚙ opens a widget's settings. — collapses it down to its title bar. ⇤ / ⇥ hop a docked widget to the other rail.
- Floating widgets have an Overlay / Solid toggle. Overlay lets terminal text flow underneath. Solid makes the text stop at the widget's edge.
- Session profiles remember your layout and settings per character. Hit + to manage them on either client.
- History opens with Ctrl+F and lets you search back through everything you have seen. The desktop client keeps full logs on disk while the web client keeps the last couple megabytes of your current session.
Widget style guide
The default widgets share one look and anything a script creates inherits it automatically. Here's the language so your custom stuff fits right in.
Bars
Every bar in the client is the same element: a bordered track with a colored fill and a shadowed label in the middle. Vitals, group frames, the target bar, prompt bars and script bars all use it. Heights vary a little by context but everything else matches.
Buttons
One button style everywhere. Dark panel background with a thin border and a green tint on hover. Buttons your scripts make look exactly like ours.
Colors
Anywhere you can change a color you get the same choices. Default is the client palette. MUD shows up where an in-game color exists, so score fields can match the in-game score sheet. Custom hands you a color picker. Every widget's settings has a restore-defaults escape hatch.
Effect icons
Effects and debuffs are a text list by default with an icon grid option. Tiles are all the same size and long names just shrink and wrap to fit. Pick your tile shape in the Effects settings: hexagon, square or circle. Buffs get a hue from their name so a spell always looks the same. Debuffs are always red. Your own effects on a target get a gold halo.
Design tokens
The shared look comes from a handful of CSS variables. Scripts pick these up for free. They're listed here in case you want to build with them on purpose.
| Token | What it is |
|---|---|
--w-bar-height | Default bar height (20px) |
--w-radius | Corner radius on bars and buttons (3px) |
--w-font | UI font for buttons and controls |
--w-mono | Monospace font for readouts |
--bg, --bg-2, --bg-3 | Background steps from darkest to lightest |
--line, --line-strong | Border colors, resting and hover |
--fg, --fg-dim | Text colors, primary and secondary |
--de-green, --de-green-lt | Accent greens |
Scripting
The client has a built-in Lua engine. Triggers and aliases, timers, GMCP access, line gagging and your own widgets with text, bars and buttons. Open Scripts in the client menu and check the Reference tab. It lists every GMCP package with a live peek at its current data plus the full de.* API and one-click starter templates.
Ground rules: scripts run sandboxed with no network access. The item database and area atlas are there through de.items and de.atlas. The client loads both for you. Scripts can hide built-in widgets but never remove them.
Starter templates
Same templates you'll find in the client's Reference tab.
Vitals bar widget
local w = de.widget('myvitals', 'My Vitals')
de.on_gmcp('Char.Vitals', function(v)
w:bar('hp', { value = v.hp, max = v.maxhp, color = '#c0392b', label = 'HP ' .. v.hp .. '/' .. v.maxhp })
w:bar('mana', { value = v.mana, max = v.maxmana, color = '#2a6fb0', label = 'Mana ' .. v.mana .. '/' .. v.maxmana })
end)
Custom buttons
local w = de.widget('mybuttons', 'Buttons')
w:button('heal', 'Heal', 'cast heal')
w:button('flee', 'Flee!', 'flee')
w:button('camp', 'Camp', function()
de.send('sit')
de.send('rest')
end)
Gag spam lines
de.on_line('The rain continues to fall', function()
de.gag()
end)
Tell alert with captures
de.on_line('^(%a+) tells you', function(line, who)
de.echo('*** ' .. who .. ' sent you a tell! ***')
end, 'pattern')
Low target announcer
local called = false
de.on_gmcp('Char.Target', function(t)
if t.id == 0 then called = false return end
if not called and t.hp and t.hp <= 15 then
called = true
de.send('say ' .. (t.name or 'Target') .. ' is nearly dead!')
end
end)
Item lookup command
de.on_command('^lookup (.+)', function(cmd, what)
local found = de.items.find(what)
if found == nil then
de.echo('Item database is still loading, try again in a moment.')
return false
end
if #found == 0 then
de.echo('No items match "' .. what .. '".')
return false
end
for i = 1, #found do
local it = found[i]
de.echo(string.format('%-40s level %-3d %s', it.short, it.level, it.type))
end
return false
end, 'pattern')
Atlas lookup command
de.on_command('^atlas (.+)', function(cmd, name)
local area = de.atlas.area(name)
if area == nil then
de.echo('Requesting that map...')
return false
end
de.echo(area.name .. ': ' .. #area.rooms .. ' rooms mapped (' .. area.explored .. '/' .. area.total .. ' explored)')
return false
end, 'pattern')
de.on_atlas(function(area)
de.echo('Map ready: ' .. area.name)
end)