socratic-debug 0.0.1__py3-none-any.whl

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.
ui/styles.py ADDED
@@ -0,0 +1,63 @@
1
+ # Global QSS Theme for Socratic Terminal GUI
2
+ SOCRATIC_DARK_THEME = """
3
+ QMainWindow {
4
+ background-color: #0d1117;
5
+ }
6
+ QWidget {
7
+ color: #e6edf3;
8
+ font-family: 'Segoe UI', 'Ubuntu', 'DejaVu Sans', 'Noto Sans Arabic', sans-serif;
9
+ font-size: 14px;
10
+ }
11
+ QFrame#crashBanner {
12
+ background-color: #1f1315;
13
+ border: 1px solid #da3633;
14
+ border-radius: 8px;
15
+ padding: 10px;
16
+ }
17
+ QTextEdit#chatArea {
18
+ background-color: #161b22;
19
+ border: 1px solid #30363d;
20
+ border-radius: 8px;
21
+ padding: 12px;
22
+ line-height: 1.6;
23
+ }
24
+ QLineEdit#inputField {
25
+ background-color: #161b22;
26
+ border: 1px solid #388bfd;
27
+ border-radius: 6px;
28
+ padding: 10px 14px;
29
+ font-size: 14px;
30
+ color: #ffffff;
31
+ }
32
+ QLineEdit#inputField:focus {
33
+ border: 2px solid #58a6ff;
34
+ background-color: #1c2128;
35
+ }
36
+ QPushButton#sendButton {
37
+ background-color: #238636;
38
+ color: #ffffff;
39
+ font-weight: bold;
40
+ border: none;
41
+ border-radius: 6px;
42
+ padding: 10px 24px;
43
+ font-size: 14px;
44
+ }
45
+ QPushButton#sendButton:hover {
46
+ background-color: #2ea043;
47
+ }
48
+ QPushButton#sendButton:disabled {
49
+ background-color: #21262d;
50
+ color: #8b949e;
51
+ }
52
+ QLabel#headerTitle {
53
+ font-size: 16px;
54
+ font-weight: bold;
55
+ color: #58a6ff;
56
+ }
57
+ QLabel#statusBadge {
58
+ font-size: 12px;
59
+ font-weight: bold;
60
+ padding: 4px 10px;
61
+ border-radius: 4px;
62
+ }
63
+ """
ui/templates.py ADDED
@@ -0,0 +1,102 @@
1
+ import html
2
+ import re
3
+
4
+
5
+ class ChatTemplates:
6
+ """HTML templates for rendering styled chat bubbles in QTextEdit."""
7
+
8
+ @staticmethod
9
+ def format_markdown(text: str) -> str:
10
+ """Converts basic markdown code blocks and inline formatting to HTML."""
11
+ parts = []
12
+ lines = text.split("\n")
13
+ in_code = False
14
+ code_buffer = []
15
+
16
+ for line in lines:
17
+ if line.strip().startswith("```"):
18
+ if in_code:
19
+ code_content = html.escape("\n".join(code_buffer))
20
+ parts.append(
21
+ f"<pre style='background: #0d1117; color: #58a6ff; padding: 10px; border-radius: 6px; font-family: monospace; font-size: 13px; text-align: left; direction: ltr;'>{code_content}</pre>"
22
+ )
23
+ code_buffer = []
24
+ in_code = False
25
+ else:
26
+ in_code = True
27
+ continue
28
+
29
+ if in_code:
30
+ code_buffer.append(line)
31
+ else:
32
+ escaped = html.escape(line)
33
+ escaped = re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>', escaped)
34
+ escaped = re.sub(
35
+ r'`(.*?)`',
36
+ r"<code style='background: #21262d; color: #79c0ff; padding: 2px 5px; border-radius: 4px; font-family: monospace; direction: ltr;'>\1</code>",
37
+ escaped,
38
+ )
39
+ parts.append(escaped + "<br/>")
40
+
41
+ if in_code and code_buffer:
42
+ code_content = html.escape("\n".join(code_buffer))
43
+ parts.append(
44
+ f"<pre style='background: #0d1117; color: #58a6ff; padding: 10px; border-radius: 6px; font-family: monospace; font-size: 13px; text-align: left; direction: ltr;'>{code_content}</pre>"
45
+ )
46
+
47
+ return "".join(parts)
48
+
49
+ @staticmethod
50
+ def mentor_bubble(content: str, hint_level: int, mentor_name: str, hint_label: str, is_arabic: bool) -> str:
51
+ border_side = "border-right" if is_arabic else "border-left"
52
+ formatted_html = ChatTemplates.format_markdown(content)
53
+ return f"""
54
+ <div style='margin-bottom: 16px; background-color: #111a2e; {border_side}: 4px solid #388bfd; border-radius: 6px; padding: 12px;'>
55
+ <div style='color: #58a6ff; font-weight: bold; font-size: 13px; margin-bottom: 6px;'>
56
+ 🤖 {mentor_name} <span style='color: #8b949e; font-size: 11px;'>({hint_label} {hint_level}/3)</span>
57
+ </div>
58
+ <div style='color: #f0f6fc; font-size: 14px; line-height: 1.6;'>
59
+ {formatted_html}
60
+ </div>
61
+ </div>
62
+ """
63
+
64
+ @staticmethod
65
+ def user_bubble(text: str, user_tag: str, is_arabic: bool) -> str:
66
+ border_side = "border-left" if is_arabic else "border-right"
67
+ escaped_text = html.escape(text)
68
+ return f"""
69
+ <div style='margin-bottom: 16px; background-color: #1b2128; {border_side}: 4px solid #f2cc60; border-radius: 6px; padding: 10px 14px;'>
70
+ <div style='color: #f2cc60; font-weight: bold; font-size: 12px; margin-bottom: 4px;'>
71
+ 👤 {user_tag}
72
+ </div>
73
+ <div style='color: #ffffff; font-size: 14px;'>
74
+ {escaped_text}
75
+ </div>
76
+ </div>
77
+ """
78
+
79
+ @staticmethod
80
+ def verdict_badge(verdict: str) -> str:
81
+ color = "#3fb950" if "SOLVED" in verdict else ("#f85149" if "SURRENDER" in verdict else "#d29922")
82
+ return f"""
83
+ <div style='text-align: center; margin: 8px 0;'>
84
+ <span style='background-color: #161b22; color: {color}; border: 1px solid {color}; border-radius: 12px; padding: 3px 14px; font-size: 12px; font-weight: bold;'>
85
+ {verdict}
86
+ </span>
87
+ </div>
88
+ """
89
+
90
+ @staticmethod
91
+ def solution_bubble(content: str, wrapup_title: str) -> str:
92
+ formatted_html = ChatTemplates.format_markdown(content)
93
+ return f"""
94
+ <div style='margin-top: 14px; margin-bottom: 20px; background-color: #122119; border: 2px solid #238636; border-radius: 8px; padding: 14px;'>
95
+ <div style='color: #3fb950; font-weight: bold; font-size: 15px; margin-bottom: 8px;'>
96
+ {wrapup_title}
97
+ </div>
98
+ <div style='color: #e6edf3; font-size: 14px; line-height: 1.6;'>
99
+ {formatted_html}
100
+ </div>
101
+ </div>
102
+ """
ui/translations.py ADDED
@@ -0,0 +1,31 @@
1
+ # Multilingual Dictionary for Socratic UI
2
+ TRANSLATIONS = {
3
+ "arabic": {
4
+ "app_title": "🧠 سُقراط ديبَج — المُرشد البرمجي التفاعلي",
5
+ "header_title": "🧠 المُرشد السقراطي للبرمجة",
6
+ "status_active": "● الجلسة نشطة",
7
+ "status_solved": "✔ تم الحل بنجاح",
8
+ "crash_location": "الموقع",
9
+ "crashing_line": "السطر المسبب للخطأ",
10
+ "placeholder": "اكتب إجابتك هنا، أو اكتب 'خروج' للإنهاء...",
11
+ "send_btn": "إرسال",
12
+ "mentor_tag": "المُرشد السقراطي",
13
+ "hint_level": "مستوى التلميح",
14
+ "user_tag": "إجابتك",
15
+ "wrapup_title": "🏆 الحل النهائي وأفضل الممارسات البرمجية",
16
+ },
17
+ "english": {
18
+ "app_title": "🧠 Socratic Debug — Autonomous Mentor",
19
+ "header_title": "🧠 SOCRATIC DEBUG MENTOR",
20
+ "status_active": "● ACTIVE SESSION",
21
+ "status_solved": "✔ SOLVED & CONCLUDED",
22
+ "crash_location": "Location",
23
+ "crashing_line": "Crashing Line",
24
+ "placeholder": "Type your answer here, or type 'exit' / 'q' to quit...",
25
+ "send_btn": "Send",
26
+ "mentor_tag": "Socratic Mentor",
27
+ "hint_level": "Hint Level",
28
+ "user_tag": "Your Response",
29
+ "wrapup_title": "🏆 Socratic Wrap-Up & Defensive Best Practice",
30
+ }
31
+ }
ui/views.py ADDED
@@ -0,0 +1,52 @@
1
+ import os
2
+ from ui.app import bridge, wait_for_gui_user_input
3
+ from ui.console import console
4
+ from rich.panel import Panel
5
+ from rich.markdown import Markdown
6
+
7
+
8
+ class SocraticUI:
9
+ """Unified UI layer: seamlessly routes to PyQt6 Desktop Terminal or CLI Fallback."""
10
+
11
+ @staticmethod
12
+ def is_gui_active() -> bool:
13
+ return os.environ.get("SOCRATIC_GUI_ACTIVE") == "1"
14
+
15
+ @staticmethod
16
+ def display_crash_banner(error_type: str, error_message: str, file_path: str, line_number: int, line_content: str):
17
+ # In CLI fallback mode, print Rich banner
18
+ if not SocraticUI.is_gui_active():
19
+ console.print(f"\n[bold red]💥 {error_type}: {error_message}[/bold red]")
20
+ console.print(f"[dim]📍 {file_path}:{line_number}[/dim]\n")
21
+
22
+ @staticmethod
23
+ def display_mentor_message(content: str, hint_level: int = 1):
24
+ if SocraticUI.is_gui_active():
25
+ bridge.mentor_message_received.emit(content, hint_level)
26
+ else:
27
+ md = Markdown(content)
28
+ panel = Panel(md, title=f"Socratic Mentor (Level {hint_level}/3)", border_style="cyan")
29
+ console.print(panel)
30
+
31
+ @staticmethod
32
+ def prompt_user_input() -> str:
33
+ if SocraticUI.is_gui_active():
34
+ return wait_for_gui_user_input()
35
+ else:
36
+ return console.input("[bold yellow]❯❯ Your Response: [/bold yellow]").strip()
37
+
38
+ @staticmethod
39
+ def display_evaluator_verdict(verdict: str):
40
+ if SocraticUI.is_gui_active():
41
+ bridge.evaluator_verdict_received.emit(verdict)
42
+ else:
43
+ console.print(f" [bold yellow]🔍 {verdict}[/bold yellow]\n")
44
+
45
+ @staticmethod
46
+ def display_solution(solution_content: str):
47
+ if SocraticUI.is_gui_active():
48
+ bridge.solution_received.emit(solution_content)
49
+ else:
50
+ md = Markdown(solution_content)
51
+ panel = Panel(md, title="🏆 Socratic Wrap-Up", border_style="green")
52
+ console.print(panel)
ui/window.py ADDED
@@ -0,0 +1,140 @@
1
+ import platform
2
+ import shutil
3
+ import subprocess
4
+ import sys
5
+ import os
6
+ from typing import List, Optional
7
+
8
+
9
+ class TerminalWindowLauncher:
10
+ """
11
+ Cross-platform Terminal Launcher.
12
+ Spawns dedicated, styled GUI terminal popup windows on:
13
+ - Linux (GNOME Terminal, Kitty, Alacritty, Konsole, XFCE, etc.)
14
+ - macOS (Terminal.app, iTerm2 via AppleScript)
15
+ - Windows (Windows Terminal wt.exe, CMD, PowerShell)
16
+ """
17
+
18
+ LINUX_TERMINALS = [
19
+ "gnome-terminal",
20
+ "io.elementary.terminal",
21
+ "x-terminal-emulator",
22
+ "xfce4-terminal",
23
+ "mate-terminal",
24
+ "konsole",
25
+ "kitty",
26
+ "alacritty",
27
+ "xterm",
28
+ ]
29
+
30
+ @staticmethod
31
+ def spawn_session_window(command_args: List[str]) -> bool:
32
+ """
33
+ Spawns a new dedicated terminal window based on host OS.
34
+ Returns True on success, or False to fallback to in-process execution.
35
+ """
36
+ # Avoid recursive popup spawning
37
+ if os.environ.get("SOCRATIC_IN_POPUP") == "1":
38
+ return False
39
+
40
+ current_os = platform.system().lower()
41
+ env = os.environ.copy()
42
+ env["SOCRATIC_IN_POPUP"] = "1"
43
+
44
+ try:
45
+ if "windows" in current_os:
46
+ return TerminalWindowLauncher._spawn_windows(command_args, env)
47
+ elif "darwin" in current_os:
48
+ return TerminalWindowLauncher._spawn_macos(command_args, env)
49
+ elif "linux" in current_os:
50
+ return TerminalWindowLauncher._spawn_linux(command_args, env)
51
+ except Exception:
52
+ return False
53
+
54
+ return False
55
+
56
+ @staticmethod
57
+ def _spawn_windows(command_args: List[str], env: dict) -> bool:
58
+ cmd_str = " ".join(f'"{arg}"' if " " in arg else arg for arg in command_args)
59
+
60
+ # 1. Try modern Windows Terminal (wt.exe)
61
+ if shutil.which("wt.exe") or shutil.which("wt"):
62
+ subprocess.Popen(
63
+ ["wt.exe", "--title", "🧠 Socratic Debug Session", "cmd", "/c", cmd_str],
64
+ env=env,
65
+ creationflags=subprocess.CREATE_NEW_CONSOLE,
66
+ )
67
+ return True
68
+
69
+ # 2. Fallback to standard Windows cmd.exe
70
+ subprocess.Popen(
71
+ f'start "🧠 Socratic Debug Session" cmd /c "{cmd_str}"',
72
+ shell=True,
73
+ env=env,
74
+ )
75
+ return True
76
+
77
+ @staticmethod
78
+ def _spawn_macos(command_args: List[str], env: dict) -> bool:
79
+ cmd_str = " ".join(f'"{arg}"' if " " in arg else arg for arg in command_args)
80
+ escaped_cmd = cmd_str.replace('\\', '\\\\').replace('"', '\\"')
81
+
82
+ # Launch dedicated macOS Terminal.app via osascript
83
+ apple_script = f'''
84
+ tell application "Terminal"
85
+ do script "{escaped_cmd}"
86
+ activate
87
+ end tell
88
+ '''
89
+ subprocess.Popen(["osascript", "-e", apple_script], env=env)
90
+ return True
91
+
92
+ @staticmethod
93
+ def _spawn_linux(command_args: List[str], env: dict) -> bool:
94
+ terminal = None
95
+ for term in TerminalWindowLauncher.LINUX_TERMINALS:
96
+ if shutil.which(term):
97
+ terminal = term
98
+ break
99
+
100
+ if not terminal:
101
+ return False
102
+
103
+ if terminal in ("gnome-terminal", "io.elementary.terminal", "x-terminal-emulator", "mate-terminal", "xfce4-terminal"):
104
+ subprocess.Popen(
105
+ [
106
+ terminal,
107
+ "--title=🧠 Socratic Debug Session",
108
+ "--geometry=84x24",
109
+ "--",
110
+ *command_args,
111
+ ],
112
+ env=env,
113
+ )
114
+ return True
115
+
116
+ elif terminal == "konsole":
117
+ subprocess.Popen(
118
+ [
119
+ terminal,
120
+ "-p", "tabtitle=🧠 Socratic Debug Session",
121
+ "-e",
122
+ *command_args,
123
+ ],
124
+ env=env,
125
+ )
126
+ return True
127
+
128
+ elif terminal in ("alacritty", "kitty", "xterm"):
129
+ subprocess.Popen(
130
+ [
131
+ terminal,
132
+ "-T", "🧠 Socratic Debug Session",
133
+ "-e",
134
+ *command_args,
135
+ ],
136
+ env=env,
137
+ )
138
+ return True
139
+
140
+ return False