clarity-ai 7.2.0 → 7.2.1
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.
- package/CHANGELOG.md +8 -0
- package/bin/clarity.js +3 -3
- package/package.json +1 -1
- package/src/app.js +19 -29
- package/src/components/Banner.js +12 -17
- package/src/components/SlashPopup.js +13 -11
- package/src/components/StreamView.js +18 -17
- package/src/hooks/useMouse.js +0 -45
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
## 7.2.1 (2026-06-06)
|
|
6
|
+
|
|
7
|
+
### Critical hotfix: mouse bleed elimination, CLARITY branding, TrueColor textures
|
|
8
|
+
- **FIXED input corruption**: Removed XTerm mouse tracking (`\x1b[?1000h`) entirely. Raw `\x1b[<0;54;41M` sequences no longer leak into the chat text buffer. Termux soft keyboard now works reliably without raw-mode conflicts.
|
|
9
|
+
- **FIXED ASCII logo**: Replaced the figlet banner that rendered as "GLARITY" with a clean 5-line solid block matrix (`██████`) that unambiguously spells CLARITY. Three-tier sizing (5-line / 3-line compact / 1-line gradient).
|
|
10
|
+
- **TrueColor panel textures**: Every message block, streaming pane, and popup container now uses explicit `#1C1C1C` graphite backgrounds. No naked terminal defaults. Orange `#FF9F43` full-width selection bar with `#000000` black text on all active popup rows.
|
|
11
|
+
- **Stderr isolation maintained**: `console.log/error/warn` continue routing to `clarity-debug.log` to prevent stray backend traces from corrupting the UI layer.
|
|
12
|
+
|
|
5
13
|
## 7.2.0 (2026-06-06)
|
|
6
14
|
|
|
7
15
|
### Sticky floating overlay engine — touch selection, phase-driven layout, virtualized log
|
package/bin/clarity.js
CHANGED
|
@@ -57,14 +57,14 @@ async function main() {
|
|
|
57
57
|
logFile.write('[CLEANUP] CLARITY exiting\n');
|
|
58
58
|
logFile.end();
|
|
59
59
|
try { clear(); } catch {}
|
|
60
|
-
process.stdout.write('\x1b[?
|
|
60
|
+
process.stdout.write('\x1b[?25h\x1b[0m');
|
|
61
61
|
process.exit(0);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
process.on('SIGINT', () => cleanup());
|
|
65
65
|
process.on('SIGTERM', () => cleanup());
|
|
66
66
|
process.on('exit', () => {
|
|
67
|
-
process.stdout.write('\x1b[?
|
|
67
|
+
process.stdout.write('\x1b[?25h\x1b[0m');
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
await new Promise(() => {});
|
|
@@ -76,7 +76,7 @@ main().catch(err => {
|
|
|
76
76
|
console.warn = originalWarn;
|
|
77
77
|
logFile.write('[FATAL] ' + (err?.message || String(err)) + '\n');
|
|
78
78
|
logFile.end();
|
|
79
|
-
process.stdout.write('\x1b[?
|
|
79
|
+
process.stdout.write('\x1b[?25h\x1b[0m');
|
|
80
80
|
console.error('\n\x1b[31mFatal error:\x1b[0m', err?.message || err);
|
|
81
81
|
process.exit(1);
|
|
82
82
|
});
|
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -8,7 +8,6 @@ import { StreamView } from './components/StreamView.js';
|
|
|
8
8
|
import { InputDock } from './components/InputDock.js';
|
|
9
9
|
import { SlashPopup } from './components/SlashPopup.js';
|
|
10
10
|
import { Footer } from './components/Footer.js';
|
|
11
|
-
import { useMouse } from './hooks/useMouse.js';
|
|
12
11
|
const { createElement: h } = React;
|
|
13
12
|
|
|
14
13
|
let abortController = null;
|
|
@@ -24,6 +23,12 @@ const FOOTER_H = 2;
|
|
|
24
23
|
const STATUS_H = 1;
|
|
25
24
|
const POPUP_W = 44;
|
|
26
25
|
|
|
26
|
+
const COMMAND_ITEMS = [
|
|
27
|
+
{ name: '/keys' }, { name: '/model' }, { name: '/provider' },
|
|
28
|
+
{ name: '/agent' }, { name: '/stop' }, { name: '/clear' },
|
|
29
|
+
{ name: '/export' }, { name: '/help' }, { name: '/exit' },
|
|
30
|
+
];
|
|
31
|
+
|
|
27
32
|
function deriveStatus(state, sc) {
|
|
28
33
|
if (!state.thinking) return 'idle';
|
|
29
34
|
return sc ? 'streaming' : 'thinking';
|
|
@@ -33,6 +38,12 @@ function hasRealMessages(msgs) {
|
|
|
33
38
|
return msgs.some(m => m.role === 'user' || m.role === 'assistant');
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
function getFiltered(input) {
|
|
42
|
+
const q = input.replace(/^\//, '').toLowerCase();
|
|
43
|
+
if (!q) return COMMAND_ITEMS;
|
|
44
|
+
return COMMAND_ITEMS.filter(c => c.name.includes(q));
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
export function App({ config }) {
|
|
37
48
|
const { stdout } = useStdout();
|
|
38
49
|
const [dims, setDims] = useState({ rows: stdout.rows || 30, cols: stdout.columns || 80 });
|
|
@@ -54,7 +65,7 @@ export function App({ config }) {
|
|
|
54
65
|
|
|
55
66
|
const rows = dims.rows;
|
|
56
67
|
const cols = dims.cols;
|
|
57
|
-
const bannerH = cols < 50 ? 2 :
|
|
68
|
+
const bannerH = cols < 50 ? 2 : (cols < 60 ? 3 : 5);
|
|
58
69
|
const status = deriveStatus(state, streamContent);
|
|
59
70
|
const isChat = hasRealMessages(state.messages);
|
|
60
71
|
const cardW = Math.min(cols - 4, 56);
|
|
@@ -62,7 +73,7 @@ export function App({ config }) {
|
|
|
62
73
|
const availLines = Math.max(2, rows - bannerH - STATUS_H - DOCK_H - FOOTER_H - 2);
|
|
63
74
|
|
|
64
75
|
useEffect(() => {
|
|
65
|
-
function onResize() { setDims({
|
|
76
|
+
function onResize() { setDims({ rows: process.stdout.rows || 30, cols: process.stdout.columns || 80 }); }
|
|
66
77
|
process.stdout.on('resize', onResize);
|
|
67
78
|
return () => process.stdout.removeListener('resize', onResize);
|
|
68
79
|
}, []);
|
|
@@ -85,8 +96,8 @@ export function App({ config }) {
|
|
|
85
96
|
}, []);
|
|
86
97
|
|
|
87
98
|
const onCommand = useCallback(async (val) => {
|
|
88
|
-
if (val
|
|
89
|
-
if (val
|
|
99
|
+
if (val === '/stop') { cancelStream(); return; }
|
|
100
|
+
if (val === '/exit') { process.exit(0); return; }
|
|
90
101
|
await handleCommand(val, stateRef.current, setState, setModel, setProvider, modelRef.current, providerRef.current);
|
|
91
102
|
}, []);
|
|
92
103
|
|
|
@@ -108,7 +119,6 @@ export function App({ config }) {
|
|
|
108
119
|
setInput('');
|
|
109
120
|
setPopupIdx(0);
|
|
110
121
|
if (cmd === '/exit') process.exit(0);
|
|
111
|
-
if (cmd === '/stop') { cancelStream(); return; }
|
|
112
122
|
onCommand(cmd);
|
|
113
123
|
}
|
|
114
124
|
|
|
@@ -118,41 +128,21 @@ export function App({ config }) {
|
|
|
118
128
|
if (!showPopup) return;
|
|
119
129
|
if (key.escape) { closePopup(); return; }
|
|
120
130
|
if (key.return) {
|
|
121
|
-
const
|
|
122
|
-
const q = input.replace(/^\//, '').toLowerCase();
|
|
123
|
-
const filtered = q ? COMMANDS.filter(c => c.name.includes(q)) : COMMANDS;
|
|
131
|
+
const filtered = getFiltered(input);
|
|
124
132
|
if (filtered[popupIdx]) handlePopupSelect(filtered[popupIdx].name);
|
|
125
133
|
return;
|
|
126
134
|
}
|
|
127
135
|
if (key.upArrow) { setPopupIdx(i => Math.max(0, i - 1)); return; }
|
|
128
136
|
if (key.downArrow) {
|
|
129
|
-
const
|
|
130
|
-
const q = input.replace(/^\//, '').toLowerCase();
|
|
131
|
-
const filtered = q ? COMMANDS.filter(c => c.name.includes(q)) : COMMANDS;
|
|
137
|
+
const filtered = getFiltered(input);
|
|
132
138
|
setPopupIdx(i => Math.min(filtered.length - 1, i + 1));
|
|
133
139
|
}
|
|
134
140
|
});
|
|
135
141
|
|
|
136
|
-
const handleClick = useCallback(({ col, row }) => {
|
|
137
|
-
if (!showPopup) return;
|
|
138
|
-
const popupLeft = Math.floor((cols - POPUP_W) / 2) + 1;
|
|
139
|
-
const popupH = 1 + 9 + 1;
|
|
140
|
-
const popupTop = rows - DOCK_H - FOOTER_H - popupH;
|
|
141
|
-
if (col >= popupLeft && col < popupLeft + POPUP_W && row >= popupTop && row < popupTop + popupH) {
|
|
142
|
-
const itemRow = row - popupTop - 1;
|
|
143
|
-
const COMMANDS = [{ name: '/keys' }, { name: '/model' }, { name: '/provider' }, { name: '/agent' }, { name: '/stop' }, { name: '/clear' }, { name: '/export' }, { name: '/help' }, { name: '/exit' }];
|
|
144
|
-
if (itemRow >= 0 && itemRow < COMMANDS.length) {
|
|
145
|
-
handlePopupSelect(COMMANDS[itemRow].name);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}, [showPopup, cols, rows]);
|
|
149
|
-
|
|
150
|
-
useMouse(handleClick);
|
|
151
|
-
|
|
152
142
|
const bottomArea = h(Box, { flexDirection: 'column', alignItems: 'center', width: '100%' },
|
|
153
143
|
showPopup
|
|
154
144
|
? h(Box, { position: 'absolute', bottom: DOCK_H + FOOTER_H, alignItems: 'center', width: '100%' },
|
|
155
|
-
h(SlashPopup, { search: input, selectedIdx: popupIdx,
|
|
145
|
+
h(SlashPopup, { search: input, selectedIdx: popupIdx, width: POPUP_W })
|
|
156
146
|
)
|
|
157
147
|
: null,
|
|
158
148
|
h(InputDock, {
|
package/src/components/Banner.js
CHANGED
|
@@ -3,22 +3,18 @@ import { Box, Text } from 'ink';
|
|
|
3
3
|
import { appGradient } from '../config/theme.js';
|
|
4
4
|
const { createElement: h } = React;
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
' ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ',
|
|
6
|
+
const BANNER = [
|
|
7
|
+
'██████ ██ █████ ██████ ██ ████████ ██ ██',
|
|
8
|
+
'██ ██ ██ ██ ██ ██ ██ ██ ██ ██',
|
|
9
|
+
'██ ██ ███████ ██████ ██ ██ ████',
|
|
10
|
+
'██ ██ ██ ██ ██ ██ ██ ██ ██',
|
|
11
|
+
'██████ ███████ ██ ██ ██ ██ ██ ██ ██',
|
|
13
12
|
];
|
|
14
13
|
|
|
15
|
-
const
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'╚════██║ ██║ ██╔══██║██╔══██╗██║ ██║ ╚██╔╝ ',
|
|
20
|
-
'██████╔╝ ███████╗██║ ██║██║ ██║██║ ██║ ██║ ',
|
|
21
|
-
'╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ',
|
|
14
|
+
const BANNER_COMPACT = [
|
|
15
|
+
'██████ ██ █████ ██████ ██ ████████ ██ ██',
|
|
16
|
+
'██ ██ ██ ██ ██ ██ ██ ██ ██ ██',
|
|
17
|
+
'██████ ███████ ██ ██ ██ ██ ██ ██ ██',
|
|
22
18
|
];
|
|
23
19
|
|
|
24
20
|
export function Banner({ cols }) {
|
|
@@ -28,10 +24,9 @@ export function Banner({ cols }) {
|
|
|
28
24
|
);
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
const lines = cols < 60 ?
|
|
32
|
-
const bannerHeight = lines.length;
|
|
27
|
+
const lines = cols < 60 ? BANNER_COMPACT : BANNER;
|
|
33
28
|
|
|
34
|
-
return h(Box, { height:
|
|
29
|
+
return h(Box, { height: lines.length, width: '100%', alignItems: 'center', justifyContent: 'center', flexDirection: 'column' },
|
|
35
30
|
lines.map((line, i) =>
|
|
36
31
|
h(Box, { key: i, height: 1 },
|
|
37
32
|
h(Text, { bold: true }, appGradient.multiline(line))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { hex } from '../config/theme.js';
|
|
4
4
|
const { createElement: h } = React;
|
|
@@ -15,7 +15,7 @@ const COMMANDS = [
|
|
|
15
15
|
{ name: '/exit', desc: 'Exit CLARITY' },
|
|
16
16
|
];
|
|
17
17
|
|
|
18
|
-
export function SlashPopup({ search, selectedIdx,
|
|
18
|
+
export function SlashPopup({ search, selectedIdx, width }) {
|
|
19
19
|
const filtered = useMemo(() => {
|
|
20
20
|
const q = search.replace(/^\//, '').toLowerCase();
|
|
21
21
|
if (!q) return COMMANDS;
|
|
@@ -25,31 +25,33 @@ export function SlashPopup({ search, selectedIdx, onHover, width }) {
|
|
|
25
25
|
}, [search]);
|
|
26
26
|
|
|
27
27
|
const innerW = Math.max(10, width - 4);
|
|
28
|
-
const
|
|
28
|
+
const label = (cmd, i) => {
|
|
29
29
|
const sel = i === selectedIdx;
|
|
30
|
-
const
|
|
31
|
-
return
|
|
30
|
+
const text = ' ' + cmd.name + ' ' + cmd.desc;
|
|
31
|
+
return text.length > innerW ? text.slice(0, innerW - 2) + '\u2026' : text;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
return h(Box, { flexDirection: 'column', width, backgroundColor: hex.
|
|
34
|
+
return h(Box, { flexDirection: 'column', width, backgroundColor: hex.cardBg },
|
|
35
35
|
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
36
|
-
h(Text, { color: hex.textMuted
|
|
36
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
37
|
+
' ' + (search || '/') + ' '.repeat(Math.max(0, innerW - (search || '/').length)))
|
|
37
38
|
),
|
|
38
39
|
filtered.map((cmd, i) =>
|
|
39
40
|
h(Box, {
|
|
40
41
|
key: cmd.name,
|
|
41
42
|
height: 1,
|
|
42
|
-
backgroundColor: i === selectedIdx ? hex.orange :
|
|
43
|
+
backgroundColor: i === selectedIdx ? hex.orange : hex.cardBg,
|
|
43
44
|
},
|
|
44
45
|
h(Text, {
|
|
45
46
|
color: i === selectedIdx ? '#000000' : hex.text,
|
|
46
47
|
bold: i === selectedIdx,
|
|
47
|
-
|
|
48
|
+
backgroundColor: i === selectedIdx ? hex.orange : hex.cardBg,
|
|
49
|
+
}, label(cmd, i))
|
|
48
50
|
)
|
|
49
51
|
),
|
|
50
52
|
filtered.length === 0
|
|
51
|
-
? h(Box, { height: 1 },
|
|
52
|
-
h(Text, { color: hex.textMuted }, ' No matching commands'))
|
|
53
|
+
? h(Box, { height: 1, backgroundColor: hex.cardBg },
|
|
54
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.cardBg }, ' No matching commands'))
|
|
53
55
|
: null,
|
|
54
56
|
);
|
|
55
57
|
}
|
|
@@ -23,7 +23,7 @@ function wrap(text, width) {
|
|
|
23
23
|
return wrapAnsi(String(text), Math.max(4, width), { trim: false, hard: true }).split('\n');
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function
|
|
26
|
+
function RoleIcon({ role }) {
|
|
27
27
|
switch (role) {
|
|
28
28
|
case 'user': return h(Text, { color: hex.orange, bold: true }, '\u276F ');
|
|
29
29
|
case 'assistant': return h(Text, { color: hex.purple, bold: true }, '\u25C6 ');
|
|
@@ -36,20 +36,21 @@ function MsgRole({ role }) {
|
|
|
36
36
|
|
|
37
37
|
function MsgBlock({ msg, width }) {
|
|
38
38
|
const lines = useMemo(() => wrap(msg.content, width), [msg.content, width]);
|
|
39
|
+
const bg = msg.role === 'user' ? hex.surface : hex.cardBg;
|
|
39
40
|
|
|
40
|
-
return h(Box, { flexDirection: 'column' },
|
|
41
|
-
h(Box, { height: 1 },
|
|
42
|
-
h(
|
|
43
|
-
h(Text, { color: hex.text }, lines[0] || '')
|
|
41
|
+
return h(Box, { flexDirection: 'column', backgroundColor: bg },
|
|
42
|
+
h(Box, { height: 1, backgroundColor: bg },
|
|
43
|
+
h(RoleIcon, { role: msg.role }),
|
|
44
|
+
h(Text, { color: hex.text, backgroundColor: bg }, lines[0] || '')
|
|
44
45
|
),
|
|
45
46
|
lines.slice(1).map((l, i) =>
|
|
46
|
-
h(Box, { key: i, height: 1 },
|
|
47
|
-
h(Text, { color: hex.text }, ' ' + l)
|
|
47
|
+
h(Box, { key: i, height: 1, backgroundColor: bg },
|
|
48
|
+
h(Text, { color: hex.text, backgroundColor: bg }, ' ' + l)
|
|
48
49
|
)
|
|
49
50
|
),
|
|
50
51
|
msg.role === 'assistant' && msg.duration
|
|
51
|
-
? h(Box, { height: 1 },
|
|
52
|
-
h(Text, { color: hex.textMuted },
|
|
52
|
+
? h(Box, { height: 1, backgroundColor: bg },
|
|
53
|
+
h(Text, { color: hex.textMuted, backgroundColor: bg },
|
|
53
54
|
' ' + (msg.duration < 1000 ? msg.duration + 'ms' : (msg.duration / 1000).toFixed(1) + 's'))
|
|
54
55
|
)
|
|
55
56
|
: null
|
|
@@ -58,14 +59,14 @@ function MsgBlock({ msg, width }) {
|
|
|
58
59
|
|
|
59
60
|
function StreamingBlock({ content, width }) {
|
|
60
61
|
const lines = useMemo(() => wrap(content, width), [content, width]);
|
|
61
|
-
return h(Box, { flexDirection: 'column' },
|
|
62
|
-
h(Box, { height: 1 },
|
|
63
|
-
h(Text, { color: hex.blue, bold: true }, '\u25CF '),
|
|
64
|
-
h(Text, { color: hex.text }, lines[0] || '')
|
|
62
|
+
return h(Box, { flexDirection: 'column', backgroundColor: hex.cardBg },
|
|
63
|
+
h(Box, { height: 1, backgroundColor: hex.cardBg },
|
|
64
|
+
h(Text, { color: hex.blue, bold: true, backgroundColor: hex.cardBg }, '\u25CF '),
|
|
65
|
+
h(Text, { color: hex.text, backgroundColor: hex.cardBg }, lines[0] || '')
|
|
65
66
|
),
|
|
66
67
|
lines.slice(1).map((l, i) =>
|
|
67
|
-
h(Box, { key: i, height: 1 },
|
|
68
|
-
h(Text, { color: hex.text }, ' ' + l)
|
|
68
|
+
h(Box, { key: i, height: 1, backgroundColor: hex.cardBg },
|
|
69
|
+
h(Text, { color: hex.text, backgroundColor: hex.cardBg }, ' ' + l)
|
|
69
70
|
)
|
|
70
71
|
)
|
|
71
72
|
);
|
|
@@ -110,8 +111,8 @@ export function StreamView({ messages, streamContent, status, maxLines, width })
|
|
|
110
111
|
? h(StreamingBlock, { content: streamContent, width: cw })
|
|
111
112
|
: null,
|
|
112
113
|
status === 'thinking' && !streamContent
|
|
113
|
-
? h(Box, { height: 1 },
|
|
114
|
-
h(Text, { color: hex.textMuted }, ' \u25CF processing...')
|
|
114
|
+
? h(Box, { height: 1, backgroundColor: hex.cardBg },
|
|
115
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.cardBg }, ' \u25CF processing...')
|
|
115
116
|
)
|
|
116
117
|
: null,
|
|
117
118
|
);
|
package/src/hooks/useMouse.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
2
|
-
import { useStdin } from 'ink';
|
|
3
|
-
|
|
4
|
-
export function useMouse(handler) {
|
|
5
|
-
const { stdin } = useStdin();
|
|
6
|
-
const handlerRef = useRef(handler);
|
|
7
|
-
handlerRef.current = handler;
|
|
8
|
-
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
process.stdout.write('\x1b[?1000h\x1b[?1006h');
|
|
11
|
-
|
|
12
|
-
function onData(chunk) {
|
|
13
|
-
const str = typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8');
|
|
14
|
-
|
|
15
|
-
const sgr = str.match(/\x1b\[<(\d+);(\d+);(\d+)([Mm])/);
|
|
16
|
-
if (sgr) {
|
|
17
|
-
const code = parseInt(sgr[1]);
|
|
18
|
-
const col = parseInt(sgr[2]);
|
|
19
|
-
const row = parseInt(sgr[3]);
|
|
20
|
-
const press = sgr[4] === 'M';
|
|
21
|
-
if (press && handlerRef.current) {
|
|
22
|
-
handlerRef.current({ col, row, button: code & 3 });
|
|
23
|
-
}
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const legacy = str.match(/\x1b\[M(.{3})/);
|
|
28
|
-
if (legacy) {
|
|
29
|
-
const chars = legacy[1];
|
|
30
|
-
const cb = chars.charCodeAt(0) - 32;
|
|
31
|
-
const col = chars.charCodeAt(1) - 32;
|
|
32
|
-
const row = chars.charCodeAt(2) - 32;
|
|
33
|
-
if (handlerRef.current) {
|
|
34
|
-
handlerRef.current({ col, row, button: cb & 3 });
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
stdin.on('data', onData);
|
|
40
|
-
return () => {
|
|
41
|
-
stdin.removeListener('data', onData);
|
|
42
|
-
process.stdout.write('\x1b[?1000l\x1b[?1006l');
|
|
43
|
-
};
|
|
44
|
-
}, [stdin]);
|
|
45
|
-
}
|