caroushell 0.1.11 → 0.1.13

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/dist/app.js CHANGED
@@ -142,7 +142,7 @@ class App {
142
142
  }
143
143
  async run() {
144
144
  await this.init();
145
- this.keyboard.start();
145
+ this.keyboard.enableCapture();
146
146
  this.keyboard.on("key", (evt) => {
147
147
  void this.handleKey(evt);
148
148
  });
@@ -174,7 +174,8 @@ class App {
174
174
  this.terminal.renderBlock(lines);
175
175
  // Ensure command output starts on the next line
176
176
  this.terminal.write("\n");
177
- this.keyboard.pause();
177
+ this.keyboard.disableCapture();
178
+ this.terminal.disableWrites();
178
179
  try {
179
180
  const storeInHistory = await (0, spawner_1.runUserCommand)(cmd);
180
181
  if (storeInHistory) {
@@ -182,13 +183,14 @@ class App {
182
183
  }
183
184
  }
184
185
  finally {
185
- this.keyboard.resume();
186
+ this.terminal.enableWrites();
187
+ this.keyboard.enableCapture();
186
188
  }
187
189
  }
188
190
  exit() {
189
191
  // Clear terminal contents before shutting down to leave a clean screen.
190
192
  this.terminal.renderBlock([]);
191
- this.keyboard.stop();
193
+ this.keyboard.disableCapture();
192
194
  process.exit(0);
193
195
  }
194
196
  async tryAutocompleteFile() {
@@ -208,6 +210,7 @@ class App {
208
210
  return true;
209
211
  }
210
212
  tryAcceptHighlightedFileSuggestion() {
213
+ // After ENTER on a file suggestion, we want to place the match at the cursor
211
214
  const currentSuggester = this.carousel.getCurrentRowSuggester();
212
215
  if (currentSuggester !== this.files)
213
216
  return false;
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.FileSuggester = void 0;
4
7
  const fs_1 = require("fs");
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
5
10
  const maxFileAiLines = 10;
6
11
  class FileSuggester {
7
12
  constructor() {
@@ -22,11 +27,53 @@ class FileSuggester {
22
27
  }
23
28
  async getMatchingFiles(queryRaw) {
24
29
  await this.refreshFiles();
25
- const query = queryRaw.trim().toLowerCase();
26
- if (!query) {
27
- return [...this.files];
30
+ const query = queryRaw.trim();
31
+ const { dirDisplay, fragment, dirPath } = this.parseQuery(query);
32
+ const entries = await this.readDirectory(dirPath);
33
+ const needle = fragment.toLowerCase();
34
+ return entries
35
+ .filter((entry) => entry.toLowerCase().startsWith(needle))
36
+ .map((entry) => `${dirDisplay}${entry}`);
37
+ }
38
+ async readDirectory(dirPath) {
39
+ try {
40
+ const entries = await fs_1.promises.readdir(dirPath);
41
+ return entries.sort((a, b) => a.localeCompare(b));
42
+ }
43
+ catch {
44
+ return [];
45
+ }
46
+ }
47
+ parseQuery(query) {
48
+ const lastForward = query.lastIndexOf("/");
49
+ const lastBackward = query.lastIndexOf("\\");
50
+ const lastSeparator = Math.max(lastForward, lastBackward);
51
+ if (lastSeparator === -1) {
52
+ return {
53
+ dirDisplay: "",
54
+ fragment: query,
55
+ dirPath: process.cwd(),
56
+ };
57
+ }
58
+ const dirDisplay = query.slice(0, lastSeparator + 1);
59
+ const fragment = query.slice(lastSeparator + 1);
60
+ return {
61
+ dirDisplay,
62
+ fragment,
63
+ dirPath: this.resolveDirectory(dirDisplay),
64
+ };
65
+ }
66
+ resolveDirectory(dirDisplay) {
67
+ if (!dirDisplay) {
68
+ return process.cwd();
69
+ }
70
+ if (dirDisplay.startsWith("~")) {
71
+ const rest = dirDisplay.slice(1);
72
+ const normalizedRest = rest.replace(/^[\\/]/, "");
73
+ return path_1.default.resolve(os_1.default.homedir(), normalizedRest.replace(/\//g, path_1.default.sep));
28
74
  }
29
- return this.files.filter((file) => file.toLowerCase().indexOf(query.toLowerCase()) === 0);
75
+ const converted = dirDisplay.replace(/\//g, path_1.default.sep);
76
+ return path_1.default.resolve(process.cwd(), converted);
30
77
  }
31
78
  async suggest(carousel, maxDisplayed) {
32
79
  const { prefix } = carousel.getWordInfoAtCursor();
package/dist/keyboard.js CHANGED
@@ -50,38 +50,15 @@ for (const seq of Object.keys(KEYMAP)) {
50
50
  class Keyboard extends events_1.EventEmitter {
51
51
  constructor() {
52
52
  super(...arguments);
53
- this.active = false;
54
53
  this.capturing = false;
55
54
  this.buffer = '';
56
55
  this.stdin = process.stdin;
57
56
  this.onData = (data) => this.handleData(data);
58
57
  }
59
- start() {
60
- if (this.active)
61
- return;
62
- this.active = true;
63
- this.stdin.setEncoding('utf8');
64
- this.enableCapture();
65
- }
66
- stop() {
67
- if (!this.active)
68
- return;
69
- this.active = false;
70
- this.disableCapture();
71
- }
72
- pause() {
73
- if (!this.active)
74
- return;
75
- this.disableCapture();
76
- }
77
- resume() {
78
- if (!this.active)
79
- return;
80
- this.enableCapture();
81
- }
82
58
  enableCapture() {
83
59
  if (this.capturing)
84
60
  return;
61
+ this.stdin.setEncoding('utf8');
85
62
  if (this.stdin.isTTY)
86
63
  this.stdin.setRawMode(true);
87
64
  this.stdin.on('data', this.onData);
package/dist/terminal.js CHANGED
@@ -21,6 +21,16 @@ class Terminal {
21
21
  this.activeRows = 0;
22
22
  this.cursorRow = 0;
23
23
  this.cursorCol = 0;
24
+ this.writesDisabled = false;
25
+ }
26
+ disableWrites() {
27
+ this.writesDisabled = true;
28
+ }
29
+ enableWrites() {
30
+ this.writesDisabled = false;
31
+ }
32
+ canWrite() {
33
+ return !this.writesDisabled;
24
34
  }
25
35
  moveCursorToTopOfBlock() {
26
36
  if (this.activeRows === 0)
@@ -51,6 +61,8 @@ class Terminal {
51
61
  }
52
62
  }
53
63
  write(text) {
64
+ if (!this.canWrite())
65
+ return;
54
66
  this.out.write(text);
55
67
  }
56
68
  hideCursor() {
@@ -61,6 +73,8 @@ class Terminal {
61
73
  }
62
74
  // Render a block of lines by clearing previous block (if any) and writing fresh
63
75
  renderBlock(lines, cursorRow, cursorCol) {
76
+ if (!this.canWrite())
77
+ return;
64
78
  this.withCork(() => {
65
79
  this.moveCursorToTopOfBlock();
66
80
  if (this.activeRows > 0) {
@@ -68,9 +82,9 @@ class Terminal {
68
82
  readline_1.default.clearScreenDown(this.out);
69
83
  }
70
84
  for (let i = 0; i < lines.length; i++) {
71
- this.out.write(lines[i]);
85
+ this.write(lines[i]);
72
86
  if (i < lines.length - 1)
73
- this.out.write("\n");
87
+ this.write("\n");
74
88
  }
75
89
  this.activeRows = lines.length;
76
90
  this.cursorRow = Math.max(0, this.activeRows - 1);
@@ -87,6 +101,8 @@ class Terminal {
87
101
  });
88
102
  }
89
103
  moveCursorTo(lineIndex, column) {
104
+ if (!this.canWrite())
105
+ return;
90
106
  if (this.activeRows === 0)
91
107
  return;
92
108
  const safeLine = Math.min(Math.max(lineIndex, 0), Math.max(0, this.activeRows - 1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caroushell",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Terminal carousel that suggests commands from history, config, and AI.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/main.js",