Skip to content

Commit 24dfb5b

Browse files
committed
feat(gui): also add diffview to gui
1 parent e23d83e commit 24dfb5b

File tree

11 files changed

+890
-11
lines changed

11 files changed

+890
-11
lines changed

AgentCrew/modules/gui/components/tool_handlers.py

Lines changed: 167 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
QVBoxLayout,
88
QPushButton,
99
QLabel,
10+
QScrollArea,
1011
)
1112
from PySide6.QtGui import QKeySequence, QShortcut
1213
from PySide6.QtCore import Qt
1314
from AgentCrew.modules.gui.widgets.tool_widget import ToolWidget
15+
from AgentCrew.modules.gui.widgets.diff_widget import DiffWidget
1416

1517

1618
class ToolEventHandler:
@@ -158,17 +160,18 @@ def handle_tool_confirmation_required(self, tool_info):
158160
tool_use = tool_info.copy()
159161
confirmation_id = tool_use.pop("confirmation_id")
160162

161-
# Special handling for 'ask' tool
162163
if tool_use["name"] == "ask":
163164
self._handle_ask_tool_confirmation(tool_use, confirmation_id)
164165
return
165166

166-
# Create dialog
167+
if tool_use["name"] == "write_or_edit_file":
168+
self._handle_write_or_edit_file_confirmation(tool_use, confirmation_id)
169+
return
170+
167171
dialog = QMessageBox(self.chat_window)
168172
dialog.setWindowTitle("Tool Execution Confirmation")
169173
dialog.setIcon(QMessageBox.Icon.Question)
170174

171-
# Format tool information for display
172175
tool_description = f"The assistant wants to use the '{tool_use['name']}' tool."
173176
params_text = ""
174177

@@ -189,7 +192,6 @@ def handle_tool_confirmation_required(self, tool_info):
189192
text_edit.setReadOnly(True)
190193
text_edit.setText(params_text)
191194

192-
# Style the text edit to match the main theme
193195
text_edit.setStyleSheet(
194196
self.chat_window.style_provider.get_tool_dialog_text_edit_style()
195197
)
@@ -293,6 +295,167 @@ def handle_tool_denied(self, data):
293295
self.chat_window.current_response_bubble = None
294296
self.chat_window.current_response_container = None
295297

298+
def _handle_write_or_edit_file_confirmation(self, tool_use, confirmation_id):
299+
"""Handle write_or_edit_file tool confirmation with diff view."""
300+
from PySide6.QtWidgets import QWidget, QHBoxLayout
301+
302+
tool_input = tool_use.get("input", {})
303+
file_path = tool_input.get("file_path", "")
304+
text_or_blocks = tool_input.get("text_or_search_replace_blocks", "")
305+
percentage = tool_input.get("percentage_to_change", 0)
306+
307+
has_diff = DiffWidget.has_search_replace_blocks(text_or_blocks)
308+
309+
dialog = QDialog(self.chat_window)
310+
dialog.setWindowTitle("File Edit Confirmation")
311+
dialog.setMinimumWidth(800 if has_diff else 600)
312+
dialog.setMinimumHeight(600 if has_diff else 400)
313+
314+
layout = QVBoxLayout()
315+
316+
diff_colors = self.chat_window.style_provider.get_diff_colors()
317+
header_label = QLabel(f"📝 <b>Edit File:</b> {file_path}")
318+
header_label.setStyleSheet(
319+
f"font-size: 14px; padding: 10px; color: {diff_colors.get('header_text', '#89b4fa')};"
320+
)
321+
layout.addWidget(header_label)
322+
323+
info_label = QLabel(
324+
f"Change percentage: {percentage}% | "
325+
f"Mode: {'Search/Replace Blocks' if has_diff else 'Full Content'}"
326+
)
327+
info_label.setStyleSheet(
328+
f"font-size: 11px; color: {diff_colors.get('line_number_text', '#6c7086')}; padding: 0 10px;"
329+
)
330+
layout.addWidget(info_label)
331+
332+
scroll_area = QScrollArea()
333+
scroll_area.setWidgetResizable(True)
334+
scroll_area.setMinimumHeight(350)
335+
336+
if has_diff:
337+
diff_widget = DiffWidget(style_provider=self.chat_window.style_provider)
338+
diff_widget.set_diff_content(text_or_blocks, file_path)
339+
scroll_area.setWidget(diff_widget)
340+
else:
341+
content_widget = QTextEdit()
342+
content_widget.setReadOnly(True)
343+
content_widget.setText(text_or_blocks)
344+
content_widget.setStyleSheet(
345+
self.chat_window.style_provider.get_tool_dialog_text_edit_style()
346+
)
347+
scroll_area.setWidget(content_widget)
348+
349+
layout.addWidget(scroll_area)
350+
351+
buttons_container = QWidget()
352+
buttons_layout = QHBoxLayout(buttons_container)
353+
buttons_layout.setContentsMargins(0, 10, 0, 0)
354+
355+
yes_button = QPushButton("✓ Approve")
356+
yes_button.setStyleSheet(
357+
self.chat_window.style_provider.get_tool_dialog_yes_button_style()
358+
)
359+
360+
all_button = QPushButton("✓✓ Yes to All")
361+
all_button.setStyleSheet(
362+
self.chat_window.style_provider.get_tool_dialog_all_button_style()
363+
)
364+
365+
forever_button = QPushButton("∞ Forever")
366+
forever_button.setStyleSheet(
367+
self.chat_window.style_provider.get_tool_dialog_all_button_style()
368+
)
369+
370+
no_button = QPushButton("✗ Deny")
371+
no_button.setStyleSheet(
372+
self.chat_window.style_provider.get_tool_dialog_no_button_style()
373+
)
374+
375+
buttons_layout.addWidget(yes_button)
376+
buttons_layout.addWidget(all_button)
377+
buttons_layout.addWidget(forever_button)
378+
buttons_layout.addStretch()
379+
buttons_layout.addWidget(no_button)
380+
381+
layout.addWidget(buttons_container)
382+
383+
dialog.setLayout(layout)
384+
dialog.setStyleSheet(self.chat_window.style_provider.get_config_window_style())
385+
386+
result = {"action": ""}
387+
388+
def on_yes():
389+
result["action"] = "approve"
390+
dialog.accept()
391+
392+
def on_all():
393+
result["action"] = "approve_all"
394+
dialog.accept()
395+
396+
def on_forever():
397+
result["action"] = "approve_forever"
398+
dialog.accept()
399+
400+
def on_no():
401+
result["action"] = "deny"
402+
dialog.reject()
403+
404+
yes_button.clicked.connect(on_yes)
405+
all_button.clicked.connect(on_all)
406+
forever_button.clicked.connect(on_forever)
407+
no_button.clicked.connect(on_no)
408+
409+
approve_shortcut = QShortcut(QKeySequence("Ctrl+Return"), dialog)
410+
approve_shortcut.activated.connect(on_yes)
411+
412+
dialog.exec()
413+
414+
if result["action"] == "approve":
415+
self.chat_window.message_handler.resolve_tool_confirmation(
416+
confirmation_id, {"action": "approve"}
417+
)
418+
self.chat_window.display_status_message(f"Approved file edit: {file_path}")
419+
420+
elif result["action"] == "approve_all":
421+
self.chat_window.message_handler.resolve_tool_confirmation(
422+
confirmation_id, {"action": "approve_all"}
423+
)
424+
self.chat_window.display_status_message(
425+
"Approved all future write_or_edit_file calls"
426+
)
427+
428+
elif result["action"] == "approve_forever":
429+
from AgentCrew.modules.config import ConfigManagement
430+
431+
config_manager = ConfigManagement()
432+
config_manager.write_auto_approval_tools("write_or_edit_file", add=True)
433+
434+
self.chat_window.message_handler.resolve_tool_confirmation(
435+
confirmation_id, {"action": "approve_all"}
436+
)
437+
self.chat_window.display_status_message(
438+
"write_or_edit_file will be auto-approved forever"
439+
)
440+
441+
else:
442+
denial_reason = self.show_denial_reason_dialog("write_or_edit_file")
443+
444+
if denial_reason:
445+
self.chat_window.message_handler.resolve_tool_confirmation(
446+
confirmation_id, {"action": "deny", "reason": denial_reason}
447+
)
448+
self.chat_window.display_status_message(
449+
f"Denied file edit - Reason: {denial_reason[:50]}..."
450+
if len(denial_reason) > 50
451+
else f"Denied file edit - Reason: {denial_reason}"
452+
)
453+
else:
454+
self.chat_window.message_handler.resolve_tool_confirmation(
455+
confirmation_id, {"action": "deny"}
456+
)
457+
self.chat_window.display_status_message("Denied file edit")
458+
296459
def _handle_ask_tool_confirmation(self, tool_use, confirmation_id):
297460
"""Handle the ask tool - display question and guided answers in GUI."""
298461
question = tool_use["input"].get("question", "")

AgentCrew/modules/gui/themes/atom_light.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,26 @@ class AtomLightTheme:
12791279
"default": "🔧", # Default icon for unspecified tools
12801280
}
12811281

1282+
# Diff Widget colors
1283+
DIFF_COLORS = {
1284+
"background": "#fafafa", # Light background
1285+
"panel_bg": "#ffffff", # White
1286+
"header_bg": "#e5e5e6", # Light gray
1287+
"header_text": "#383a42", # Dark text
1288+
"line_number_bg": "#f0f0f0", # Very light gray
1289+
"line_number_text": "#9d9d9f", # Medium gray
1290+
"removed_bg": "#ffeef0", # Light red background
1291+
"removed_text": "#d73a49", # Red text
1292+
"removed_highlight": "#d73a49", # Red for character highlight
1293+
"added_bg": "#e6ffec", # Light green background
1294+
"added_text": "#22863a", # Green text
1295+
"added_highlight": "#22863a", # Green for character highlight
1296+
"unchanged_text": "#9d9d9f", # Gray
1297+
"border": "#e5e5e6", # Light border
1298+
"block_header_bg": "#ddf4ff", # Light blue
1299+
"block_header_text": "#0969da", # Blue text
1300+
}
1301+
12821302
# JSON Editor styles
12831303
JSON_EDITOR_COLORS = {
12841304
"background": "#fafafa",

AgentCrew/modules/gui/themes/catppuccin.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,26 @@ class CatppuccinTheme:
13181318
"default": "🔧", # Default icon for unspecified tools
13191319
}
13201320

1321+
# Diff Widget colors
1322+
DIFF_COLORS = {
1323+
"background": "#1e1e2e", # Base
1324+
"panel_bg": "#313244", # Surface0
1325+
"header_bg": "#45475a", # Surface1
1326+
"header_text": "#cdd6f4", # Text
1327+
"line_number_bg": "#181825", # Mantle
1328+
"line_number_text": "#6c7086", # Overlay0
1329+
"removed_bg": "#3b2d33", # Subtle red background
1330+
"removed_text": "#f38ba8", # Red
1331+
"removed_highlight": "#f38ba8", # Red for character highlight
1332+
"added_bg": "#2d3b33", # Subtle green background
1333+
"added_text": "#a6e3a1", # Green
1334+
"added_highlight": "#a6e3a1", # Green for character highlight
1335+
"unchanged_text": "#6c7086", # Overlay0
1336+
"border": "#45475a", # Surface1
1337+
"block_header_bg": "#585b70", # Surface2
1338+
"block_header_text": "#b4befe", # Lavender
1339+
}
1340+
13211341
# JSON Editor styles
13221342
JSON_EDITOR_COLORS = {
13231343
"background": "#313244", # Surface0

AgentCrew/modules/gui/themes/dracula.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,6 +1286,26 @@ class DraculaTheme:
12861286
"default": "🔧", # Default icon for unspecified tools
12871287
}
12881288

1289+
# Diff Widget colors
1290+
DIFF_COLORS = {
1291+
"background": "#282a36", # Dracula Background
1292+
"panel_bg": "#21222c", # Darker
1293+
"header_bg": "#44475a", # Comment
1294+
"header_text": "#f8f8f2", # Foreground
1295+
"line_number_bg": "#1e1f29", # Very dark
1296+
"line_number_text": "#6272a4", # Comment
1297+
"removed_bg": "#3d2a36", # Subtle red background
1298+
"removed_text": "#ff5555", # Red
1299+
"removed_highlight": "#ff5555", # Red
1300+
"added_bg": "#2a3d36", # Subtle green background
1301+
"added_text": "#50fa7b", # Green
1302+
"added_highlight": "#50fa7b", # Green
1303+
"unchanged_text": "#6272a4", # Comment
1304+
"border": "#44475a", # Border
1305+
"block_header_bg": "#bd93f9", # Purple
1306+
"block_header_text": "#282a36", # Background
1307+
}
1308+
12891309
# JSON Editor styles
12901310
JSON_EDITOR_COLORS = {
12911311
"background": "#282a36",

AgentCrew/modules/gui/themes/nord.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,26 @@ class NordTheme:
12791279
"default": "🔧", # Default icon for unspecified tools
12801280
}
12811281

1282+
# Diff Widget colors
1283+
DIFF_COLORS = {
1284+
"background": "#2e3440", # Polar Night
1285+
"panel_bg": "#3b4252", # Polar Night lighter
1286+
"header_bg": "#434c5e", # Polar Night
1287+
"header_text": "#d8dee9", # Snow Storm
1288+
"line_number_bg": "#2e3440", # Polar Night
1289+
"line_number_text": "#4c566a", # Polar Night
1290+
"removed_bg": "#3d2f3a", # Subtle red background
1291+
"removed_text": "#bf616a", # Aurora Red
1292+
"removed_highlight": "#bf616a", # Aurora Red
1293+
"added_bg": "#2f3d38", # Subtle green background
1294+
"added_text": "#a3be8c", # Aurora Green
1295+
"added_highlight": "#a3be8c", # Aurora Green
1296+
"unchanged_text": "#4c566a", # Polar Night
1297+
"border": "#4c566a", # Border
1298+
"block_header_bg": "#5e81ac", # Frost
1299+
"block_header_text": "#eceff4", # Snow Storm
1300+
}
1301+
12821302
# JSON Editor styles
12831303
JSON_EDITOR_COLORS = {
12841304
"background": "#2e3440",

AgentCrew/modules/gui/themes/saigontech.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,6 +1273,26 @@ class SaigonTechTheme:
12731273
"default": "🔧", # Default icon for unspecified tools
12741274
}
12751275

1276+
# Diff Widget colors
1277+
DIFF_COLORS = {
1278+
"background": "#334155", # Slate 700
1279+
"panel_bg": "#475569", # Slate 600
1280+
"header_bg": "#1e293b", # Slate 800
1281+
"header_text": "#f8fafc", # Slate 50
1282+
"line_number_bg": "#1e293b", # Slate 800
1283+
"line_number_text": "#64748b", # Slate 500
1284+
"removed_bg": "#4c2c3a", # Subtle red
1285+
"removed_text": "#fca5a5", # Red 300
1286+
"removed_highlight": "#fca5a5", # Red 300
1287+
"added_bg": "#2c4c3a", # Subtle green
1288+
"added_text": "#7fb239", # Primary Green
1289+
"added_highlight": "#7fb239", # Primary Green
1290+
"unchanged_text": "#64748b", # Slate 500
1291+
"border": "#475569", # Border
1292+
"block_header_bg": "#638b2c", # Secondary Green
1293+
"block_header_text": "#f8fafc", # Slate 50
1294+
}
1295+
12761296
# JSON Editor styles
12771297
JSON_EDITOR_COLORS = {
12781298
"background": "#475569", # Input Fields - Slate 600

AgentCrew/modules/gui/themes/style_provider.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,28 @@ def get_markdown_editor_style(self):
395395
}
396396
""",
397397
)
398+
399+
def get_diff_colors(self):
400+
"""Get color scheme for diff widget from current theme."""
401+
return getattr(
402+
self.theme_class,
403+
"DIFF_COLORS",
404+
{
405+
"background": "#1e1e2e",
406+
"panel_bg": "#313244",
407+
"header_bg": "#45475a",
408+
"header_text": "#cdd6f4",
409+
"line_number_bg": "#181825",
410+
"line_number_text": "#6c7086",
411+
"removed_bg": "#3b2d33",
412+
"removed_text": "#f38ba8",
413+
"removed_highlight": "#f38ba8",
414+
"added_bg": "#2d3b33",
415+
"added_text": "#a6e3a1",
416+
"added_highlight": "#a6e3a1",
417+
"unchanged_text": "#6c7086",
418+
"border": "#45475a",
419+
"block_header_bg": "#585b70",
420+
"block_header_text": "#b4befe",
421+
},
422+
)

0 commit comments

Comments
 (0)