tinky-mouse 0.1.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.
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getMouseEventName = getMouseEventName;
4
+ exports.parseSGRMouseEvent = parseSGRMouseEvent;
5
+ exports.parseX11MouseEvent = parseX11MouseEvent;
6
+ exports.parseMouseEvent = parseMouseEvent;
7
+ exports.isIncompleteMouseSequence = isIncompleteMouseSequence;
8
+ var sequences_js_1 = require("./sequences.js");
9
+ /**
10
+ * Derives the `MouseEventName` from the SGR button code and release state.
11
+ *
12
+ * @param buttonCode - The numerical code from the SGR sequence.
13
+ * @param isRelease - Whether the event is a release event (last char is 'm').
14
+ * @returns The `MouseEventName` or `null` if unknown.
15
+ */
16
+ function getMouseEventName(buttonCode, isRelease) {
17
+ var isMove = (buttonCode & 32) !== 0;
18
+ if (buttonCode === 66) {
19
+ return "scroll-left";
20
+ }
21
+ else if (buttonCode === 67) {
22
+ return "scroll-right";
23
+ }
24
+ else if ((buttonCode & 64) === 64) {
25
+ if ((buttonCode & 1) === 0) {
26
+ return "scroll-up";
27
+ }
28
+ else {
29
+ return "scroll-down";
30
+ }
31
+ }
32
+ else if (isMove) {
33
+ return "move";
34
+ }
35
+ else {
36
+ var button = buttonCode & 3;
37
+ var type = isRelease ? "release" : "press";
38
+ switch (button) {
39
+ case 0:
40
+ return "left-".concat(type);
41
+ case 1:
42
+ return "middle-".concat(type);
43
+ case 2:
44
+ return "right-".concat(type);
45
+ default:
46
+ return null;
47
+ }
48
+ }
49
+ }
50
+ function getButtonFromCode(code) {
51
+ var button = code & 3;
52
+ switch (button) {
53
+ case 0:
54
+ return "left";
55
+ case 1:
56
+ return "middle";
57
+ case 2:
58
+ return "right";
59
+ default:
60
+ return "none";
61
+ }
62
+ }
63
+ /**
64
+ * Parses an SGR (1006) mouse sequence.
65
+ *
66
+ * @param buffer - The input string containing the sequence.
67
+ * @returns An object containing the parsed event and the length of the matched sequence, or `null` if not an SGR sequence.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * parseSGRMouseEvent("\x1b[<0;10;10M"); // Left press at 10,10
72
+ * ```
73
+ */
74
+ function parseSGRMouseEvent(buffer) {
75
+ var match = buffer.match(sequences_js_1.SGR_MOUSE_REGEX);
76
+ if (match) {
77
+ var buttonCode = parseInt(match[1], 10);
78
+ var col = parseInt(match[2], 10);
79
+ var row = parseInt(match[3], 10);
80
+ var action = match[4];
81
+ var isRelease = action === "m";
82
+ var shift = (buttonCode & 4) !== 0;
83
+ var meta = (buttonCode & 8) !== 0;
84
+ var ctrl = (buttonCode & 16) !== 0;
85
+ var name_1 = getMouseEventName(buttonCode, isRelease);
86
+ if (name_1) {
87
+ return {
88
+ event: {
89
+ name: name_1,
90
+ ctrl: ctrl,
91
+ meta: meta,
92
+ shift: shift,
93
+ col: col,
94
+ row: row,
95
+ button: getButtonFromCode(buttonCode),
96
+ },
97
+ length: match[0].length,
98
+ };
99
+ }
100
+ return null;
101
+ }
102
+ return null;
103
+ }
104
+ /**
105
+ * Parses an X11 mouse sequence.
106
+ *
107
+ * @param buffer - The input string containing the sequence.
108
+ * @returns An object containing the parsed event and the length of the matched sequence, or `null` if not an X11 sequence.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * parseX11MouseEvent("\x1b[M..."); // Parsed X11 event
113
+ * ```
114
+ */
115
+ function parseX11MouseEvent(buffer) {
116
+ var match = buffer.match(sequences_js_1.X11_MOUSE_REGEX);
117
+ if (!match)
118
+ return null;
119
+ // The 3 bytes are in match[1]
120
+ var b = match[1].charCodeAt(0) - 32;
121
+ var col = match[1].charCodeAt(1) - 32;
122
+ var row = match[1].charCodeAt(2) - 32;
123
+ var shift = (b & 4) !== 0;
124
+ var meta = (b & 8) !== 0;
125
+ var ctrl = (b & 16) !== 0;
126
+ var isMove = (b & 32) !== 0;
127
+ var isWheel = (b & 64) !== 0;
128
+ var name = null;
129
+ if (isWheel) {
130
+ var button = b & 3;
131
+ switch (button) {
132
+ case 0:
133
+ name = "scroll-up";
134
+ break;
135
+ case 1:
136
+ name = "scroll-down";
137
+ break;
138
+ default:
139
+ break;
140
+ }
141
+ }
142
+ else if (isMove) {
143
+ name = "move";
144
+ }
145
+ else {
146
+ var button = b & 3;
147
+ if (button === 3) {
148
+ // X11 reports 'release' (3) for all button releases without specifying which one.
149
+ // We'll default to 'left-release' as a best-effort guess if we don't track state.
150
+ name = "left-release";
151
+ }
152
+ else {
153
+ switch (button) {
154
+ case 0:
155
+ name = "left-press";
156
+ break;
157
+ case 1:
158
+ name = "middle-press";
159
+ break;
160
+ case 2:
161
+ name = "right-press";
162
+ break;
163
+ default:
164
+ break;
165
+ }
166
+ }
167
+ }
168
+ if (name) {
169
+ var button = getButtonFromCode(b);
170
+ if (name === "left-release" && button === "none") {
171
+ button = "left";
172
+ }
173
+ return {
174
+ event: {
175
+ name: name,
176
+ ctrl: ctrl,
177
+ meta: meta,
178
+ shift: shift,
179
+ col: col,
180
+ row: row,
181
+ button: button,
182
+ },
183
+ length: match[0].length,
184
+ };
185
+ }
186
+ return null;
187
+ }
188
+ /**
189
+ * Attempts to parse a mouse event from the buffer, trying both SGR and X11 formats.
190
+ *
191
+ * @param buffer - The input string.
192
+ * @returns The parsed event and length, or `null`.
193
+ */
194
+ function parseMouseEvent(buffer) {
195
+ return parseSGRMouseEvent(buffer) || parseX11MouseEvent(buffer);
196
+ }
197
+ /**
198
+ * Checks if the buffer contains an incomplete mouse sequence.
199
+ * This is useful for knowing if we should wait for more data before processing.
200
+ *
201
+ * @param buffer - The input string.
202
+ * @returns `true` if the buffer looks like the start of a mouse sequence but is incomplete.
203
+ */
204
+ function isIncompleteMouseSequence(buffer) {
205
+ if (!(0, sequences_js_1.couldBeMouseSequence)(buffer))
206
+ return false;
207
+ // If it matches a complete sequence, it's not incomplete.
208
+ if (parseMouseEvent(buffer))
209
+ return false;
210
+ if (buffer.startsWith(sequences_js_1.X11_EVENT_PREFIX)) {
211
+ // X11 needs exactly 3 bytes after prefix.
212
+ return buffer.length < sequences_js_1.X11_EVENT_PREFIX.length + 3;
213
+ }
214
+ if (buffer.startsWith(sequences_js_1.SGR_EVENT_PREFIX)) {
215
+ // SGR sequences end with 'm' or 'M'.
216
+ // If it doesn't have it yet, it's incomplete.
217
+ // Add a reasonable max length check to fail early on garbage.
218
+ return !/[mM]/.test(buffer) && buffer.length < 50;
219
+ }
220
+ // It's a prefix of the prefix (e.g. "ESC" or "ESC [")
221
+ return true;
222
+ }
@@ -0,0 +1,52 @@
1
+ /** ESC character (ASCII 27). */
2
+ export declare const ESC = "\u001B";
3
+ /** Prefix for SGR (1006) mouse sequences. */
4
+ export declare const SGR_EVENT_PREFIX = "\u001B[<";
5
+ /** Prefix for X11 mouse sequences. */
6
+ export declare const X11_EVENT_PREFIX = "\u001B[M";
7
+ /** Regex to match a complete SGR mouse event sequence. */
8
+ export declare const SGR_MOUSE_REGEX: RegExp;
9
+ /** Regex to match a complete X11 mouse event sequence. */
10
+ export declare const X11_MOUSE_REGEX: RegExp;
11
+ /**
12
+ * Checks if the buffer starts with or is a prefix of an SGR mouse sequence.
13
+ *
14
+ * @param buffer - The input string buffer.
15
+ * @returns `true` if it could be an SGR sequence, `false` otherwise.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * couldBeSGRMouseSequence("\x1b[<"); // true
20
+ * couldBeSGRMouseSequence("\x1b[<0;0;0M"); // true
21
+ * couldBeSGRMouseSequence("hello"); // false
22
+ * ```
23
+ */
24
+ export declare function couldBeSGRMouseSequence(buffer: string): boolean;
25
+ /**
26
+ * Checks if the buffer starts with or is a prefix of any known mouse sequence (SGR or X11).
27
+ *
28
+ * @param buffer - The input string buffer.
29
+ * @returns `true` if it could be a mouse sequence, `false` otherwise.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * couldBeMouseSequence("\x1b[M"); // true
34
+ * couldBeMouseSequence("\x1b[<"); // true
35
+ * ```
36
+ */
37
+ export declare function couldBeMouseSequence(buffer: string): boolean;
38
+ /**
39
+ * Checks if the buffer *starts* with a complete mouse sequence.
40
+ * Returns the length of the sequence if matched, or 0 if not.
41
+ *
42
+ * @param buffer - The input string buffer.
43
+ * @returns The length of the sequence in bytes/characters, or 0 if no complete sequence is found at the start.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * getMouseSequenceLength("\x1b[<0;10;10M"); // Returns length of the sequence
48
+ * getMouseSequenceLength("\x1b[M "); // Returns length of X11 sequence
49
+ * getMouseSequenceLength("not mouse"); // 0
50
+ * ```
51
+ */
52
+ export declare function getMouseSequenceLength(buffer: string): number;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.X11_MOUSE_REGEX = exports.SGR_MOUSE_REGEX = exports.X11_EVENT_PREFIX = exports.SGR_EVENT_PREFIX = exports.ESC = void 0;
4
+ exports.couldBeSGRMouseSequence = couldBeSGRMouseSequence;
5
+ exports.couldBeMouseSequence = couldBeMouseSequence;
6
+ exports.getMouseSequenceLength = getMouseSequenceLength;
7
+ /* eslint-disable no-control-regex */
8
+ /** ESC character (ASCII 27). */
9
+ exports.ESC = "\u001B";
10
+ /** Prefix for SGR (1006) mouse sequences. */
11
+ exports.SGR_EVENT_PREFIX = "".concat(exports.ESC, "[<");
12
+ /** Prefix for X11 mouse sequences. */
13
+ exports.X11_EVENT_PREFIX = "".concat(exports.ESC, "[M");
14
+ /** Regex to match a complete SGR mouse event sequence. */
15
+ exports.SGR_MOUSE_REGEX = /^\x1b\[<(\d+);(\d+);(\d+)([mM])/; // SGR mouse events
16
+ // X11 is ESC [ M followed by 3 bytes.
17
+ /** Regex to match a complete X11 mouse event sequence. */
18
+ exports.X11_MOUSE_REGEX = /^\x1b\[M([\s\S]{3})/;
19
+ /**
20
+ * Checks if the buffer starts with or is a prefix of an SGR mouse sequence.
21
+ *
22
+ * @param buffer - The input string buffer.
23
+ * @returns `true` if it could be an SGR sequence, `false` otherwise.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * couldBeSGRMouseSequence("\x1b[<"); // true
28
+ * couldBeSGRMouseSequence("\x1b[<0;0;0M"); // true
29
+ * couldBeSGRMouseSequence("hello"); // false
30
+ * ```
31
+ */
32
+ function couldBeSGRMouseSequence(buffer) {
33
+ if (buffer.length === 0)
34
+ return true;
35
+ // Check if buffer is a prefix of a mouse sequence starter
36
+ if (exports.SGR_EVENT_PREFIX.startsWith(buffer))
37
+ return true;
38
+ // Check if buffer is a mouse sequence prefix
39
+ if (buffer.startsWith(exports.SGR_EVENT_PREFIX))
40
+ return true;
41
+ return false;
42
+ }
43
+ /**
44
+ * Checks if the buffer starts with or is a prefix of any known mouse sequence (SGR or X11).
45
+ *
46
+ * @param buffer - The input string buffer.
47
+ * @returns `true` if it could be a mouse sequence, `false` otherwise.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * couldBeMouseSequence("\x1b[M"); // true
52
+ * couldBeMouseSequence("\x1b[<"); // true
53
+ * ```
54
+ */
55
+ function couldBeMouseSequence(buffer) {
56
+ if (buffer.length === 0)
57
+ return true;
58
+ // Check SGR prefix
59
+ if (exports.SGR_EVENT_PREFIX.startsWith(buffer) ||
60
+ buffer.startsWith(exports.SGR_EVENT_PREFIX))
61
+ return true;
62
+ // Check X11 prefix
63
+ if (exports.X11_EVENT_PREFIX.startsWith(buffer) ||
64
+ buffer.startsWith(exports.X11_EVENT_PREFIX))
65
+ return true;
66
+ return false;
67
+ }
68
+ /**
69
+ * Checks if the buffer *starts* with a complete mouse sequence.
70
+ * Returns the length of the sequence if matched, or 0 if not.
71
+ *
72
+ * @param buffer - The input string buffer.
73
+ * @returns The length of the sequence in bytes/characters, or 0 if no complete sequence is found at the start.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * getMouseSequenceLength("\x1b[<0;10;10M"); // Returns length of the sequence
78
+ * getMouseSequenceLength("\x1b[M "); // Returns length of X11 sequence
79
+ * getMouseSequenceLength("not mouse"); // 0
80
+ * ```
81
+ */
82
+ function getMouseSequenceLength(buffer) {
83
+ var sgrMatch = buffer.match(exports.SGR_MOUSE_REGEX);
84
+ if (sgrMatch)
85
+ return sgrMatch[0].length;
86
+ var x11Match = buffer.match(exports.X11_MOUSE_REGEX);
87
+ if (x11Match)
88
+ return x11Match[0].length;
89
+ return 0;
90
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "tinky-mouse",
3
+ "version": "0.1.0",
4
+ "description": "Mouse event handling for tinky applications",
5
+ "keywords": [
6
+ "tinky",
7
+ "mouse",
8
+ "event",
9
+ "input",
10
+ "terminal",
11
+ "interaction"
12
+ ],
13
+ "homepage": "https://github.com/ByteLandTechnology/tinky-mouse#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/ByteLandTechnology/tinky-mouse/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/ByteLandTechnology/tinky-mouse.git"
20
+ },
21
+ "license": "Apache-2.0",
22
+ "author": {
23
+ "name": "ByteLand Technology Limited"
24
+ },
25
+ "type": "module",
26
+ "main": "./lib/index.js",
27
+ "scripts": {
28
+ "test": "bun test",
29
+ "build": "tsc && npm run docs",
30
+ "lint": "eslint src tests",
31
+ "prepublish": "npm run build",
32
+ "prepare": "husky",
33
+ "docs": "typedoc --plugin typedoc-plugin-markdown --disableSources --out docs/api && prettier --write docs/api"
34
+ },
35
+ "files": [
36
+ "./bin/*",
37
+ "./lib/*"
38
+ ],
39
+ "typings": "./lib/index.d.ts",
40
+ "peerDependencies": {
41
+ "tinky": "^1.2.2"
42
+ },
43
+ "devDependencies": {
44
+ "@commitlint/cli": "^20.3.0",
45
+ "@commitlint/config-conventional": "^20.3.0",
46
+ "@semantic-release/changelog": "^6.0.3",
47
+ "@semantic-release/commit-analyzer": "^13.0.1",
48
+ "@semantic-release/git": "^10.0.1",
49
+ "@semantic-release/github": "^12.0.2",
50
+ "@semantic-release/npm": "^13.1.3",
51
+ "@semantic-release/release-notes-generator": "^14.1.0",
52
+ "@types/bun": "^1.3.6",
53
+ "conventional-changelog-conventionalcommits": "^9.1.0",
54
+ "eslint": "^9.39.2",
55
+ "eslint-plugin-react": "^7.37.5",
56
+ "husky": "^9.1.7",
57
+ "jiti": "^2.6.1",
58
+ "lint-staged": "^16.2.7",
59
+ "prettier": "^3.7.4",
60
+ "semantic-release": "^25.0.2",
61
+ "typedoc": "^0.28.16",
62
+ "typedoc-plugin-markdown": "^4.9.0",
63
+ "typescript-eslint": "^8.53.0"
64
+ },
65
+ "commitlint": {
66
+ "extends": [
67
+ "@commitlint/config-conventional"
68
+ ]
69
+ },
70
+ "lint-staged": {
71
+ "*.{js,ts,jsx,tsx,json,md,yaml,yml}": "prettier --write"
72
+ }
73
+ }