blun-king-cli 7.3.0 → 7.3.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/lib/chat.js +93 -157
- package/lib/ui.js +14 -4
- package/package.json +1 -1
package/lib/chat.js
CHANGED
|
@@ -378,209 +378,136 @@ function startChat() {
|
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
// ── Raw-Mode Settings Panel (no inquirer needed) ──────
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
var
|
|
384
|
-
var
|
|
381
|
+
// ── Generic dropdown below input panel ─────────────────
|
|
382
|
+
function openDropdownBelow(items, onSelect, onClose) {
|
|
383
|
+
var active = true;
|
|
384
|
+
var selected = 0;
|
|
385
385
|
|
|
386
|
-
function
|
|
386
|
+
function getMenuStart() {
|
|
387
387
|
var rows = getRows();
|
|
388
|
-
var cols = getCols();
|
|
389
|
-
var t = ui.getTheme();
|
|
390
388
|
var panelTop = rows - INPUT_PANEL_HEIGHT + 1;
|
|
389
|
+
return panelTop + 5; // right after the bottom frame of input panel
|
|
390
|
+
}
|
|
391
391
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
for (var r = panelTop; r <= panelTop + 4; r++) {
|
|
392
|
+
function clearMenu() {
|
|
393
|
+
var start = getMenuStart();
|
|
394
|
+
var rows = getRows();
|
|
395
|
+
for (var r = start; r <= rows; r++) {
|
|
397
396
|
process.stdout.write(cursorTo(r, 1) + clearLine());
|
|
398
397
|
}
|
|
398
|
+
}
|
|
399
399
|
|
|
400
|
-
|
|
401
|
-
var
|
|
402
|
-
|
|
400
|
+
function drawMenu() {
|
|
401
|
+
var cols = getCols();
|
|
402
|
+
var t = ui.getTheme();
|
|
403
|
+
var start = getMenuStart();
|
|
403
404
|
|
|
404
|
-
|
|
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("╮"));
|
|
405
|
+
process.stdout.write(saveCursor());
|
|
407
406
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
var row = settingsTop + 1 + i;
|
|
407
|
+
for (var i = 0; i < items.length; i++) {
|
|
408
|
+
var row = start + i;
|
|
411
409
|
process.stdout.write(cursorTo(row, 1) + clearLine());
|
|
412
|
-
var marker = i ===
|
|
413
|
-
|
|
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("│"));
|
|
410
|
+
var marker = i === selected ? chalk.cyan(" ▸ ") : " ";
|
|
411
|
+
process.stdout.write(marker + items[i].display);
|
|
418
412
|
}
|
|
419
413
|
|
|
420
|
-
//
|
|
421
|
-
var
|
|
422
|
-
process.stdout.write(cursorTo(
|
|
423
|
-
process.stdout.write(
|
|
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("╯"));
|
|
414
|
+
// Help line
|
|
415
|
+
var helpRow = start + items.length;
|
|
416
|
+
process.stdout.write(cursorTo(helpRow, 1) + clearLine());
|
|
417
|
+
process.stdout.write(chalk.dim(" ↑↓ select " + (items[0].help || "Enter confirm Esc close")));
|
|
428
418
|
|
|
429
419
|
process.stdout.write(restoreCursor());
|
|
430
420
|
}
|
|
431
421
|
|
|
432
|
-
// Replace data listener temporarily
|
|
433
422
|
process.stdin.removeAllListeners("data");
|
|
434
423
|
|
|
435
|
-
function
|
|
436
|
-
if (!
|
|
424
|
+
function onKey(key) {
|
|
425
|
+
if (!active) return;
|
|
437
426
|
|
|
438
|
-
// Escape
|
|
439
|
-
if (key === "\u001b" && key.length === 1 || key === "\
|
|
440
|
-
|
|
427
|
+
// Escape
|
|
428
|
+
if ((key === "\u001b" && key.length === 1) || key === "\u0003") {
|
|
429
|
+
active = false;
|
|
441
430
|
process.stdin.removeAllListeners("data");
|
|
442
|
-
|
|
443
|
-
|
|
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();
|
|
431
|
+
clearMenu();
|
|
432
|
+
if (onClose) onClose();
|
|
450
433
|
startRawInput();
|
|
451
434
|
return;
|
|
452
435
|
}
|
|
453
436
|
|
|
454
|
-
// Up
|
|
437
|
+
// Up
|
|
455
438
|
if (key === "\u001b[A") {
|
|
456
|
-
|
|
457
|
-
|
|
439
|
+
selected = (selected - 1 + items.length) % items.length;
|
|
440
|
+
drawMenu();
|
|
458
441
|
return;
|
|
459
442
|
}
|
|
460
443
|
|
|
461
|
-
// Down
|
|
444
|
+
// Down
|
|
462
445
|
if (key === "\u001b[B") {
|
|
463
|
-
|
|
464
|
-
|
|
446
|
+
selected = (selected + 1) % items.length;
|
|
447
|
+
drawMenu();
|
|
465
448
|
return;
|
|
466
449
|
}
|
|
467
450
|
|
|
468
|
-
// Left/Right
|
|
451
|
+
// Left/Right for settings cycling
|
|
469
452
|
if (key === "\u001b[D" || key === "\u001b[C") {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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();
|
|
453
|
+
if (items[selected].onCycle) {
|
|
454
|
+
items[selected].onCycle(key === "\u001b[C" ? 1 : -1);
|
|
455
|
+
drawMenu();
|
|
456
|
+
}
|
|
480
457
|
return;
|
|
481
458
|
}
|
|
482
459
|
|
|
483
|
-
//
|
|
484
|
-
if (key === "\
|
|
485
|
-
|
|
460
|
+
// Enter
|
|
461
|
+
if (key === "\r" || key === "\n") {
|
|
462
|
+
active = false;
|
|
486
463
|
process.stdin.removeAllListeners("data");
|
|
487
|
-
|
|
464
|
+
clearMenu();
|
|
465
|
+
if (onSelect) onSelect(items[selected]);
|
|
488
466
|
startRawInput();
|
|
489
467
|
return;
|
|
490
468
|
}
|
|
491
469
|
}
|
|
492
470
|
|
|
493
|
-
process.stdin.on("data",
|
|
494
|
-
|
|
471
|
+
process.stdin.on("data", onKey);
|
|
472
|
+
drawMenu();
|
|
495
473
|
}
|
|
496
474
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
var
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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());
|
|
475
|
+
function openRawSettings() {
|
|
476
|
+
var fields = ui.SETTINGS_FIELDS;
|
|
477
|
+
var items = fields.map(function (f) {
|
|
478
|
+
return {
|
|
479
|
+
display: chalk.white(f.label) + " ".repeat(Math.max(1, 16 - f.label.length)) + ui.formatSettingValue(f, settings[f.key]),
|
|
480
|
+
field: f,
|
|
481
|
+
help: "← → change Enter/Esc close",
|
|
482
|
+
onCycle: function (dir) {
|
|
483
|
+
var values = f.values;
|
|
484
|
+
var curIdx = values.indexOf(settings[f.key]);
|
|
485
|
+
if (curIdx < 0) curIdx = 0;
|
|
486
|
+
var newIdx = (curIdx + dir + values.length) % values.length;
|
|
487
|
+
settings[f.key] = values[newIdx];
|
|
488
|
+
if (f.key === "theme") ui.setTheme(settings.theme);
|
|
489
|
+
auth.saveSettings(settings);
|
|
490
|
+
// Update display text
|
|
491
|
+
this.display = chalk.white(f.label) + " ".repeat(Math.max(1, 16 - f.label.length)) + ui.formatSettingValue(f, settings[f.key]);
|
|
492
|
+
drawInputPanel(); // refresh status bar
|
|
574
493
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
}
|
|
494
|
+
};
|
|
495
|
+
});
|
|
496
|
+
openDropdownBelow(items, function () { drawInputPanel(); }, function () { drawInputPanel(); });
|
|
497
|
+
}
|
|
581
498
|
|
|
582
|
-
|
|
583
|
-
|
|
499
|
+
function openRawCommandPicker() {
|
|
500
|
+
var items = SLASH_COMMANDS.map(function (c) {
|
|
501
|
+
return {
|
|
502
|
+
display: chalk.green(c[0]) + " ".repeat(Math.max(1, 14 - c[0].length)) + chalk.gray(c[1]),
|
|
503
|
+
cmd: c[0],
|
|
504
|
+
help: "Enter run Esc close"
|
|
505
|
+
};
|
|
506
|
+
});
|
|
507
|
+
openDropdownBelow(items, function (item) {
|
|
508
|
+
drawInputPanel();
|
|
509
|
+
processCommand(item.cmd);
|
|
510
|
+
}, function () { drawInputPanel(); });
|
|
584
511
|
}
|
|
585
512
|
|
|
586
513
|
// ── Settings Panel (inquirer) ─────────────────────────
|
|
@@ -775,6 +702,15 @@ function startChat() {
|
|
|
775
702
|
inputBuffer = inputBuffer.slice(0, cursorPos) + key + inputBuffer.slice(cursorPos);
|
|
776
703
|
cursorPos += key.length;
|
|
777
704
|
drawInputPanel();
|
|
705
|
+
|
|
706
|
+
// Live "/" dropdown — show command picker as soon as "/" is typed
|
|
707
|
+
if (inputBuffer === "/") {
|
|
708
|
+
inputBuffer = "";
|
|
709
|
+
cursorPos = 0;
|
|
710
|
+
drawInputPanel();
|
|
711
|
+
openRawCommandPicker();
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
778
714
|
});
|
|
779
715
|
}
|
|
780
716
|
|
package/lib/ui.js
CHANGED
|
@@ -61,13 +61,23 @@ function setTheme(name) {
|
|
|
61
61
|
// ── Settings Fields ───────────────────────────────────────
|
|
62
62
|
var SETTINGS_FIELDS = [
|
|
63
63
|
{ section: "Core", key: "model", label: "Model", values: ["auto", "gemma4", "claude", "llama"], help: "Welches Modell standardmaessig antwortet." },
|
|
64
|
-
{ section: "Core", key: "language", label: "Language", values: ["de", "en"], help: "Primaere Antwortsprache." },
|
|
64
|
+
{ section: "Core", key: "language", label: "Language", values: ["de", "en", "tr", "fr", "es"], help: "Primaere Antwortsprache." },
|
|
65
|
+
{ section: "Core", key: "permissionMode", label: "Permission", values: ["safe", "normal", "god"], help: "Welche Aktionen BLUN ausfuehren darf." },
|
|
66
|
+
{ section: "Core", key: "maxTokens", label: "Max Tokens", values: [1024, 2048, 4096, 8192, 16384], help: "Maximale Antwortlaenge." },
|
|
65
67
|
{ section: "Console", key: "theme", label: "Theme", values: ["neon", "midnight", "minimal"], help: "Farbstimmung der Konsole." },
|
|
66
|
-
{ section: "Console", key: "streamLevel", label: "Stream", values: ["off", "normal"], help: "
|
|
68
|
+
{ section: "Console", key: "streamLevel", label: "Stream", values: ["off", "normal", "verbose"], help: "Wie Antworten angezeigt werden." },
|
|
69
|
+
{ section: "Console", key: "markdownRendering", label: "Markdown", values: [true, false], help: "Markdown in Antworten rendern." },
|
|
70
|
+
{ section: "Console", key: "codeHighlight", label: "Syntax", values: [true, false], help: "Code-Syntax-Highlighting." },
|
|
71
|
+
{ section: "Console", key: "statuslineMode", label: "Statusline", values: ["compact", "full", "off"], help: "Wie detailliert die Statuszeile ist." },
|
|
67
72
|
{ section: "Runtime", key: "voice", label: "Voice", values: [true, false], help: "Voice-Features an/aus." },
|
|
68
|
-
{ section: "Runtime", key: "
|
|
73
|
+
{ section: "Runtime", key: "autoSave", label: "AutoSave", values: [true, false], help: "Chat-Verlauf automatisch speichern." },
|
|
74
|
+
{ section: "Runtime", key: "contextMemory", label: "Memory", values: [true, false], help: "Kontext aus vorherigen Chats nutzen." },
|
|
75
|
+
{ section: "Runtime", key: "fileAccess", label: "File Access", values: ["ask", "auto", "off"], help: "Dateizugriff: fragen/automatisch/aus." },
|
|
76
|
+
{ section: "Runtime", key: "shellAccess", label: "Shell", values: ["ask", "auto", "off"], help: "Shell-Befehle: fragen/automatisch/aus." },
|
|
69
77
|
{ section: "Workflow", key: "costVisibility", label: "Cost", values: [true, false], help: "Token-/Kosten-Footer anzeigen." },
|
|
70
|
-
{ section: "Workflow", key: "
|
|
78
|
+
{ section: "Workflow", key: "autoCommit", label: "AutoCommit", values: [true, false], help: "Automatisch Git-Commits erstellen." },
|
|
79
|
+
{ section: "Workflow", key: "codeReview", label: "Review", values: [true, false], help: "Code-Review vor Aenderungen." },
|
|
80
|
+
{ section: "Workflow", key: "notifications", label: "Notify", values: [true, false], help: "Desktop-Benachrichtigungen." },
|
|
71
81
|
];
|
|
72
82
|
|
|
73
83
|
var PERMISSION_MODES = ["safe", "normal", "god"];
|