shellwise 0.2.0 → 0.2.2
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/package.json +1 -1
- package/src/cli/search.ts +11 -34
- package/src/index.ts +1 -1
- package/src/tui/input.ts +31 -9
- package/src/tui/renderer.ts +9 -4
package/package.json
CHANGED
package/src/cli/search.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { search, type ScoredResult } from "../search";
|
|
2
|
-
import { enableRawMode, disableRawMode, parseKeypress } from "../tui/input";
|
|
2
|
+
import { enableRawMode, disableRawMode, parseKeypress, readKeypress, closeTtyInput } from "../tui/input";
|
|
3
3
|
import {
|
|
4
4
|
write,
|
|
5
5
|
clearLine,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { renderSearchBox, getSearchBoxCursorCol } from "../tui/components/search-box";
|
|
15
15
|
import { renderResultList } from "../tui/components/result-list";
|
|
16
16
|
import { renderStatusBar } from "../tui/components/status-bar";
|
|
17
|
+
import { writeSync } from "fs";
|
|
17
18
|
|
|
18
19
|
interface SearchState {
|
|
19
20
|
query: string;
|
|
@@ -24,7 +25,7 @@ interface SearchState {
|
|
|
24
25
|
renderedLines: number;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
export
|
|
28
|
+
export function runSearch(initialQuery: string = ""): void {
|
|
28
29
|
const cwd = process.env.PWD || process.cwd();
|
|
29
30
|
|
|
30
31
|
const state: SearchState = {
|
|
@@ -52,6 +53,7 @@ export async function runSearch(initialQuery: string = ""): Promise<void> {
|
|
|
52
53
|
write(showCursor());
|
|
53
54
|
write(moveCursorToColumn(1));
|
|
54
55
|
disableRawMode();
|
|
56
|
+
closeTtyInput();
|
|
55
57
|
closeTty();
|
|
56
58
|
};
|
|
57
59
|
|
|
@@ -66,14 +68,16 @@ export async function runSearch(initialQuery: string = ""): Promise<void> {
|
|
|
66
68
|
// Render initial frame
|
|
67
69
|
render(state);
|
|
68
70
|
|
|
69
|
-
// Input loop
|
|
71
|
+
// Input loop — sync reads from /dev/tty
|
|
70
72
|
try {
|
|
71
|
-
|
|
73
|
+
while (true) {
|
|
74
|
+
const chunk = readKeypress();
|
|
75
|
+
if (chunk.length === 0) continue;
|
|
76
|
+
|
|
72
77
|
const key = parseKeypress(chunk);
|
|
73
78
|
|
|
74
79
|
if (key.type === "special" && key.key === "escape") {
|
|
75
80
|
cleanup();
|
|
76
|
-
// Output nothing = cancel
|
|
77
81
|
return;
|
|
78
82
|
}
|
|
79
83
|
|
|
@@ -86,8 +90,8 @@ export async function runSearch(initialQuery: string = ""): Promise<void> {
|
|
|
86
90
|
const selected = state.results[state.selectedIndex];
|
|
87
91
|
cleanup();
|
|
88
92
|
if (selected) {
|
|
89
|
-
// Output to stdout for shell to capture
|
|
90
|
-
|
|
93
|
+
// Output to stdout (fd 1) for shell to capture
|
|
94
|
+
writeSync(1, selected.command);
|
|
91
95
|
}
|
|
92
96
|
return;
|
|
93
97
|
}
|
|
@@ -264,30 +268,3 @@ function render(state: SearchState): void {
|
|
|
264
268
|
);
|
|
265
269
|
write(showCursor());
|
|
266
270
|
}
|
|
267
|
-
|
|
268
|
-
async function* readStdin(): AsyncGenerator<Buffer> {
|
|
269
|
-
const stdin = process.stdin;
|
|
270
|
-
stdin.resume();
|
|
271
|
-
|
|
272
|
-
const buffers: Buffer[] = [];
|
|
273
|
-
let resolve: (() => void) | null = null;
|
|
274
|
-
|
|
275
|
-
stdin.on("data", (data: Buffer) => {
|
|
276
|
-
buffers.push(data);
|
|
277
|
-
if (resolve) {
|
|
278
|
-
resolve();
|
|
279
|
-
resolve = null;
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
while (true) {
|
|
284
|
-
if (buffers.length === 0) {
|
|
285
|
-
await new Promise<void>((r) => {
|
|
286
|
-
resolve = r;
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
while (buffers.length > 0) {
|
|
290
|
-
yield buffers.shift()!;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
package/src/index.ts
CHANGED
package/src/tui/input.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { openSync, readSync, closeSync } from "fs";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
|
|
1
4
|
export type KeyEvent =
|
|
2
5
|
| { type: "char"; char: string }
|
|
3
6
|
| { type: "special"; key: SpecialKey }
|
|
@@ -53,19 +56,38 @@ export function parseKeypress(data: Buffer): KeyEvent {
|
|
|
53
56
|
return { type: "char", char: str };
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
// Use /dev/tty directly — avoids Bun kqueue bug with process.stdin
|
|
60
|
+
// inside $() capture from shell integration
|
|
61
|
+
let ttyReadFd: number | null = null;
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
process.stdin.setRawMode(true);
|
|
62
|
-
process.stdin.resume();
|
|
63
|
+
function getTtyReadFd(): number {
|
|
64
|
+
if (ttyReadFd === null) {
|
|
65
|
+
ttyReadFd = openSync("/dev/tty", "r");
|
|
63
66
|
}
|
|
67
|
+
return ttyReadFd;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function readKeypress(): Buffer {
|
|
71
|
+
const buf = Buffer.alloc(16);
|
|
72
|
+
const bytesRead = readSync(getTtyReadFd(), buf);
|
|
73
|
+
return buf.subarray(0, bytesRead);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function enableRawMode(): void {
|
|
77
|
+
try {
|
|
78
|
+
execSync("stty raw -echo </dev/tty", { stdio: "ignore" });
|
|
79
|
+
} catch {}
|
|
64
80
|
}
|
|
65
81
|
|
|
66
82
|
export function disableRawMode(): void {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
83
|
+
try {
|
|
84
|
+
execSync("stty -raw echo </dev/tty", { stdio: "ignore" });
|
|
85
|
+
} catch {}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function closeTtyInput(): void {
|
|
89
|
+
if (ttyReadFd !== null) {
|
|
90
|
+
try { closeSync(ttyReadFd); } catch {}
|
|
91
|
+
ttyReadFd = null;
|
|
70
92
|
}
|
|
71
93
|
}
|
package/src/tui/renderer.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { openSync, writeSync, closeSync } from "fs";
|
|
2
|
+
import { execSync } from "child_process";
|
|
2
3
|
|
|
3
4
|
const ESC = "\x1b[";
|
|
4
5
|
|
|
@@ -49,10 +50,14 @@ export function showCursor(): string {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
export function getTerminalSize(): { rows: number; cols: number } {
|
|
52
|
-
// process.stdout
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
// Avoid process.stdout/stderr entirely — Bun crashes with kqueue error
|
|
54
|
+
// when they're accessed inside $() capture. Use stty instead.
|
|
55
|
+
try {
|
|
56
|
+
const output = execSync("stty size </dev/tty", { encoding: "utf-8" }).trim();
|
|
57
|
+
const [rows, cols] = output.split(" ").map(Number);
|
|
58
|
+
if (rows > 0 && cols > 0) return { rows, cols };
|
|
59
|
+
} catch {}
|
|
60
|
+
return { rows: 24, cols: 80 };
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
export function write(text: string): void {
|