@mudlet/mudlet-web 0.4.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/README.md +1 -1
  2. package/dist-lib/analytics.d.ts +27 -0
  3. package/dist-lib/assets/fonts/Vera.woff2 +0 -0
  4. package/dist-lib/assets/fonts/VeraBI.woff2 +0 -0
  5. package/dist-lib/assets/fonts/VeraBd.woff2 +0 -0
  6. package/dist-lib/assets/fonts/VeraIt.woff2 +0 -0
  7. package/dist-lib/assets/mapParser.worker-DrVZ3wg_.js +9 -0
  8. package/dist-lib/backupImport-wt8r7JXI.js +233 -0
  9. package/dist-lib/branding.d.ts +111 -13
  10. package/dist-lib/db/sqliteClient.d.ts +195 -7
  11. package/dist-lib/hooks/useAutoReconnect.d.ts +61 -0
  12. package/dist-lib/hooks/useDocumentFullscreen.d.ts +23 -0
  13. package/dist-lib/hooks/useKeyboardShortcuts.d.ts +37 -0
  14. package/dist-lib/hooks/useRecordLastPlayed.d.ts +10 -0
  15. package/dist-lib/import/applyMudletProfile.d.ts +35 -9
  16. package/dist-lib/import/backupImport.d.ts +118 -0
  17. package/dist-lib/import/collectProfileExport.d.ts +33 -2
  18. package/dist-lib/import/defaultPackages.d.ts +57 -14
  19. package/dist-lib/import/defaults/CF-loader/CF-loader.mpackage +0 -0
  20. package/dist-lib/import/defaults/MedBootstrap/MedBootstrap.mpackage +0 -0
  21. package/dist-lib/import/defaults/deleteOldProfiles/deleteOldProfiles.mpackage +0 -0
  22. package/dist-lib/import/defaults/echo/echo.mpackage +0 -0
  23. package/dist-lib/import/defaults/enable-accessibility/enable-accessibility.mpackage +0 -0
  24. package/dist-lib/import/defaults/generic_mapper/generic_mapper.mpackage +0 -0
  25. package/dist-lib/import/defaults/gui-drop/gui-drop.mpackage +0 -0
  26. package/dist-lib/import/defaults/icesus-loader/icesus-loader.mpackage +0 -0
  27. package/dist-lib/import/defaults/mg-loader/mg-loader.mpackage +0 -0
  28. package/dist-lib/import/defaults/mudlet-base-ui/mudlet-base-ui.mpackage +0 -0
  29. package/dist-lib/import/defaults/mudlet-mapper.xml +1215 -389
  30. package/dist-lib/import/mudletControlChars.d.ts +14 -0
  31. package/dist-lib/import/mudletHost.d.ts +73 -10
  32. package/dist-lib/import/mudletLink.d.ts +53 -2
  33. package/dist-lib/import/mudletProfileExport.d.ts +38 -5
  34. package/dist-lib/import/mudletProfileImport.d.ts +110 -12
  35. package/dist-lib/import/mudletVariables.d.ts +4 -3
  36. package/dist-lib/import/mudletWriteback.d.ts +15 -1
  37. package/dist-lib/import/mudletXmlImport.d.ts +46 -0
  38. package/dist-lib/import/packageFonts.d.ts +33 -0
  39. package/dist-lib/import/packageInstaller.d.ts +111 -7
  40. package/dist-lib/import/qtProfileData.d.ts +10 -0
  41. package/dist-lib/import/remotePackageInstall.d.ts +23 -7
  42. package/dist-lib/import/zipEntries.d.ts +45 -0
  43. package/dist-lib/index.d.ts +7 -3
  44. package/dist-lib/index.js +50660 -29735
  45. package/dist-lib/logging/SessionLogger.d.ts +75 -15
  46. package/dist-lib/map/MapSelectionOverlay.d.ts +2 -2
  47. package/dist-lib/map/MapStore.d.ts +432 -59
  48. package/dist-lib/map/MudletHighlightOverlay.d.ts +2 -2
  49. package/dist-lib/map/{MudixMapReader.d.ts → MudletMapReader.d.ts} +2 -2
  50. package/dist-lib/map/areaGraph.d.ts +120 -0
  51. package/dist-lib/map/environmentKit.d.ts +36 -0
  52. package/dist-lib/map/labelPixmap.d.ts +90 -0
  53. package/dist-lib/map/mapImageExport.d.ts +36 -2
  54. package/dist-lib/map/mapStyle.d.ts +11 -0
  55. package/dist-lib/map/mapVersion.d.ts +66 -0
  56. package/dist-lib/map/pathfinding.d.ts +2 -2
  57. package/dist-lib/mud/CharLoginController.d.ts +230 -0
  58. package/dist-lib/mud/ItemIdSequence.d.ts +1 -1
  59. package/dist-lib/mud/MudSession.d.ts +279 -22
  60. package/dist-lib/mud/PatternEngine.d.ts +153 -14
  61. package/dist-lib/mud/aliases/AliasEngine.d.ts +55 -9
  62. package/dist-lib/mud/autoLogin.d.ts +59 -0
  63. package/dist-lib/mud/commandSplit.d.ts +9 -0
  64. package/dist-lib/mud/connection/LineAssembler.d.ts +38 -11
  65. package/dist-lib/mud/connection/MudClient.d.ts +291 -53
  66. package/dist-lib/mud/connection/SubnegotiationRepair.d.ts +24 -0
  67. package/dist-lib/mud/connection/TelnetNegotiator.d.ts +208 -68
  68. package/dist-lib/mud/connection/telnetDebug.d.ts +8 -8
  69. package/dist-lib/mud/events.d.ts +102 -23
  70. package/dist-lib/mud/games/bundledGames.d.ts +12 -7
  71. package/dist-lib/mud/games/gameLinks.d.ts +40 -0
  72. package/dist-lib/mud/games/icons/3Kingdoms.png +0 -0
  73. package/dist-lib/mud/games/icons/3Scapes.png +0 -0
  74. package/dist-lib/mud/games/icons/achaea.png +0 -0
  75. package/dist-lib/mud/games/icons/aetolia.png +0 -0
  76. package/dist-lib/mud/games/icons/banner_astaria.png +0 -0
  77. package/dist-lib/mud/games/icons/banner_darkmists.png +0 -0
  78. package/dist-lib/mud/games/icons/banner_dreamland.png +0 -0
  79. package/dist-lib/mud/games/icons/banner_petria.jpg +0 -0
  80. package/dist-lib/mud/games/icons/banner_ropmud.png +0 -0
  81. package/dist-lib/mud/games/icons/blackmud.png +0 -0
  82. package/dist-lib/mud/games/icons/carrionfields.png +0 -0
  83. package/dist-lib/mud/games/icons/dragonfire_icon.png +0 -0
  84. package/dist-lib/mud/games/icons/fed2-logo.png +0 -0
  85. package/dist-lib/mud/games/icons/icesus_480x120.png +0 -0
  86. package/dist-lib/mud/games/icons/imperian.png +0 -0
  87. package/dist-lib/mud/games/icons/infinity_480x120.png +0 -0
  88. package/dist-lib/mud/games/icons/lusternia.png +0 -0
  89. package/dist-lib/mud/games/icons/morgengrauen_480x120.png +0 -0
  90. package/dist-lib/mud/games/icons/mudlet-tutorial.png +0 -0
  91. package/dist-lib/mud/games/icons/phoenixmud_480x120.png +0 -0
  92. package/dist-lib/mud/games/icons/starmourn.png +0 -0
  93. package/dist-lib/mud/games/icons/voidmud_icon.png +0 -0
  94. package/dist-lib/mud/games/icons/wotmudicon.png +0 -0
  95. package/dist-lib/mud/games/websiteLinks.d.ts +21 -0
  96. package/dist-lib/mud/keybindings/KeyEngine.d.ts +110 -4
  97. package/dist-lib/mud/keybindings/browserReservedKeys.d.ts +2 -2
  98. package/dist-lib/mud/keybindings/keyEventTarget.d.ts +61 -0
  99. package/dist-lib/mud/keybindings/qtKeys.d.ts +91 -12
  100. package/dist-lib/mud/protocol/atcp.d.ts +19 -0
  101. package/dist-lib/mud/protocol/byteString.d.ts +24 -0
  102. package/dist-lib/mud/protocol/charLoginFlow.d.ts +243 -16
  103. package/dist-lib/mud/protocol/charset.d.ts +111 -46
  104. package/dist-lib/mud/protocol/codePages.d.ts +10 -0
  105. package/dist-lib/mud/protocol/constants.d.ts +17 -10
  106. package/dist-lib/mud/protocol/echo.d.ts +23 -27
  107. package/dist-lib/mud/protocol/gmcp.d.ts +36 -6
  108. package/dist-lib/mud/protocol/index.d.ts +3 -2
  109. package/dist-lib/mud/protocol/luaLiteral.d.ts +12 -0
  110. package/dist-lib/mud/protocol/mccp.d.ts +83 -1
  111. package/dist-lib/mud/protocol/mnes.d.ts +41 -58
  112. package/dist-lib/mud/protocol/msdp.d.ts +11 -4
  113. package/dist-lib/mud/protocol/msp.d.ts +3 -2
  114. package/dist-lib/mud/protocol/mssp.d.ts +9 -4
  115. package/dist-lib/mud/protocol/multiByte.d.ts +51 -0
  116. package/dist-lib/mud/protocol/multiByteEncode.d.ts +42 -0
  117. package/dist-lib/mud/protocol/mxp.d.ts +193 -14
  118. package/dist-lib/mud/protocol/oauthClientFlow.d.ts +128 -0
  119. package/dist-lib/mud/protocol/qtCodePages.generated.d.ts +1 -0
  120. package/dist-lib/mud/protocol/tlsCodes.d.ts +40 -3
  121. package/dist-lib/mud/replay/ReplayPlayer.d.ts +16 -1
  122. package/dist-lib/mud/replay/replayFormat.d.ts +29 -2
  123. package/dist-lib/mud/text/Console.d.ts +255 -49
  124. package/dist-lib/mud/text/FormatState.d.ts +137 -14
  125. package/dist-lib/mud/text/ansiEscapes.d.ts +32 -16
  126. package/dist-lib/mud/text/colorParsers.d.ts +14 -2
  127. package/dist-lib/mud/text/colorSpaceId.d.ts +2 -0
  128. package/dist-lib/mud/text/colors.d.ts +17 -10
  129. package/dist-lib/mud/text/controlCharacterMode.d.ts +1 -1
  130. package/dist-lib/mud/text/hyperlinkVisibility.d.ts +1 -1
  131. package/dist-lib/mud/text/lineBreak.d.ts +5 -0
  132. package/dist-lib/mud/text/serverWrap.d.ts +2 -2
  133. package/dist-lib/mud/text/textAnalyzer.d.ts +79 -0
  134. package/dist-lib/mud/text/unicodeRuns.d.ts +11 -0
  135. package/dist-lib/mud/text/unicodeTables.generated.d.ts +3 -0
  136. package/dist-lib/mud/text/wcwidth.d.ts +27 -22
  137. package/dist-lib/mud/timers/TimerEngine.d.ts +163 -11
  138. package/dist-lib/mud/timers/workerClock.d.ts +67 -0
  139. package/dist-lib/mud/triggers/TriggerEngine.d.ts +449 -47
  140. package/dist-lib/mud/triggers/legacyColorPatterns.d.ts +58 -0
  141. package/dist-lib/mud/triggers/pcre/Pcre2.d.ts +83 -1
  142. package/dist-lib/oauth-callback.html +94 -0
  143. package/dist-lib/pcre2Wasm.d.ts +54 -0
  144. package/dist-lib/pcre2Wasm.js +241 -0
  145. package/dist-lib/scripting/EngineHost.d.ts +54 -21
  146. package/dist-lib/scripting/IScriptingRuntime.d.ts +103 -27
  147. package/dist-lib/scripting/MxpFrameManager.d.ts +10 -1
  148. package/dist-lib/scripting/ScriptingAPI.d.ts +1039 -213
  149. package/dist-lib/scripting/ScriptingEngine.d.ts +753 -103
  150. package/dist-lib/scripting/StopwatchManager.d.ts +97 -36
  151. package/dist-lib/scripting/http/HttpService.d.ts +9 -1
  152. package/dist-lib/scripting/http/userUrl.d.ts +41 -0
  153. package/dist-lib/scripting/lua/LuaRuntime.d.ts +352 -77
  154. package/dist-lib/scripting/lua/argContracts.d.ts +71 -0
  155. package/dist-lib/scripting/lua/bindings/automation.d.ts +1 -1
  156. package/dist-lib/scripting/lua/bindings/commands.d.ts +13 -0
  157. package/dist-lib/scripting/lua/bindings/context.d.ts +20 -2
  158. package/dist-lib/scripting/lua/bindings/diagnostics.d.ts +1 -1
  159. package/dist-lib/scripting/lua/bindings/map.d.ts +1 -1
  160. package/dist-lib/scripting/lua/bindings/media.d.ts +2 -2
  161. package/dist-lib/scripting/lua/bindings/output.d.ts +1 -1
  162. package/dist-lib/scripting/lua/bindings/session.d.ts +2 -2
  163. package/dist-lib/scripting/lua/bindings/textEdit.d.ts +1 -1
  164. package/dist-lib/scripting/lua/bindings/userWindows.d.ts +1 -1
  165. package/dist-lib/scripting/lua/byteArmor.d.ts +36 -0
  166. package/dist-lib/scripting/lua/imageSize.d.ts +7 -0
  167. package/dist-lib/scripting/lua/rex.d.ts +4 -2
  168. package/dist-lib/scripting/lua/sqlRowEncoder.d.ts +6 -1
  169. package/dist-lib/scripting/lua/utf8CaseMap.d.ts +1 -0
  170. package/dist-lib/scripting/lua/utf8Patterns.d.ts +21 -1
  171. package/dist-lib/scripting/lua/wellFormedStrings.d.ts +40 -0
  172. package/dist-lib/scripting/lua/yajl.d.ts +43 -5
  173. package/dist-lib/scripting/profilesPresence.d.ts +20 -2
  174. package/dist-lib/scripting/vfs/ProfileVFS.d.ts +164 -10
  175. package/dist-lib/scripting/vfs/fsErrors.d.ts +28 -0
  176. package/dist-lib/scripting/vfs/idbCommit.d.ts +20 -0
  177. package/dist-lib/storage/appStore.d.ts +24 -9
  178. package/dist-lib/storage/hooks.d.ts +5 -1
  179. package/dist-lib/storage/index.d.ts +2 -2
  180. package/dist-lib/storage/mapStorage.d.ts +2 -0
  181. package/dist-lib/storage/persistentStorage.d.ts +3 -1
  182. package/dist-lib/storage/profileStorage.d.ts +15 -0
  183. package/dist-lib/storage/profileVfsData.d.ts +3 -3
  184. package/dist-lib/storage/schema.d.ts +521 -44
  185. package/dist-lib/storage/storageMigration.d.ts +82 -0
  186. package/dist-lib/storage-g5V1b-c1.js +2039 -0
  187. package/dist-lib/styles.css +1 -1
  188. package/dist-lib/ui/AboutModal.d.ts +1 -1
  189. package/dist-lib/ui/BrandLoginFields.d.ts +1 -1
  190. package/dist-lib/ui/BrowserSignInModal.d.ts +23 -0
  191. package/dist-lib/ui/CharLoginModal.d.ts +24 -15
  192. package/dist-lib/ui/CmdLineMenuRegistry.d.ts +14 -5
  193. package/dist-lib/ui/CommandBar.d.ts +8 -8
  194. package/dist-lib/ui/ConnectionFormModal.d.ts +25 -1
  195. package/dist-lib/ui/ConnectionScreen.d.ts +9 -1
  196. package/dist-lib/ui/GameLinkPrompt.d.ts +28 -0
  197. package/dist-lib/ui/HelpModal.d.ts +5 -1
  198. package/dist-lib/ui/MapSymbolUsageModal.d.ts +32 -0
  199. package/dist-lib/ui/MouseEventRegistry.d.ts +31 -4
  200. package/dist-lib/ui/NotepadModal.d.ts +22 -0
  201. package/dist-lib/ui/ResizableModal.d.ts +16 -1
  202. package/dist-lib/ui/SettingsModal.d.ts +10 -1
  203. package/dist-lib/ui/TlsAlertBanner.d.ts +3 -1
  204. package/dist-lib/ui/Toolbar.d.ts +22 -1
  205. package/dist-lib/ui/VaultManageButton.d.ts +20 -0
  206. package/dist-lib/ui/VaultModal.d.ts +5 -0
  207. package/dist-lib/ui/VaultUnlockPrompt.d.ts +15 -0
  208. package/dist-lib/ui/buttons/ButtonsBar.d.ts +3 -1
  209. package/dist-lib/ui/buttons/inactiveButtons.d.ts +25 -0
  210. package/dist-lib/ui/cmdline/CommandLineManager.d.ts +19 -0
  211. package/dist-lib/ui/cmdline/plainText.d.ts +16 -0
  212. package/dist-lib/ui/cmdline/subCommandLine.d.ts +78 -0
  213. package/dist-lib/ui/cmdline/useCmdLineSelection.d.ts +26 -0
  214. package/dist-lib/ui/cmdline/useSubCommandLineKeys.d.ts +33 -0
  215. package/dist-lib/ui/codemirror/lineParagraphMarks.d.ts +19 -0
  216. package/dist-lib/ui/codemirror/luaHover.d.ts +7 -0
  217. package/dist-lib/ui/codemirror/options.d.ts +33 -0
  218. package/dist-lib/ui/codemirror/theme.d.ts +29 -5
  219. package/dist-lib/ui/commands/addonCommands.d.ts +134 -0
  220. package/dist-lib/ui/commands/appShortcuts.d.ts +86 -0
  221. package/dist-lib/ui/commands/hostCommands.d.ts +88 -0
  222. package/dist-lib/ui/commands/keyMatch.d.ts +20 -0
  223. package/dist-lib/ui/commands/keySequence.d.ts +57 -0
  224. package/dist-lib/ui/components/ContextMenu.d.ts +21 -1
  225. package/dist-lib/ui/components/FontPicker.d.ts +5 -1
  226. package/dist-lib/ui/components/useModalFocus.d.ts +13 -1
  227. package/dist-lib/ui/helpTopics.d.ts +15 -0
  228. package/dist-lib/ui/labels/LabelManager.d.ts +172 -9
  229. package/dist-lib/ui/labels/LabelOverlay.d.ts +19 -0
  230. package/dist-lib/ui/labels/backgroundImageSize.d.ts +19 -0
  231. package/dist-lib/ui/labels/labelModel.d.ts +64 -0
  232. package/dist-lib/ui/labels/qColor.d.ts +13 -0
  233. package/dist-lib/ui/labels/qtCss.d.ts +12 -4
  234. package/dist-lib/ui/landmarks.d.ts +9 -0
  235. package/dist-lib/ui/launcher/ActionMenu.d.ts +19 -0
  236. package/dist-lib/ui/launcher/GameDetails.d.ts +21 -0
  237. package/dist-lib/ui/launcher/GameSection.d.ts +18 -0
  238. package/dist-lib/ui/launcher/LauncherSearch.d.ts +23 -0
  239. package/dist-lib/ui/launcher/ProfileArt.d.ts +20 -0
  240. package/dist-lib/ui/launcher/ProfileSection.d.ts +43 -0
  241. package/dist-lib/ui/launcher/launcherModel.d.ts +72 -0
  242. package/dist-lib/ui/layout/overlayLayerOrder.d.ts +2 -2
  243. package/dist-lib/ui/menu/MenuBar.d.ts +31 -0
  244. package/dist-lib/ui/menu/SplitButton.d.ts +39 -0
  245. package/dist-lib/ui/menu/anchoredPopup.d.ts +74 -0
  246. package/dist-lib/ui/menu/appMenus.d.ts +65 -0
  247. package/dist-lib/ui/menu/menuModel.d.ts +88 -0
  248. package/dist-lib/ui/menu/toolbarModel.d.ts +56 -0
  249. package/dist-lib/ui/notepad.d.ts +76 -0
  250. package/dist-lib/ui/openSignInPage.d.ts +14 -0
  251. package/dist-lib/ui/output/OutputContextMenu.d.ts +14 -1
  252. package/dist-lib/ui/output/OutputRenderer.d.ts +5 -0
  253. package/dist-lib/ui/output/OutputSearchBar.d.ts +5 -1
  254. package/dist-lib/ui/output/StickyOutputPanel.d.ts +17 -3
  255. package/dist-lib/ui/output/TextAnalyzerModal.d.ts +25 -0
  256. package/dist-lib/ui/output/caretMode.d.ts +3 -2
  257. package/dist-lib/ui/output/clearSplit.d.ts +11 -0
  258. package/dist-lib/ui/output/outputCopy.d.ts +41 -2
  259. package/dist-lib/ui/output/outputRows.d.ts +43 -0
  260. package/dist-lib/ui/output/selectionGesture.d.ts +35 -0
  261. package/dist-lib/ui/output/wordSelection.d.ts +33 -0
  262. package/dist-lib/ui/search/matcher.d.ts +14 -4
  263. package/dist-lib/ui/settings/SettingsShell.d.ts +91 -0
  264. package/dist-lib/ui/settings/ShortcutEditor.d.ts +13 -0
  265. package/dist-lib/ui/sound/SoundManager.d.ts +210 -44
  266. package/dist-lib/ui/sound/closedCaption.d.ts +4 -1
  267. package/dist-lib/ui/sound/mediaDirection.d.ts +35 -0
  268. package/dist-lib/ui/tabCompletion.d.ts +53 -0
  269. package/dist-lib/ui/textedit/TextEditManager.d.ts +11 -2
  270. package/dist-lib/ui/useOpenProfiles.d.ts +1 -1
  271. package/dist-lib/ui/useVaultSaver.d.ts +26 -0
  272. package/dist-lib/ui/video/VideoManager.d.ts +7 -0
  273. package/dist-lib/ui/windows/WindowManager.d.ts +452 -37
  274. package/dist-lib/ui/windows/panels/ScriptEditorPanel.d.ts +14 -0
  275. package/dist-lib/ui/windows/panels/ScriptSearch.d.ts +6 -1
  276. package/dist-lib/ui/windows/panels/TextPanel.d.ts +4 -1
  277. package/dist-lib/ui/windows/panels/VariablesView.d.ts +37 -7
  278. package/dist-lib/ui/windows/panels/WindowCmdLine.d.ts +4 -2
  279. package/dist-lib/ui/windows/panels/aliasLoop.d.ts +24 -0
  280. package/dist-lib/ui/windows/panels/editorClipboard.d.ts +45 -0
  281. package/dist-lib/ui/windows/panels/editorUndo.d.ts +79 -0
  282. package/dist-lib/ui/windows/panels/mapAreaChange.d.ts +10 -0
  283. package/dist-lib/utils/appShellCache.d.ts +25 -0
  284. package/dist-lib/utils/fontFamilyName.d.ts +13 -0
  285. package/dist-lib/utils/fontLoader.d.ts +31 -0
  286. package/dist-lib/utils/formatBytes.d.ts +4 -0
  287. package/dist-lib/utils/mustBeOneOf.d.ts +9 -0
  288. package/dist-lib/utils/profileLock.d.ts +1 -1
  289. package/dist-lib/utils/storedCredentials.d.ts +31 -0
  290. package/dist-lib/utils/systemTheme.d.ts +24 -0
  291. package/dist-lib/utils/timeZone.d.ts +10 -0
  292. package/dist-lib/vault/CredentialVault.d.ts +197 -0
  293. package/dist-lib/vault/deviceUnlock.d.ts +47 -0
  294. package/dist-lib/vault/passkeyPrf.d.ts +56 -0
  295. package/dist-lib/vault/useVault.d.ts +8 -0
  296. package/dist-lib/vault/vaultAccess.d.ts +80 -0
  297. package/dist-lib/vault/vaultCrypto.d.ts +56 -0
  298. package/dist-lib/vault/vaultHelpTopic.d.ts +9 -0
  299. package/dist-lib/vault/vaultRecord.d.ts +92 -0
  300. package/dist-lib/vfs-sw.js +220 -11
  301. package/dist-lib/vite.d.ts +1 -1
  302. package/dist-lib/vite.js +66 -43
  303. package/package.json +22 -14
  304. package/dist-lib/assets/mapParser.worker-t-3rd1zR.js +0 -9
  305. package/dist-lib/mud/connection/PingTracker.d.ts +0 -41
  306. package/dist-lib/ui/BundledGameGrid.d.ts +0 -21
  307. package/dist-lib/ui/ConnectionGrid.d.ts +0 -21
  308. package/dist-lib/ui/bufferWords.d.ts +0 -38
@@ -3,13 +3,14 @@ import type { LogFormat } from '../logging/SessionLogger';
3
3
  import type { AliasEngine } from '../mud/aliases/AliasEngine';
4
4
  import { TriggerEngine } from '../mud/triggers/TriggerEngine';
5
5
  import type { TimerEngine } from '../mud/timers/TimerEngine';
6
- import type { KeyEngine } from '../mud/keybindings/KeyEngine';
6
+ import type { KeyEngine, KeyNode } from '../mud/keybindings/KeyEngine';
7
7
  import type { ButtonNode, ScriptNode } from '../storage/schema';
8
8
  import { type InstallOutcome } from './ScriptingAPI';
9
9
  import { type EngineHost, type TempComplexTriggerSpec } from './EngineHost';
10
10
  import { ItemIdSequence } from '../mud/ItemIdSequence';
11
- import type { LuaGlobalEntry } from './IScriptingRuntime';
11
+ import type { LuaGlobalEntry, VariableEdit } from './IScriptingRuntime';
12
12
  import { ProfileVFS } from './vfs/ProfileVFS';
13
+ import type { MudletImportResult } from '../import/mudletXmlImport';
13
14
  import type { PackageManifest } from '../storage/schema';
14
15
  export declare class ScriptingEngine implements EngineHost {
15
16
  private readonly session;
@@ -23,11 +24,7 @@ export declare class ScriptingEngine implements EngineHost {
23
24
  private readonly api;
24
25
  private promptPending;
25
26
  private mudCarryState;
26
- private gmcpNegotiated;
27
- private msdpNegotiated;
28
- private msspNegotiated;
29
- private mnesNegotiated;
30
- private mxpNegotiated;
27
+ private readonly enabledProtocols;
31
28
  /** True once MXP (telnet option 91) has been negotiated on the live
32
29
  * connection. Gates in-band MXP markup parsing in processFlushBatch so
33
30
  * non-MXP MUDs (where `<grin>` is literal text) are untouched. Reset on
@@ -38,22 +35,25 @@ export declare class ScriptingEngine implements EngineHost {
38
35
  * script can drive MXP markup through feedTriggers on a profile that never
39
36
  * negotiated it. Does NOT enable the handshake replies. */
40
37
  forceMxpProcessorOn: boolean;
41
- /** Whether MXP `<SUPPORTS>`/`<VERSION>` handshake replies may be sent. Only
42
- * true when MXP was started via the telnet option-91 handshake — an
43
- * in-band-detected server's inbound MXP isn't confirmed, so we'd otherwise
44
- * spam it with text it reads as invalid commands. */
45
- private mxpHandshakeEnabled;
38
+ /** cTelnet's `enableMXP`: the game switched MXP on (WILL, DO or a bare
39
+ * subnegotiation) or it was auto-enabled, and has not turned it off. */
40
+ private telnetMxpEnabled;
41
+ /** cTelnet's `mMxpAutoEnabled`: MXP is on because the auto-detect turned it
42
+ * on, so the game switching it on properly starts it afresh in open mode. */
43
+ private mxpAutoEnabled;
44
+ /** Mudlet's TMxpClient::mMxpEvents — element uses the parser has read but
45
+ * not yet published, drained at the end of processFlushBatch. */
46
+ private readonly mxpEventQueue;
46
47
  /** Per-session OSC 8 preset registry (`preset:NAME` definitions). Shared
47
48
  * between the MXP parser and the plain-ANSI render path so a preset defined
48
49
  * in either mode resolves in both. */
49
50
  private readonly osc8Presets;
50
51
  /** Drives expire-on-event OSC 8 visibility links (conceal on the next user
51
- * input / prompt / output after they're clicked). Scans the live output. */
52
+ * input / prompt / output after they're clicked) within the live output. */
52
53
  private readonly visibility;
53
54
  /** Per-session MXP parser. The send callback carries the in-band
54
- * `<SUPPORTS>`/`<VERSION>` handshake replies (gated on
55
- * `mxpHandshakeEnabled`); `api` is read lazily at call time so this
56
- * initializer is safe before the constructor body runs. */
55
+ * `<SUPPORTS>`/`<VERSION>` answers; `api` is read lazily at call time so
56
+ * this initializer is safe before the constructor body runs. */
57
57
  private readonly mxp;
58
58
  private vfs;
59
59
  private readonly runtimeReady;
@@ -67,6 +67,13 @@ export declare class ScriptingEngine implements EngineHost {
67
67
  private triggersReady;
68
68
  private triggersDirty;
69
69
  private prevScripts;
70
+ private profileScriptsCompiled;
71
+ private readonly scriptHandlerState;
72
+ private readonly failedScripts;
73
+ private readonly scriptLoadErrors;
74
+ private scriptCompileSeq;
75
+ private readonly scriptCompiledAt;
76
+ private compiledThisLoad;
70
77
  private resetting;
71
78
  private disposed;
72
79
  private moduleSyncTimers;
@@ -78,13 +85,25 @@ export declare class ScriptingEngine implements EngineHost {
78
85
  private mudletSaveName;
79
86
  private mspBaseUrl?;
80
87
  private gmcpMediaDefaultUrl;
88
+ private readonly gmcpMediaDownloads;
81
89
  private exitFired;
82
- private readonly beforeUnload;
90
+ private readonly onPageHide;
91
+ private readonly onPageShow;
83
92
  private uuidToNumericId;
84
93
  /** The profile's single item-id sequence, shared with every engine and with
85
94
  * the Lua runtime's temporary items — see {@link ItemIdSequence}. */
86
95
  readonly itemIds: ItemIdSequence;
87
- private numericIdFor;
96
+ /**
97
+ * The number Lua sees for a store node, allocating one on first sight.
98
+ *
99
+ * Public because the editor's "Show Items' ID number" shows the same number:
100
+ * the whole point of that option is to read off the id you would then pass to
101
+ * `enableTrigger` or `killTimer`, so it has to be *this* id and not the store's
102
+ * UUID. Allocating on first sight matches Mudlet, whose own tooltip warns that
103
+ * an item's id "is constant during a session of the profile [but] may be
104
+ * different the next time the profile is loaded".
105
+ */
106
+ numericIdFor(uuid: string): number;
88
107
  /** Hand out the next item id. Backs the Lua runtime's temporary items, so a
89
108
  * temp and a permanent item can never collide on one number. */
90
109
  allocateItemId(): number;
@@ -130,6 +149,36 @@ export declare class ScriptingEngine implements EngineHost {
130
149
  * `_G`. Called once before the initial script load so handlers see their
131
150
  * persisted state, and again after a profile reset (fresh global table).
132
151
  */
152
+ /**
153
+ * Put a package's own `<VariablePackage>` globals into `_G` and onto the
154
+ * profile's save-list.
155
+ *
156
+ * A package may carry nothing else — a module of variables, fonts, images or
157
+ * a map loads completely and leaves every one of the six item units empty —
158
+ * so skipping these made such a package indistinguishable from one whose
159
+ * contents never loaded at all. They are not items: nothing tags them and an
160
+ * uninstall does not take them away, which is Mudlet's behaviour too.
161
+ */
162
+ private restorePackageVariables;
163
+ /**
164
+ * Load an installed package's `<VariablePackage>` globals: into `_G`, and
165
+ * onto the save-list. For the installs that commit in one step — the
166
+ * Package Manager, the repository and Client.GUI — which desktop reads the
167
+ * same way as a script's installPackage. Call before the package's items go
168
+ * into the store, so its scripts see the values as they load.
169
+ */
170
+ loadPackageVariables(data: MudletImportResult): void;
171
+ /**
172
+ * Put an installed package's `<VariablePackage>` globals on the profile's
173
+ * save-list.
174
+ *
175
+ * Desktop's `XMLimport::readVariable` reads a package's variables exactly as
176
+ * it reads the profile's own, so each one lands on `VarUnit`'s saved list
177
+ * and it — and any later change to it — survives a restart. Called once the
178
+ * install commits, so a refused one adds nothing; the store change schedules
179
+ * the profile-data save that captures their values.
180
+ */
181
+ private savePackageVariables;
133
182
  private restoreSavedVariables;
134
183
  /**
135
184
  * Snapshot the save-listed globals out of `_G` into the store, just before a
@@ -142,24 +191,100 @@ export declare class ScriptingEngine implements EngineHost {
142
191
  * Write the live store state back into a linked Mudlet folder's profile save.
143
192
  * Bases on the newest parseable save (so external Mudlet edits to Host/unknown
144
193
  * fields are preserved), replaces the automation + variable packages with
145
- * mudix's current data, and writes a Mudlet-style timestamped file in current/
194
+ * Mudlet Web's current data, and writes a Mudlet-style timestamped file in current/
146
195
  * — one per session, overwritten on subsequent saves (the overwrite is safe
147
196
  * now that the folder backend truncates). Mudlet loads the newest such file.
148
197
  */
149
198
  private writeBackLinkedProfile;
199
+ /**
200
+ * Write this client's server encoding, TLS and character name back to a
201
+ * linked Mudlet folder's own files for them (`encoding`, `ssl_tsl`,
202
+ * `login`), in desktop's format, so desktop connects the same way. Only a
203
+ * linked folder — a Mudlet Web profile that called saveProfile() has a
204
+ * current/ too, but no desktop reading these (#259) — and only files whose
205
+ * value changed (desktopProfileFileUpdates). Never the password.
206
+ */
207
+ private writeBackDesktopProfileFiles;
208
+ /**
209
+ * The profile as a Mudlet-format `<MudletPackage>` save: `baseXml` with its
210
+ * automation and variable packages replaced by the live store state, and its
211
+ * `<Host>` settings updated in place.
212
+ *
213
+ * Basing on an existing save is what keeps the ~130 `<Host>` fields Mudlet Web
214
+ * doesn't model — and any element it doesn't understand — from being dropped
215
+ * on the way out. With no save to base on, a profile imported from Mudlet
216
+ * falls back to the `<Host>` retained at import; one born in Mudlet Web gets the
217
+ * empty skeleton, so its `<Host>` carries only the settings Mudlet Web does model.
218
+ */
219
+ private buildProfileXml;
220
+ /**
221
+ * The `<Host>` a save starts from when there's no `current/*.xml` to base
222
+ * on: the one retained when this profile was imported from Mudlet, else the
223
+ * empty skeleton — either way stamped with the connection's current identity
224
+ * and package set, which desktop's `Host::saveProfile` always writes (the
225
+ * retained copy still names them as they were at import).
226
+ *
227
+ * Only reached when there's no `current/*.xml` to base on, so it never
228
+ * displaces a linked folder's own save. Undefined without a VFS; the bare
229
+ * retained file (or nothing) when the connection record is gone.
230
+ */
231
+ private hostBaseXml;
232
+ /**
233
+ * The XML half of Mudlet `saveProfile([location [, saveName]])`: write this
234
+ * profile out as a Mudlet-format save and answer with the full path written.
235
+ *
236
+ * Mirrors `Host::saveProfile`'s naming — an empty location means the
237
+ * profile's own `current/`, an empty name means a `YYYY-MM-DD#HH-mm-ss`
238
+ * stamp, and a name without one gains `.xml`. A default-located save also
239
+ * takes over as this session's write-back target, so the automatic flushes
240
+ * keep updating the newest file rather than leaving a stale older one as the
241
+ * save Mudlet would load.
242
+ */
243
+ saveProfileXml(location?: string, saveName?: string): {
244
+ ok: true;
245
+ path: string;
246
+ } | {
247
+ ok: false;
248
+ err: string;
249
+ path?: string;
250
+ };
150
251
  /** `_G` entries for the Variables view (user globals nested, built-ins
151
252
  * flagged). Empty until the runtime is up. */
152
253
  listGlobals(): LuaGlobalEntry[];
254
+ /** Apply one Variables-view edit to `_G`. Returns null on success, or the
255
+ * reason it was refused — including there being no runtime to edit. */
256
+ editVariable(request: VariableEdit): string | null;
153
257
  /**
154
258
  * Write the current in-store nodes for a module back to its XML file on disk.
155
259
  * Used both by the auto-sync debounce and the "Sync to file" UI action.
156
260
  */
157
261
  syncModuleToFile(moduleName: string): Promise<void>;
158
262
  /**
159
- * Re-read a module's XML from disk and replace its tagged nodes in the store.
160
- * Pairs with the "Reload from file" UI action.
161
- */
162
- reloadModuleFromFile(moduleName: string): boolean;
263
+ * Mudlet `reloadModule(name)` — Host::reloadModule, for a script and for
264
+ * the editor's "Reload from file" alike. Answers like an install does:
265
+ * `{ ok: true, error }` with whatever in the module does not work, or
266
+ * `{ ok: false, error }` with why it was not reloaded.
267
+ *
268
+ * Two things are checked before anything else, so the module's items are
269
+ * untouched by either: that it is installed at all, and that the file it
270
+ * was installed from can still be read — the reload takes the module
271
+ * apart before reading that file back in, and a file that has gone would
272
+ * leave it listed with nothing in it. Only then is a reload asked for
273
+ * during a profile save put off until the save is over; the true answered
274
+ * now is all the caller hears, so how the reload went afterwards is said on
275
+ * the console.
276
+ */
277
+ reloadModuleFromFile(moduleName: string): InstallOutcome;
278
+ /** Whether the file a module was installed from is a file that reads —
279
+ * QFileInfo's isFile() && isReadable() — in the profile or under the
280
+ * read-only /lua/ namespace. */
281
+ private moduleFileReadable;
282
+ /** Reloads put off by a profile save, carried out once it is over. */
283
+ private readonly postponedReloads;
284
+ /** The end of a profile save: the reloads it held up are carried out now,
285
+ * and only the console is left to tell how they went. */
286
+ private runPostponedReloads;
287
+ private reloadModuleNow;
163
288
  /** Toggle a module's sync flag. Updates the manifest in the store. */
164
289
  setModuleSync(moduleName: string, sync: boolean): void;
165
290
  /** Read a module's sync flag. Returns false if the package isn't a module. */
@@ -173,7 +298,9 @@ export declare class ScriptingEngine implements EngineHost {
173
298
  setModulePriority(moduleName: string, priority: number): boolean;
174
299
  /** Read a module's load priority (default 0). Returns 0 for non-modules. */
175
300
  getModulePriority(moduleName: string): number;
176
- /** List installed module names. Order matches install order. */
301
+ /** List installed module names, in byte order by name — desktop keeps
302
+ * them in a QMap keyed by name (Host::mInstalledModules), so that is the
303
+ * order getModules() reports, not the order they were installed in. */
177
304
  getModuleNames(): string[];
178
305
  /**
179
306
  * Write every module flagged to sync back out to its own file.
@@ -207,12 +334,21 @@ export declare class ScriptingEngine implements EngineHost {
207
334
  * via setPackageInfo. Empty table when the package is unknown and nothing
208
335
  * was set for that name. */
209
336
  getPackageInfo(name: string): Record<string, string>;
210
- /** Mudlet `setPackageInfo(name, key, value)`. Records a custom info field.
211
- * Always succeeds (matches Mudlet, which sets the map unconditionally). */
212
- setPackageInfo(name: string, key: string, value: string): boolean;
337
+ /** Mudlet `setPackageInfo(name, key, value)`. Records a custom info field,
338
+ * answering why it would not, or null once it has
339
+ * (TLuaInterpreter::setPackageInfo). */
340
+ setPackageInfo(name: string, key: string, value: string): string | null;
213
341
  /** Mudlet `setModuleInfo(name, key, value)`. Records a custom info field
214
- * surfaced by getModuleInfo. Always succeeds. */
215
- setModuleInfo(name: string, key: string, value: string): boolean;
342
+ * surfaced by getModuleInfo; see {@link setPackageInfo}. */
343
+ setModuleInfo(name: string, key: string, value: string): string | null;
344
+ /**
345
+ * Only for something installed — one being read in counts, so its own
346
+ * scripts can still describe it. Filed for any other name, the details were
347
+ * there for whatever was installed under it later. And `mpackage` is the
348
+ * name it is installed under, so only that same name is accepted for it: a
349
+ * package without a config.lua has no details to start from.
350
+ */
351
+ private setInfoField;
216
352
  /**
217
353
  * Mudlet `getModulePath(name) → path`. Absolute VFS path of an installed
218
354
  * module's XML. Modules referencing a file outside the managed package dir
@@ -224,11 +360,10 @@ export declare class ScriptingEngine implements EngineHost {
224
360
  /**
225
361
  * Install a module from a path inside the profile VFS. Plain XML stays in
226
362
  * place (manifest holds the absolute VFS path). Zips/.mpackages extract into
227
- * the standard pkgDir. Raises sysInstall, sysInstallPackage and
228
- * sysInstallModule on success — sysInstallModule is the module-specific
229
- * counterpart to sysInstallPackage. This method is reached only via the
230
- * Lua `installModule()` binding, so it also raises Mudlet's
231
- * `sysLuaInstallModule` (name, fileName) for ported-script parity.
363
+ * the standard pkgDir. This method is reached only via the Lua
364
+ * `installModule()` binding, so on success it raises what desktop's
365
+ * ModuleFromScript install does: sysInstall and sysLuaInstallModule
366
+ * (name, fileName) — not sysInstallPackage or sysInstallModule.
232
367
  */
233
368
  installModuleFromPath(path: string): InstallOutcome;
234
369
  /**
@@ -250,17 +385,83 @@ export declare class ScriptingEngine implements EngineHost {
250
385
  flushPendingApplies(): void;
251
386
  private applyTimersFromStore;
252
387
  private applyKeybindingsFromStore;
388
+ /** What this engine last told the button bar cannot be active, so that
389
+ * destroy() takes back only its own answer. */
390
+ private publishedInactiveButtons;
391
+ /**
392
+ * Tell the button bar which buttons, toolbars and menus will not compile.
393
+ * Desktop's TAction::compileScript leaves such a one with `mOK_code` false
394
+ * and Tree::activate refuses it, so the toolbar builders leave it off —
395
+ * a toolbar or menu with its buttons — while its switch stays as it was.
396
+ */
397
+ private applyButtonsFromStore;
398
+ /**
399
+ * Run every active push-down button once, in its saved state, as desktop
400
+ * does while a profile loads: TToolBar/TEasyButtonBar::addActionButtons
401
+ * and fillMenu execute each push-down button (menus' too) when
402
+ * `mIsProfileLoadingSequence` is set, with the console's mButtonState set
403
+ * from the saved state first. That is how a state-restoring button script
404
+ * re-applies itself on start (mudlet-web#356). Plain buttons do not run.
405
+ */
406
+ private runPushDownButtonsAtLoad;
253
407
  /**
254
408
  * On profile open, warn about enabled keybindings the browser intercepts
255
409
  * above the page (Ctrl+T new tab, Ctrl+W close tab, F12 devtools, …). Native
256
410
  * Mudlet packages bind these freely, but in a browser they can never reach
257
- * mudix — the warning explains why they "do nothing". Page-level shortcuts
258
- * mudix can still capture (Ctrl+R, Ctrl+S, F5, …) are intentionally not
411
+ * Mudlet Web — the warning explains why they "do nothing". Page-level shortcuts
412
+ * Mudlet Web can still capture (Ctrl+R, Ctrl+S, F5, …) are intentionally not
259
413
  * flagged: they work while the client is focused, same as in Mudlet.
260
414
  * Fired once at load (not on every store change) so it can't spam the output.
261
415
  */
262
416
  private warnBrowserReservedKeybindings;
263
417
  private applyScriptsFromStore;
418
+ /**
419
+ * Mudlet-style module load priority: scripts owned by modules with a
420
+ * negative priority load before profile scripts; non-negative priorities
421
+ * load after. Within a priority bucket, original array order is preserved
422
+ * so existing trees stay deterministic. Profile-owned scripts (no
423
+ * packageName) are treated as priority 0. Equal priorities: the profile
424
+ * before its modules (its XML is read before any module installs), and
425
+ * modules by name among themselves.
426
+ */
427
+ private scriptLoadOrder;
428
+ /**
429
+ * ScriptUnit::compileAll at profile load. Desktop walks its live tree and
430
+ * asks each root whether it is active only when it reaches it, so a body
431
+ * that runs earlier can switch a later script on or off, replace its code
432
+ * or remove it, and the walk honours that. Here every step re-reads the
433
+ * store, and first takes in whatever the last body changed (handler
434
+ * activity, a body setScript replaced) — the store subscription is not
435
+ * attached yet on a first load, and the walk has to see the change before
436
+ * it moves on either way.
437
+ */
438
+ private compileProfileScripts;
439
+ /**
440
+ * Host::reportScriptsThatFailedToLoad: once the profile's scripts have run
441
+ * — at open, and again after a reset — one warning names every switched-on
442
+ * script whose body failed, in script-list order, so a broken script is
443
+ * not lost among the errors that scrolled past. Module scripts are left
444
+ * out, as on desktop: a module of positive priority runs its scripts only
445
+ * after this, so reporting the others would make the warning depend on a
446
+ * module's priority.
447
+ */
448
+ private reportScriptsThatFailedToLoad;
449
+ /** Whether the root `s` sits under is switched on — what compileAll asks
450
+ * of each root before compiling its subtree. */
451
+ private scriptRootActive;
452
+ /**
453
+ * Event-handler lists (Host::mEventHandlerMap). Every script that lists
454
+ * events is registered, enabled or not — desktop registers them as the XML
455
+ * is read and asks isActive() && ancestorsActive() only when an event comes
456
+ * (TScript::callEventHandler). isActive() wants the body to have compiled
457
+ * too, so a script that failed, or sits under one that did, is registered
458
+ * inactive. Registration order is load order: the profile's own tree
459
+ * first, then modules by priority, which install after it
460
+ * (mudlet::slot_connectionDialogueFinished). Only what changed since the
461
+ * last sync is sent.
462
+ */
463
+ private syncScriptHandlersFrom;
464
+ private syncScriptHandlers;
264
465
  /** Map of module name → priority. Profile (no packageName) is implicitly 0. */
265
466
  private modulePriorityMap;
266
467
  private initRuntime;
@@ -286,9 +487,6 @@ export declare class ScriptingEngine implements EngineHost {
286
487
  * `expandAlias(text, echo)` is exactly this function.
287
488
  */
288
489
  hostSend(text: string, echo?: boolean): void;
289
- /** Nesting depth of {@link hostSend} — see the runaway-alias note there. */
290
- private sendDepth;
291
- private static readonly MAX_SEND_DEPTH;
292
490
  /** Mudlet `expandAlias(text, echo)` — `Host::send` with both defaults, i.e.
293
491
  * "run this as though I had typed it". */
294
492
  expandAlias(text: string, echo: boolean): void;
@@ -296,11 +494,34 @@ export declare class ScriptingEngine implements EngineHost {
296
494
  requestReconnect(): boolean;
297
495
  /** Raise sysDataSendRequest; true means a handler called denyCurrentSend(). */
298
496
  dispatchSendRequest(text: string): boolean;
497
+ /**
498
+ * The refusal for installing something under a name the *other* kind is
499
+ * already using, or null when the name is free of it.
500
+ *
501
+ * A package and a module may not share one name. Uninstall removes items by
502
+ * name alone, so a name held both ways loses both halves' items whichever
503
+ * half is removed — the user uninstalls a package and a module's triggers
504
+ * and scripts go with it, unwarned. Each half also displaced the other from
505
+ * its own listing, so nothing in the UI revealed the collision first
506
+ * (issue #104).
507
+ *
508
+ * Desktop refuses both directions with these two sentences
509
+ * (`Host::installPackage`, Host.cpp:2567 and :2584) and deliberately lets a
510
+ * profile that already holds the combination go on loading — only a *fresh*
511
+ * install is turned away, or a profile saved before the check existed would
512
+ * become unopenable. Here that exemption needs no flag: reopening a profile
513
+ * replays its modules through `reloadModuleFromVfs`, which never reaches
514
+ * this, so only the install entry points below can be refused.
515
+ *
516
+ * Asked after the name is settled, never before: `config.lua` can rename an
517
+ * archive to anything at all, so the file's own name answers nothing.
518
+ */
519
+ crossKindInstallRefusal(name: string, installing: 'package' | 'module'): string | null;
299
520
  /** Names of every package installed in this profile. */
300
521
  getPackageNames(): string[];
301
522
  /** What the module's config.lua declared, with Lua-set overrides applied.
302
523
  * The same contract as getPackageInfo — see manifestInfoBase. This used to
303
- * hand back the whole manifest, so callers saw mudix's own bookkeeping
524
+ * hand back the whole manifest, so callers saw Mudlet Web's own bookkeeping
304
525
  * (installedAt, sourceFile, and latterly declaredInfo itself) alongside the
305
526
  * fields the module author actually wrote. */
306
527
  getModuleInfoRecord(name: string): Record<string, unknown> | null;
@@ -316,30 +537,87 @@ export declare class ScriptingEngine implements EngineHost {
316
537
  readFileBytes(path: string): Uint8Array | null;
317
538
  writeFileBytes(path: string, bytes: Uint8Array): boolean;
318
539
  configDirectory(): string | null;
319
- private createRuntime;
320
540
  /**
321
- * Tear down a script's event-handler registrations. Used when a script is
322
- * removed or transitions enabled→disabled so its handlers stop firing
323
- * before the next full runtime reload.
324
- *
325
- * Runs synchronously once the runtime is up so the store-subscription
326
- * pipeline (script removed → handlers gone) completes inside the same
327
- * tick as the store mutation. See attachToStore for the rationale.
328
- */
329
- unloadScript(scriptId: string): void;
541
+ * The VFS paths a media name may refer to, most likely first. An absolute
542
+ * path is taken as it stands (getMudletHomeDir().."/media/x.wav" is the
543
+ * common package idiom), falling back to reading it as profile-relative
544
+ * for the older "/media/x.wav" spelling. A relative name is looked up in
545
+ * the profile's media/ directory first, where Mudlet resolves it, then
546
+ * against the profile root ("media/x.wav" as packages have long written it).
547
+ */
548
+ private mediaPathCandidates;
549
+ /**
550
+ * The file a media name plays: the first of its {@link mediaPathCandidates}
551
+ * that exists, as a full VFS path. A `*` or `?` in the file name is a
552
+ * wildcard, as in Mudlet's TMedia, and plays one of the files it matches,
553
+ * picked at random. A URL is returned as it is; a name that matches no
554
+ * file is null, which the SoundManager refuses without listing it.
555
+ */
556
+ private resolveMediaPath;
557
+ /** Sound/video loader: absolute URLs hit the network; everything else is
558
+ * read from the mounted profile VFS (see {@link mediaPathCandidates}). */
559
+ private loadMediaBytes;
560
+ private createRuntime;
330
561
  /**
331
562
  * Raise sysInstall / sysInstallPackage. The caller is expected to have
332
563
  * just committed the package's items to the appStore — our subscription
333
564
  * loads the new scripts synchronously inside that commit, so by the time
334
565
  * this method runs the package's event handlers are already registered.
335
566
  */
336
- notifyPackageInstalled(packageName: string, fileName?: string): void;
567
+ notifyPackageInstalled(packageName: string, fileName?: string, problems?: string | null): void;
568
+ /** The half of an install every kind shares: apply the new items, register
569
+ * their fonts and raise the generic sysInstall. The caller raises the one
570
+ * detailed event that fits how the install was asked for. */
571
+ private notifyInstalled;
572
+ /** Everything an install does once its items are in, short of saying so:
573
+ * apply them and register the package's fonts. */
574
+ private finishInstall;
575
+ /** How many times each package or module has been removed — Host's
576
+ * mRemovalsOf. A reload's own removal is not counted: it puts the same
577
+ * module straight back. */
578
+ private readonly removalsOf;
579
+ private countRemoval;
580
+ /** A module whose XML broke before anything was read in has nothing
581
+ * running, so it is not announced (Host's moduleLoadedNothing). */
582
+ private moduleLoadedNothing;
583
+ /**
584
+ * Raise an install's sysInstall and then its detailed event on the next
585
+ * pass of the event loop, as Host::installPackage does (a zero-timer), so
586
+ * everything the install set up - its variables above all - is in place
587
+ * before a handler runs. An install undone before then is not announced,
588
+ * nor is the detailed event raised once a sysInstall handler has taken it
589
+ * away again: its errors would be stale by then.
590
+ */
591
+ private announceInstallLater;
592
+ /** Re-register every installed package's fonts on profile open. See the
593
+ * call site in `start()` and {@link registerPackageFonts}. */
594
+ private refreshInstalledPackageFonts;
595
+ /**
596
+ * Register the fonts one just-installed package ships — desktop's
597
+ * `Host::installPackageFonts` (Host.cpp:3513), called from `installPackage`
598
+ * at :2802. Without it a package's font unpacked correctly and was then
599
+ * never handed to the browser, so nothing in the profile could use it and
600
+ * nothing said why (issue #103).
601
+ *
602
+ * The faces are in `document.fonts` — so `getAvailableFonts()` lists them
603
+ * and `setFont` takes them — before this returns, which is before the
604
+ * caller raises sysInstall/sysInstallPackage: a package that sets its own
605
+ * font in its install handler finds it there, as on desktop (#438). Only
606
+ * the parse finishes later (`FontFace.load()` is async), and a font is not
607
+ * a reason to hold up an install that has otherwise succeeded — desktop's
608
+ * `loadFont` reports and carries on too.
609
+ *
610
+ * Failures reach the error log rather than the console, because a font that
611
+ * will not load is a defect in the package, which is exactly what the
612
+ * Errors tab is for.
613
+ */
614
+ private registerPackageFonts;
337
615
  /**
338
616
  * Raise sysUninstall / sysUninstallPackage. Call this BEFORE removing the
339
617
  * package's items from the store so the package's own handlers (and the
340
618
  * scripts they live in) are still loaded when the event fires.
341
619
  */
342
- notifyPackageUninstalled(packageName: string): void;
620
+ notifyPackageUninstalled(packageName: string, kind?: 'package' | 'module'): void;
343
621
  /**
344
622
  * Stage a File dropped on a window into the profile VFS, then raise Mudlet's
345
623
  * sysDropEvent(filepath, suffix, x, y, name) with the resulting VFS path. The
@@ -407,9 +685,10 @@ export declare class ScriptingEngine implements EngineHost {
407
685
  */
408
686
  private defaultMediaLocation;
409
687
  /**
410
- * Resolve a media `file` (+ optional base `url` directory) to a VFS-relative
411
- * path under `media/` the SoundManager loader can read, downloading and
412
- * caching it on a miss. Shared by MSP (`resolveMspMedia`) and the GMCP media
688
+ * Resolve a media `file` (+ optional base `url` directory) to its absolute
689
+ * VFS path under the profile's `media/` the SoundManager loader can read,
690
+ * downloading and caching it on a miss (raising sysDownloadDone /
691
+ * sysDownloadError for the download, as TMedia does). Shared by MSP (`resolveMspMedia`) and the GMCP media
413
692
  * protocol (`handleClientMedia`). The whole filename — including any
414
693
  * subdirectories — is appended to the base URL and mirrored under `media/`,
415
694
  * so the cache layout matches the server's. `..`/`.` segments are rejected
@@ -419,13 +698,32 @@ export declare class ScriptingEngine implements EngineHost {
419
698
  */
420
699
  private resolveMediaFile;
421
700
  /**
422
- * Handle a GMCP media message (`Client.Media.Play/Load/Stop/Default`).
701
+ * Handle a GMCP media message (`Client.Media.Play/Load/Stop/Pause/Default`).
423
702
  * Mirrors Mudlet's `TMedia` MediaProtocolGMCP. The payload is the already-
424
703
  * parsed JSON body; `action` is the lowercased segment after `Client.Media`
425
- * (defaulting to `play`, matching Mudlet's bare `Client.Media`). Server-
426
- * driven, so playback rides the `game` mute gate like MSP.
704
+ * (a bare `Client.Media` is Default, as in TMedia::parseGMCP). Server-
705
+ * driven, so playback rides the `game` mute gate like MSP, and a Stop or
706
+ * Pause reaches only server media — never what a script started.
427
707
  */
428
708
  private handleClientMedia;
709
+ /** Mudlet's `cTelnet::postMessage`: a client notice on the main console,
710
+ * written to the buffer as well as the renderer so getLines() and the
711
+ * cursor APIs see the line the player reads (see ScriptingAPI.postInfo). */
712
+ private postMainMessage;
713
+ /**
714
+ * A `Client.GUI` offer, after its gmcp table update. Mudlet
715
+ * (cTelnet::setGMCPVariables) ignores the whole message when the profile
716
+ * refuses server packages; otherwise it acts on the install, and then — if
717
+ * the game declined the built-in starter UI with `{"baseui": false}` —
718
+ * raises sysServerGuiInstalled with no package named, the same event an
719
+ * installed interface raises. The install's synchronous part (its
720
+ * "Downloading" notice) runs first, as it does on desktop; the download
721
+ * itself settles later.
722
+ */
723
+ private handleClientGui;
724
+ /** Mudlet's `mAcceptServerGUI` — the profile's `allowMudPackageInstall`,
725
+ * where undefined means allowed. */
726
+ private serverGuiAccepted;
429
727
  private handleClientGuiInstall;
430
728
  /** Guards against overlapping downloads — Mudlet TMap::mImportRunning. */
431
729
  private mapDownloadRunning;
@@ -437,6 +735,43 @@ export declare class ScriptingEngine implements EngineHost {
437
735
  * importer and everything else through the binary `.dat` reader.
438
736
  */
439
737
  downloadMap(remoteUrl?: string): Promise<boolean>;
738
+ /** The script whose body is running right now (Host's mRunningScript): its
739
+ * chunk name and the package or module it came with ('' for the profile). */
740
+ private runningScript;
741
+ /**
742
+ * Which package or module owns the code a Lua frame is running, '' for the
743
+ * profile's own — desktop's packageOfFrame, which addCommand asks so that a
744
+ * package's commands go with it.
745
+ *
746
+ * The running script's own body is its package's. Otherwise the chunk is
747
+ * whoever's items run under that chunk name, or whose folder the file lies
748
+ * in; when several do — a package script and a profile script may share a
749
+ * name — the running script's package still owns it if a main chunk of
750
+ * the same source calls this frame directly (a local function its body
751
+ * called), and anything else is the profile's: code a package merely runs
752
+ * for someone else is theirs.
753
+ */
754
+ packageOfCaller(source: string, isMain: boolean, calledFromMain: boolean): string;
755
+ /**
756
+ * The chunks of a package's items as they are removed, with the package
757
+ * that owned them, until the next pass of the event loop. Desktop takes a
758
+ * removed item out of the tree at once but frees it only once the pass
759
+ * that is running it is over, so code of the item that is still running -
760
+ * a trigger that uninstalled its own package, then placed a command - is
761
+ * still its package's.
762
+ */
763
+ private readonly removedChunkOwners;
764
+ private rememberOwnersOfRemovedItems;
765
+ private ownersOfChunk;
766
+ /**
767
+ * Lua `uninstallPackage(name)`: why it would not, or null once it has
768
+ * (TLuaInterpreter::uninstallPackage). Whether the package is installed is
769
+ * asked first — retrying one that is not never works, so a running save is
770
+ * not the reason to give for it — and an installed one is refused only
771
+ * while a profile save is running. A save a handler starts once the
772
+ * uninstall is under way is no reason to abandon it.
773
+ */
774
+ uninstallPackageFromScript(packageName: string): string | null;
440
775
  /**
441
776
  * Uninstall a previously installed package by name. Raises sysUninstallPackage
442
777
  * before the store removal so the package's own handlers can still run.
@@ -476,6 +811,63 @@ export declare class ScriptingEngine implements EngineHost {
476
811
  * Same batching as toggleTriggerByName: one set() collapses N matches into
477
812
  * a single subscription tick (and a single TimerEngine.loadPerm rebuild).
478
813
  */
814
+ /** Whether a profile save still counts as running: a second save is
815
+ * refused, and so is an uninstall, as Host::currentlySavingProfile()
816
+ * makes them. */
817
+ private profileSaveInFlight;
818
+ /** Which save the scheduled end below belongs to, so the end of one that
819
+ * was waited out cannot cut a later save short. */
820
+ private profileSaveGeneration;
821
+ /**
822
+ * Mark a profile save as running until the next pass of the event loop.
823
+ */
824
+ markProfileSaveInFlight(): void;
825
+ /**
826
+ * Host::waitForProfileSave for a script's install: desktop runs the event
827
+ * loop until the save's writes are done, delivering nothing but the save's
828
+ * own completions. Mudlet Web's save has already written everything by the
829
+ * time it counts as running (the VFS flush that follows only makes it
830
+ * durable, and later writes join it), so what is left to wait for is the
831
+ * window itself - ended here and now, so nothing else gets to run in the
832
+ * middle of the waiting script either.
833
+ */
834
+ private waitOutProfileSave;
835
+ private saveAfterPackageChangePending;
836
+ /**
837
+ * The profile save a package install or uninstall is followed by, put off
838
+ * to the next pass of the event loop (Host's mDeferredSaveTimer) so a batch
839
+ * of installs is not queued behind one save each — and again until no save
840
+ * or install is running, as one started under an install would hold its
841
+ * scripts up. Asked for twice, it saves once.
842
+ *
843
+ * Nothing is waiting on an answer, so a save that cannot be written is said
844
+ * on the console (Host::saveProfileReportingFailure); one turned away
845
+ * because another is running names no file and is let be.
846
+ */
847
+ private saveAfterPackageChange;
848
+ /** Runs a package's config.lua the way Mudlet does — in an environment with
849
+ * nothing in it — so a manifest that would raise is read as raising here
850
+ * too. Bound rather than a method reference so the installer can hold it. */
851
+ private get readPackageConfig();
852
+ /**
853
+ * A crash part-way through an install leaves the folder it was unpacking
854
+ * into, a "mudlet-installing-" one marked ".mudlet-unpacking"; the next
855
+ * install of an archive clears it away (Host::installPackage). One
856
+ * holding files an install had moved out of the way (".mudlet-replaced")
857
+ * is all that is left of them, so it is kept and its place said instead.
858
+ * A package or module that is itself called that is left alone.
859
+ */
860
+ private clearAwayCrashedUnpacking;
861
+ /** Said when a package's XML would not read, once per file that broke,
862
+ * with the reader's reason — Host::installPackage's "Failed to load
863
+ * package/module" warning. Only modules were ever asked whether their
864
+ * contents loaded, so a package installed to silence. */
865
+ private announceUnreadableContents;
866
+ /** What a lost manifest costs, said once, where the player will see it: the
867
+ * package installed under the archive's own name and filed no details, so a
868
+ * name-based uninstall, a repository update and anything depending on it
869
+ * all stop matching with nothing to go on. */
870
+ private announceLostManifest;
479
871
  toggleTimerByName(name: string, enabled: boolean): boolean;
480
872
  /**
481
873
  * Toggle aliases' enabled flag by name (Mudlet enableAlias/disableAlias).
@@ -501,13 +893,12 @@ export declare class ScriptingEngine implements EngineHost {
501
893
  /** The saved keybinding carrying this numeric id, or null. Lets getKeyCode
502
894
  * answer for a permanent key by the id permKey handed back, not just by
503
895
  * name — the key engine only indexes permanent keys by name. */
504
- keyNodeByNumericId(numericId: number): {
505
- key: string;
506
- modifiers: string[];
507
- } | null;
508
- /** enable/disable a script-created temp trigger or alias by numeric id.
509
- * Those live in the Lua runtime, not the saved tree. */
510
- setTempItemEnabled(id: number, enabled: boolean): boolean;
896
+ savedKeyNode(idOrName: number | string): KeyNode | null;
897
+ /** Host::setForceMXPProcessorOn and the slot its signal drives: forcing on a
898
+ * processor that is off turns it on locked to secure mode (IRE-style games
899
+ * use secure tags without sending mode switches), while one the game
900
+ * already had on keeps its mode. Turning it off leaves the modes alone and
901
+ * stops the processor unless the game has MXP on. */
511
902
  setForceMxpProcessorOn(on: boolean): void;
512
903
  /** Mudlet `cTelnet::autoEnableMXPProcessor`. Runs when MXP started from an
513
904
  * in-band `ESC[<n>z` rather than a telnet option-91 handshake: such servers
@@ -522,6 +913,24 @@ export declare class ScriptingEngine implements EngineHost {
522
913
  * server re-announcing MXP after e.g. `config mxp on`): re-apply the lock,
523
914
  * but stay silent — Mudlet returns before its postMessage there. */
524
915
  private autoEnableMxpProcessor;
916
+ /**
917
+ * The temporary half of a kind's one tree, as the lookups below read it, or
918
+ * null for a kind whose temporaries are not yet held this way.
919
+ *
920
+ * Desktop keeps temporary and permanent items of a kind in one tree and
921
+ * one name table, so exists/isActive/findItems/ancestors see both; here
922
+ * the permanent half is the store and the temporary half is the engine.
923
+ */
924
+ private tempTree;
925
+ /**
926
+ * The temporary item a store node hangs off, when that item is killed or
927
+ * already freed — desktop's `*Unit::pendingDeletion`, which walks the item
928
+ * and every ancestor, so a killed parent takes its whole subtree out of
929
+ * exists/isActive/findItems until the cleanup frees it for good.
930
+ */
931
+ private isPendingDeletion;
932
+ /** `list` without the nodes {@link isPendingDeletion} rules out. */
933
+ private liveNodes;
525
934
  existsByName(nameOrId: string | number, type: string): number;
526
935
  /**
527
936
  * Mudlet `isActive(nameOrId, type [, checkAncestors])`. Returns the count of
@@ -538,6 +947,25 @@ export declare class ScriptingEngine implements EngineHost {
538
947
  */
539
948
  isKnownItemType(type: string): boolean;
540
949
  private nodeListForType;
950
+ /**
951
+ * Give every item saved with this profile its number, at load, before a
952
+ * script can run and claim one.
953
+ *
954
+ * {@link numericIdFor} allocates on first sight, which is enough for the Lua
955
+ * API on its own — but "first sight" then depends on what a script happened
956
+ * to touch, and (since the editor's "Show Items' ID number" reads the same
957
+ * ids) on whether the editor had been opened. Desktop has no such ambiguity:
958
+ * `TTrigger`, `TAlias` and the rest take their id as they are constructed
959
+ * from the profile XML, so the profile's own items always hold the first
960
+ * numbers and a temporary one created later carries on from there. Doing the
961
+ * same here costs one pass over the tree and makes the ids people read off
962
+ * the editor the ones they would have got anyway.
963
+ *
964
+ * Same order as the store, which is the order the profile was written in.
965
+ * Groups are numbered too: desktop gives a group an id like any other item,
966
+ * and `ancestors()` already reports them by number.
967
+ */
968
+ private assignSavedItemIds;
541
969
  private nodeTypeLabel;
542
970
  /**
543
971
  * Mudlet `ancestors(id, type)`. Walks from the item's immediate parent up to
@@ -570,18 +998,18 @@ export declare class ScriptingEngine implements EngineHost {
570
998
  * Mudlet `getProfileStats()`. Counts of total/active items per family plus
571
999
  * the trigger pattern tally. Temporary items (tempTimer/tempAlias/... ) are
572
1000
  * not kept in the persisted node tree but live in the per-family engines, so
573
- * their counts come from those engines: `temp` is the live temp count and
574
- * is folded into `total`/`active` (a live temp is always active), matching
575
- * Mudlet's report. Scripts have no temp form. There's no animated-GIF
576
- * tracker, so `gifs` is always zero.
1001
+ * their counts come from those engines: `temp` is the temp count and is
1002
+ * folded into `total`/`active`, matching Mudlet's report. Desktop's
1003
+ * assembleReport walks the live root lists unfiltered, so a killed item —
1004
+ * and whatever hangs under it — still counts until the cleanup frees it.
1005
+ * Scripts have no temp form.
577
1006
  */
578
1007
  getProfileStats(): Record<string, unknown>;
579
1008
  /**
580
1009
  * Mudlet `permScript(name, parent, luaCode)`. Creates a saved Lua script
581
1010
  * named `name` under the script group `parent` (empty = root). Returns the
582
1011
  * new script's id on success, -1 if `parent` is given but no script group
583
- * with that name exists. The store subscription loads the script's
584
- * handlers synchronously inside the addScript commit.
1012
+ * with that name exists. The body runs once, before this returns.
585
1013
  */
586
1014
  createPermScript(name: string, parent: string, code: string): number;
587
1015
  /**
@@ -618,6 +1046,15 @@ export declare class ScriptingEngine implements EngineHost {
618
1046
  */
619
1047
  createPermPromptTrigger(name: string, parent: string, code: string): number;
620
1048
  private createPermTrigger;
1049
+ /**
1050
+ * The parent a `perm*(name, parent, …)` call names: the NEWEST item of the
1051
+ * kind carrying that name, temporary or permanent — desktop's findTrigger /
1052
+ * findFirstAlias / findFirstTimer / findFirstKey look both up in the one
1053
+ * name table, newest first, with no folder and no pending-deletion check.
1054
+ * A temporary parent is answered as its temp parent ref (see
1055
+ * TEMP_PARENT_PREFIX in schema.ts); null when nothing carries the name.
1056
+ */
1057
+ private resolvePermParent;
621
1058
  /**
622
1059
  * Mudlet `tempComplexRegexTrigger(...)`. A session-scoped trigger with
623
1060
  * everything a permanent one can have — an AND chain across lines, a filter
@@ -634,17 +1071,30 @@ export declare class ScriptingEngine implements EngineHost {
634
1071
  */
635
1072
  createTempComplexTrigger(spec: TempComplexTriggerSpec): number;
636
1073
  /**
637
- * Remove a temporary trigger by the numeric id `tempComplexRegexTrigger`
638
- * handed the script. False when that id names no temporary trigger — a
639
- * permanent one is refused, as Mudlet's killTrigger refuses it.
1074
+ * Remove a temporary trigger node — an isTempTrigger="yes" item a package
1075
+ * XML brought in — by its numeric id. False when that id names no such
1076
+ * node — a permanent one is refused, as Mudlet's killTrigger refuses it.
640
1077
  */
641
1078
  removeTemporaryTriggerById(id: number): boolean;
642
1079
  /** Remove a trigger and everything under it. A child outlives its parent
643
1080
  * nowhere in the tree, and a permanent trigger parented to a temporary one
644
1081
  * goes with it — which is the only way it could have been reached. */
645
1082
  private removeTriggerSubtree;
646
- /** The root ancestor of a trigger node — what a lineage is recorded against. */
647
- private rootTriggerOf;
1083
+ /**
1084
+ * Remove from the store the permanent items whose temporary parent has been
1085
+ * freed. Desktop deletes an item's children with it (~TTrigger →
1086
+ * deleteChildren), temporary or not, so the cleanup that frees a killed
1087
+ * temporary item frees the permanent subtree a script hung under it.
1088
+ * The engines report which parents went (takeOrphanedParents); until this
1089
+ * runs nothing reaches those nodes, and the lookups count them as gone.
1090
+ */
1091
+ private removeOrphanedTempChildren;
1092
+ /** Remove every permanent item hanging off a temporary one, of every kind. */
1093
+ private dropTempParentedNodes;
1094
+ /** Remove every store node of `type` hanging (at any depth) off one of the
1095
+ * temp parent refs in `refs`, or off any temp parent at all when `refs`
1096
+ * is null. */
1097
+ private removeSubtreesUnder;
648
1098
  /**
649
1099
  * Mudlet `permAlias(name, parent, regex, luaCode)`. Creates a saved alias
650
1100
  * named `name` under the alias group `parent` (empty = root). The pattern
@@ -655,7 +1105,7 @@ export declare class ScriptingEngine implements EngineHost {
655
1105
  createPermAlias(name: string, parent: string, pattern: string, code: string): number;
656
1106
  /**
657
1107
  * Mudlet `permTimer(name, parent, seconds, luaCode)`. Creates a saved
658
- * one-shot timer under the timer group `parent` (empty = root). Returns
1108
+ * repeating timer under the timer group `parent` (empty = root). Returns
659
1109
  * the new timer id, or -1 if `parent` is non-empty but no timer group of
660
1110
  * that name exists.
661
1111
  */
@@ -670,43 +1120,71 @@ export declare class ScriptingEngine implements EngineHost {
670
1120
  */
671
1121
  createPermKey(name: string, parent: string, modifier: number, key: string | number, code: string): number;
672
1122
  /**
673
- * Mudlet `tempButton(toolbar, name, code[, orientation])`. Appends a
674
- * transient button under an existing toolbar group. Returns the new id, or
675
- * -1 when no toolbar of that name exists. `orientation` is round-tripped
1123
+ * Mudlet `tempButton(toolbar, name, orientation)`. Appends a transient
1124
+ * button under an existing toolbar group, with no command and no script:
1125
+ * TLuaInterpreter::tempButton gives it an empty one, and its third
1126
+ * argument is the orientation. Returns the new id, or a refusal code:
1127
+ * -1 when nothing has the toolbar's name, -2 when what has it is a plain
1128
+ * button (desktop's findAction(toolbar) takes the first action of that
1129
+ * name and refuses one that is not a folder, as a button under a button
1130
+ * never reaches a toolbar), -3 when a button or toolbar already has the
1131
+ * name — desktop refuses a duplicate rather than adding a second button,
1132
+ * so a script that makes its buttons on every load does not pile them up
1133
+ * (mudlet-web#238). The Lua wrapper turns each into nil and desktop's
1134
+ * message. `orientation` is round-tripped
676
1135
  * onto the leaf for parity with Mudlet — the renderer doesn't use it at the
677
1136
  * leaf, but ports that read it back via the store get a stable value.
678
1137
  */
679
- createTempButton(toolbar: string, name: string, code: string, _orientation: number): number;
1138
+ createTempButton(toolbar: string, name: string, _orientation: number): number;
680
1139
  /**
681
1140
  * Mudlet `tempButtonToolbar(name [, orientation [, location]])`. Creates a
682
1141
  * transient toolbar (ButtonNode group). `orientation`: 0=horizontal,
683
1142
  * 1=vertical. `location`: 0=top, 1=bottom, 2=left, 3=right, 4=floating.
684
- * Returns -1 when a toolbar group of that name already exists.
1143
+ * Returns -1 when a toolbar or button of that name already exists —
1144
+ * desktop's findAction(name) is not limited to toolbars.
685
1145
  */
686
1146
  createTempButtonToolbar(name: string, orientation: number, location: number): number;
687
1147
  /** Mudlet `setButtonState(name, state)`. Flips the buttonState on the named
688
1148
  * two-state button. */
1149
+ /**
1150
+ * The button the button-state functions mean: a name finds the first
1151
+ * button (not toolbar) called that, a number is an item ID — the one
1152
+ * findItems() handed out, which Mudlet's getAction(id) resolves whatever
1153
+ * the item is, so a toolbar found by ID is simply not a push-down button.
1154
+ */
1155
+ private findButton;
689
1156
  /**
690
1157
  * What `name` refers to, so the Lua side can say which of Mudlet's several
691
1158
  * refusals applies: no such button, or a button that has no state to set
692
1159
  * because it is not a two-state one.
693
1160
  */
694
- buttonKindByName(name: string): 'missing' | 'plain' | 'pushdown';
1161
+ buttonKindByName(name: string | number): 'missing' | 'plain' | 'pushdown';
695
1162
  /** True when the state actually changed. Mudlet answers false for a button
696
1163
  * that was already in the state asked for — "nothing to do" rather than
697
1164
  * "could not do it", which is why the caller can tell them apart only by
698
1165
  * having checked the name first. */
699
- setButtonStateByName(name: string, state: boolean): boolean;
1166
+ setButtonStateByName(name: string | number, state: boolean): boolean;
700
1167
  /** Mudlet `getButtonState(name)`. Reads the pressed state on a two-state
701
1168
  * button by name. Returns null when no such button (the Lua binding maps
702
- * that to nil — Mudlet returns false/error). */
703
- getButtonStateByName(name: string): boolean | null;
704
- /** Mudlet `setButtonStyleSheet(name, css)`. Stores raw CSS on the
705
- * ButtonNode; ButtonsBar applies it inline. */
1169
+ * that to nil — Mudlet returns false/error). A button whose code will not
1170
+ * compile still answers its state: TLuaInterpreter::getButtonState reads
1171
+ * mButtonState without asking whether the button is active. */
1172
+ getButtonStateByName(name: string | number): boolean | null;
1173
+ /** Mudlet `setButtonStyleSheet(name, css)`. Stores raw CSS on every
1174
+ * button, toolbar and menu with that name — desktop's
1175
+ * `getActionUnit()->findItems(name)` styles each match, and a toolbar's
1176
+ * sheet restyles the whole bar (mudlet-web#356). ButtonsBar applies it. */
706
1177
  setButtonStyleSheetByName(name: string, css: string): boolean;
1178
+ /**
1179
+ * Apply a change to the button bars and lay it out before the script goes
1180
+ * on. A bar along the top or bottom takes its room from the main console,
1181
+ * which getMainWindowSize() and sysWindowResizeEvent report at once on
1182
+ * desktop — and a script holds the event loop a deferred render waits for.
1183
+ */
1184
+ private layOutButtonBarsNow;
707
1185
  /**
708
1186
  * Mudlet `showToolBar(name)` / `hideToolBar(name)`. Toggles the toolbar's
709
- * own enabled flag — the ButtonsBar filters by isEffectivelyEnabled, so that
1187
+ * own enabled flag — the ButtonsBar shows only buttons whose toolbar is on, so that
710
1188
  * is the show/hide hook. Returns null on success, or the reason it moved
711
1189
  * nothing (which the Bridge shapes into Mudlet's `(nil, errMsg)`).
712
1190
  *
@@ -724,9 +1202,8 @@ export declare class ScriptingEngine implements EngineHost {
724
1202
  toggleToolBarByName(name: string, show: boolean): string | null;
725
1203
  /**
726
1204
  * Mudlet `setScript(name, luaCode[, pos])`. Replaces the source of the
727
- * `pos`-th script (1-indexed; default 1) named `name`. Updating via the
728
- * store re-runs the script load through the regular subscription pipeline,
729
- * so handlers re-register cleanly. Returns true on success, -1 if no such
1205
+ * `pos`-th script (1-indexed; default 1) named `name` and runs the new
1206
+ * body once, before this returns. Returns the script's id, -1 if no such
730
1207
  * script exists.
731
1208
  */
732
1209
  setScriptByName(name: string, code: string, pos: number): number;
@@ -765,14 +1242,42 @@ export declare class ScriptingEngine implements EngineHost {
765
1242
  /**
766
1243
  * Force-run a profile script's body right now, even if its code is unchanged.
767
1244
  * Backs the editor's "Run" / "Save & Run" button: the store subscription only
768
- * re-runs a script when its code or handlers actually change (so editing one
1245
+ * re-runs a script when its code actually changes (so editing one
769
1246
  * script doesn't re-execute its untouched siblings), which means clicking
770
1247
  * "Run" twice — or running a script you didn't edit — would otherwise do
771
- * nothing. wrapScript kills the script's previously registered anonymous
772
- * handlers before re-registering, so repeated runs stay idempotent.
1248
+ * nothing. Event-handler registrations live apart from the body (see
1249
+ * applyScriptsFromStore), so repeated runs stay idempotent.
773
1250
  */
774
1251
  runScript(scriptId: string): void;
775
1252
  private runScriptLoad;
1253
+ /**
1254
+ * Bring the runtime up to date with the scripts in the store right now,
1255
+ * and make sure the script `id` has been compiled since `before` (a
1256
+ * {@link scriptCompileSeq} read ahead of the store write): a body runs as
1257
+ * its code is set, whether or not the code changed (TScript::setScript).
1258
+ * The store subscription will usually have run it already. Returns why
1259
+ * that compile failed, or null when it ran. Backs permScript, setScript
1260
+ * and appendScript, which run the body exactly once — through here — and
1261
+ * report its error themselves.
1262
+ */
1263
+ private compileScriptNow;
1264
+ /**
1265
+ * Take in a script-tree change a Lua call just made, now rather than when
1266
+ * the store subscription next runs: it is not attached while a first
1267
+ * profile load compiles the scripts, and a script that a body switches off
1268
+ * then must stop receiving events at once, as one it replaces must not be
1269
+ * compiled again by the walk. A no-op once the subscription has seen it,
1270
+ * and before the profile's scripts were first loaded, which takes in
1271
+ * everything anyway.
1272
+ */
1273
+ private takeInScriptChanges;
1274
+ /** What a script's last compile raised, for permScript/setScript's own
1275
+ * error: the message, or for an error object that is not one, its type
1276
+ * (TLuaInterpreter treats a number as a message, as lua_isstring does). */
1277
+ private describeScriptLoadError;
1278
+ /** Why the script with this numeric id failed the last time its body ran,
1279
+ * or null if it ran cleanly. */
1280
+ scriptLoadErrorById(id: number): string | null;
776
1281
  get currentVFS(): ProfileVFS | null;
777
1282
  /**
778
1283
  * Submit one line from the command bar — Mudlet's `TCommandLine` calling
@@ -786,10 +1291,17 @@ export declare class ScriptingEngine implements EngineHost {
786
1291
  processInput(text: string): boolean;
787
1292
  /** Nesting depth of {@link processInput} — see the note there. */
788
1293
  private aliasDepth;
1294
+ /** Set when a chain hits {@link MAX_ALIAS_DEPTH}, until it unwinds to the outermost pass. */
1295
+ private aliasRunawayStopped;
1296
+ /** AliasUnit::scmMaxProcessingDepth. */
1297
+ private static readonly MAX_ALIAS_DEPTH;
789
1298
  private processInputPass;
790
1299
  /** Process a keyboard event. Returns true if a keybinding consumed it. */
791
1300
  processKey(event: KeyboardEvent): boolean;
792
1301
  raiseEvent(event: string, args?: unknown[]): void;
1302
+ /** Part of {@link EngineHost}: closeMudlet raises the exit event before it
1303
+ * disconnects. */
1304
+ raiseExitEvent(): void;
793
1305
  /** Raise sysExitEvent at most once, while the Lua runtime is still alive. */
794
1306
  private fireExit;
795
1307
  notifyMapOpen(): void;
@@ -799,9 +1311,31 @@ export declare class ScriptingEngine implements EngineHost {
799
1311
  /** Mirror a command-bar edit so Lua's getCmdLine() reads it without waiting
800
1312
  * for a re-render. Called by ProfileSession on every change. */
801
1313
  setCmdLineValue(text: string): void;
1314
+ /** What getCmdLine() answers with — the mirror, which is current the moment
1315
+ * a script writes it, unlike the React state behind it. */
1316
+ getCmdLineValue(): string;
1317
+ /** The game took hidden input (ECHO): what the command bar holds is put
1318
+ * aside as the command line, and the bar becomes the hidden-input box
1319
+ * (see ScriptingAPI.beginHiddenInput). */
1320
+ beginHiddenInput(command: string): void;
1321
+ /** The prompt for hidden input ended. */
1322
+ endHiddenInput(): void;
1323
+ /** The commands packages placed with addCommand, for the bar that draws
1324
+ * them. Narrower than handing out the whole API: the toolbar needs to read
1325
+ * the list, hear when it changes, and report a click. */
1326
+ get addonCommands(): import("../ui/commands/addonCommands").AddonCommandRegistry;
1327
+ addonCommandClicked(id: number): void;
1328
+ /** See ScriptingAPI.interceptMapperButton — the map button, menu entry and
1329
+ * shortcut ask this before toggling the map. */
1330
+ interceptMapperButton(): boolean;
1331
+ mapperButtonMode(): 'default' | 'scripted' | 'disabled';
1332
+ onMapperButtonModeChange(fn: () => void): () => void;
802
1333
  /** Hand the API a startLogging hook. Wired by ProfileSession, which owns
803
1334
  * the actual SessionLogger lifecycle. */
804
1335
  setLoggingToggler(fn: ((enabled: boolean, format: LogFormat) => boolean) | null): void;
1336
+ /** Mudlet `startLogging(state)` from outside a script — the autolog resume
1337
+ * on profile load (Host::startSavedLogging). */
1338
+ startLogging(enabled: boolean): void;
805
1339
  /** Where the live logger is writing, so startLogging can report the file. */
806
1340
  setLoggingPathProvider(fn: (() => string | null) | null): void;
807
1341
  /** Mudlet's "Show errors in main console" — when on, script errors are also
@@ -818,14 +1352,20 @@ export declare class ScriptingEngine implements EngineHost {
818
1352
  * profile VFS at `filePath` (relative paths resolve under the profile root,
819
1353
  * the same convention as `io.open`/`downloadFile`). Returns the absolute
820
1354
  * path written, or an error string (no VFS, mapper not open, or write
821
- * failure). Mudlet requires the mapper open; mudix's renderer lives in the
1355
+ * failure). Mudlet requires the mapper open; Mudlet Web's renderer lives in the
822
1356
  * map widget, so the same precondition applies.
823
1357
  */
824
- exportAreaImageToVfs(areaId: number, filePath: string, zLevel?: number): {
1358
+ exportAreaImageToVfs(areaId: number, filePath: string, zLevel?: number | true): {
825
1359
  path: string;
826
1360
  } | {
827
1361
  error: string;
1362
+ target?: boolean;
828
1363
  };
1364
+ /** exportAreaImage(areaID, filePath, true) — T2DMap::exportAreaToImage's
1365
+ * every-z-level branch: one image per level of the area, each named
1366
+ * `<base>_level_<z>.<suffix>` beside `filePath` (QFileInfo's
1367
+ * completeBaseName and suffix, `png` when there is none). */
1368
+ private exportEveryAreaLevelToVfs;
829
1369
  /**
830
1370
  * Mudlet `resetProfile()` — reload the entire profile as if just reopened:
831
1371
  * clear every UI surface, tear down and recreate the Lua runtime (fresh
@@ -836,8 +1376,12 @@ export declare class ScriptingEngine implements EngineHost {
836
1376
  * exactly why Mudlet warns against calling it from a script-item — defer it
837
1377
  * (`tempTimer(0, resetProfile)` works) or run it from the command line. A
838
1378
  * concurrent reset is coalesced.
1379
+ *
1380
+ * Answers what Host::resetProfile_phase1 does: true once the reset is
1381
+ * armed, false for one already in progress (which it folds into) or one
1382
+ * refused because the profile is closing.
839
1383
  */
840
- resetProfile(): void;
1384
+ resetProfile(): boolean;
841
1385
  private performReset;
842
1386
  /**
843
1387
  * Resolves once the initial load pass has finished (scripts/aliases/timers/
@@ -859,7 +1403,7 @@ export declare class ScriptingEngine implements EngineHost {
859
1403
  /**
860
1404
  * Report a script-initiated call that teardown refused.
861
1405
  *
862
- * Mudlet and mudix reach the same end state by opposite routes, and it is
1406
+ * Mudlet and Mudlet Web reach the same end state by opposite routes, and it is
863
1407
  * worth being precise about which:
864
1408
  *
865
1409
  * Mudlet guards the ARRIVAL side. `TMainConsole::closeEvent` raises
@@ -871,14 +1415,14 @@ export declare class ScriptingEngine implements EngineHost {
871
1415
  * postMessage all early-return. The connection is accepted, then
872
1416
  * discarded.
873
1417
  *
874
- * mudix guards the INITIATION side, because its teardown is synchronous:
1418
+ * Mudlet Web guards the INITIATION side, because its teardown is synchronous:
875
1419
  * destroy() runs start to finish with no yield point, and MudSession
876
1420
  * disposes the client immediately after, so a socket opened here would
877
1421
  * be torn down microseconds later regardless. Refusing up front is the
878
1422
  * same outcome without the pointless socket.
879
1423
  *
880
1424
  * The observable difference is only what the script is told, and there
881
- * mudix is deliberately the more honest of the two: Mudlet returns success
1425
+ * Mudlet Web is deliberately the more honest of the two: Mudlet returns success
882
1426
  * for a connection that will never deliver a byte. Hence this log line —
883
1427
  * the divergence is intentional, but it must not be silent.
884
1428
  *
@@ -896,15 +1440,111 @@ export declare class ScriptingEngine implements EngineHost {
896
1440
  */
897
1441
  private markScriptsLoaded;
898
1442
  destroy(): void;
1443
+ /** Packages and modules whose items are being read in right now — their
1444
+ * scripts are running as part of the install. */
1445
+ private readonly installing;
1446
+ /**
1447
+ * Modules whose XML stopped part-way through (#8696). The items read in
1448
+ * before the break are in the profile and running, but they are only part
1449
+ * of the module, so writing them back out over its file — which is what a
1450
+ * sync does — would destroy the rest. Kept until a read gets all the way
1451
+ * through.
1452
+ */
1453
+ private readonly unloadedModules;
1454
+ private noteModuleLoaded;
1455
+ /** Removals asked of a package or module by its own install-time scripts,
1456
+ * waiting for that install to finish. */
1457
+ private readonly heldRemovals;
1458
+ /**
1459
+ * A package's scripts run while its install is still reading it in, and a
1460
+ * one-shot installer package asks for its own removal from one of them.
1461
+ * Carried out there and then, that took the items out from under the
1462
+ * install (Mudlet #10867 — a crash there, a half-installed package here),
1463
+ * so a removal of the package being installed is held over and carried
1464
+ * out once the install is done: the install is announced, and then the
1465
+ * removal. Removing any OTHER package still happens at once. Answers
1466
+ * whether it was held; asking twice holds it once.
1467
+ */
1468
+ private holdRemovalIfInstalling;
1469
+ /** Carry out the held removals once nothing they name is installing, on the
1470
+ * next pass of the event loop — after the install that held them has
1471
+ * answered and raised its events. On the timer engine, as the postponed
1472
+ * installs are, so a spec's pumpEvents() reaches it. */
1473
+ private carryOutHeldRemovals;
1474
+ /**
1475
+ * Each item's last compile check, per kind: what it was run on (chunk name
1476
+ * and code) and the error, or null. Kept so an item is compiled again only
1477
+ * when its code or name changes, not on every reload of its unit.
1478
+ */
1479
+ private readonly compileChecks;
1480
+ /** Compile errors already put in the error log, by kind, chunk and code —
1481
+ * not by id, so a module re-read under new ids does not report again. */
1482
+ private readonly reportedCompileErrors;
1483
+ /**
1484
+ * The error compiling a piece of an item's code gives (see itemSyntaxError),
1485
+ * or null when it compiles — or when there is no runtime to ask yet, which
1486
+ * lets the item be. A new error is handed to `report`, once.
1487
+ */
1488
+ private compileError;
1489
+ /**
1490
+ * Whether an item's code will not compile — Mudlet's `mOK_code = false`,
1491
+ * or for a trigger's Lua-function pattern `mOK_init = false`. Either way
1492
+ * `Tree::canBeActivated` refuses it: the item never matches or fires, its
1493
+ * command is not sent, and `isActive` reports it off, whatever its switch
1494
+ * says. Fixing the code brings it back as its switch has it.
1495
+ *
1496
+ * The error goes in the error log once: desktop shows it on the item in
1497
+ * the editor, where it is found when the item "does nothing".
1498
+ */
1499
+ private cannotCompile;
1500
+ /** The ids of `items` whose code will not compile (see cannotCompile), for
1501
+ * their engine to leave inactive. Checks for items no longer in the list
1502
+ * are forgotten. */
1503
+ private uncompilableIds;
1504
+ /**
1505
+ * Whether a permanent item of `kind` cannot be active because something in
1506
+ * it failed to compile: its code, or (triggers and aliases) a pattern.
1507
+ */
1508
+ private itemCannotBeActive;
1509
+ /** Scripts and triggers of the package being installed that do not work,
1510
+ * gathered while it installs; null when no install is under way. */
1511
+ private installProblems;
1512
+ /**
1513
+ * Commit a package's (or module's) items with `commit` and answer what in
1514
+ * them does not work, as Host::installPackage does: every script whose body
1515
+ * stopped with an error as it ran, in the order the package lists them,
1516
+ * then every trigger whose body will not even compile — a trigger's body
1517
+ * does not run until its pattern matches, so compiling it is the only way
1518
+ * to find out now. Plain text, joined with "; ", or null when all is well.
1519
+ *
1520
+ * The console is left alone: a script asked for this install and has the
1521
+ * answer to report however it likes, and a module sync would otherwise
1522
+ * repeat it on every save for as long as the module stays broken.
1523
+ */
1524
+ private collectInstallProblems;
1525
+ /**
1526
+ * Why a trigger's patterns do not all compile, in the plain text
1527
+ * XMLimport hands back for TTrigger::setRegexCodeList's error: one
1528
+ * sentence per pattern that failed, numbered by its place in the list, or
1529
+ * null when they all compile (a trigger with no patterns is kept for its
1530
+ * script and is not broken).
1531
+ */
1532
+ private triggerPatternErrors;
899
1533
  private reportEntityError;
900
- private wrapScript;
901
1534
  private executePermAlias;
902
1535
  private executePermTrigger;
1536
+ /** Paint a trigger's built-in highlight over what one match captured, on
1537
+ * the line being processed. Shared by the single-line fire and by each
1538
+ * condition of a multiline trigger as it matches. */
1539
+ private paintHighlight;
903
1540
  private executePermKeybinding;
904
1541
  /**
905
1542
  * Run a button's command + code. The Lua `code` runs on every click.
906
1543
  * For two-state buttons, `nextState=true` (going DOWN) sends `commandDown`,
907
- * otherwise (going UP, or single-state click) sends `command`.
1544
+ * otherwise (going UP) sends `command`. A single-state button sends
1545
+ * `command` — its desktop commandButtonDown, which the importer moves
1546
+ * there. A plain button imported before that still carries the desktop
1547
+ * down command in `commandDown`, and that is the one desktop sends.
908
1548
  */
909
1549
  executeButton(button: ButtonNode, nextState: boolean): void;
910
1550
  /**
@@ -921,6 +1561,13 @@ export declare class ScriptingEngine implements EngineHost {
921
1561
  type: string;
922
1562
  fromServer?: boolean;
923
1563
  }[]): void;
1564
+ /**
1565
+ * Lead the first server group with whatever an unterminated feedTriggers
1566
+ * left held. Mudlet feeds both into the one TBuffer, whose mMudLine the
1567
+ * fed text is still sitting in, so the server's next line completes it
1568
+ * rather than arriving above it.
1569
+ */
1570
+ private withHeldFeedText;
924
1571
  /**
925
1572
  * Free everything killed while this batch was processed.
926
1573
  *
@@ -958,6 +1605,9 @@ export declare class ScriptingEngine implements EngineHost {
958
1605
  * and flushed after all lines in the batch are rendered.
959
1606
  */
960
1607
  private processLineTriggers;
1608
+ /** How many {@link processLineTriggers} passes are running, one inside the
1609
+ * other when a trigger calls feedTriggers. */
1610
+ private lineTriggerDepth;
961
1611
  private emit;
962
1612
  private bridgeEvents;
963
1613
  }