Skip to content

Commit 6345d7d

Browse files
Quang Tran Minhclaude
andcommitted
feat: add Claude Code mode toggle button with custom SVG icon
- Add mode cycling button next to image button in input area - Send Shift+Tab key sequence to toggle Claude Code modes (auto-accept/plan/etc) - Beautiful custom SVG icon with circular arrows representing mode switching - Theme-adaptive icon colors for light/dark VSCode themes - Command palette integration: "Claude Code: Toggle Mode (Shift+Tab)" - Proper error handling for inactive terminals - Consistent styling with existing UI buttons Features: - UI button in input-bottom-actions for easy access - Custom SVG icon with professional design - Automatic theme color adaptation - Command palette access for keyboard users - Terminal validation before sending commands 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8de3c1b commit 6345d7d

6 files changed

Lines changed: 110 additions & 5 deletions

File tree

media/input.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
const imageInputElement = document.getElementById('imageInput');
1414
const imagePreviewContainer = document.getElementById('imagePreviewContainer');
1515
const problemPreviewContainer = document.getElementById('problemPreviewContainer');
16+
const modeToggleButtonElement = document.getElementById('modeToggleButton');
1617

1718
// RegExp for detecting @ mentions
1819
const mentionRegex = /@((?:\/|\w+:\/\/)[^\s]+?|[a-f0-9]{7,40}\b|problems\b|git-changes\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/;
@@ -1396,6 +1397,16 @@
13961397
});
13971398
}
13981399

1400+
// Event listener for mode toggle button
1401+
if (modeToggleButtonElement) {
1402+
modeToggleButtonElement.addEventListener('click', () => {
1403+
// Send mode toggle command to extension
1404+
vscode.postMessage({
1405+
command: 'toggleMode'
1406+
});
1407+
});
1408+
}
1409+
13991410
// Handle drag and drop for files/folders and images
14001411
if (messageInputElement) {
14011412
const inputWrapper = document.querySelector('.input-wrapper');

media/styles.css

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,8 @@ button svg {
12231223
}
12241224

12251225
.input-bottom-actions .context-button,
1226-
.input-bottom-actions .image-button {
1226+
.input-bottom-actions .image-button,
1227+
.input-bottom-actions .mode-toggle-button {
12271228
width: 28px;
12281229
height: 28px;
12291230
padding: 6px;
@@ -1241,7 +1242,8 @@ button svg {
12411242
}
12421243

12431244
.input-bottom-actions .context-button:hover,
1244-
.input-bottom-actions .image-button:hover {
1245+
.input-bottom-actions .image-button:hover,
1246+
.input-bottom-actions .mode-toggle-button:hover {
12451247
opacity: 1;
12461248
background-color: var(--vscode-list-hoverBackground);
12471249
}
@@ -1251,6 +1253,16 @@ button svg {
12511253
font-size: 16px;
12521254
}
12531255

1256+
.input-bottom-actions .mode-toggle-button {
1257+
font-size: 18px;
1258+
font-weight: normal;
1259+
}
1260+
1261+
.input-bottom-actions .mode-toggle-button img {
1262+
width: 16px;
1263+
height: 16px;
1264+
}
1265+
12541266
.input-bottom-actions .image-button svg,
12551267
.input-bottom-actions .image-button img {
12561268
width: 14px;
@@ -1259,11 +1271,14 @@ button svg {
12591271

12601272
/* Apply filter to make the image match the text color */
12611273
.vscode-dark .input-bottom-actions .image-button img,
1262-
.vscode-high-contrast .input-bottom-actions .image-button img {
1274+
.vscode-high-contrast .input-bottom-actions .image-button img,
1275+
.vscode-dark .input-bottom-actions .mode-toggle-button img,
1276+
.vscode-high-contrast .input-bottom-actions .mode-toggle-button img {
12631277
filter: brightness(0) saturate(100%) invert(100%);
12641278
}
12651279

1266-
.vscode-light .input-bottom-actions .image-button img {
1280+
.vscode-light .input-bottom-actions .image-button img,
1281+
.vscode-light .input-bottom-actions .mode-toggle-button img {
12671282
filter: brightness(0) saturate(100%);
12681283
}
12691284

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "claude-code-extension",
33
"displayName": "Claude Code Assistant for VSCode",
44
"description": "Unofficial integration of Anthropic's Claude Code AI assistant into VSCode",
5-
"version": "0.1.3",
5+
"version": "0.1.5",
66
"publisher": "codeflow-studio",
77
"icon": "resources/claude-icon.png",
88
"repository": {
@@ -53,6 +53,10 @@
5353
{
5454
"command": "claude-code-extension.addSelectionToInput",
5555
"title": "Add to Claude Code Input"
56+
},
57+
{
58+
"command": "claude-code-extension.toggleMode",
59+
"title": "Claude Code: Toggle Mode (Shift+Tab)"
5660
}
5761
],
5862
"viewsContainers": {

resources/mode-toggle.svg

Lines changed: 16 additions & 0 deletions
Loading

src/extension.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,20 @@ export async function activate(context: vscode.ExtensionContext) {
422422

423423
context.subscriptions.push(addSelectionToInputCommand);
424424

425+
// Register command to toggle Claude Code mode
426+
const toggleModeCommand = vscode.commands.registerCommand('claude-code-extension.toggleMode', async () => {
427+
// Check if provider is initialized
428+
if (!claudeTerminalInputProvider) {
429+
vscode.window.showErrorMessage('Claude terminal input provider not initialized');
430+
return;
431+
}
432+
433+
// Call the public toggle mode method
434+
await claudeTerminalInputProvider.toggleMode();
435+
});
436+
437+
context.subscriptions.push(toggleModeCommand);
438+
425439
// Register Claude Code Action Provider for Quick Fix menu
426440
const claudeCodeActionProvider = new ClaudeCodeActionProvider(claudeTerminalInputProvider);
427441
context.subscriptions.push(

src/ui/claudeTerminalInputProvider.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ export class ClaudeTerminalInputProvider implements vscode.WebviewViewProvider {
202202
case "launchClaudeHistory":
203203
this._handleLaunchClaudeHistory();
204204
return;
205+
206+
case "toggleMode":
207+
this._handleModeToggle();
208+
return;
205209
}
206210
},
207211
undefined,
@@ -1023,6 +1027,41 @@ export class ClaudeTerminalInputProvider implements vscode.WebviewViewProvider {
10231027
}
10241028
}
10251029

1030+
/**
1031+
* Public method to toggle Claude Code mode (exposed for command palette access)
1032+
*/
1033+
public async toggleMode(): Promise<void> {
1034+
await this._handleModeToggle();
1035+
}
1036+
1037+
/**
1038+
* Handles mode toggle by sending Shift+Tab to Claude Code terminal
1039+
*/
1040+
private async _handleModeToggle() {
1041+
try {
1042+
console.log('Toggling Claude Code mode (Shift+Tab)');
1043+
1044+
// Check if terminal is available
1045+
if (this._isTerminalClosed || !this._terminal) {
1046+
vscode.window.showWarningMessage('Claude Code terminal is not active. Please start Claude Code first.');
1047+
return;
1048+
}
1049+
1050+
// Show the terminal in the background (preserves focus)
1051+
this._terminal?.show(true);
1052+
1053+
// Send Shift+Tab key sequence to toggle mode
1054+
// \x1b[Z is the escape sequence for Shift+Tab
1055+
this._terminal?.sendText('\x1b[Z', false);
1056+
1057+
console.log('Mode toggle command sent to Claude Code terminal');
1058+
1059+
} catch (error) {
1060+
console.error('Error toggling Claude mode:', error);
1061+
vscode.window.showErrorMessage(`Failed to toggle Claude mode: ${error}`);
1062+
}
1063+
}
1064+
10261065
private _getHtmlForWebview(webview: vscode.Webview) {
10271066
// Generate nonce for script security
10281067
const nonce = getNonce();
@@ -1043,6 +1082,9 @@ export class ClaudeTerminalInputProvider implements vscode.WebviewViewProvider {
10431082
const imageIconPath = webview.asWebviewUri(
10441083
vscode.Uri.joinPath(this._extensionUri, "resources", "image-svgrepo-com.svg")
10451084
);
1085+
const modeToggleIconPath = webview.asWebviewUri(
1086+
vscode.Uri.joinPath(this._extensionUri, "resources", "mode-toggle.svg")
1087+
);
10461088
const codiconsCss = webview.asWebviewUri(
10471089
vscode.Uri.joinPath(this._extensionUri, "media", "codicon.css")
10481090
);
@@ -1156,6 +1198,9 @@ export class ClaudeTerminalInputProvider implements vscode.WebviewViewProvider {
11561198
<button id="imageButton" title="Attach image" class="image-button">
11571199
<img src="${imageIconPath}" width="20" height="20" alt="Attach Image" />
11581200
</button>
1201+
<button id="modeToggleButton" title="Toggle Claude Mode (Shift+Tab)" class="mode-toggle-button">
1202+
<img src="${modeToggleIconPath}" width="16" height="16" alt="Toggle Mode" />
1203+
</button>
11591204
</div>
11601205
<div id="contextMenuContainer" class="context-menu-container" style="display: none;"></div>
11611206
<div id="imagePreviewContainer" class="image-preview-container"></div>

0 commit comments

Comments
 (0)