@mudlet/mudlet-web 0.3.1 → 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 (97) hide show
  1. package/README.md +8 -0
  2. package/dist-lib/assets/qt-resources/index.d.ts +16 -0
  3. package/dist-lib/branding.d.ts +1 -1
  4. package/dist-lib/hooks/useMudSession.d.ts +1 -1
  5. package/dist-lib/hooks/useOutput.d.ts +3 -1
  6. package/dist-lib/import/defaultPackages.d.ts +35 -2
  7. package/dist-lib/import/defaults/generic_mapper/generic_mapper.mpackage +0 -0
  8. package/dist-lib/import/defaults/gui-drop/gui-drop.mpackage +0 -0
  9. package/dist-lib/import/defaults/{run-lua-code.mpackage → mudlet-base-ui/mudlet-base-ui.mpackage} +0 -0
  10. package/dist-lib/import/defaults/run-lua-code/run-lua-code.mpackage +0 -0
  11. package/dist-lib/import/packageExport.d.ts +71 -0
  12. package/dist-lib/import/packageInstaller.d.ts +4 -1
  13. package/dist-lib/index.js +45935 -40688
  14. package/dist-lib/logging/SessionLogger.d.ts +66 -1
  15. package/dist-lib/map/MapStore.d.ts +32 -4
  16. package/dist-lib/map/mapImageExport.d.ts +1 -1
  17. package/dist-lib/map/mapZoom.d.ts +71 -0
  18. package/dist-lib/mud/ItemIdSequence.d.ts +23 -0
  19. package/dist-lib/mud/MudSession.d.ts +113 -11
  20. package/dist-lib/mud/PatternEngine.d.ts +6 -1
  21. package/dist-lib/mud/commandSplit.d.ts +16 -0
  22. package/dist-lib/mud/connection/LineAssembler.d.ts +56 -0
  23. package/dist-lib/mud/connection/MudClient.d.ts +120 -16
  24. package/dist-lib/mud/connection/TelnetNegotiator.d.ts +39 -0
  25. package/dist-lib/mud/connection/telnetByteTags.d.ts +35 -0
  26. package/dist-lib/mud/events.d.ts +19 -4
  27. package/dist-lib/mud/games/bundledGames.d.ts +29 -0
  28. package/dist-lib/mud/games/gameIcons.d.ts +5 -0
  29. package/dist-lib/mud/keybindings/KeyEngine.d.ts +9 -2
  30. package/dist-lib/mud/keybindings/browserReservedKeys.d.ts +5 -1
  31. package/dist-lib/mud/keybindings/keyEventTarget.d.ts +17 -0
  32. package/dist-lib/mud/keybindings/qtKeys.d.ts +2 -0
  33. package/dist-lib/mud/protocol/charLoginFlow.d.ts +68 -0
  34. package/dist-lib/mud/protocol/charset.d.ts +27 -5
  35. package/dist-lib/mud/protocol/constants.d.ts +1 -0
  36. package/dist-lib/mud/protocol/gmcp.d.ts +19 -3
  37. package/dist-lib/mud/protocol/index.d.ts +1 -1
  38. package/dist-lib/mud/protocol/mxp.d.ts +28 -5
  39. package/dist-lib/mud/replay/ReplayPlayer.d.ts +17 -0
  40. package/dist-lib/mud/text/Console.d.ts +64 -0
  41. package/dist-lib/mud/text/FormatState.d.ts +66 -3
  42. package/dist-lib/mud/text/hyperlinkVisibility.d.ts +35 -0
  43. package/dist-lib/mud/text/osc8Docs.d.ts +23 -0
  44. package/dist-lib/mud/text/serverWrap.d.ts +191 -0
  45. package/dist-lib/mud/timers/TimerEngine.d.ts +43 -12
  46. package/dist-lib/mud/triggers/TriggerEngine.d.ts +112 -3
  47. package/dist-lib/scripting/EngineHost.d.ts +56 -3
  48. package/dist-lib/scripting/IScriptingRuntime.d.ts +16 -0
  49. package/dist-lib/scripting/MxpFrameManager.d.ts +118 -0
  50. package/dist-lib/scripting/ScriptingAPI.d.ts +323 -54
  51. package/dist-lib/scripting/ScriptingEngine.d.ts +180 -23
  52. package/dist-lib/scripting/http/HttpService.d.ts +51 -1
  53. package/dist-lib/scripting/lua/LuaRuntime.d.ts +151 -2
  54. package/dist-lib/scripting/lua/bindings/map.d.ts +0 -12
  55. package/dist-lib/scripting/lua/utf8Patterns.d.ts +59 -0
  56. package/dist-lib/storage/index.d.ts +1 -1
  57. package/dist-lib/storage/persistentStorage.d.ts +10 -0
  58. package/dist-lib/storage/schema.d.ts +116 -0
  59. package/dist-lib/styles.css +1 -1
  60. package/dist-lib/ui/BundledGameGrid.d.ts +21 -0
  61. package/dist-lib/ui/CommandBar.d.ts +7 -1
  62. package/dist-lib/ui/ConnectionGrid.d.ts +4 -1
  63. package/dist-lib/ui/FilePickerModal.d.ts +3 -3
  64. package/dist-lib/ui/HelpModal.d.ts +11 -0
  65. package/dist-lib/ui/LogBrowserModal.d.ts +4 -1
  66. package/dist-lib/ui/PlayerMarkerPreview.d.ts +20 -0
  67. package/dist-lib/ui/Toolbar.d.ts +2 -1
  68. package/dist-lib/ui/VfsPickerModal.d.ts +26 -0
  69. package/dist-lib/ui/components/FileSourceButton.d.ts +47 -0
  70. package/dist-lib/ui/components/index.d.ts +1 -0
  71. package/dist-lib/ui/helpTopics.d.ts +19 -0
  72. package/dist-lib/ui/labels/LabelManager.d.ts +7 -0
  73. package/dist-lib/ui/layout/ScriptWindow.d.ts +11 -2
  74. package/dist-lib/ui/markdown.d.ts +10 -1
  75. package/dist-lib/ui/output/OutputArea.d.ts +11 -1
  76. package/dist-lib/ui/output/OutputContextMenu.d.ts +3 -1
  77. package/dist-lib/ui/output/OutputRenderer.d.ts +7 -1
  78. package/dist-lib/ui/output/OutputSearchBar.d.ts +25 -0
  79. package/dist-lib/ui/output/StickyOutputPanel.d.ts +5 -1
  80. package/dist-lib/ui/output/outputSearch.d.ts +130 -0
  81. package/dist-lib/ui/search/matcher.d.ts +15 -0
  82. package/dist-lib/ui/search/useDebounced.d.ts +3 -0
  83. package/dist-lib/ui/tts/TtsManager.d.ts +3 -0
  84. package/dist-lib/ui/useCommandHistory.d.ts +4 -2
  85. package/dist-lib/ui/useOpenProfiles.d.ts +26 -0
  86. package/dist-lib/ui/video/VideoManager.d.ts +17 -0
  87. package/dist-lib/ui/windows/WindowManager.d.ts +132 -2
  88. package/dist-lib/ui/windows/panels/MapPanel.d.ts +4 -1
  89. package/dist-lib/ui/windows/panels/PackageExportModal.d.ts +18 -0
  90. package/dist-lib/ui/windows/panels/TextPanel.d.ts +3 -1
  91. package/dist-lib/ui/windows/types.d.ts +35 -0
  92. package/dist-lib/utils/describeThrown.d.ts +22 -0
  93. package/dist-lib/utils/fontLoader.d.ts +5 -0
  94. package/dist-lib/vite.js +22 -0
  95. package/package.json +10 -6
  96. package/dist-lib/scripting/lua/mudlet-lua/generic-mapper/generic_mapper.mpackage +0 -0
  97. package/dist-lib/ui/windows/panels/VfsModulePickerModal.d.ts +0 -8
@@ -1,4 +1,20 @@
1
1
  import type { MudSession } from '../mud/MudSession';
2
+ /**
3
+ * How the file log is written. Mudlet's `logInHTML` picks between two genuinely
4
+ * different documents, and the HTML one has to name the console's own font and
5
+ * background — neither of which the logger can see for itself.
6
+ */
7
+ export interface LogFormat {
8
+ html: boolean;
9
+ /** Resolved console font family, the one `getFont()` reports. */
10
+ font?: string;
11
+ /** Console background, painted behind the whole document as Mudlet does. */
12
+ background?: {
13
+ r: number;
14
+ g: number;
15
+ b: number;
16
+ };
17
+ }
2
18
  /**
3
19
  * Records one gameplay session to IndexedDB. Subscribes to the session's
4
20
  * `message` event — the single choke point every line of output passes
@@ -14,9 +30,19 @@ export declare class SessionLogger {
14
30
  private readonly session;
15
31
  private readonly connectionId;
16
32
  private readonly connectionName;
33
+ /** Profile filesystem the plain-text log is written into. Optional: the
34
+ * IndexedDB record is the primary log and works without one. */
35
+ private readonly vfs?;
17
36
  private readonly sessionId;
18
37
  private readonly startedAt;
19
38
  private buffer;
39
+ /** Absolute VFS path of the plain-text log, or null when there is no VFS to
40
+ * write one into. See {@link openLogFile}. */
41
+ private logFilePath;
42
+ /** Lines written to the text log but not yet flushed to the VFS. Kept
43
+ * separate from `buffer` (the IndexedDB batch) because the two flush on
44
+ * different triggers and the text log has to survive an IDB failure. */
45
+ private fileBuffer;
20
46
  private seq;
21
47
  private totalCount;
22
48
  private flushTimer;
@@ -24,8 +50,47 @@ export declare class SessionLogger {
24
50
  private sessionCreated;
25
51
  /** Serializes flushes so a size-triggered flush can't race the timer. */
26
52
  private flushing;
27
- constructor(session: MudSession, connectionId: string, connectionName: string);
53
+ constructor(session: MudSession, connectionId: string, connectionName: string,
54
+ /** Profile filesystem the plain-text log is written into. Optional: the
55
+ * IndexedDB record is the primary log and works without one. */
56
+ vfs?: {
57
+ profilePath: string;
58
+ mkdir(p: string): void;
59
+ writeFile(p: string, c: string): void;
60
+ exists(p: string): boolean;
61
+ readFile(p: string): string;
62
+ } | null | undefined);
28
63
  start(): void;
64
+ /** Where this session's plain-text log is being written, or null when the
65
+ * file log is off. Backs Mudlet's `startLogging` path return. */
66
+ get filePath(): string | null;
67
+ /**
68
+ * Mudlet's `startLogging(true)` — begin mirroring output to a file as well.
69
+ * Deliberately separate from {@link start}: recording to the log browser is
70
+ * a mudix profile setting that is on by default, whereas Mudlet's file log
71
+ * is something a player or script asks for. Sharing one switch would have
72
+ * every profile quietly writing a file nobody asked for, and would make
73
+ * `startLogging(true)` report "already on" for ever.
74
+ *
75
+ * The file is created up front, before any line has arrived, so a script can
76
+ * hand its path straight to something else. Named as Mudlet names its own:
77
+ * `<profile>/log/<yyyy-MM-dd#hh-mm-ss>.txt` — or `.html` when `format.html`
78
+ * is set (Mudlet's `logInHTML`), which is a whole second document format
79
+ * rather than the same lines with markup: a `<html>` wrapper, a stylesheet
80
+ * naming the console's own font and background, and a closing tag pair only
81
+ * {@link stopFileLog} writes.
82
+ */
83
+ startFileLog(format?: LogFormat): string | null;
84
+ /** Stop mirroring to the file, writing out whatever is buffered — and, for
85
+ * an HTML log, the closing tags that make it a document. */
86
+ stopFileLog(): void;
87
+ /** Whether the open file log is HTML rather than plain text. */
88
+ private logHtml;
89
+ private openLogFile;
90
+ /** Append the buffered lines to the log file. ZenFS has no
91
+ * append mode we can rely on across both backends, so this re-writes the
92
+ * file with the new tail — hence the buffering. */
93
+ private flushLogFile;
29
94
  private capture;
30
95
  /**
31
96
  * Mudlet `appendLog(text)` — append an arbitrary line to the current log,
@@ -168,6 +168,7 @@ export declare class MapStore {
168
168
  profileName: string;
169
169
  private mapEventDispatcher;
170
170
  private mapInfoContributors;
171
+ private pendingMapInfoEnabled;
171
172
  private builtinMapInfo;
172
173
  private mapInfoEvaluator;
173
174
  constructor();
@@ -232,6 +233,14 @@ export declare class MapStore {
232
233
  * adding rooms). Returns true.
233
234
  */
234
235
  deleteMap(): boolean;
236
+ /**
237
+ * Mudlet `T2DMap::slot_newMap` — the mapper's "Create new map" action,
238
+ * offered when the profile has no map at all. Wipes to a fresh empty map
239
+ * and seeds it with a single room at the origin of the default area (-1),
240
+ * marked as the player's position so the marker and the opening view both
241
+ * land on it. Returns the new room's id.
242
+ */
243
+ createNewMap(): number;
235
244
  toRendererData(): MapRendererData | null;
236
245
  /**
237
246
  * Mudlet `saveJsonMap(path)` backbone — serialise the entire MapStore as a
@@ -707,9 +716,15 @@ export declare class MapStore {
707
716
  searchAreaUserData(key?: string, value?: string): string[] | number[];
708
717
  /**
709
718
  * Per-area 2D-map zoom stored in the map file (area userData under
710
- * {@link AREA_ZOOM_KEY}). Returns `undefined` when the area is missing or has
711
- * no saved zoom (caller falls back to fitArea). Mirrors Mudlet's treatment of
712
- * zoom as map data rather than client config.
719
+ * {@link AREA_ZOOM_KEY}), in Mudlet units. Returns `undefined` when the area
720
+ * is missing or has no usable saved zoom, in which case the caller opens at
721
+ * {@link DEFAULT_MAP_ZOOM}. Mirrors Mudlet's treatment of zoom as map data
722
+ * rather than client config.
723
+ *
724
+ * A value below {@link MIN_MAP_ZOOM} is treated as absent, exactly as
725
+ * TMap::restore does with the same key — it can only come from a corrupt or
726
+ * foreign write, and honouring it would open the area zoomed in past the
727
+ * limit the wheel itself enforces.
713
728
  */
714
729
  getAreaZoom(id: number): number | undefined;
715
730
  /**
@@ -719,7 +734,10 @@ export declare class MapStore {
719
734
  * handler calls this on every wheel tick; notifying would rebuild the whole
720
735
  * scene on each one. The value persists to IndexedDB the next time the map is
721
736
  * serialised (saveMap → toMudletMapForSave). Returns false when the area is
722
- * missing or the zoom isn't a positive finite number.
737
+ * missing or the zoom is below {@link MIN_MAP_ZOOM} — TArea::set2DMapZoom
738
+ * silently ignores those too, and the floor is what stops a bad reading (a
739
+ * fit computed against a not-yet-laid-out 0×0 panel, say) from being written
740
+ * out and then restored on every subsequent open.
723
741
  */
724
742
  setAreaZoom(id: number, zoom: number): boolean;
725
743
  /**
@@ -892,6 +910,16 @@ export declare class MapStore {
892
910
  };
893
911
  enableMapInfo(label: string): boolean;
894
912
  disableMapInfo(label: string): boolean;
913
+ /** Mudlet `setConfig("showMapInfo", label)` — switch a map-info overlay on.
914
+ * Unlike `enableMapInfo`, an unknown label is *not* an error: Mudlet inserts
915
+ * the name straight into `mMapInfoContributors` without consulting the
916
+ * registry, so the overlay lights up if and when something registers under
917
+ * that label. Always succeeds, matching setConfig's unconditional success. */
918
+ showMapInfo(label: string): void;
919
+ /** Mudlet `setConfig("hideMapInfo", label)` — switch a map-info overlay off.
920
+ * Clears a pending enable as well, so hiding a label that was turned on
921
+ * before its contributor existed actually cancels it. */
922
+ hideMapInfo(label: string): void;
895
923
  /** Snapshot for tests / debug. The panel goes through evaluateMapInfos. */
896
924
  getMapInfoContributors(): MapInfoContributor[];
897
925
  /** Run every enabled contributor and collect their (text, style, color)
@@ -1,6 +1,6 @@
1
1
  import type { Settings as MapRendererSettings } from 'mudlet-map-renderer';
2
2
  import type { MapStore } from './MapStore';
3
- import type { MapperSettings } from '../storage';
3
+ import { type MapperSettings } from '../storage/schema';
4
4
  /**
5
5
  * Copy user-set fields from MapperSettings onto a live renderer settings
6
6
  * object. Anything left undefined in `mapper` is intentionally not touched
@@ -0,0 +1,71 @@
1
+ import type { MapRenderer } from 'mudlet-map-renderer';
2
+ /**
3
+ * Map-view zoom: the two units it comes in, and the policy for opening an area.
4
+ *
5
+ * Two different numbers are both called "zoom", and mixing them is what made
6
+ * maps open absurdly far in or out:
7
+ *
8
+ * - **renderer zoom** — a scale multiplier; `camera.getScale() === BASE_SCALE *
9
+ * zoom` is the pixels drawn per map unit. Bigger = zoomed in. A pure renderer
10
+ * concept that only means anything paired with the current viewport size.
11
+ * - **Mudlet zoom** — how many map units the *shorter* viewport edge spans
12
+ * (T2DMap's `xyzoom`: 3 → 3 rooms across, 100 → ~100). Bigger = zoomed *out*.
13
+ * This is what `getMapZoom`/`setMapZoom` speak and what gets persisted into
14
+ * the map file, because it survives the panel being a different size in a
15
+ * later session.
16
+ *
17
+ * Everything outside the renderer is in Mudlet units; {@link toMudletZoom} and
18
+ * {@link toRendererZoom} convert at that boundary.
19
+ */
20
+ /** Mudlet's hard floor for the 2D map zoom (T2DMap `csmMinXYZoom`): the shorter
21
+ * viewport edge may never span fewer than this many map units, i.e. you can't
22
+ * zoom in any closer. Mirrored so wheel/pinch zoom obeys the same limit. */
23
+ export declare const MUDLET_MIN_MAP_ZOOM = 3;
24
+ /** Mudlet's `T2DMap::csmDefaultXYZoom` — what an area opens at when it has no
25
+ * remembered zoom of its own. */
26
+ export declare const MUDLET_DEFAULT_MAP_ZOOM = 20;
27
+ /** How far past "the whole area exactly fills the panel" the user may keep
28
+ * zooming out. The renderer's `fitToMapBounds` ends with `minZoom = zoom`, so
29
+ * every `fitArea()` pins the zoom-out floor to that exact fit — the wheel then
30
+ * stops dead with the area flush against the panel edges, and there's no way
31
+ * to pull back for context or to see where an area sits relative to its
32
+ * surroundings. Relaxing the floor afterwards restores that headroom. */
33
+ export declare const ZOOM_OUT_HEADROOM = 4;
34
+ /**
35
+ * The renderer's current view expressed in Mudlet units, or null when the camera
36
+ * has no usable size yet.
37
+ *
38
+ * A panel that hasn't been laid out reports 0×0, and a "zoom" derived from that
39
+ * is meaningless — persisting one is exactly how an area got stuck at a few
40
+ * pixels per room on every subsequent open.
41
+ */
42
+ export declare function toMudletZoom(renderer: MapRenderer): number | null;
43
+ /**
44
+ * The renderer zoom that makes `mudletZoom` map units span the shorter viewport
45
+ * edge, or null when the camera isn't sized (see {@link toMudletZoom}) or the
46
+ * request isn't a positive number.
47
+ */
48
+ export declare function toRendererZoom(renderer: MapRenderer, mudletZoom: number): number | null;
49
+ /** Fit the area, then relax the floor the fit just pinned (see
50
+ * {@link ZOOM_OUT_HEADROOM}). Use everywhere instead of `renderer.fitArea()`. */
51
+ export declare function fitAreaWithHeadroom(renderer: MapRenderer): void;
52
+ /**
53
+ * Open an area at its remembered zoom, or at Mudlet's default when it has none,
54
+ * with a zoom-out floor derived from the area's actual extent.
55
+ *
56
+ * The fit runs first, but only for the floor it leaves behind — it is the only
57
+ * thing that derives one from how big the area actually is. The camera's own
58
+ * default floor is a fixed 0.05 with no relation to map size: on any map larger
59
+ * than the viewport that sits far *above* the zoom needed to see the whole thing,
60
+ * so opening without fitting first leaves the wheel pinned at a floor from which
61
+ * the map can never be framed.
62
+ *
63
+ * The fit deliberately does NOT survive as the opening view. Mudlet never fits an
64
+ * area to the widget — T2DMap opens at `TArea::mLast2DMapZoom`, defaulting to
65
+ * `csmDefaultXYZoom`. Fitting produced both halves of a reported bug: a four-room
66
+ * area fitted to roughly one room per screen (the fit of a 1×1-unit extent is
67
+ * already past Mudlet's zoom-in limit), while a city-sized area fitted to rooms a
68
+ * few pixels wide. A fixed unit count is scale-free and gives both the same
69
+ * legible room size.
70
+ */
71
+ export declare function applyAreaZoom(renderer: MapRenderer, savedZoom: number | null | undefined): void;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The one source of item ids in a profile.
3
+ *
4
+ * Mudlet hands every trigger, alias, timer, key, button and script its id from
5
+ * a single running number — temporary and permanent alike — so `tempAlias()`
6
+ * followed by `permAlias()` followed by `tempAlias()` yields n, n+1, n+2. mudix
7
+ * grew a counter per engine plus one for the permanent tree, which meant a
8
+ * temporary item and a permanent one could be handed the *same* id: two
9
+ * different things answering to one number, which `killAlias(id)` and
10
+ * `exists(name)` then could not tell apart.
11
+ *
12
+ * Injected rather than constructed in place (see {@link ScriptingEngine}, which
13
+ * hands its own sequence to each engine) so every engine in a profile draws
14
+ * from the same one, while a bare engine built for a unit test still works with
15
+ * a private sequence of its own.
16
+ */
17
+ export declare class ItemIdSequence {
18
+ private n;
19
+ next(): number;
20
+ /** Ensure future ids exceed `id` — used when an id arrives from outside the
21
+ * sequence (a restored profile, say) so it can never be handed out twice. */
22
+ reserve(id: number): void;
23
+ }
@@ -54,6 +54,10 @@ export declare class MudSession {
54
54
  private lastUrl;
55
55
  private pingTracker;
56
56
  private stateUnsubs;
57
+ /** The profile's server encoding, as `getServerEncodingsList()` spells it.
58
+ * Lives here rather than on the client so it survives having none — see
59
+ * {@link getServerEncoding}. */
60
+ private serverEncoding;
57
61
  private _status;
58
62
  private _ping;
59
63
  private _outputReady;
@@ -114,11 +118,53 @@ export declare class MudSession {
114
118
  * local echo, under the current showSentText mode. `script` defers to the
115
119
  * flag; `always`/`never` ignore it. */
116
120
  private shouldEchoSentText;
121
+ /** Set by ScriptingAPI while trigger-mode echo deferral owns the main
122
+ * console's in-flight partial line (beginLine → flushDeferredEcho). Read
123
+ * by {@link echoCommand}, which must not close a partial the deferral is
124
+ * about to emit itself. */
125
+ scriptEchoDeferred: boolean;
126
+ /** Host::send's echo stage: print a command the player (or an item acting
127
+ * for them) sent, under the showSentText mode. `wantPrint` is the per-call
128
+ * flag `script` mode defers to — `always` and `never` overrule it. */
129
+ echoSentCommand(text: string, wantPrint: boolean): void;
117
130
  echoCommand(text: string): void;
118
131
  /** Wrap the echoed command in ANSI truecolor escapes from commandEchoColor
119
132
  * so it renders in the configured foreground (and optional background). */
120
133
  private styleEchoCommand;
121
- send(text: string, echo?: boolean): void;
134
+ /** `isGameCommand` mirrors `cTelnet::sendData`'s flag — see
135
+ * {@link MudClient.send}. Everything the player or a script submits is one;
136
+ * auto-login credentials are not, so they can't arm character-at-a-time
137
+ * detection. */
138
+ send(text: string, echo?: boolean, isGameCommand?: boolean): void;
139
+ /**
140
+ * `cTelnet::sendData` — put one command on the wire with **no** local echo
141
+ * under any showSentText mode.
142
+ *
143
+ * Mudlet echoes player input exactly once, at the top of `Host::send`,
144
+ * before the command separator split and before aliases see it (see
145
+ * {@link ScriptingEngine.hostSend}); the parts that come out the far side
146
+ * must not echo again. `send(text, false)` cannot express that — `always`
147
+ * overrides the per-call flag — which is the same reason
148
+ * {@link sendSecret} exists. Unlike sendSecret this is an ordinary command:
149
+ * it still warns about text the server encoding can't carry.
150
+ */
151
+ sendData(text: string, isGameCommand?: boolean): void;
152
+ /** Mudlet's `mEncodingWarningIssued`: once per encoding, not once per
153
+ * command, so a script sending in a loop doesn't paper the screen. Reset by
154
+ * {@link setServerEncoding} / {@link noteNegotiatedEncoding}, because the
155
+ * new encoding may have no trouble with what the old one couldn't say. */
156
+ private encodingWarningIssued;
157
+ /**
158
+ * Warn when a command cannot survive the trip to the game — cTelnet::sendData
159
+ * posts this before sending anyway, and so do we: the server may still make
160
+ * something of what arrives, and silently dropping the command would be
161
+ * worse than sending it mangled.
162
+ *
163
+ * Warning here rather than in MudClient is deliberate: the encoding belongs
164
+ * to the profile, not the socket, and a player typing at a client that isn't
165
+ * connected yet has just as much use for the notice.
166
+ */
167
+ private warnIfUnencodable;
122
168
  /** Send credentials/secrets that must NEVER be echoed locally — regardless of
123
169
  * the showSentText mode (including `always`) or the server-echo state. Used
124
170
  * for auto-login passwords. The normal `send(text, false)` only suppresses
@@ -135,6 +181,15 @@ export declare class MudSession {
135
181
  sendSocket(data: string): boolean;
136
182
  /** True when there is no live socket — see MudClient.isSocketUnconnected. */
137
183
  isSocketUnconnected(): boolean;
184
+ /** Mudlet `feedTelnet(data)`. Injects raw bytes into the inbound pipeline as
185
+ * if the server had sent them.
186
+ *
187
+ * Works with no connection, which is the state the API is *for*: Mudlet
188
+ * refuses to inject into a live socket (it would interleave with the real
189
+ * stream), so every caller — its own test suite included — is offline by
190
+ * definition. With no client there is no pipeline to inject into, so this
191
+ * borrows the detached one replay playback already builds for the same
192
+ * reason: the telnet/ANSI/trigger path needs no socket to run. */
138
193
  feedTelnet(data: string): void;
139
194
  sendATCP(message: string): boolean;
140
195
  sendTelnetChannel102(msg: string): boolean;
@@ -144,6 +199,10 @@ export declare class MudSession {
144
199
  get replaySpeed(): number;
145
200
  get isReplayRecording(): boolean;
146
201
  get isReplaying(): boolean;
202
+ /** Deliver any replay chunk that has come due. See ReplayPlayer.pumpDue —
203
+ * a script standing in for the event loop has to drive this queue too, or a
204
+ * replay it started never advances past its first chunk. */
205
+ pumpReplay(): number;
147
206
  /** Set the playback speed divisor, clamped to Mudlet's 1..1024 range.
148
207
  * Takes effect from the next chunk of an active replay. */
149
208
  setReplaySpeed(speed: number): void;
@@ -162,23 +221,35 @@ export declare class MudSession {
162
221
  /** Stop the active replay without delivering its remaining chunks.
163
222
  * Returns false when no replay is running. */
164
223
  abortReplay(): boolean;
165
- /** The client whose parsing pipeline replay chunks feed into. Reuses the
166
- * live client when one exists; otherwise creates a detached MudClient
167
- * that is never connect()ed — the inbound pipeline works without a
168
- * socket, and any replies the replayed IAC traffic provokes are dropped
169
- * by the readyState guards, which is exactly right for playback. */
170
- private ensureReplayClient;
224
+ /** The client whose parsing pipeline injected bytes feed into replay
225
+ * chunks and feedTelnet alike. Reuses the live client when one exists;
226
+ * otherwise creates a detached MudClient that is never connect()ed — the
227
+ * inbound pipeline works without a socket, and any replies the injected IAC
228
+ * traffic provokes are dropped by the readyState guards, which is exactly
229
+ * right when nothing is listening for them. */
230
+ private ensureParsingClient;
171
231
  private postReplayInfo;
172
232
  /** Mudlet `reconnect()`. Disconnect and redial the most recently connected
173
233
  * URL (set by connect(), so it covers both the app and Lua connect paths).
174
234
  * Returns false when nothing has been dialed yet. */
175
235
  reconnect(): boolean;
176
- /** Mudlet `getServerEncoding()`. The live client's inbound decoder name;
177
- * 'utf-8' when no client is attached. */
236
+ /**
237
+ * Mudlet `getServerEncoding()` / `setServerEncoding(name)`.
238
+ *
239
+ * The encoding is the profile's, not the socket's: it can be read and
240
+ * changed with nothing dialed, and the setting carries across a connect.
241
+ * A live client is told about the change so its decoder follows, and a
242
+ * CHARSET negotiation writes the agreed name back here — but the client is
243
+ * not where the answer comes from. It used to be, which made both functions
244
+ * inert until a connection existed, and the name they reported was the
245
+ * decoder's IANA spelling ("utf-8") rather than the one the caller chose
246
+ * ("UTF-8" as `getServerEncodingsList()` spells it).
247
+ */
178
248
  getServerEncoding(): string;
179
- /** Mudlet `setServerEncoding(name)`. Returns false when no client is
180
- * attached or the name isn't supported. */
181
249
  setServerEncoding(name: string): boolean;
250
+ /** Called when CHARSET negotiation settles on a name, so the profile-level
251
+ * setting reflects what the connection actually agreed. */
252
+ noteNegotiatedEncoding(name: string): void;
182
253
  /** Mudlet `getServerEncodingsList()`. The fixed set of encodings mudix can
183
254
  * decode — available even before a connection is dialed. */
184
255
  getServerEncodingsList(): string[];
@@ -194,6 +265,37 @@ export declare class MudSession {
194
265
  /** Mudlet `setConfig("fixUnnecessaryLinebreaks", …)`. Updates the live client
195
266
  * and the stored options so the setting survives a reconnect. */
196
267
  setFixUnnecessaryLinebreaks(enabled: boolean): void;
268
+ /** Mudlet `Host::mUndoServerWrap` / `mUndoServerWrapWidth`, as getConfig
269
+ * reads them back. */
270
+ get undoServerWrap(): boolean;
271
+ get undoServerWrapWidth(): number;
272
+ /** Mudlet `setConfig("undoServerWrap", …)`. Updates the live client and the
273
+ * stored options so the setting survives a reconnect. */
274
+ setUndoServerWrap(enabled: boolean): void;
275
+ /** Mudlet `setConfig("undoServerWrapWidth", …)`. */
276
+ setUndoServerWrapWidth(width: number): void;
277
+ /** Commit a line held for a wrap continuation whose flush delay has passed —
278
+ * the busted `pumpEvents` path, which cannot wait for a real timer. */
279
+ pumpServerWrap(): boolean;
280
+ /** Mudlet `setConfig("inputLineStrictUnixEndings", …)`. Live — the next
281
+ * command submitted uses the new line terminator. */
282
+ setInputLineStrictUnixEndings(enabled: boolean): void;
283
+ /** Mudlet `setConfig("specialForceGAOff", …)`. Stored for the next dial, and
284
+ * applied at once to a client that is not connected — which is the state a
285
+ * profile driving `feedTelnet` is in, and the only one where Mudlet lets
286
+ * the change land early. See MudClient.setForceGaOff. */
287
+ setSpecialForceGAOff(enabled: boolean): void;
288
+ /** Mudlet `setConfig("versionInTTYPE", …)` / `("promptForVersionInTTYPE", …)`.
289
+ * Stored only: TTYPE is negotiated at connect, so both take effect on the
290
+ * next dial — the same reconnect requirement Mudlet's own auto-detect works
291
+ * around by redialing for you. */
292
+ setVersionInTTYPE(enabled: boolean, prompted?: boolean): void;
293
+ /** Mudlet `setConfig("promptForMXPProcessorOn", …)` / `("specialForceMXPProcessorOn", …)`.
294
+ * Stored only — together they gate whether an in-band `ESC[<n>z` may still
295
+ * auto-start MXP, which the negotiator decides at connect. The *parser*
296
+ * side of `specialForceMXPProcessorOn` is applied live by ScriptingEngine;
297
+ * this is just the detection gate. */
298
+ setMxpProcessorFlags(prompted: boolean, forced: boolean): void;
197
299
  /** Mudlet `setConfig("controlCharacterHandling", …)`. Applies immediately —
198
300
  * every console/text window re-renders control characters (and tab-stops)
199
301
  * under the new mode on their next paint. */
@@ -14,7 +14,12 @@ export declare class PatternEngine<T extends PatternItem> {
14
14
  pattern: RegExp;
15
15
  fn: TempFn;
16
16
  }>;
17
- protected nextId: number;
17
+ /** Key for this engine's own temp map. NOT an item id: addTemp hands the
18
+ * caller an unsubscribe function, and the id Lua sees is allocated by the
19
+ * runtime from the profile's shared sequence. Drawing from that sequence
20
+ * here would burn a number per temp item and put permAlias/tempAlias out
21
+ * of step (Alias_spec pins the run of ids). */
22
+ protected nextInternalId: number;
18
23
  protected permCompiled: Array<{
19
24
  item: T;
20
25
  re: RegExp;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The command-separator split from Mudlet's `Host::send`.
3
+ *
4
+ * One line typed (or one `command` field on an alias/key/trigger/timer/button)
5
+ * can carry several commands, joined by the profile's separator — `;;` by
6
+ * default. Mudlet splits with `Qt::SkipEmptyParts`, so `n;;;;s` is two commands
7
+ * rather than three, and a line that is nothing but separators collapses to
8
+ * nothing at all.
9
+ *
10
+ * An empty result is meaningful: Mudlet answers it by putting a bare line feed
11
+ * on the wire, so pressing Enter on an empty command line still reaches the
12
+ * game (menus, "more" prompts). Callers handle that case themselves — see
13
+ * {@link ScriptingEngine.hostSend} — which is why this returns `[]` rather than
14
+ * quietly inventing an empty command.
15
+ */
16
+ export declare function splitCommands(text: string, separator: string): string[];
@@ -19,6 +19,11 @@ export interface LineAssemblerCallbacks {
19
19
  }
20
20
  export interface LineAssemblerOptions {
21
21
  promptTimeoutMs?: number;
22
+ /** Mudlet `Host::mUndoServerWrap` — rejoin lines the game hard-wrapped
23
+ * itself. Off by default; toggled live via setConfig. */
24
+ undoServerWrap?: boolean;
25
+ /** Mudlet `Host::mUndoServerWrapWidth` — the column the game wraps at. */
26
+ undoServerWrapWidth?: number;
22
27
  /** Mudlet `mUSE_IRE_DRIVER_BUGFIX` — strip a spurious leading newline from
23
28
  * GA-driven prompt blocks. Off by default; toggled live via
24
29
  * setFixUnnecessaryLinebreaks (setConfig). */
@@ -58,6 +63,19 @@ export declare class LineAssembler {
58
63
  * the block's leading-newline question has been settled. */
59
64
  private atPromptBlockStart;
60
65
  private fixUnnecessaryLinebreaks;
66
+ private undoServerWrap;
67
+ private undoServerWrapWidth;
68
+ private serverWrapPending;
69
+ /** Visible length of the last *game line* joined into the held text — what
70
+ * the next word is measured against, not the whole joined paragraph. */
71
+ private serverWrapPendingSegmentLength;
72
+ /** Opening of that same game line, kept so a continuation repeating it can
73
+ * be told from one that carries on where it left off. */
74
+ private serverWrapPendingSegmentStart;
75
+ private serverWrapTimer;
76
+ /** When the held line falls due, so a blocked event loop can still commit it
77
+ * on demand — see {@link pumpServerWrapDue}. */
78
+ private serverWrapDeadline;
61
79
  constructor(callbacks: LineAssemblerCallbacks, options?: LineAssemblerOptions);
62
80
  /** True once the session has latched into GA-driven prompt mode. */
63
81
  get gaDriver(): boolean;
@@ -66,6 +84,12 @@ export declare class LineAssembler {
66
84
  /** Mudlet `setConfig("fixUnnecessaryLinebreaks", …)`. Takes effect on the
67
85
  * next GA-driven block; never retroactive. */
68
86
  setFixUnnecessaryLinebreaks(enabled: boolean): void;
87
+ /** Mudlet `setConfig("undoServerWrap", …)`. Turning it off commits anything
88
+ * held on the spot rather than stranding it until the next line. */
89
+ setUndoServerWrap(enabled: boolean): void;
90
+ /** Mudlet `setConfig("undoServerWrapWidth", …)`. Applies from the next line;
91
+ * the held one was already judged against the previous column. */
92
+ setUndoServerWrapWidth(width: number): void;
69
93
  /** Drop all held state (call on connect and after close). */
70
94
  reset(): void;
71
95
  /** Feed one frame's decoded text plus whether the frame carried a prompt
@@ -79,6 +103,38 @@ export declare class LineAssembler {
79
103
  * socket close. Pushes the tail through the normal chunk path so triggers
80
104
  * and rendering treat it as a complete line. */
81
105
  flush(ts: number, final?: boolean): void;
106
+ /**
107
+ * Run the whole lines of one ready chunk through the server-wrap join and
108
+ * emit what comes out. With the option off this is `onChunk(ready)` and
109
+ * nothing else.
110
+ *
111
+ * Mudlet decides this per '\n' inside `TBuffer::append`, gated on the line
112
+ * having come from the server: everything else (prompts, timer-flushed
113
+ * fragments, MXP `<br>`, blank lines) is a real boundary. Here the gate is
114
+ * structural — this is the only path whole server lines take, and
115
+ * feedTriggers/echoes never reach it.
116
+ */
117
+ private emitServerLines;
118
+ /**
119
+ * Offer one whole server line to the join. Returns the text to commit —
120
+ * newline-terminated lines, or the empty string when the line was held back
121
+ * for a continuation.
122
+ */
123
+ private consumeServerLine;
124
+ /** Take the held line, if any, and stop its timer. */
125
+ private takeServerWrapPending;
126
+ /** Commit the held line on its own, as its own chunk. */
127
+ private commitServerWrapPending;
128
+ /**
129
+ * Commit a held line whose flush delay has elapsed. The timer below cannot
130
+ * be relied on alone: a busted run is one synchronous call sitting on top of
131
+ * the event loop, so no `setTimeout` of ours can fire until it returns. The
132
+ * spec's `pumpEvents` reaches this instead, exactly as it reaches the Lua
133
+ * timer queue and the replay player. Returns true when a line was committed.
134
+ */
135
+ pumpServerWrapDue(now?: number): boolean;
136
+ private startServerWrapTimer;
137
+ private clearServerWrapTimer;
82
138
  private scheduleTailFlush;
83
139
  private clearTailTimer;
84
140
  }