diff --git a/icons/debug-icon.svg b/icons/debug-icon.svg
new file mode 100644
index 0000000..2e1ae3c
--- /dev/null
+++ b/icons/debug-icon.svg
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/icons/debug-terminal.svg b/icons/debug-terminal.svg
new file mode 100644
index 0000000..ddbd871
--- /dev/null
+++ b/icons/debug-terminal.svg
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/icons/output-terminal.svg b/icons/output-terminal.svg
new file mode 100644
index 0000000..80fd4c0
--- /dev/null
+++ b/icons/output-terminal.svg
@@ -0,0 +1,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/pippy_app.py b/pippy_app.py
index 1cc3111..a2504c0 100644
--- a/pippy_app.py
+++ b/pippy_app.py
@@ -33,6 +33,8 @@
import locale
import json
import sys
+import requests
+import threading
from shutil import copy2
from signal import SIGTERM
from gettext import gettext as _
@@ -122,6 +124,8 @@
)
""" # This is .format()'ed with the list of the file names.
+API_URL = "http://192.168.64.1:8000/debug"
+X_API_KEY = ""
def _has_new_vte_api():
try:
@@ -289,6 +293,17 @@ def initialize_display(self):
actions_toolbar.insert(button, -1)
button.show()
+ icon_bw = Gtk.Image()
+ icon_bw.set_from_file(os.path.join(icons_path, 'debug-icon.svg'))
+ icon_bw.show()
+ button = ToolButton(label=_('Run and Debug'))
+ button.props.accelerator = _('d')
+ button.set_icon_widget(icon_bw)
+ button.set_tooltip(_('Run and Debug'))
+ button.connect('clicked', self._debug_button_cb)
+ actions_toolbar.insert(button, -1)
+ button.show()
+
icon_bw = Gtk.Image()
icon_bw.set_from_file(os.path.join(icons_path, 'stopit_bw.svg'))
icon_bw.show()
@@ -426,15 +441,66 @@ def initialize_display(self):
self._vte.set_size(30, 5)
self._vte.set_scrollback_lines(-1)
+ self._debug_vte = Vte.Terminal()
+ self._debug_vte.set_encoding('utf-8')
+ self._debug_vte.set_size(30,5)
+ self._debug_vte.set_scrollback_lines(-1)
+
self._vte_set_colors('#000000', '#E7E7E7')
+ self._vte_set_debug_colors('#000000', "#D9E0EE")
self._child_exited_handler = None
self._vte.connect('child_exited', self._child_exited_cb)
self._vte.connect('drag_data_received', self._vte_drop_cb)
- self._outbox.pack_start(self._vte, True, True, 0)
+ self._debug_vte.connect('child_exited', self._child_exited_cb)
+
+ icon_bw = Gtk.Image()
+ icon_bw.set_from_file(os.path.join(icons_path, 'output-terminal.svg'))
+ icon_bw.show()
+ btn_output = ToolButton(label=_("Terminal"))
+ btn_output.props.accelerator = _('r')
+ btn_output.set_icon_widget(icon_bw)
+ btn_output.set_tooltip(_("Terminal"))
+ btn_output.connect("clicked", lambda w: self._terminal_stack.set_visible_child_name("output"))
+
+ icon_bw = Gtk.Image()
+ icon_bw.set_from_file(os.path.join(icons_path, 'debug-terminal.svg'))
+ icon_bw.show()
+ btn_debug = ToolButton(label=_("Debug Terminal"))
+ btn_debug.props.accelerator = _('d')
+ btn_debug.set_icon_widget(icon_bw)
+ btn_debug.set_tooltip(_("Debug Terminal"))
+ btn_debug.connect('clicked', self._debug_terminal_cb)
+ btn_debug.connect("clicked", lambda w: self._terminal_stack.set_visible_child_name("debug"))
+
+ style_provider = Gtk.CssProvider()
+ style_provider.load_from_data(b"""
+ .colored-box {
+ background-color: #282828;
+ }
+ """)
+ Gtk.StyleContext.add_provider_for_screen(
+ Gdk.Screen.get_default(),
+ style_provider,
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
+ )
+
+ switch_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+ switch_box.get_style_context().add_class("colored-box")
+ switch_box.pack_start(btn_output, False, False, 0)
+ switch_box.pack_start(btn_debug, False, False, 0)
+
+ self._terminal_stack = Gtk.Stack()
+ self._terminal_stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
+ self._terminal_stack.add_named(self._vte, "output")
+ self._terminal_stack.add_named(self._debug_vte, "debug")
+
+ self._outbox.pack_start(switch_box, False, False, 0)
+ self._outbox.pack_start(self._terminal_stack, True, True, 0)
outsb = Gtk.Scrollbar(orientation=Gtk.Orientation.VERTICAL)
outsb.set_adjustment(self._vte.get_vadjustment())
+ outsb.set_adjustment(self._debug_vte.get_vadjustment())
outsb.show()
self._outbox.pack_start(outsb, False, False, 0)
@@ -458,6 +524,18 @@ def _vte_set_colors(self, bg, fg):
self._vte.set_colors(foreground, background, [])
+ def _vte_set_debug_colors(self, bg, fg):
+ if _has_new_vte_api():
+ foreground = Gdk.RGBA()
+ foreground.parse(bg)
+ background = Gdk.RGBA()
+ background.parse(fg)
+ else:
+ foreground = Gdk.color_parse(bg)
+ background = Gdk.color_parse(fg)
+
+ self._debug_vte.set_colors(foreground, background, [])
+
def after_init(self):
self._outbox.hide()
@@ -465,6 +543,8 @@ def _font_size_changed_cb(self, widget, size):
self._source_tabs.set_font_size(size)
self._vte.set_font(
Pango.FontDescription('Monospace {}'.format(size)))
+ self._debug_vte.set_font(
+ Pango.FontDescription('Monospace {}'.format(size)))
def _store_config(self):
font_size = self._source_tabs.get_font_size()
@@ -525,11 +605,13 @@ def _toggle_output_cb(self, button):
def __inverted_colors_toggled_cb(self, button):
if button.props.active:
self._vte_set_colors('#E7E7E7', '#000000')
+ self._vte_set_debug_colors( '#D9E0EE', '#000000')
self._source_tabs.set_dark()
button.set_icon_name('light-theme')
button.set_tooltip(_('Normal Colors'))
else:
self._vte_set_colors('#000000', '#E7E7E7')
+ self._vte_set_debug_colors('#000000', '#D9E0EE')
self._source_tabs.set_light()
button.set_icon_name('dark-theme')
button.set_tooltip(_('Inverted Colors'))
@@ -691,6 +773,11 @@ def _reset_vte(self):
self._vte.grab_focus()
self._vte.feed(b'\x1B[H\x1B[J\x1B[0;39m')
+ # Reset debugging terminal
+ def _reset_debug_vte(self):
+ self._debug_vte.grab_focus()
+ self._debug_vte.feed(b'\x1B[H\x1B[2J\x1B[3J\x1B[0;39m')
+
def __undobutton_cb(self, button):
text_buffer = self._source_tabs.get_text_buffer()
if text_buffer.can_undo():
@@ -759,6 +846,150 @@ def _go_button_cb(self, button):
GLib.SpawnFlags.DO_NOT_REAP_CHILD,
None,
None,)
+
+ # To extract the source code
+ def _get_current_code(self):
+ pippy_tmp_dir = '%s/tmp/' % self.get_activity_root()
+ current_file = os.path.join(
+ pippy_tmp_dir,
+ self._source_tabs.get_current_file_name()
+ )
+
+ try:
+ with open(current_file, 'r') as f:
+ lines = f.readlines()
+ filtered_lines = [
+ line for line in lines
+ if not (
+ line.strip().startswith('#!') or
+ 'coding' in line.lower()
+ )
+ ]
+ return ''.join(filtered_lines)
+
+ except Exception as e:
+ print(f"Error reading file {current_file}: {e}")
+ return None
+
+ def markdown_parser(self, answer):
+ lines = answer.splitlines()
+ output = []
+ in_code_block = False
+
+ for line in lines:
+ stripped = line.strip()
+
+ if stripped.startswith("```"):
+ in_code_block = not in_code_block
+ continue
+
+ # Inside code block → render the line in bold
+ if in_code_block:
+ output.append("\033[1m" + line + "\033[0m\r\n")
+ continue
+
+ # Parse headings
+ if stripped.startswith("#### "): # H4 → Blue, bold
+ output.append("\033[1;34m" + stripped[5:] + "\033[0m\r\n")
+ continue
+ elif stripped.startswith("### "): # H3 → Blue, bold
+ output.append("\033[1;34m" + stripped[4:] + "\033[0m\r\n")
+ continue
+ elif stripped.startswith("## "): # H2 → Blue, bold
+ output.append("\033[1;34m" + stripped[3:] + "\033[0m\r\n")
+ continue
+ elif stripped.startswith("# "): # H1 → Green, bold + underlined
+ output.append("\033[1;32;4m" + stripped[2:] + "\033[0m\r\n")
+ continue
+
+ # Parse bullet points (- or *) → replace with •
+ if stripped.startswith("- " or "* "):
+ line = "• " + stripped[2:]
+
+ # Parse inline code: `...` → bold
+ line = re.sub(r"`([^`]*)`", r"\033[1m\1\033[0m", line)
+
+ # Parse bold text: **...** → dim
+ line = re.sub(r"\*\*(.*?)\*\*", r"\033[2m\1\033[0m", line)
+
+ # Parse italic text: *...* → dim (excluding bold markers)
+ line = re.sub(r"(?