liveConsole 1.5.3__py3-none-any.whl → 1.6.0__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.
- {liveconsole-1.5.3.dist-info → liveconsole-1.6.0.dist-info}/METADATA +4 -6
- liveconsole-1.6.0.dist-info/RECORD +18 -0
- liveconsole-1.6.0.dist-info/top_level.txt +1 -0
- pysole/__init__.py +1 -0
- pysole/__main__.py +4 -0
- helpTab.py → pysole/helpTab.py +5 -5
- liveConsole.py → pysole/liveConsole.py +1 -1
- mainConsole.py → pysole/mainConsole.py +9 -10
- pysole/pysole.py +250 -0
- pysole/settings.json +25 -0
- styledTextbox.py → pysole/styledTextbox.py +11 -12
- suggestionManager.py → pysole/suggestionManager.py +9 -6
- pysole/themes.json +78 -0
- pysole/utils.py +4 -0
- __init__.py +0 -0
- liveconsole-1.5.3.dist-info/RECORD +0 -14
- liveconsole-1.5.3.dist-info/top_level.txt +0 -8
- pysole.py +0 -126
- {liveconsole-1.5.3.dist-info → liveconsole-1.6.0.dist-info}/WHEEL +0 -0
- {liveconsole-1.5.3.dist-info → liveconsole-1.6.0.dist-info}/entry_points.txt +0 -0
- {liveconsole-1.5.3.dist-info → liveconsole-1.6.0.dist-info}/licenses/LICENSE +0 -0
- /commandHistory.py → /pysole/commandHistory.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: liveConsole
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.6.0
|
4
4
|
Summary: An IDLE-like debugger to allow for real-time command injection for debugging and testing python code
|
5
5
|
Author-email: Tzur Soffer <tzur.soffer@gmail.com>
|
6
6
|
License: MIT
|
@@ -127,11 +127,9 @@ pysole.probe()
|
|
127
127
|
|
128
128
|
## Customization
|
129
129
|
|
130
|
-
* **Appearance mode**: Dark mode is default
|
131
|
-
|
132
|
-
* **
|
133
|
-
|
134
|
-
* **Syntax Highlighting Style**: Change Pygments style by modifying `get_style_by_name("monokai")`
|
130
|
+
* **Appearance mode**: Dark mode is default, but can be changed via files menu
|
131
|
+
|
132
|
+
* **Themes**: Pysole has multiple preconfigured themes. You can choose a theme via the Theme Picker, which updates the console colors and appearance. Preconfigured themes are loaded from themes.json and the selected theme is saved in settings.json so it persists across sessions.
|
135
133
|
|
136
134
|
|
137
135
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
liveconsole-1.6.0.dist-info/licenses/LICENSE,sha256=7dZ0zL72aGaFE0C9DxacOpnaSkC5jajhG6iL7lqhWmU,1064
|
2
|
+
pysole/__init__.py,sha256=Bi1HZqtw7QftfodvEV1uPESgCSdhDxTfTkOazgopyVg,47
|
3
|
+
pysole/__main__.py,sha256=QvVFH8J2yzgQaF9MosQ6ajCW67uiRbYliePsTEUCIAg,82
|
4
|
+
pysole/commandHistory.py,sha256=xJtWbJ_vgJo2QGgaZJsApTOi_Hm8Yz0V9_zqQUCItj8,1084
|
5
|
+
pysole/helpTab.py,sha256=o0uSY-8sw7FuuBrt0FQwcgK6ljNVx3IgRnW7heZ6GvY,2454
|
6
|
+
pysole/liveConsole.py,sha256=lzS3dphAQ1i8pQC4E2FY-FyMMSKi-dAN0xr6XUgrNmo,168
|
7
|
+
pysole/mainConsole.py,sha256=9Kdjpe9rQzx3Nk9aBaECR3PlwAXszgRt4VfIJmVJnRw,13141
|
8
|
+
pysole/pysole.py,sha256=vTmPvS4nzapVudNPtf2JrnwBWB-x_n-XxBsr4jW7MK8,9230
|
9
|
+
pysole/settings.json,sha256=OWbEDbI8Ov-R8-1bCcb-t07by_ljSKyVxmT9uoP9bzg,693
|
10
|
+
pysole/styledTextbox.py,sha256=zpbaN0qX5FduhNyuWGU7y8Ll8J9p9YqczpSuRLo4PX0,2120
|
11
|
+
pysole/suggestionManager.py,sha256=EUFeCQoZnLS8EjPPNuZpzjY0BR07m2KP5TloyaMDVGY,6457
|
12
|
+
pysole/themes.json,sha256=2RG3ohn2ZBc2-h9bdooQZD6hW5pD41a5c2jdaKDA4MA,2385
|
13
|
+
pysole/utils.py,sha256=VN42ukHMJpOvGb7ZV-qhF_zRUt13hzJKV_uRn9t6580,155
|
14
|
+
liveconsole-1.6.0.dist-info/METADATA,sha256=8x76Zla154uZZS2-wO1Bkl3kW7J4AJybMx04H4AvLog,4011
|
15
|
+
liveconsole-1.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
16
|
+
liveconsole-1.6.0.dist-info/entry_points.txt,sha256=qtvuJHcex4QqM97x_UawFWJYnfhQRl0jhqLcWRpnAGo,84
|
17
|
+
liveconsole-1.6.0.dist-info/top_level.txt,sha256=DlpA93ScJbRZcF8kGSc5YoO8Ntu1ib1_MEZMb4xea_Q,7
|
18
|
+
liveconsole-1.6.0.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
pysole
|
pysole/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
from .pysole import probe, InteractiveConsole
|
pysole/__main__.py
ADDED
helpTab.py → pysole/helpTab.py
RENAMED
@@ -2,12 +2,12 @@ import sys
|
|
2
2
|
import io
|
3
3
|
from pygments.styles import get_style_by_name
|
4
4
|
import customtkinter as ctk
|
5
|
-
from styledTextbox import StyledTextWindow
|
5
|
+
from .styledTextbox import StyledTextWindow
|
6
6
|
|
7
7
|
class HelpTab(ctk.CTkFrame):
|
8
8
|
"""A right-hand help tab with closable and updateable text content."""
|
9
9
|
|
10
|
-
def __init__(self, parent, width=500, title="Help", **kwargs):
|
10
|
+
def __init__(self, parent, theme, font, width=500, title="Help", **kwargs):
|
11
11
|
super().__init__(parent, width=width, **kwargs)
|
12
12
|
self.parent = parent
|
13
13
|
self.visible = False
|
@@ -18,16 +18,16 @@ class HelpTab(ctk.CTkFrame):
|
|
18
18
|
# Header frame with title and close button
|
19
19
|
headerFrame = ctk.CTkFrame(self, height=30)
|
20
20
|
headerFrame.pack(fill="x")
|
21
|
-
self.style = get_style_by_name("
|
21
|
+
self.style = get_style_by_name(theme["LEXER_STYLE"])
|
22
22
|
|
23
|
-
self.titleLabel = ctk.CTkLabel(headerFrame, text=title, font=("
|
23
|
+
self.titleLabel = ctk.CTkLabel(headerFrame, text=title, font=(font["FONT"], font["FONT_SIZE"], "bold"))
|
24
24
|
self.titleLabel.pack(side="left", padx=5)
|
25
25
|
|
26
26
|
self.closeButton = ctk.CTkButton(headerFrame, text="X", height=20, command=self.close)
|
27
27
|
self.closeButton.pack(side="right", padx=5)
|
28
28
|
|
29
29
|
# Scrollable text area
|
30
|
-
self.textBox = StyledTextWindow(self,
|
30
|
+
self.textBox = StyledTextWindow(self, theme, {"FONT": font["FONT"], "FONT_SIZE": max(0, (font["FONT_SIZE"]-1))}, wrap="word", bg="#2e2e2e")
|
31
31
|
self.textBox.pack(fill="both", expand=True, padx=5, pady=5)
|
32
32
|
self.textBox.configure(state="disabled") # read-only
|
33
33
|
|
@@ -1,22 +1,21 @@
|
|
1
1
|
import threading
|
2
2
|
import traceback
|
3
|
-
from suggestionManager import CodeSuggestionManager
|
4
|
-
from commandHistory import CommandHistory
|
5
|
-
from styledTextbox import StyledTextWindow
|
3
|
+
from .suggestionManager import CodeSuggestionManager
|
4
|
+
from .commandHistory import CommandHistory
|
5
|
+
from .styledTextbox import StyledTextWindow
|
6
6
|
|
7
7
|
import tkinter as tk
|
8
8
|
|
9
9
|
class InteractiveConsoleText(StyledTextWindow):
|
10
10
|
"""TBD"""
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def __init__(self, master, helpTab, userLocals=None, userGlobals=None, **kwargs):
|
16
|
-
super().__init__(master, **kwargs)
|
11
|
+
def __init__(self, master, helpTab, theme, font, behavior, userLocals=None, userGlobals=None, **kwargs):
|
12
|
+
super().__init__(master, theme=theme, font=font, **kwargs)
|
13
|
+
self.font=(font["FONT"], font["FONT_SIZE"])
|
17
14
|
|
18
15
|
# Initialize components
|
19
|
-
self.
|
16
|
+
self.PROMPT = behavior["PRIMARY_PROMPT"]
|
17
|
+
self.PROMPT_LENGTH = len(self.PROMPT)
|
18
|
+
self.suggestionManager = CodeSuggestionManager(self, userLocals=userLocals, userGlobals=userGlobals, theme=theme, font=font)
|
20
19
|
self.helpTab = helpTab
|
21
20
|
|
22
21
|
self.navigatingHistory = False
|
pysole/pysole.py
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
import customtkinter as ctk
|
2
|
+
import tkinter as tk
|
3
|
+
from tkinter import messagebox
|
4
|
+
import inspect
|
5
|
+
import sys
|
6
|
+
import io
|
7
|
+
import json
|
8
|
+
|
9
|
+
from .utils import settingsPath, themesPath
|
10
|
+
from .helpTab import HelpTab
|
11
|
+
from .mainConsole import InteractiveConsoleText
|
12
|
+
|
13
|
+
class StdoutRedirect(io.StringIO):
|
14
|
+
"""Redirects stdout/stderr to a callback function."""
|
15
|
+
|
16
|
+
def __init__(self, writeCallback):
|
17
|
+
super().__init__()
|
18
|
+
self.writeCallback = writeCallback
|
19
|
+
|
20
|
+
def write(self, s):
|
21
|
+
if s.strip():
|
22
|
+
self.writeCallback(s, "output")
|
23
|
+
|
24
|
+
def flush(self):
|
25
|
+
pass
|
26
|
+
|
27
|
+
class StdinRedirect(io.StringIO):
|
28
|
+
"""Redirects stdin to capture input() from the console."""
|
29
|
+
def __init__(self, readCallback):
|
30
|
+
super().__init__()
|
31
|
+
self.readCallback = readCallback
|
32
|
+
|
33
|
+
def readline(self, *args, **kwargs):
|
34
|
+
return(self.readCallback())
|
35
|
+
|
36
|
+
class InteractiveConsole(ctk.CTk):
|
37
|
+
"""Main console window application."""
|
38
|
+
|
39
|
+
def __init__(self, userGlobals=None, userLocals=None, callerFrame=None, theme=None, defaultSize=None, primaryPrompt=None):
|
40
|
+
super().__init__()
|
41
|
+
with open(settingsPath, "r") as f:
|
42
|
+
settings = json.load(f)
|
43
|
+
self.THEME = settings["THEME"]
|
44
|
+
self.FONT = self.THEME["FONT"]
|
45
|
+
self.BEHAVIOR = settings["BEHAVIOR"]
|
46
|
+
|
47
|
+
if primaryPrompt != None:
|
48
|
+
self.BEHAVIOR["PRIMARY_PROMPT"] = primaryPrompt
|
49
|
+
if defaultSize != None:
|
50
|
+
self.BEHAVIOR["DEFAULT_SIZE"] = defaultSize
|
51
|
+
|
52
|
+
self.title("Live Interactive Console")
|
53
|
+
self.geometry(self.BEHAVIOR["DEFAULT_SIZE"])
|
54
|
+
|
55
|
+
ctk.set_appearance_mode(self.THEME["APPEARANCE"])
|
56
|
+
ctk.set_default_color_theme("blue")
|
57
|
+
|
58
|
+
# Get namespace from caller if not provided
|
59
|
+
if userGlobals is None or userLocals is None:
|
60
|
+
if callerFrame == None:
|
61
|
+
callerFrame = inspect.currentframe().f_back
|
62
|
+
if userGlobals is None:
|
63
|
+
userGlobals = callerFrame.f_globals
|
64
|
+
if userLocals is None:
|
65
|
+
userLocals = callerFrame.f_locals
|
66
|
+
|
67
|
+
self.userGlobals = userGlobals
|
68
|
+
self.userLocals = userLocals
|
69
|
+
|
70
|
+
# Create UI
|
71
|
+
self._createMenu()
|
72
|
+
self._createUi()
|
73
|
+
|
74
|
+
# Redirect stdout/stderr
|
75
|
+
self._setupOutputRedirect()
|
76
|
+
self._setupInputRedirect()
|
77
|
+
|
78
|
+
def _createMenu(self):
|
79
|
+
"""Create a menu bar using CTkOptionMenu."""
|
80
|
+
menuBar = ctk.CTkFrame(self, fg_color=self.THEME["BACKGROUND"])
|
81
|
+
menuBar.pack(side="top", fill="x")
|
82
|
+
|
83
|
+
self.menu_var = ctk.StringVar(value="File")
|
84
|
+
fileMenu = ctk.CTkOptionMenu(menuBar,
|
85
|
+
values=["Edit Settings", "Load Theme"],
|
86
|
+
variable=self.menu_var,
|
87
|
+
command=self._handleMenu,
|
88
|
+
fg_color=self.THEME["BACKGROUND"],
|
89
|
+
button_color=self.THEME["BACKGROUND"],
|
90
|
+
button_hover_color=self.THEME["HIGHLIGHTED_BACKGROUND"],
|
91
|
+
dropdown_fg_color=self.THEME["BACKGROUND"],
|
92
|
+
dropdown_hover_color=self.THEME["HIGHLIGHTED_BACKGROUND"],
|
93
|
+
dropdown_text_color=self.THEME["FOREGROUND"],
|
94
|
+
text_color=self.THEME["FOREGROUND"])
|
95
|
+
fileMenu.pack(side="left", padx=5, pady=2)
|
96
|
+
|
97
|
+
def _handleMenu(self, choice):
|
98
|
+
if choice == "Edit Settings":
|
99
|
+
self._editSettings()
|
100
|
+
elif choice == "Load Theme":
|
101
|
+
self._loadTheme()
|
102
|
+
|
103
|
+
def _loadTheme(self):
|
104
|
+
"""
|
105
|
+
Open a CTk popup to let the user choose a theme from themes.json.
|
106
|
+
The chosen theme will override the THEME key in settings.json
|
107
|
+
and apply immediately.
|
108
|
+
"""
|
109
|
+
|
110
|
+
# Load themes from themes.json
|
111
|
+
try:
|
112
|
+
with open(themesPath, "r") as f:
|
113
|
+
themes = json.load(f)
|
114
|
+
except Exception as e:
|
115
|
+
messagebox.showerror("Error", f"Failed to load themes.json:\n{e}")
|
116
|
+
return
|
117
|
+
|
118
|
+
if not themes:
|
119
|
+
messagebox.showerror("Error", "No themes found in themes.json")
|
120
|
+
return
|
121
|
+
|
122
|
+
# Create the CTk popup window
|
123
|
+
popup = ctk.CTkToplevel(self)
|
124
|
+
popup.title("Select Theme")
|
125
|
+
popup.geometry("300x150")
|
126
|
+
popup.grab_set()
|
127
|
+
|
128
|
+
ctk.CTkLabel(popup, text="Choose a theme:").pack(pady=(10, 5))
|
129
|
+
|
130
|
+
# Dropdown with theme keys
|
131
|
+
themeVar = tk.StringVar(value=list(themes.keys())[0])
|
132
|
+
themeDropdown = ctk.CTkOptionMenu(popup, values=list(themes.keys()), variable=themeVar)
|
133
|
+
themeDropdown.pack(pady=5)
|
134
|
+
|
135
|
+
def applyTheme():
|
136
|
+
chosenKey = themeVar.get()
|
137
|
+
chosenTheme = themes[chosenKey]
|
138
|
+
|
139
|
+
# Update self.THEME
|
140
|
+
self.THEME = chosenTheme
|
141
|
+
|
142
|
+
# Save to settings.json
|
143
|
+
try:
|
144
|
+
with open(settingsPath, "r") as f:
|
145
|
+
settings = json.load(f)
|
146
|
+
settings["THEME"] = chosenTheme
|
147
|
+
with open(settingsPath, "w") as f:
|
148
|
+
json.dump(settings, f, indent=4)
|
149
|
+
except Exception as e:
|
150
|
+
messagebox.showerror("Error", f"Failed to save settings.json:\n{e}")
|
151
|
+
return
|
152
|
+
|
153
|
+
messagebox.showinfo("Theme Applied", f"Theme '{chosenKey}' applied successfully, relaunch app for changes to take effect!")
|
154
|
+
popup.destroy()
|
155
|
+
|
156
|
+
applyBtn = ctk.CTkButton(popup, text="Apply", command=applyTheme)
|
157
|
+
applyBtn.pack(pady=(10, 10))
|
158
|
+
|
159
|
+
def _editSettings(self):
|
160
|
+
"""Open a simple JSON editor for settings.json."""
|
161
|
+
editor = ctk.CTkToplevel(self)
|
162
|
+
editor.title("Edit Settings")
|
163
|
+
editor.geometry("500x400")
|
164
|
+
|
165
|
+
try:
|
166
|
+
with open(settingsPath, "r") as f:
|
167
|
+
jsonText = f.read()
|
168
|
+
except Exception as e:
|
169
|
+
jsonText = f"{{}}\n\n# Failed to load settings.json:\n{e}"
|
170
|
+
|
171
|
+
textbox = ctk.CTkTextbox(editor)
|
172
|
+
textbox.pack(fill="both", expand=True, padx=10, pady=10)
|
173
|
+
textbox.insert("0.0", jsonText)
|
174
|
+
|
175
|
+
def saveSettings():
|
176
|
+
try:
|
177
|
+
newSettings = json.loads(textbox.get("0.0", "end-1c"))
|
178
|
+
with open("settings.json", "w") as f:
|
179
|
+
json.dump(newSettings, f, indent=4)
|
180
|
+
messagebox.showinfo("Success", "Settings saved!")
|
181
|
+
editor.destroy()
|
182
|
+
except Exception as e:
|
183
|
+
messagebox.showerror("Error", f"Invalid JSON:\n{e}")
|
184
|
+
|
185
|
+
saveBtn = ctk.CTkButton(editor, text="Save", command=saveSettings)
|
186
|
+
saveBtn.pack(pady=5)
|
187
|
+
|
188
|
+
def _createUi(self):
|
189
|
+
"""Create UI with console and help tab."""
|
190
|
+
frame = ctk.CTkFrame(self)
|
191
|
+
frame.pack(padx=10, pady=10, fill="both", expand=True)
|
192
|
+
|
193
|
+
# Horizontal frame
|
194
|
+
self.horizFrame = ctk.CTkFrame(frame)
|
195
|
+
self.horizFrame.pack(fill="both", expand=True)
|
196
|
+
|
197
|
+
# Right: Help Tab
|
198
|
+
self.helpTab = HelpTab(self.horizFrame, theme=self.THEME, font=self.FONT, width=500)
|
199
|
+
|
200
|
+
# Left: Console
|
201
|
+
self.consoleFrame = ctk.CTkFrame(self.horizFrame, width=600)
|
202
|
+
self.consoleFrame.pack(side="left", fill="both", expand=True)
|
203
|
+
self.consoleFrame.pack_propagate(False) # prevent shrinking to fit contents
|
204
|
+
|
205
|
+
self.console = InteractiveConsoleText(
|
206
|
+
self.consoleFrame,
|
207
|
+
self.helpTab,
|
208
|
+
userGlobals=self.userGlobals,
|
209
|
+
userLocals=self.userLocals,
|
210
|
+
theme=self.THEME,
|
211
|
+
font=self.FONT,
|
212
|
+
behavior=self.BEHAVIOR,
|
213
|
+
wrap="word",
|
214
|
+
bg=self.THEME["BACKGROUND"],
|
215
|
+
fg=self.THEME["FOREGROUND"],
|
216
|
+
insertbackground=self.THEME["INSERTBACKGROUND"]
|
217
|
+
)
|
218
|
+
self.console.pack(fill="both", expand=True, padx=5, pady=5)
|
219
|
+
self.console.master = self
|
220
|
+
|
221
|
+
|
222
|
+
def _setupOutputRedirect(self):
|
223
|
+
"""Setup stdout/stderr redirection to console."""
|
224
|
+
sys.stdout = StdoutRedirect(self.console.writeOutput)
|
225
|
+
sys.stderr = StdoutRedirect(
|
226
|
+
lambda text, tag: self.console.writeOutput(text, "error")
|
227
|
+
)
|
228
|
+
|
229
|
+
def _setupInputRedirect(self):
|
230
|
+
"""Setup stdin redirection to console."""
|
231
|
+
sys.stdin = StdinRedirect(self.console.readInput)
|
232
|
+
|
233
|
+
def probe(self, *args, **kwargs):
|
234
|
+
"""Start the console main loop."""
|
235
|
+
self.mainloop(*args, **kwargs)
|
236
|
+
|
237
|
+
def probe(userGlobals=None, userLocals=None, callerFrame=None):
|
238
|
+
if callerFrame == None:
|
239
|
+
callerFrame = inspect.currentframe().f_back
|
240
|
+
InteractiveConsole(userGlobals=userGlobals,
|
241
|
+
userLocals=userLocals,
|
242
|
+
callerFrame=callerFrame).probe()
|
243
|
+
|
244
|
+
def _standalone():
|
245
|
+
import pysole
|
246
|
+
pysole.probe(callerFrame=inspect.currentframe().f_back)
|
247
|
+
|
248
|
+
# Example usage
|
249
|
+
if __name__ == "__main__":
|
250
|
+
_standalone()
|
pysole/settings.json
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
{
|
2
|
+
"THEME": {
|
3
|
+
"APPEARANCE": "dark",
|
4
|
+
"LEXER_STYLE": "dracula",
|
5
|
+
"BACKGROUND": "#282a36",
|
6
|
+
"HIGHLIGHTED_BACKGROUND": "#44475a",
|
7
|
+
"FOREGROUND": "#f8f8f2",
|
8
|
+
"HIGHLIGHTED_FOREGROUND": "#ffffff",
|
9
|
+
"INSERTBACKGROUND": "#f8f8f2",
|
10
|
+
"PROMPT": "#50fa7b",
|
11
|
+
"OUTPUT": "#f8f8f2",
|
12
|
+
"ERROR": "#ff5555",
|
13
|
+
"RESULT": "#8be9fd",
|
14
|
+
"SUGGESTION_BOX_BG": "#44475a",
|
15
|
+
"SUGGESTION_BOX_SELECTION_BG": "#6272a4",
|
16
|
+
"FONT": {
|
17
|
+
"FONT": "Consolas",
|
18
|
+
"FONT_SIZE": 12
|
19
|
+
}
|
20
|
+
},
|
21
|
+
"BEHAVIOR": {
|
22
|
+
"DEFAULT_SIZE": "900x600",
|
23
|
+
"PRIMARY_PROMPT": ">>> "
|
24
|
+
}
|
25
|
+
}
|
@@ -4,29 +4,28 @@ from pygments.lexers import PythonLexer
|
|
4
4
|
from pygments.styles import get_style_by_name
|
5
5
|
|
6
6
|
class StyledTextWindow(tk.Text):
|
7
|
-
def __init__(self, master,
|
7
|
+
def __init__(self, master, theme, font, **kwargs):
|
8
8
|
super().__init__(master, **kwargs)
|
9
9
|
|
10
10
|
# Syntax highlighting setup
|
11
11
|
self.lexer = PythonLexer()
|
12
|
-
self.style = get_style_by_name("
|
13
|
-
|
14
|
-
|
15
|
-
self._setupTags()
|
12
|
+
self.style = get_style_by_name(theme["LEXER_STYLE"])
|
13
|
+
|
14
|
+
self._setupTags(theme, font)
|
16
15
|
|
17
|
-
def _setupTags(self):
|
16
|
+
def _setupTags(self, theme, font):
|
18
17
|
"""Configure text tags for different output types."""
|
19
|
-
self.tag_configure("prompt", foreground="
|
20
|
-
self.tag_configure("output", foreground="
|
21
|
-
self.tag_configure("error", foreground="
|
22
|
-
self.tag_configure("result", foreground="
|
18
|
+
self.tag_configure("prompt", foreground=theme["PROMPT"], font=(font["FONT"], font["FONT_SIZE"], "bold"))
|
19
|
+
self.tag_configure("output", foreground=theme["OUTPUT"], font=(font["FONT"], font["FONT_SIZE"]))
|
20
|
+
self.tag_configure("error", foreground=theme["ERROR"], font=(font["FONT"], font["FONT_SIZE"]))
|
21
|
+
self.tag_configure("result", foreground=theme["RESULT"], font=(font["FONT"], font["FONT_SIZE"]))
|
23
22
|
|
24
23
|
# Configure syntax highlighting tags
|
25
24
|
for token, style in self.style:
|
26
25
|
if style["color"]:
|
27
26
|
fg = f"#{style['color']}"
|
28
|
-
|
29
|
-
self.tag_configure(str(token), foreground=fg, font=
|
27
|
+
tagFont = (font["FONT"], font["FONT_SIZE"], "bold" if style["bold"] else "normal")
|
28
|
+
self.tag_configure(str(token), foreground=fg, font=tagFont)
|
30
29
|
|
31
30
|
|
32
31
|
def updateStyling(self, start="1.0"):
|
@@ -5,7 +5,10 @@ import tkinter as tk
|
|
5
5
|
class CodeSuggestionManager:
|
6
6
|
"""Manages code suggestions and autocomplete functionality."""
|
7
7
|
|
8
|
-
def __init__(self, textWidget, userLocals, userGlobals):
|
8
|
+
def __init__(self, textWidget, userLocals, userGlobals, theme, font):
|
9
|
+
self.THEME = theme
|
10
|
+
self.FONT = font
|
11
|
+
|
9
12
|
self.userLocals = userLocals
|
10
13
|
self.userGlobals = userGlobals
|
11
14
|
self.textWidget = textWidget
|
@@ -111,14 +114,14 @@ class CodeSuggestionManager:
|
|
111
114
|
"""Create the suggestion popup window."""
|
112
115
|
self.suggestionWindow = tk.Toplevel(self.textWidget)
|
113
116
|
self.suggestionWindow.wm_overrideredirect(True)
|
114
|
-
self.suggestionWindow.configure(bg="
|
117
|
+
self.suggestionWindow.configure(bg=self.THEME["SUGGESTION_BOX_BG"])
|
115
118
|
|
116
119
|
self.suggestionListbox = tk.Listbox(
|
117
120
|
self.suggestionWindow,
|
118
|
-
bg="
|
119
|
-
fg="
|
120
|
-
selectbackground="
|
121
|
-
font=("
|
121
|
+
bg=self.THEME["SUGGESTION_BOX_BG"],
|
122
|
+
fg=self.THEME["FOREGROUND"],
|
123
|
+
selectbackground=self.THEME["SUGGESTION_BOX_SELECTION_BG"],
|
124
|
+
font=(self.FONT["FONT"], max(2, (self.FONT["FONT_SIZE"]-2))),
|
122
125
|
height=8
|
123
126
|
)
|
124
127
|
self.suggestionListbox.pack()
|
pysole/themes.json
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
{
|
2
|
+
"Dark": {
|
3
|
+
"APPEARANCE": "dark",
|
4
|
+
"LEXER_STYLE": "monokai",
|
5
|
+
"BACKGROUND": "#1e1e1e",
|
6
|
+
"HIGHLIGHTED_BACKGROUND": "#0078d7",
|
7
|
+
"FOREGROUND": "white",
|
8
|
+
"HIGHLIGHTED_FOREGROUND": "black",
|
9
|
+
"INSERTBACKGROUND": "white",
|
10
|
+
"PROMPT": "#00ff00",
|
11
|
+
"OUTPUT": "#ffffff",
|
12
|
+
"ERROR": "#ff0000",
|
13
|
+
"RESULT": "#66ccff",
|
14
|
+
"SUGGESTION_BOX_BG": "#2d2d2d",
|
15
|
+
"SUGGESTION_BOX_SELECTION_BG": "#0066cc",
|
16
|
+
"FONT": {
|
17
|
+
"FONT": "Consolas",
|
18
|
+
"FONT_SIZE": 12
|
19
|
+
}
|
20
|
+
},
|
21
|
+
"Light": {
|
22
|
+
"APPEARANCE": "light",
|
23
|
+
"LEXER_STYLE": "friendly",
|
24
|
+
"BACKGROUND": "#ffffff",
|
25
|
+
"HIGHLIGHTED_BACKGROUND": "#cce6ff",
|
26
|
+
"FOREGROUND": "#000000",
|
27
|
+
"HIGHLIGHTED_FOREGROUND": "#000000",
|
28
|
+
"INSERTBACKGROUND": "#000000",
|
29
|
+
"PROMPT": "#008000",
|
30
|
+
"OUTPUT": "#000000",
|
31
|
+
"ERROR": "#ff0000",
|
32
|
+
"RESULT": "#0066cc",
|
33
|
+
"SUGGESTION_BOX_BG": "#f0f0f0",
|
34
|
+
"SUGGESTION_BOX_SELECTION_BG": "#cce6ff",
|
35
|
+
"FONT": {
|
36
|
+
"FONT": "Consolas",
|
37
|
+
"FONT_SIZE": 12
|
38
|
+
}
|
39
|
+
},
|
40
|
+
"Solarized": {
|
41
|
+
"APPEARANCE": "light",
|
42
|
+
"LEXER_STYLE": "solarized-light",
|
43
|
+
"BACKGROUND": "#fdf6e3",
|
44
|
+
"HIGHLIGHTED_BACKGROUND": "#b58900",
|
45
|
+
"FOREGROUND": "#657b83",
|
46
|
+
"HIGHLIGHTED_FOREGROUND": "#fdf6e3",
|
47
|
+
"INSERTBACKGROUND": "#657b83",
|
48
|
+
"PROMPT": "#268bd2",
|
49
|
+
"OUTPUT": "#586e75",
|
50
|
+
"ERROR": "#dc322f",
|
51
|
+
"RESULT": "#2aa198",
|
52
|
+
"SUGGESTION_BOX_BG": "#eee8d5",
|
53
|
+
"SUGGESTION_BOX_SELECTION_BG": "#b58900",
|
54
|
+
"FONT": {
|
55
|
+
"FONT": "Consolas",
|
56
|
+
"FONT_SIZE": 12
|
57
|
+
}
|
58
|
+
},
|
59
|
+
"Dracula": {
|
60
|
+
"APPEARANCE": "dark",
|
61
|
+
"LEXER_STYLE": "dracula",
|
62
|
+
"BACKGROUND": "#282a36",
|
63
|
+
"HIGHLIGHTED_BACKGROUND": "#44475a",
|
64
|
+
"FOREGROUND": "#f8f8f2",
|
65
|
+
"HIGHLIGHTED_FOREGROUND": "#ffffff",
|
66
|
+
"INSERTBACKGROUND": "#f8f8f2",
|
67
|
+
"PROMPT": "#50fa7b",
|
68
|
+
"OUTPUT": "#f8f8f2",
|
69
|
+
"ERROR": "#ff5555",
|
70
|
+
"RESULT": "#8be9fd",
|
71
|
+
"SUGGESTION_BOX_BG": "#44475a",
|
72
|
+
"SUGGESTION_BOX_SELECTION_BG": "#6272a4",
|
73
|
+
"FONT": {
|
74
|
+
"FONT": "Consolas",
|
75
|
+
"FONT_SIZE": 12
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
pysole/utils.py
ADDED
__init__.py
DELETED
File without changes
|
@@ -1,14 +0,0 @@
|
|
1
|
-
__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
commandHistory.py,sha256=xJtWbJ_vgJo2QGgaZJsApTOi_Hm8Yz0V9_zqQUCItj8,1084
|
3
|
-
helpTab.py,sha256=5Fxj_wTNTbuF6sBIN8GdbE6i_zbAEdlrFnbfhnDk5ms,2360
|
4
|
-
liveConsole.py,sha256=dqYaCvBs_aDfqrbhXYBp5vLkksmAiUv0DWwsbu1R3B4,167
|
5
|
-
mainConsole.py,sha256=MxgJJihgbPtKZ8FqXSFczj5neRVGRiM8RX4lcm0ewk0,12973
|
6
|
-
pysole.py,sha256=bkwnzm2BLZMOiA63cI27FLnXmcOMpDdZVCnfOgP-GoU,4018
|
7
|
-
styledTextbox.py,sha256=pc-7gaq_pGTZGZmtr_ARbPuKlKgJYqzD6HTR7tFhx7k,1989
|
8
|
-
suggestionManager.py,sha256=GRde3c1gFAWt_3rvBoFt_-Xl0aOzzloBMD7MHJA2W8U,6256
|
9
|
-
liveconsole-1.5.3.dist-info/licenses/LICENSE,sha256=7dZ0zL72aGaFE0C9DxacOpnaSkC5jajhG6iL7lqhWmU,1064
|
10
|
-
liveconsole-1.5.3.dist-info/METADATA,sha256=Ad_v6Vo0ZFdUU9WDUftPe7tHVggvslTi94RUeNZbd1k,3936
|
11
|
-
liveconsole-1.5.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
12
|
-
liveconsole-1.5.3.dist-info/entry_points.txt,sha256=qtvuJHcex4QqM97x_UawFWJYnfhQRl0jhqLcWRpnAGo,84
|
13
|
-
liveconsole-1.5.3.dist-info/top_level.txt,sha256=yEmcEVam34LgZbQWtT6RdrdJVzsXwElx6Wpsn3eR2as,95
|
14
|
-
liveconsole-1.5.3.dist-info/RECORD,,
|
pysole.py
DELETED
@@ -1,126 +0,0 @@
|
|
1
|
-
import customtkinter as ctk
|
2
|
-
import inspect
|
3
|
-
import sys
|
4
|
-
import io
|
5
|
-
|
6
|
-
from helpTab import HelpTab
|
7
|
-
from mainConsole import InteractiveConsoleText
|
8
|
-
|
9
|
-
|
10
|
-
class StdoutRedirect(io.StringIO):
|
11
|
-
"""Redirects stdout/stderr to a callback function."""
|
12
|
-
|
13
|
-
def __init__(self, writeCallback):
|
14
|
-
super().__init__()
|
15
|
-
self.writeCallback = writeCallback
|
16
|
-
|
17
|
-
def write(self, s):
|
18
|
-
if s.strip():
|
19
|
-
self.writeCallback(s, "output")
|
20
|
-
|
21
|
-
def flush(self):
|
22
|
-
pass
|
23
|
-
|
24
|
-
class StdinRedirect(io.StringIO):
|
25
|
-
"""Redirects stdin to capture input() from the console."""
|
26
|
-
def __init__(self, readCallback):
|
27
|
-
super().__init__()
|
28
|
-
self.readCallback = readCallback
|
29
|
-
|
30
|
-
def readline(self, *args, **kwargs):
|
31
|
-
return(self.readCallback())
|
32
|
-
|
33
|
-
|
34
|
-
class InteractiveConsole(ctk.CTk):
|
35
|
-
"""Main console window application."""
|
36
|
-
|
37
|
-
def __init__(self, userGlobals=None, userLocals=None, callerFrame=None):
|
38
|
-
super().__init__()
|
39
|
-
|
40
|
-
# Window setup
|
41
|
-
self.title("Live Interactive Console")
|
42
|
-
self.geometry("900x600")
|
43
|
-
|
44
|
-
ctk.set_appearance_mode("dark")
|
45
|
-
ctk.set_default_color_theme("blue")
|
46
|
-
|
47
|
-
# Get namespace from caller if not provided
|
48
|
-
if userGlobals is None or userLocals is None:
|
49
|
-
if callerFrame == None:
|
50
|
-
callerFrame = inspect.currentframe().f_back
|
51
|
-
if userGlobals is None:
|
52
|
-
userGlobals = callerFrame.f_globals
|
53
|
-
if userLocals is None:
|
54
|
-
userLocals = callerFrame.f_locals
|
55
|
-
|
56
|
-
self.userGlobals = userGlobals
|
57
|
-
self.userLocals = userLocals
|
58
|
-
|
59
|
-
# Create UI
|
60
|
-
self._createUi()
|
61
|
-
|
62
|
-
# Redirect stdout/stderr
|
63
|
-
self._setupOutputRedirect()
|
64
|
-
self._setupInputRedirect()
|
65
|
-
|
66
|
-
def _createUi(self):
|
67
|
-
"""Create UI with console and help tab."""
|
68
|
-
frame = ctk.CTkFrame(self)
|
69
|
-
frame.pack(padx=10, pady=10, fill="both", expand=True)
|
70
|
-
|
71
|
-
# Horizontal frame
|
72
|
-
self.horizFrame = ctk.CTkFrame(frame)
|
73
|
-
self.horizFrame.pack(fill="both", expand=True)
|
74
|
-
|
75
|
-
# Right: Help Tab
|
76
|
-
self.helpTab = HelpTab(self.horizFrame, width=500)
|
77
|
-
|
78
|
-
# Left: Console
|
79
|
-
self.consoleFrame = ctk.CTkFrame(self.horizFrame, width=600)
|
80
|
-
self.consoleFrame.pack(side="left", fill="both", expand=True)
|
81
|
-
self.consoleFrame.pack_propagate(False) # prevent shrinking to fit contents
|
82
|
-
|
83
|
-
self.console = InteractiveConsoleText(
|
84
|
-
self.consoleFrame,
|
85
|
-
self.helpTab,
|
86
|
-
userGlobals=self.userGlobals,
|
87
|
-
userLocals=self.userLocals,
|
88
|
-
wrap="word",
|
89
|
-
bg="#1e1e1e",
|
90
|
-
fg="white",
|
91
|
-
insertbackground="white",
|
92
|
-
font=("Consolas", 12)
|
93
|
-
)
|
94
|
-
self.console.pack(fill="both", expand=True, padx=5, pady=5)
|
95
|
-
self.console.master = self
|
96
|
-
|
97
|
-
|
98
|
-
def _setupOutputRedirect(self):
|
99
|
-
"""Setup stdout/stderr redirection to console."""
|
100
|
-
sys.stdout = StdoutRedirect(self.console.writeOutput)
|
101
|
-
sys.stderr = StdoutRedirect(
|
102
|
-
lambda text, tag: self.console.writeOutput(text, "error")
|
103
|
-
)
|
104
|
-
|
105
|
-
def _setupInputRedirect(self):
|
106
|
-
"""Setup stdin redirection to console."""
|
107
|
-
sys.stdin = StdinRedirect(self.console.readInput)
|
108
|
-
|
109
|
-
def probe(self, *args, **kwargs):
|
110
|
-
"""Start the console main loop."""
|
111
|
-
self.mainloop(*args, **kwargs)
|
112
|
-
|
113
|
-
def probe(userGlobals=None, userLocals=None, callerFrame=None):
|
114
|
-
if callerFrame == None:
|
115
|
-
callerFrame = inspect.currentframe().f_back
|
116
|
-
InteractiveConsole(userGlobals=userGlobals,
|
117
|
-
userLocals=userLocals,
|
118
|
-
callerFrame=callerFrame).probe()
|
119
|
-
|
120
|
-
def _standalone():
|
121
|
-
import pysole
|
122
|
-
pysole.probe(callerFrame=inspect.currentframe().f_back)
|
123
|
-
|
124
|
-
# Example usage
|
125
|
-
if __name__ == "__main__":
|
126
|
-
_standalone()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|