@austinthesing/magic-shell 0.2.5 → 0.2.8

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/tui.js CHANGED
@@ -20475,6 +20475,523 @@ class EditBufferRenderable extends Renderable {
20475
20475
  }
20476
20476
  }
20477
20477
  }
20478
+ var defaultTextareaKeybindings = [
20479
+ { name: "left", action: "move-left" },
20480
+ { name: "right", action: "move-right" },
20481
+ { name: "up", action: "move-up" },
20482
+ { name: "down", action: "move-down" },
20483
+ { name: "left", shift: true, action: "select-left" },
20484
+ { name: "right", shift: true, action: "select-right" },
20485
+ { name: "up", shift: true, action: "select-up" },
20486
+ { name: "down", shift: true, action: "select-down" },
20487
+ { name: "home", action: "buffer-home" },
20488
+ { name: "end", action: "buffer-end" },
20489
+ { name: "home", shift: true, action: "select-buffer-home" },
20490
+ { name: "end", shift: true, action: "select-buffer-end" },
20491
+ { name: "a", ctrl: true, action: "line-home" },
20492
+ { name: "e", ctrl: true, action: "line-end" },
20493
+ { name: "a", ctrl: true, shift: true, action: "select-line-home" },
20494
+ { name: "e", ctrl: true, shift: true, action: "select-line-end" },
20495
+ { name: "a", meta: true, action: "visual-line-home" },
20496
+ { name: "e", meta: true, action: "visual-line-end" },
20497
+ { name: "a", meta: true, shift: true, action: "select-visual-line-home" },
20498
+ { name: "e", meta: true, shift: true, action: "select-visual-line-end" },
20499
+ { name: "f", ctrl: true, action: "move-right" },
20500
+ { name: "b", ctrl: true, action: "move-left" },
20501
+ { name: "w", ctrl: true, action: "delete-word-backward" },
20502
+ { name: "backspace", ctrl: true, action: "delete-word-backward" },
20503
+ { name: "d", meta: true, action: "delete-word-forward" },
20504
+ { name: "delete", meta: true, action: "delete-word-forward" },
20505
+ { name: "delete", ctrl: true, action: "delete-word-forward" },
20506
+ { name: "d", ctrl: true, shift: true, action: "delete-line" },
20507
+ { name: "k", ctrl: true, action: "delete-to-line-end" },
20508
+ { name: "u", ctrl: true, action: "delete-to-line-start" },
20509
+ { name: "backspace", action: "backspace" },
20510
+ { name: "backspace", shift: true, action: "backspace" },
20511
+ { name: "d", ctrl: true, action: "delete" },
20512
+ { name: "delete", action: "delete" },
20513
+ { name: "delete", shift: true, action: "delete" },
20514
+ { name: "return", action: "newline" },
20515
+ { name: "linefeed", action: "newline" },
20516
+ { name: "return", meta: true, action: "submit" },
20517
+ { name: "-", ctrl: true, action: "undo" },
20518
+ { name: ".", ctrl: true, action: "redo" },
20519
+ { name: "z", super: true, action: "undo" },
20520
+ { name: "z", super: true, shift: true, action: "redo" },
20521
+ { name: "f", meta: true, action: "word-forward" },
20522
+ { name: "b", meta: true, action: "word-backward" },
20523
+ { name: "right", meta: true, action: "word-forward" },
20524
+ { name: "left", meta: true, action: "word-backward" },
20525
+ { name: "right", ctrl: true, action: "word-forward" },
20526
+ { name: "left", ctrl: true, action: "word-backward" },
20527
+ { name: "f", meta: true, shift: true, action: "select-word-forward" },
20528
+ { name: "b", meta: true, shift: true, action: "select-word-backward" },
20529
+ { name: "right", meta: true, shift: true, action: "select-word-forward" },
20530
+ { name: "left", meta: true, shift: true, action: "select-word-backward" },
20531
+ { name: "backspace", meta: true, action: "delete-word-backward" },
20532
+ { name: "left", super: true, action: "visual-line-home" },
20533
+ { name: "right", super: true, action: "visual-line-end" },
20534
+ { name: "up", super: true, action: "buffer-home" },
20535
+ { name: "down", super: true, action: "buffer-end" },
20536
+ { name: "left", super: true, shift: true, action: "select-visual-line-home" },
20537
+ { name: "right", super: true, shift: true, action: "select-visual-line-end" },
20538
+ { name: "up", super: true, shift: true, action: "select-buffer-home" },
20539
+ { name: "down", super: true, shift: true, action: "select-buffer-end" }
20540
+ ];
20541
+
20542
+ class TextareaRenderable extends EditBufferRenderable {
20543
+ _placeholder;
20544
+ _unfocusedBackgroundColor;
20545
+ _unfocusedTextColor;
20546
+ _focusedBackgroundColor;
20547
+ _focusedTextColor;
20548
+ _keyBindingsMap;
20549
+ _keyAliasMap;
20550
+ _keyBindings;
20551
+ _actionHandlers;
20552
+ _initialValueSet = false;
20553
+ _submitListener = undefined;
20554
+ static defaults = {
20555
+ backgroundColor: "transparent",
20556
+ textColor: "#FFFFFF",
20557
+ focusedBackgroundColor: "transparent",
20558
+ focusedTextColor: "#FFFFFF",
20559
+ placeholder: null
20560
+ };
20561
+ constructor(ctx, options) {
20562
+ const defaults = TextareaRenderable.defaults;
20563
+ const baseOptions = {
20564
+ ...options,
20565
+ backgroundColor: options.backgroundColor || defaults.backgroundColor,
20566
+ textColor: options.textColor || defaults.textColor
20567
+ };
20568
+ super(ctx, baseOptions);
20569
+ this._unfocusedBackgroundColor = parseColor(options.backgroundColor || defaults.backgroundColor);
20570
+ this._unfocusedTextColor = parseColor(options.textColor || defaults.textColor);
20571
+ this._focusedBackgroundColor = parseColor(options.focusedBackgroundColor || options.backgroundColor || defaults.focusedBackgroundColor);
20572
+ this._focusedTextColor = parseColor(options.focusedTextColor || options.textColor || defaults.focusedTextColor);
20573
+ this._placeholder = options.placeholder ?? defaults.placeholder;
20574
+ this._keyAliasMap = mergeKeyAliases(defaultKeyAliases, options.keyAliasMap || {});
20575
+ this._keyBindings = options.keyBindings || [];
20576
+ const mergedBindings = mergeKeyBindings(defaultTextareaKeybindings, this._keyBindings);
20577
+ this._keyBindingsMap = buildKeyBindingsMap(mergedBindings, this._keyAliasMap);
20578
+ this._actionHandlers = this.buildActionHandlers();
20579
+ this._submitListener = options.onSubmit;
20580
+ if (options.initialValue) {
20581
+ this.setText(options.initialValue);
20582
+ this._initialValueSet = true;
20583
+ }
20584
+ this.updateColors();
20585
+ this.applyPlaceholder(this._placeholder);
20586
+ }
20587
+ applyPlaceholder(placeholder) {
20588
+ if (placeholder === null) {
20589
+ this.editorView.setPlaceholderStyledText([]);
20590
+ return;
20591
+ }
20592
+ if (typeof placeholder === "string") {
20593
+ const defaultGray = fg("#666666");
20594
+ const chunks = [defaultGray(placeholder)];
20595
+ this.editorView.setPlaceholderStyledText(chunks);
20596
+ } else {
20597
+ this.editorView.setPlaceholderStyledText(placeholder.chunks);
20598
+ }
20599
+ }
20600
+ buildActionHandlers() {
20601
+ return new Map([
20602
+ ["move-left", () => this.moveCursorLeft()],
20603
+ ["move-right", () => this.moveCursorRight()],
20604
+ ["move-up", () => this.moveCursorUp()],
20605
+ ["move-down", () => this.moveCursorDown()],
20606
+ ["select-left", () => this.moveCursorLeft({ select: true })],
20607
+ ["select-right", () => this.moveCursorRight({ select: true })],
20608
+ ["select-up", () => this.moveCursorUp({ select: true })],
20609
+ ["select-down", () => this.moveCursorDown({ select: true })],
20610
+ ["line-home", () => this.gotoLineHome()],
20611
+ ["line-end", () => this.gotoLineEnd()],
20612
+ ["select-line-home", () => this.gotoLineHome({ select: true })],
20613
+ ["select-line-end", () => this.gotoLineEnd({ select: true })],
20614
+ ["visual-line-home", () => this.gotoVisualLineHome()],
20615
+ ["visual-line-end", () => this.gotoVisualLineEnd()],
20616
+ ["select-visual-line-home", () => this.gotoVisualLineHome({ select: true })],
20617
+ ["select-visual-line-end", () => this.gotoVisualLineEnd({ select: true })],
20618
+ ["select-buffer-home", () => this.gotoBufferHome({ select: true })],
20619
+ ["select-buffer-end", () => this.gotoBufferEnd({ select: true })],
20620
+ ["buffer-home", () => this.gotoBufferHome()],
20621
+ ["buffer-end", () => this.gotoBufferEnd()],
20622
+ ["delete-line", () => this.deleteLine()],
20623
+ ["delete-to-line-end", () => this.deleteToLineEnd()],
20624
+ ["delete-to-line-start", () => this.deleteToLineStart()],
20625
+ ["backspace", () => this.deleteCharBackward()],
20626
+ ["delete", () => this.deleteChar()],
20627
+ ["newline", () => this.newLine()],
20628
+ ["undo", () => this.undo()],
20629
+ ["redo", () => this.redo()],
20630
+ ["word-forward", () => this.moveWordForward()],
20631
+ ["word-backward", () => this.moveWordBackward()],
20632
+ ["select-word-forward", () => this.moveWordForward({ select: true })],
20633
+ ["select-word-backward", () => this.moveWordBackward({ select: true })],
20634
+ ["delete-word-forward", () => this.deleteWordForward()],
20635
+ ["delete-word-backward", () => this.deleteWordBackward()],
20636
+ ["submit", () => this.submit()]
20637
+ ]);
20638
+ }
20639
+ handlePaste(event) {
20640
+ this.insertText(event.text);
20641
+ }
20642
+ handleKeyPress(key) {
20643
+ const bindingKey = getKeyBindingKey({
20644
+ name: key.name,
20645
+ ctrl: key.ctrl,
20646
+ shift: key.shift,
20647
+ meta: key.meta,
20648
+ super: key.super,
20649
+ action: "move-left"
20650
+ });
20651
+ const action = this._keyBindingsMap.get(bindingKey);
20652
+ if (action) {
20653
+ const handler = this._actionHandlers.get(action);
20654
+ if (handler) {
20655
+ return handler();
20656
+ }
20657
+ }
20658
+ if (!key.ctrl && !key.meta && !key.super && !key.hyper) {
20659
+ if (key.name === "space") {
20660
+ this.insertText(" ");
20661
+ return true;
20662
+ }
20663
+ if (key.sequence) {
20664
+ const firstCharCode = key.sequence.charCodeAt(0);
20665
+ if (firstCharCode < 32) {
20666
+ return false;
20667
+ }
20668
+ if (firstCharCode === 127) {
20669
+ return false;
20670
+ }
20671
+ this.insertText(key.sequence);
20672
+ return true;
20673
+ }
20674
+ }
20675
+ return false;
20676
+ }
20677
+ updateColors() {
20678
+ const effectiveBg = this._focused ? this._focusedBackgroundColor : this._unfocusedBackgroundColor;
20679
+ const effectiveFg = this._focused ? this._focusedTextColor : this._unfocusedTextColor;
20680
+ super.backgroundColor = effectiveBg;
20681
+ super.textColor = effectiveFg;
20682
+ }
20683
+ insertChar(char) {
20684
+ if (this.hasSelection()) {
20685
+ this.deleteSelectedText();
20686
+ }
20687
+ this.editBuffer.insertChar(char);
20688
+ this.requestRender();
20689
+ }
20690
+ insertText(text) {
20691
+ if (this.hasSelection()) {
20692
+ this.deleteSelectedText();
20693
+ }
20694
+ this.editBuffer.insertText(text);
20695
+ this.requestRender();
20696
+ }
20697
+ deleteChar() {
20698
+ if (this.hasSelection()) {
20699
+ this.deleteSelectedText();
20700
+ return true;
20701
+ }
20702
+ this._ctx.clearSelection();
20703
+ this.editBuffer.deleteChar();
20704
+ this.requestRender();
20705
+ return true;
20706
+ }
20707
+ deleteCharBackward() {
20708
+ if (this.hasSelection()) {
20709
+ this.deleteSelectedText();
20710
+ return true;
20711
+ }
20712
+ this._ctx.clearSelection();
20713
+ this.editBuffer.deleteCharBackward();
20714
+ this.requestRender();
20715
+ return true;
20716
+ }
20717
+ deleteSelectedText() {
20718
+ this.editorView.deleteSelectedText();
20719
+ this._ctx.clearSelection();
20720
+ this.requestRender();
20721
+ }
20722
+ newLine() {
20723
+ this._ctx.clearSelection();
20724
+ this.editBuffer.newLine();
20725
+ this.requestRender();
20726
+ return true;
20727
+ }
20728
+ deleteLine() {
20729
+ this._ctx.clearSelection();
20730
+ this.editBuffer.deleteLine();
20731
+ this.requestRender();
20732
+ return true;
20733
+ }
20734
+ moveCursorLeft(options) {
20735
+ const select = options?.select ?? false;
20736
+ this.updateSelectionForMovement(select, true);
20737
+ this.editBuffer.moveCursorLeft();
20738
+ this.updateSelectionForMovement(select, false);
20739
+ this.requestRender();
20740
+ return true;
20741
+ }
20742
+ moveCursorRight(options) {
20743
+ const select = options?.select ?? false;
20744
+ this.updateSelectionForMovement(select, true);
20745
+ this.editBuffer.moveCursorRight();
20746
+ this.updateSelectionForMovement(select, false);
20747
+ this.requestRender();
20748
+ return true;
20749
+ }
20750
+ moveCursorUp(options) {
20751
+ const select = options?.select ?? false;
20752
+ this.updateSelectionForMovement(select, true);
20753
+ this.editorView.moveUpVisual();
20754
+ this.updateSelectionForMovement(select, false);
20755
+ this.requestRender();
20756
+ return true;
20757
+ }
20758
+ moveCursorDown(options) {
20759
+ const select = options?.select ?? false;
20760
+ this.updateSelectionForMovement(select, true);
20761
+ this.editorView.moveDownVisual();
20762
+ this.updateSelectionForMovement(select, false);
20763
+ this.requestRender();
20764
+ return true;
20765
+ }
20766
+ gotoLine(line) {
20767
+ this.editBuffer.gotoLine(line);
20768
+ this.requestRender();
20769
+ }
20770
+ gotoLineHome(options) {
20771
+ const select = options?.select ?? false;
20772
+ this.updateSelectionForMovement(select, true);
20773
+ const cursor = this.editorView.getCursor();
20774
+ if (cursor.col === 0 && cursor.row > 0) {
20775
+ this.editBuffer.setCursor(cursor.row - 1, 0);
20776
+ const prevLineEol = this.editBuffer.getEOL();
20777
+ this.editBuffer.setCursor(prevLineEol.row, prevLineEol.col);
20778
+ } else {
20779
+ this.editBuffer.setCursor(cursor.row, 0);
20780
+ }
20781
+ this.updateSelectionForMovement(select, false);
20782
+ this.requestRender();
20783
+ return true;
20784
+ }
20785
+ gotoLineEnd(options) {
20786
+ const select = options?.select ?? false;
20787
+ this.updateSelectionForMovement(select, true);
20788
+ const cursor = this.editorView.getCursor();
20789
+ const eol = this.editBuffer.getEOL();
20790
+ const lineCount = this.editBuffer.getLineCount();
20791
+ if (cursor.col === eol.col && cursor.row < lineCount - 1) {
20792
+ this.editBuffer.setCursor(cursor.row + 1, 0);
20793
+ } else {
20794
+ this.editBuffer.setCursor(eol.row, eol.col);
20795
+ }
20796
+ this.updateSelectionForMovement(select, false);
20797
+ this.requestRender();
20798
+ return true;
20799
+ }
20800
+ gotoVisualLineHome(options) {
20801
+ const select = options?.select ?? false;
20802
+ this.updateSelectionForMovement(select, true);
20803
+ const sol = this.editorView.getVisualSOL();
20804
+ this.editBuffer.setCursor(sol.logicalRow, sol.logicalCol);
20805
+ this.updateSelectionForMovement(select, false);
20806
+ this.requestRender();
20807
+ return true;
20808
+ }
20809
+ gotoVisualLineEnd(options) {
20810
+ const select = options?.select ?? false;
20811
+ this.updateSelectionForMovement(select, true);
20812
+ const eol = this.editorView.getVisualEOL();
20813
+ this.editBuffer.setCursor(eol.logicalRow, eol.logicalCol);
20814
+ this.updateSelectionForMovement(select, false);
20815
+ this.requestRender();
20816
+ return true;
20817
+ }
20818
+ gotoBufferHome(options) {
20819
+ const select = options?.select ?? false;
20820
+ this.updateSelectionForMovement(select, true);
20821
+ this.editBuffer.setCursor(0, 0);
20822
+ this.updateSelectionForMovement(select, false);
20823
+ this.requestRender();
20824
+ return true;
20825
+ }
20826
+ gotoBufferEnd(options) {
20827
+ const select = options?.select ?? false;
20828
+ this.updateSelectionForMovement(select, true);
20829
+ this.editBuffer.gotoLine(999999);
20830
+ this.updateSelectionForMovement(select, false);
20831
+ this.requestRender();
20832
+ return true;
20833
+ }
20834
+ deleteToLineEnd() {
20835
+ const cursor = this.editorView.getCursor();
20836
+ const eol = this.editBuffer.getEOL();
20837
+ if (eol.col > cursor.col) {
20838
+ this.editBuffer.deleteRange(cursor.row, cursor.col, eol.row, eol.col);
20839
+ }
20840
+ this.requestRender();
20841
+ return true;
20842
+ }
20843
+ deleteToLineStart() {
20844
+ const cursor = this.editorView.getCursor();
20845
+ if (cursor.col > 0) {
20846
+ this.editBuffer.deleteRange(cursor.row, 0, cursor.row, cursor.col);
20847
+ }
20848
+ this.requestRender();
20849
+ return true;
20850
+ }
20851
+ undo() {
20852
+ this._ctx.clearSelection();
20853
+ this.editBuffer.undo();
20854
+ this.requestRender();
20855
+ return true;
20856
+ }
20857
+ redo() {
20858
+ this._ctx.clearSelection();
20859
+ this.editBuffer.redo();
20860
+ this.requestRender();
20861
+ return true;
20862
+ }
20863
+ moveWordForward(options) {
20864
+ const select = options?.select ?? false;
20865
+ this.updateSelectionForMovement(select, true);
20866
+ const nextWord = this.editBuffer.getNextWordBoundary();
20867
+ this.editBuffer.setCursorByOffset(nextWord.offset);
20868
+ this.updateSelectionForMovement(select, false);
20869
+ this.requestRender();
20870
+ return true;
20871
+ }
20872
+ moveWordBackward(options) {
20873
+ const select = options?.select ?? false;
20874
+ this.updateSelectionForMovement(select, true);
20875
+ const prevWord = this.editBuffer.getPrevWordBoundary();
20876
+ this.editBuffer.setCursorByOffset(prevWord.offset);
20877
+ this.updateSelectionForMovement(select, false);
20878
+ this.requestRender();
20879
+ return true;
20880
+ }
20881
+ deleteWordForward() {
20882
+ if (this.hasSelection()) {
20883
+ this.deleteSelectedText();
20884
+ return true;
20885
+ }
20886
+ const currentCursor = this.editBuffer.getCursorPosition();
20887
+ const nextWord = this.editBuffer.getNextWordBoundary();
20888
+ if (nextWord.offset > currentCursor.offset) {
20889
+ this.editBuffer.deleteRange(currentCursor.row, currentCursor.col, nextWord.row, nextWord.col);
20890
+ }
20891
+ this._ctx.clearSelection();
20892
+ this.requestRender();
20893
+ return true;
20894
+ }
20895
+ deleteWordBackward() {
20896
+ if (this.hasSelection()) {
20897
+ this.deleteSelectedText();
20898
+ return true;
20899
+ }
20900
+ const currentCursor = this.editBuffer.getCursorPosition();
20901
+ const prevWord = this.editBuffer.getPrevWordBoundary();
20902
+ if (prevWord.offset < currentCursor.offset) {
20903
+ this.editBuffer.deleteRange(prevWord.row, prevWord.col, currentCursor.row, currentCursor.col);
20904
+ }
20905
+ this._ctx.clearSelection();
20906
+ this.requestRender();
20907
+ return true;
20908
+ }
20909
+ focus() {
20910
+ super.focus();
20911
+ this.updateColors();
20912
+ }
20913
+ blur() {
20914
+ super.blur();
20915
+ if (!this.isDestroyed) {
20916
+ this.updateColors();
20917
+ }
20918
+ }
20919
+ get placeholder() {
20920
+ return this._placeholder;
20921
+ }
20922
+ set placeholder(value) {
20923
+ if (this._placeholder !== value) {
20924
+ this._placeholder = value;
20925
+ this.applyPlaceholder(value);
20926
+ this.requestRender();
20927
+ }
20928
+ }
20929
+ get backgroundColor() {
20930
+ return this._unfocusedBackgroundColor;
20931
+ }
20932
+ set backgroundColor(value) {
20933
+ const newColor = parseColor(value ?? TextareaRenderable.defaults.backgroundColor);
20934
+ if (this._unfocusedBackgroundColor !== newColor) {
20935
+ this._unfocusedBackgroundColor = newColor;
20936
+ this.updateColors();
20937
+ }
20938
+ }
20939
+ get textColor() {
20940
+ return this._unfocusedTextColor;
20941
+ }
20942
+ set textColor(value) {
20943
+ const newColor = parseColor(value ?? TextareaRenderable.defaults.textColor);
20944
+ if (this._unfocusedTextColor !== newColor) {
20945
+ this._unfocusedTextColor = newColor;
20946
+ this.updateColors();
20947
+ }
20948
+ }
20949
+ set focusedBackgroundColor(value) {
20950
+ const newColor = parseColor(value ?? TextareaRenderable.defaults.focusedBackgroundColor);
20951
+ if (this._focusedBackgroundColor !== newColor) {
20952
+ this._focusedBackgroundColor = newColor;
20953
+ this.updateColors();
20954
+ }
20955
+ }
20956
+ set focusedTextColor(value) {
20957
+ const newColor = parseColor(value ?? TextareaRenderable.defaults.focusedTextColor);
20958
+ if (this._focusedTextColor !== newColor) {
20959
+ this._focusedTextColor = newColor;
20960
+ this.updateColors();
20961
+ }
20962
+ }
20963
+ set initialValue(value) {
20964
+ if (!this._initialValueSet) {
20965
+ this.setText(value);
20966
+ this._initialValueSet = true;
20967
+ }
20968
+ }
20969
+ submit() {
20970
+ if (this._submitListener) {
20971
+ this._submitListener({});
20972
+ }
20973
+ return true;
20974
+ }
20975
+ set onSubmit(handler) {
20976
+ this._submitListener = handler;
20977
+ }
20978
+ get onSubmit() {
20979
+ return this._submitListener;
20980
+ }
20981
+ set keyBindings(bindings) {
20982
+ this._keyBindings = bindings;
20983
+ const mergedBindings = mergeKeyBindings(defaultTextareaKeybindings, bindings);
20984
+ this._keyBindingsMap = buildKeyBindingsMap(mergedBindings, this._keyAliasMap);
20985
+ }
20986
+ set keyAliasMap(aliases) {
20987
+ this._keyAliasMap = mergeKeyAliases(defaultKeyAliases, aliases);
20988
+ const mergedBindings = mergeKeyBindings(defaultTextareaKeybindings, this._keyBindings);
20989
+ this._keyBindingsMap = buildKeyBindingsMap(mergedBindings, this._keyAliasMap);
20990
+ }
20991
+ get extmarks() {
20992
+ return this.editorView.extmarks;
20993
+ }
20994
+ }
20478
20995
 
20479
20996
  // src/cli.ts
20480
20997
  import { spawn } from "child_process";
@@ -22106,6 +22623,8 @@ var headerText;
22106
22623
  var statusBarText;
22107
22624
  var chatScrollBox;
22108
22625
  var inputField;
22626
+ var inputContainer;
22627
+ var inputHintText;
22109
22628
  var helpBarText;
22110
22629
  var modelSelector = null;
22111
22630
  var providerSelector = null;
@@ -22333,59 +22852,75 @@ function createMainUI() {
22333
22852
  });
22334
22853
  mainContainer.add(chatScrollBox);
22335
22854
  addSystemMessage(getWelcomeMessage());
22336
- const inputRow = new BoxRenderable(renderer, {
22337
- id: "input-row",
22338
- flexDirection: "row",
22855
+ inputContainer = new BoxRenderable(renderer, {
22856
+ id: "input-container",
22857
+ flexDirection: "column",
22339
22858
  width: "100%",
22340
22859
  marginTop: 1,
22341
- alignItems: "center"
22342
- });
22343
- mainContainer.add(inputRow);
22344
- const promptText = new TextRenderable(renderer, {
22345
- id: "prompt-text",
22346
- content: t`${fg(theme.colors.primary)("~>")} `,
22347
- width: 3,
22348
- height: 1
22860
+ border: true,
22861
+ borderColor: theme.colors.primary,
22862
+ borderStyle: "rounded",
22863
+ paddingLeft: 1,
22864
+ paddingRight: 1,
22865
+ paddingTop: 0,
22866
+ paddingBottom: 0,
22867
+ backgroundColor: theme.colors.backgroundPanel
22349
22868
  });
22350
- inputRow.add(promptText);
22351
- inputField = new InputRenderable(renderer, {
22869
+ mainContainer.add(inputContainer);
22870
+ inputField = new TextareaRenderable(renderer, {
22352
22871
  id: "input-field",
22353
- flexGrow: 1,
22354
- height: 1,
22355
- placeholder: "describe what you want to do...",
22872
+ width: "100%",
22873
+ height: 3,
22874
+ placeholder: t`${fg(theme.colors.textMuted)("Describe what you want to do...")}`,
22356
22875
  backgroundColor: "transparent",
22357
- focusedBackgroundColor: theme.colors.backgroundPanel,
22876
+ focusedBackgroundColor: "transparent",
22358
22877
  textColor: theme.colors.text,
22359
- placeholderColor: theme.colors.textMuted,
22360
- cursorColor: theme.colors.primary,
22361
- onPaste: (event) => {
22362
- inputField.insertText(event.text);
22878
+ keyBindings: [
22879
+ { name: "return", action: "submit" },
22880
+ { name: "linefeed", action: "submit" },
22881
+ { name: "return", shift: true, action: "newline" },
22882
+ { name: "linefeed", shift: true, action: "newline" },
22883
+ { name: "return", meta: true, action: "submit" }
22884
+ ],
22885
+ onSubmit: () => {
22886
+ const value = inputField.editBuffer.getText();
22887
+ handleInput(value);
22363
22888
  }
22364
22889
  });
22365
- inputRow.add(inputField);
22890
+ inputContainer.add(inputField);
22891
+ inputHintText = new TextRenderable(renderer, {
22892
+ id: "input-hint",
22893
+ content: getInputHintContent()
22894
+ });
22895
+ inputContainer.add(inputHintText);
22366
22896
  helpBarText = new TextRenderable(renderer, {
22367
22897
  id: "help-bar-text",
22368
22898
  content: getHelpBarContent(),
22369
22899
  marginTop: 1
22370
22900
  });
22371
22901
  mainContainer.add(helpBarText);
22372
- inputField.on(InputRenderableEvents.ENTER, handleInput);
22373
22902
  renderer.keyInput.on("keypress", handleKeypress);
22374
22903
  inputField.focus();
22375
22904
  }
22376
22905
  function getStatusBarContent() {
22377
22906
  const theme = getTheme();
22378
22907
  const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
22379
- const safeModeIndicator = dryRunMode ? fg(theme.colors.warning)("[DRY RUN]") : fg(theme.colors.success)("Safe");
22908
+ const safeModeIndicator = dryRunMode ? fg(theme.colors.warning)("[DRY RUN]") : "";
22909
+ const safetyLevelColor = config.safetyLevel === "strict" ? theme.colors.warning : config.safetyLevel === "relaxed" ? theme.colors.error : theme.colors.success;
22910
+ const safetyIndicator = fg(safetyLevelColor)(`[${config.safetyLevel}]`);
22380
22911
  const repoContextIndicator = config.repoContext ? fg(theme.colors.info)("[Repo]") : "";
22381
- return t`${fg(theme.colors.textMuted)("Provider:")} ${fg(theme.colors.text)(providerName)} ${fg(theme.colors.textMuted)("Model:")} ${fg(theme.colors.text)(currentModel.name)} ${safeModeIndicator}${repoContextIndicator ? " " : ""}${repoContextIndicator}`;
22912
+ return t`${fg(theme.colors.textMuted)("Provider:")} ${fg(theme.colors.text)(providerName)} ${fg(theme.colors.textMuted)("Model:")} ${fg(theme.colors.text)(currentModel.name)} ${safetyIndicator}${safeModeIndicator ? " " : ""}${safeModeIndicator}${repoContextIndicator ? " " : ""}${repoContextIndicator}`;
22382
22913
  }
22383
22914
  function getHelpBarContent() {
22384
22915
  const theme = getTheme();
22385
22916
  if (awaitingConfirmation) {
22386
22917
  return t`${fg(theme.colors.warning)(">>> Press Enter to execute command <<<")} ${fg(theme.colors.textMuted)("|")} ${fg(theme.colors.error)("Esc")}${fg(theme.colors.textMuted)(" Cancel")} ${fg(theme.colors.primary)("e")}${fg(theme.colors.textMuted)(" Edit")} ${fg(theme.colors.primary)("c")}${fg(theme.colors.textMuted)(" Copy")}`;
22387
22918
  }
22388
- return t`${fg(theme.colors.textMuted)("Ctrl+X")} ${fg(theme.colors.primary)("P")}${fg(theme.colors.textMuted)(" Palette")} ${fg(theme.colors.primary)("M")}${fg(theme.colors.textMuted)(" Model")} ${fg(theme.colors.primary)("T")}${fg(theme.colors.textMuted)(" Theme")} ${fg(theme.colors.primary)("D")}${fg(theme.colors.textMuted)(" Dry-run")} ${fg(theme.colors.primary)("?")}${fg(theme.colors.textMuted)(" Help")}`;
22919
+ return t`${fg(theme.colors.primary)("Ctrl+X P")}${fg(theme.colors.textMuted)(" Commands")} ${fg(theme.colors.primary)("Ctrl+Y")}${fg(theme.colors.textMuted)(" Safety")} ${fg(theme.colors.primary)("Ctrl+Z")}${fg(theme.colors.textMuted)(" Exit")}`;
22920
+ }
22921
+ function getInputHintContent() {
22922
+ const theme = getTheme();
22923
+ return t`${fg(theme.colors.textMuted)("Enter")}${fg(theme.colors.border)(" send")} ${fg(theme.colors.textMuted)("Shift+Enter")}${fg(theme.colors.border)(" newline")} ${fg(theme.colors.textMuted)("!")}${fg(theme.colors.border)(" shell")}`;
22389
22924
  }
22390
22925
  function getWelcomeMessage() {
22391
22926
  const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
@@ -22647,17 +23182,23 @@ function refreshThemeColors() {
22647
23182
  };
22648
23183
  }
22649
23184
  if (inputField) {
22650
- inputField.focusedBackgroundColor = theme.colors.backgroundPanel;
23185
+ inputField.focusedBackgroundColor = "transparent";
22651
23186
  inputField.textColor = theme.colors.text;
22652
- inputField.placeholderColor = theme.colors.textMuted;
22653
- inputField.cursorColor = theme.colors.primary;
23187
+ inputField.placeholder = t`${fg(theme.colors.textMuted)("Describe what you want to do...")}`;
23188
+ }
23189
+ if (inputContainer) {
23190
+ inputContainer.borderColor = theme.colors.primary;
23191
+ inputContainer.backgroundColor = theme.colors.backgroundPanel;
23192
+ }
23193
+ if (inputHintText) {
23194
+ inputHintText.content = getInputHintContent();
22654
23195
  }
22655
23196
  }
22656
23197
  async function handleInput(value) {
22657
23198
  const input = value.trim();
22658
23199
  if (!input)
22659
23200
  return;
22660
- inputField.value = "";
23201
+ inputField.setText("");
22661
23202
  if (input.startsWith("!")) {
22662
23203
  await handleSpecialCommand(input);
22663
23204
  return;
@@ -22850,16 +23391,22 @@ function clearChat() {
22850
23391
  addSystemMessage(getWelcomeMessage());
22851
23392
  }
22852
23393
  function showHelp() {
22853
- const helpText = `Keyboard Shortcuts (Ctrl+X then...):
23394
+ const helpText = `Direct Shortcuts:
23395
+ Ctrl+Y Cycle safety level (strict/moderate/relaxed)
23396
+ Ctrl+Z Exit magic-shell
23397
+ Ctrl+C Cancel / Close popup
23398
+
23399
+ Chord Shortcuts (Ctrl+X then...):
22854
23400
  P Command palette M Change model
22855
23401
  S Switch provider D Toggle dry-run
22856
23402
  T Change theme R Toggle repo context
22857
23403
  H Show history L Clear chat
22858
23404
  C Show config ? This help
22859
- Q Exit
22860
23405
 
22861
- Other:
22862
- Ctrl+C Exit / Cancel Esc Close palette
23406
+ Safety Levels:
23407
+ - strict: Confirm ALL potentially dangerous commands
23408
+ - moderate: Confirm high/critical severity commands (default)
23409
+ - relaxed: Only confirm critical commands
22863
23410
 
22864
23411
  Tips:
22865
23412
  - Type naturally: "list all files" -> ls -la
@@ -23148,6 +23695,26 @@ function getCommandPaletteOptions() {
23148
23695
  addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
23149
23696
  }
23150
23697
  },
23698
+ {
23699
+ name: "Cycle Safety Level",
23700
+ description: `Current: ${config.safetyLevel}`,
23701
+ key: "y",
23702
+ chord: "y",
23703
+ action: () => {
23704
+ const levels = ["moderate", "strict", "relaxed"];
23705
+ const currentIndex = levels.indexOf(config.safetyLevel);
23706
+ const nextIndex = (currentIndex + 1) % levels.length;
23707
+ config.safetyLevel = levels[nextIndex];
23708
+ saveConfig(config);
23709
+ statusBarText.content = getStatusBarContent();
23710
+ const descriptions = {
23711
+ strict: "confirms ALL potentially dangerous commands",
23712
+ moderate: "confirms high/critical severity commands",
23713
+ relaxed: "only confirms critical commands"
23714
+ };
23715
+ addSystemMessage(`Safety level: ${config.safetyLevel} (${descriptions[config.safetyLevel]})`);
23716
+ }
23717
+ },
23151
23718
  {
23152
23719
  name: "Toggle Project Context",
23153
23720
  description: config.repoContext ? "Currently ON (sends script names to AI)" : "Currently OFF",
@@ -23270,6 +23837,25 @@ function closeCommandPalette() {
23270
23837
  }
23271
23838
  function handleKeypress(key) {
23272
23839
  const commands = getCommandPaletteOptions();
23840
+ if (key.ctrl && key.name === "y") {
23841
+ const levels = ["moderate", "strict", "relaxed"];
23842
+ const currentIndex = levels.indexOf(config.safetyLevel);
23843
+ const nextIndex = (currentIndex + 1) % levels.length;
23844
+ config.safetyLevel = levels[nextIndex];
23845
+ saveConfig(config);
23846
+ statusBarText.content = getStatusBarContent();
23847
+ const descriptions = {
23848
+ strict: "confirms ALL potentially dangerous commands",
23849
+ moderate: "confirms high/critical severity commands",
23850
+ relaxed: "only confirms critical commands"
23851
+ };
23852
+ addSystemMessage(`Safety level: ${config.safetyLevel} (${descriptions[config.safetyLevel]})`);
23853
+ return;
23854
+ }
23855
+ if (key.ctrl && key.name === "z") {
23856
+ renderer.destroy();
23857
+ process.exit(0);
23858
+ }
23273
23859
  if (key.ctrl && key.name === "x") {
23274
23860
  chordMode = "ctrl-x";
23275
23861
  return;
@@ -23308,12 +23894,24 @@ function handleKeypress(key) {
23308
23894
  inputField.focus();
23309
23895
  return;
23310
23896
  }
23897
+ if (themeSelector) {
23898
+ renderer.root.remove("theme-selector-container");
23899
+ themeSelector = null;
23900
+ inputField.focus();
23901
+ return;
23902
+ }
23903
+ if (awaitingConfirmation && pendingMessageId) {
23904
+ clearCommandState();
23905
+ addSystemMessage("Command cancelled.");
23906
+ inputField.focus();
23907
+ return;
23908
+ }
23311
23909
  if (providerSelector) {
23312
23910
  renderer.destroy();
23313
23911
  process.exit(0);
23314
23912
  }
23315
- renderer.destroy();
23316
- process.exit(0);
23913
+ addSystemMessage("Press Ctrl+Z to exit.");
23914
+ return;
23317
23915
  }
23318
23916
  if (key.name === "escape") {
23319
23917
  chordMode = "none";
@@ -23345,7 +23943,7 @@ function handleKeypress(key) {
23345
23943
  if (key.name === "e" && awaitingConfirmation && pendingMessageId) {
23346
23944
  const msg = chatMessages.find((m) => m.id === pendingMessageId);
23347
23945
  if (msg && msg.command) {
23348
- inputField.value = msg.command;
23946
+ inputField.setText(msg.command);
23349
23947
  clearCommandState();
23350
23948
  inputField.focus();
23351
23949
  }