Skip to content

Commit 4c57af4

Browse files
committed
MultiEdit UI
1 parent 64767e3 commit 4c57af4

3 files changed

Lines changed: 144 additions & 3 deletions

File tree

src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ class ClaudeChatProvider {
580580
const toolName = lastToolUse?.data?.toolName;
581581

582582
// Don't send tool result for Read and Edit tools unless there's an error
583-
if ((toolName === 'Read' || toolName === 'Edit' || toolName === 'TodoWrite') && !isError) {
583+
if ((toolName === 'Read' || toolName === 'Edit' || toolName === 'TodoWrite' || toolName === 'MultiEdit') && !isError) {
584584
// Still send to UI to hide loading state, but mark it as hidden
585585
this._sendAndSaveMessage({
586586
type: 'toolResult',

src/ui-styles.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,31 @@ const styles = `
534534
transform: translateY(1px);
535535
}
536536
537+
/* MultiEdit specific styles */
538+
.single-edit {
539+
margin-bottom: 12px;
540+
}
541+
542+
.edit-number {
543+
background: rgba(255, 255, 255, 0.05);
544+
border: 1px solid rgba(255, 255, 255, 0.15);
545+
color: var(--vscode-descriptionForeground);
546+
padding: 4px 8px;
547+
border-radius: 4px;
548+
font-size: 11px;
549+
font-weight: 600;
550+
margin-top: 6px;
551+
display: inline-block;
552+
text-transform: uppercase;
553+
letter-spacing: 0.5px;
554+
}
555+
556+
.diff-edit-separator {
557+
height: 1px;
558+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
559+
margin: 12px 0;
560+
}
561+
537562
/* File path display styles */
538563
.diff-file-path {
539564
padding: 8px 12px;

src/ui.ts

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -640,9 +640,11 @@ const html = `<!DOCTYPE html>
640640
contentDiv.innerHTML = todoHtml;
641641
} else {
642642
// Format raw input with expandable content for long values
643-
// Use diff format for Edit tool, regular format for others
643+
// Use diff format for Edit and MultiEdit tools, regular format for others
644644
if (data.toolName === 'Edit') {
645645
contentDiv.innerHTML = formatEditToolDiff(data.rawInput);
646+
} else if (data.toolName === 'MultiEdit') {
647+
contentDiv.innerHTML = formatMultiEditToolDiff(data.rawInput);
646648
} else {
647649
contentDiv.innerHTML = formatToolInputUI(data.rawInput);
648650
}
@@ -728,7 +730,7 @@ const html = `<!DOCTYPE html>
728730
729731
function addToolResultMessage(data) {
730732
// For Read and Edit tools with hidden flag, just hide loading state and show completion message
731-
if (data.hidden && (data.toolName === 'Read' || data.toolName === 'Edit' || data.toolName === 'TodoWrite') && !data.isError) {
733+
if (data.hidden && (data.toolName === 'Read' || data.toolName === 'Edit' || data.toolName === 'TodoWrite' || data.toolName === 'MultiEdit') && !data.isError) {
732734
return
733735
// Show completion message
734736
const toolName = data.toolName;
@@ -898,6 +900,120 @@ const html = `<!DOCTYPE html>
898900
return result;
899901
}
900902
903+
function formatMultiEditToolDiff(input) {
904+
if (!input || typeof input !== 'object') {
905+
return formatToolInputUI(input);
906+
}
907+
908+
// Check if this is a MultiEdit tool (has file_path and edits array)
909+
if (!input.file_path || !input.edits || !Array.isArray(input.edits)) {
910+
return formatToolInputUI(input);
911+
}
912+
913+
// Format file path with better display
914+
const formattedPath = formatFilePath(input.file_path);
915+
let result = '<div class="diff-file-path" onclick="openFileInEditor(\\\'' + escapeHtml(input.file_path) + '\\\')">' + formattedPath + '</div>\\n';
916+
917+
// Count total lines across all edits for truncation
918+
let totalLines = 0;
919+
for (const edit of input.edits) {
920+
if (edit.old_string && edit.new_string) {
921+
const oldLines = edit.old_string.split('\\n');
922+
const newLines = edit.new_string.split('\\n');
923+
totalLines += oldLines.length + newLines.length;
924+
}
925+
}
926+
927+
const maxLines = 6;
928+
const shouldTruncate = totalLines > maxLines;
929+
930+
result += '<div class="diff-container">';
931+
result += '<div class="diff-header">Changes (' + input.edits.length + ' edit' + (input.edits.length > 1 ? 's' : '') + '):</div>';
932+
933+
// Create a unique ID for this diff
934+
const diffId = 'multiedit_' + Math.random().toString(36).substr(2, 9);
935+
936+
let currentLineCount = 0;
937+
let visibleEdits = [];
938+
let hiddenEdits = [];
939+
940+
// Determine which edits to show/hide based on line count
941+
for (let i = 0; i < input.edits.length; i++) {
942+
const edit = input.edits[i];
943+
if (!edit.old_string || !edit.new_string) continue;
944+
945+
const oldLines = edit.old_string.split('\\n');
946+
const newLines = edit.new_string.split('\\n');
947+
const editLines = oldLines.length + newLines.length;
948+
949+
if (shouldTruncate && currentLineCount + editLines > maxLines && visibleEdits.length > 0) {
950+
hiddenEdits.push(edit);
951+
} else {
952+
visibleEdits.push(edit);
953+
currentLineCount += editLines;
954+
}
955+
}
956+
957+
// Show visible edits
958+
result += '<div id="' + diffId + '_visible">';
959+
for (let i = 0; i < visibleEdits.length; i++) {
960+
const edit = visibleEdits[i];
961+
if (i > 0) result += '<div class="diff-edit-separator"></div>';
962+
result += formatSingleEdit(edit, i + 1);
963+
}
964+
result += '</div>';
965+
966+
// Show hidden edits (initially hidden)
967+
if (hiddenEdits.length > 0) {
968+
result += '<div id="' + diffId + '_hidden" style="display: none;">';
969+
for (let i = 0; i < hiddenEdits.length; i++) {
970+
const edit = hiddenEdits[i];
971+
result += '<div class="diff-edit-separator"></div>';
972+
result += formatSingleEdit(edit, visibleEdits.length + i + 1);
973+
}
974+
result += '</div>';
975+
976+
// Add expand button
977+
result += '<div class="diff-expand-container">';
978+
result += '<button class="diff-expand-btn" onclick="toggleDiffExpansion(\\\'' + diffId + '\\\')">Show ' + hiddenEdits.length + ' more edit' + (hiddenEdits.length > 1 ? 's' : '') + '</button>';
979+
result += '</div>';
980+
}
981+
982+
result += '</div>';
983+
984+
// Add other properties if they exist
985+
for (const [key, value] of Object.entries(input)) {
986+
if (key !== 'file_path' && key !== 'edits') {
987+
const valueStr = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
988+
result += '\\n<strong>' + key + ':</strong> ' + valueStr;
989+
}
990+
}
991+
992+
return result;
993+
}
994+
995+
function formatSingleEdit(edit, editNumber) {
996+
let result = '<div class="single-edit">';
997+
result += '<div class="edit-number">Edit #' + editNumber + '</div>';
998+
999+
// Create diff view for this single edit
1000+
const oldLines = edit.old_string.split('\\n');
1001+
const newLines = edit.new_string.split('\\n');
1002+
1003+
// Show removed lines
1004+
for (const line of oldLines) {
1005+
result += '<div class="diff-line removed">- ' + escapeHtml(line) + '</div>';
1006+
}
1007+
1008+
// Show added lines
1009+
for (const line of newLines) {
1010+
result += '<div class="diff-line added">+ ' + escapeHtml(line) + '</div>';
1011+
}
1012+
1013+
result += '</div>';
1014+
return result;
1015+
}
1016+
9011017
function escapeHtml(text) {
9021018
const div = document.createElement('div');
9031019
div.textContent = text;

0 commit comments

Comments
 (0)