@lzhzzzzwill/cofos 1.2.1 → 1.2.3

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.
Files changed (2) hide show
  1. package/bin/cofos.js +89 -26
  2. package/package.json +1 -1
package/bin/cofos.js CHANGED
@@ -169,7 +169,7 @@ function createTui() {
169
169
  alwaysScroll: true,
170
170
  keys: true,
171
171
  vi: true,
172
- mouse: true,
172
+ mouse: false,
173
173
  tags: false,
174
174
  scrollbar: { ch: " ", track: { bg: "black" }, style: { bg: "cyan" } },
175
175
  });
@@ -192,7 +192,7 @@ function createTui() {
192
192
  height: 3,
193
193
  inputOnFocus: true,
194
194
  keys: true,
195
- mouse: true,
195
+ mouse: false,
196
196
  border: { type: "line" },
197
197
  label: " cofos ",
198
198
  style: {
@@ -207,6 +207,10 @@ function createTui() {
207
207
 
208
208
  let buffer = "";
209
209
  let onSubmit = () => {};
210
+ let onExit = () => {};
211
+ let submitting = false;
212
+ let suggestionMatches = [];
213
+ let selectedSuggestion = 0;
210
214
 
211
215
  function append(text = "") {
212
216
  const data = String(text).replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
@@ -226,59 +230,91 @@ function createTui() {
226
230
  }
227
231
 
228
232
  function hideSuggestions() {
233
+ suggestionMatches = [];
234
+ selectedSuggestion = 0;
229
235
  if (!suggestions.hidden) {
230
236
  suggestions.hide();
231
237
  screen.render();
232
238
  }
233
239
  }
234
240
 
235
- function updateSuggestions() {
236
- const value = input.getValue() || "";
237
- if (!value.startsWith("/")) {
241
+ function renderSuggestions() {
242
+ if (!suggestionMatches.length) {
238
243
  hideSuggestions();
239
244
  return;
240
245
  }
241
- const matches = slashMatches(value);
242
- if (!matches.length) {
246
+ selectedSuggestion = Math.max(0, Math.min(selectedSuggestion, suggestionMatches.length - 1));
247
+ suggestions.setContent(suggestionMatches.map(({ cmd, desc }, i) => {
248
+ const marker = i === selectedSuggestion ? "{black-fg}{yellow-bg}›" : " ";
249
+ const close = i === selectedSuggestion ? "{/yellow-bg}{/black-fg}" : "";
250
+ return `${marker} {cyan-fg}${cmd}{/cyan-fg} ${desc}${close}`;
251
+ }).join("\n"));
252
+ suggestions.height = Math.min(8, suggestionMatches.length + 2);
253
+ suggestions.show();
254
+ screen.render();
255
+ }
256
+
257
+ function updateSuggestions() {
258
+ const value = input.getValue() || "";
259
+ if (!value.startsWith("/")) {
243
260
  hideSuggestions();
244
261
  return;
245
262
  }
246
- suggestions.setContent(matches.map(({ cmd, desc }) => `{cyan-fg}${cmd}{/cyan-fg} ${desc}`).join("\n"));
247
- suggestions.height = Math.min(8, matches.length + 2);
248
- suggestions.show();
249
- screen.render();
263
+ suggestionMatches = slashMatches(value);
264
+ selectedSuggestion = 0;
265
+ renderSuggestions();
266
+ }
267
+
268
+ function selectedCommand() {
269
+ return suggestionMatches[selectedSuggestion]?.cmd || "";
250
270
  }
251
271
 
252
272
  function submitInput() {
253
- const value = input.getValue();
273
+ if (submitting) return;
274
+ submitting = true;
275
+ let value = input.getValue();
276
+ if (!suggestions.hidden && selectedCommand()) value = selectedCommand();
254
277
  hideSuggestions();
255
278
  input.clearValue();
256
279
  input.focus();
257
280
  screen.render();
258
281
  onSubmit(String(value || ""));
282
+ setTimeout(() => { submitting = false; }, 0);
259
283
  }
260
284
 
261
285
  input.key(["enter"], submitInput);
286
+ screen.key(["enter"], submitInput);
262
287
 
263
- input.on("keypress", () => setTimeout(updateSuggestions, 0));
288
+ input.on("keypress", (_ch, key = {}) => {
289
+ if (["up", "down", "tab", "enter", "escape"].includes(key.name)) return;
290
+ setTimeout(updateSuggestions, 0);
291
+ });
264
292
 
265
293
  screen.key(["tab"], () => {
266
294
  const value = input.getValue() || "";
267
295
  if (!value.startsWith("/")) return;
268
- const matches = slashMatches(value);
269
- if (!matches.length) return;
270
- if (matches.length === 1) {
271
- input.setValue(matches[0].cmd);
272
- hideSuggestions();
273
- input.focus();
274
- screen.render();
275
- return;
276
- }
277
- updateSuggestions();
296
+ if (!suggestionMatches.length) suggestionMatches = slashMatches(value);
297
+ if (!suggestionMatches.length) return;
298
+ input.setValue(selectedCommand());
299
+ hideSuggestions();
300
+ input.focus();
301
+ screen.render();
302
+ });
303
+
304
+ screen.key(["up"], () => {
305
+ if (suggestions.hidden || !suggestionMatches.length) return;
306
+ selectedSuggestion = (selectedSuggestion - 1 + suggestionMatches.length) % suggestionMatches.length;
307
+ renderSuggestions();
308
+ });
309
+
310
+ screen.key(["down"], () => {
311
+ if (suggestions.hidden || !suggestionMatches.length) return;
312
+ selectedSuggestion = (selectedSuggestion + 1) % suggestionMatches.length;
313
+ renderSuggestions();
278
314
  });
279
315
 
280
316
  screen.key(["escape"], hideSuggestions);
281
- screen.key(["C-c"], () => process.emit("SIGINT"));
317
+ screen.key(["C-c"], () => onExit());
282
318
 
283
319
  input.focus();
284
320
  screen.render();
@@ -289,6 +325,8 @@ function createTui() {
289
325
  append,
290
326
  print: (text = "") => append(String(text) + "\n"),
291
327
  setSubmitHandler: (fn) => { onSubmit = fn; },
328
+ setExitHandler: (fn) => { onExit = fn; },
329
+ focus: () => { input.focus(); screen.render(); },
292
330
  setPrompt: (label) => { input.setLabel(` ${label} `); screen.render(); },
293
331
  destroy: () => screen.destroy(),
294
332
  };
@@ -393,7 +431,7 @@ function main() {
393
431
  proc.stdout.setEncoding("utf-8");
394
432
  proc.stderr.setEncoding("utf-8");
395
433
 
396
- let buf = "", ready = false, generating = false, responseOpen = false, closed = false;
434
+ let buf = "", ready = false, generating = false, responseOpen = false, commandOpen = false, closed = false;
397
435
  const inputQ = [];
398
436
  let mlBuf = [];
399
437
 
@@ -405,6 +443,7 @@ function main() {
405
443
  ui.destroy();
406
444
  }
407
445
 
446
+ ui.setExitHandler(shutdown);
408
447
  process.on("SIGTERM", shutdown);
409
448
  process.on("SIGINT", shutdown);
410
449
 
@@ -440,6 +479,14 @@ function main() {
440
479
  if (/^\/(clear)$/.test(input)) {
441
480
  mlBuf = [];
442
481
  responseOpen = false;
482
+ commandOpen = true;
483
+ proc.stdin.write(input + "\n");
484
+ generating = true;
485
+ return true;
486
+ }
487
+ if (/^\/(topk)(?:\s+\d+)?$/.test(input) || /^\/rag\s+(?:on|off)$/.test(input)) {
488
+ responseOpen = false;
489
+ commandOpen = true;
443
490
  proc.stdin.write(input + "\n");
444
491
  generating = true;
445
492
  return true;
@@ -449,7 +496,7 @@ function main() {
449
496
  return false;
450
497
  }
451
498
 
452
- ui.print(G + " you " + R + input);
499
+ ui.print(Y + "" + R + input);
453
500
  responseOpen = false;
454
501
  generating = true;
455
502
  proc.stdin.write(input + "\n");
@@ -477,6 +524,20 @@ function main() {
477
524
  }
478
525
  if (!generating) { buf = ""; return; }
479
526
 
527
+ if (commandOpen) {
528
+ const di = buf.indexOf(DONE);
529
+ if (di === -1) return;
530
+ const pre = buf.slice(0, di).trim();
531
+ if (pre) ui.print(G + "• " + R + pre);
532
+ buf = buf.slice(di + DONE.length);
533
+ if (buf[0] === "\n") buf = buf.slice(1);
534
+ generating = false;
535
+ commandOpen = false;
536
+ ui.focus();
537
+ drain();
538
+ return;
539
+ }
540
+
480
541
  if (!responseOpen) {
481
542
  const ri = buf.indexOf(RESPONSE);
482
543
  if (ri === -1) {
@@ -487,6 +548,7 @@ function main() {
487
548
  buf = buf.slice(ri + RESPONSE.length);
488
549
  if (buf[0] === "\n") buf = buf.slice(1);
489
550
  responseOpen = true;
551
+ ui.append(GR + B + "◆ COFOS " + R);
490
552
  }
491
553
 
492
554
  const di = buf.indexOf(DONE);
@@ -498,6 +560,7 @@ function main() {
498
560
  generating = false;
499
561
  responseOpen = false;
500
562
  ui.append("\n");
563
+ ui.focus();
501
564
  drain();
502
565
  } else {
503
566
  const keep = DONE.length - 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lzhzzzzwill/cofos",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "COFOS 9B + RAG chat CLI with optional local PDF ingestion.",
5
5
  "type": "module",
6
6
  "bin": {