@mudlet/mudlet-web 0.3.0 → 0.4.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 (111) hide show
  1. package/LICENSE +338 -338
  2. package/README.md +158 -150
  3. package/dist-lib/assets/qt-resources/index.d.ts +16 -0
  4. package/dist-lib/branding.d.ts +1 -1
  5. package/dist-lib/hooks/useMudSession.d.ts +1 -1
  6. package/dist-lib/hooks/useOutput.d.ts +3 -1
  7. package/dist-lib/import/defaultPackages.d.ts +35 -2
  8. package/dist-lib/import/defaults/generic_mapper/generic_mapper.mpackage +0 -0
  9. package/dist-lib/import/defaults/gui-drop/gui-drop.mpackage +0 -0
  10. package/dist-lib/import/defaults/{run-lua-code.mpackage → mudlet-base-ui/mudlet-base-ui.mpackage} +0 -0
  11. package/dist-lib/import/defaults/mudlet-mapper.xml +13023 -13023
  12. package/dist-lib/import/defaults/run-lua-code/run-lua-code.mpackage +0 -0
  13. package/dist-lib/import/packageExport.d.ts +71 -0
  14. package/dist-lib/import/packageInstaller.d.ts +4 -1
  15. package/dist-lib/index.js +45910 -39931
  16. package/dist-lib/logging/SessionLogger.d.ts +66 -1
  17. package/dist-lib/map/MapStore.d.ts +32 -4
  18. package/dist-lib/map/mapImageExport.d.ts +1 -1
  19. package/dist-lib/map/mapZoom.d.ts +71 -0
  20. package/dist-lib/mud/ItemIdSequence.d.ts +23 -0
  21. package/dist-lib/mud/MudSession.d.ts +113 -11
  22. package/dist-lib/mud/PatternEngine.d.ts +6 -1
  23. package/dist-lib/mud/commandSplit.d.ts +16 -0
  24. package/dist-lib/mud/connection/LineAssembler.d.ts +56 -0
  25. package/dist-lib/mud/connection/MudClient.d.ts +136 -19
  26. package/dist-lib/mud/connection/TelnetNegotiator.d.ts +39 -0
  27. package/dist-lib/mud/connection/telnetByteTags.d.ts +35 -0
  28. package/dist-lib/mud/events.d.ts +86 -4
  29. package/dist-lib/mud/games/bundledGames.d.ts +29 -0
  30. package/dist-lib/mud/games/gameIcons.d.ts +5 -0
  31. package/dist-lib/mud/keybindings/KeyEngine.d.ts +9 -2
  32. package/dist-lib/mud/keybindings/browserReservedKeys.d.ts +5 -1
  33. package/dist-lib/mud/keybindings/keyEventTarget.d.ts +17 -0
  34. package/dist-lib/mud/keybindings/qtKeys.d.ts +2 -0
  35. package/dist-lib/mud/protocol/charLoginFlow.d.ts +68 -0
  36. package/dist-lib/mud/protocol/charset.d.ts +27 -5
  37. package/dist-lib/mud/protocol/constants.d.ts +1 -0
  38. package/dist-lib/mud/protocol/gmcp.d.ts +19 -3
  39. package/dist-lib/mud/protocol/index.d.ts +1 -1
  40. package/dist-lib/mud/protocol/msspTls.d.ts +54 -0
  41. package/dist-lib/mud/protocol/mxp.d.ts +28 -5
  42. package/dist-lib/mud/protocol/tlsCodes.d.ts +4 -0
  43. package/dist-lib/mud/replay/ReplayPlayer.d.ts +17 -0
  44. package/dist-lib/mud/text/Console.d.ts +64 -0
  45. package/dist-lib/mud/text/FormatState.d.ts +66 -3
  46. package/dist-lib/mud/text/hyperlinkVisibility.d.ts +35 -0
  47. package/dist-lib/mud/text/osc8Docs.d.ts +23 -0
  48. package/dist-lib/mud/text/serverWrap.d.ts +191 -0
  49. package/dist-lib/mud/timers/TimerEngine.d.ts +43 -12
  50. package/dist-lib/mud/triggers/TriggerEngine.d.ts +112 -3
  51. package/dist-lib/scripting/EngineHost.d.ts +56 -3
  52. package/dist-lib/scripting/IScriptingRuntime.d.ts +22 -0
  53. package/dist-lib/scripting/MxpFrameManager.d.ts +118 -0
  54. package/dist-lib/scripting/ScriptingAPI.d.ts +331 -54
  55. package/dist-lib/scripting/ScriptingEngine.d.ts +181 -24
  56. package/dist-lib/scripting/http/HttpService.d.ts +51 -1
  57. package/dist-lib/scripting/lua/LuaRuntime.d.ts +159 -2
  58. package/dist-lib/scripting/lua/bindings/map.d.ts +0 -12
  59. package/dist-lib/scripting/lua/utf8Patterns.d.ts +59 -0
  60. package/dist-lib/storage/index.d.ts +1 -1
  61. package/dist-lib/storage/persistentStorage.d.ts +10 -0
  62. package/dist-lib/storage/schema.d.ts +166 -4
  63. package/dist-lib/styles.css +1 -1
  64. package/dist-lib/ui/BundledGameGrid.d.ts +21 -0
  65. package/dist-lib/ui/CharLoginModal.d.ts +5 -1
  66. package/dist-lib/ui/CommandBar.d.ts +7 -1
  67. package/dist-lib/ui/ConnectionGrid.d.ts +4 -1
  68. package/dist-lib/ui/FileBrowserModal.d.ts +1 -0
  69. package/dist-lib/ui/FilePickerModal.d.ts +3 -3
  70. package/dist-lib/ui/HelpModal.d.ts +11 -0
  71. package/dist-lib/ui/LogBrowserModal.d.ts +4 -1
  72. package/dist-lib/ui/PlayerMarkerPreview.d.ts +20 -0
  73. package/dist-lib/ui/SettingsModal.d.ts +5 -1
  74. package/dist-lib/ui/TlsAlertBanner.d.ts +18 -0
  75. package/dist-lib/ui/TlsCertificateBox.d.ts +20 -0
  76. package/dist-lib/ui/TlsUpgradeModal.d.ts +16 -0
  77. package/dist-lib/ui/Toolbar.d.ts +2 -1
  78. package/dist-lib/ui/VfsPickerModal.d.ts +26 -0
  79. package/dist-lib/ui/components/FileSourceButton.d.ts +47 -0
  80. package/dist-lib/ui/components/index.d.ts +1 -0
  81. package/dist-lib/ui/components/useModalFocus.d.ts +6 -0
  82. package/dist-lib/ui/helpTopics.d.ts +19 -0
  83. package/dist-lib/ui/labels/LabelManager.d.ts +32 -0
  84. package/dist-lib/ui/labels/labelLinks.d.ts +52 -0
  85. package/dist-lib/ui/layout/ScriptWindow.d.ts +11 -2
  86. package/dist-lib/ui/markdown.d.ts +10 -1
  87. package/dist-lib/ui/output/OutputArea.d.ts +11 -1
  88. package/dist-lib/ui/output/OutputContextMenu.d.ts +3 -1
  89. package/dist-lib/ui/output/OutputRenderer.d.ts +7 -1
  90. package/dist-lib/ui/output/OutputSearchBar.d.ts +25 -0
  91. package/dist-lib/ui/output/StickyOutputPanel.d.ts +5 -1
  92. package/dist-lib/ui/output/linkNavigation.d.ts +22 -0
  93. package/dist-lib/ui/output/outputSearch.d.ts +130 -0
  94. package/dist-lib/ui/search/matcher.d.ts +15 -0
  95. package/dist-lib/ui/search/useDebounced.d.ts +3 -0
  96. package/dist-lib/ui/tts/TtsManager.d.ts +3 -0
  97. package/dist-lib/ui/useCommandHistory.d.ts +4 -2
  98. package/dist-lib/ui/useOpenProfiles.d.ts +26 -0
  99. package/dist-lib/ui/video/VideoManager.d.ts +17 -0
  100. package/dist-lib/ui/windows/WindowManager.d.ts +149 -2
  101. package/dist-lib/ui/windows/panels/MapPanel.d.ts +4 -1
  102. package/dist-lib/ui/windows/panels/PackageExportModal.d.ts +18 -0
  103. package/dist-lib/ui/windows/panels/TextPanel.d.ts +3 -1
  104. package/dist-lib/ui/windows/types.d.ts +35 -0
  105. package/dist-lib/utils/describeThrown.d.ts +22 -0
  106. package/dist-lib/utils/fontLoader.d.ts +5 -0
  107. package/dist-lib/vfs-sw.js +131 -131
  108. package/dist-lib/vite.js +22 -0
  109. package/package.json +97 -93
  110. package/dist-lib/scripting/lua/mudlet-lua/generic-mapper/generic_mapper.mpackage +0 -0
  111. package/dist-lib/ui/windows/panels/VfsModulePickerModal.d.ts +0 -8
package/README.md CHANGED
@@ -1,150 +1,158 @@
1
- <div align="center">
2
-
3
- # 🗺️ Mudlet Web
4
-
5
- ### Mudlet in your browser — the full Mudlet Lua scripting stack, running entirely client-side.
6
-
7
- [![React](https://img.shields.io/badge/React-19-61DAFB?logo=react&logoColor=white)](https://react.dev)
8
- [![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
9
- [![Vite](https://img.shields.io/badge/Vite-8-646CFF?logo=vite&logoColor=white)](https://vite.dev)
10
- [![Lua 5.1](https://img.shields.io/badge/Lua-5.1%20(WASM)-000080?logo=lua&logoColor=white)](https://www.lua.org)
11
-
12
- *No installs. No native dependencies. Open a tab and play.*
13
-
14
- </div>
15
-
16
- ---
17
-
18
- ## What is Mudlet Web?
19
-
20
- **Mudlet Web** is a browser edition of [Mudlet](https://www.mudlet.org/), the [MUD](https://en.wikipedia.org/wiki/Multi-user_dungeon) (Multi-User Dungeon) client. It speaks the full telnet protocol stack that modern MUDs use, renders rich ANSI output, and runs **real Lua scripting** compiled to WebAssembly — with an API built for **drop-in compatibility** with Mudlet packages, maps, and profiles.
21
-
22
- If you have Mudlet scripts, triggers, aliases, or maps, Mudlet Web aims to run them unchanged — anywhere you have a web browser. It is not a rewrite of desktop Mudlet and doesn't replace it: some things a native client can do simply aren't available inside a browser sandbox (see [Limitations](#️-limitations--known-constraints)).
23
-
24
- ## ✨ Features
25
-
26
- ### 🔌 Connectivity
27
- - **Direct WebSocket** connections to MUDs that expose a `ws(s)://` endpoint.
28
- - **Telnet via proxy** — connect to any classic `host:port` MUD through the bundled telnet→WebSocket proxy.
29
- - Full protocol support: **GMCP**, **MSDP**, **MSSP**, **MCCP** (compression), **MSP** (sound), telnet **CHARSET**, **TTYPE/MTTS**, and GA/EOR prompt detection.
30
-
31
- ### 📜 Mudlet Lua scripting
32
- - A complete **Lua 5.1** runtime (WASM) with Mudlet-native globals — `send`, `echo`/`cecho`/`decho`/`hecho`, `tempTimer`, `tempTrigger`, `tempAlias`, and [hundreds more](docs/MUDLET_API.md).
33
- - **Triggers, aliases, timers, keybindings, and buttons** organized in Mudlet-style folder trees.
34
- - **PCRE regex** powered by `pcre2-wasm` — the same engine flavour as desktop Mudlet.
35
- - Bundled Mudlet standard library: Geyser GUI toolkit, the generic mapper, string/table utilities, and the `db:*` database API.
36
- - A built-in **CodeMirror** script editor with autocompletion for the entire API surface.
37
-
38
- ### 🗺️ Maps & GUI
39
- - Renders **Mudlet binary maps** with a built-in viewer and editor.
40
- - **Geyser** mini-consoles, labels, gauges, and command lines as pixel-positioned overlays.
41
- - A **custom dock/float window system** — split, tab, dock to any edge, or tear off into free-floating windows.
42
-
43
- ### 💾 Storage & persistence
44
- - A **per-profile virtual filesystem** (ZenFS) backed by IndexedDB — or linked to a **real folder on your disk** via the File System Access API, with two-way sync.
45
- - An in-browser **SQLite** database for the Mudlet `db:*` API.
46
- - **Session logging** to IndexedDB, browsable and exportable.
47
- - Install Mudlet **packages and modules** (`.mpackage` / `.zip` / XML) — including browsing the public Mudlet package repository.
48
-
49
- ### 🎨 Polish
50
- - Multiple themes (dark, light, amber, sky), custom fonts, configurable colors and backgrounds.
51
- - Text-to-speech, sound, and video playback wired to Mudlet's media APIs.
52
-
53
- ## 🚀 Getting Started
54
-
55
- ### Prerequisites
56
- - [Node.js](https://nodejs.org/) 20+ and [yarn](https://yarnpkg.com/).
57
-
58
- ### Run the client
59
-
60
- ```bash
61
- git clone <repo-url> mudlet-web
62
- cd mudlet-web
63
- yarn
64
- yarn dev
65
- ```
66
-
67
- Open the printed local URL (usually `http://localhost:5173`) in your browser, then create a connection on the start screen.
68
-
69
- ### Run the telnet proxy (for `host:port` MUDs)
70
-
71
- The browser cannot open raw TCP sockets, so connecting to a classic telnet MUD needs the small bundled proxy:
72
-
73
- ```bash
74
- cd proxy
75
- yarn
76
- yarn start # listens on ws://localhost:3001 by default
77
- ```
78
-
79
- Then choose **MUD (host:port)** mode when creating a connection. Servers that expose a native WebSocket endpoint don't need the proxy — use **WebSocket** mode and point it straight at the `ws(s)://` URL.
80
-
81
- ## 🛠️ Development
82
-
83
- ```bash
84
- yarn dev # Start the Vite dev server
85
- yarn build # Type-check + production build
86
- yarn preview # Preview the production build
87
- yarn typecheck # Type-check only (src + tests)
88
- yarn test # Run the Vitest suite
89
- yarn test:watch # Vitest in watch mode
90
- ```
91
-
92
- Production builds deploy to **GitHub Pages** automatically on push to `master`.
93
-
94
- ## 🧱 Architecture at a glance
95
-
96
- ```
97
- MudClient (WebSocket — direct or via telnet proxy)
98
- telnet/protocol parsing (GMCP · MSDP · MSSP · MCCP · MSP · CHARSET · TTYPE)
99
- → MudSession.events (a typed EventBus — the spine of the app)
100
- ├─ ScriptingEngine LuaRuntime (WASM) + alias/trigger/timer/key engines
101
- ├─ React UI (output area, dock/float panels, toolbar)
102
- └─ SessionLogger (persists output to IndexedDB)
103
- ```
104
-
105
- | Layer | Lives in | Notes |
106
- |---|---|---|
107
- | **Connection** | `src/mud/connection`, `src/mud/protocol` | Telnet over binary WebSocket frames |
108
- | **Scripting** | `src/scripting` | wasmoon Lua 5.1, Mudlet-native API, bundled Mudlet Lua |
109
- | **UI / Windows** | `src/ui` | Custom dock/float layout, CodeMirror editor, overlays |
110
- | **Maps** | `src/map`, `MapPanel` | Mudlet binary map reader/renderer/editor |
111
- | **Storage** | `src/storage`, `src/scripting/vfs`, `src/db` | Zustand + per-profile VFS + SQLite + IndexedDB logs |
112
- | **Import** | `src/import` | Mudlet XML & package install/export |
113
-
114
- A deeper tour of the internals lives in [`CLAUDE.md`](./CLAUDE.md); the full Mudlet API implementation status is tracked in [`MUDLET_API.md`](docs/MUDLET_API.md).
115
-
116
- ## 🏷️ Branded builds
117
-
118
- Mudlet Web is also published as an npm library for shipping a **white-label client for one specific MUD** your own name, login screen, theme, toolbar, and bundled Lua packages, without forking the repo. See [`docs/BRANDED_BUILDS.md`](docs/BRANDED_BUILDS.md) for the setup guide, or [embervale-web](https://github.com/Delwing/embervale-web) for a live example.
119
-
120
- > The package is published as [`@mudlet/mudlet-web`](https://www.npmjs.com/package/@mudlet/mudlet-web). It was previously `@delwing/mudix` (now deprecated) the app root export was renamed `MudixApp` → `MudletWebApp` with the move.
121
-
122
- ## ⚠️ Limitations & known constraints
123
-
124
- Mudlet Web runs inside a browser sandbox, which trades some of desktop Mudlet's native reach for zero-install portability. Worth knowing before you switch:
125
-
126
- - **Telnet MUDs need the proxy.** Browsers can't open raw TCP sockets, so classic `host:port` MUDs are reachable only through the bundled telnet→WebSocket proxy. Servers with a native `ws(s)://` endpoint connect directly.
127
- - **One profile per tab.** There's no desktop-Mudlet-style multi-profile tabbing *within* a tab — you open one connection per browser tab (`loadProfile()` and in-tab profile switching are no-op stubs). You can still run **different profiles in separate tabs**: each profile is locked to a single tab (opening the same one elsewhere shows a "waiting" screen until the first tab releases it), every profile's data is isolated in its own filesystem, and profiles can signal each other across tabs via Mudlet's `raiseGlobalEvent`.
128
- - **Storage is browser-scoped.** Profiles, scripts, maps, and logs live in the browser's IndexedDB/localStorage for the app's origin. Clearing site data wipes them — unless you **link a real disk folder** for that profile (see below). Different browsers/machines don't share state automatically.
129
- - **Disk-folder linking is Chromium-only.** The "link a folder on disk" feature uses the File System Access API, which Firefox and Safari don't implement. Those browsers fall back to IndexedDB-only storage.
130
- - **Secure context required.** The VFS service worker (which serves profile images/fonts/CSS) needs HTTPS or `localhost`.
131
- - **Clipboard access is best-effort.** The browser's OS clipboard is asynchronous and gated on a user gesture (and a secure context), whereas desktop Mudlet's `getClipboardText`/`setClipboardText` are synchronous. Mudlet Web keeps a session-local text-clipboard mirror as the authoritative value and syncs it to the real OS clipboard opportunistically — so `setClipboardText` may not reach the system clipboard without a user gesture, and `getClipboardText` returns the last value Mudlet Web knows about (an external copy made elsewhere shows up on the *next* call, once the async read completes). This mirror is separate from the rich-text buffer used by `copy()`/`paste()`.
132
- - **Some Mudlet APIs are stubbed or partial.** Anything fundamentally native is bound as a warning-emitting no-op so imported packages still load, but does nothing: **Discord** Rich Presence, **IRC** client, OS `spawn`/subprocess, and the system dictionary. A few synchronous Mudlet calls (`invokeFileDialog`, `getImageSize`) don't map cleanly onto the browser's async pickers/loaders and are still in progress. See [`MUDLET_API.md`](docs/MUDLET_API.md) for the per-function status (✅ / ⚠️ / 🚧 / ❌).
133
- - **The main window is the viewport.** Calls like `setMainWindowSize` are no-ops the browser window is the main window.
134
- - **The Lua↔JS boundary has a per-call cost.** Each crossing between the Lua VM and JS is cheap individually but adds up a script that makes thousands of tiny boundary calls in a tight loop (e.g. iterating every room in a large area one `getRoom*` call at a time) will feel noticeably slower than in native Mudlet. The hot paths you actually hit constantly (line/trigger processing, GMCP) are batched and stay fast; prefer bulk/batched APIs over per-item calls when you can.
135
-
136
- ## 🤝 Contributing
137
-
138
- When adding a Mudlet API function, implement the JS-side method in `ScriptingAPI`, bind the Lua global in `LuaRuntime`, and add the autocomplete entry in `luaCompletions.ts`. Run `yarn typecheck` and `yarn test` before opening a PR.
139
-
140
- ## License
141
-
142
- Mudlet Web is licensed under the **GNU General Public License, version 2 or (at your option) any later version** — see [LICENSE](./LICENSE). This matches [Mudlet](https://github.com/Mudlet/Mudlet)'s license (GPL-2.0-or-later): Mudlet Web bundles Mudlet's Lua runtime files (`src/scripting/lua/mudlet-lua/` — LuaGlobal, Geyser, the generic mapper, DB utilities, and more), copyright the Mudlet contributors, so every distribution is a combined work with that GPL code.
143
-
144
- Lua scripts and packages that Mudlet Web merely *runs* (your profile scripts, installed `.mpackage`s, branded-build packages) are separate works and are not required to be GPL the same way the Mudlet ecosystem treats its packages.
145
-
146
- Mudlet Web also bundles **Bitstream Vera Sans Mono** (`src/assets/fonts/bitstream-vera-sans-mono/`) as the default console output and command-line font, matching Mudlet's own default console typeface. It's under the separate, permissive [Bitstream Vera license](src/assets/fonts/bitstream-vera-sans-mono/COPYRIGHT.TXT) (free redistribution and embedding; just don't sell the font standalone or rename a modified copy while keeping "Bitstream"/"Vera" in the name).
147
-
148
- <div align="center">
149
- <sub>Built with React, TypeScript, and a lot of WebAssembly. Happy MUDding. 🐉</sub>
150
- </div>
1
+ <div align="center">
2
+
3
+ # 🗺️ Mudlet Web
4
+
5
+ ### Mudlet in your browser — the full Mudlet Lua scripting stack, running entirely client-side.
6
+
7
+ [![React](https://img.shields.io/badge/React-19-61DAFB?logo=react&logoColor=white)](https://react.dev)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
9
+ [![Vite](https://img.shields.io/badge/Vite-8-646CFF?logo=vite&logoColor=white)](https://vite.dev)
10
+ [![Lua 5.1](https://img.shields.io/badge/Lua-5.1%20(WASM)-000080?logo=lua&logoColor=white)](https://www.lua.org)
11
+
12
+ *No installs. No native dependencies. Open a tab and play.*
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ ## What is Mudlet Web?
19
+
20
+ **Mudlet Web** is a browser edition of [Mudlet](https://www.mudlet.org/), the [MUD](https://en.wikipedia.org/wiki/Multi-user_dungeon) (Multi-User Dungeon) client. It speaks the full telnet protocol stack that modern MUDs use, renders rich ANSI output, and runs **real Lua scripting** compiled to WebAssembly — with an API built for **drop-in compatibility** with Mudlet packages, maps, and profiles.
21
+
22
+ If you have Mudlet scripts, triggers, aliases, or maps, Mudlet Web aims to run them unchanged — anywhere you have a web browser. It is not a rewrite of desktop Mudlet and doesn't replace it: some things a native client can do simply aren't available inside a browser sandbox (see [Limitations](#️-limitations--known-constraints)).
23
+
24
+ ## ✨ Features
25
+
26
+ ### 🔌 Connectivity
27
+ - **Direct WebSocket** connections to MUDs that expose a `ws(s)://` endpoint.
28
+ - **Telnet via proxy** — connect to any classic `host:port` MUD through the bundled telnet→WebSocket proxy.
29
+ - Full protocol support: **GMCP**, **MSDP**, **MSSP**, **MCCP** (compression), **MSP** (sound), telnet **CHARSET**, **TTYPE/MTTS**, and GA/EOR prompt detection.
30
+
31
+ ### 📜 Mudlet Lua scripting
32
+ - A complete **Lua 5.1** runtime (WASM) with Mudlet-native globals — `send`, `echo`/`cecho`/`decho`/`hecho`, `tempTimer`, `tempTrigger`, `tempAlias`, and [hundreds more](docs/MUDLET_API.md).
33
+ - **Triggers, aliases, timers, keybindings, and buttons** organized in Mudlet-style folder trees.
34
+ - **PCRE regex** powered by `pcre2-wasm` — the same engine flavour as desktop Mudlet.
35
+ - Bundled Mudlet standard library: Geyser GUI toolkit, the generic mapper, string/table utilities, and the `db:*` database API.
36
+ - A built-in **CodeMirror** script editor with autocompletion for the entire API surface.
37
+
38
+ ### 🗺️ Maps & GUI
39
+ - Renders **Mudlet binary maps** with a built-in viewer and editor.
40
+ - **Geyser** mini-consoles, labels, gauges, and command lines as pixel-positioned overlays.
41
+ - A **custom dock/float window system** — split, tab, dock to any edge, or tear off into free-floating windows.
42
+
43
+ ### 💾 Storage & persistence
44
+ - A **per-profile virtual filesystem** (ZenFS) backed by IndexedDB — or linked to a **real folder on your disk** via the File System Access API, with two-way sync.
45
+ - An in-browser **SQLite** database for the Mudlet `db:*` API.
46
+ - **Session logging** to IndexedDB, browsable and exportable.
47
+ - Install Mudlet **packages and modules** (`.mpackage` / `.zip` / XML) — including browsing the public Mudlet package repository.
48
+
49
+ ### 🎨 Polish
50
+ - Multiple themes (dark, light, amber, sky), custom fonts, configurable colors and backgrounds.
51
+ - Text-to-speech, sound, and video playback wired to Mudlet's media APIs.
52
+
53
+ ## 🚀 Getting Started
54
+
55
+ ### Prerequisites
56
+ - [Node.js](https://nodejs.org/) 20+ and [yarn](https://yarnpkg.com/).
57
+
58
+ ### Run the client
59
+
60
+ ```bash
61
+ git clone <repo-url> mudlet-web
62
+ cd mudlet-web
63
+ yarn
64
+ yarn dev
65
+ ```
66
+
67
+ Open the printed local URL (usually `http://localhost:5173`) in your browser, then create a connection on the start screen.
68
+
69
+ ### Run the telnet proxy (for `host:port` MUDs)
70
+
71
+ The browser cannot open raw TCP sockets, so connecting to a classic telnet MUD needs the small bundled proxy:
72
+
73
+ ```bash
74
+ cd proxy
75
+ yarn
76
+ yarn start # listens on ws://localhost:3001 by default
77
+ ```
78
+
79
+ Then choose **MUD (host:port)** mode when creating a connection. Servers that expose a native WebSocket endpoint don't need the proxy — use **WebSocket** mode and point it straight at the `ws(s)://` URL.
80
+
81
+ ### Already play in desktop Mudlet?
82
+
83
+ Your existing profile comes across whole — triggers, aliases, scripts, keybindings, buttons, saved variables, installed packages, your map, and your colours and fonts. **[docs/help/migrating.md](docs/help/migrating.md)** walks through it: where the profile folder lives on each platform, the three ways in (copy a folder, import a `.zip`, or link the folder and keep using both), what doesn't come across, and how to export back to desktop Mudlet.
84
+
85
+ The same guide plus [connecting](docs/help/connecting.md), [storage and backups](docs/help/storage.md), and [browsers and limits](docs/help/browsers.md) — is built into the app behind the **Help** button, on the start screen and in the toolbar.
86
+
87
+ ## 🛠️ Development
88
+
89
+ ```bash
90
+ yarn dev # Start the Vite dev server
91
+ yarn build # Type-check + production build
92
+ yarn preview # Preview the production build
93
+ yarn typecheck # Type-check only (src + tests)
94
+ yarn test # Run the Vitest suite
95
+ yarn test:watch # Vitest in watch mode
96
+ ```
97
+
98
+ Production builds deploy to **GitHub Pages** automatically on push to `master`.
99
+
100
+ ## 🧱 Architecture at a glance
101
+
102
+ ```
103
+ MudClient (WebSocket — direct or via telnet proxy)
104
+ → telnet/protocol parsing (GMCP · MSDP · MSSP · MCCP · MSP · CHARSET · TTYPE)
105
+ MudSession.events (a typed EventBus the spine of the app)
106
+ ├─ ScriptingEngine → LuaRuntime (WASM) + alias/trigger/timer/key engines
107
+ ├─ React UI (output area, dock/float panels, toolbar)
108
+ └─ SessionLogger (persists output to IndexedDB)
109
+ ```
110
+
111
+ | Layer | Lives in | Notes |
112
+ |---|---|---|
113
+ | **Connection** | `src/mud/connection`, `src/mud/protocol` | Telnet over binary WebSocket frames |
114
+ | **Scripting** | `src/scripting` | wasmoon Lua 5.1, Mudlet-native API, bundled Mudlet Lua |
115
+ | **UI / Windows** | `src/ui` | Custom dock/float layout, CodeMirror editor, overlays |
116
+ | **Maps** | `src/map`, `MapPanel` | Mudlet binary map reader/renderer/editor |
117
+ | **Storage** | `src/storage`, `src/scripting/vfs`, `src/db` | Zustand + per-profile VFS + SQLite + IndexedDB logs |
118
+ | **Import** | `src/import` | Mudlet XML & package install/export |
119
+
120
+ A deeper tour of the internals lives in [`CLAUDE.md`](./CLAUDE.md); the full Mudlet API implementation status is tracked in [`MUDLET_API.md`](docs/MUDLET_API.md).
121
+
122
+ ## 🏷️ Branded builds
123
+
124
+ Mudlet Web is also published as an npm library for shipping a **white-label client for one specific MUD** — your own name, login screen, theme, toolbar, and bundled Lua packages, without forking the repo. See [`docs/BRANDED_BUILDS.md`](docs/BRANDED_BUILDS.md) for the setup guide, or [embervale-web](https://github.com/Delwing/embervale-web) for a live example.
125
+
126
+ > The package is published as [`@mudlet/mudlet-web`](https://www.npmjs.com/package/@mudlet/mudlet-web). It was previously `@delwing/mudix` (now deprecated) the app root export was renamed `MudixApp` `MudletWebApp` with the move.
127
+
128
+ ## ⚠️ Limitations & known constraints
129
+
130
+ Mudlet Web runs inside a browser sandbox, which trades some of desktop Mudlet's native reach for zero-install portability. Worth knowing before you switch:
131
+
132
+ - **Telnet MUDs need the proxy.** Browsers can't open raw TCP sockets, so classic `host:port` MUDs are reachable only through the bundled telnet→WebSocket proxy. Servers with a native `ws(s)://` endpoint connect directly.
133
+ - **One profile per tab.** There's no desktop-Mudlet-style multi-profile tabbing *within* a tab — you open one connection per browser tab (`loadProfile()` and in-tab profile switching are no-op stubs). You can still run **different profiles in separate tabs**: each profile is locked to a single tab (opening the same one elsewhere shows a "waiting" screen until the first tab releases it), every profile's data is isolated in its own filesystem, and profiles can signal each other across tabs via Mudlet's `raiseGlobalEvent`.
134
+ - **Storage is browser-scoped.** Profiles, scripts, maps, and logs live in the browser's IndexedDB/localStorage for the app's origin. Clearing site data wipes them unless you **link a real disk folder** for that profile (see below). Different browsers/machines don't share state automatically.
135
+ - **Disk-folder linking is Chromium-only.** The "link a folder on disk" feature uses the File System Access API, which Firefox and Safari don't implement. Those browsers fall back to IndexedDB-only storage.
136
+ - **Secure context required.** The VFS service worker (which serves profile images/fonts/CSS) needs HTTPS or `localhost`.
137
+ - **Clipboard access is best-effort.** The browser's OS clipboard is asynchronous and gated on a user gesture (and a secure context), whereas desktop Mudlet's `getClipboardText`/`setClipboardText` are synchronous. Mudlet Web keeps a session-local text-clipboard mirror as the authoritative value and syncs it to the real OS clipboard opportunistically — so `setClipboardText` may not reach the system clipboard without a user gesture, and `getClipboardText` returns the last value Mudlet Web knows about (an external copy made elsewhere shows up on the *next* call, once the async read completes). This mirror is separate from the rich-text buffer used by `copy()`/`paste()`.
138
+ - **Some Mudlet APIs are stubbed or partial.** Anything fundamentally native is bound as a warning-emitting no-op so imported packages still load, but does nothing: **Discord** Rich Presence, **IRC** client, OS `spawn`/subprocess, and the system dictionary. A few synchronous Mudlet calls (`invokeFileDialog`, `getImageSize`) don't map cleanly onto the browser's async pickers/loaders and are still in progress. See [`MUDLET_API.md`](docs/MUDLET_API.md) for the per-function status (✅ / ⚠️ / 🚧 / ❌).
139
+ - **The main window is the viewport.** Calls like `setMainWindowSize` are no-ops — the browser window is the main window.
140
+ - **The Lua↔JS boundary has a per-call cost.** Each crossing between the Lua VM and JS is cheap individually but adds up — a script that makes thousands of tiny boundary calls in a tight loop (e.g. iterating every room in a large area one `getRoom*` call at a time) will feel noticeably slower than in native Mudlet. The hot paths you actually hit constantly (line/trigger processing, GMCP) are batched and stay fast; prefer bulk/batched APIs over per-item calls when you can.
141
+
142
+ ## 🤝 Contributing
143
+
144
+ When adding a Mudlet API function, implement the JS-side method in `ScriptingAPI`, bind the Lua global in `LuaRuntime`, and add the autocomplete entry in `luaCompletions.ts`. Run `yarn typecheck` and `yarn test` before opening a PR.
145
+
146
+ ## License
147
+
148
+ Mudlet Web is licensed under the **GNU General Public License, version 2 or (at your option) any later version** — see [LICENSE](./LICENSE). This matches [Mudlet](https://github.com/Mudlet/Mudlet)'s license (GPL-2.0-or-later): Mudlet Web bundles Mudlet's Lua runtime files (`src/scripting/lua/mudlet-lua/` — LuaGlobal, Geyser, the generic mapper, DB utilities, and more), copyright the Mudlet contributors, so every distribution is a combined work with that GPL code.
149
+
150
+ Lua scripts and packages that Mudlet Web merely *runs* (your profile scripts, installed `.mpackage`s, branded-build packages) are separate works and are not required to be GPL — the same way the Mudlet ecosystem treats its packages.
151
+
152
+ Mudlet Web also bundles **Bitstream Vera Sans Mono** (`src/assets/fonts/bitstream-vera-sans-mono/`) as the default console output and command-line font, matching Mudlet's own default console typeface. It's under the separate, permissive [Bitstream Vera license](src/assets/fonts/bitstream-vera-sans-mono/COPYRIGHT.TXT) (free redistribution and embedding; just don't sell the font standalone or rename a modified copy while keeping "Bitstream"/"Vera" in the name).
153
+
154
+ **Ubuntu** and **Ubuntu Mono** (`src/assets/fonts/ubuntu/`, regular/bold/italic/bold-italic each) are bundled for the same reason: Mudlet registers both families into Qt's font database at startup, so packages written for Mudlet — including the bundled `mudlet-base-ui` — ask for them by name for labels, headers, and miniconsole stylesheets. They're under the [Ubuntu Font Licence 1.0](src/assets/fonts/ubuntu/LICENCE.txt) (free redistribution and embedding; modified copies must be renamed).
155
+
156
+ <div align="center">
157
+ <sub>Built with React, TypeScript, and a lot of WebAssembly. Happy MUDding. 🐉</sub>
158
+ </div>
@@ -0,0 +1,16 @@
1
+ /** Whether `path` uses Qt's resource syntax. Mudlet also accepts the `qrc:///`
2
+ * URL form for the same bundle. */
3
+ export declare function isQtResourcePath(path: string): boolean;
4
+ /**
5
+ * A `:/…` path as a URL usable anywhere a browser wants one — an `<img>` src, a
6
+ * CSS `url(...)`, a background image. Null when the resource isn't vendored.
7
+ */
8
+ export declare function qtResourceUrl(path: string): string | null;
9
+ /**
10
+ * The bytes behind a `:/…` path, decoded synchronously. Null when the resource
11
+ * isn't vendored, or the entry isn't a base64 data URI (which would mean the
12
+ * bundler emitted it as a file instead of inlining it).
13
+ */
14
+ export declare function qtResourceBytes(path: string): Uint8Array | null;
15
+ /** Every vendored resource path, in Qt form. For diagnostics and tests. */
16
+ export declare function qtResourcePaths(): string[];
@@ -70,7 +70,7 @@ export declare const STOCK_THEMES: {
70
70
  /** Ids of the stock toolbar buttons, for `BrandToolbarConfig.hide`.
71
71
  * `connection` is the Reconnect/Disconnect pair; `close` closes the profile;
72
72
  * `record` toggles Mudlet-format replay recording. */
73
- export type StockToolbarButton = 'scripts' | 'files' | 'map' | 'logs' | 'docs' | 'reportBug' | 'settings' | 'record' | 'connection' | 'close';
73
+ export type StockToolbarButton = 'scripts' | 'files' | 'map' | 'logs' | 'docs' | 'help' | 'reportBug' | 'settings' | 'record' | 'connection' | 'close';
74
74
  /** What a brand toolbar button can do when clicked. */
75
75
  export interface BrandToolbarContext {
76
76
  connectionId: string;
@@ -6,5 +6,5 @@ export declare function useMudSession(options?: MudSessionOptions): {
6
6
  passwordMode: boolean;
7
7
  connect: (url: string) => void;
8
8
  disconnect: () => void;
9
- send: (text: string, echo?: boolean) => void;
9
+ send: (text: string, echo?: boolean, isGameCommand?: boolean) => void;
10
10
  };
@@ -5,6 +5,8 @@ export interface UseStickyOutputOptions {
5
5
  maxElements?: number;
6
6
  splitViewThreshold?: number;
7
7
  showTimestamps?: boolean;
8
+ /** See OutputHandlerOptions.followTail. Omit for a normal scrollback. */
9
+ followTail?: () => boolean;
8
10
  }
9
11
  export interface UseStickyOutputResult {
10
12
  outputRef: React.RefObject<HTMLDivElement | null>;
@@ -14,4 +16,4 @@ export interface UseStickyOutputResult {
14
16
  scrollToBottom: () => void;
15
17
  controls: OutputRendererControls | null;
16
18
  }
17
- export declare function useStickyOutput(source: MessageSource | null, { stickyLines, maxElements, splitViewThreshold, showTimestamps, }?: UseStickyOutputOptions): UseStickyOutputResult;
19
+ export declare function useStickyOutput(source: MessageSource | null, { stickyLines, maxElements, splitViewThreshold, showTimestamps, followTail, }?: UseStickyOutputOptions): UseStickyOutputResult;
@@ -15,6 +15,13 @@ interface DefaultPackage {
15
15
  * `mudlet-mapper.xml` row of `defaultScripts` in mudlet.cpp.
16
16
  */
17
17
  export declare const IRE_MAPPER_GAMES: string[];
18
+ /**
19
+ * Games whose own bundled loader installs a full interface, so the starter UI is
20
+ * not preinstalled for them — it would only fight the game's GUI for the same
21
+ * screen space. Mirrors the `providesOwnUi` entries of `TGameDetails.h`'s
22
+ * `scmDefaultGames`, including each game's `alternateHostUrls`.
23
+ */
24
+ export declare const GAMES_WITH_OWN_UI: string[];
18
25
  /** Every bundled default, whatever the host — for tests and tooling. */
19
26
  export declare const ALL_DEFAULTS: DefaultPackage[];
20
27
  /**
@@ -29,8 +36,32 @@ export declare const ALL_DEFAULTS: DefaultPackage[];
29
36
  * Exactly one mapper, always. `centerview()` is the only thing that moves the
30
37
  * map view and only a mapper calls it, so a profile with no mapper never follows
31
38
  * the player — and two mappers would both fire on the same movement.
39
+ *
40
+ * The starter UI is the one conditional pick: Mudlet skips it for players who
41
+ * aren't new (`experiencedMudletPlayer()` — any profile folder older than six
42
+ * months) because "veterans will have their own layouts already", and for games
43
+ * whose own loader installs a full interface. mudix has no profile-age signal to
44
+ * mirror the first, so `createdAt` stands in for it — see {@link isNewProfile}.
45
+ */
46
+ export declare function stockDefaults(host?: string, conn?: {
47
+ createdAt?: string;
48
+ }): DefaultPackage[];
49
+ /**
50
+ * Whether a profile is new enough to be offered the starter UI.
51
+ *
52
+ * `addConnection` stamps `createdAt` on every profile it creates, so a profile
53
+ * without one was made before the field existed — i.e. someone has already been
54
+ * using it, quite possibly with a layout of their own. That's mudix's stand-in
55
+ * for Mudlet's "no profile folder older than six months" check: it errs the safe
56
+ * way, since dropping a dock, gauges and a chat window onto an established
57
+ * profile is far more disruptive than withholding them from a new one.
58
+ *
59
+ * No profile at all (tooling, a preview, `stockDefaults()` with no argument)
60
+ * counts as new — nothing established is at risk.
32
61
  */
33
- export declare function stockDefaults(host?: string): DefaultPackage[];
62
+ export declare function isNewProfile(conn?: {
63
+ createdAt?: string;
64
+ }): boolean;
34
65
  /** A default or brand package as the install loop sees it. */
35
66
  export type InstallablePackage = DefaultPackage & {
36
67
  removable?: boolean;
@@ -42,7 +73,9 @@ export type InstallablePackage = DefaultPackage & {
42
73
  * them — `[]` preinstalls nothing, and a brand shipping its own mapper simply
43
74
  * doesn't list ours. Unset means no opinion: the stock defaults for this game.
44
75
  */
45
- export declare function resolveDefaultPackages(brandPackages: InstallablePackage[] | undefined, host?: string): InstallablePackage[];
76
+ export declare function resolveDefaultPackages(brandPackages: InstallablePackage[] | undefined, host?: string, conn?: {
77
+ createdAt?: string;
78
+ }): InstallablePackage[];
46
79
  /** Hostname `stockDefaults` matches its game lists against, lowercased. */
47
80
  export declare function connectionHost(conn: {
48
81
  mode?: string;