@austinthesing/magic-shell 0.2.7 → 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.
Files changed (3) hide show
  1. package/dist/cli.js +567 -28
  2. package/dist/tui.js +567 -28
  3. package/package.json +1 -1
package/dist/cli.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,43 +22852,53 @@ 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
  }
@@ -22389,6 +22918,10 @@ function getHelpBarContent() {
22389
22918
  }
22390
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")}`;
22391
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")}`;
22924
+ }
22392
22925
  function getWelcomeMessage() {
22393
22926
  const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
22394
22927
  const freeNote = config.provider === "opencode-zen" ? `
@@ -22649,17 +23182,23 @@ function refreshThemeColors() {
22649
23182
  };
22650
23183
  }
22651
23184
  if (inputField) {
22652
- inputField.focusedBackgroundColor = theme.colors.backgroundPanel;
23185
+ inputField.focusedBackgroundColor = "transparent";
22653
23186
  inputField.textColor = theme.colors.text;
22654
- inputField.placeholderColor = theme.colors.textMuted;
22655
- 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();
22656
23195
  }
22657
23196
  }
22658
23197
  async function handleInput(value) {
22659
23198
  const input = value.trim();
22660
23199
  if (!input)
22661
23200
  return;
22662
- inputField.value = "";
23201
+ inputField.setText("");
22663
23202
  if (input.startsWith("!")) {
22664
23203
  await handleSpecialCommand(input);
22665
23204
  return;
@@ -23404,7 +23943,7 @@ function handleKeypress(key) {
23404
23943
  if (key.name === "e" && awaitingConfirmation && pendingMessageId) {
23405
23944
  const msg = chatMessages.find((m) => m.id === pendingMessageId);
23406
23945
  if (msg && msg.command) {
23407
- inputField.value = msg.command;
23946
+ inputField.setText(msg.command);
23408
23947
  clearCommandState();
23409
23948
  inputField.focus();
23410
23949
  }
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,43 +22852,53 @@ 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
  }
@@ -22389,6 +22918,10 @@ function getHelpBarContent() {
22389
22918
  }
22390
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")}`;
22391
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")}`;
22924
+ }
22392
22925
  function getWelcomeMessage() {
22393
22926
  const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
22394
22927
  const freeNote = config.provider === "opencode-zen" ? `
@@ -22649,17 +23182,23 @@ function refreshThemeColors() {
22649
23182
  };
22650
23183
  }
22651
23184
  if (inputField) {
22652
- inputField.focusedBackgroundColor = theme.colors.backgroundPanel;
23185
+ inputField.focusedBackgroundColor = "transparent";
22653
23186
  inputField.textColor = theme.colors.text;
22654
- inputField.placeholderColor = theme.colors.textMuted;
22655
- 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();
22656
23195
  }
22657
23196
  }
22658
23197
  async function handleInput(value) {
22659
23198
  const input = value.trim();
22660
23199
  if (!input)
22661
23200
  return;
22662
- inputField.value = "";
23201
+ inputField.setText("");
22663
23202
  if (input.startsWith("!")) {
22664
23203
  await handleSpecialCommand(input);
22665
23204
  return;
@@ -23404,7 +23943,7 @@ function handleKeypress(key) {
23404
23943
  if (key.name === "e" && awaitingConfirmation && pendingMessageId) {
23405
23944
  const msg = chatMessages.find((m) => m.id === pendingMessageId);
23406
23945
  if (msg && msg.command) {
23407
- inputField.value = msg.command;
23946
+ inputField.setText(msg.command);
23408
23947
  clearCommandState();
23409
23948
  inputField.focus();
23410
23949
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@austinthesing/magic-shell",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "Natural language to terminal commands with safety features. Supports OpenCode Zen (with free models) and OpenRouter.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",