@austinthesing/magic-shell 0.2.26 → 0.2.28

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/README.md CHANGED
@@ -145,7 +145,7 @@ Launch with `mshell` for a full interactive experience.
145
145
 
146
146
  ### Keyboard Shortcuts
147
147
 
148
- Magic Shell follows OpenCode-style TUI shortcuts where they map cleanly:
148
+ Magic Shell follows OpenCode-style TUI shortcuts where they fit cleanly:
149
149
 
150
150
  | Shortcut | Action |
151
151
  | ---------- | ---------------------- |
@@ -154,23 +154,28 @@ Magic Shell follows OpenCode-style TUI shortcuts where they map cleanly:
154
154
  | `Ctrl+X T` | Change theme |
155
155
  | `Ctrl+X Q` | Exit |
156
156
  | `Ctrl+T` | Cycle thinking level |
157
- | `Ctrl+C` | Exit / Cancel active UI |
157
+ | `Ctrl+C` | Exit, or cancel/close the active dialog first |
158
158
  | `Ctrl+D` | Exit |
159
- | `Esc` | Close dialogs |
159
+ | `Esc` | Close dialogs or cancel pending actions |
160
160
 
161
- ### Direct Commands in TUI
161
+ ### Slash Commands in TUI
162
162
 
163
- You can also type commands directly in the TUI:
163
+ Typing `/` opens a slash command menu under the prompt. Type to filter, use arrow keys to move, `Tab` to complete, and `Enter` to run the selected command.
164
+
165
+ You can also submit slash commands directly:
164
166
 
165
167
  - `!help` or `/help` - Show help
166
- - `!model` or `/model` - Change model
167
- - `!provider` or `/provider` - Switch provider
168
+ - `!model` or `/models` - Change model
169
+ - `!provider` or `/providers` - Switch provider
170
+ - `!theme` or `/themes` - Change theme
168
171
  - `!dry` or `/dry` - Toggle dry-run mode
172
+ - `!thinking` or `/thinking` - Cycle thinking level
169
173
  - `!config` or `/config` - Show current configuration
170
174
  - `!history` or `/history` - Show command history
171
175
  - `!clear` or `/clear` - Clear output
176
+ - `!exit` or `/exit` - Exit the TUI
172
177
 
173
- > **Note:** Both `!` and `/` prefixes work for all commands. Use whichever feels more natural!
178
+ Both `!` and `/` prefixes work for these commands.
174
179
 
175
180
  ## AI Providers
176
181
 
package/dist/cli.js CHANGED
@@ -76817,6 +76817,11 @@ var inputHintText;
76817
76817
  var helpBarText;
76818
76818
  var modelSelector = null;
76819
76819
  var providerSelector = null;
76820
+ var slashCommandContainer = null;
76821
+ var slashCommandSelector = null;
76822
+ var slashCommandMatches = [];
76823
+ var slashCommandSelectedIndex = 0;
76824
+ var slashCommandSyncQueued = false;
76820
76825
  var pendingMessageId = null;
76821
76826
  var awaitingConfirmation = false;
76822
76827
  function generateMessageId() {
@@ -77119,6 +77124,7 @@ function createMainUI() {
77119
77124
  });
77120
77125
  mainContainer.add(helpBarText);
77121
77126
  renderer.keyInput.on("keypress", handleKeypress);
77127
+ renderer.keyInput.on("keypress", queueSlashCommandSync);
77122
77128
  inputField.focus();
77123
77129
  }
77124
77130
  function getStatusBarContent() {
@@ -77428,13 +77434,28 @@ function refreshThemeColors() {
77428
77434
  if (inputHintText) {
77429
77435
  inputHintText.content = getInputHintContent();
77430
77436
  }
77437
+ if (slashCommandContainer) {
77438
+ slashCommandContainer.borderColor = theme.colors.border;
77439
+ slashCommandContainer.backgroundColor = theme.colors.backgroundPanel;
77440
+ }
77441
+ if (slashCommandSelector) {
77442
+ slashCommandSelector.selectedBackgroundColor = theme.colors.backgroundElement;
77443
+ slashCommandSelector.textColor = theme.colors.text;
77444
+ slashCommandSelector.selectedTextColor = theme.colors.primary;
77445
+ slashCommandSelector.descriptionColor = theme.colors.textMuted;
77446
+ slashCommandSelector.selectedDescriptionColor = theme.colors.textMuted;
77447
+ }
77431
77448
  }
77432
77449
  async function handleInput(value) {
77433
77450
  const input = value.trim();
77434
77451
  if (!input)
77435
77452
  return;
77436
77453
  inputField.setText("");
77437
- if (input.startsWith("!") || input.startsWith("/")) {
77454
+ closeSlashCommandMenu();
77455
+ if (input.startsWith("/") && await tryHandleSlashCommand(input)) {
77456
+ return;
77457
+ }
77458
+ if (input.startsWith("!")) {
77438
77459
  await handleSpecialCommand(input);
77439
77460
  return;
77440
77461
  }
@@ -77445,6 +77466,18 @@ async function handleInput(value) {
77445
77466
  }
77446
77467
  await translateAndProcess(input);
77447
77468
  }
77469
+ async function tryHandleSlashCommand(input) {
77470
+ if (!input.startsWith("/"))
77471
+ return false;
77472
+ const slashBody = input.slice(1).trim();
77473
+ if (!slashBody.includes(" ") && slashCommandMatches.length > 0) {
77474
+ const selected = slashCommandMatches[Math.min(slashCommandSelectedIndex, slashCommandMatches.length - 1)];
77475
+ await handleSpecialCommand(`/${selected.slash}`);
77476
+ return true;
77477
+ }
77478
+ await handleSpecialCommand(input);
77479
+ return true;
77480
+ }
77448
77481
  function isDirectCommand(input) {
77449
77482
  const directCommands = ["ls", "pwd", "cd", "cat", "echo", "mkdir", "touch", "rm", "cp", "mv", "git", "npm", "bun", "node", "python", "pip", "brew", "apt", "docker", "kubectl"];
77450
77483
  const firstWord = input.split(/\s+/)[0].toLowerCase();
@@ -77620,6 +77653,7 @@ async function handleSpecialCommand(input) {
77620
77653
  }
77621
77654
  }
77622
77655
  function clearChat() {
77656
+ closeSlashCommandMenu();
77623
77657
  for (const msg of chatMessages) {
77624
77658
  chatScrollBox.remove(`msg-${msg.id}`);
77625
77659
  }
@@ -78031,6 +78065,166 @@ function getCommandPaletteOptions() {
78031
78065
  }
78032
78066
  ];
78033
78067
  }
78068
+ function getSlashCommands() {
78069
+ return [
78070
+ {
78071
+ slash: "help",
78072
+ name: "Help",
78073
+ description: "Show commands and shortcuts",
78074
+ action: () => showHelp()
78075
+ },
78076
+ {
78077
+ slash: "models",
78078
+ name: "Models",
78079
+ description: `Change model · ${currentModel.name}`,
78080
+ action: () => showModelSelector()
78081
+ },
78082
+ {
78083
+ slash: "providers",
78084
+ name: "Providers",
78085
+ description: `Switch provider · ${getProviderDisplayName(config2.provider)}`,
78086
+ action: () => switchProvider()
78087
+ },
78088
+ {
78089
+ slash: "themes",
78090
+ name: "Themes",
78091
+ description: `Change theme · ${getTheme().name}`,
78092
+ action: () => showThemeSelector()
78093
+ },
78094
+ {
78095
+ slash: "dry",
78096
+ name: "Dry Run",
78097
+ description: dryRunMode ? "Turn dry-run off" : "Turn dry-run on",
78098
+ action: () => {
78099
+ dryRunMode = !dryRunMode;
78100
+ statusBarText.content = getStatusBarContent();
78101
+ addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
78102
+ }
78103
+ },
78104
+ {
78105
+ slash: "thinking",
78106
+ name: "Thinking",
78107
+ description: `Cycle thinking level · ${config2.thinkingLevel}`,
78108
+ action: () => cycleThinkingLevel()
78109
+ },
78110
+ {
78111
+ slash: "config",
78112
+ name: "Config",
78113
+ description: "Show current configuration",
78114
+ action: () => showConfig()
78115
+ },
78116
+ {
78117
+ slash: "history",
78118
+ name: "History",
78119
+ description: `${history.length} commands`,
78120
+ action: () => showHistory()
78121
+ },
78122
+ {
78123
+ slash: "clear",
78124
+ name: "Clear",
78125
+ description: "Clear the chat history",
78126
+ action: () => clearChat()
78127
+ },
78128
+ {
78129
+ slash: "exit",
78130
+ name: "Exit",
78131
+ description: "Close magic-shell",
78132
+ action: () => {
78133
+ renderer.destroy();
78134
+ process.exit(0);
78135
+ }
78136
+ }
78137
+ ];
78138
+ }
78139
+ function getSlashCommandMatches(inputText) {
78140
+ if (!inputText.startsWith("/"))
78141
+ return [];
78142
+ const slashBody = inputText.slice(1);
78143
+ if (slashBody.includes(" "))
78144
+ return [];
78145
+ const query = slashBody.trim().toLowerCase();
78146
+ return getSlashCommands().filter((cmd) => {
78147
+ if (!query)
78148
+ return true;
78149
+ return `${cmd.slash} ${cmd.name} ${cmd.description}`.toLowerCase().includes(query);
78150
+ });
78151
+ }
78152
+ function ensureSlashCommandMenu() {
78153
+ if (slashCommandContainer && slashCommandSelector)
78154
+ return;
78155
+ slashCommandContainer = new BoxRenderable(renderer, {
78156
+ id: "slash-command-container",
78157
+ width: "100%",
78158
+ border: true,
78159
+ borderColor: getTheme().colors.border,
78160
+ borderStyle: "single",
78161
+ backgroundColor: getTheme().colors.backgroundPanel,
78162
+ paddingLeft: 1,
78163
+ paddingRight: 1,
78164
+ paddingTop: 0,
78165
+ paddingBottom: 0,
78166
+ marginTop: 1
78167
+ });
78168
+ inputContainer.add(slashCommandContainer);
78169
+ slashCommandSelector = new SelectRenderable(renderer, {
78170
+ id: "slash-command-select",
78171
+ width: "100%",
78172
+ height: 6,
78173
+ options: [],
78174
+ backgroundColor: "transparent",
78175
+ focusedBackgroundColor: "transparent",
78176
+ selectedBackgroundColor: getTheme().colors.backgroundElement,
78177
+ textColor: getTheme().colors.text,
78178
+ selectedTextColor: getTheme().colors.primary,
78179
+ descriptionColor: getTheme().colors.textMuted,
78180
+ selectedDescriptionColor: getTheme().colors.textMuted,
78181
+ showDescription: true,
78182
+ wrapSelection: true
78183
+ });
78184
+ slashCommandContainer.add(slashCommandSelector);
78185
+ }
78186
+ function updateSlashCommandMenuOptions() {
78187
+ if (!slashCommandSelector)
78188
+ return;
78189
+ slashCommandSelector.options = slashCommandMatches.map((cmd) => ({
78190
+ name: `/${cmd.slash}`,
78191
+ description: cmd.description,
78192
+ value: cmd
78193
+ }));
78194
+ slashCommandSelector.height = Math.min(Math.max(slashCommandMatches.length, 1), 6);
78195
+ slashCommandSelector.setSelectedIndex(slashCommandSelectedIndex);
78196
+ }
78197
+ function closeSlashCommandMenu() {
78198
+ if (slashCommandContainer) {
78199
+ inputContainer.remove("slash-command-container");
78200
+ slashCommandContainer = null;
78201
+ slashCommandSelector = null;
78202
+ }
78203
+ slashCommandMatches = [];
78204
+ slashCommandSelectedIndex = 0;
78205
+ }
78206
+ function syncSlashCommandMenu() {
78207
+ const inputText = inputField.editBuffer.getText();
78208
+ const matches = getSlashCommandMatches(inputText);
78209
+ if (matches.length === 0) {
78210
+ closeSlashCommandMenu();
78211
+ return;
78212
+ }
78213
+ ensureSlashCommandMenu();
78214
+ slashCommandMatches = matches;
78215
+ slashCommandSelectedIndex = Math.min(slashCommandSelectedIndex, slashCommandMatches.length - 1);
78216
+ updateSlashCommandMenuOptions();
78217
+ }
78218
+ function queueSlashCommandSync() {
78219
+ if (slashCommandSyncQueued)
78220
+ return;
78221
+ slashCommandSyncQueued = true;
78222
+ setTimeout(() => {
78223
+ slashCommandSyncQueued = false;
78224
+ if (inputField)
78225
+ syncSlashCommandMenu();
78226
+ }, 0);
78227
+ }
78034
78228
  function showCommandPalette() {
78035
78229
  if (commandPalette) {
78036
78230
  return;
@@ -78118,6 +78312,24 @@ function closeCommandPalette() {
78118
78312
  }
78119
78313
  function handleKeypress(key) {
78120
78314
  const commands = getCommandPaletteOptions();
78315
+ if (slashCommandMatches.length > 0) {
78316
+ if (key.name === "up") {
78317
+ slashCommandSelectedIndex = slashCommandSelectedIndex === 0 ? slashCommandMatches.length - 1 : slashCommandSelectedIndex - 1;
78318
+ updateSlashCommandMenuOptions();
78319
+ return;
78320
+ }
78321
+ if (key.name === "down") {
78322
+ slashCommandSelectedIndex = (slashCommandSelectedIndex + 1) % slashCommandMatches.length;
78323
+ updateSlashCommandMenuOptions();
78324
+ return;
78325
+ }
78326
+ if (key.name === "tab") {
78327
+ inputField.setText(`/${slashCommandMatches[slashCommandSelectedIndex].slash}`);
78328
+ syncSlashCommandMenu();
78329
+ inputField.focus();
78330
+ return;
78331
+ }
78332
+ }
78121
78333
  if (key.ctrl && key.name === "p") {
78122
78334
  showCommandPalette();
78123
78335
  return;
@@ -78162,6 +78374,10 @@ function handleKeypress(key) {
78162
78374
  closeCommandPalette();
78163
78375
  return;
78164
78376
  }
78377
+ if (slashCommandMatches.length > 0) {
78378
+ closeSlashCommandMenu();
78379
+ return;
78380
+ }
78165
78381
  if (modelSelector) {
78166
78382
  renderer.root.remove("model-selector-container");
78167
78383
  modelSelector = null;
@@ -78189,6 +78405,10 @@ function handleKeypress(key) {
78189
78405
  closeCommandPalette();
78190
78406
  return;
78191
78407
  }
78408
+ if (slashCommandMatches.length > 0) {
78409
+ closeSlashCommandMenu();
78410
+ return;
78411
+ }
78192
78412
  if (modelSelector) {
78193
78413
  renderer.root.remove("model-selector-container");
78194
78414
  modelSelector = null;
package/dist/tui.js CHANGED
@@ -76817,6 +76817,11 @@ var inputHintText;
76817
76817
  var helpBarText;
76818
76818
  var modelSelector = null;
76819
76819
  var providerSelector = null;
76820
+ var slashCommandContainer = null;
76821
+ var slashCommandSelector = null;
76822
+ var slashCommandMatches = [];
76823
+ var slashCommandSelectedIndex = 0;
76824
+ var slashCommandSyncQueued = false;
76820
76825
  var pendingMessageId = null;
76821
76826
  var awaitingConfirmation = false;
76822
76827
  function generateMessageId() {
@@ -77119,6 +77124,7 @@ function createMainUI() {
77119
77124
  });
77120
77125
  mainContainer.add(helpBarText);
77121
77126
  renderer.keyInput.on("keypress", handleKeypress);
77127
+ renderer.keyInput.on("keypress", queueSlashCommandSync);
77122
77128
  inputField.focus();
77123
77129
  }
77124
77130
  function getStatusBarContent() {
@@ -77428,13 +77434,28 @@ function refreshThemeColors() {
77428
77434
  if (inputHintText) {
77429
77435
  inputHintText.content = getInputHintContent();
77430
77436
  }
77437
+ if (slashCommandContainer) {
77438
+ slashCommandContainer.borderColor = theme.colors.border;
77439
+ slashCommandContainer.backgroundColor = theme.colors.backgroundPanel;
77440
+ }
77441
+ if (slashCommandSelector) {
77442
+ slashCommandSelector.selectedBackgroundColor = theme.colors.backgroundElement;
77443
+ slashCommandSelector.textColor = theme.colors.text;
77444
+ slashCommandSelector.selectedTextColor = theme.colors.primary;
77445
+ slashCommandSelector.descriptionColor = theme.colors.textMuted;
77446
+ slashCommandSelector.selectedDescriptionColor = theme.colors.textMuted;
77447
+ }
77431
77448
  }
77432
77449
  async function handleInput(value) {
77433
77450
  const input = value.trim();
77434
77451
  if (!input)
77435
77452
  return;
77436
77453
  inputField.setText("");
77437
- if (input.startsWith("!") || input.startsWith("/")) {
77454
+ closeSlashCommandMenu();
77455
+ if (input.startsWith("/") && await tryHandleSlashCommand(input)) {
77456
+ return;
77457
+ }
77458
+ if (input.startsWith("!")) {
77438
77459
  await handleSpecialCommand(input);
77439
77460
  return;
77440
77461
  }
@@ -77445,6 +77466,18 @@ async function handleInput(value) {
77445
77466
  }
77446
77467
  await translateAndProcess(input);
77447
77468
  }
77469
+ async function tryHandleSlashCommand(input) {
77470
+ if (!input.startsWith("/"))
77471
+ return false;
77472
+ const slashBody = input.slice(1).trim();
77473
+ if (!slashBody.includes(" ") && slashCommandMatches.length > 0) {
77474
+ const selected = slashCommandMatches[Math.min(slashCommandSelectedIndex, slashCommandMatches.length - 1)];
77475
+ await handleSpecialCommand(`/${selected.slash}`);
77476
+ return true;
77477
+ }
77478
+ await handleSpecialCommand(input);
77479
+ return true;
77480
+ }
77448
77481
  function isDirectCommand(input) {
77449
77482
  const directCommands = ["ls", "pwd", "cd", "cat", "echo", "mkdir", "touch", "rm", "cp", "mv", "git", "npm", "bun", "node", "python", "pip", "brew", "apt", "docker", "kubectl"];
77450
77483
  const firstWord = input.split(/\s+/)[0].toLowerCase();
@@ -77620,6 +77653,7 @@ async function handleSpecialCommand(input) {
77620
77653
  }
77621
77654
  }
77622
77655
  function clearChat() {
77656
+ closeSlashCommandMenu();
77623
77657
  for (const msg of chatMessages) {
77624
77658
  chatScrollBox.remove(`msg-${msg.id}`);
77625
77659
  }
@@ -78031,6 +78065,166 @@ function getCommandPaletteOptions() {
78031
78065
  }
78032
78066
  ];
78033
78067
  }
78068
+ function getSlashCommands() {
78069
+ return [
78070
+ {
78071
+ slash: "help",
78072
+ name: "Help",
78073
+ description: "Show commands and shortcuts",
78074
+ action: () => showHelp()
78075
+ },
78076
+ {
78077
+ slash: "models",
78078
+ name: "Models",
78079
+ description: `Change model · ${currentModel.name}`,
78080
+ action: () => showModelSelector()
78081
+ },
78082
+ {
78083
+ slash: "providers",
78084
+ name: "Providers",
78085
+ description: `Switch provider · ${getProviderDisplayName(config2.provider)}`,
78086
+ action: () => switchProvider()
78087
+ },
78088
+ {
78089
+ slash: "themes",
78090
+ name: "Themes",
78091
+ description: `Change theme · ${getTheme().name}`,
78092
+ action: () => showThemeSelector()
78093
+ },
78094
+ {
78095
+ slash: "dry",
78096
+ name: "Dry Run",
78097
+ description: dryRunMode ? "Turn dry-run off" : "Turn dry-run on",
78098
+ action: () => {
78099
+ dryRunMode = !dryRunMode;
78100
+ statusBarText.content = getStatusBarContent();
78101
+ addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
78102
+ }
78103
+ },
78104
+ {
78105
+ slash: "thinking",
78106
+ name: "Thinking",
78107
+ description: `Cycle thinking level · ${config2.thinkingLevel}`,
78108
+ action: () => cycleThinkingLevel()
78109
+ },
78110
+ {
78111
+ slash: "config",
78112
+ name: "Config",
78113
+ description: "Show current configuration",
78114
+ action: () => showConfig()
78115
+ },
78116
+ {
78117
+ slash: "history",
78118
+ name: "History",
78119
+ description: `${history.length} commands`,
78120
+ action: () => showHistory()
78121
+ },
78122
+ {
78123
+ slash: "clear",
78124
+ name: "Clear",
78125
+ description: "Clear the chat history",
78126
+ action: () => clearChat()
78127
+ },
78128
+ {
78129
+ slash: "exit",
78130
+ name: "Exit",
78131
+ description: "Close magic-shell",
78132
+ action: () => {
78133
+ renderer.destroy();
78134
+ process.exit(0);
78135
+ }
78136
+ }
78137
+ ];
78138
+ }
78139
+ function getSlashCommandMatches(inputText) {
78140
+ if (!inputText.startsWith("/"))
78141
+ return [];
78142
+ const slashBody = inputText.slice(1);
78143
+ if (slashBody.includes(" "))
78144
+ return [];
78145
+ const query = slashBody.trim().toLowerCase();
78146
+ return getSlashCommands().filter((cmd) => {
78147
+ if (!query)
78148
+ return true;
78149
+ return `${cmd.slash} ${cmd.name} ${cmd.description}`.toLowerCase().includes(query);
78150
+ });
78151
+ }
78152
+ function ensureSlashCommandMenu() {
78153
+ if (slashCommandContainer && slashCommandSelector)
78154
+ return;
78155
+ slashCommandContainer = new BoxRenderable(renderer, {
78156
+ id: "slash-command-container",
78157
+ width: "100%",
78158
+ border: true,
78159
+ borderColor: getTheme().colors.border,
78160
+ borderStyle: "single",
78161
+ backgroundColor: getTheme().colors.backgroundPanel,
78162
+ paddingLeft: 1,
78163
+ paddingRight: 1,
78164
+ paddingTop: 0,
78165
+ paddingBottom: 0,
78166
+ marginTop: 1
78167
+ });
78168
+ inputContainer.add(slashCommandContainer);
78169
+ slashCommandSelector = new SelectRenderable(renderer, {
78170
+ id: "slash-command-select",
78171
+ width: "100%",
78172
+ height: 6,
78173
+ options: [],
78174
+ backgroundColor: "transparent",
78175
+ focusedBackgroundColor: "transparent",
78176
+ selectedBackgroundColor: getTheme().colors.backgroundElement,
78177
+ textColor: getTheme().colors.text,
78178
+ selectedTextColor: getTheme().colors.primary,
78179
+ descriptionColor: getTheme().colors.textMuted,
78180
+ selectedDescriptionColor: getTheme().colors.textMuted,
78181
+ showDescription: true,
78182
+ wrapSelection: true
78183
+ });
78184
+ slashCommandContainer.add(slashCommandSelector);
78185
+ }
78186
+ function updateSlashCommandMenuOptions() {
78187
+ if (!slashCommandSelector)
78188
+ return;
78189
+ slashCommandSelector.options = slashCommandMatches.map((cmd) => ({
78190
+ name: `/${cmd.slash}`,
78191
+ description: cmd.description,
78192
+ value: cmd
78193
+ }));
78194
+ slashCommandSelector.height = Math.min(Math.max(slashCommandMatches.length, 1), 6);
78195
+ slashCommandSelector.setSelectedIndex(slashCommandSelectedIndex);
78196
+ }
78197
+ function closeSlashCommandMenu() {
78198
+ if (slashCommandContainer) {
78199
+ inputContainer.remove("slash-command-container");
78200
+ slashCommandContainer = null;
78201
+ slashCommandSelector = null;
78202
+ }
78203
+ slashCommandMatches = [];
78204
+ slashCommandSelectedIndex = 0;
78205
+ }
78206
+ function syncSlashCommandMenu() {
78207
+ const inputText = inputField.editBuffer.getText();
78208
+ const matches = getSlashCommandMatches(inputText);
78209
+ if (matches.length === 0) {
78210
+ closeSlashCommandMenu();
78211
+ return;
78212
+ }
78213
+ ensureSlashCommandMenu();
78214
+ slashCommandMatches = matches;
78215
+ slashCommandSelectedIndex = Math.min(slashCommandSelectedIndex, slashCommandMatches.length - 1);
78216
+ updateSlashCommandMenuOptions();
78217
+ }
78218
+ function queueSlashCommandSync() {
78219
+ if (slashCommandSyncQueued)
78220
+ return;
78221
+ slashCommandSyncQueued = true;
78222
+ setTimeout(() => {
78223
+ slashCommandSyncQueued = false;
78224
+ if (inputField)
78225
+ syncSlashCommandMenu();
78226
+ }, 0);
78227
+ }
78034
78228
  function showCommandPalette() {
78035
78229
  if (commandPalette) {
78036
78230
  return;
@@ -78118,6 +78312,24 @@ function closeCommandPalette() {
78118
78312
  }
78119
78313
  function handleKeypress(key) {
78120
78314
  const commands = getCommandPaletteOptions();
78315
+ if (slashCommandMatches.length > 0) {
78316
+ if (key.name === "up") {
78317
+ slashCommandSelectedIndex = slashCommandSelectedIndex === 0 ? slashCommandMatches.length - 1 : slashCommandSelectedIndex - 1;
78318
+ updateSlashCommandMenuOptions();
78319
+ return;
78320
+ }
78321
+ if (key.name === "down") {
78322
+ slashCommandSelectedIndex = (slashCommandSelectedIndex + 1) % slashCommandMatches.length;
78323
+ updateSlashCommandMenuOptions();
78324
+ return;
78325
+ }
78326
+ if (key.name === "tab") {
78327
+ inputField.setText(`/${slashCommandMatches[slashCommandSelectedIndex].slash}`);
78328
+ syncSlashCommandMenu();
78329
+ inputField.focus();
78330
+ return;
78331
+ }
78332
+ }
78121
78333
  if (key.ctrl && key.name === "p") {
78122
78334
  showCommandPalette();
78123
78335
  return;
@@ -78162,6 +78374,10 @@ function handleKeypress(key) {
78162
78374
  closeCommandPalette();
78163
78375
  return;
78164
78376
  }
78377
+ if (slashCommandMatches.length > 0) {
78378
+ closeSlashCommandMenu();
78379
+ return;
78380
+ }
78165
78381
  if (modelSelector) {
78166
78382
  renderer.root.remove("model-selector-container");
78167
78383
  modelSelector = null;
@@ -78189,6 +78405,10 @@ function handleKeypress(key) {
78189
78405
  closeCommandPalette();
78190
78406
  return;
78191
78407
  }
78408
+ if (slashCommandMatches.length > 0) {
78409
+ closeSlashCommandMenu();
78410
+ return;
78411
+ }
78192
78412
  if (modelSelector) {
78193
78413
  renderer.root.remove("model-selector-container");
78194
78414
  modelSelector = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@austinthesing/magic-shell",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
4
4
  "description": "Natural language to terminal commands with safety features. Supports OpenCode Zen, OpenRouter, AI gateways, Workers AI, and custom models.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",