@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
@@ -20,7 +20,26 @@ export declare function normalizeCharsetName(raw: string): string | null;
20
20
  * via `getServerEncodingsList()`. Every entry round-trips through
21
21
  * {@link normalizeCharsetName}, so any name here is a valid `setServerEncoding`
22
22
  * argument. ("ASCII" maps to the UTF-8 decoder, which handles it byte-for-byte.) */
23
+ /** What `getServerEncoding()` reports before anything has changed it. UTF-8
24
+ * rather than Mudlet's ASCII: the decoder handles ASCII byte-for-byte anyway,
25
+ * and a browser stream is far more likely to be UTF-8 than not. */
26
+ export declare const DEFAULT_SERVER_ENCODING = "UTF-8";
23
27
  export declare const SUPPORTED_SERVER_ENCODINGS: readonly string[];
28
+ /** The {@link SUPPORTED_SERVER_ENCODINGS} entry a caller's spelling means, or
29
+ * null when mudix cannot decode it. Dash/space/case differences are all the
30
+ * same encoding — the profile XML, the wire, and Mudlet's own list disagree
31
+ * about which to use — so the answer is always the list's own spelling and
32
+ * `getServerEncoding()` reports one canonical name whatever was set. */
33
+ export declare function canonicalServerEncoding(raw: string): string | null;
34
+ /**
35
+ * Whether every character of `text` survives a trip to the game under
36
+ * `serverEncoding` — Mudlet's TEncodingHelper::canEncode, which is what decides
37
+ * whether a send gets the "unlikely to understand it" warning.
38
+ *
39
+ * Anything mudix cannot judge (an unknown label) is called encodable: the point
40
+ * is to warn about a loss that will certainly happen, not to guess.
41
+ */
42
+ export declare function canEncodeForServer(text: string, serverEncoding: string): boolean;
24
43
  /**
25
44
  * Parse an `IAC SB CHARSET REQUEST ...` subnegotiation body (leading byte is
26
45
  * the option code 42, then subcommand byte 1, then optional `[TTABLE]<ver>`
@@ -61,11 +80,14 @@ export declare class SessionCodec {
61
80
  decode(byteString: string): string;
62
81
  /** Convert a user-typed JS string (UTF-16) into the Latin-1 byte-string the
63
82
  * socket layer expects, using the currently negotiated outgoing encoding.
64
- * For UTF-8 we run it through TextEncoder so multi-byte chars survive;
65
- * for other encodings we fall back to a per-char `& 0xff` truncation,
66
- * which is lossless for ASCII and acceptable for Latin-1-family inputs.
67
- * TextEncoder has no API for non-UTF-8 outputs, so a full per-encoding
68
- * outbound table isn't worth the bytes given how rare non-UTF-8 MUDs are. */
83
+ * UTF-8 goes through TextEncoder so multi-byte chars survive; every other
84
+ * encoding here is single-byte and goes through the inverted decode table,
85
+ * which puts the codepage's own byte on the wire. (A plain `& 0xff`
86
+ * truncation used to stand in for that, and was right only for the
87
+ * Latin-1 range every Polish, Cyrillic, or Greek character above it went
88
+ * out as a byte meaning something else entirely.) A character the codepage
89
+ * has no byte for becomes '?', as Qt's encoder does it — and the send path
90
+ * warns before it comes to that. */
69
91
  encodeOutgoing(text: string): string;
70
92
  }
71
93
  export interface CharsetHandlerHooks {
@@ -1,4 +1,5 @@
1
1
  export declare const TELNET_OPTION_REGEX: RegExp;
2
+ export declare const TELNET_OPTION_REGEX_NO_SB: RegExp;
2
3
  export declare const TELNET_GA = "\u00FF\u00F9";
3
4
  export declare const TELNET_EOR = "\u00FF\u00EF";
4
5
  export declare const TELOPT_EOR = "\u0019";
@@ -3,12 +3,28 @@ export interface GmcpEnvelope {
3
3
  value: unknown;
4
4
  }
5
5
  export type TelnetOptionHandler = (data: string) => string;
6
- export declare const createTelnetOptionParser: (onSubnegotiation: (data: string) => void) => TelnetOptionHandler;
6
+ /** UTF-8-encode into a Latin-1 byte-string, for MudClient.sendBytes (which
7
+ * writes `charCodeAt(i) & 0xff` per char). The inverse of fromByteString. */
8
+ export declare const toByteString: (s: string) => string;
9
+ export declare const createTelnetOptionParser: (onSubnegotiation: (data: string) => void, opts?: {
10
+ /** When true, an inbound IAC GA / IAC EOR is replaced by a newline
11
+ * rather than stripped — Mudlet's `mFORCE_GA_OFF` behaviour
12
+ * (`cTelnet::processSocketData` pushes `'\n'` in place of the marker
13
+ * instead of treating it as a prompt). Doing the substitution here,
14
+ * inside the sequence parser, keeps it positional: the newline lands
15
+ * exactly where the marker was, and a `\xFF\xF9` byte pair inside a
16
+ * subnegotiation payload is never mistaken for one. */
17
+ promptMarkerAsNewline?: boolean;
18
+ }) => TelnetOptionHandler;
7
19
  export declare const stripTelnetSequences: (data: string, handler: TelnetOptionHandler) => string;
8
20
  export declare const encodeGmcp: (path: string, payload: unknown) => string;
9
21
  /** Encode a GMCP frame from a single pre-formatted body (e.g. `"Module.Sub args"`).
10
- * Matches Mudlet's `sendGMCP` semantics — the caller controls the exact bytes
11
- * between IAC SB GMCP and IAC SE. */
22
+ * Mudlet's `sendGMCP` semantics — the caller controls the body between IAC SB
23
+ * GMCP and IAC SE — except that the body is transcoded to UTF-8 rather than to
24
+ * the session's outgoing encoding, so it can't be used to place arbitrary raw
25
+ * bytes on the wire. UTF-8 is what the GMCP spec asks for, and it's also what
26
+ * keeps a 0xFF byte (which would need IAC-escaping inside a subnegotiation)
27
+ * out of the body in the first place. */
12
28
  export declare const encodeGmcpRaw: (message: string) => string;
13
29
  export interface GmcpStreamOptions {
14
30
  onEnvelope: (payload: GmcpEnvelope) => void;
@@ -8,4 +8,4 @@ export { MspParser, type MspCommand, type MspKind } from "./msp";
8
8
  export { MxpParser, splitMxpResultLines, type MxpLink, type MxpLineResult } from "./mxp";
9
9
  export { parseMnesRequest, encodeMnesIs, selectMnesVars, buildNewEnvironVars, CLIENT_NAME, CLIENT_VERSION, TERMINAL_TYPE, type MnesVar, type MnesRequest, type NewEnvironState } from "./mnes";
10
10
  export { encodeNaws } from "./naws";
11
- export { SessionCodec, CharsetHandler, normalizeCharsetName, pickCharsetFromRequest, SUPPORTED_SERVER_ENCODINGS, type CharsetHandlerHooks } from "./charset";
11
+ export { SessionCodec, CharsetHandler, normalizeCharsetName, pickCharsetFromRequest, SUPPORTED_SERVER_ENCODINGS, DEFAULT_SERVER_ENCODING, canonicalServerEncoding, canEncodeForServer, type CharsetHandlerHooks } from "./charset";
@@ -0,0 +1,54 @@
1
+ /**
2
+ * MSSP-advertised TLS port tracking.
3
+ *
4
+ * Mirrors Mudlet's `cTelnet::promptTlsConnectionAvailable` (ctelnet.cpp) —
5
+ * a server may advertise a secure port through the MSSP `TLS` / `SSL` variable,
6
+ * and the client offers, once, to switch to it.
7
+ *
8
+ * Kept free of React and session plumbing so the decision rules can be tested
9
+ * directly; the caller supplies the current connection state.
10
+ */
11
+ /** Per-connection MSSP facts that bear on the TLS offer. */
12
+ export interface MsspTlsFacts {
13
+ /** The advertised secure port, or 0 when none/unusable. */
14
+ tlsPort: number;
15
+ /** MSSP `HOSTNAME`, used to catch an advertisement meant for a different host. */
16
+ hostName: string;
17
+ }
18
+ export declare function emptyMsspTlsFacts(): MsspTlsFacts;
19
+ /**
20
+ * Fold one MSSP variable into the accumulated facts.
21
+ *
22
+ * `TLS`/`SSL` normally carries a port number, but the values `-1` and `1` are
23
+ * used by some servers as plain "unsupported"/"supported" booleans. Mudlet
24
+ * discards both rather than dialling port 1, and so do we.
25
+ */
26
+ export declare function applyMsspVariable(facts: MsspTlsFacts, name: string, value: string): MsspTlsFacts;
27
+ /** True for a literal IPv4/IPv6 address. A certificate is very unlikely to be
28
+ * issued for a bare IP, so Mudlet suppresses the offer in that case rather
29
+ * than steering the user into a guaranteed validation failure. */
30
+ export declare function isIpAddress(host: string): boolean;
31
+ export interface TlsOfferState {
32
+ /** MSSP facts gathered on this connection. */
33
+ facts: MsspTlsFacts;
34
+ /** The host this profile actually dialled. */
35
+ host: string;
36
+ /** The port currently in use. */
37
+ port: number;
38
+ /** Whether this profile is already connecting over TLS. */
39
+ tlsEnabled: boolean;
40
+ /** The profile's "remind me about secure connections" preference. */
41
+ askTlsAvailable: boolean;
42
+ /** True while an offer is already on screen, so repeats can't stack. */
43
+ promptInFlight: boolean;
44
+ /** Only proxy-mode connections can be upgraded: an MSSP TLS port is a raw
45
+ * telnet-over-TLS port, which a browser cannot dial directly. */
46
+ proxyMode: boolean;
47
+ }
48
+ /**
49
+ * Decide whether to offer the secure-port switch. Mirrors Mudlet's guard set:
50
+ * a usable advertised port, not already encrypted, the reminder still enabled,
51
+ * the host not a bare IP, MSSP `HOSTNAME` (when given) matching the host we
52
+ * dialled, and no offer already pending.
53
+ */
54
+ export declare function shouldOfferTlsUpgrade(s: TlsOfferState): boolean;
@@ -29,9 +29,16 @@ export interface MxpFrameCommand {
29
29
  name: string;
30
30
  /** Upper-cased attribute keys → raw string values. Flag attributes
31
31
  * (`INTERNAL`/`EXTERNAL`/`FLOATING`) are present with value `"true"`.
32
- * `ACTION` is `open` (default) / `close` / `redirect`; geometry lives in
33
- * `LEFT`/`TOP`/`WIDTH`/`HEIGHT`. */
32
+ * `ACTION` is `open` (default) / `close` / `focus`; geometry lives in
33
+ * `ALIGN`/`LEFT`/`TOP`/`WIDTH`/`HEIGHT`. */
34
34
  attrs: Record<string, string>;
35
+ /** Name of the `<DEST>` frame that was open when this tag was seen, if any.
36
+ * A `<FRAME>` nested inside a `<DEST>` is laid out *inside* that frame
37
+ * rather than against the main window — Mudlet's `mCurrentDestination`
38
+ * check in TMxpFrameManager::layoutInternalFrame. The parser batches frames
39
+ * and redirects into separate arrays, so the association has to travel with
40
+ * the command. */
41
+ dest?: string;
35
42
  }
36
43
  /** Text the parser redirected into a named frame via `<DEST>…</DEST>`. */
37
44
  export interface MxpRedirect {
@@ -41,6 +48,9 @@ export interface MxpRedirect {
41
48
  segments: BufferSegment[];
42
49
  /** Plain text of the redirected run. */
43
50
  plain: string;
51
+ /** Clickable regions inside this run, offset into `plain`. Separate from
52
+ * `MxpLineResult.links` because the two index different strings. */
53
+ links: MxpLink[];
44
54
  /** `EOL` attr (or the network line ended mid-DEST): the write is a complete
45
55
  * line. */
46
56
  eol: boolean;
@@ -84,6 +94,8 @@ export declare class MxpParser {
84
94
  private mxpColorStack;
85
95
  /** Partial tag/entity held from the end of the previous line. */
86
96
  private pendingTag;
97
+ /** Whether the line being parsed came off the socket — see {@link parseLine}. */
98
+ private fromServer;
87
99
  /** Active `<DEST>` target frame (persists across lines until `</DEST>`), or
88
100
  * null when output flows to the main window. While set, appended text and
89
101
  * flushed runs route to `destOut`/`destPlain` instead of `out`/`plain`. */
@@ -103,9 +115,10 @@ export declare class MxpParser {
103
115
  private redirects;
104
116
  /** `<SOUND>`/`<MUSIC>` commands accumulated this line. */
105
117
  private sounds;
106
- /** Redirected-text scratch — the current `<DEST>` run's segments/plain. */
118
+ /** Redirected-text scratch — the current `<DEST>` run's segments/plain/links. */
107
119
  private destOut;
108
120
  private destPlain;
121
+ private destLinks;
109
122
  constructor(opts: {
110
123
  send: (raw: string) => void;
111
124
  presets?: HyperlinkPresetRegistry;
@@ -113,6 +126,9 @@ export declare class MxpParser {
113
126
  * tag's attributes resolved the way Mudlet resolves them (see
114
127
  * {@link elementEventAttrs}). Backs the Lua `mxp` table. */
115
128
  onElementEvent?: (name: string, attrs: Record<string, string>) => void;
129
+ /** Columns the main window wraps at — how wide `<HR>` draws its rule.
130
+ * Mudlet's `TMxpClient::getWrapWidth`, whose own fallback is 80. */
131
+ wrapWidth?: () => number;
116
132
  });
117
133
  /**
118
134
  * Lock the parser to secure line mode (or release the lock). Mudlet does
@@ -127,8 +143,15 @@ export declare class MxpParser {
127
143
  reset(): void;
128
144
  /** Parse one raw line (post telnet-strip, post UTF-8 decode), which may carry
129
145
  * ANSI SGR, MXP tags, `ESC[#z` modes, and entities. `baseSnapshot` is the
130
- * carried pen from the previous line. */
131
- parseLine(rawLine: string, baseSnapshot?: FormatStateSnapshot): MxpLineResult;
146
+ * carried pen from the previous line.
147
+ *
148
+ * `fromServer` false means the line was synthesised locally (feedTriggers).
149
+ * Such a line's `ESC[#z` is consumed but does not switch mode, matching
150
+ * Mudlet's `isFromServer` gate on `mMxpProcessor.setMode` — otherwise a
151
+ * script feeding a captured game line would leave the parser in whatever
152
+ * mode that capture ended on, and the next real tag would be discarded as
153
+ * unsafe. */
154
+ parseLine(rawLine: string, baseSnapshot?: FormatStateSnapshot, fromServer?: boolean): MxpLineResult;
132
155
  private effectiveMode;
133
156
  private resetTransientMode;
134
157
  private appendText;
@@ -0,0 +1,4 @@
1
+ export declare function describeCertCode(code: string): string;
2
+ /** Which of Mudlet's three tolerance checkboxes would clear a given fault.
3
+ * `all` means only "accept all certificate errors" covers it. */
4
+ export declare function toleranceForCode(code: string): 'expired' | 'selfSigned' | 'all';
@@ -27,5 +27,22 @@ export declare class ReplayPlayer {
27
27
  /** Stop playback and drop any pending chunk. Idempotent; onDone is not
28
28
  * fired for an aborted replay. */
29
29
  abort(): void;
30
+ /**
31
+ * Deliver every chunk that has come due, without waiting for its setTimeout.
32
+ *
33
+ * The counterpart of TimerEngine.pumpDue, and there for the same caller: a
34
+ * script blocked in waitForEvent/pumpEvents is not running the event loop,
35
+ * so the pending timeout below can never land and a replay started from such
36
+ * a script would sit at chunk zero forever. Mudlet has no equivalent problem
37
+ * — its nested QEventLoop keeps driving the replay timer along with
38
+ * everything else — so this is what standing in for that loop costs.
39
+ *
40
+ * Returns the number of chunks delivered.
41
+ */
42
+ pumpDue(now?: number): number;
43
+ /** When the pending chunk is due, in epoch ms; null when none is scheduled. */
44
+ private dueAt;
45
+ /** Feed the chunk at `index` and schedule the one after it. */
46
+ private deliverPending;
30
47
  private scheduleNext;
31
48
  }
@@ -1,6 +1,9 @@
1
1
  import { FormatState } from './FormatState';
2
2
  import { AnsiAwareBuffer } from './FormatState';
3
3
  import type { FormatStateSnapshot } from './FormatState';
4
+ /** Longest run of characters one echo or insert may add to a line. Mudlet's
5
+ * `TBuffer::MAX_CHARACTERS_PER_ECHO`. */
6
+ export declare const MAX_CHARACTERS_PER_ECHO = 1000000;
4
7
  /**
5
8
  * Self-contained text output entity — equivalent of Mudlet's TConsole.
6
9
  * Owns format state, line history, and cursor position.
@@ -36,6 +39,13 @@ export declare class Console {
36
39
  get batchDeleteSize(): number;
37
40
  setBatchDeleteSize(n: number): void;
38
41
  echo(text: string): void;
42
+ /**
43
+ * Move every newline-terminated line out of the in-flight `partial` and
44
+ * into history, leaving whatever follows the last newline as the new
45
+ * partial. Shared by `echo` and by `insertText`, which can put a newline
46
+ * into the line being built just as an echo can.
47
+ */
48
+ private promotePartialLines;
39
49
  /**
40
50
  * Append a pre-built complete line buffer to history. Used by the network
41
51
  * trigger pipeline: the line has already been parsed and is being added as
@@ -55,6 +65,14 @@ export declare class Console {
55
65
  * `selectCurrentLine` sees the pasted line.
56
66
  */
57
67
  appendBuffer(buffer: AnsiAwareBuffer): void;
68
+ /**
69
+ * The one way a completed line enters history. An OSC 8 link set to reveal
70
+ * itself after a delay is written into the buffer as spaces here and gets
71
+ * its text back when the delay is up — Mudlet does the same, so that a
72
+ * script reading the line back sees what the player can see rather than the
73
+ * secret behind it.
74
+ */
75
+ private store;
58
76
  private evict;
59
77
  /** Drain newly completed lines to hand to the renderer. */
60
78
  takeLines(): AnsiAwareBuffer[];
@@ -82,12 +100,32 @@ export declare class Console {
82
100
  clearPartial(): void;
83
101
  private get cursor();
84
102
  private get followingEnd();
103
+ /**
104
+ * Whether the line under the cursor is the in-flight `partial` — an echo
105
+ * that hasn't seen its newline yet — rather than a finished history line.
106
+ *
107
+ * The distinction matters to every cursor-line operation, not just the
108
+ * readers: Mudlet's cursor sits on the line being built, so `insertText`,
109
+ * `replace` and friends have to reach it there. Reading it but not writing
110
+ * it made `prefix()`/`suffix()` on an unfinished line silent no-ops.
111
+ */
112
+ private get onPartialLine();
85
113
  getLine(): string;
86
114
  getBuffer(): AnsiAwareBuffer | null;
115
+ /** The buffer the cursor is on: the partial when it is following the end,
116
+ * otherwise the history line it was parked on. */
117
+ private currentBuffer;
87
118
  /** Per-line prompt flag on the current cursor line. Mirrors Mudlet's TBuffer
88
119
  * behaviour: `isPrompt()` follows the cursor, so moveCursor + isPrompt can
89
120
  * inspect any historical line's prompt status, not just the most recent. */
90
121
  cursorOnPrompt(): boolean;
122
+ /** Whether the cursor was left one slot beyond the last line — the state
123
+ * deleteLine() leaves behind when it removes the line the cursor was on and
124
+ * there is nothing after it to shift up. Distinct from the ordinary
125
+ * "following the end" cursor (index -1), which every read treats as the last
126
+ * line. Reads cursorIdx rather than `cursor`, which clamps and so can never
127
+ * report either state. */
128
+ cursorPastEnd(): boolean;
91
129
  deleteLine(): void;
92
130
  /**
93
131
  * Insert `text` at the cursor (Mudlet `insertText`). Embedded `\n` split the
@@ -124,6 +162,13 @@ export declare class Console {
124
162
  * `keepHorizontal` move never reports past the end of a shorter line.
125
163
  */
126
164
  getCursorColumn(): number;
165
+ /**
166
+ * The column as it was set, unclamped. Mudlet keeps `mUserCursor.x()` raw
167
+ * and only the operations that write through it decide what a column past
168
+ * the end of the line means — `paste` pads out to it, which the clamped
169
+ * reading above cannot express.
170
+ */
171
+ getCursorColumnRaw(): number;
127
172
  setCursorColumn(col: number): boolean;
128
173
  /** Mark cursor as positioned at the end of existing rendered content, so the
129
174
  * next leading `\n` is treated as cursor advance rather than a blank line.
@@ -179,5 +224,24 @@ export declare class Console {
179
224
  * every continuation, matching setWindowWrapIndent /
180
225
  * setWindowWrapHangingIndent.
181
226
  */
227
+ /**
228
+ * The wrap this console applies to a line as it is stored — the console's
229
+ * own `setWindowWrap` width, so a line longer than it becomes several
230
+ * buffer lines rather than one long one that only *looks* wrapped.
231
+ *
232
+ * Off (width 0) unless a script sets one, which is the state nearly every
233
+ * console is in: the renderer wraps to the panel's real pixel width, and
234
+ * splitting on a column nobody chose would only fight it. Mudlet always
235
+ * splits because its buffer IS its layout.
236
+ */
237
+ setWrapWidth(width: number, indent?: number, hangingIndent?: number): void;
238
+ private wrapWidth;
239
+ private wrapIndent;
240
+ private wrapHangingIndent;
241
+ /**
242
+ * Split one completed line to the console's wrap width, newest-last. With no
243
+ * width set — the usual case — this is the line itself, untouched.
244
+ */
245
+ private toStoredLines;
182
246
  wrapLine(lineNumber: number, wrapAt?: number, indent?: number, hangingIndent?: number): boolean;
183
247
  }
@@ -1,5 +1,5 @@
1
1
  import { type OscPaletteOp } from "./ansiEscapes";
2
- import { HyperlinkPresetRegistry, type HyperlinkConfig } from "./hyperlinkConfig";
2
+ import { HyperlinkPresetRegistry, type HyperlinkConfig, type UnderlineStyle } from "./hyperlinkConfig";
3
3
  /** Apply OSC 4/104 palette operations to the global colour tables. Palette
4
4
  * changes affect text parsed *after* this point — which is exactly document
5
5
  * order, since lines are fed to the parser in the order the server sent them. */
@@ -53,6 +53,9 @@ export interface HexColor {
53
53
  color: string;
54
54
  }
55
55
  export type FormatColor = IndexedColor | RgbColor | HexColor;
56
+ /** One SGR parameter: a number, or its sub-parameters when it carried any
57
+ * (`4:3` → `[4, 3]`). See {@link parseSgrCodes}. */
58
+ export type SgrParam = number | number[];
56
59
  export type DimEasing = 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out';
57
60
  export interface DimEffect {
58
61
  startOpacity: number;
@@ -66,6 +69,9 @@ export interface FormatStateSnapshot {
66
69
  bold?: boolean;
67
70
  italic?: boolean;
68
71
  underline?: boolean;
72
+ /** Which underline SGR asked for (`4:1`..`4:5`). Only meaningful while
73
+ * `underline` is set; absent means the plain solid one. */
74
+ underlineStyle?: UnderlineStyle;
69
75
  inverse?: boolean;
70
76
  strikethrough?: boolean;
71
77
  overline?: boolean;
@@ -84,11 +90,11 @@ declare function cloneState(state?: FormatStateSnapshot): FormatStateSnapshot |
84
90
  declare function statesEqual(a?: FormatStateSnapshot, b?: FormatStateSnapshot): boolean;
85
91
  export declare class FormatState {
86
92
  static DEFAULT: {};
87
- foreground?: FormatColor;
88
93
  background?: FormatColor;
89
94
  bold?: boolean;
90
95
  italic?: boolean;
91
96
  underline?: boolean;
97
+ underlineStyle?: UnderlineStyle;
92
98
  inverse?: boolean;
93
99
  strikethrough?: boolean;
94
100
  overline?: boolean;
@@ -96,13 +102,66 @@ export declare class FormatState {
96
102
  rapidBlink?: boolean;
97
103
  dim?: DimEffect;
98
104
  hyperlink?: FormatHyperlink;
105
+ private fgNormal?;
106
+ private fgLight?;
107
+ /** True while the foreground is the profile's own — as it starts, and as
108
+ * SGR 0 and SGR 39 leave it. Bold never brightens a default foreground. */
109
+ private fgIsDefault;
110
+ private _foreground?;
111
+ /** The foreground in force, both variants already resolved. */
112
+ get foreground(): FormatColor | undefined;
113
+ /** Writing the foreground from outside the SGR decoder — `setFgColor`, MXP
114
+ * colour attributes, link styling — names one colour and knows nothing of
115
+ * the bold pair, so it becomes both variants. Without that a later bold
116
+ * would re-resolve to whatever the escape stream had left behind and undo
117
+ * the write. */
118
+ set foreground(color: FormatColor | undefined);
99
119
  constructor(initial?: FormatStateSnapshot);
100
120
  private applySnapshot;
101
121
  reset(): void;
102
122
  toSnapshot(): FormatStateSnapshot;
103
- applySgr(params: number[]): void;
123
+ applySgr(params: SgrParam[]): void;
124
+ /**
125
+ * Apply one SGR parameter that carried sub-parameters (`4:3`, `38:5:196`).
126
+ *
127
+ * Only the underline styles need reading apart from the flat form: an
128
+ * extended colour means the same written either way, so it is handed back to
129
+ * the ordinary path with its sub-parameters flattened.
130
+ */
131
+ private applySgrGroup;
132
+ /**
133
+ * `38:…` / `48:…` — an extended colour written with sub-parameters.
134
+ *
135
+ * The colon form carries one element the semicolon form does not: a colour
136
+ * space identifier sits between the `2` and the red component, almost always
137
+ * empty (`38:2::255:0:0`). So the components are the 4th, 5th and 6th
138
+ * sub-parameters here where they are the 3rd, 4th and 5th parameters there.
139
+ * Missing components are zero, as in Mudlet's decodeSGR38.
140
+ */
141
+ private applyExtendedColorGroup;
142
+ /** Record a foreground and the colour bold should show instead of it, then
143
+ * resolve which of the two is in force. Pass the same colour twice when it
144
+ * has no brighter twin. */
145
+ private setForeground;
146
+ /** Pick the variant the current bold state calls for. Mudlet writes each
147
+ * cell as `(!mIsDefaultColor && mBold) ? light : normal` — a default
148
+ * foreground stays default however bold the text is. */
149
+ private resolveForeground;
104
150
  setHyperlink(link?: FormatHyperlink): void;
105
151
  }
152
+ /**
153
+ * Split an SGR sequence into its parameters. `;` separates parameters and `:`
154
+ * their sub-parameters (ECMA-48 / ITU T.416), so a parameter that carries
155
+ * sub-parameters comes back as an array and a plain one as a number.
156
+ *
157
+ * The distinction matters for exactly one reason, but it matters a lot: `4:3` is
158
+ * a *curly underline*, one parameter with a sub-parameter, while `4;3` is an
159
+ * underline followed by italics. Flattening both — which mudix did, to make
160
+ * `38:5:1` work alongside `38;5;1` — turned every styled underline into an
161
+ * accidental italic. Extended colours still read either form, since applySgr
162
+ * looks ahead across parameters for the `38;5;n` spelling.
163
+ */
164
+ export declare function parseSgrCodes(sequence: string): SgrParam[];
106
165
  /**
107
166
  * A run of text with its visual attributes resolved to concrete CSS colour
108
167
  * strings — the shape a canvas/image renderer wants. `color`/`background` are
@@ -165,6 +224,10 @@ export declare class AnsiAwareBuffer {
165
224
  remove(range: [number, number]): this;
166
225
  /** @internal */
167
226
  getSegments(): BufferSegment[];
227
+ /** Whether any segment carries an OSC 8 link with `visibility` settings.
228
+ * A cheap pre-check (no cloning, no grouping) for the concealment pass,
229
+ * which every stored line goes through. */
230
+ hasVisibilityLink(): boolean;
168
231
  toHyperlinkSegments(): {
169
232
  text: string;
170
233
  hyperlink?: FormatHyperlink;
@@ -15,8 +15,15 @@
15
15
  * (owned by the session) conceals them when the matching session event fires.
16
16
  * The first occurrence of each trigger is skipped: it's the response to the very
17
17
  * command the click sent, not a fresh event.
18
+ *
19
+ * **The buffer half.** Hiding an element is not enough for a *delayed reveal*:
20
+ * Mudlet writes such a link into its buffer as spaces and puts the text back
21
+ * when the delay is up, so a script reading the line back (`getLines`) sees what
22
+ * the player sees rather than the secret. {@link concealDelayedReveals} does the
23
+ * same to a stored line here — see the section at the foot of this file.
18
24
  */
19
25
  import type { VisibilitySettings } from "./hyperlinkConfig";
26
+ import type { AnsiAwareBuffer } from "./FormatState";
20
27
  /** Wire one link element's visibility behaviour. Call *after* the element's base
21
28
  * style has been applied (it may set `visibility: hidden`, which a later
22
29
  * `cssText` assignment would wipe). */
@@ -35,3 +42,31 @@ export declare class HyperlinkVisibilityController {
35
42
  onOutput(): void;
36
43
  private fire;
37
44
  }
45
+ /**
46
+ * Whether a link with these settings is written concealed and revealed later.
47
+ *
48
+ * Only the timed reveals are, unlike Mudlet, which also conceals a reveal armed
49
+ * on an expire trigger. mudix drives expire triggers off the rendered element
50
+ * (see {@link HyperlinkVisibilityController}), and that path only ever conceals
51
+ * — a link the buffer blanked with nothing to un-blank it would lose its text
52
+ * for good, which is worse than showing it early.
53
+ */
54
+ export declare function startsConcealed(vis: VisibilitySettings): boolean;
55
+ /**
56
+ * Blank out every delayed-reveal link on a freshly stored line and schedule the
57
+ * text to come back. Called once per line as it enters a console's history.
58
+ */
59
+ export declare function concealDelayedReveals(line: AnsiAwareBuffer): void;
60
+ /**
61
+ * Put back the text of every concealed link whose delay has elapsed, and arm a
62
+ * timer for the earliest one still waiting. Returns true when a line changed.
63
+ *
64
+ * Also the hook the busted harness drives: a spec runs as one synchronous call
65
+ * on top of the event loop, so no `setTimeout` of ours can fire until it
66
+ * returns, and `pumpEvents` calls in here instead.
67
+ */
68
+ export declare function pumpDelayedReveals(now?: number): boolean;
69
+ /** Drop everything still waiting — a profile teardown, or a test resetting
70
+ * between cases. The lines themselves are left as they are; they are going
71
+ * away with the console. */
72
+ export declare function resetDelayedReveals(): void;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Mudlet's `!osc8-docs` easter egg — the worked OSC 8 examples banner
3
+ * (`TBuffer::injectOSC8DocumentationExamples`).
4
+ *
5
+ * A line printed through the echo family that holds the phrase is swallowed and
6
+ * this banner goes into the MAIN console instead, whichever window the echo was
7
+ * addressed to. It is the one page of OSC 8 documentation that can be read
8
+ * without leaving the client, so every escape in it is load-bearing: the text is
9
+ * a verbatim port of Mudlet's, kept one source line per array entry so the two
10
+ * can be diffed after a re-sync.
11
+ *
12
+ * Not every printing path reaches it — what the game sends and what a trigger
13
+ * echoes onto the matched line print the phrase as ordinary text, exactly as in
14
+ * Mudlet, where those go through `commitLine()` rather than `appendLine()`.
15
+ */
16
+ /** The phrase itself, split so that this file does not set off its own check
17
+ * when something echoes a line of it. */
18
+ export declare const OSC8_DOCS_PHRASE: string;
19
+ /** Once a second at most — an echo and the server response that repeats it must
20
+ * not print the banner twice. Mudlet's `mLastOSC8DocsInjectionTime` window. */
21
+ export declare const OSC8_DOCS_DEBOUNCE_MS = 1000;
22
+ /** The banner as one blob, ready to hand to `Console.echo`. */
23
+ export declare function osc8DocumentationExamples(): string;