Changes since last PTB (Mudlet-4.22.0-ptb-2026-08-16-44d23ebf)
Improved:
- undoing the game's own wrapping is marked experimental, and no longer advertised #9873
- text throughput by not rewrapping lines that already fit #9928
Fixed:
- profile saves no longer leak Lua stack slots #9895
- A damaged trigger no longer crashes Mudlet while the profile loads #9884
- the starter UI's chat window no longer makes captured lines expensive #9962
- Undoing the game's word wrapping no longer merges separate sentences #9872
- Crash when an update download is requested twice in quick succession #9948
- Rare crash if a profile closes while an MXP tag is stalled #9946
- Crash on macOS 12 shortly after startup #9945
- keep the selected room when right-clicking elsewhere on the map #9932
- a game that cannot be reached now says so, instead of nothing at all #9914
- three first-screen defects in the connection dialog #9894
- triggers no longer act on the wrong line after another trigger feeds text #9898
- hidden control codes no longer leave stray characters in the game's text #9901
- New variables no longer occasionally vanish from the variable editor and profile saves #9944
- Windows CI builds again after an msys2 update broke Lua module installs #9963
- games offering several character sets no longer knock a profile out of UTF-8 #9861
Infrastructure:
- spec the multiline trigger behaviour that does not need a functional test #9943
- Auto-setup build environment for Claude Code web sessions #9931
- drag the starter UI's map and chat anywhere too #9863
- drag the starter UI's health bars anywhere you like #9840
- PhoenixMUD #9874
- adjustable containers can be dragged out of their parent #9850
- widget state getters for titles, stylesheets, tooltips and scroll bars #9645
- getWindowGeometry(), windowVisible() and getLabelText() functions #9528
- help links for packages, wired to the Module Help button #9453
- full-window background image/gradient support #9394
- starter interface with health bars, map and chat for new players #9454
- Federation 2 Community Edition #9500
- remainingNamedTimer() to check time left on a named timer #9411
- keyboard shortcuts to switch between game tabs #9460
- option to undo the game's own line wrapping #9455
- warn when two keyboard shortcuts clash #9449
- Show tutorial invitation on first launch #9169
- sign in to supported games using your browser (e.g. Google, Discord, or the game's own account) #9378
- feature callouts #9390
- UI tour to complement the Mudlet tutorial #9385
- undoing the game's own wrapping is marked experimental, and no longer advertised #9873
- text throughput by not rewrapping lines that already fit #9928
- text and trigger throughput, and a CSI sequence that read freed memory #9916
- moving or resizing a Geyser layout no longer lays it out several times over #9919
- the ISO 8859-1 decoder no longer allocates a string for every character #9856
- base UI captures your gossips in its chat window #9838
- "Copy as image" now works without selecting text first #9738
- the starter UI's chat capture is now a trigger tree players can read and copy #9736
- OSC 8 hyperlink handling, and a setting to turn it off #9731
- credit seven more contributors in the About Mudlet dialog #9675
- make filler widget size in toolbars user settable #9332
- ship every default package as an mpackage #9626
- make the New profile button show what it did #9607
- do not copy leading spaces in multi-line select #9560
- split the games list into My games and All games tabs #9452
- honor XDG_CONFIG_HOME for Mudlet's config directory #9552
- show a simpler trigger editor by default #9450
- embed leak suppressions file in Mudlet binary #9465
- scripts can now match map menus with their map events #9443
- make keyboard shortcut settings work with screen readers #9322
- faster colour tag processing in cecho, decho and hecho #9426
- profile saves no longer leak Lua stack slots #9895
- A damaged trigger no longer crashes Mudlet while the profile loads #9884
- the starter UI's chat window no longer makes captured lines expensive #9962
- Undoing the game's word wrapping no longer merges separate sentences #9872
- Crash when an update download is requested twice in quick succession #9948
- Rare crash if a profile closes while an MXP tag is stalled #9946
- Crash on macOS 12 shortly after startup #9945
- keep the selected room when right-clicking elsewhere on the map #9932
- a game that cannot be reached now says so, instead of nothing at all #9914
- three first-screen defects in the connection dialog #9894
- triggers no longer act on the wrong line after another trigger feeds text #9898
- hidden control codes no longer leave stray characters in the game's text #9901
- New variables no longer occasionally vanish from the variable editor and profile saves #9944
- Windows CI builds again after an msys2 update broke Lua module installs #9963
- games offering several character sets no longer knock a profile out of UTF-8 #9861
- Mudlet no longer crashes when a multiline trigger's script feeds text back #9897
- Quitting a testing build no longer ends in a LeakSanitizer error #9930
- Music stopped with a fade-away right after it starts no longer plays on #9907
- a script connecting while the profile is still connecting no longer leaves it connected to nothing #9896
- replaceAll() replaces the wrong characters when a line has accents #9906
- the Carrion Fields loader no longer fails silently or re-downloads on every reconnect #9921
- the Variables editor no longer creates a junk copy of a variable it cannot name exactly #9908
- a profile save no longer blanks, drops or inflates saved variables #9892
- update notes no longer skip releases your platform never got #9891
- a killed alias, key binding or timer can come back to life and fire again #9888
- Geyser elements asked to start hidden no longer appear on screen #9883
- copying text no longer grabs an extra character or re-wraps the line per character #9855
- sharing an area from the generic mapper now carries its room hashes #9364
- a trigger with a blank pattern before a real one silently never matches #9851
- building a map from a script no longer slows to a crawl as it grows #9763
- the last line of a paragraph no longer goes missing when undoing game wrapping #9859
- reconnecting a profile to a different game no longer carries the last game's protocols over #9773
- pasted text goes in at the cursor, not at the start of the line #9871
- saved variables holding functions come back empty again instead of half-filled #9860
- game text no longer breaks into one character per line with GA forced off #9858
- Mudlet no longer copies the whole screen an extra time on every repaint #9854
- exact-match triggers no longer copy every line they check #9853
- trigger matching no longer allocates memory for every pattern on every line #9852
- a queued quit no longer runs from inside a profile save #9813
- Mudlet no longer crashes when adding an event handler after switching scripts #9839
- undoing the game's line wrapping no longer merges list entries #9836
- delete old maps/modules doing nothing when there is no backup folder #9849
- profile saves silently dropping saved variables that a second global references #9762
- preloading a sound no longer plays it, and IRC settings stop losing your password #9817
- three Lua API bugs the core specs found - command history, phantom profiles, package names #9816
- seven Lua library and mapper bugs found while speccing them #9815
- db:create crashing on a single column name as _index #9799
- gauges no longer make every line of game text expensive #9766
- a stray escape code no longer silences your game for the rest of the session #9765
- brand-new installs get the new-player experience again #9745
- deleting a big Geyser layout no longer freezes Mudlet #9760
- notepad, IRC client and toolbars outliving their profile #9706
- underscores and descenders no longer clipped at certain font sizes #9743
- an empty XDG config directory hiding every profile #9712
- MXP frames overlapping UI packages that reserve screen space #9737
- don't offer updates that don't yet have a sha256 sum #9735
- new profiles process game text about twice as fast #9705
- a profile named "." or ".." deletes every profile when removed #9722
- game text no longer loses a character to a stray escape code #9723
- triggers being deleted when a script creates a lot of them at once #9724
- macOS builds sometimes failing their tests for no reason #9733
- unbreak development - cloneExportDocument was renamed to takeExportDocument #9732
- getWindowGeometry() and windowVisible() answer for the main window #9714
- three full-window background bugs - vanishing console, lost border colour, huge cover scaling #9711
- two ways a profile save could lose or resurrect your data #9704
- three crashes in the game selection screen #9702
- four security holes in the new browser sign-in #9713
- pausing a track and then playing a different one left you with silence #9710
- mapper scripts stopped being told the map had opened #9709
- trigger editor and deleted item subtrees leaking memory #9700
- stop the test-only waitForEvent() wedging Mudlet on macOS #9691
- profile close during a map operation, Discord presence truncation, and interrupting ttsSpeak() #9686
- user key bindings on Ctrl+1 to Ctrl+9 and Ctrl+Tab #9703
- a trigger that re-creates itself freezes Mudlet #9697
- stop treating long-time Mudlet users as brand new players #9695
- module save reaching into the profile after it is destroyed #9690
- package lifecycle - queued save outliving the profile, unremovable archives, module priority #9684
- six command line and console bugs #9683
- temporary trigger/alias/key/timer cleanup evicting same-named items #9682
- eight Geyser layout and container bugs #9680
- four Lua API corrections - colours, room name offsets, map window state and printTable #9678
- stop the telnet reader writing a NUL past the data it was given #9677
- Mudlet freezing when a window is set to wrap too narrowly #9623
- Discord getters no longer treat presence text as a format string #9660
- don't replay a sign-in token the game already rejected #9676
- stop map saves polluting userData with fallback keys #9469
- Sounds going silent when a file fails to load #9612
- stop table.union modifying a table it was given #9613
- table.n_collect drops values that match an index #9615
- profiles with parentheses in their name can be opened again #9610
- removing a game's profile no longer hides it from the All Games list #9608
- compare() no longer rejects tables that hold false #9614
- event handlers no longer wipe the caller's Lua stack #9621
- killTrigger and killAlias report failure when the item is already dead #9620
- crash when opening settings on macOS #9625
- crash when closing Mudlet while an update is available #9604
- script error messages are readable in dark mode again #9606
- help banners no longer show up in the wrong editor section #9609
- Windows auto-update failing on a release binary with no checksum #9617
- strand no heap objects across the remaining lua_error() raises #9605
- tempTimer negative time, compile() error slot, killTimer idempotency #9597
- ttsSetVoiceByName return garbage and stale ttsSpeechStarted text #9595
- media start events fired too early when replaying a just-stopped sound #9611
- Geyser.Window:echo() keeps the stored message
- gauge three value CSS shorthand and non-positive maximums
- lay out new2 boxes, and apply the layout end_update deferred
- Geyser.Button:setState refuses 'down' before writing it
- Adjustable.Container resetTitle error and delete leaks
- setTextFormat leak, createLabel return convention, windowType scrollbox
- looping sounds playing only once #9569
- garbled error messages in networking and media functions #9551
- permGroup reports failure properly and listRemove removes consecutive duplicates #9550
- cap the length of inserted text like echoed text #9497
- ttsGetQueue out-of-bounds read and ttsClearQueue message #9471
- prevent a rare crash while saving a profile that uses modules #9559
- crash when a button uninstalls its own package #9558
- stop crashes while saving and profiles losing their triggers #9557
- replacing a temp item's script no longer keeps running the old function callback #9496
- clearing the screen no longer loses the last line from the log #9486
- variables added while playing are no longer lost when saving #9492
- highlight triggers no longer stop later color triggers from matching #9425
- getNamedTriggers no longer drops names when trigger types are mixed #9549
- replacing the game GUI download dialog no longer cancels the new download #9519
- scaleMovie collateral disconnect, isAnsi*Color returns, scrolling messages #9474
- colon-form underline styles now match other terminals #9498
- memory leak when perm* functions receive invalid Lua code #9556
- caret-mode Shift+Arrow selection off-by-one and Ctrl+C copy #9393
- bold text no longer brings back a colour the game already reset #9515
- exporting a package no longer destroys an existing file when you decline to overwrite #9495
- restarting to apply an update no longer leaves a second Mudlet running #9487
- harden the TLS-upgrade question against reconnects and profile closure #9518
- log no longer duplicates a line when logging is restarted #9490
- mapper API error reporting for getMapEvents and addRoom #9473
- color triggers now work inside 'only pass matches' trigger groups #9442
- triggers created by other triggers react to the current line again #9458
- flag invalid aliases and block loops while typing them #9499
- tempAnsiColorTrigger can match background colour only #9494
- MXP text now shows correctly on non-English games #9491
- non-English game text no longer garbles after a stray "&" #9489
- script output no longer breaks the game's colour and control codes #9485
- character-at-a-time warning misfires on password prompts #9391
- apply Discord API permission gating consistently #9472
- searchRoom returns nil+message for a missing roomID #9467
- tempButton and tempButtonToolbar return the created ID #9470
- remove duplicate cechoLink/dechoLink/hechoLink in GeyserMiniConsole #9477
- profile reset no longer forgets which variables to save #9484
- notepad arrows now visible in dark mode #9488
- align getTimestamp, getModulePriority, getModulePath failure returns #9480
- guard sendMSDP against disconnected/unnegotiated state #9478
- stop a malicious game from writing files outside the media folder #9504
- show connection details when picking a game on a fresh install #9451
- game information sent via MXP is no longer all-lowercase #9447
- 200 byte leak when loading a map #9464
- cap telnet subnegotiation length to bound memory use #9440
- recover from corrupt MCCP compression and stop losing data #9441
- stray ESC sequence no longer corrupts later text #9436
- script editor's info banner no longer glares in dark mode #9414
- script editor's search box no longer jumps around #9444
- keep non-ASCII characters in MXP custom entities #9439
- enforce the per-echo character limit #9438
- colon-form italic styling no longer corrupts underline #9437
- cap over-long escape sequences to bound memory use #9435
- hidden terminal control sequences no longer show up as text #9433
- certificate warnings now open settings for the right profile #9432
- package description images now work for packages with spaces in their name #9431
- tempAnsiColorTrigger now runs function callbacks #9427
- pasting several copied items now works for every item type #9423
- Variables view handles decimal numbers and true/false keys properly #9422
- editor copy/paste works after scripts create triggers or aliases #9421
- debug console shows its black background again #9419
- certificate warnings are now readable in dark mode #9418
- Copy button no longer oversized on the connection screen #9417
- main display no longer leaves a blank strip when the top border shrinks #9413
- word wrap changed by scripts now shows correctly in Preferences #9410
- clicking into the main window no longer steals copy from the command line #9305
- no more crash when viewing Variables after resetting a profile #9430
- deleting lines in triggers no longer corrupts the log file #9429
- save new modules to the location you choose #9424
- alias patterns no longer break after editing them #9420
- trigger editor's extra-options arrow is now visible in dark mode #9412
- Windows crash reports now show the actual cause #9370
- line-initial underscores no longer clipped at certain font sizes (regression of #7866) #9288
- updater not running on close, showing empty changelog #9388
- prevent crash from a trigger that endlessly feeds itself #9368
- variable editor keeps new variables nested in their parent table #9285
- trigger multi-line attribute resets when adding a sibling trigger #9284
- crash when a package uninstalls itself from its own alias/key/tr… #9383
- don't load a map if trying to create a mapper & map is already loaded #9415
- apply map label edits immediately in label preview #9387
- shrink preferences dialog to fit on 1377x768 screen #9407
- keep saved profile passwords working on Windows after qtkeychain library update #9395
- clickable links could show the wrong colours and styling after heavy output #9406
- add missing dictionary name #9400
- resize command line box for appendCmdLine/printCmdLine/clearCmdLine #9301
- console text not redrawing after resizing the window #9369
- editor keeps its maximized/full-screen state when toggled back to #9303
- spec the multiline trigger behaviour that does not need a functional test #9943
- Auto-setup build environment for Claude Code web sessions #9931
- link the Linux CI builds with mold #9927
- Guide AI assistants to prefer Lua specs over C++ functional tests #9925
- test the guard that keeps a cleared shortcut from swallowing every key #9886
- MXP frame placement test no longer depends on the startup window size #9876
- stop the development ccache from going stale #9905
- Bump 3rdparty/sentry-native from - Bump 3rdparty/edbee-lib from - give the undo-server-wrap tests a known starting buffer #9866
- document the xdotool no-op drag-step trap in the video workflow #9870
- retry the LeakSanitizer tracer crash instead of failing the leak job #9812
- Update text for translation in Crowdin #9875
- stop the telnet test stub losing data sent before a client connects #9862
- Add CMake presets for every platform, and one home for AI assistant skills #9742
- fix the main window restore baseline the map dock invalidates #9828
- fix a test running against the real config dir, and guard the recipe #9811
- Add tests for the core and misc Lua functions that had none #9802
- Add functional tests for the profile lifecycle Lua API #9776
- busted specs for the last uncovered UI functions #9775
- busted specs for the last uncovered lua-lib functions #9774
- test coverage for the IRC configuration and media playback APIs #9772
- rename the six tests Windows refuses to launch without elevation #9753
- drop comments that restate the code beside them #9681
- fix two tests still opting in with an empty XDG config dir #9810
- keep QTest's output visible when ctest runs on Windows #9751
- a media player no longer announces its own destruction #9746
- fix Windows CI losing the per-user luarocks tree #9752
- keep the profile-removal file list from going stale #9739
- Bump lukka/get-cmake from 4.4.0 to 4.4.2 #9729
- tidy up how CI installs Lua #9734
- Update autocompletion data in Mudlet #9720
- Bump 3rdparty/sentry-native from - Bump 3rdparty/edbee-lib from - Bump github/codeql-action from 4.37.3 to 4.37.6 #9730
- reject a release tag that does not match APP_VERSION #9701
- move module documents into their write jobs instead of copying #9699
- leak checking now gates the functional tests #9694
- Update text for translation in Crowdin #9718
- trim the comments left behind by two merged QA fixes #9708
- trim the comments around the experienced-player gate #9707
- assign milestones with the built-in token, not a PAT #9696
- fix milestone assignment, unbreak the key sequence tests #9679
- take re-entrancy depth counts off with scope guards #9687
- MMCP effect specs against a scripted chat peer #9629
- busted specs for the Geyser widget classes with no spec home #9665
- package and module lifecycle specs with a committed fixture kit #9632
- Discord effect specs against a fake Discord IPC server #9631
- cover the untested lua-lib surface #9630
- check mpackage archives match their sources #9624
- cut PR CI matrix in half and fix build flakes #9618
- make windows safe to destroy while a field has the text cursor #9596
- stop suppressing QString leaks in LSan enforcement #9602
- ignore multiple build targets #9562
- real-firing timer and script family Lua API tests #9572
- deepen the Geyser specs after test-coverage review
- address review of the Geyser specs
- Geyser geometry and visibility tests
- keep the HTTP fixtures byte-exact, harden the Windows step
- run the HTTP fixture server on Windows CI
- fix Windows media path assert, spare the media cache
- download, HTTP, media and TTS effect tests
- move media and TTS specs into Media_spec.lua
- drop attempt cap in setFont round-trip test
- window and label geometry/visibility Lua API tests
- document the LSan tracer-crash CI flake #9592
- decouple the mapper engine from UI dialogs #9513
- decouple profile management (Host) from UI dialogs #9514
- bump github/codeql-action from 4 to 4.37.3 #9565
- bump 3rdparty/sentry-native from - Update text for translation in Crowdin #9563
- document un-suppressible CI leak flakes #9561
- networking, MMCP, media and Discord API contract tests #9535
- replace test mocks with real API assertions; config, stopwatch and utils tests #9534
- database and datetime Lua API tests #9531
- add waitForEvent test helper for async Lua API tests #9527
- mock TTS engine and local HTTP server for tests #9529
- extensive mapper Lua API tests #9530
- ignore speech-dispatcher's one-time startup leak in leak detection #9521
- release resources in test fixture destructors #9522
- resolve CodeQL warnings in the telnet code #9523
- guard functional-test lookups with QVERIFY #9524
- clear 5 CodeQL warning-level alerts in src/ #9525
- re-enable Sentry crash reporting on PTB builds #9553
- survive xorg.freedesktop.org certificate expiry in CI #9554
- clean vestigial widget includes from the Lua engine files #9526
- trigger, alias, key and named-timer Lua API tests #9532
- UI text output and readback Lua API tests #9533
- decouple profile export and media playback from UI widgets #9512
- add performance baseline benchmark for the text and trigger pipeline #9509
- add Discord IPC server stub for end-to-end tests #9475
- harden the Widgets audit script and refresh its baseline #9516
- slim down the AI assistant instructions #9511
- harden the build for the minimum supported Qt version #9510
- add Qt Widgets dependency audit for the core library #9508
- decouple telnet engine from UI dialogs #9507
- use std::chrono literals for time durations #9493
- remove unregistered setMiniConsoleFontSize implementation #9479
- add profile and map save/load round-trip tests #9468
- Bump 3rdparty/sentry-native from - Update sanitizers.cmake to latest upstream #9503
- Update text for translation in Crowdin #9502
- document how to record before & after demo videos #9482
- test suites no longer clash when run at the same time #9483
- stop known ASan leak noise failing the password migration test #9461
- memory leaks found during CI tests now fail the build #9481
- fix else-after-return in codebase #9096
- Bump actions/setup-node from 6 to 7 #9457
- Bump lukka/get-cmake from 4.3.4 to 4.4.0 #9456
- Update text for translation in Crowdin #9360
- PTBs shipping without all platforms' installers #9389
- add Mudlet lua files to those shown by Qt Creator #9409
- Bump 3rdparty/qtkeychain from - Bump 3rdparty/sentry-native from - merge release-4.22 back into development #9386
Full changelog since last release (Mudlet-4.22.0)
Added:
Improved:
Fixed:
Infrastructure:
a185ce8 to 724479b #9899
a3ae51b to 9a91551 #9900
a99d64e to a185ce8 #9727
a3ae51b to 62ca709 #9728
a182754 to a99d64e #9564
0f7802f to a182754 #9505
7718d0a to 875f77d #9399
fea16b8 to 0f7802f #9398