#### Brief overview of PR changes/additions
- Hardens how OSC 8 link payloads and link text are handled before they
are run or displayed; link commands are no longer built by
string-formatting remote text into Lua source.
- Adds a per-profile setting (General → Game protocols) to turn OSC 8
hyperlinks off, which also reports `0` for every `OSC_HYPERLINKS*`
NEW-ENVIRON variable and sends an INFO update if toggled mid-session.
- Fixes `selected=` callbacks on `send:` links, which never fired, and
keeps emoji and Persian/Arabic/Indic text intact in tooltips and menu
labels.
#### Motivation for adding to Mudlet
Inspired by
[conversation](https://discord.com/channels/279748146316312576/1416447642472284261/1535109010066251890)
on the MUD Discord and updates to terminal emulators.
OSC 8 sequences arrive from the game server — and often from another
player whose say/tell text the server relays — so they have to be
treated as untrusted input rather than as content the user chose to
load.
#### Other info (issues closed, discussion etc)
New unit tests: `LuaLiteralTest` (28 cases, including an exhaustive
sweep over the bracket alphabet, each evaluated in a real Lua 5.1 state)
and `UntrustedTextTest` (26 cases covering emoji sequences, non-Latin
shaping and the two sanitization policies). There is no automated
NEW-ENVIRON coverage anywhere in the repo, so that path was verified
manually against a live server instead.
**Test case:**
1. `say !osc8-docs` — every documented feature still works.
2. Send a link whose command ends in `]`, e.g. `send:say [OOC]` —
clicking sends the literal text (previously the click silently did
nothing).
3. Settings → General → Game protocols → uncheck "Enable OSC 8
hyperlinks from the server" — links stop rendering and the server is
told without a reconnect; re-check and they return.
4. Send a tooltip or menu label containing a multi-part emoji such as
👨🍳 — it renders normally, not as its component parts.
---------
Signed-off-by: Michael Conley <sousesider@gmail.com>
#### Brief overview of PR changes/additions
- line wrapping now always moves on: a width too narrow for a single
glyph used to break the line at the character the scan was already on,
looping forever on the main thread
- `setWindowWrap()` refuses widths below 1 (the range Preferences
offers) and answers `true` when it accepts one; Geyser's `setWrap()`
passes a refusal on and its autoWrap never derives 0 columns
- new `NarrowWindowWrapTest` (9 cases, 5 of them verified to hang
without the fix) plus busted coverage of the Lua and Geyser contracts
#### Motivation for adding to Mudlet
`setWindowWrap(0)` froze Mudlet completely as soon as the next line was
displayed, and so did an ordinary wrap width of 1 with East Asian text,
or an indent that used the width up.
#### Other info (issues closed, discussion etc)
Fixes#9622
**Test case:** `lua setWindowWrap(0)` then `lua getWindowWrap()` -
Mudlet used to freeze; now the first call reports "wrapAt must be
greater than zero" and the client keeps running.
Assisted-by: Claude:claude-opus-5
#### Brief overview of PR changes/additions
Replace code that tried to offset the first button in a toolbar by one
extra "space" every time it is saved by a settable variable that can add
zero to one less than the number of rows/columns that a toolbar has.
The maximum value for this and the control in the editor is
automatically set to be that one less every time the number of
rows/columns is changed, and the control disabled should the other one
be set to less that 2. It seems that it is possible to set the number of
rows/columns to zero and it looks as though, many years ago, it was
possible to use that zero value to disable the use of a `QGridLayout`
for the toolbar and instead allow the buttons to have a manually/custom
layout. The code with reproducing the manual layout seems to have
persisted but that to allow it to be modified looks to have disappeared.
That bares further investigation.
#### Motivation for adding to Mudlet
With the introduction of autosaving in the editor it is no longer
reasonable to change the layout every time something in a toolbar is
edited causing things to be saved - and relying on the end-user not ever
touching the arrangement in the editor window. Instead this knob can be
used to set it explicitly.
There is no provision in the Lua API for this "knob" in this PR because
the current Button/Menu/Toolbar implementation in the Lua subsystem is
seriously borked/incomplete. A major overhaul of that is intended for a
future PR!
#### Other info (issues closed, discussion etc)
Also:
* Make members of `TAction` class that have getters/setters `private:`.
* Make the text in the editor for toolbars: "Number of columns/rows
(depending on orientation):" actually change to match the setting for
the selected orientation.
* Move the `QLineEdit` to show the file name for the button icon into
the appropriate `QGroupBox` and add a `QLabel` for it - but keep them
hidden for now. This feature was disabled (without reason?) in
4e651d55fd which removed the button that
was used to select a file to provide an icon however the reproduction of
icons on buttons and menus was never removed. Previously the `QLineEdit`
was used in a read-only mode to display (until it was shrunk to a zero
size before this change) the file chosen.
I intend to re-enable this functionality in the future.
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
- Removes all raw Qt Widgets usage from the map engine: `TMap.{h,cpp}`
no longer owns a `QProgressDialog` (and drops a dead `QFileDialog`
include), and `XMLimport.{h,cpp}` no longer pulls in `QApplication` (the
clipboard read now uses `QGuiApplication::clipboard()`, which lives in
Qt Gui).
- The standalone map-progress dialog (shown when the mapper is not
visible, for map download / XML import and JSON export/import) is now
driven by Qt signals carrying pre-translated payloads; the frontend
(`TMainConsole`) owns the actual `QProgressDialog`, and a user cancel
returns to the engine through `TMap::slot_mapProgressDialogCancelled()`.
- Adds `MapProgressDialogSeamTest` covering the transfer-progress state
machine, a JSON export/import round trip driving the new signals, a
mid-import cancel delivered through the seam (the highest-risk change,
since the JSON reader used to poll `QProgressDialog::wasCanceled()`
synchronously), and an XML map import re-entered from inside a running
JSON operation.
#### Motivation for adding to Mudlet
Second concrete step of the re-scoped libmudlet plan (a Qt-Widgets-free
`mudlet_core` for headless use, testability and WASM). It copies the
seam template established in #9507: core emits a pre-translated payload
-> frontend owns the widget -> a callback slot returns the answer. The
Qt Widgets dependency audit (`cmake/audit-core-widgets.sh`) drops from
**151 to 147** offending files; `TMap.cpp`, `TMap.h`, `XMLimport.cpp`
and `XMLimport.h` are all now clean. The mapper-owned inline progress
path (`dlgMapper`/`T2DMap`, Mudlet's own widgets) is deliberately
untouched here - those move wholesale in the later target-split phase.
#### Other info (issues closed, discussion etc)
Part of #8681 / #9011. Existing translations are unaffected: every
progress string keeps its `TMap` `tr()` context, so current translations
carry straight over. Two new strings do arrive, both with `//:`
translator comments - the warnings shown when a map download or an XML
map import is refused because a JSON import/export is already running.
The JSON dialog stays non-modal and the download/import dialog keeps its
modeless styling, each applied by the frontend. The engine keeps its own
`mMapProgressStandalone` / `mMapProgressCancelRequested` /
`mMapProgressStandaloneMaximum` state to replace the widget read-backs
it used to do (`!= nullptr`, `wasCanceled()`, `maximum()`). If a map
operation ever reaches the engine before a console is wired (checked via
`isSignalConnected`), `TMap::warnIfMapProgressUnwired()` logs a loud
`qWarning` rather than silently running with no progress UI.
It also closes a latent null-dereference that exists on `development`
today. With the mapper visible a map download takes the inline-progress
path, leaving `mpProgressDialog` null - so a JSON export started
meanwhile sails past the `if (mpProgressDialog)` "already in progress"
check and creates a dialog of its own. When the download then finishes
inside the `processEvents()` pump the export is running,
`clearTransferProgress()` deletes and nulls *that* dialog, and the
export's next `incrementJsonProgressDialog()` dereferences null. The
engine now records whose dialog is up (`mMapProgressIsTransfer`) so a
transfer only ever closes its own, and `importMap()` refuses to start
while a JSON operation holds the progress - the mirror of the guard
`downloadMap()` has.
Two review-driven details worth flagging: the frontend only wires the
dialog's cancel to the engine when the operation is actually cancelable,
so a non-cancelable local XML import no longer turns a window-close into
a spurious "Map download was canceled" message; and the standalone
download/import dialog is now parented to the console (like the JSON one
always was, and like #9507's package-download dialog), so it centres on
and dies with the profile window. The three `#include <QApplication>`
additions to `Host.cpp` / `dlgTriggerEditor.cpp` /
`dlgConnectionProfiles.cpp` replace the transitive include they used to
get from `XMLimport.h`; all three are already Qt Widgets consumers, so
the audit count is unaffected.
Assisted-by: Claude:claude-opus-4-8
Assisted-by: Claude:claude-opus-5
**Test case:** With a mapper window open, use a game that supports map
download (or call `downloadMap()`) and confirm the progress dialog
shows, updates, and its Abort cancels the download. Then with the mapper
window closed, run `exportJsonMap()` and `importJsonMap()` on a large
map and confirm the non-modal JSON progress dialog appears, updates its
Areas/Rooms/Labels counts, and that clicking Abort during an import
stops it with an "aborted by user" result. Load a local XML map
(Settings -> Map -> load) and confirm closing its progress window does
not print a "Map download was canceled" line. Everything should behave
exactly as on `development`.
#### Demo (before & after)
https://github.com/user-attachments/assets/f1c62580-2d03-4e5b-a6ee-f6e2b78d113d
https://github.com/user-attachments/assets/49e8a176-899c-43fd-a931-6e04ab5b5efb
#### Brief overview of PR changes/additions
New opt-in profile option that rejoins lines the game hard-wrapped
itself, so triggers see the whole logical line and Mudlet's own wrapping
handles display. Enable in Settings → Main display, or
`setConfig("undoServerWrap", true)`. Prompts, blank lines, MXP `<br>`
and script-fed text never join, and prose/indentation gates keep ASCII
art, menus and tables intact. A one-time callout points the option out,
and games that look like they wrap raise a one-time hint with a
click-to-enable link.
#### Motivation for adding to Mudlet
On games that can't disable server-side wrapping, triggers need fragile
multiline patterns. This fixes it client-side - as far as we know, a
first among MUD clients.
#### Other info (issues closed, discussion etc)
Tests added.
---------
Signed-off-by: Vadim Peretokin <vadim.peretokin@mudlet.org>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remove unnecessary else/else-if after return, break, continue, and throw
statements - and in places where fixing them is more trouble than its
worth, added NOLINT.
#### Motivation for adding to Mudlet
https://clang.llvm.org/extra/clang-tidy/checks/readability/else-after-return.html,
so it doesn't pop up in PR reviews.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
#### Brief overview of PR changes/additions
A rename to more accurately reflect that the method works on ALL
toolBars/easyButtonBars and not just a single one.
#### Motivation for adding to Mudlet
To improve nomenclature.
#### Other info (issues closed, discussion etc)
There should be no functional changes with this item.
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
### Refactor: replace raw pointer ownership with smart pointers across
core subsystems
#### Brief overview of PR changes/additions
Replaces raw pointer ownership patterns with `std::unique_ptr` and
`std::map`
across several core subsystems:
- **Host**: `mStopWatchMap` (`QMap<int, stopWatch*>` → `std::map<int,
unique_ptr<stopWatch>>`), `profileShortcuts` (`QMap<QString,
QKeySequence*>` → `std::map<QString, unique_ptr<QKeySequence>>`).
Removes `qDeleteAll` in destructor and `delete mMMCPServer`.
- **TMap**: `mpRoomDB` raw pointer → `unique_ptr`
- **VarUnit**: `base` raw pointer → `unique_ptr`
- **TTrigger**: condition map storage converted to `unique_ptr`,
destructor simplified
- **discord**: handler and presence maps converted from raw pointer
`QMap` to `unique_ptr` + `std::map`
- **Updater**: `mFeed` and `mUpdateDialog` converted to `unique_ptr`
#### Motivation for adding to Mudlet
These patterns were identified as sources of memory leaks and potential
use-after-free bugs. Using smart pointers makes ownership explicit,
eliminates manual cleanup code, and ensures correct destruction even on
early-exit paths.
#### Other info (issues closed, discussion etc)
sorry this one is still pretty big, but most of the changes are the same
for each thing so reviewing them together probably makes sense. sadly
there isn't much to see here other than no slow uptick of heap size :-[
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
#### Brief overview of PR changes/additions
Auto-switch the code editor theme when toggling between light and dark
mode - finds counterpart themes by name (e.g. "Solarized light" <->
"Solarized dark"), falls back to stored preference, then defaults
(Monokai/Mudlet).
#### Motivation for adding to Mudlet
Follow-up to #8897 - the editor theme is a separate option that should
follow the app appearance.
#### Other info (issues closed, discussion etc)
https://github.com/Mudlet/Mudlet/pull/8897#issuecomment-4188760804
**Test case:** Open Preferences, switch Appearance to Dark - editor
theme should auto-switch. Pick a paired theme like "Seafoam Pastel
Dark", switch to Light - should find "Seafoam Pastel Light". Toggle back
- should restore dark variant.
## Summary
Players had no clear way to control what Discord shows about their
Mudlet activity. The old checkbox in the connection pane only gated
server GMCP data but didn't prevent Discord from showing "Playing
Mudlet", and the privacy controls were confusing. This PR replaces all
of that with three straightforward modes via radio buttons in Profile
Preferences > Chat:
- **Show full game details (if supported)** - full game integration with
server-provided presence (default)
- **Show Mudlet only** - only shows "playing Mudlet", game server is not
told about Discord
- **Disabled** - Discord shows nothing about Mudlet
Players pick the mode that matches their comfort level, and the existing
privacy checkboxes (hide detail, hide state, etc.) remain available in
Game details mode for finer control.
### What changed
- **Three-mode radio buttons** in Profile Preferences > Chat with a
two-column layout (modes on the left, privacy controls on the right),
replacing the old connection-pane checkbox
- **Server-origin tracking** so privacy checkboxes only gate data sent
by the game server - Lua API calls always pass through (only Disabled
mode blocks Lua entirely)
- **Mid-session mode switching** via dynamic GMCP negotiation
(Core.Supports.Add/Remove + External.Discord.Hello/Get)
- **Deferred RPC init** - Discord RPC now starts when a profile loads,
not on app launch
- **Username restriction improvements** - takes effect immediately,
case-insensitive (Discord usernames are lowercase-only since 2023),
shuts down RPC when mismatched
- **Shows logged-in Discord user** in preferences next to the
restriction field, with a tooltip explaining the desktop app requirement
when not connected
- **Presence fix** - empty string fields now send nullptr so Discord
hides them instead of showing blanks
- **Memory leak fix** - presence allocations are now freed in the
destructor regardless of RPC state
### Cleanup
- Removed obsolete discriminator field
(`mRequiredDiscordUserDiscriminator`) - Discord removed discriminators
in 2023
- Removed dead code (`getDiscordUserDetails()`, never called)
- Restored `Discord_ClearPresence` function pointer for potential future
use
- Use proper `Host::DiscordOptionFlags` types instead of raw `int`
(thanks @SlySven)
### Known quirks
- The "Hide timer" checkbox correctly omits timestamps from presence
data, but Discord's client starts its own activity timer for any
presence without a timestamp - this is Discord client behavior outside
our control.
- The "Hide large icon" setting clears the image key, but some Discord
clients fall back to the application's default icon instead of hiding it
entirely.
### Test plan
- [ ] Open Profile Preferences > Chat tab
- [ ] Switch between the three radio button modes and verify Discord
presence updates accordingly
- [ ] In Game details mode, toggle privacy checkboxes and verify fields
are hidden/shown
- [ ] Use Lua API (e.g. `setDiscordDetail("test")`) in Mudlet only mode
- should work. Try in Disabled mode - should fail with error
- [ ] Set a username restriction and verify presence clears immediately
if mismatched
- [ ] Run unit tests: `cd build && ./test/DiscordTest`
- [ ] Run functional tests: `cd build && ctest -R TDiscordModeTest -V`
Closes#6967. Supersedes #7438.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: SlySven <slysven@virginmedia.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Importing a zip that isn't a valid Mudlet package but contains XML files
with a <map> root element would crash (null pointer dereference on
mpMapper) and destroy the user's existing map data.
#### Motivation for adding to Mudlet
Crash fix.
#### Other info (issues closed, discussion etc)
This is a rare intersection of my main job and Mudlet, as I tried to
import a work-related package in Mudlet to see what would happen.
#### Brief overview of PR changes/additions
Closes#8857
This is a larger PR than I actually wanted it to be, but I could not
make it all work, independently. Either it was one large PR, or several
with dependencies to each other.
I think this makes more sense as a single large one, as it's all related
to the same settings window for the mapper.
This is the window with all the changes:
<img width="1116" height="1025" alt="Screenshot 2026-02-20 at 20 15 53"
src="https://github.com/user-attachments/assets/b5a7aa04-5db2-434c-b1a2-9763a96e00ff"
/>
- Separate border size from exit line size with independent spinner
controls, all rescaled to 1-11 range matching room size. The UI spinners
use a simple reciprocal mapping (`mLineSize = 50 / spinner`) to convert
the engine's inverse size representation into a "higher = thicker" scale
<img width="613" height="74" alt="Screenshot 2026-02-20 at 20 08 21"
src="https://github.com/user-attachments/assets/ffc6f141-38d2-457d-8e1e-35e042ad6be2"
/>
<img width="300" height="418" alt="Screenshot 2026-02-20 at 20 09 51"
src="https://github.com/user-attachments/assets/a55b4aa4-7b83-48e7-82c4-de3cbcab490a"
/>
<img width="300" height="412" alt="Screenshot 2026-02-20 at 20 10 05"
src="https://github.com/user-attachments/assets/8bdaccab-3e38-419f-ba4e-4c974856d66d"
/>
- Fix player room settings (style, colors) being lost when the mapper is
opened after changing them, by syncing Host and TMap copies
- Fix color swatch buttons showing stale icon alongside new color
- Player room marker radius now accounts for room size, border width,
and diagonal so 100% fully covers the room
<img width="956" height="149" alt="Screenshot 2026-02-20 at 20 11 52"
src="https://github.com/user-attachments/assets/0077c528-343a-46e1-afd5-4a3e3c31489b"
/>
- Add live-update connections for room borders, anti-alias, upper/lower
levels, and symbol scaling factor
- Extract gradient stop generation into a shared
`T2DMap::buildPlayerRoomGradientStops()` static method used by both the
map renderer and the preferences dialog
- Cache invalidation for room size changes uses per-instance member
variables instead of static locals, so multiple map views work correctly
- Fix grid line width spinner having no visible effect - grid pen now
scales with room dimensions like exits and borders, and includes the
`setCosmetic()` call that all other map pens use
#### Motivation for adding to Mudlet
The exit size control also affected border width with no way to adjust
them independently. The player room marker settings also had several
persistence bugs where values would be lost when opening the mapper.
#### Other info (issues closed, discussion etc)
None
**Test case:**
1. Open Profile Preferences > Mapper tab > Player room marker section
2. Change room size, exit size, and border size spinners independently -
verify each affects only its respective element on the map
3. Change marker colors, switch to main Mudlet window, open the mapper,
switch back to preferences and touch other controls - verify colors are
not reset
4. If using multiple map views, verify each view's symbol/label caches
invalidate independently when resized
5. Change the grid width spinner - verify grid line thickness changes
visibly on the map at any zoom level
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
#### Brief overview of PR changes/additions
Adds a smooth pulsing animation for blinking text (SGR codes 5 and 6)
controlled by a new "Enable blinking text" checkbox in Settings >
Accessibility. When disabled, blinking text is shown in italics instead.
The pulse effect is also applied to HTML log exports using matching CSS
animations.
#### Motivation for adding to Mudlet
Blinking text is a standard terminal feature that MUD servers can send.
This adds an accessible, WCAG-compliant way to display it — the smooth
pulse avoids harsh on/off flashing while the checkbox gives users full
control to disable it.
#### Other info (issues closed, discussion etc)
- Lua API: `setConfig("enableBlinkText", true/false)` and
`getConfig("enableBlinkText")`
- WCAG 2.3.1 compliant: slow blink at 0.5 Hz, fast blink at 1 Hz (both
well under 3 flashes/second limit), with opacity floor of 0.4
---
https://github.com/user-attachments/assets/292aa7ef-0239-44dd-9d9c-8e5079a7e6e4
#### Brief overview of PR changes/additions
- Fix map info contributors not persisting correctly across profile
saves - "Short" was always re-added on load regardless of user settings
- Move the default `{"Short"}` from an in-class initializer on
`mMapInfoContributors` to explicit initialization for new profiles only
in `loadProfile()`
- Add `getMapInfo()` Lua function and `getConfig("mapInfo")` key to
query map info contributor state
#### Motivation for adding to Mudlet
Map info checkbox settings should persist exactly as the user configured
them, and scripts should be able to query which map info contributors
are active.
#### Other info (issues closed, discussion etc)
**Root cause:** `Host.h` initialized `mMapInfoContributors` with
`{"Short"}`. On profile load, XML import *inserted* saved values into
this set without clearing it first, so the default "Short" survived and
merged with whatever was actually saved. The `mShowInfo="no"`
conditional clear only handled the empty case, not "Full only" or other
combinations.
**The fix** removes the in-class default (set starts empty), sets
`{"Short"}` explicitly for brand-new profiles in `loadProfile()`, and
removes the now-unnecessary conditional clear from `XMLimport`. Loaded
profiles get exactly what was saved.
**New Lua API - `getMapInfo()`:**
Returns a table of all registered map info contributors mapped to their
enabled/disabled state. Complements the existing
`enableMapInfo()`/`disableMapInfo()` pair.
```lua
-- returns e.g. { Short = true, Full = false }
local info = getMapInfo()
for name, enabled in pairs(info) do
print(name .. " is " .. (enabled and "enabled" or "disabled"))
end
```
**New `getConfig("mapInfo")` key:**
Returns the currently enabled contributors as an array of strings.
```lua
-- returns e.g. {"Short"} or {"Short", "Full"} or {}
local enabled = getConfig("mapInfo")
```
**Test case:**
1. Open an existing profile, set map info to "Full" only (uncheck
"Short"), close and reopen - verify only "Full" is checked
2. Set no checkboxes at all, close and reopen - verify none are checked
3. Create a brand-new profile - verify "Short" is enabled by default
4. Run `lua getMapInfo()` - verify it returns the correct table of
contributors with their states
5. Run `lua getConfig("mapInfo")` - verify it returns the enabled
contributors as an array
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
Co-authored-by: Vadim Peretokin <vperetokin@hey.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
TMxpFrameManager::resetAllFrames() was zeroing Host::mBorders on
reconnect, wiping user-configured borders. Split into mUserBorders and
mMxpBorders with additive effective borders. MXP callers use
setMxpBorders(), all others use setUserBorders(). setBorders() is now
private.
#### Motivation for adding to Mudlet
Fixes#8871
#### Other info (issues closed, discussion etc)
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
Brief overview of PR changes/additions
Add MMCP Client and Server classes, Lua scriptability, and options for
the MudMaster Chat Protocol
Motivation for adding to Mudlet
This is a feature largely requested by players of the Medievia MUD, it
allows peer to peer client communication integrated directly into the
main console.
Other info (issues closed, discussion etc)
Starting a new PR as the original PR 7155 was unintentionally
closed/deleted from my local disk
---------
Co-authored-by: Tim Johnson <29287358+atari2600tim@users.noreply.github.com>
Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
Co-authored-by: Vadim Peretokin <vperetokin@hey.com>
## Summary
Adds support for SGR codes 5 (slow blink) and 6 (rapid blink/flash) text
attributes.
## Implementation Details
### Blink Timer Architecture
- Global blink timer in `mudlet` singleton runs at 200ms interval (2.5
Hz, WCAG 2.3.1 compliant - under 3 Hz limit)
- TTextEdit widgets register/unregister as blink clients
- Timer only runs when at least one client needs it
- Uses 4-state counter per ISO/IEC 8613-6:1994 to create two speeds:
- **Slow blink (SGR 5)**: < 150 cycles/min (~1.25 Hz)
- **Fast blink (SGR 6)**: > 150 cycles/min (~2.5 Hz)
### Text Attributes
- New `TChar::AttributeFlags`: `Blink` and `FastBlink`
- SGR 5 sets `Blink`, SGR 6 sets `FastBlink`
- SGR 25 clears both flags
### Rendering
- `TTextEdit::drawBackground()` skips drawing background for hidden
blink text
- `TTextEdit::drawForeground()` skips drawing foreground for hidden
blink text
- When blinking is disabled, blink text renders as italics instead
### User Preference
- Per-profile `enableBlinkText` setting (disabled by default for
accessibility)
- Checkbox in Settings → Accessibility tab
- Lua API: `getConfig("enableBlinkText")` /
`setConfig("enableBlinkText", bool)`
- Saved/loaded in profile XML
### Lua API
- `getTextFormat()` reports blinking as `"none"`, `"slow"`, or `"fast"`
- `setTextFormat()` accepts optional blink parameter: `"none"`,
`"slow"`, or `"fast"`
## Testing
To test blinking text, connect to a game that sends SGR 5/6 codes, or
use:
```lua
echo("\27[5mSlow blink\27[0m \27[6mFast blink\27[0m\n")
```
## Checklist
- [x] Blink timer starts/stops based on client registration
- [x] Slow and fast blink speeds are visually distinct
- [x] Preference toggles blinking on/off per profile
- [x] Fallback to italics when blinking disabled
- [x] WCAG 2.3.1 compliant (2.5 Hz, under 3 Hz limit)
- [x] Default is disabled for accessibility considerations
- [x]
[`setTextFormat()`](https://wiki.mudlet.org/w/Area_51#setTextFormat.2C_PR_.238983)
supports blink mode parameter
---
https://github.com/user-attachments/assets/25f63605-7b90-40c3-963f-53889e41328d
---------
Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Reverts the change from PR #7917 to omit drawing the background color of
text when it matches the console's BG (the reversion is applied
exclusively to the Main console).
Expose custom opacity in the color picker of [Color view] >
[Background].
Text with a matching background in the main console will now use the
opacity as set in the color palette in settings (default opaque).
#### Motivation for adding to Mudlet
Keeps the functionality added in PR #7917 for good looking transparent
miniconsoles while reverting to the prior (4.19.1) Main console text
background behavior. Allow users users to choose if they'd prefer
transparent text background or something in between.
#### Other info (issues closed, discussion etc)
Resolves#8885
End effect of custom-opacity background use can look like this:
<img width="1159" height="859" alt="image"
src="https://github.com/user-attachments/assets/07bff055-e12a-4c0e-be3a-7c1df482e0da"
/>
#### Brief overview of PR changes/additions
Ran clang-format on all 134 CPP files in src/ using the project's
.clang-format config
#### Motivation for adding to Mudlet
Ensures consistent code formatting across the codebase.
#### Other info (issues closed, discussion etc)
None
**Test case:** Build the project and verify it compiles successfully.
#### Brief overview of PR changes/additions
Extracts color/border parsing from `XMLimport::readHost()` into helper
methods. Removes dead Qt < 6.6.0 code. Net -110 lines.
#### Motivation for adding to Mudlet
Improves maintainability by consolidating 47 color settings into a
single data-driven lookup.
#### Other info (issues closed, discussion etc)
**Test case:**
1. Create a profile with custom colors (Preferences → Colors)
2. Save and close Mudlet
3. Reopen and verify colors load correctly
4. Import an existing profile package and verify colors preserved
No functional changes.
Co-authored-by: Claude <noreply@anthropic.com>
#### Brief overview of PR changes/additions
Enables importing of special/custom exits (like "climb ladder", "enter
portal") when loading XML map files. Previously these non-standard
directions were silently ignored.
#### Motivation for adding to Mudlet
Maps with custom exits would lose those exits on import, requiring
manual re-creation. This completes the exit handling in the XML
importer.
#### Other info (issues closed, discussion etc)
Resolves a TODO in `XMLimport.cpp:546`.
**Test case:**
1. Create an XML map file with a room containing a non-standard exit
direction (e.g., `<exit direction="climb tree" target="42" door="0"/>`)
2. Import the map via File > Import
3. Verify the special exit appears in the room's exit list
Co-authored-by: Claude <noreply@anthropic.com>
#### Brief overview of PR changes/additions
Adds a new setting to control NAWS (window size) protocol negotiation.
Users can now disable NAWS for servers that have problems with automatic
window size updates.
#### Motivation for adding to Mudlet
[Discord](https://discord.com/channels/283581582550237184/283582439002210305/1448174522900414495)
conversation.
Some MUD servers have issues handling NAWS protocol messages, which can
cause connection problems or unwanted behavior. This setting gives users
control over whether Mudlet sends window size information to the server.
#### Other info (issues closed, discussion etc)
- New Lua API: `setConfig("enableNAWS", true/false)` and
`getConfig("enableNAWS")`
- NAWS is enabled by default (existing behavior preserved)
- Window size tracking continues even when disabled for smooth
re-enabling
- Improved debug message consistency across telnet protocols
<img width="492" height="223" alt="Screenshot 2025-12-26 at 1 28 47 PM"
src="https://github.com/user-attachments/assets/9606d2fd-3cd9-4962-a9b9-138581da1a1f"
/>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
- Add QBuffer::open() return value checks in
TArea::convertImageToBase64Data(), XMLimport::importFromClipboard(), and
TMapLabel::base64EncodePixmap()
- Add bounds checking to TLuaInterpreter::generateElapsedTimeTable() to
validate expected 6 elements in time string list
- Add null pointer check to Host::findConsole() for mpConsole
- Add array bounds validation to TLuaInterpreter::callEventHandler() to
handle mismatched argument and type list sizes
#### Motivation for adding to Mudlet
Better code quality.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Claude <noreply@anthropic.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Adds undo/redo for Mudlet editor - allows you to undo
adding/changing/deleting triggers, aliases and so on. This PR also adds
visual tests that can be run inside the `Mudlet self-test` profile, and
they are hooked up to run in the CI pipeline automatically. The deletion
confirmation dialog has been itself deleted since we can now undo.
#### Motivation for adding to Mudlet
Long-awaited and often requested feature. Fixes#707 and addresses
#8272.
#### Other info (issues closed, discussion etc)
This test also adds 300+ tests that can be run in the Mudlet self-test
profile (launch it from the CLI using `--profile "Mudlet self-test"`):
https://github.com/user-attachments/assets/f7f10fcc-0129-47f0-ad8c-d54816820d57
/claim #707
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remove unnecessary else blocks following return statements throughout
the codebase.
#### Motivation for adding to Mudlet
Better code readability
#### Other info (issues closed, discussion etc)
Other anti patterns are visible in this PR thanks to this, but let's
keep this PR focused on one pattern only.
---------
Co-authored-by: Claude <noreply@anthropic.com>
#### Brief overview of PR changes/additions
Add toggle for showing map grid
Added as well to setConfig, so it can be turned on and off via lua api
<img width="645" height="789" alt="image"
src="https://github.com/user-attachments/assets/094f08b9-a938-466e-85cf-47ff5a7344a6"
/>
While separating grid setting from exit settings I reorganized a bit map
preferences as well:
<img width="1048" height="508" alt="image"
src="https://github.com/user-attachments/assets/45f1fd57-ef75-4e46-9620-731e311281c2"
/>
Color setting for grid is now separated as well.
#### Motivation for adding to Mudlet
Especially when moving and creating rooms, it might be beneficial to see
how grid looks like
Also some... like me, might like more "technical" look of mapper.
#### Other info (issues closed, discussion etc)
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remove support for leak detection using MSVC - this functionality hasn't
been in use in a decade, and we're switching to to using
[LeakSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer)
instead.
#### Motivation for adding to Mudlet
Cleaner code.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Claude <noreply@anthropic.com>
#### Brief overview of PR changes/additions
Moved the CHARSET and NEW-ENVIRON protocol checkboxes from the Special
Options tab to the General tab's protocol menu, alongside other protocol
settings like GMCP, MSDP, and MXP. Protocol menu items are now sorted
alphabetically for easier navigation.
#### Motivation for adding to Mudlet
This change improves consistency in the UI by grouping all protocol
settings together in one location. Users can now find and manage all
telnet protocol options (CHARSET, NEW-ENVIRON, GMCP, MSDP, MSSP, MSP,
MXP, MTTS, MNES) in a single, organized dropdown menu on the General
tab.
The migration follows the same pattern established in PRs #7862 and
#7916, ensuring backward compatibility with existing profiles and Lua
scripts.
#### Other info (issues closed, discussion etc)
- Follows the migration pattern from PRs #7862 (MXP) and #7916
- Maintains full backward compatibility with existing profiles
(automatic XML migration)
- Lua API compatibility preserved for scripts using old config keys
- All protocols now appear alphabetically in the UI menu
<img width="518" height="234" alt="Screenshot 2025-10-18 at 8 05 37 AM"
src="https://github.com/user-attachments/assets/34ca0cac-f15b-4908-8471-a964dfd64c22"
/>
#### Brief overview of PR changes/additions
Enhances existing command preservation by preventing restoration when
users type during password entry, plus adds an optional setting to
disable password masking entirely for users who prefer it.
#### Motivation for adding to Mudlet
Builds upon existing command preservation (PR #7924) to handle edge
cases where the previous implementation could overwrite passwords if
users typed during echo suppression.
#### Other info (issues closed, discussion etc)
May close#8127
Key enhancements:
- Smart restoration logic prevents overwriting user input during
password mode
- Optional password masking disable setting for trusted environments
- Improved debug messaging for troubleshooting
Small refinements that make the existing preservation feature more
robust.
---
Disable password masking feature:
<img width="2174" height="846" alt="Screenshot 2025-09-13 at 10 11
21 AM"
src="https://github.com/user-attachments/assets/698e4394-a025-433b-87b3-97bfdea1576e"
/>
---
Disable password masking enable and disable
https://github.com/user-attachments/assets/2cc33216-665a-43b4-a957-97a1f0a28771
---
Preserving text sent before an auto-login (still works)
https://github.com/user-attachments/assets/2b7c0029-d3dc-43cf-ab61-69d982993deb
---
There is presumed to be one more case where the end-user has their own
Lua script for password entry outside the auto-login where this change
should preserve their pretyped but unsent text.
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Move all variable initializers to header classes
#### Motivation for adding to Mudlet
Cleaner code, and a better example for LLMs to follow when generating
code
#### Other info (issues closed, discussion etc)
No functional changed are expected
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
Co-authored-by: Kebap <kebap_spam@gmx.net>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Add UI option for setting buffer size:
[Screencast from 2025-09-15
08-10-18.webm](https://github.com/user-attachments/assets/e5c0ec57-d7e1-4014-95d5-f57403717489)
#### Motivation for adding to Mudlet
Requested by players in the [2025 player
survey](https://www.mudlet.org/2025/09/mudlet-2025-survey-responses/).
Closes https://github.com/Mudlet/Mudlet/issues/8145. The logic is
straightforward: options players ask for should be added, options
players aren't asking for should not be added.
#### Other info (issues closed, discussion etc)
The 'use maximum size possible' option, which sets the buffer to the
maximum amount of lines RAM can handle, is something that has been in
the code for a while. It isalso mirrored to the Lua script for
consistency to UI. That does present a problem however - if you have
more than 1 console set to maximum size, you will use more memory than
your system can handle, which some folks have done in the past and
complained about it. This could use solving - with a global maximum on
the entire Mudlet instance, perhaps? Ideas welcome.
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
This adds an experimental, new 3D mapper that uses shaders, more modern
openGL, and a far better code reorganization that makes it an easier
foundation to build upon.
The new 3D mapper is here side by side with the original and can be
toggled on for experimentation. There's a lot of work to be done, so I'd
rather merge it early instead of making a mega-PR.
#### Motivation for adding to Mudlet
So we have a new foundation to build upon and improve.
#### Other info (issues closed, discussion etc)
Old and new mapper can be toggled dynamically with:
```lua
-- this can be a keybinding
setConfig("experiment.3dmap.modernmapper", not getConfig("experiment.3dmap.modernmapper"))
```
Smooth movement is one experiment in the new mapper, and it can be
enabled with:
```lua
lua setConfig("experiment.rendering.smooth-camera", true)
```
As you notice an experiments system has been added so we can implement
things at once and experiment to choose the one that works best. This
system can be used in other places in Mudlet as well.
<details><summary>Details</summary>
<p>
## Experiments System
### Overview
Allows enabling/disabling experimental features via
`setConfig`/`getConfig` with validation against a predefined
whitelist.
### Usage
```lua
-- Enable experiment
setConfig("experiment.rendering.more-transparent", true)
-- Check if enabled
local enabled = getConfig("experiment.rendering.more-transparent") --
returns true/false
-- Get active experiment in group
local active = getConfig("experiment.rendering.active") -- returns
"more-transparent"
-- List all valid experiments
local experiments = getConfig("experiment.list") -- returns table of
valid keys
```
### Behavior
- Grouped experiments: Mutually exclusive (enabling one disables others in same group)
- Validation: Only predefined experiments allowed, invalid keys return errors
- Persistence: Experiment states saved/loaded with profiles
### Adding New Experiments
Edit Host::mValidExperiments in src/Host.cpp:
```cpp
const QSet<QString> Host::mValidExperiments = {
qsl("experiment.rendering.originalish"),
qsl("experiment.rendering.more-transparent"),
qsl("experiment.newfeature.option1"), // Add here
};
```
### Current Experiments
- experiment.rendering.originalish
- experiment.rendering.more-transparent
</p>
</details>
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remember 2D/3D map status for the mapper. If you closed the mapper with
the 3D view, it should still be there when you open it.
#### Motivation for adding to Mudlet
Bugfix.
#### Other info (issues closed, discussion etc)
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Modernize font fallback on Linux to use [a new
function](https://doc.qt.io/qt-6/qfontdatabase.html#addApplicationEmojiFontFamily)
introduced in Qt 6.9.
#### Motivation for adding to Mudlet
Very often do [people in
Discord](https://discord.com/channels/283581582550237184/283582068334526464/1404150768977842219)
get issues where the only available font is the emoji one so they get 1
character displayed per one line:
I'm hoping this will fix such cases.
#### Other info (issues closed, discussion etc)
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
#### Brief overview of PR changes/additions
- Enable users to set an alpha value for the mapper background in:
Preferences > Mapper colors > Background color
- Make transparent/translucent backgrounds actually work (e.g. 0 for a
fully transparent map background).
- Make the 3D mapper use the color set in Preferences > Mapper colors >
Background color (previously it was permanently black no matter what).
- Properly save map background alpha across sessions.
#### Motivation for adding to Mudlet
This unlocks some really neat GUI options as I personally love having a
map overlay on top of the main console.
#### Other info (issues closed, discussion etc)
The 3D mapper is a bit funky since it is based on QOpenGLWidget which
has some limitations:
> [Limitations and Other
Considerations](https://doc.qt.io/qt-6/qopenglwidget.html#limitations-and-other-considerations)
>Putting other widgets underneath and making the QOpenGLWidget
transparent will not lead to the expected results: The widgets
underneath will not be visible. This is because in practice the
QOpenGLWidget is drawn before all other regular, non-OpenGL widgets, and
so see-through type of solutions are not feasible. Other type of
layouts, like having widgets on top of the QOpenGLWidget, will function
as expected.
>When absolutely necessary, this limitation can be overcome by setting
the
[Qt::WA_AlwaysStackOnTop](https://doc.qt.io/qt-6/qt.html#WidgetAttribute-enum)
attribute on the QOpenGLWidget. Be aware however that this breaks
stacking order, for example it will not be possible to have other
widgets on top of the QOpenGLWidget, so it should only be used in
situations where a semi-transparent QOpenGLWidget with other widgets
visible underneath is required.
So I've gone ahead and made it so setting the alpha value of the map
under 255 toggles transparency on AND the 3D map will always stack on
top. The unfortunate side effect is that everything then gets stuck
behind the 3D mapper. Hopefully this is niche enough to not cause
issues. If it is problematic, users are still free to set the alpha back
to 255 and AlwaysStackOnTop will be disabled.
Pardon the light mode!
https://github.com/user-attachments/assets/0c501008-974b-41bd-b1be-ecba288f4c71
To make background transparent as in the video set the alpha to `0` in:
Preferences > Mapper colors > Background color
---------
Co-authored-by: Zooka <136661366+ZookaOnGit@users.noreply.github.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
This reverts commit 53bcb3112b, as it was
breaking profile XML loading.
#### Motivation for adding to Mudlet
Fix `development` to load profiles.
#### Other info (issues closed, discussion etc)
We should have automated tests about this!
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>
#### Brief overview of PR changes/additions
This pull request implements secure credential management for Mudlet
with system keychain integration and encrypted fallback storage:
**Core Components:**
- **CredentialManager**: High-level API for secure credential storage
with QtKeychain integration
- **SecureStringUtils**: Qt-based cryptographic utilities for encrypted
file storage
- **Legacy Migration**: Automatic detection and migration of existing
plaintext passwords
**Key Features:**
- **System Keychain Integration**: Primary storage in macOS Keychain,
Windows Credential Store, and Linux Secret Service via QtKeychain
- **Encrypted File Fallback**: Qt crypto-based AES encryption for
portable mode and keychain unavailability
- **Seamless Migration**: Automatic detection and upgrade of legacy
password storage formats
- **Profile Isolation**: Per-profile encryption keys prevent
cross-profile credential access
- **Portable Mode Support**: Automatic detection and secure file-based
storage for portable installations
#### Motivation for adding to Mudlet
**Security Enhancement:**
- Eliminates plaintext password storage in profile XML files
- Provides industry-standard system keychain integration for credential
security
- Implements authenticated encryption for fallback scenarios
**User Experience:**
- Zero configuration required - works automatically across all platforms
- Seamless migration from existing plaintext passwords to secure storage
- Native system integration provides familiar credential management
experience
**Future-Proofing:**
- Extensible architecture supports additional credential types (API
keys, tokens, etc.)
- Robust fallback ensures functionality in all deployment scenarios
- Prepares foundation for OAuth and external service integrations
#### Other info (issues closed, discussion etc)
**Security Architecture:**
- **Keychain-First Strategy**: Prefers system keychain with automatic
encrypted file fallback
- **Legacy Format Detection**: Automatically migrates passwords from
development branch keychain format
- **Memory Security**: Secure string clearing and controlled credential
lifecycle management
- **Input Validation**: Path traversal protection and service name
sanitization
**Implementation Highlights:**
- **Async Operations**: Non-blocking keychain operations with timeout
protection
- **Thread Safety**: Event-driven architecture prevents UI blocking and
race conditions
- **Comprehensive Testing**: Full test coverage for encryption,
migration, and fallback scenarios
- **Cross-Platform**: Unified API across Windows, macOS, and Linux with
platform-specific optimizations
**Version Compatibility & Migration:**
- **Mudlet 4.19.x Compatibility**: Preserves existing plaintext password
storage to ensure compatibility when switching between 4.19.x stable and
development builds
- **Mudlet 4.20.x Migration**: Post-4.20.0 release, automatic migration
begins converting plaintext passwords to secure storage
- **Bidirectional Safety**: Users can safely run both 4.19.x and
development versions without losing access to their passwords during the
transition period
- **Legacy Format Support**: Automatically detects and migrates
passwords from the original development branch keychain format
(`service="Mudlet profile"`) to the new secure format
# Credential Management Workflows
## 1. Credential Storage Strategy
```mermaid
flowchart TD
A[Store Password Request] --> B{Portable Mode?}
B -->|Yes| C[Encrypt & Store in Profile File]
B -->|No| D[Store in System Keychain]
D --> E{Keychain Success?}
E -->|Yes| F[Remove Encrypted Fallback File]
E -->|No| G[Fallback to Encrypted File]
F --> H[Success]
G --> I{Encryption Success?}
I -->|Yes| H
I -->|No| J[Failure]
C --> I
classDef primary fill:#4CAF50,stroke:#2E7D32,stroke-width:2px,color:#000
classDef fallback fill:#FF9800,stroke:#E65100,stroke-width:2px,color:#000
classDef decision fill:#2196F3,stroke:#0D47A1,stroke-width:2px,color:#fff
classDef result fill:#9C27B0,stroke:#4A148C,stroke-width:2px,color:#fff
class D,F primary
class C,G fallback
class B,E,I decision
class H,J result
```
## 2. Legacy Migration Workflow
```mermaid
flowchart TD
A[Retrieve Password Request] --> B[Try New Keychain Format]
B --> C{Password Found?}
C -->|Yes| D[Return Password]
C -->|No| E[Check Legacy Keychain Format]
E --> F{Legacy Found?}
F -->|Yes| G[Migrate to New Format]
G --> H[Store in New Format]
H --> I[Remove Legacy Entry]
I --> J[Return Migrated Password]
F -->|No| K[Try Encrypted File]
K --> L{File Found?}
L -->|Yes| M[Decrypt & Return]
L -->|No| N[Return Empty - No Password Stored]
classDef newformat fill:#4CAF50,stroke:#2E7D32,stroke-width:2px,color:#000
classDef legacy fill:#FF9800,stroke:#E65100,stroke-width:2px,color:#000
classDef migration fill:#9C27B0,stroke:#4A148C,stroke-width:2px,color:#fff
classDef fallback fill:#607D8B,stroke:#263238,stroke-width:2px,color:#fff
classDef result fill:#2196F3,stroke:#0D47A1,stroke-width:2px,color:#fff
class B,H newformat
class E,I legacy
class G migration
class K fallback
class D,J,M,N result
```
## 3. Cross-Platform Keychain Integration
```mermaid
flowchart TD
A[QtKeychain Request] --> B{Platform Detection}
B -->|macOS| C[Access Keychain Services]
B -->|Windows| D[Access Credential Store]
B -->|Linux| E[Access Secret Service]
C --> F[Store/Retrieve Credential]
D --> F
E --> F
F --> G{Operation Success?}
G -->|Yes| H[Return Result]
G -->|No| I[Log Error & Fallback]
I --> J[Use Encrypted File Storage]
J --> K[AES Encryption with Profile Key]
K --> L[Store in Profile Directory]
classDef platform fill:#2196F3,stroke:#0D47A1,stroke-width:2px,color:#fff
classDef keychain fill:#4CAF50,stroke:#2E7D32,stroke-width:2px,color:#000
classDef fallback fill:#FF9800,stroke:#E65100,stroke-width:2px,color:#000
classDef crypto fill:#9C27B0,stroke:#4A148C,stroke-width:2px,color:#fff
class C,D,E platform
class F,H keychain
class I,J fallback
class K,L crypto
```
This implementation provides a comprehensive, secure, and user-friendly
credential management system that seamlessly upgrades existing Mudlet
installations while providing robust security for future credential
storage needs.
---------
Co-authored-by: Vadim Peretokin <vperetokin@hey.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Remove ambiguous width East Asian option - while it made sense in
theory, in practice we have not found need for it, and we haven't found
anyone who uses it either - making it hard to justify to keep it.
#### Motivation for adding to Mudlet
Simplifying options by removing this one:
<img width="780" height="417" alt="image"
src="https://github.com/user-attachments/assets/add59fd5-3c46-44a3-a3fb-fbb5414dfbf6"
/>
#### Other info (issues closed, discussion etc)
Should we find users who need this, we can revert this PR and do an
update.
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
This PR enhances the "Show the text you sent" setting from a simple
boolean checkbox to a tri-state system, providing more granular control
over command echoing behavior while maintaining complete backward
compatibility.
**New Options:**
- **Never**: Commands are never shown on screen, regardless of script
settings
- **Script controlled** (Default): Scripts can control visibility using
`send(cmd, true/false)`
- **Always**: Commands are always shown on screen, regardless of script
settings
**Key Changes:**
- Replaced `bool mPrintCommand` with `enum class CommandEchoMode`
(Never=0, ScriptControl=1, Always=2)
- Enhanced `send()` function logic to respect the new tri-state mode
- Updated UI from checkbox to combo box with descriptive tooltips
- Implemented automatic migration from legacy boolean settings
- Enhanced Lua API with dual-mode backward compatibility

#### Motivation for adding to Mudlet
Resolves the inconsistent behavior reported in #6919 where `send(cmd,
false)` could suppress echo regardless of the global setting, but
`send(cmd, true)` could not show text if the global setting was
disabled.
**Problems Solved:**
1. **API Consistency**: Both `true` and `false` parameters to `send()`
now properly override global settings when appropriate
2. **User Control**: Users can choose between three clear modes instead
of confusing boolean behavior
3. **Script Safety**: Packages can provide critical feedback to users
even when global echo is disabled
4. **Backward Compatibility**: All existing scripts and profiles
continue to work unchanged
**Use Cases Addressed:**
- Users who never want to see commands (accessibility, clean interface)
- Users who want full script control (current behavior, new default)
- Users who always want to see commands (debugging, transparency)
- Package authors who need to ensure important messages are visible
#### Other info (issues closed, discussion etc)
**Closes:** #6919
**Backward Compatibility Strategy:**
- Legacy profile files: `printCommand="yes"` → ScriptControl,
`printCommand="no"` → Never
- Legacy Lua API: `getConfig("showSentText")` returns boolean
(true/false) for existing scripts
- Enhanced Lua API: `getConfig("showSentText", true)` returns string
("never"/"script"/"always") for new scripts
- Universal `setConfig()`: Accepts both boolean and string values with
automatic conversion
**Migration Path:**
- Existing scripts work unchanged - no breaking changes
- Profile settings automatically converted on load using
`getBoolValueFromLegacyAttributeOrDefault`
- XML export includes both new and legacy attributes for compatibility
**Implementation Details:**
- Uses existing `getBoolValueFromLegacyAttributeOrDefault` helper for
seamless profile migration
- Maintains all existing `send()` behavior in ScriptControl mode (new
default)
- Command line echo logic updated to respect tri-state mode
- Complete test coverage for all three modes and migration scenarios
**Testing:**
- All existing functionality preserved and tested
- New tri-state behavior verified for each mode
- Legacy profile migration tested with real profile files
- Lua API backward compatibility confirmed with existing script patterns
This hybrid approach addresses all concerns raised in the original PR
discussion while providing a clear upgrade path that satisfies both user
control advocates and script compatibility requirements.
---
### Show sent commands: Never
| Command | Displays |
| --- | --- |
| `send("smile", true)` | You smile 😄 |
| `send("smile", false)` | You smile 😄 |
### Show sent commands: Script controlled
| Command | Displays |
| --- | --- |
| `send("smile", true)` | smile |
| | You smile 😄 |
| `send("smile", false)` | You smile 😄 |
### Show sent commands: Always
| Command | Displays |
| --- | --- |
| `send("smile", true)` | smile |
| | You smile 😄 |
| `send("smile", false)` | smile |
| | You smile 😄 |
---
https://github.com/user-attachments/assets/f747329a-e9e2-4cff-b87a-333acca031ca
---------
Co-authored-by: Vadim Peretokin <vperetokin@hey.com>
#### Brief overview of PR changes/additions
* Revert redesign of main font selection in preferences.
* Restructure usage of fonts to remove the somewhat redundent `(QFont)
mDisplayFont` for widgets that inherently have their own (QFont) which
they use for painting operations.
* Provide a means to track only the details of `QFont`s that we care
about which should be more lightweight than holding a complete copy of a
font (in `TFontDetails`).
* Include the "antialisaing" detall for the main display font (only for
the main console) in the active updates that changing the main font in
the preferences produces.
* Apply changes to the font in a `TConsole` to **all** the
`TCommandLine`s associated with it.
#### Brief overview of PR changes/additions
* The previously revised font settings in the preferences that used a
native font selection was not as useful as it seemed - at least the
Windows one offered controls that we did not need or use which meant
they had not actual effects which would be confusing to the end user.
* Storing a separate copy of `QFont`s did not seem productive especially
as it was easy to modify the intrinsic one and not the copy or
vice-versa.
* Adding a separate class to track the font details we ARE interested in
makes it easier to compare fonts and to tell whether two instances are
the same as far as Mudlet is concerned
* It was not clear that the "anti-aliasing" setting was being correctly
applied/used where it was intended - with this PR it is also updated as
it is changed in the prefernces along with the other two settings: font
"family" and "size".
* The font in command-lines now clearly follow the main console or a
sub- console that they belong to.
#### Other info (issues closed, discussion etc)
Once this is done it is perhaps a bit clearer that the Lua API
`setFont(["windowName", ] "fontName")` sets the font family to use for
the main or (if given a name) a sub-console/user-window - and if it is
the "Main console" that also carries through into:
* the Lua script window in the editor
* the error window in the editor
* the "notepad"
* the map info display in the 2D mapper
the font is also replicated in the corresponding "command-lines" for
that "console".
The Lua API `setFontSize(["windowName", ]integer)` sets the font size
for all the places where the correspond font had been set by the
`setFont(...)` call. Setting the font name and size via the Lua API are
two separate operations and they do act seemingly independently of each
other.
Setting these things via the preferences take effect on the main console
(and related things) immediately (except for the mapper - that only
updates when the preferences dialogue is closed) - the anti-alias
setting also acts on these things in the same manner (which it didn't
before IIRC).
As a side effect of the reversion this will close#7907!
I have a separate commit that gives the option for
sub-console/user-windows to track the main console font family (so that
changing the font in there carries through into those other windows) -
optionally they can *also* track the size of the main font (by a
user/scriptable factor) so that as the main font is made smaller or
larger so do the other consoles - however this can be switched off if
not wanted. I've let that as a separate commit because it would just
enlarge this already meaty PR and it can be safely left to be
reviewed/done separately!
---------
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
Provides code compatible with later Qt versions when reading the colours
used to show rooms above and below the current z-coordinate in the 2D
map. The original PR that introduced this detail #7654 only included
code for Qt < 6.6.0 - using `(void) QColor::setNamedColor(const
QString&)` but this has been deprecated since Qt 6.6 and has been
replaced by `(Qcolor) QColor::fromString(QAnyStringView)` - the original
will now produce build warnings in the current 6.9 versions.
#### Motivation for adding to Mudlet
Make the settings for these colours be preserved between sessions and
stop the generation of (OS command-line) warnings of the form:
`XMLimport::readUnknownElement("Host") ERROR - UNKNOWN Package Element
name:` for `mLowerLevelColor` and `mUpperLevelColor`.
#### Other info (issues closed, discussion etc)
This was brought to my attention by **termie** in the Discord **#help**
channel at 2025/07/01 20:12 UTC, though it also tickled a memory I had
of seeing the same warnings about these (but I had thought it was just a
problem in my own setup).
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
This PR is a follow-up to #7862 and improves how Mudlet handles MXP
support across old profiles, new profiles, and in-band detection
scenarios:
1. Ensures the *Choose Protocols → MXP* preference is correctly
defaulted to checked on first load of existing profiles that did not
previously have it set (new profiles already worked).
2. Migrates the old "disable MXP" special option to uncheck the *Choose
Protocols → MXP* setting for users who previously opted out.
3. Retains support for scripts using the deprecated
`getConfig("specialForceMxpNegotiationOff")` and `setConfig(...)`.
4. Adds content-based MXP detection: if the MXP processor is off and
tags like `<VERSION>`, `<SUPPORT>`, or `<!ELEMENT>` are detected, MXP is
automatically enabled once per profile with a console notification.
#### Motivation for adding to Mudlet
MXP negotiation is not required by spec. Games fall into one of three
categories:
1. Games that do not use MXP at all — enabling the processor may break
gameplay.
2. Games that negotiate MXP — StickMUD and others using the KaVir
protocol snippet work as expected.
3. Games like Lusternia — expect MXP to be processed based on user-side
`CONFIG MXP ON` without negotiation.
This PR improves compatibility in all three cases:
- By default, MXP is off unless negotiated or enabled by the user.
- When tags indicating MXP use are detected without negotiation, MXP is
automatically enabled once per profile with a console message explaining
what happened and how to disable it.
- Prior profile settings and legacy script API usage are honored and
migrated smoothly.
#### Other info (issues closed, discussion etc)
Fixes#7833
Related to and builds on #7862
Future idea: migrate CHARSET and NEW-ENVIRON to the protocol dropdown to
simplify the UI further.
---
https://github.com/user-attachments/assets/ec21db2d-89b1-404e-acff-3bae472a6e0c
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
This PR addresses [#7826](https://github.com/Mudlet/Mudlet/issues/7826),
where some MUD servers using KaVir’s protocol snippet [
[1](https://github.com/Xavious/MSDP_Protocol_Handler/blob/master/protocol.c)
] [
[2](https://github.com/scandum/msdp_protocol_snippet_by_kavir/blob/master/protocol.c)
] [
[3](https://github.com/halimcme/worldofpain/blob/master/protocol.cpp) ]
expect both the client name and a numeric version (i.e., `MUDLET
4.19.1`) during Telnet TTYPE negotiation. This change allows users to
optionally include the version number in the terminal type, restoring
compatibility for the servers running this legacy script.
* A `Send Mudlet version in terminal type` checkbox was added to the
Special Options tab of Settings, which is disabled by default.
* To streamline the process of applying the checkbox where needed,
Mudlet will detect KaVir protocol snippet's standard pattern of 8
negotiations occuring in a specific order, responding with a *one-time*
prompt for a user choice to automatically mark the checkbox and
reconnect to obtain the 256 color setting within their game.
* Also available via the Lua API:
* `getConfig("versionInTTYPE")`
* `setConfig("versionInTTYPE", option)`
* `getConfig("promptForVersionInTTYPE")`
* `setConfig("promptForVersionInTTYPE", option)`
#### Motivation for adding to Mudlet
To improve compatibility with MUD servers that require a version number
in TTYPE for enhanced color support, without violating protocol
standards.
Since 2024 ([#7103](https://github.com/Mudlet/Mudlet/issues/7826)),
Mudlet stopped sending the version number by default, because 1) it is
not required by RFCs and 2) MTTS, New-Environ, and MNES were added to
Mudlet. However, servers relying on this version information via KaVir's
snippet started assuming Mudlet was version 1.0 or earlier and defaulted
color support to 16 colors instead of 256-color mode.
#### Other info (issues closed, discussion etc)
Closes#7826.
Restores expected behavior for servers using KaVir’s protocol snippet.
No impact on servers that do not require the version number.
---
New Special Option
<img width="1010" alt="Screenshot 2025-06-02 at 8 20 02 AM"
src="https://github.com/user-attachments/assets/65a78f8a-aa93-4073-ae48-fe4a59f2da60"
/>
---
Evidence of Appending Version Number with the Special Option
<img width="969" alt="Screenshot 2025-06-02 at 8 19 40 AM"
src="https://github.com/user-attachments/assets/1909af0b-bb1d-413d-ad6e-019b9942fa02"
/>
---
Detecting the Legacy Script and Prompting Special Option Activation
<img width="1080" alt="Screenshot 2025-06-08 at 10 29 12 PM"
src="https://github.com/user-attachments/assets/b84f5917-a4cd-467e-b1eb-be5087b4da3c"
/>
---
Confirming Application of the Special Option and Reconnecting
<img width="907" alt="Screenshot 2025-06-08 at 10 29 41 PM"
src="https://github.com/user-attachments/assets/3a26409a-958e-4735-bba2-5485f09045af"
/>
---------
Co-authored-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
This PR fixes an issue where the "F3 Search" accessibility checkbox in
Preferences → Accessibility would not persist its checked state after
restarting the profile or when set as a global preference (without a
profile loaded). The setting is now correctly saved and restored per
profile and globally.
#### Motivation for adding to Mudlet
Addresses #7770: Users reported that enabling the F3 Search
accessibility shortcut was not being saved, causing accessibility
features to be lost after restarting or reopening Preferences.
#### Other info (issues closed, discussion etc)
Closes#7770
Ensures accessibility settings are reliably saved and restored for all
users.
Testing results here:
https://github.com/user-attachments/assets/04a31e66-ef1a-4a36-8c01-76a86eda8ae2
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
This pull request ensures Mudlet’s MXP processor is enabled or disabled
based on MXP negotiation with the server. The MXP protocol setting in
Settings → General now accurately reflects and controls the negotiation
state and protocol activation. Variable handling aligns with the other
protocols throughout the code.
<img width="527" alt="Screenshot 2025-05-24 at 11 59 13 AM"
src="https://github.com/user-attachments/assets/ca875373-76a8-4c5e-997e-24e032b6e880"
/>
#### Motivation for adding to Mudlet
Fixes#7833 by making MXP protocol handling consistent and
user-controllable.
#### Other info (issues closed, discussion etc)
Tested on StickMUD: Toggle MXP in the protocols drop-down, reconnect,
and verify that room exits are underlined (MXP enabled) or not (MXP
disabled).
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
To improve accessibility for hearing impaired within the Media handling
in Mudlet (or those who just want sound off while they game), this
enhancement adds a closed captioning feature and the following optional
parameter implemented in the GMCP syntax for `Client.Media.Play` and Lua
API functions `playSoundFile`, `playMusicFile`, `playVideoFile` for
playing media:
* `caption`: A caption-friendly textual representation of the sound,
such as onomatopoeia or sound cues (e.g., *thunderclap*, *blacksmith
hammering*).
Inspired by [Captioning Key – Sound Effects and
Music](https://dcmp.org/learn/602-captioning-key---sound-effects-and-music%7CDCMP).
-----
To enable the closed caption capability in Mudlet, a checkbox was added
into the Settings->Accessibility menu:
<img width="626" alt="Screenshot 2025-05-11 at 4 01 40 PM"
src="https://github.com/user-attachments/assets/d8b68c79-ed4d-4b80-b4c1-4c3ada8326e2"
/>
-----
Here is the outcome [in brackets] where a caption is not set (the cow)
and where a caption is set (rugby club):
<img width="821" alt="Screenshot 2025-05-11 at 9 57 55 AM"
src="https://github.com/user-attachments/assets/026fa694-1bb7-46b8-837b-78f9d17b1d23"
/>
#### Motivation for adding to Mudlet
Discussions with @RahjIII who authors the LociTerm client.
#### Other info (issues closed, discussion etc)
Aligns with update 1.0.3 for the [Mud Client Media
Protocol](https://wiki.mudlet.org/w/Standards:MUD_Client_Media_Protocol).
#### Brief overview of PR changes/additions
In all places in our code where we use `Q_UNUSED(...)` to silence a
warning about an argument to a method/function that isn't used this PR
removes any following `;` as this macro does NOT need it.
#### Motivation for adding to Mudlet
For consistency across the entirety of our code, so that all our usage
of this macro is "correct" and doesn't include something that is
unneeded.
#### Other info (issues closed, discussion etc)
There are places in third-party code that does include it but it isn't
our job to clean those up!
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
#### Brief overview of PR changes/additions
This PR adds a missing Line-Feed to those methods that are missing them
after the closing `)` or the `) const` of the arguments to that method.
#### Motivation for adding to Mudlet
We intend for the opening `{` to be in the first column of the next line
but some PRs in the past have added methods (functions) where this
hasn't been done and this is not something that the `clang-format` that
we bundle can fix-up for us automatically.
#### Other info (issues closed, discussion etc)
Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
<!-- Keep the title short & concise so anyone non-technical can
understand it,
the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
Attempt to remove all obsolete qt5 checks.
#### Motivation for adding to Mudlet
Migrate to qt6.
#### Other info (issues closed, discussion etc)
---------
Co-authored-by: Vadim Peretokin <vadi2@users.noreply.github.com>