Organised to match the Mudlet wiki Lua Functions reference. Each section below corresponds to a top-level category on that page; rows appear in the wiki's alphabetical order.
Status legend:
- ✅ Implemented and callable from Lua (JS-bound, pure-Lua, or wasmoon stdlib)
⚠️ Partial — skeleton exists, signature is incomplete, or pure-Lua impl is bundled but blocked by a missing dependency- 🚧 Feasible — worth implementing
- ❌ N/A — fundamentally inapplicable (multi-profile, Qt-specific, Discord SDK, etc.). These should still be bound as warning-emitting no-op stubs so imported Mudlet scripts that reference them don't crash; the stub logs once per call site and returns a sensible default (
nil/false/empty table).
Known blockers:
- (none currently —
invokeFileDialog's sync/async mismatch was solved by parking the calling coroutine; see the row in Mudlet Object Functions)
createMiniConsole, createLabel, createGauge, createCommandLine and friends are implemented as absolutely-positioned HTML elements rendered in an overlay layer on top of the main output area, mirroring Mudlet's pixel-coordinate layout.
moveWindow(name, x, y)andresizeWindow(name, w, h)apply to overlay elements via CSSleft/top/width/height.- Dockview panels (opened via
openWindow) follow dockview's own layout and are not absolutely positioned. showWindow/hideWindowandraiseWindow/lowerWindowapply to both overlay elements (CSSdisplay/z-index) and dockview panels.
A profile-scoped IndexedDB VFS provides path-based file I/O from Lua. getMudletHomeDir() returns the VFS root; io.open, io.exists, table.save/table.load, downloadFile, saveMap/loadMap, and sound/video paths all resolve through it.
Mudlet's DB.lua is bundled and runs against sqlite-wasm through a Luasql.lua shim, so the entire db:* API works in the browser.
A subset of the Geyser OOP framework (Container, Label, MiniConsole, Gauge, HBox, VBox, UserWindow) is bundled in pure Lua on top of the overlay primitive API.
| Function | Status | Notes |
|---|---|---|
debugc |
✅ | Alias for debug — console.debug |
display(value) |
✅ | Pretty-prints tables recursively |
echo([window,] text) |
✅ | Main window; window arg routes to overlay/panel |
printDebug |
✅ | Bound in LuaRuntime |
printError |
✅ | Bound in LuaRuntime |
send(text [, echo]) |
✅ | Send command to MUD |
All db:* calls run against sqlite-wasm via the Luasql shim; Mudlet's bundled DB.lua provides the high-level API unchanged.
| Function | Status | Notes |
|---|---|---|
db:add |
✅ | DB.lua |
db:aggregate |
✅ | DB.lua |
db:close |
✅ | DB.lua |
db:create |
✅ | DB.lua |
db:delete |
✅ | DB.lua |
db:fetch |
✅ | DB.lua |
db:fetch_sql |
✅ | DB.lua |
db:get_database |
✅ | DB.lua |
db:merge_unique |
✅ | DB.lua |
db:query_by_example |
✅ | DB.lua |
db:Timestamp |
✅ | DB.lua |
db:Null |
✅ | DB.lua |
db:safe_name |
✅ | DB.lua |
db:set |
✅ | DB.lua |
db:update |
✅ | DB.lua |
db:_sql_convert |
✅ | DB.lua internal — exposed |
db:_sql_values |
✅ | DB.lua internal — exposed |
| Function | Status | Notes |
|---|---|---|
db:AND |
✅ | DB.lua |
db:OR |
✅ | DB.lua |
db:between |
✅ | DB.lua |
db:eq |
✅ | DB.lua |
db:exp |
✅ | DB.lua |
db:gt |
✅ | DB.lua |
db:gte |
✅ | DB.lua |
db:in_ |
✅ | DB.lua |
db:is_nil |
✅ | DB.lua |
db:is_not_nil |
✅ | DB.lua |
db:like |
✅ | DB.lua |
db:lt |
✅ | DB.lua |
db:lte |
✅ | DB.lua |
db:not_between |
✅ | DB.lua |
db:not_eq |
✅ | DB.lua |
db:not_in |
✅ | DB.lua |
db:not_like |
✅ | DB.lua |
Transactions are driven through the Luasql connection (conn:commit()/conn:rollback()); DB.lua's auto-commit wrapping handles the documented db:_* entry points.
| Function | Status | Notes |
|---|---|---|
db:_begin |
✅ | Via DB.lua autocommit toggling |
db:_commit |
✅ | Via conn:commit() |
db:_end |
✅ | Closes transaction window |
db:_rollback |
✅ | Via conn:rollback() |
| Function | Status | Notes |
|---|---|---|
datetime:parse |
✅ | DateTime.lua |
getEpoch() |
✅ | JS-exposed (Date.now() / 1000) |
getTime([returnAsTable, format]) |
✅ | Bridge.lua — full Qt QDateTime token formatting |
getTimestamp([window,] lineNumber) |
✅ | Bridge.lua → __getTimestamp → "hh:mm:ss.zzz" string. Each AnsiAwareBuffer carries a construction-time timestamp; Console.getLineTimestamp reads it (1-based, matching getLines). (nil, errMsg) when out of range |
shms(seconds) |
✅ | DateTime.lua |
| Function | Status | Notes |
|---|---|---|
io.exists(path) |
✅ | Other.lua (uses io.open) backed by ProfileVFS |
lfs.attributes(path [, attrib]) |
✅ | VFS.lua exposes LuaFileSystem 1.9.0's lfs over the profile VFS — attributes returns {mode, size, modification, access, ...} (or the single named attribute). currentdir/chdir/mkdir/rmdir/dir/touch/symlinkattributes/link/lock/unlock/lock_dir/setmode and _VERSION also wired (plus Mudlet Web's own isfile/isdir). lfs.dir raises cannot open <path>: <reason> for a folder it can't read and returns the iterator plus a directory object, as LuaFileSystem does |
io.* |
✅ | The whole of Lua 5.1's io library over the profile VFS: file handles are userdata; io.stdin/io.stdout/io.stderr exist (stdout/stderr writes go to the browser console, where desktop writes to its process streams); io.tmpfile, io.flush; io.open refuses a mode glibc refuses (nil, "<path>: Invalid argument", 22). io.popen raises 'popen' not supported — a browser can't start processes |
openMudletHomeDir() |
✅ | openUrl("file:") routes to the VFS file browser |
saveProfile([name]) |
✅ | Forces the debounced VFS flush to IndexedDB (see Miscellaneous Functions) |
| Function | Status | Notes |
|---|---|---|
addAreaName(name) |
✅ | Bridge.lua |
addCustomLine(roomID, toID, direction, style, color, arrow) |
✅ | MapStore.addCustomLine — toID is a target room id (same area) or a { {x,y,z}, … } point list. style is a Mudlet pen-style name; Bridge.lua flattens the id_to/color tables to a R:/P: string + r,g,b. Round-trips through getCustomLines/removeCustomLine |
addMapEvent(uniquename, event, parent, displayName, ...) |
✅ | Map context-menu event registration |
addMapMenu(name, parent, displayName) |
✅ | Registers a submenu in the map right-click menu; MapPanel surfaces it as a container node so addMapEvent entries whose parent names it nest underneath. Pairs with getMapMenus/removeMapMenu |
addRoom(roomID) |
✅ | JS-exposed |
addSpecialExit(fromID, toID, cmd) |
✅ | JS-exposed |
auditAreas() |
✅ | Rebuilds each area's rooms[] from the authoritative room.area back-pointers (drops dangling ids, re-files missing rooms); returns a summary {checkedAreas, checkedRooms, fixedAreas, orphanRooms, danglingRefs} (Mudlet returns nothing) |
centerview(roomID) |
✅ | JS-exposed; also sets the player room (matches Mudlet) |
clearAreaUserData(areaID) |
✅ | Bridge.lua → __clearAreaUserData; (false, errMsg) when area missing |
clearAreaUserDataItem(areaID, key) |
✅ | Bridge.lua → __clearAreaUserDataItem |
clearMapSelection() |
✅ | Clears the room-selection set + center. MapSelectionOverlay redraws. Returns false when already empty |
clearMapUserData() |
✅ | JS-exposed |
clearMapUserDataItem(key) |
✅ | JS-exposed |
clearRoomUserData(roomID) |
✅ | Bridge.lua → __clearRoomUserData |
clearRoomUserDataItem(roomID, key) |
✅ | Bridge.lua → __clearRoomUserDataItem |
clearSpecialExits(roomID) |
✅ | Removes special exits and the locks/doors/custom lines keyed by their commands |
closeMapWidget() |
✅ | Closes the dockable map widget (id map); returns false if none open |
connectExitStub(fromID, dir) / (fromID, toID[, dir]) |
✅ | Direction-only finds the nearest in-area room with a matching reverse stub (Mudlet's unit-vector/compSign search); toID-only requires exactly one reverse-stub pair |
createMapLabel(areaID, text, x, y, z, fg, bg, …) |
✅ | Adds a text label (new per-area id) to MapStore, drawing its pixmap on a canvas as TMap::createMapLabel does (map/labelPixmap.ts) so a saved map carries the image desktop paints; font and outline colour ride in the area's system.labelFont_N/system.labelOutlineColor_N keys like desktop's v20 save. Round-trips through getMapLabels/getMapLabel and binary save, and is painted by the renderer (mudlet-map-renderer ScenePipeline.renderLabels → labelToShape, default labelRenderMode:"image"). -1 when the area is missing |
createMapImageLabel(areaID, imagePath, x, y, z, w, h, zoom, …) |
✅ | Image-label sibling of createMapLabel; reads the image when the label is made and stores it as a base64 PNG in the label pixMap (surfaced as Pixmap, saved by saveMap/saveJsonMap), which MudletMapReader patches through to the renderer so it paints. PNG files are kept as they are and XPM files are read in place. Other formats are drawn by the browser shortly afterwards; until then the label holds a transparent w*zoom × h*zoom image. The tenth argument is temporary, as on desktop. -1 when the area is missing |
createMapper(x, y, w, h) |
✅ | Singleton embedded mapper widget sharing MapStore with the dock |
createRoomID([minimumID]) |
✅ | JS-exposed |
deleteArea(areaID|name) |
✅ | JS-exposed |
deleteMap() |
✅ | Wipes every room/area/label back to a single empty default area |
deleteMapLabel(areaID, labelID) |
✅ | Removes the label by id; false when the area or id is unknown |
deleteRoom(roomID) |
✅ | JS-exposed |
disableMapInfo(label) |
✅ | Toggles a registered info contributor off |
enableMapInfo(label) |
✅ | Toggles a registered info contributor on |
exportAreaImage(areaID, filePath [, zLevel]) |
✅ | Renders the area (optionally one z-level) to a PNG in the profile VFS via a headless mudlet-map-renderer PngBytesExporter (the live view is untouched). The whole area is fitted into the image; hidden rooms follow the current viewing/editing mode. (true, absPath) / (false, errMsg) via Bridge.lua. Requires the map widget open (Mudlet requires the mapper open) |
getAllAreaUserData(areaID) |
✅ | Bridge.lua → __getAllAreaUserData |
getAllMapUserData() |
✅ | JS-exposed |
getAllRoomEntrances(roomID) |
✅ | Sorted, de-duped list of rooms with a stock or special exit into this one |
getAllRoomUserData(roomID) |
✅ | Bridge.lua → __getAllRoomUserData |
getAreaExits(areaID[, fullData]) |
✅ | Default → sorted id list; fullData → { [fromRoomID] = { [exit] = toRoomID } } |
getAreaRooms(areaID) |
✅ | JS-exposed (0-indexed) |
getAreaRooms1(areaID) |
✅ | Bridge.lua — 1-based reindex |
getAreaTable() |
✅ | JS-exposed |
getAreaTableSwap() |
✅ | Bridge.lua re-keys numeric-string ids back to integers |
getAreaUserData(areaID, key) |
✅ | Bridge.lua → __getAreaUserData |
getCustomEnvColorTable() |
✅ | Bridge.lua |
getCustomLines(roomID) |
✅ | { dir = { attributes={color,style,arrow}, points={[0]={x,y,z},...} } } |
getCustomLines1(roomID) |
✅ | Bridge.lua — 1-indexed point arrays |
getDoors(roomID) |
✅ | JS-exposed |
getExitStubs(roomID) |
✅ | JS-exposed; 0-indexed (wasmoon array convention, matches Mudlet) |
getExitStubs1(roomID) |
✅ | Bridge.lua — 1-indexed |
getExitWeights(roomID) |
✅ | JS-exposed; {exit=weight} |
getGridMode(areaID) |
✅ | Bridge.lua → __getGridMode; (false, errMsg) when area missing |
getHiddenRooms(areaID) |
✅ | Bridge.lua — 1-indexed array of room ids in the area whose hidden flag is set; (false, errMsg) when the area is missing |
getMapEvents() |
✅ | Bridge.lua |
getMapLabel(areaID, labelID|labelText) |
✅ | Bridge.lua |
getMapLabels(areaID) |
✅ | Bridge.lua → __getMapLabels |
getMapMenus() |
✅ | { [menuName] = { ["parent"], ["display name"] } }; Bridge.lua reshapes the JS array |
getMapSelection() |
✅ | { rooms = {1-indexed roomIDs}, center = roomID }. Selection lives on MapStore with a dedicated subscribe channel; UI: left-click selects + sets center, ctrl/cmd-click toggles, click on empty area clears. registerMapInfo callbacks now receive the real selection size + center room |
getMapUserData(key) |
✅ | Bridge.lua |
getMapZoom([areaID]) |
✅ | Mudlet-compatible zoom semantics (units across the shorter viewport edge). setMapZoom enforces min of 3.0; areaID accepted for compat |
getPath(fromID, toID) |
✅ | A* via __getPath; populates speedWalkPath/speedWalkDir/speedWalkWeight (1-indexed) |
getPlayerRoom() |
✅ | Returns the id last passed to centerview; nil when unset/deleted |
getRoomArea(roomID) |
✅ | JS-exposed |
getRoomAreaName(roomID) |
✅ | JS-exposed |
getRoomChar(roomID) |
✅ | Bridge.lua |
getRoomCharColor(roomID) |
✅ | Bridge.lua → r, g, b, a; nil when unset |
getRoomCoordinates(roomID) |
✅ | Bridge.lua → __getRoomCoordinates |
getRoomEnv(roomID) |
✅ | JS-exposed |
getRoomExits(roomID) |
✅ | JS-exposed |
getRoomHashByID(roomID) |
✅ | Bridge.lua |
getRoomHidden(roomID) |
✅ | Bridge.lua — bool, (false, errMsg) when the room is missing. MapStore side-table; renderer's RoomLens skips hidden rooms in viewing mode (editing mode shows them) |
getRoomIDbyHash(hash) |
✅ | JS-exposed |
getRoomName(roomID) |
✅ | Bridge.lua → __getRoomName |
getRooms() |
✅ | JS-exposed |
getRoomsByPosition(areaID, x, y, z) |
✅ | JS-exposed (0-indexed) |
getRoomsByPosition1(areaID, x, y, z) |
✅ | Bridge.lua — 1-based reindex |
getRoomUserData(roomID, key) |
✅ | Bridge.lua → __getRoomUserData |
getRoomUserDataKeys(roomID) |
✅ | Bridge.lua — re-indexes JS 0-based array to 1-based |
getRoomWeight(roomID) |
✅ | JS-exposed; false when missing |
getSpecialExits(roomID [, listAllExits]) |
✅ | {[exitRoomID]={[cmd]="0"|"1"}}; lowest-weight command per room unless listAllExits |
getSpecialExitsSwap(roomID) |
✅ | JS-exposed; {cmd=toId} |
gotoRoom(targetRoomID) |
✅ | Pure Lua (Bridge.lua): getPath then sends the moves. Mudlet Web sends immediately (no autonomous timed-walk engine) |
hasSpecialExitLock(fromID, toID, cmd) |
✅ | toID ignored; returns the lock boolean or (nil, errMsg) when missing |
highlightRoom(roomID, …) |
✅ | JS-exposed — color1/color2 + radius + alpha |
killMapInfo(label) |
✅ | Removes a contributor entirely |
loadJsonMap(path) |
✅ | JS-exposed via MapStore.loadFromJsonString; raises no event (desktop has no sysMapLoadEvent) |
loadMap(path) |
✅ | JS-exposed. Binary .dat maps, plus IRE-style XML maps when the path ends in .xml (Mudlet's XMLimport::readMap) |
lockExit(roomID, dir, bool) |
✅ | MapStore.lockExit mutates room.exitLocks, which __getPath reads — locks set from Lua are honoured by pathfinding |
hasExitLock(roomID, dir) |
✅ | MapStore.hasExitLock; reads room.exitLocks directly. Direction accepts the 1-12 int or names ("north"/"n"/…) |
lockRoom(roomID, bool) |
✅ | JS-exposed; honoured by pathfinding |
lockSpecialExit(fromID, toID, cmd, lockIfTrue) |
✅ | Bridge.lua drops the (Mudlet-ignored) toID |
moveMapWidget(x, y) |
✅ | JS-exposed (alias for moveWindow on the embedded mapper) |
openMapWidget([…]) |
✅ | Opens the dockable mapper panel |
pauseSpeedwalk() |
✅ | Pure Lua via Other.lua |
registerMapInfo(label, fn) |
✅ | MapStore.registerMapInfo keyed by label; callback receives (roomId, selectionSize, areaId, displayedAreaId) and returns (text, bold?, italic?, r?, g?, b?). New contributors land disabled — call enableMapInfo(label) to show. MapPanel re-evaluates every enabled contributor on map updates. Two built-in native contributors mirror Mudlet's defaults: Short (name / id (area)) and Full (area extent + room id/position with selection-aware suffix & styling); Full is enabled by default, both are evaluated without Lua and can't be killMapInfo'd. The map hamburger menu's Map info overlays submenu toggles every contributor (built-in + script) with checkboxes plus a None entry |
resumeSpeedwalk() |
✅ | Other.lua |
removeCustomLine(roomID, direction) |
✅ | Direction = 1-12/name/special command; false when missing |
removeMapEvent(uniquename) |
✅ | Pairs with addMapEvent |
removeMapMenu(name) |
✅ | Removes a registered submenu; true if it existed |
removeSpecialExit(fromID, cmd) |
✅ | JS-exposed |
resetRoomArea(roomID) |
✅ | Bridge.lua → moves the room to the void area (-1) |
resizeMapWidget(w, h) |
✅ | JS-exposed (alias for resizeWindow on the embedded mapper) |
roomExists(roomID) |
✅ | JS-exposed |
roomLocked(roomID) |
✅ | JS-exposed; nil when missing |
saveJsonMap(path) |
✅ | Same MudletMap shape as saveMap, just JSON |
saveMap(path) |
✅ | Serialises MapStore via writeMapToBuffer to VFS / IDB |
searchAreaUserData([key[, value]]) |
✅ | 1-indexed |
searchRoom(roomID | name[, caseSensitive[, exactMatch]]) |
✅ | By id → name (false on miss); by name → { [roomID] = name } |
searchRoomUserData([key[, value]]) |
✅ | 1-indexed |
setAreaName(idOrName, newName) |
✅ | Bridge.lua |
setAreaUserData(areaID, key, value) |
✅ | JS-exposed; false when missing |
setCustomEnvColor(envID, r, g, b, a) |
✅ | JS-exposed |
setDoor(roomID, exitCmd, type) |
✅ | JS-exposed |
setExit(fromID, toID, dir) |
✅ | JS-exposed |
setExitStub(roomID, dir, bool) |
✅ | JS-exposed |
setExitWeight(roomID, exitCommand, weight) |
✅ | Weight 0 resets to destination-room weight; rejects negatives/unknown exits |
setGridMode(areaID, bool) |
✅ | api.map.setGridMode; false when missing |
setMapUserData(key, value) |
✅ | JS-exposed |
setMapPerspective(r, theta, phi) / shiftMapPerspective(verticalAngle, horizontalAngle, rotationAngle) |
✅ | Desktop's 3D-map camera. The checks desktop makes (no map → nil, "you haven't opened a map yet"; three numbers) and then nothing, as on desktop while the 3D view isn't the one showing — the map here is 2D |
setMapZoom(zoom[, areaID]) |
✅ | See getMapZoom |
setRoomArea(roomID, areaID) |
✅ | JS-exposed |
setRoomChar(roomID, char) |
✅ | JS-exposed |
setRoomCharColor(roomID, r, g, b [, a]) |
✅ | Side-table on MapStore (upstream MudletRoom has no charColor field); cleared by map reset |
setRoomCoordinates(roomID, x, y, z) |
✅ | JS-exposed |
setRoomEnv(roomID, envID) |
✅ | JS-exposed |
setRoomHidden(roomID, bool) |
✅ | JS-exposed via MapStore side-table; round-trips through binary maps via the system.fallback_hidden userData key (Mudlet v20-compatible). False when the room is missing |
setRoomIDbyHash(hash, roomID) |
✅ | JS-exposed |
setRoomName(roomID, name) |
✅ | JS-exposed |
setRoomUserData(roomID, key, value) |
✅ | JS-exposed |
setRoomWeight(roomID, weight) |
✅ | JS-exposed; rejects negatives |
speedwalk(roomID [, walkcmd, delay]) |
✅ | Pure Lua via Other.lua (send + tempTimer) |
stopSpeedwalk() |
✅ | Other.lua |
unHighlightRoom(roomID) |
✅ | JS-exposed |
unsetRoomCharColor(roomID) |
✅ | Drops the side-table entry; false when the room is missing or had no override |
updateMap() |
✅ | Forces the map panel to re-read MapStore and redraw |
Mudlet Web-specific extras (not on the wiki): getMapMode/setMapMode("viewing"\|"editing"), getMapRoomSize/setMapRoomSize, setMapBackgroundColor, removeCustomEnvColor.
| Function | Status | Notes |
|---|---|---|
addFileWatch(path) |
✅ | Tracks resolved VFS paths, fires sysPathChanged by QFileSystemWatcher's rules: a file reports changes and its removal (which ends the watch); a directory reports direct children added, removed or touched, not writes into them. false for a path already watched |
addSupportedTelnetOption(option) |
✅ | Registers a telnet option byte so the next IAC WILL/DO is auto-accepted |
alert([secs]) |
✅ | Flashes document.title for secs (default 10). No-op while focused |
announce(text [, processing]) |
✅ | ARIA live region; processing (importantall/importantmostrecent → assertive, else polite) matches Mudlet's politeness mapping |
appendLog(text) |
✅ | Appends a line (type appendLog) to the active SessionLogger; false when logging is off |
cfeedTriggers(text) |
✅ | Pure Lua via GUIUtils.lua |
clearVisitedLinks() |
✅ | True no-op — Mudlet Web tracks no visited-link state, so there is nothing to clear (bound for script portability) |
closeMudlet() |
✅ | Closes the active profile — disconnects then returns to the connection screen (callback wired by ProfileSession). Armed, like desktop's armForceClose: the call returns and the rest of the calling script runs first |
compare(a, b) |
✅ | Other.lua — alias for _comp deep equality |
deleteAllNamedEventHandlers([type]) |
✅ | IDManager.lua |
deleteNamedEventHandler(name) |
✅ | IDManager.lua |
denyCurrentSend() |
✅ | Cancels the currently-dispatched send |
dfeedTriggers(text) |
✅ | Pure Lua via GUIUtils.lua |
disableModuleSync(name) |
✅ | Marks the module non-syncing in profile state |
enableModuleSync(name) |
✅ | Marks the module syncing |
expandAlias(text [, echo]) |
✅ | ScriptingAPI.expandAlias |
feedTriggers(text) |
✅ | Feeds text through trigger pipeline + shows in output |
getCharacterName() |
✅ | The profile's saved login name (charLoginAccount, or a branded build's in-memory login) — Host::getLogin(); nil, "no character name set" when none is saved |
getConfig(key) |
✅ | Config registry in ScriptingAPI. Structured keys (protocol toggles, mapper, autoClearInputLine, showSentText, mapperPanelVisible) read their real field / live state; UI-consumed keys (commandLineHistorySaveSize, showTabConnectionIndicators) round-trip via the config bag and drive behaviour; other catalogued keys persist-only. Unknown key → nil. Full key table + enforced/persist-only breakdown: docs/config-api.md |
getCommandSeparator() |
✅ | Reads the profile's commandSeparator (default ;;) |
getModuleInfo(name, key) |
✅ | Bridge.lua |
getModulePath(name) |
✅ | Absolute VFS path of a module's XML — xmlVfsPath verbatim, else <profilePath>/<name>/<xmlPath>; nil when not an installed module |
getModulePriority(name) |
✅ | JS-exposed |
getModules() |
✅ | JS-exposed |
getModuleSync(name) |
✅ | JS-exposed |
getMudletHomeDir() |
✅ | VFS.lua — returns the profile's VFS root path |
getMudletInfo() |
✅ | Echoes a diagnostic block (profile, server encoding, platform/user-agent) to the main window |
getMudletVersion([mode]) |
✅ | Supports nil/"string"/"major"/"minor"/"revision"/"build"/"table" |
getNamedEventHandlers() |
✅ | IDManager.lua |
getNewIDManager() |
✅ | IDManager.lua factory |
getOS() |
✅ | Sniffed from user agent → "windows"/"mac"/"linux"/"freebsd"/"openbsd"/"netbsd"/"unknown" |
getProcessMemoryUsage() |
✅ | (Mudlet 4.21) Memory in Kb. Browser-adapted: the JS heap in use (performance.memory, Chromium only), else 0 |
getSubsystemMemoryStats() |
✅ | (Mudlet 4.21) Diagnostic table under Mudlet's own key names: lua_heap_kb/lua_heap_mb (Bridge.lua via collectgarbage("count")), triggers_total/triggers_temp, timers_total/timers_temp, aliases_total/aliases_temp, map_rooms/map_areas, console_buffer_lines, media_sound_players/media_music_players/media_stopped_players, plus heap_in_use_mb/heap_allocated_mb/heap_limit_mb from performance.memory (Chromium only, absent elsewhere) and the browser-only loaded_fonts. No event_handlers — Other.lua's registry is a local upvalue. Best-effort |
lpeg (library) |
✅ | (Mudlet 4.21 bundles C lpeg) Mudlet Web bundles the pure-Lua LuLPeg port at mudlet-lua/3rdparty/lulpeg.lua, registered as package.loaded["lpeg"] before LuaGlobal.lua's guard publishes the lpeg global. Full PEG API (P/R/S/C/Ct/match/…) |
getPackages() |
✅ | JS-exposed |
getPackageInfo(name [, key]) |
✅ | Merged table: manifest fields (name/title/author/version/description/created/icon/installed) overlaid with setPackageInfo overrides; single-key form returns "" when absent |
getPausedMusic() / getPausedSounds() |
✅ | Always empty — Mudlet Web's Web Audio backend stops rather than pauses sources, so nothing sits paused (kept for parity) |
getPausedVideos() |
✅ | Lists genuinely-paused <video> elements (element.paused), optionally name-filtered. 1-indexed {name, path, volume} |
getPlayingMusic() |
✅ | Sister of getPlayingSounds for the music channel; 1-indexed {name, key, tag, volume} |
getPlayingVideos() |
✅ | Currently-playing <video> elements, optionally name-filtered. 1-indexed {name, path, volume} |
getPlayingSounds([filter]) |
✅ | 1-based array of {name, key, tag, volume, priority}; optional name/key/tag/priority filter; lists only media the Lua API started (not MSP/GMCP) |
getProfileName() |
✅ | JS-exposed |
getServerEncoding() / setServerEncoding(name) / getServerEncodingsList() |
✅ | Exposes MudClient's CHARSET (RFC 2066) decoder. getServerEncoding → current IANA name (default "utf-8"); setServerEncoding validates via normalizeCharsetName and swaps the TextDecoder (false when unsupported); getServerEncodingsList → 1-indexed SUPPORTED_SERVER_ENCODINGS (UTF-8, ISO-8859-x, Windows-125x, KOI8-R/U) |
getWindowsCodepage() |
✅ | Returns "65001" (UTF-8) on every platform |
hfeedTriggers(text) |
✅ | Pure Lua via GUIUtils.lua |
holdingModifiers(number) |
✅ | Exact-match against the live held modifiers (Qt bitmask, as in mudlet.keymodifier). A heldModifiers tracker snapshots shift/ctrl/alt/meta off every keyboard/pointer event |
installModule(path) |
✅ | JS-exposed |
installPackage(path) |
✅ | JS-exposed |
killAnonymousEventHandler(id) |
✅ | Other.lua: removes handler by ID |
loadMusicFile(path | {name=…}) |
✅ | SoundManager.preload |
loadSoundFile(path | {name=…}) |
✅ | SoundManager.preload |
loadVideoFile(path | {name=…}) |
✅ | Preload variant of playVideoFile — VideoManager.preload fetches + caches a VFS-backed video so the first play has no fetch latency (fire-and-forget; http(s)/data/blob URLs need no preloading) |
mudletOlderThan(major, minor, revision) |
✅ | Built on getMudletVersion("table") |
openWebPage(url) |
✅ | Routes to openUrl |
playMusicFile(path | {…}) |
✅ | SoundManager (Web Audio + VFS or http(s) URL) |
playSoundFile(path | {…}) |
✅ | SoundManager |
playVideoFile(path | {…}) |
✅ | VideoManager; absolutely-positioned <video> on the main viewport. loops=-1 plays indefinitely. Fires sysMediaFinished(name, path) on natural end |
pauseMusic([channel]) |
✅ | Web Audio can't truly pause — fades out + stops matching music sources (optionally tag-filtered), mirroring pauseSounds. Re-trigger playMusicFile to "resume" |
pauseSounds([channel]) |
✅ | Web Audio source nodes can't truly pause — stops sources (optionally tag-filtered). Re-trigger playSoundFile to "resume" |
pauseVideos() |
✅ | Pauses every active <video> element |
purgeMediaCache() |
✅ | Drops every decoded-audio buffer; active playback unaffected |
receiveMSP(payload) |
✅ | Parses the payload through a fresh MspParser and re-emits each !!SOUND/!!MUSIC command as a msp session event, so ScriptingEngine.handleMspCommand plays it through SoundManager. Returns true when ≥1 command parsed |
GMCP media protocol (Client.Media.Play/Load/Stop/Default) |
✅ | Mudlet's TMedia MediaProtocolGMCP. We advertise Client.Media 1 in Core.Supports.Set; ScriptingEngine.handleClientMedia resolves the name (+ optional url base, or the Client.Media.Default url) through the shared media/ VFS cache and plays it via SoundManager on the game mute gate. Supports type (sound/music), volume, loops, key, tag, fadein/fadeout, start, and music continue. Not yet mapped: priority, finish, name wildcards; Stop filters sounds only by all-stop (music honours name/key/tag/fadeout) |
registerAnonymousEventHandler(event, fn) |
✅ | Other.lua override tracks IDs in handlerIdsToHandlers |
registerNamedEventHandler(name, event, code) |
✅ | IDManager.lua |
reloadModule(name) |
✅ | JS-exposed |
removeFileWatch(path) |
✅ | Stops watching a path |
resetLinkStyle(labelName) / setLinkStyle(labelName, linkColor, visitedColor[, underline]) |
✅ | Styles the <a> links inside a label. LabelManager stores the per-label linkStyle; LabelOverlay injects a <style> scoped via the label's data-mudlet-label selector (a { color; text-decoration }, a:visited { color }). underline defaults to true. Visited links are tracked per label (LabelState.visitedLinks, populated on click like Mudlet's TLabel::mVisitedLinks) and get an explicit a[href="…"] rule, since CSS :visited only matches real browser history and so never fires for send:/prompt: links |
resetProfile() |
✅ | Reloads the profile as if just reopened: clears every UI surface (windows, labels, gauges, command lines, scroll boxes; stops sound/video), recreates the Lua runtime (fresh globals + event handlers), and re-runs all scripts/aliases/triggers/timers/keys from current profile state, re-firing sysLoadEvent; non-persistent stopwatches are removed. Returns true once armed, false while a reset is already in progress. Deferred to a fresh task (it closes the running lua_State), so call it from an alias / command line, not a script-item — matching Mudlet's own guidance. Mudlet Web reloads from the live store, not a re-read of disk |
resumeNamedEventHandler(name) |
✅ | IDManager.lua |
saveProfile([name]) |
✅ | Bridge.lua → __mudlet_saveProfile forces the debounced VFS flush through to IndexedDB; (nil, errMsg) when no VFS, else true, path. name ignored (single-profile) |
setConfig(key, value) |
✅ | Config registry in ScriptingAPI (base global; Other.lua adds the table-form/no-arg wrappers). Enforced: protocol enables + specialForce*Off/forceNewEnvironNegotiationOff (next connect), mapRoomSize/mapExitSize/mapRoundRooms/mapShowRoomBorders/mapShowGrid, autoClearInputLine, showSentText, mapperPanelVisible, showMapInfo/hideMapInfo (live), commandLineHistorySaveSize/showTabConnectionIndicators/f3SearchEnabled (config bag, consumed by UI). Other keys persist only. Read-only/unknown → false. Absent: the six irc* keys (no IRC client) and undoServerWrap/undoServerWrapWidth. Details: docs/config-api.md |
setMergeTables(...) |
✅ | Pure Lua (Bridge.lua), mirroring Host::mGMCP_merge_table_keys. Accumulates GMCP keys (dotted, e.g. "Char.Status") into mudlet.mergeTables; __mudlet_set_gmcp merges those keys' incoming payloads into the existing gmcp sub-table instead of replacing it |
setModuleInfo(name, key, value) |
✅ | Stores a custom info field (in-memory override map) surfaced by getModuleInfo; always true |
setModulePriority(name, n) |
✅ | JS-exposed |
setPackageInfo(name, key, value) |
✅ | Stores a custom info field (in-memory override map) surfaced by getPackageInfo; always true |
showNotification(title, text [, expirySecs]) |
✅ | Web Notifications API; gated on the Settings opt-in |
spawn(...) |
❌ stub | No subprocess in the browser; stub returns false with a warning |
startLogging(bool) |
✅ | Toggles the per-profile SessionLogger. Mudlet Web records to IndexedDB (the same store the toolbar Logs button browses) |
stopAllNamedEventHandlers([type]) |
✅ | IDManager.lua |
stopMusic([channel]) |
✅ | SoundManager |
stopNamedEventHandler(name) |
✅ | IDManager.lua |
stopSounds([filter]) |
✅ | Table or ordered (name, key, tag, priority, fadeaway, fadeout) filter; priority is a ceiling; no filter stops every sound |
stopVideos() |
✅ | Removes every active <video> element; revokes blob: URLs |
timeframe(s) |
✅ | Other.lua humanises seconds |
translateTable(t) |
✅ | Other.lua |
uninstallModule(name) |
✅ | JS-exposed |
uninstallPackage(name) |
✅ | JS-exposed |
unzipAsync(zipPath, destDir) |
✅ | Creates destDir before returning, then fires sysUnzipDone/sysUnzipError after the call has returned — a missing or invalid zip included |
yajl.to_string / yajl.to_value / yajl.generator / yajl.parser / yajl.null |
✅ | Matches desktop lua-yajl (lua_yajl.c). Yajl.lua: a port of yajl_gen (sparse arrays with null holes, indent, depth-128 limit, __gen_json) and of yajl's push parser (streaming yajl.parser, yajl's error text). yajl.ts: the JSON.parse fast path behind to_value (Lua source, or an iterative raw-API build for deep documents, capped where lua_yajl's stack check is), and yajl.null as a userdata. Loaded at startup via setupYajl |
| Function | Status | Notes |
|---|---|---|
addCmdLineSuggestion([name,] text) |
✅ | Per command line (main bar, createCommandLine, or a console's own line once enabled); Tab completes from these plus the main console's last 500 lines |
adjustStopWatch(id|name, seconds) |
✅ | Add (or subtract) seconds |
ancestors(id, type) |
✅ | Ancestor chain (immediate parent → root) as 1-indexed {id, name, node, isActive}; node is "package"/"group"/"item". (false, errMsg) when no item of that type has the id |
appendCmdLine([name,] text) |
✅ | Routes to overlay cmd lines (createCommandLine), per-userwindow cmd lines, or the main bar |
appendScript(name, code) |
✅ | JS-exposed |
clearCmdLine([name]) |
✅ | Routes to overlay cmd lines, per-userwindow cmd lines, or the main bar |
clearCmdLineSuggestions([name]) |
✅ | Per command line |
clearProfileInformation() |
✅ | Resets the profile description to "" |
createStopWatch([name], [autostart]) |
✅ | performance.now()-based high-res stopwatch (StopwatchManager). Named watches default autostart off |
deleteAllNamedTimers(parent) |
✅ | IDManager.lua |
deleteAllNamedTriggers(parent) |
✅ | IDManager.lua |
deleteNamedTimer(parent, name) |
✅ | IDManager.lua |
deleteNamedTrigger(parent, name) |
✅ | IDManager.lua |
deleteStopWatch(id|name) |
✅ | |
disableAlias(name) |
✅ | |
disableKey(name) |
✅ | Cascades to children |
disableScript(name) |
✅ | JS-exposed |
disableTimer(name) |
✅ | A permanent timer name, or a temp timer id (number or its string form) |
disableTrigger(name) |
✅ | JS-exposed |
enableAlias(name) |
✅ | |
enableKey(name) |
✅ | Cascades to children |
enableScript(name) |
✅ | JS-exposed |
enableTimer(name) |
✅ | JS-exposed |
enableTrigger(name) |
✅ | JS-exposed |
exists(name, type) |
✅ | ScriptingAPI.exists |
findItems(name, type [, exact [, caseSensitive]]) |
✅ | 1-indexed numeric ids of matching items/groups. exact/caseSensitive default true (Mudlet). type as for exists |
getButtonState(name) |
✅ | Two-state button pressed state; nil when missing |
getCmdLine([name]) |
✅ | Reads the live main bar, a named overlay command line, or a console's own line (nil + error until enableCommandLine gives it one) |
getConsoleBufferSize([window]) |
✅ | Bridge.lua → linesLimit, batchSize; nil when console missing |
getExitStubsNames(roomID) |
✅ | Stub direction names ("north"/…/"other"), 1-indexed |
getNamedTimers(parent) |
✅ | IDManager.lua |
getNamedTriggers(parent) |
✅ | IDManager.lua |
getProfileInformation() |
✅ | Returns the profile's free-text description ("" when unset); stored in ProfileSettings.description |
getProfileStats() |
✅ | {triggers={total,temp,active,patterns={total,active}}, aliases=, timers=, keys=, scripts={total,temp,active}, gifs={total,active}}. temp counts live session-scoped temp items from the per-family engines (folded into total/active); scripts have no temp form. No gif tracker, so gifs is always 0 |
getProfiles() |
✅ | Table keyed by profile name, one entry per configured connection: { host, port, loaded, connected, description }. loaded (open & editable) is cross-tab via the per-profile Web Lock; connected is live for this tab and last-announced for others via a BroadcastChannel presence channel (ProfilesPresence), forced false when not loaded; host/port from the connection record; description from the connection record (editable on the connection screen) |
getStopWatches() |
✅ | Re-keys to integer ids → { name, isRunning, isPersistent, elapsedTime } |
getStopWatchTime(id|name) |
✅ | Elapsed seconds without stopping |
getStopWatchBrokenDownTime(id|name) |
✅ | {negative, days, hours, minutes, seconds, milliSeconds, decimalSeconds} off the proxy; false on miss |
getScript(name [, pos]) |
✅ | → code, count for the pos-th (1-indexed) script named name; ("", 0) on miss. Bridge.lua unpacks the {code,count} from __getScript. Unblocks appendScript's code-preserving path (Other.lua) |
invokeFileDialog(fileOrFolder, title [, location]) |
✅ | Synchronous from the script's view: the handler's coroutine parks at the JS resume boundary while an in-app picker browses the profile VFS, then resumes with the picked path ('' on cancel). The client keeps running meanwhile — matching Mudlet's nested QFileDialog event loop. Caveat: can't be called inside a user pcall (Lua 5.1 can't yield across C frames); __exec/event dispatch use the yield-transparent __mudlet_pcall_co instead |
isActive(name, type [, checkAncestors]) |
✅ | Count active items by name/id |
isAncestorsActive(id, type) |
✅ | True when every ancestor group of the item is enabled (item's own state ignored). (false, errMsg) when no item of that type has the id |
isPrompt() |
✅ | True when the current trigger fired against a prompt line |
killAlias(id) |
✅ | |
killKey(id) |
✅ | |
killTimer(id) |
✅ | |
killTrigger(name|id) |
✅ | String → name-based delete; numeric → temp-trigger disposer |
loadProfile(name) |
✅ | Opens the named profile in a NEW browser tab (?profile=<id>&connect=1) and connects — each profile lives in its own tab (per-profile lock), so the calling profile stays open alongside, matching Mudlet's multi-profile model. Returns nil plus a message for an unknown name, a profile already open, or a blocked popup. A browser opens one new tab per user gesture, so the second loadProfile in one alias, and any from a trigger/timer/event, is blocked; the refusal then leaves an Open profile name link in the main console that opens it on click (knownDivergences.ts, #453) |
setActiveProfile(name) |
✅ | true for this tab's own profile (asks for window focus), or false plus why not (empty name, no such profile, not loaded); the name matches case-insensitively. A page can't switch browser tabs, so for a profile open in another tab it returns false ("is open in another browser tab…") and that tab flashes its title until the user switches to it (knownDivergences.ts, #453) |
permAlias(name, parent, pattern, code) |
✅ | Pattern is a single PCRE string (Mudlet TAlias.mRegexCode). Returns the new id, or -1 |
permGroup(name, type [, parent]) |
✅ | Creates a group node in the requested family |
permPromptTrigger(name, parent, code) |
✅ | Persistent trigger firing on every server prompt (GA/EOR); single prompt-type pattern, never a group. Returns the new id or -1 |
permRegexTrigger(name, parent, patterns, code) |
✅ | patterns is a table of regex strings (empty table → creates a trigger group). Bridge.lua joins to \x01 and the JS binding splits it back |
permBeginOfLineStringTrigger(name, parent, patterns, code) |
✅ | Like permSubstringTrigger but each literal pattern matches only at the start of the line (startOfLine kind). Empty patterns array → trigger group |
permSubstringTrigger(name, parent, patterns, code) |
✅ | Each pattern is a literal substring. Empty patterns array creates a trigger group |
permExactMatchTrigger(name, parent, patterns, code) |
✅ | (Mudlet 4.21) Like permSubstringTrigger but each pattern matches only on full-line equality (exactMatch kind). Empty patterns array → trigger group |
permScript(name, parent, code) |
✅ | ScriptingEngine.createPermScript creates a saved Lua script node under a script group (parent "" → root). Returns the new id or -1. Bound via __mudlet_permScript + Bridge.lua wrapper |
permTimer(name, parent, delay, code) |
✅ | Persistent one-shot timer; returns the new id or -1 |
permKey(name, parent, modifier, key, code) |
✅ | modifier is the Qt::KeyboardModifier int (1=shift, 2=ctrl, 4=alt, 8=meta; -1 → none). key accepts a Qt::Key int or a KeyboardEvent.code string |
printCmdLine([name,] text) |
✅ | Routes to overlay cmd lines, per-userwindow cmd lines, or the main bar |
raiseEvent(name, ...) |
✅ | |
raiseGlobalEvent(name, ...) |
✅ | Cross-tab via BroadcastChannel (GlobalEventChannel). Fires the event in every OTHER open profile (each runs in its own browser tab), never the sender; incoming events dispatch through emitEvent so registerAnonymousEventHandler handlers fire. Args limited to string/number/boolean/nil (matches Mudlet); the sender's profile name is appended as the last arg. No per-profile opt-in (matches Mudlet's postInterHostEvent). Asynchronous: the other profiles' handlers run a task later, not before raiseGlobalEvent returns as on desktop — to ask another profile for a value, have it reply with an event of its own and act on the value in that handler, not on the next line (knownDivergences.ts, #453) |
registerNamedTimer(parent, name, delay, code) |
✅ | IDManager.lua |
registerNamedTrigger(parent, name, pattern, code) |
✅ | IDManager.lua |
remainingTime(id) |
✅ | JS-exposed |
removeCmdLineSuggestion([name,] text) |
✅ | Per command line |
resetProfileIcon() |
✅ | Clears ProfileSettings.icon so the connection screen falls back to the auto-generated name tile |
resetStopWatch(id|name) |
✅ | Zeroes elapsed; a running watch keeps running |
resumeNamedTimer(parent, name) |
✅ | IDManager.lua |
resumeNamedTrigger(parent, name) |
✅ | IDManager.lua |
setButtonState(name, state) |
✅ | Pressed state on a two-state (push-down) button |
sendCmdLine(text) |
✅ | Set + send the main command bar |
setConsoleBufferSize([window,] linesLimit [, batchSize]) |
✅ | Maps to Console.setMaxLines |
setProfileIcon(path) |
✅ | Reads the VFS image and inlines it as a data: URI into ProfileSettings.icon so the picker screen renders it without mounting the profile VFS. (true, path) / (false, errMsg) via Bridge.lua |
setProfileInformation(text) |
✅ | Stores the profile's free-text description (ProfileSettings.description); the optional profile-name overload is ignored (single-profile) |
setScript(name, code) |
✅ | JS-exposed |
setStopWatchName(id|currentName, newName) |
✅ | Empty name or duplicate name → false |
setStopWatchPersistence(id|name, state) |
✅ | Persistent watches saved to localStorage and restored on reload; running ones keep counting across reloads (wall-clock Date.now()) |
setTriggerStayOpen(name, lines) |
✅ | Extends the named chain head's open window |
startStopWatch(id|name [, resetAndRestart]) |
✅ | Bare numeric id resets+restarts (legacy); name form resumes |
stopAllNamedTimers(parent) |
✅ | IDManager.lua |
stopAllNamedTrigger(parent) |
✅ | IDManager.lua alias of stopAllNamedTriggers (the wiki lists the singular name) |
stopNamedTimer(parent, name) |
✅ | IDManager.lua |
stopNamedTrigger(parent, name) |
✅ | IDManager.lua |
stopStopWatch(id|name) |
✅ | Returns elapsed seconds |
tempAlias(pattern, code) |
✅ | |
tempAnsiColorTrigger(fg, bg, code) |
✅ | ANSI 256-colour-index variant of tempColorTrigger (shares the palette-matching engine); any negative index (Mudlet ColorIgnore/ColorDefault) → match any |
tempBeginOfLineTrigger(pattern, code) |
✅ | Literal prefix (String.prototype.startsWith), NOT regex ^ — matches Mudlet's match_begin_of_line_substring |
tempButton(toolbar, name, orientation) |
✅ | Appends a transient ButtonNode, with no command or script, under the named toolbar |
tempButtonToolbar(name, orientation, location) |
✅ | orientation: 0=horizontal, 1=vertical. location: 0=top, 1=bottom, 2=left, 3=right, 4=floating |
tempColorTrigger(fg, bg, code) |
✅ | Matches on ANSI palette indices on the current rendered line (-1 = any). Non-indexed RGB segments never match a positive index, matching Mudlet's palette-only semantics |
tempComplexRegexTrigger(...) |
✅ | Bridge.lua over the temp regex-trigger primitive. Honours regex + code/fn, highlight (hlFgColor/hlBgColor — colour name / #rrggbb / "r,g,b"; all occurrences when matchAll), soundFile, expireAfter, and named triggers (re-call with an existing name replaces it; killTrigger(name) removes it). multiline-AND, filter, fireLength, lineDelta and colour-pattern (fgColor/bgColor) need a permanent trigger (permRegexTrigger + editor) and emit a one-time printDebug warning when requested |
tempExactMatchTrigger(pattern, code) |
✅ | Full-line exact match |
tempKey(modifier, key, code) |
✅ | |
tempLineTrigger(from, count, code) |
✅ | Position-based: fires on count lines starting from lines ahead, then self-expires |
tempPromptTrigger(code) |
✅ | Fires on GA/EOR-flagged prompt lines; expirationCount honoured |
tempRegexTrigger(pattern, code) |
✅ | Bridge.lua wraps __mudlet_tempRegexTrigger |
tempTimer(delay, code [, repeat]) |
✅ | One-shot or repeating timer |
tempTrigger(pattern, code) |
✅ | Temporary substring/regex trigger |
Earlier revisions of this file documented a
mudlet.windows.*/mudlet.timers.*/mudlet.aliases.*Lua namespace. No such table has ever been bound — use the Mudlet-native globals.
| Function | Status | Notes |
|---|---|---|
connectToServer(host, port [, save]) |
✅ | Builds the proxy ?host=&port= URL the connection screen uses and (re)connects. save persists host/port onto the active connection |
customHTTP(method, url, data [, headers]) |
✅ | Bridge.lua → HttpService.customHTTP; fires sysCustomHttp* |
deleteHTTP(url [, headers]) |
✅ | Bridge.lua → HttpService.deleteHTTP |
disconnect() |
✅ | MudSession.disconnect |
downloadFile(url, path) |
✅ | Bridge.lua → HttpService.downloadFile, writes to profile VFS |
feedTelnet(data) |
✅ | Injects raw bytes into MudClient.processIncomingData (telnet strip → ANSI → triggers → render). Mudlet Web feeds the live inbound pipeline (Mudlet only loops back when unconnected) |
getConnectionInfo() |
✅ | Bridge.lua → host, port, connected |
getHTTP(url [, headers]) |
✅ | Bridge.lua → HttpService.getHTTP; fires sysGetHttpDone/sysGetHttpError |
getIrcChannels() / getIrcConnectedHost() / getIrcNick() / getIrcServer() |
❌ stub | No IRC client in Mudlet Web; bind as warning-emitting no-op stubs (getters return empty table / "") |
getNetworkLatency() |
✅ | JS-exposed |
loadReplay(fileName) |
✅ | Plays a Mudlet binary replay (.dat) from the profile VFS on its recorded timeline (src/mud/replay/). Reads both Mudlet layouts (original 4-byte + PR-#4400 8-byte offsets). Toolbar Record button writes recordings to log/ in Mudlet's own format |
loadRawFile(fileName) |
✅ | Legacy alias of loadReplay (matches Mudlet's dual registration) |
openIRC() / restartIrc() / sendIrc() / setIrcChannels() / setIrcNick() / setIrcServer() |
❌ stub | No IRC client; bind as warning-emitting no-op stubs |
mmcp.* (MudMaster Chat Protocol) |
❌ stub | (Mudlet 4.21) Peer-to-peer TCP chat between clients — impossible in a browser (no raw/listening sockets, no P2P). The full mmcp.* table (chatTo/chatAll/call/startServer/…) is bound as warning-emitting no-op stubs in Bridge.lua; mudlet.supports.mmcp is false |
openUrl(url) |
✅ | window.open(url, '_blank'); file: prefix routes to the VFS file browser |
postHTTP(url, data [, headers]) |
✅ | Bridge.lua → HttpService.postHTTP |
putHTTP(url, data [, headers]) |
✅ | Bridge.lua → HttpService.putHTTP |
reconnect() |
✅ | Disconnect + redial the last-connected URL (MudSession.lastUrl, set by every connect()), else the profile's configured server; returns nothing, as Mudlet's does |
sendAll(text1, text2, ...) |
✅ | Other.lua |
sendATCP(msg) |
✅ | IAC SB ATCP(200) <payload> IAC SE via MudClient.sendRaw (shared sendSubnegotiation helper); nil, "ATCP is not currently enabled" unless the server has taken ATCP up (isATCPEnabled). Inbound ATCP fills the atcp table and raises one event per message, named after it without dots (CharVitals), and Auth.Request is answered with the hello |
sendGMCP(message) |
✅ | Frames as IAC SB GMCP … |
sendMSDP(var, ...) |
✅ | Frames IAC SB MSDP MSDP_VAR var [MSDP_VAL val]… IAC SE. Bridge.lua packs varargs |
sendSocket(data) |
✅ | Literal bytes (no telnet/encoding processing) |
sendTelnetChannel102(data) |
✅ | IAC SB 102 <data> IAC SE via MudClient.sendRaw (shared sendSubnegotiation helper); false when the socket is closed |
Mudlet Web-specific extras: gmcp table, msdp table, gmcp.<path> per-key event chain.
Standard Lua 5.1 string functions (string.byte, string.char, string.find, string.format, string.gmatch, string.gsub, string.len, string.lower, string.match, string.rep, string.reverse, string.sub, string.upper) ship with wasmoon and are listed once below.
| Function | Status | Notes |
|---|---|---|
addWordToDictionary(word) |
❌ stub | No Hunspell in browser; bind as warning-emitting no-op stub |
cecho2string(text) |
✅ | Pure Lua via GUIUtils.lua |
decho2string(text) |
✅ | Pure Lua via GUIUtils.lua |
f(str) |
✅ | StringUtils.lua — {expr} interpolation |
getDictionaryWordList() |
❌ stub | Stub returns empty table |
hecho2string(text) |
✅ | Pure Lua via GUIUtils.lua |
removeWordFromDictionary(word) |
❌ stub | Warning-emitting no-op |
spellCheckWord(word, useUser) |
❌ stub | Stub returns true (treat every word as spelled correctly) |
spellSuggestWord(word, useUser, n) |
❌ stub | Stub returns empty table |
string.byte / string.char / string.find / string.format / string.gmatch / string.gsub / string.len / string.lower / string.match / string.rep / string.reverse / string.sub / string.upper |
✅ | Lua 5.1 stdlib (wasmoon) |
string.cut(s, maxlen) |
✅ | StringUtils.lua |
string.dump(fn) |
✅ | Lua 5.1 stdlib |
string.enclose(s [, level]) |
✅ | StringUtils.lua (bundled verbatim) |
string.ends(s, suffix) |
✅ | StringUtils.lua |
string.findPattern(s, pattern) |
✅ | StringUtils.lua (bundled verbatim) |
string.genNocasePattern(s) |
✅ | StringUtils.lua |
string.gfind(s, pat) |
✅ | Lua 5.1 alias for string.gmatch (wasmoon) |
string.patternEscape(s) |
✅ | StringUtils.lua |
string.split(s, sep) |
✅ | StringUtils.lua |
string.starts(s, prefix) |
✅ | StringUtils.lua |
string.title(s) |
✅ | StringUtils.lua |
string.trim(s) |
✅ | StringUtils.lua |
utf8.byte / utf8.char / utf8.find / utf8.gmatch / utf8.gsub / utf8.len / utf8.lower / utf8.match / utf8.reverse / utf8.sub / utf8.upper |
✅ | utf8.lua, the utf8 global. find/match/gmatch/gsub, len, sub and byte are luautf8 0.2.1's, ported from its C: position captures, %f, %b and back-references work, an invalid byte is a character of its own where luautf8 only walks, and a regex-expressible pattern runs natively (utf8Patterns.ts). reverse/char are the Stepets helpers |
utf8.patternEscape |
✅ | StringUtils.lua. Escapes Lua-pattern magic chars (function replacement — the bundled utf8.gsub drops table-replacement misses) |
utf8.title / utf8.codes / utf8.isvalid / utf8.invalidoffset / utf8.clean / utf8.isnfc / utf8.normalize_nfc / utf8.widthlimit / utf8.grapheme_indices / utf8.version |
✅ | luautf8 0.2.1, ported from its C into utf8.lua. title maps every character to its titlecase, as luautf8 does (utf8.title("élan") is ÉLAN) — not StringUtils' first-letter string.title. NFC and grapheme clusters come from the JS engine (String.prototype.normalize, Intl.Segmenter) |
utf8.charpos / utf8.escape / utf8.fold / utf8.insert / utf8.ncasecmp / utf8.next / utf8.offset / utf8.remove / utf8.width / utf8.widthindex |
✅ | luautf8 0.2.1, ported from its C into utf8.lua. fold/ncasecmp use luautf8's own case-folding table; width/widthindex use Markus Kuhn's wcwidth ranges (combining → 0, East-Asian wide/fullwidth → 2) and accept (but don't tabulate) ambi_is_double |
Standard Lua 5.1 table functions (table.concat, table.insert, table.maxn, table.remove, table.sort) ship with wasmoon.
| Function | Status | Notes |
|---|---|---|
spairs(t [, fn]) |
✅ | TableUtils.lua — sorted-key iterator |
table.collect(t, fn) |
✅ | TableUtils.lua |
table.complement(t1, t2) |
✅ | TableUtils.lua |
table.concat |
✅ | Lua 5.1 stdlib |
table.contains(t, val) |
✅ | TableUtils.lua |
table.deepcopy(t) |
✅ | TableUtils.lua |
table.insert |
✅ | Lua 5.1 stdlib |
table.intersection(t1, t2) |
✅ | TableUtils.lua |
table.index_of(t, val) |
✅ | TableUtils.lua |
table.is_empty(t) |
✅ | TableUtils.lua |
table.keys(t) |
✅ | TableUtils.lua |
table.load(filename) |
✅ | Other.lua, uses dofile/VFS |
table.matches(t, ...) |
✅ | TableUtils.lua |
table.maxn |
✅ | Lua 5.1 stdlib |
table.n_collect(t, fn) |
✅ | TableUtils.lua |
table.n_filter(t, fn) |
✅ | TableUtils.lua |
table.n_flatten(t) |
✅ | TableUtils.lua |
table.n_matches(t, ...) |
✅ | TableUtils.lua |
table.n_union(t1, t2) |
✅ | TableUtils.lua |
table.n_complement(t1, t2) |
✅ | TableUtils.lua |
table.n_intersection(t1, t2) |
✅ | TableUtils.lua |
table.pickle(t) |
✅ | TableUtils.lua |
table.remove |
✅ | Lua 5.1 stdlib |
table.save(filename, t) |
✅ | Other.lua, uses io.open/VFS |
table.sort |
✅ | Lua 5.1 stdlib |
table.size(t) |
✅ | Counts all keys including non-integer |
table.unpickle(s) |
✅ | TableUtils.lua |
table.update(t1, t2) |
✅ | TableUtils.lua |
table.union(t1, t2, ...) |
✅ | TableUtils.lua |
Implemented via the Web Speech API (TtsManager). Mudlet uses ranges -1..1 for rate/pitch and 0..1 for volume; Mudlet Web maps these to Web Speech ranges at speak time.
| Function | Status | Notes |
|---|---|---|
ttsClearQueue([index]) |
✅ | Whole queue or the 1-based index item (false if out of bounds) |
ttsGetCurrentLine() |
✅ | Maps idle/errored to (nil, "not speaking any text") |
ttsGetCurrentVoice() |
✅ | Selected voice name, or engine default |
ttsGetPitch() |
✅ | |
ttsGetQueue([index]) |
✅ | 1-based; index form returns one item or false |
ttsGetRate() |
✅ | |
ttsGetState() |
✅ | ttsSpeechReady/ttsSpeechStarted/ttsSpeechPaused/ttsSpeechError/ttsUnknownState, raised as events on transitions |
ttsGetVoices() |
✅ | 1-based array of voice names |
ttsGetVolume() |
✅ | |
ttsPause() |
✅ | |
ttsQueue(text [, index]) |
✅ | Inserts at 1-based index (default end); raises ttsSpeechQueued(text, index) |
ttsResume() |
✅ | |
ttsSpeak(text) |
✅ | Speaks immediately, interrupting current. Strips angle brackets like Mudlet |
ttsSetPitch(pitch) |
✅ | Raises ttsPitchChanged |
ttsSetRate(rate) |
✅ | Raises ttsRateChanged |
ttsSetVolume(vol) |
✅ | Raises ttsVolumeChanged |
ttsSetVoiceByIndex(index) |
✅ | 1-based; returns bool |
ttsSetVoiceByName(name) |
✅ | Returns bool; raises ttsVoiceChanged |
ttsSkip() |
✅ | Stops current, advances to next queued |
setAppStyleSheet / setProfileStyleSheet install a QApplication-level stylesheet in
Mudlet, so themes address Qt widget classes (QToolButton { … }) and Mudlet widgets by
Qt objectName (QWidget#widget_panel { … }). Both forms are syntactically valid CSS, so
the browser parses them happily and they match nothing. rewriteQtSelectors
(src/ui/labels/qtCss.ts) bridges them onto the DOM; the same pass Qt→CSS translates the
declarations of any rule it rewrote (0–255 rgba() alpha, unitless lengths,
QLinearGradient, Qt-only subcontrol-*). Unmapped types and plain .mudlet-* CSS pass
through byte-identical — app stylesheets are also Mudlet Web's brand-styling hook.
- objectName forms —
QWidget#widget_paneland bare#widget_panelmap onto thedata-qt-objectattributes components render (QT_OBJECT_NAMES). Because Qt clips a widget to its geometry, a rule withmax-height: 0also getsoverflow: hidden. - Widget types —
QT_TYPE_MAPmaps types and their subcontrols onto Mudlet Web classes: main window, dock widgets (user windows), dialogs, splitters, toolbars and toolbar/push buttons, menus, text inputs, combo boxes, lists, trees, headers, tabs, progress bars, tooltips, labels, scrollbars — plus Mudlet's own widget classes (TConsole,TTextEdit,TCommandLine,TLabel,TEasyButtonBar,dlgMapper), so the wiki's descendant recipeTConsole QScrollBar:vertical { … }scopes correctly. Adding coverage is a table entry, not new code. Deliberately absent:QStatusBar(Mudlet Web has no status bar) and Qt's::up-arrow/::down-arrow(painted inside the buttons::add-line/::sub-linealready claim). QWidgetmeans what it means in Qt — a type selector matches the class and its subclasses, andQWidgetis the root, so it matches every widget; themes use it for a base coat. The expansion is the union of every mapped type, so it widens as the table does. Two exclusions, both matching Mudlet rather than departing from it: self-painted widgets (Mudlet's console draws its own text, so a blanket rule never reached it there) and the scrollbar pseudo-elements. Name those types directly and they style fine.- States —
:hover,:pressed→:active,:focus,:disabled,:!x→:not(:x), and per-type spellings for what CSS has no pseudo-class for (a tab's:selectedis a modifier class, a tool button's:checkedisaria-pressed). States that describe a fixed property of the layout (:top,:activeas in "active window") are dropped from the selector, keeping the rule. A state that would widen a rule if ignored — a list item's:selectedwith no modifier class to hang off — leaves the whole rule inert instead. - Rewritten rules outrank Mudlet Web's own CSS. Landing on the right element isn't
enough — Mudlet Web's rules often carry more specificity than the class the table maps to
(
.mudlet-btn:hoverbeats a bare.mudlet-btn), so a theme would set a base colour and then lose every hover. Every rewritten selector is prefixed with:root:root, which matches the same elements and adds two classes' worth of specificity. Deliberately not!important: that would also override inline style, and inline style is how a widget's own stylesheet is applied (setLabelStyleSheeton a Geyser label) — Qt resolves that the same way round, per-widget sheet over application sheet. - Scrollbars are the loosest mapping: the browser has no scrollbar element, only
WebKit/Blink's
::-webkit-scrollbar…pseudo-elements, so themed scrollbars are Chromium-only (Firefox exposes justscrollbar-color/-width). They're emitted as a rule of their own — one unknown selector invalidates an entire CSS selector list, which would take the ordinary selectors down with it. Chromium also ignores the whole WebKit family once the standardscrollbar-width/scrollbar-colorare set, andApp.csssets both globally, so a sheet that styles scrollbars emits a scopedscrollbar-width: auto; scrollbar-color: autopreamble first. Scrollbar selectors are also the one place the:root:rootboost is not applied, and a scoped rule (TConsole QScrollBar:vertical) resolves to the widget's own scroller (QtTypeEntry.scrollers) rather than to a descendant selector: Chromium stops matching the orientation pseudo-classes as soon as a combinator appears in front of the pseudo-element, so.output-container ::-webkit-scrollbar:verticalparses, matches nothing, and paints nothing. A widget with no scroller of its own can't host a scoped rule, and one is left inert rather than widened to every scrollbar in the app. Two escape hatches are excluded from all of it:mudlet-native-scrollbar(surfaces that hide their scrollbar by design — the tab strip, the mobile switcher, the settings tabs — and the documented opt-out for anything else) andmudlet-no-scrollbar(a console that calleddisableScrollBar; an explicit call outranks a theme).
| Function | Status | Notes |
|---|---|---|
addCommandLineMenuEvent(name, event) |
✅ | Right-click command-line menu hook |
addMouseEvent(uniquename, event [, displayName [, tooltip]]) |
✅ | MouseEventRegistry (mirrors Host::mConsoleActions) on MudSession. Adds a custom entry to the main output area's right-click menu (OutputArea context menu); clicking raises event. False on a duplicate uniqueName |
ansi2decho(text) |
✅ | Pure Lua via GUIUtils.lua |
ansi2string(text) |
✅ | Pure Lua via GUIUtils.lua |
appendBuffer([window]) |
✅ | Appends the clipboard (from copy()) as a new line to the named console |
bg([window,] colorname) |
✅ | Set background color by name |
calcFontSize(size[, family]) | calcFontSize(windowName) |
✅ | Canvas-2D monospace cell measurement, falls back to App.css --font-mono. Rounded to whole pixels like QFontMetrics, so N * width lands on a real widget size |
cecho([window,] text) |
✅ | <colorname>text syntax |
cechoLink([window,] text, cmd, hint) |
✅ | Pure Lua via GUIUtils.lua |
cecho2ansi(text) |
✅ | Pure Lua via GUIUtils.lua |
cecho2decho(text) |
✅ | Pure Lua via GUIUtils.lua |
cecho2hecho(text) |
✅ | Pure Lua via GUIUtils.lua |
cecho2html(text) |
✅ | Pure Lua via GUIUtils.lua |
cechoPopup(...) |
✅ | Pure Lua via GUIUtils.lua |
cinsertLink([window,] text, cmd, hint) |
✅ | Pure Lua via GUIUtils.lua |
cinsertPopup([window,] text, cmds, hints) |
✅ | Pure Lua via GUIUtils.lua |
cinsertText([window,] text) |
✅ | Pure Lua via GUIUtils.lua |
clearUserWindow(name) |
✅ | Alias of clearWindow on user windows |
clearWindow(name) |
✅ | Clears panel content |
closestColor(r, g, b) |
✅ | Pure Lua via GUIUtils.lua |
copy([window]) |
✅ | Copies the current selection (with formatting) into the session clipboard (Mudlet's host-global mClipboard) |
copy2decho() |
✅ | Returns the current selection as decho text |
copy2html() |
✅ | Returns the current selection as HTML |
createBuffer(name) |
✅ | Off-screen text buffer (no panel) — registers a named Console; output stays in history (never opens a panel) and is selectable/copyable. windowType reports "buffer" |
createCommandLine([parent,] name, x, y, w, h) |
✅ | Absolutely-positioned overlay <input> rendered by CommandLineOverlay on the named parent viewport (defaults to main). Sibling to createLabel / createMiniConsole — uses the unified moveWindow / resizeWindow / showWindow / hideWindow / raiseWindow / lowerWindow lookup |
createConsole(name, fontSize, charsW, linesH, x, y) |
✅ | JS-exposed |
createGauge(name, x, y, w, h, parent) |
✅ | Pure Lua via GUIUtils.lua (3× createLabel + setBackgroundColor) |
createLabel(name, x, y, w, h, passthrough) |
✅ | JS-exposed |
<a href="…"> links inside a label |
✅ | Mudlet's TLabel::slot_linkActivated. LabelOverlay routes a click on an anchor in the label's echoed HTML to LabelManager.activateLink → ScriptingAPI.activateLabelLink: send:CMD sends, prompt:CMD stages the command bar, http(s): opens a tab, and a scheme-less href runs as Lua (see ui/labels/labelLinks.ts). Any other scheme (javascript:, file:, …) is ignored rather than run. As in Mudlet, a click that lands on a link is not delivered to the label's own click/release callback |
createMiniConsole(name, x, y, w, h) |
✅ | JS-exposed |
createScrollBox([parent,] name, x, y, w, h) |
✅ | Absolutely-positioned scrollable overlay container (ScrollBoxManager + ScrollBoxOverlay) on the named parent viewport (defaults to main). Other overlay widgets (labels, command lines, nested scroll boxes) nest inside it by passing the box name as their parent; backs Geyser.ScrollBox. Routed by the unified moveWindow/resizeWindow/showWindow/hideWindow/raiseWindow/lowerWindow lookups; windowType reports "scrollbox". Opaque default background (themed --bg-input) mirroring Mudlet's bare QScrollArea, so an empty box is visible. Real overflow scrolling: each box wraps its children in a content div sized to their furthest edge (computed by subscribing to the label/cmdline/scrollbox managers for the box), and scrolls a given axis only when its children overflow it |
creplace([window,] text) |
✅ | Pure Lua via GUIUtils.lua |
creplaceLine([window,] text) |
✅ | Pure Lua via GUIUtils.lua |
decho([window,] text) |
✅ | <r,g,b>text syntax |
decho2ansi(text) |
✅ | Pure Lua via GUIUtils.lua |
decho2cecho(text) |
✅ | Pure Lua via GUIUtils.lua |
decho2hecho(text) |
✅ | Pure Lua via GUIUtils.lua |
decho2html(text) |
✅ | Pure Lua via GUIUtils.lua |
dechoLink([window,] text, cmd, hint) |
✅ | Pure Lua via GUIUtils.lua |
dechoPopup(...) |
✅ | Pure Lua via GUIUtils.lua |
dinsertLink([window,] text, cmd, hint) |
✅ | Pure Lua via GUIUtils.lua |
dinsertPopup(...) |
✅ | Pure Lua via GUIUtils.lua |
deleteCommandLine(name) |
✅ | Destroys an overlay cmd line; fires sysCommandLineDeleted(name) and frees the bound action callback chunk |
deleteLabel(name) |
✅ | Bridge.lua → __deleteLabel |
deleteLine() |
✅ | Removes last output element |
deleteMiniConsole(name) |
✅ | Rejects non-miniconsole targets (CONSOLE-only, matches Mudlet) |
deleteMultiline(text) |
✅ | Multi-line deletion (GUIUtils.lua) |
deleteScrollBox(name) |
✅ | Destroys a scroll box created by createScrollBox; fires sysScrollBoxDeleted(name) on success |
deselect([window]) |
✅ | JS-exposed |
disableClickthrough(name) |
✅ | JS-exposed |
disableCommandLine(name) |
✅ | Overlay cmd lines disable the input (greyed); per-userwindow cmd lines hide the docked input; main bar is a no-op |
disableHorizontalScrollBar(name) |
✅ | JS-exposed |
disableScrollBar(name) |
✅ | JS-exposed |
disableScrolling(name) |
✅ | JS-exposed |
dreplace([window,] text) |
✅ | Pure Lua via GUIUtils.lua |
dreplaceLine([window,] text) |
✅ | Pure Lua via GUIUtils.lua |
echoLink([window,] text, cmd, hint) |
✅ | Bridge.lua maps function cmd to a callback id |
echoUserWindow(name, text) |
🚧 | Not bound. Deprecated in Mudlet in favour of echo(window, text), which is implemented |
echoPopup([window,] text, cmds, hints) |
✅ | Bridge.lua flattens cmds/hints tables |
enableClickthrough(name) |
✅ | JS-exposed |
enableCommandLine(name) |
✅ | Overlay cmd lines re-enable a disabled input; per-userwindow cmd lines show the docked input; main bar is a no-op |
enableHorizontalScrollBar(name) |
✅ | JS-exposed |
enableScrollBar(name) |
✅ | JS-exposed |
enableScrolling(name) |
✅ | JS-exposed |
fg([window,] colorname) |
✅ | Set foreground color by name |
getAvailableFonts() |
✅ | {[family]=true} set merging web-safe families, FontFaceSet registrations, the profile font, and Local Font Access results |
getBackgroundColor([window]) |
✅ | JS-exposed |
getBgColor([window]) |
✅ | Bridge.lua — color at selection start; distinct from window-background getBackgroundColor |
getBorderBottom() / getBorderTop() / getBorderLeft() / getBorderRight() |
✅ | JS-exposed |
getBorderSizes() |
✅ | JS-exposed |
getClipboardText() |
✅ | Returns a session text-clipboard mirror synchronously (Mudlet's signature); the OS clipboard can only be read async in the browser, so it kicks off a best-effort navigator.clipboard.readText refresh for the next call. Distinct from copy/paste's rich-text buffer |
getColorWildcard() |
✅ | Returns the captured colour wildcard from the current trigger |
getColumnCount([window]) |
✅ | JS-exposed |
getColumnNumber([window]) |
✅ | JS-exposed |
getCurrentLine([window]) |
✅ | Bridge.lua wraps __getCurrentLine |
getFgColor([window]) |
✅ | Bridge.lua — color at selection start; falls back to profile default |
getFont([window]) |
✅ | Bridge.lua → __getFont |
getFontSize([window]) |
✅ | Bridge.lua → __getFontSize |
getHTMLformat(text) |
✅ | Mudlet-format → HTML serialisation |
getImageSize(path) |
✅ | Synchronous — reads dimensions straight out of the VFS file's header (imageSize.ts parses PNG/GIF/JPEG/BMP/WebP), no Image.onload decode needed. Returns width, height or nil; Bridge.lua unpacks the 0-indexed [w,h] array |
getLabelFormat(name) |
✅ | GUIUtils.lua |
getLabelSizeHint(name) |
✅ | Bridge.lua → width, height. Browser analogue of Qt sizeHint (rendered content extent); a label showing only an SVG answers the document size plus its stylesheet chrome, as TLabel::sizeHint does |
getLabelStyleSheet(name) |
✅ | Reads the CSS last set via setLabelStyleSheet |
getLastLineNumber([window]) |
✅ | JS-exposed |
getLineCount([window]) |
✅ | JS-exposed |
getLines([window,] from, to) |
✅ | Bridge.lua wraps __getLines |
getLineNumber([window]) |
✅ | JS-exposed |
getMainConsoleWidth() |
✅ | Monospace cell width × (wrap columns + 1) |
getMouseEvents() |
✅ | { [uniqueName] = { ["event name"], ["display name"], ["tooltip text"] } } from the MouseEventRegistry |
getMousePosition() |
✅ | Bridge.lua — last-seen cursor position in main viewport coords |
getProfileTabNumber(name) |
✅ | No tab UI in Mudlet Web; single-profile, so always returns 1 |
getMainWindowSize() |
✅ | Returns window.innerWidth, window.innerHeight |
getRowCount([window]) |
✅ | JS-exposed |
getScroll([window]) |
✅ | Returns the scroll position (top-most visible line) |
getSelection([window]) |
✅ | Bridge.lua wraps __getSelection |
getTextFormat([window]) |
✅ | Bridge.lua → documented attribute table |
getUserWindowSize(name) |
✅ | Bridge.lua → __getUserWindowSize |
getWindowWrap(name) |
✅ | → wrap columns, with desktop's defaults: "main" reads the profile outputWrapAt, 100 unless changed (Host::mWrapAt); a miniconsole or user window reads the WindowManager hint, 99999999 when unset (TBuffer::mWrapAt); a createBuffer buffer takes main's width when it is made. 0 only when the Settings turned main's wrapping off. -1 when the window is missing. Used by Geyser.MiniConsole:getWindowWrap |
handleWindowResizeEvent() |
✅ | Fires the resize listener chain (no-op shim that's part of the public API) |
hasFocus([window]) |
✅ | document.activeElement check. No name = command bar; a name targets the registered overlay element |
hecho([window,] text) |
✅ | #RRGGBBtext syntax |
hecho2ansi(text) / hecho2cecho(text) / hecho2decho(text) / hecho2html(text) |
✅ | Pure Lua via GUIUtils.lua |
hechoLink([window,] text, cmd, hint) |
✅ | Pure Lua via GUIUtils.lua |
hechoPopup(...) |
✅ | Pure Lua via GUIUtils.lua |
hideGauge(name) |
✅ | Pure Lua via GUIUtils.lua |
hinsertLink([window,] text, cmd, hint) |
✅ | Pure Lua via GUIUtils.lua |
hinsertPopup(...) |
✅ | Pure Lua via GUIUtils.lua |
hreplaceLine([window,] text) |
✅ | Pure Lua via GUIUtils.lua |
hreplace([window,] text) |
✅ | Pure Lua via GUIUtils.lua |
hideToolBar(name) |
✅ | Disables the toolbar group; false when no toolbar of that name exists |
hideWindow(name) |
✅ | JS-exposed |
insertLink([window,] text, cmd, hint) |
✅ | Bridge.lua maps function cmd to a callback id |
insertPopup([window,] text, cmds, hints) |
✅ | Bridge.lua flattens cmds/hints tables |
insertText([window,] text) |
✅ | JS-exposed |
ioprint(...) |
✅ | Mudlet's print-to-stdout helper; routes to the devtools console.log in the browser |
isAnsiBgColor(idx) / isAnsiFgColor(idx) |
✅ | True when the fg/bg color at the current selection start equals ANSI/xterm index idx (0-7 normal, 8-15 bright, 16-255 xterm-256). Mudlet Web stores rendered RGB, so it compares against the palette entry's RGB; false with no selection. Used by Other.lua |
loadWindowLayout() |
✅ | Re-applies the saved snapshot — re-positions live windows and reopens saved-visible windows |
lowerWindow(name) |
✅ | JS-exposed |
moveCursor([window,] x, y) |
✅ | JS-exposed |
moveCursorDown([window]) / moveCursorUp([window]) |
✅ | GUIUtils.lua |
moveCursorEnd([window]) |
✅ | JS-exposed |
moveGauge(name, x, y) |
✅ | Pure Lua via GUIUtils.lua |
moveWindow(name, x, y) |
✅ | JS-exposed |
openUserWindow(name [, …]) |
✅ | Opens (or focuses) a dockable user-window panel |
paste([window]) |
✅ | Pastes the clipboard at the cursor; appends at end when on the last line |
pauseMovie(name) |
✅ | Pauses the label's GIF player on its current frame (in-browser QMovie replacement — see setMovie) |
prefix(text) |
✅ | Pure Lua via GUIUtils.lua (moveCursor + insertText) |
print(...) |
✅ | Alias for echo |
raiseWindow(name) |
✅ | CSS z-index on labels via raiseLabel/lowerLabel |
removeCommandLineMenuEvent(name, event) |
✅ | Pairs with addCommandLineMenuEvent |
removeMouseEvent(uniquename) |
✅ | Removes a MouseEventRegistry entry; pairs with addMouseEvent |
replace(text) |
✅ | JS-exposed |
replaceAll(what, with) |
✅ | Pure Lua sweep over the current line buffer |
replaceLine(text) |
✅ | Pure Lua via GUIUtils.lua (selectCurrentLine + replace) |
replaceWildcard(n, text) |
✅ | Replace the n-th capture group in the current line |
resetCmdLineAction([name]) |
✅ | Routes to overlay cmd lines, per-userwindow cmd lines, or the main bar |
resetBackgroundImage(name) |
✅ | Clears the label's (or window's) background image |
resetFormat([window]) |
✅ | Reset all formatting |
resetLabelCursor(name) |
✅ | JS-exposed |
resetLabelToolTip(name) |
✅ | JS-exposed |
resetMapWindowTitle() |
✅ | Pairs with setMapWindowTitle |
resetUserWindowTitle(name) |
✅ | Pairs with setUserWindowTitle |
resizeWindow(name, w, h) |
✅ | JS-exposed |
saveWindowLayout() |
✅ | Snapshots window hints + dock extents into connectionLayoutSnapshots |
scaleMovie(name, [autoscale]) |
✅ | Scales the GIF to the label; autoscale (default) keeps tracking label resizes via CSS 100%, false freezes at the current size |
selectCaptureGroup(n) |
✅ | JS-exposed |
selectCmdLineText([name]) |
✅ | Selects all text in the targeted overlay cmd line or the main bar (per-userwindow cmd lines accept the name for parity) |
selectCurrentLine([window]) |
✅ | JS-exposed |
selectSection([window,] col, len) |
✅ | JS-exposed |
selectString([window,] text, n) |
✅ | JS-exposed |
setAppStyleSheet(css) |
✅ | Installs/replaces a CSS block in document.head; raises sysAppStyleSheetChange. Profile-local, and identical in scope to setProfileStyleSheet — Mudlet separates the two because one QApplication hosts several profiles, but a Mudlet Web tab hosts one, so both are keyed by connection id and removed with the profile (leaving either installed restyled the next profile opened in that tab). Qt selectors are bridged to the DOM by rewriteQtSelectors (ui/labels/qtCss.ts) — see Qt stylesheet selectors |
setBackgroundColor([window,] r,g,b,a) |
✅ | JS-exposed |
setBackgroundImage(name, path) |
✅ | Pure Lua via GUIUtils.lua → setLabelStyleSheet |
setBgColor([window,] r, g, b) |
✅ | JS-exposed |
setBold([window,] bool) |
✅ | JS-exposed |
setBorderBottom(px) / setBorderTop(px) / setBorderLeft(px) / setBorderRight(px) |
✅ | JS-exposed |
setBorderColor(r,g,b) |
✅ | Also resetBorderColor |
getBorderColor() |
✅ | (Mudlet 4.21) → r, g, b of the main console frame border. Returns the setBorderColor override when set, else the main window background, else 0,0,0. Bridge.lua unpacks the 3-channel array |
setBorderSizes(...) |
✅ | Bulk setter via the four side-specific routines |
setFgColor([window,] r, g, b) |
✅ | JS-exposed |
setButtonStyleSheet(name, css) |
✅ | Raw QSS → inline React style. Pseudo-state selectors (:hover/:pressed) drop through |
setClipboardText(text) |
✅ | Updates the session text-clipboard mirror (authoritative) and best-effort writes to navigator.clipboard (may be gesture/permission gated). Always true |
setCmdLineAction([name,] fn) |
✅ | Routes to overlay cmd lines, per-userwindow cmd lines, or the main bar. Prior callback freed on rebind |
setCmdLineStyleSheet([name,] css) |
✅ | Translates QSS through cmdLineQssToScopedCss for overlay and per-userwindow cmd lines; main bar has no QSS hook so returns true as a no-op |
setFont([window,] font) |
✅ | Bridge.lua → __setFont |
setFontSize([window,] size) |
✅ | Bridge.lua → __setFontSize |
setGauge(name, current, max [, text]) |
✅ | Pure Lua via GUIUtils.lua |
setGaugeStyleSheet(name, css [, textcss]) |
✅ | Pure Lua via GUIUtils.lua → setLabelStyleSheet |
setGaugeText(name, text [, r, g, b]) |
✅ | Pure Lua via GUIUtils.lua |
setHexBgColor([window,] hex) |
✅ | Pure Lua via GUIUtils.lua → setBgColor |
setHexFgColor([window,] hex) |
✅ | Pure Lua via GUIUtils.lua → setFgColor |
setItalics([window,] bool) |
✅ | JS-exposed |
setLabelToolTip(name, text, delay) |
✅ | JS-exposed |
setLabelClickCallback(name, fn) |
✅ | Bridge.lua + JS callback registry |
setLabelDoubleClickCallback(name, fn) |
✅ | Bridge.lua |
setLabelMoveCallback(name, fn) |
✅ | Bridge.lua |
setLabelOnEnter(name, fn) |
✅ | Bridge.lua |
setLabelOnLeave(name, fn) |
✅ | Bridge.lua |
setLabelReleaseCallback(name, fn) |
✅ | Bridge.lua |
setLabelStyleSheet(name, css) |
✅ | JS-exposed |
setLabelCursor(name, shape) |
✅ | JS-exposed |
setLabelCustomCursor(name, path[, hotX, hotY]) |
✅ | CSS cursor: url(...) hotX hotY, auto; path resolved through the VFS-aware rewriter |
setLabelWheelCallback(name, fn) |
✅ | Bridge.lua |
setSvgTint(name, r, g, b | colour) / resetSvgTint(name) |
✅ | Bridge.lua; Mudlet colour name (Geyser.Color matching), QColor string (qColor.ts) or r,g,b. Drawn as a CSS mask of the SVG over a fill (QPainter SourceIn). Belongs to the label: survives a reset and a new image |
setSvgRotation(name, deg) / setSvgShear(name, sx, sy) / resetSvgRotation / resetSvgShear / resetSvgTransform |
✅ | Bridge.lua; CSS transform about the SVG's centre, clipped to the contents rect. Non-finite values refused with (nil, reason) |
setLink([window,] cmd, hint) |
✅ | Bridge.lua maps function cmd to a callback id |
setMainWindowSize(w, h) |
✅ | Sizes the main viewport — the rectangle getMainWindowSize reports — since a tab cannot resize itself. No window chrome between the two |
setMapWindowTitle(title) |
✅ | Sets the dockable map panel (id "map") tab title via WindowManager.setTitle; empty title resets to default. False when the map widget is closed. Unblocks resetMapWindowTitle (GUIUtils) and Geyser.Mapper |
setMiniConsoleFontSize(name, size) |
✅ | Bridge.lua; rejects non-miniconsole targets (CONSOLE-only, matches Mudlet) |
setMovie(name, path) / setMovieFrame(name, n) / setMovieSpeed(name, factor) / startMovie(name) |
✅ | QMovie replaced by an in-browser GIF decoder (gifMovie.ts) rendering to a <canvas> in the label — full pause/frame/speed/scale control; path resolves through the profile VFS. Animated WebP / APNG also decode via WebCodecs ImageDecoder where available (Chromium/Safari; frames land async into a pending player). Geyser Label:setMovie etc. work via the bundled wrappers. mp4 is NOT a QMovie format — video goes through playVideoFile (already ✅) |
setOverline([window,] bool) |
✅ | FormatState overline channel (ANSI SGR 53/55) → CSS text-decoration: overline; selection-aware like the other style setters. setTextFormat/getTextFormat carry it too |
setPopup([window,] cmds, hints) |
✅ | Right-click popup on the current selection (preserves formatting, like setLink) |
setProfileStyleSheet(css) |
✅ | Installs/replaces a profile-wide <style> block in document.head (keyed apart from setAppStyleSheet so the two coexist, and removed with the profile); raises sysAppStyleSheetChange with tag "profile". Same scope and same Qt-selector bridge as setAppStyleSheet |
setReverse([window,] bool) |
✅ | Sets FormatState.inverse on pen + selection (renderer swaps fg/bg) |
setStrikeOut([window,] bool) |
✅ | JS-exposed |
setTextFormat([window,] ...) |
✅ | r1,g1,b1,r2,g2,b2,bold,underline,italics[,strikeout,overline,reverse,blink] |
setUnderline([window,] bool) |
✅ | JS-exposed |
setUserWindowTitle(name, title) |
✅ | JS-exposed |
setUserWindowStyleSheet(name, css) |
✅ | JS-exposed |
setWindow(windowName, name[, x, y, show]) |
✅ | Reparents a label / overlay cmdline / text edit / scroll box (manager setParent, notifies old + new parent overlays) or a miniconsole / mapper panel (WindowManager.setParent re-portals it) into main, a userwindow, or a scroll box. show=false keeps it hidden after the move (Qt reparent semantics). Userwindow bases refuse to move; scroll-box cycles refused |
setWindowWrap(name, col) |
✅ | JS-exposed. "main" stores ProfileSettings.outputWrapAt (default 100, as desktop). A server line or echo longer than the width is stored as several buffer lines (Console.wrapAppendedLine, after the triggers saw it whole), so getLines/getLineCount/cursor positions match desktop. col < 1 is refused from Lua, as desktop does. Also settable from Settings → Main display → Word wrapping, which applies at once, and a saved width is applied whenever the profile loads; only that field can set 0, which turns the stored-line wrap off |
setWindowWrapHangingIndent(name, n) |
✅ | Indent (chars) of wrapped continuation lines. Stored on ProfileSettings.outputWrapHangingIndent ("main") or the WindowManager hint (named windows); StickyOutputPanel applies it as the --wrap-hanging CSS var (.output-msg-content padding-left). 0 clears |
setWindowWrapIndent(name, n) |
✅ | Indent (chars) of newline-started lines. Stored on ProfileSettings.outputWrapIndent ("main") or the WindowManager hint; applied via the --wrap-indent CSS var (text-indent, relative to the hanging indent). 0 clears |
showCaptureGroups() |
✅ | Pure Lua via DebugTools.lua (uses matches) |
showColors([columns]) |
✅ | Pure Lua via GUIUtils.lua |
showGauge(name) |
✅ | Pure Lua via GUIUtils.lua |
showMultimatches() |
✅ | Pure Lua via DebugTools.lua |
showToolBar(name) |
✅ | Flips a toolbar group's enabled flag; false when no toolbar of that name exists |
showWindow(name) |
✅ | JS-exposed |
suffix(text) |
✅ | Pure Lua via GUIUtils.lua |
setCommandBackgroundColor([window,] r,g,b[,a]) |
✅ | Patches the inputBackground profile field. Main bar only |
setCommandForegroundColor([window,] r,g,b[,a]) |
✅ | Patches the inputForeground profile field. Main bar only |
scrollDown([window,] lines) |
✅ | Pure Lua via GUIUtils.lua |
scrollUp([window,] lines) |
✅ | Pure Lua via GUIUtils.lua |
scrollTo([window,] line) |
✅ | Jumps the scroll position |
windowType(name) |
✅ | Bridge.lua → __windowType |
wrapLine([window,] linenum) |
✅ | Re-renders the line buffer (0-indexed) so embedded \n is interpreted; Mudlet Web renders with white-space: pre-wrap |
Mudlet Web-specific extras: color_table.
All Discord Rich Presence functions require the Discord SDK and have no real implementation in a browser MUD client. They should be bound as warning-emitting no-op stubs (getters return nil, setters/resets are no-ops) so packages that touch Discord on load don't blow up.
| Function | Status |
|---|---|
getDiscordDetail / setDiscordDetail |
❌ stub |
getDiscordLargeIcon / setDiscordLargeIcon |
❌ stub |
getDiscordLargeIconText / setDiscordLargeIconText |
❌ stub |
getDiscordSmallIcon / setDiscordSmallIcon |
❌ stub |
getDiscordSmallIconText / setDiscordSmallIconText |
❌ stub |
getDiscordParty / setDiscordParty |
❌ stub |
getDiscordState / setDiscordState |
❌ stub |
getDiscordTimeStamps / setDiscordElapsedStartTime / setDiscordRemainingEndTime |
❌ stub |
resetDiscordData |
❌ stub |
setDiscordApplicationID / setDiscordGame / setDiscordGameUrl |
❌ stub |
usingMudletsDiscordID |
❌ stub |
Reconciled against the authoritative Mudlet Event Engine list. Arg lists exclude the implicit leading event-name argument.
Lifecycle / connection
| Event | Status | Notes |
|---|---|---|
sysLoadEvent |
✅ | After the initial script load, with true; with false after resetProfile() |
sysExitEvent |
✅ | Fired once at ScriptingEngine.destroy() (connection switch/unmount), on window pagehide (not beforeunload, which a player can still cancel at the "Leave site?" prompt; re-armed if the page returns from the back/forward cache), or by closeMudlet()/closeProfile() — whichever comes first. closeMudlet() raises it before disconnecting, so a handler can still send (as TMainConsole::closeEvent) |
sysConnectionEvent |
✅ | On connect; Mudlet Web also fires native connect |
sysDisconnectionEvent |
✅ | On disconnect |
sysProfileFocusChangeEvent |
✅ | On document.visibilitychange — arg: isFocused |
Input / send
| Event | Status | Notes |
|---|---|---|
sysDataSendRequest |
✅ | Before each send; handler may call denyCurrentSend() — arg: text |
Packages / modules
| Event | Status | Notes |
|---|---|---|
sysInstall / sysUninstall |
✅ | After/before any package/module install or uninstall — arg: name. Each install raises it plus exactly one of the detailed events below, as Host::installPackage does |
sysInstallPackage / sysUninstallPackage |
✅ | Packages only — args: name, fileName / name |
sysInstallModule / sysUninstallModule |
✅ | Modules installed from the UI — args: name, fileName / name |
sysLuaInstallModule / sysLuaUninstallModule |
✅ | Fired by the Lua installModule/uninstallModule paths, instead of sysInstallPackage/sysInstallModule — args: name, fileName / name |
sysSyncInstallModule / sysSyncUninstallModule |
✅ | Sync-flagged modules; single-profile, no sibling propagation |
HTTP / download
| Event | Status | Notes |
|---|---|---|
sysGetHttpDone / sysGetHttpError |
✅ | getHTTP — done: url, body · error: error, url |
sysPostHttpDone / sysPostHttpError |
✅ | postHTTP |
sysPutHttpDone / sysPutHttpError |
✅ | putHTTP |
sysDeleteHttpDone / sysDeleteHttpError |
✅ | deleteHTTP |
sysCustomHttpDone / sysCustomHttpError |
✅ | customHTTP with a non-standard verb — extra arg: HTTP method. A GET/PUT/POST/DELETE raises that verb's own pair and a HEAD raises nothing, as Qt remaps them (remapCustom) |
sysDownloadDone / sysDownloadError / sysDownloadFileProgress |
✅ | downloadFile |
sysUnzipDone / sysUnzipError |
✅ | unzipAsync |
Speedwalk (pure Lua — bundled Other.lua)
| Event | Status |
|---|---|
sysSpeedwalkStarted / sysSpeedwalkPaused / sysSpeedwalkResumed / sysSpeedwalkStopped / sysSpeedwalkFinished |
✅ |
Mapper
| Event | Status | Notes |
|---|---|---|
mapOpenEvent |
✅ | Mapper opened |
mapModeChangeEvent |
✅ | View↔edit transitions (setMapMode/getMapMode) — arg: "viewing"/"editing" |
sysManualLocationSetEvent |
✅ | MapPanel's right-click "Set player location" — arg: roomID |
sysMapAreaChanged |
✅ | Whenever the displayed area changes — args: newAreaID, prevAreaID (-1 on initial transition) |
sysMapDownloadEvent |
✅ | GMCP Client.Map records the game's MMP map URL; the map panel then offers "Download map from game" (hamburger menu + empty-state overlay). Fires after the downloaded map parses and loads. Both binary .dat and IRE-style .xml maps are supported |
sysMapWindowMousePressEvent |
✅ | args: button (1=left, 2=right, 3=middle), x, y |
Windows / UI elements
| Event | Status | Notes |
|---|---|---|
sysWindowResizeEvent |
✅ | Main output resize — args: width, height |
sysUserWindowResizeEvent |
✅ | User-window / miniconsole resize — args: width, height, name |
sysConsoleSizeChanged |
✅ | Char-grid change. Cols come from the wrap setting (falling back to floor(width / fontSize*0.6)); rows from floor(height / lineHeight). Also force-fires on setWindowWrap, and before sysWindowResizeEvent on a resize or border change — args: name, columns, rows, timestamp gutter (13 while main shows timestamps, else 0) |
sysWindowOverflowEvent |
✅ | Non-scrolling console (scrollState.scrollingEnabled === false) when scrollHeight > clientHeight; overflowLines = ceil(overflowPx / lineHeight) — args: name, overflowLines |
sysBufferShrinkEvent |
✅ | Whenever scrollback cap drops one or more lines (one event per evict batch) — args: name, linesRemoved |
sysWindowMousePressEvent / sysWindowMouseReleaseEvent |
✅ | Mouse press/release. Button is Mudlet-numbered (1=left, 2=right, 3=middle, 4=back, 5=forward, 0=other); x/y are pixels relative to the window — args: button, x, y, name |
sysLabelDeleted |
✅ | On successful deleteLabel — arg: name |
sysMiniConsoleDeleted |
✅ | On successful deleteMiniConsole — arg: name |
sysCommandLineDeleted |
✅ | On successful deleteCommandLine — arg: name |
sysScrollBoxDeleted |
✅ | On successful deleteScrollBox — arg: name |
sysTextEditDeleted |
✅ | On successful deleteTextEdit — arg: name |
sysFontChangeEvent |
✅ | A console's font changed: a new miniconsole or user window, a setFont/setFontSize/setMiniConsoleFontSize that changes it, or the main font from any source (raised before sysSettingChanged("main window font")). Labels don't raise it — args: name, fontFamily, fontSize |
Protocol / telnet
| Event | Status | Notes |
|---|---|---|
sysProtocolEnabled |
✅ | Fired "GMCP" on GMCP negotiation; bundled GMCP.lua re-subscribes its modules here. Also fires "MSDP", "MSSP", "MXP", "MNES" |
sysProtocolDisabled |
✅ | On disconnect for each protocol that was active (GMCP/MSDP/MSSP/MXP/MNES) |
sysTelnetEvent |
✅ | For every IAC command except GA/EOR, natively handled options included (as cTelnet::processTelnetCommand). type is the command byte (251=WILL, 252=WONT, 253=DO, 254=DONT, 250=SB); message is the SB body for a subnegotiation, else the raw command — args: type, option, message |
Drag & drop
| Event | Status | Notes |
|---|---|---|
sysDropEvent |
✅ | When a real File is dropped on a window. path falls back to the file's name since browsers only expose a real path on Electron-flavoured drops — args: filepath, suffix, x, y, name |
sysDropUrlEvent |
✅ | When a textual URL is dropped — args: url, schema, x, y, name |
Media / misc
| Event | Status | Notes |
|---|---|---|
sysAppStyleSheetChange |
✅ | setAppStyleSheet — args: css, tag |
sysPathChanged |
✅ | VFS mutation of a watched path — arg: path |
sysMediaFinished |
✅ | Sound/music/video source ended or stopped — args: name, path |
sysSettingChanged |
✅ | Desktop's list only: compactInputLine, mapperPanelVisible, enableClosedCaption, advertiseScreenReader, announceIncomingText, muteMediaAPI, muteMediaGame — args: getConfig key, new boolean; raised only when the value changed, from a script or the Settings UI alike. Plus ("main window font", family, size) when the main console's font changes. Every other setting changes silently, as on desktop |
sysSoundFinished |
➖ | Not raised: desktop Mudlet raises only sysMediaFinished (the old name has no emitter in TMedia). |
sysIrcMessage |
❌ | No IRC client in Mudlet Web; nothing fires it (no stub needed — events don't break callers when never raised) |
Not Mudlet events — do not implement under these names:
sysConnect/sysDisconnect/sysGmcpMessage(Mudlet usessysConnectionEvent/sysDisconnectionEventand thegmcp.<path>event chain),sysUserWindowCreated/sysUserWindowClosed,sysMapperLocationChanged.Mudlet Web-specific events (no Mudlet equivalent):
output(per output line),gmcp.<path>chain (✅, the real GMCP mechanism — args: eventName, fullKey),sysSaveProfileError(✅),sysSyncOnModule(✅, module-sync internals).
Pure Lua on top of the overlay primitive API. No additional JS required.
| Class | Status | Notes |
|---|---|---|
Geyser.Container |
✅ | Pure layout, no missing deps |
Geyser.Label |
✅ | Bundled; getLabelFormat resolves now that getLabelStyleSheet is implemented |
Geyser.MiniConsole |
✅ | Bundled |
Geyser.Gauge |
✅ | Bundled; wraps GUIUtils createGauge/setGauge |
Geyser.HBox / Geyser.VBox |
✅ | Bundled |
Geyser.CommandLine |
✅ | Bundled; the underlying createCommandLine overlay primitive is now wired |
Geyser.UserWindow |
✅ | Bundled; uses openUserWindow |
Geyser.ScrollBox |
✅ | Bundled; the underlying createScrollBox/deleteScrollBox overlay primitives are now wired (see UI Functions) |
These features have no real implementation in Mudlet Web, but to keep imported Mudlet scripts/packages portable they are still bound as warning-emitting no-op stubs (see the legend). Stubs log once per call site and return a sensible default — see the per-section notes above for the exact return value of each stub.
| Feature | Reason |
|---|---|
Discord Rich Presence (getDiscord* / setDiscord*) |
Requires Discord SDK |
IRC client (openIRC, sendIrc, *IrcChannels, *IrcNick, *IrcServer, restartIrc, getIrcConnectedHost) |
Separate external service |
MMCP / MudMaster Chat Protocol (mmcp.*) |
Peer-to-peer chat over direct TCP — no raw/listening sockets or P2P in a browser. mudlet.supports.mmcp reports false |
| In-tab multi-profile switching (loading multiple profiles into one tab) | One profile per tab. (getProfiles DOES enumerate every configured profile with cross-tab loaded/connected status, loadProfile opens another profile in a new tab, and raiseGlobalEvent works across tabs — see above) |
spawn(...) |
No subprocess in the browser |
Spell-check API (spellCheckWord, spellSuggestWord, addWordToDictionary, removeWordFromDictionary, getDictionaryWordList) |
No Hunspell in browser |