claudescreenfix-hardwicksoftware 1.0.0 → 1.0.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/README.md +16 -0
- package/index.cjs +96 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,6 +117,22 @@ for resize events, we intercept `process.on('SIGWINCH', ...)` and debounce the h
|
|
|
117
117
|
|
|
118
118
|
bottom line: smooth terminal, no lag, no memory bloat. it just works.
|
|
119
119
|
|
|
120
|
+
## changelog
|
|
121
|
+
|
|
122
|
+
### v1.0.1 (2025-01-08)
|
|
123
|
+
- **FIXED: typing issue** - keystrokes were getting lost because the fix was intercepting stdin echoes
|
|
124
|
+
- now detects stdin echo writes (single chars, backspace, arrow keys) and passes them through unmodified
|
|
125
|
+
- added typing cooldown detection - clears are deferred during active typing
|
|
126
|
+
- periodic clears now use `setImmediate` to not block the event loop
|
|
127
|
+
- added stdin tracking to properly detect user input activity
|
|
128
|
+
- new config option: `typingCooldownMs` (default 500ms) - how long to wait after typing before allowing clears
|
|
129
|
+
|
|
130
|
+
### v1.0.0 (2025-01-08)
|
|
131
|
+
- initial release
|
|
132
|
+
- scrollback clearing after 500 renders or 60 seconds
|
|
133
|
+
- SIGWINCH debouncing for tmux/screen users
|
|
134
|
+
- enhanced /clear command to actually clear scrollback
|
|
135
|
+
|
|
120
136
|
## known issues
|
|
121
137
|
|
|
122
138
|
- some old terminals don't support `\x1b[3J` but that's pretty rare nowadays
|
package/index.cjs
CHANGED
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
* - hook stdout.write to inject scrollback clears periodically
|
|
14
14
|
* - debounce SIGWINCH so resize aint thrashing
|
|
15
15
|
* - enhance /clear to actually clear scrollback not just the screen
|
|
16
|
+
*
|
|
17
|
+
* FIXED v1.0.1: typing issue where stdin echo was being intercepted
|
|
18
|
+
* - now detects stdin echo writes and passes them through unmodified
|
|
19
|
+
* - uses setImmediate for periodic clears to not interrupt typing
|
|
20
|
+
* - tracks "active typing" window to defer clears during input
|
|
16
21
|
*/
|
|
17
22
|
|
|
18
23
|
const CLEAR_SCROLLBACK = '\x1b[3J';
|
|
@@ -26,6 +31,7 @@ const config = {
|
|
|
26
31
|
resizeDebounceMs: 150, // how long to wait before firing resize
|
|
27
32
|
periodicClearMs: 60000, // clear scrollback every 60s
|
|
28
33
|
clearAfterRenders: 500, // or after 500 render cycles
|
|
34
|
+
typingCooldownMs: 500, // wait this long after typing to clear
|
|
29
35
|
debug: process.env.CLAUDE_TERMINAL_FIX_DEBUG === '1',
|
|
30
36
|
disabled: process.env.CLAUDE_TERMINAL_FIX_DISABLED === '1'
|
|
31
37
|
};
|
|
@@ -36,6 +42,9 @@ let lastResizeTime = 0;
|
|
|
36
42
|
let resizeTimeout = null;
|
|
37
43
|
let originalWrite = null;
|
|
38
44
|
let installed = false;
|
|
45
|
+
let lastTypingTime = 0; // track when user last typed
|
|
46
|
+
let pendingClear = false; // defer clear if typing active
|
|
47
|
+
let clearIntervalId = null;
|
|
39
48
|
|
|
40
49
|
function log(...args) {
|
|
41
50
|
if (config.debug) {
|
|
@@ -43,6 +52,64 @@ function log(...args) {
|
|
|
43
52
|
}
|
|
44
53
|
}
|
|
45
54
|
|
|
55
|
+
/**
|
|
56
|
+
* check if user is actively typing (within cooldown window)
|
|
57
|
+
*/
|
|
58
|
+
function isTypingActive() {
|
|
59
|
+
return (Date.now() - lastTypingTime) < config.typingCooldownMs;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* detect if this looks like a stdin echo (single printable char or short sequence)
|
|
64
|
+
* stdin echoes are typically: single chars, backspace sequences, arrow key echoes
|
|
65
|
+
*/
|
|
66
|
+
function isStdinEcho(chunk) {
|
|
67
|
+
// single printable character (including space)
|
|
68
|
+
if (chunk.length === 1 && chunk.charCodeAt(0) >= 32 && chunk.charCodeAt(0) <= 126) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
// backspace/delete echo (usually 1-3 chars with control codes)
|
|
72
|
+
if (chunk.length <= 4 && (chunk.includes('\b') || chunk.includes('\x7f'))) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
// arrow key echo or cursor movement (short escape sequences)
|
|
76
|
+
if (chunk.length <= 6 && chunk.startsWith('\x1b[') && !chunk.includes('J') && !chunk.includes('H')) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
// enter/newline
|
|
80
|
+
if (chunk === '\n' || chunk === '\r' || chunk === '\r\n') {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* safe clear - defers if typing active
|
|
88
|
+
*/
|
|
89
|
+
function safeClearScrollback() {
|
|
90
|
+
if (isTypingActive()) {
|
|
91
|
+
if (!pendingClear) {
|
|
92
|
+
pendingClear = true;
|
|
93
|
+
log('deferring clear - typing active');
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
pendingClear = false;
|
|
96
|
+
if (!isTypingActive()) {
|
|
97
|
+
safeClearScrollback();
|
|
98
|
+
}
|
|
99
|
+
}, config.typingCooldownMs);
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (originalWrite && process.stdout.isTTY) {
|
|
105
|
+
// use setImmediate to not block the event loop
|
|
106
|
+
setImmediate(() => {
|
|
107
|
+
log('executing deferred scrollback clear');
|
|
108
|
+
originalWrite(CURSOR_SAVE + CLEAR_SCROLLBACK + CURSOR_RESTORE);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
46
113
|
/**
|
|
47
114
|
* installs the fix - hooks into stdout and sigwinch
|
|
48
115
|
* call this once at startup, calling again is a no-op
|
|
@@ -55,21 +122,41 @@ function install() {
|
|
|
55
122
|
|
|
56
123
|
originalWrite = process.stdout.write.bind(process.stdout);
|
|
57
124
|
|
|
125
|
+
// track stdin to know when user is typing
|
|
126
|
+
if (process.stdin.isTTY) {
|
|
127
|
+
process.stdin.on('data', () => {
|
|
128
|
+
lastTypingTime = Date.now();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
58
132
|
// hook stdout.write - this is where the magic happens
|
|
59
133
|
process.stdout.write = function(chunk, encoding, callback) {
|
|
134
|
+
// CRITICAL FIX: pass stdin echoes through unmodified
|
|
135
|
+
// this prevents the typing issue where keystrokes get lost
|
|
60
136
|
if (typeof chunk === 'string') {
|
|
137
|
+
// check if this is a stdin echo - if so, pass through immediately
|
|
138
|
+
if (isStdinEcho(chunk)) {
|
|
139
|
+
lastTypingTime = Date.now(); // update typing time
|
|
140
|
+
return originalWrite(chunk, encoding, callback);
|
|
141
|
+
}
|
|
142
|
+
|
|
61
143
|
renderCount++;
|
|
62
144
|
|
|
63
145
|
// ink clears screen before re-render, we piggyback on that
|
|
146
|
+
// but only if not actively typing
|
|
64
147
|
if (chunk.includes(CLEAR_SCREEN) || chunk.includes(HOME_CURSOR)) {
|
|
65
148
|
if (config.clearAfterRenders > 0 && renderCount >= config.clearAfterRenders) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
149
|
+
if (!isTypingActive()) {
|
|
150
|
+
log('clearing scrollback after ' + renderCount + ' renders');
|
|
151
|
+
renderCount = 0;
|
|
152
|
+
chunk = CLEAR_SCROLLBACK + chunk;
|
|
153
|
+
} else {
|
|
154
|
+
log('skipping render-based clear - typing active');
|
|
155
|
+
}
|
|
69
156
|
}
|
|
70
157
|
}
|
|
71
158
|
|
|
72
|
-
// /clear command should actually clear everything
|
|
159
|
+
// /clear command should actually clear everything (immediate, user-requested)
|
|
73
160
|
if (chunk.includes('Conversation cleared') || chunk.includes('Chat cleared')) {
|
|
74
161
|
log('/clear detected, nuking scrollback');
|
|
75
162
|
chunk = CLEAR_SCROLLBACK + chunk;
|
|
@@ -83,17 +170,16 @@ function install() {
|
|
|
83
170
|
installResizeDebounce();
|
|
84
171
|
|
|
85
172
|
// periodic cleanup so long sessions dont get cooked
|
|
173
|
+
// uses safeClearScrollback which respects typing activity
|
|
86
174
|
if (config.periodicClearMs > 0) {
|
|
87
|
-
setInterval(() => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
originalWrite(CURSOR_SAVE + CLEAR_SCROLLBACK + CURSOR_RESTORE);
|
|
91
|
-
}
|
|
175
|
+
clearIntervalId = setInterval(() => {
|
|
176
|
+
log('periodic clear check');
|
|
177
|
+
safeClearScrollback();
|
|
92
178
|
}, config.periodicClearMs);
|
|
93
179
|
}
|
|
94
180
|
|
|
95
181
|
installed = true;
|
|
96
|
-
log('installed successfully');
|
|
182
|
+
log('installed successfully - v1.0.1 with typing fix');
|
|
97
183
|
}
|
|
98
184
|
|
|
99
185
|
function installResizeDebounce() {
|
package/package.json
CHANGED