mirror of
https://github.com/Mudlet/Mudlet
synced 2026-08-13 18:26:27 -04:00
2 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5dd5b14ee1
|
fix: package lifecycle - queued save outliving the profile, unremovable archives, module priority (#9684)
#### Brief overview of PR changes/additions - The profile save that installing or uninstalling a package owes is now held in a member `QTimer` that the profile close and `~Host()` stop, instead of a `QTimer::singleShot()` queued on the `Host`: that call was still delivered after `HostManager::deleteHost()` had destroyed the profile, and `Host::saveProfile()` then read freed members. - `installPackage()` refuses an archive it could read no package out of and takes the folder it unpacked back off disk - but only a folder inside the profile, since the name can be whatever an untrusted `config.lua` says. It answered `true` for such an archive before, leaving something registered nowhere that could never be uninstalled. - `getModulePriority()` asks `mInstalledModules` whether the module exists, the same list `setModulePriority()` uses, and reports the default priority of 0 for one nobody has prioritised yet. #### Motivation for adding to Mudlet Uninstall a package, close Mudlet, and the queued save runs against the destroyed profile - a heap use-after-free on the way out, which is what a "Mudlet crashed when I closed it" report looks like. Reproduced under AddressSanitizer, clean afterwards. The other two are smaller but user-visible: picking the wrong zip in the package manager reported success and left a folder behind that nothing could remove, and a script could not tell "module not installed" from "installed, never prioritised". #### Other info (issues closed, discussion etc) Closes #9653, closes #9654, closes #9655. Test case: `installPackage("something.mpackage")`, `uninstallPackage("something")`, then close Mudlet straight away - it exits cleanly; `installPackage()` on a zip with no package XML in it now answers `nil` plus a message and leaves nothing behind; `getModulePriority()` on a freshly installed module answers `0`. The package lifecycle specs carried the last two as `pending()`; both are flipped to real specs, and a new `PackageUninstallSaveTeardownTest` covers the save deferral, its coalescing, the profile close, and that a refused archive can only take its own folder with it. Assisted-by: Claude:claude-opus-5 |
||
|
|
1f00cd0ad7
|
infrastructure: decouple profile management (Host) from UI dialogs (#9514)
#### Brief overview of PR changes/additions
- Removes all raw Qt Widgets usage from `Host.{h,cpp}` so the
`mudlet_core` Qt Widgets audit (`cmake/audit-core-widgets.sh`, added in
#9508) drops both files to zero: the offending-file count goes from 158
to 156 and both move to the "Clean files" list. (The committed
report/baseline are intentionally not regenerated here, to avoid
conflicts with sibling wave-2 PRs.)
- Follows the seam pattern established by #9507: the core (`Host`) emits
Qt signals carrying already-translated `tr()` strings, and the frontend
(`TMainConsole`/`mudlet`) owns the actual widgets.
- The dockable map widget (`mpDockableMapWidget`, a `QDockWidget`) moved
from `Host` to the profile's own `TMainConsole`.
`TMainConsole::createMapperDock()` constructs it and the console's
destructor disposes of it. `Host` still drives it through
`mpConsole->mpDockableMapWidget` (an already out-of-scope pointer per
the split plan) but no longer names any Qt Widgets type. The external
accessors in `mudlet.cpp`/`TDetachedWindow.cpp` gained an `mpConsole &&`
null-guard.
- The mapping-script reminder and package-unpacking progress dialogs are
now shown by the frontend in response to
`signal_showMapperScriptReminder` / `signal_showUnpackingProgress` /
`signal_hideUnpackingProgress`, wired up in
`mudlet::addConsoleForNewHost`.
- `TDockWidget` now sets its own dock features (moved out of
`Host::openWindow`); `Host::setBorders` uses
`QCoreApplication::sendEvent`; and the user-window scrollbar is hidden
via `TConsole::setScrollBarVisible()` instead of reaching into the raw
`QScrollBar`.
- Adds `HostWidgetDecouplingTest` (ephemeral port-0 stub + a real
profile, modelled on `TelnetTlsPromptTest`): verifies the map dock is
created and owned by the console, that `setMapperTitle` routes through
it, that the reminder dialog is raised, and that the unpacking dialog is
replaced then disposed (asserting the replaced dialog is destroyed, not
leaked). Two further tests cover the seams end to end: a real package
install has to reach the dialog through the `addConsoleForNewHost`
wiring, and closing a profile has to take the console-owned map dock
with it.
#### Motivation for adding to Mudlet
Continues the re-scoped libmudlet plan (a Qt Widgets-free `mudlet_core`
for headless use, testability and WASM). `Host` is the second concrete
extraction after `cTelnet` (#9507) and copies its template so later
extractions can follow the same shape.
#### Other info (issues closed, discussion etc)
Part of #8681 / #9011. Behavior-preserving: dialogs keep the same
modality/defaults and all strings stay in `Host`'s translation context,
so existing translations are unaffected. One intentional behaviour
change: failing to load the cosmetic unpacking/reminder `.ui` now warns
and no-ops instead of aborting the package install (a fire-and-forget
signal cannot fail the install back to `Host`), which is strictly
better. The dock's `deleteLater()` cleanup moved from `Host`'s
destructor to `TMainConsole`'s.
Reviewed with the code-reviewer and silent-failure-hunter agents; both
flagged a leak in the unpacking-dialog replace path (parentless dialog
`close()`d instead of `deleteLater()`d) and the missing destructor
cleanup, both fixed and now covered by the test. Expect the `mudlet.cpp`
`addConsoleForNewHost` wiring to conflict with sibling wave-2 PRs; that
is fine.
Assisted-by: Claude:claude-opus-4-8
**Test case:** Open a profile and open the mapper via the Map toolbar
button - it appears docked as before, and its title can be changed with
`setMapperTitle(...)`. Install a `.zip`/`.mpackage` from the package
manager (a normal package, not a module-from-UI and not a script/quiet
install) and confirm the "Unpacking..." progress dialog shows and then
closes. On a profile with no mapper script, open the mapper and confirm
the "you have no mapper script" reminder dialog appears and its link
opens the mapping scripts page.
#### Demo (before & after)
Parity check that the moved dialog/dock flows behave identically before
(development) and after this PR: the package-install "Unpacking..."
dialog and the dockable map widget.
https://github.com/user-attachments/assets/41579b67-7de1-463b-b642-bd7660b52431
|