blun-king-cli 7.2.11 → 7.3.0

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/lib/chat.js +246 -58
  2. package/package.json +1 -1
package/lib/chat.js CHANGED
@@ -276,61 +276,25 @@ function startChat() {
276
276
  printToScrollArea(chalk.yellow(" [SYS] Model: " + parts[1]));
277
277
  drawInputPanel();
278
278
  } else {
279
- // Open inquirer menu
280
- enterMenuMode(function (done) {
281
- var inquirer = require("inquirer");
282
- inquirer.prompt([{
283
- type: "list",
284
- name: "model",
285
- message: chalk.cyan("Select Model"),
286
- choices: [
287
- { name: "Auto (recommended)", value: "auto" },
288
- { name: "Gemma 4", value: "gemma4" },
289
- { name: "Claude", value: "claude" },
290
- { name: "Llama", value: "llama" },
291
- ],
292
- }]).then(function (ans) {
293
- if (ans.model) {
294
- model = ans.model;
295
- settings.model = ans.model;
296
- auth.saveSettings(settings);
297
- printToScrollArea(chalk.yellow(" [SYS] Model: " + ans.model));
298
- }
299
- done();
300
- });
301
- });
279
+ // Cycle model
280
+ var models = ["auto", "gemma4", "claude", "llama"];
281
+ var midx = models.indexOf(settings.model || "auto");
282
+ settings.model = models[(midx + 1) % models.length];
283
+ model = settings.model;
284
+ auth.saveSettings(settings);
285
+ printToScrollArea(chalk.yellow(" [SYS] Model: " + settings.model));
286
+ drawInputPanel();
302
287
  }
303
288
  return;
304
289
  }
305
290
 
306
291
  if (input === "/settings") {
307
- enterMenuMode(function (done) {
308
- openSettingsPanel(done);
309
- });
292
+ openRawSettings();
310
293
  return;
311
294
  }
312
295
 
313
296
  if (input === "/") {
314
- enterMenuMode(function (done) {
315
- var inquirer = require("inquirer");
316
- inquirer.prompt([{
317
- type: "list",
318
- name: "cmd",
319
- message: chalk.cyan("Select command"),
320
- choices: SLASH_COMMANDS.map(function (c) {
321
- return { name: chalk.green(c[0]) + chalk.gray(" " + c[1]), value: c[0] };
322
- }).concat([
323
- new inquirer.Separator(),
324
- { name: chalk.dim("Cancel"), value: "" },
325
- ]),
326
- pageSize: 12,
327
- }]).then(function (ans) {
328
- done();
329
- if (ans.cmd) {
330
- processCommand(ans.cmd);
331
- }
332
- });
333
- });
297
+ openRawCommandPicker();
334
298
  return;
335
299
  }
336
300
 
@@ -413,6 +377,212 @@ function startChat() {
413
377
  });
414
378
  }
415
379
 
380
+ // ── Raw-Mode Settings Panel (no inquirer needed) ──────
381
+ function openRawSettings() {
382
+ var settingsActive = true;
383
+ var selectedField = 0;
384
+ var fields = ui.SETTINGS_FIELDS;
385
+
386
+ function drawSettings() {
387
+ var rows = getRows();
388
+ var cols = getCols();
389
+ var t = ui.getTheme();
390
+ var panelTop = rows - INPUT_PANEL_HEIGHT + 1;
391
+
392
+ // Draw settings over the input panel area
393
+ process.stdout.write(saveCursor());
394
+
395
+ // Clear panel area
396
+ for (var r = panelTop; r <= panelTop + 4; r++) {
397
+ process.stdout.write(cursorTo(r, 1) + clearLine());
398
+ }
399
+
400
+ // Also use scroll area bottom for more space
401
+ var settingsTop = panelTop - fields.length - 3;
402
+ if (settingsTop < 2) settingsTop = 2;
403
+
404
+ // Header
405
+ process.stdout.write(cursorTo(settingsTop, 1) + clearLine());
406
+ process.stdout.write(t.frame(" ╭─ Settings ") + t.frame("─".repeat(Math.max(1, cols - 16))) + t.frame("╮"));
407
+
408
+ // Fields
409
+ for (var i = 0; i < fields.length; i++) {
410
+ var row = settingsTop + 1 + i;
411
+ process.stdout.write(cursorTo(row, 1) + clearLine());
412
+ var marker = i === selectedField ? chalk.cyan(" ▸ ") : " ";
413
+ var label = fields[i].label;
414
+ var value = ui.formatSettingValue(fields[i], settings[fields[i].key]);
415
+ var help = i === selectedField ? chalk.dim(" ← → change") : "";
416
+ process.stdout.write(t.frame(" │") + marker + chalk.white(label) + " ".repeat(Math.max(1, 16 - label.length)) + value + help);
417
+ process.stdout.write(cursorTo(row, cols) + t.frame("│"));
418
+ }
419
+
420
+ // Footer
421
+ var footerRow = settingsTop + 1 + fields.length;
422
+ process.stdout.write(cursorTo(footerRow, 1) + clearLine());
423
+ process.stdout.write(t.frame(" │") + chalk.dim(" ↑↓ select ← → change Enter/Esc close"));
424
+ process.stdout.write(cursorTo(footerRow, cols) + t.frame("│"));
425
+
426
+ process.stdout.write(cursorTo(footerRow + 1, 1) + clearLine());
427
+ process.stdout.write(t.frame(" ╰") + t.frame("─".repeat(Math.max(1, cols - 4))) + t.frame("╯"));
428
+
429
+ process.stdout.write(restoreCursor());
430
+ }
431
+
432
+ // Replace data listener temporarily
433
+ process.stdin.removeAllListeners("data");
434
+
435
+ function onSettingsKey(key) {
436
+ if (!settingsActive) return;
437
+
438
+ // Escape or Enter — close settings
439
+ if (key === "\u001b" && key.length === 1 || key === "\r" || key === "\n") {
440
+ settingsActive = false;
441
+ process.stdin.removeAllListeners("data");
442
+ // Redraw normal UI
443
+ var rows = getRows();
444
+ var settingsTop = rows - INPUT_PANEL_HEIGHT + 1 - fields.length - 3;
445
+ if (settingsTop < 2) settingsTop = 2;
446
+ for (var r = settingsTop; r <= rows; r++) {
447
+ process.stdout.write(cursorTo(r, 1) + clearLine());
448
+ }
449
+ drawInputPanel();
450
+ startRawInput();
451
+ return;
452
+ }
453
+
454
+ // Up arrow
455
+ if (key === "\u001b[A") {
456
+ selectedField = (selectedField - 1 + fields.length) % fields.length;
457
+ drawSettings();
458
+ return;
459
+ }
460
+
461
+ // Down arrow
462
+ if (key === "\u001b[B") {
463
+ selectedField = (selectedField + 1) % fields.length;
464
+ drawSettings();
465
+ return;
466
+ }
467
+
468
+ // Left/Right arrow — cycle value
469
+ if (key === "\u001b[D" || key === "\u001b[C") {
470
+ var field = fields[selectedField];
471
+ var values = field.values;
472
+ var curIdx = values.indexOf(settings[field.key]);
473
+ if (curIdx < 0) curIdx = 0;
474
+ var dir = key === "\u001b[C" ? 1 : -1;
475
+ var newIdx = (curIdx + dir + values.length) % values.length;
476
+ settings[field.key] = values[newIdx];
477
+ if (field.key === "theme") ui.setTheme(settings.theme);
478
+ auth.saveSettings(settings);
479
+ drawSettings();
480
+ return;
481
+ }
482
+
483
+ // Ctrl+C
484
+ if (key === "\u0003") {
485
+ settingsActive = false;
486
+ process.stdin.removeAllListeners("data");
487
+ drawInputPanel();
488
+ startRawInput();
489
+ return;
490
+ }
491
+ }
492
+
493
+ process.stdin.on("data", onSettingsKey);
494
+ drawSettings();
495
+ }
496
+
497
+ // ── Raw Command Picker ─────────────────────────────────
498
+ function openRawCommandPicker() {
499
+ var pickerActive = true;
500
+ var selectedCmd = 0;
501
+
502
+ function drawPicker() {
503
+ var rows = getRows();
504
+ var cols = getCols();
505
+ var t = ui.getTheme();
506
+ var panelTop = rows - INPUT_PANEL_HEIGHT + 1;
507
+ var pickerTop = panelTop - SLASH_COMMANDS.length - 3;
508
+ if (pickerTop < 2) pickerTop = 2;
509
+
510
+ process.stdout.write(saveCursor());
511
+
512
+ // Header
513
+ process.stdout.write(cursorTo(pickerTop, 1) + clearLine());
514
+ process.stdout.write(t.frame(" ╭─ Commands ") + t.frame("─".repeat(Math.max(1, cols - 16))) + t.frame("╮"));
515
+
516
+ for (var i = 0; i < SLASH_COMMANDS.length; i++) {
517
+ var row = pickerTop + 1 + i;
518
+ process.stdout.write(cursorTo(row, 1) + clearLine());
519
+ var marker = i === selectedCmd ? chalk.cyan(" ▸ ") : " ";
520
+ process.stdout.write(t.frame(" │") + marker + chalk.green(SLASH_COMMANDS[i][0]) + " ".repeat(Math.max(1, 14 - SLASH_COMMANDS[i][0].length)) + chalk.gray(SLASH_COMMANDS[i][1]));
521
+ process.stdout.write(cursorTo(row, cols) + t.frame("│"));
522
+ }
523
+
524
+ var footerRow = pickerTop + 1 + SLASH_COMMANDS.length;
525
+ process.stdout.write(cursorTo(footerRow, 1) + clearLine());
526
+ process.stdout.write(t.frame(" │") + chalk.dim(" ↑↓ select Enter run Esc close"));
527
+ process.stdout.write(cursorTo(footerRow, cols) + t.frame("│"));
528
+ process.stdout.write(cursorTo(footerRow + 1, 1) + clearLine());
529
+ process.stdout.write(t.frame(" ╰") + t.frame("─".repeat(Math.max(1, cols - 4))) + t.frame("╯"));
530
+
531
+ process.stdout.write(restoreCursor());
532
+ }
533
+
534
+ process.stdin.removeAllListeners("data");
535
+
536
+ function onPickerKey(key) {
537
+ if (!pickerActive) return;
538
+
539
+ if (key === "\u001b" && key.length === 1 || key === "\u0003") {
540
+ pickerActive = false;
541
+ process.stdin.removeAllListeners("data");
542
+ var rows = getRows();
543
+ var pickerTop = rows - INPUT_PANEL_HEIGHT + 1 - SLASH_COMMANDS.length - 3;
544
+ if (pickerTop < 2) pickerTop = 2;
545
+ for (var r = pickerTop; r <= rows; r++) {
546
+ process.stdout.write(cursorTo(r, 1) + clearLine());
547
+ }
548
+ drawInputPanel();
549
+ startRawInput();
550
+ return;
551
+ }
552
+
553
+ if (key === "\u001b[A") {
554
+ selectedCmd = (selectedCmd - 1 + SLASH_COMMANDS.length) % SLASH_COMMANDS.length;
555
+ drawPicker();
556
+ return;
557
+ }
558
+
559
+ if (key === "\u001b[B") {
560
+ selectedCmd = (selectedCmd + 1) % SLASH_COMMANDS.length;
561
+ drawPicker();
562
+ return;
563
+ }
564
+
565
+ if (key === "\r" || key === "\n") {
566
+ pickerActive = false;
567
+ process.stdin.removeAllListeners("data");
568
+ var cmd = SLASH_COMMANDS[selectedCmd][0];
569
+ var rows = getRows();
570
+ var pickerTop = rows - INPUT_PANEL_HEIGHT + 1 - SLASH_COMMANDS.length - 3;
571
+ if (pickerTop < 2) pickerTop = 2;
572
+ for (var r = pickerTop; r <= rows; r++) {
573
+ process.stdout.write(cursorTo(r, 1) + clearLine());
574
+ }
575
+ drawInputPanel();
576
+ startRawInput();
577
+ processCommand(cmd);
578
+ return;
579
+ }
580
+ }
581
+
582
+ process.stdin.on("data", onPickerKey);
583
+ drawPicker();
584
+ }
585
+
416
586
  // ── Settings Panel (inquirer) ─────────────────────────
417
587
  function openSettingsPanel(done) {
418
588
  var inquirer = require("inquirer");
@@ -460,23 +630,35 @@ function startChat() {
460
630
  // ── Menu Mode (temporarily disable raw input) ────────
461
631
  function enterMenuMode(fn) {
462
632
  isInMenu = true;
463
- // Reset scroll region for inquirer to work properly
633
+ // Reset everything for inquirer
464
634
  var rows = getRows();
635
+ // Remove scroll region
465
636
  process.stdout.write(setScrollRegion(1, rows));
466
- process.stdout.write(cursorTo(rows - INPUT_PANEL_HEIGHT, 1));
637
+ // Move cursor below the panel area
638
+ process.stdout.write(cursorTo(rows - INPUT_PANEL_HEIGHT - 1, 1));
467
639
  process.stdout.write(showCursor());
468
- try { process.stdin.setRawMode(false); } catch (e) {}
640
+ // Exit raw mode and clean up listeners
469
641
  process.stdin.removeAllListeners("data");
642
+ process.stdin.removeAllListeners("keypress");
643
+ try { process.stdin.setRawMode(false); } catch (e) {}
644
+ // Fully reset stdin for inquirer
470
645
  process.stdin.pause();
471
- process.stdin.resume();
472
-
473
- fn(function () {
474
- // Re-enter TUI mode
475
- isInMenu = false;
476
- setupLayout();
477
- drawInputPanel();
478
- startRawInput();
479
- });
646
+ // Small delay to let stdin settle before inquirer takes over
647
+ setTimeout(function () {
648
+ fn(function () {
649
+ // Re-enter TUI mode
650
+ isInMenu = false;
651
+ console.clear();
652
+ ui.renderBanner();
653
+ var creds3 = auth.getStoredCredentials();
654
+ if (creds3) {
655
+ console.log(chalk.yellow(" [SYS] Connected as " + (creds3.name || creds3.email || "user") + " | Plan: " + (creds3.plan || "free")));
656
+ }
657
+ setupLayout();
658
+ drawInputPanel();
659
+ startRawInput();
660
+ });
661
+ }, 50);
480
662
  }
481
663
 
482
664
  // ── Cleanup ──────────────────────────────────────────
@@ -602,6 +784,12 @@ function startChat() {
602
784
  setTimeout(function () {
603
785
  // Clear screen, draw banner, then enter TUI mode
604
786
  console.clear();
787
+ // Push banner down a few lines
788
+ var rows = getRows();
789
+ var contentHeight = 12; // banner ~6 + info ~3 + padding ~3
790
+ var scrollArea = rows - INPUT_PANEL_HEIGHT;
791
+ var topPadding = Math.max(0, Math.floor((scrollArea - contentHeight) / 2));
792
+ for (var p = 0; p < topPadding; p++) console.log("");
605
793
  // Render banner in normal mode first (before scroll region)
606
794
  ui.renderBanner();
607
795
  var creds2 = auth.getStoredCredentials();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "7.2.11",
3
+ "version": "7.3.0",
4
4
  "description": "BLUN AI Assistant - Command Line Interface",
5
5
  "bin": {
6
6
  "blun": "./bin/blun.js"