liveConsole 1.3.2__py3-none-any.whl → 1.4.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.
liveConsole.py CHANGED
@@ -1,15 +1,10 @@
1
1
  import customtkinter as ctk
2
- import tkinter as tk
3
- import traceback
4
2
  import inspect
5
- import threading
6
3
  import sys
7
4
  import io
8
- import pygments
9
- from pygments.lexers import PythonLexer
10
- from pygments.styles import get_style_by_name
11
- import keyword
12
- import builtins
5
+
6
+ from helpTab import HelpTab
7
+ from mainConsole import InteractiveConsoleText
13
8
 
14
9
 
15
10
  class StdoutRedirect(io.StringIO):
@@ -26,554 +21,14 @@ class StdoutRedirect(io.StringIO):
26
21
  def flush(self):
27
22
  pass
28
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
29
 
30
- class CodeSuggestionManager:
31
- """Manages code suggestions and autocomplete functionality."""
32
-
33
- def __init__(self, textWidget, userLocals, userGlobals):
34
- self.userLocals = userLocals
35
- self.userGlobals = userGlobals
36
- self.textWidget = textWidget
37
- self.suggestionWindow = None
38
- self.suggestionListbox = None
39
- self.suggestions = []
40
- self.selectedSuggestion = 0
41
-
42
- # Build suggestion sources
43
- self.keywords = keyword.kwlist
44
- self.builtins = [name for name in dir(builtins) if not name.startswith('_')]
45
-
46
- def getCurrentWord(self):
47
- """Extract the word being typed at cursor position and suggest dir() if applicable."""
48
- suggestions = []
49
- cursorPos = self.textWidget.index(tk.INSERT)
50
- lineStart = self.textWidget.index(f"{cursorPos} linestart")
51
- currentLine = self.textWidget.get(lineStart, cursorPos)
52
-
53
- # Find the current word
54
- words = currentLine.split()
55
- if not words:
56
- return "", suggestions
57
-
58
- currentWord = words[-1]
59
-
60
-
61
- # If the word contains a dot, try to evaluate the base and get its dir()
62
- if '.' in currentWord:
63
- try:
64
- base_expr = '.'.join(currentWord.split('.')[:-1])
65
- obj = eval(base_expr, self.userLocals, self.userGlobals)
66
- suggestions = dir(obj)
67
- except:
68
- pass
69
- for char in "([{,.":
70
- if char in currentWord:
71
- currentWord = currentWord.split(char)[-1]
72
-
73
- return currentWord, suggestions
74
-
75
- def getSuggestions(self, partialWord, suggestions=[]):
76
- """Get code suggestions for partial word."""
77
- if len(partialWord) < 2:
78
- return suggestions
79
-
80
- # print(partialWord)
81
- if suggestions != []:
82
- suggestions = [suggestion for suggestion in suggestions if suggestion.lower().startswith(partialWord.lower())]
83
- else:
84
- # Add matching keywords
85
- for kw in self.keywords:
86
- if kw.startswith(partialWord.lower()):
87
- suggestions.append(kw)
88
-
89
- # Add matching builtins
90
- for builtin in self.builtins:
91
- if builtin.startswith(partialWord):
92
- suggestions.append(builtin)
93
-
94
- # Add matching variables from namespace
95
- master = self.textWidget.master
96
- if hasattr(master, 'userLocals'):
97
- for var in master.userLocals:
98
- if var.startswith(partialWord) and not var.startswith('_'):
99
- suggestions.append(var)
100
-
101
- if hasattr(master, 'userGlobals'):
102
- for var in master.userGlobals:
103
- if var.startswith(partialWord) and not var.startswith('_'):
104
- suggestions.append(var)
105
-
106
- # Remove duplicates and sort
107
- return sorted(list(set(suggestions)))
108
-
109
- def showSuggestions(self):
110
- """Display the suggestions popup."""
111
- currentWord, extraSuggestions = self.getCurrentWord()
112
- suggestions = self.getSuggestions(currentWord, extraSuggestions)
113
-
114
- if not suggestions:
115
- self.hideSuggestions()
116
- return
117
-
118
- self.suggestions = suggestions
119
- self.selectedSuggestion = 0
120
-
121
- # Create suggestion window if needed
122
- if not self.suggestionWindow:
123
- self._createSuggestionWindow()
124
-
125
- # Update listbox content
126
- self.suggestionListbox.delete(0, tk.END)
127
- for suggestion in suggestions:
128
- self.suggestionListbox.insert(tk.END, suggestion)
129
-
130
- self.suggestionListbox.selection_set(0)
131
-
132
- # Position window near cursor
133
- self._positionSuggestionWindow()
134
- self.suggestionWindow.deiconify()
135
-
136
- def _createSuggestionWindow(self):
137
- """Create the suggestion popup window."""
138
- self.suggestionWindow = tk.Toplevel(self.textWidget)
139
- self.suggestionWindow.wm_overrideredirect(True)
140
- self.suggestionWindow.configure(bg="#2d2d2d")
141
-
142
- self.suggestionListbox = tk.Listbox(
143
- self.suggestionWindow,
144
- bg="#2d2d2d",
145
- fg="white",
146
- selectbackground="#0066cc",
147
- font=("Consolas", 10),
148
- height=8
149
- )
150
- self.suggestionListbox.pack()
151
-
152
- def _positionSuggestionWindow(self):
153
- """Position the suggestion window near the cursor."""
154
- cursorPos = self.textWidget.index(tk.INSERT)
155
- x, y, _, _ = self.textWidget.bbox(cursorPos)
156
- x += self.textWidget.winfo_rootx()
157
- y += self.textWidget.winfo_rooty() + 20
158
- self.suggestionWindow.geometry(f"+{x}+{y}")
159
-
160
- def hideSuggestions(self):
161
- """Hide the suggestions popup."""
162
- if self.suggestionWindow:
163
- self.suggestionWindow.withdraw()
164
-
165
- def applySuggestion(self, suggestion=None):
166
- """Apply the selected suggestion at cursor position."""
167
- if not suggestion and self.suggestions:
168
- suggestion = self.suggestions[self.selectedSuggestion]
169
- if not suggestion:
170
- return
171
-
172
- currentWord, _ = self.getCurrentWord()
173
- # Only insert the missing part
174
- missingPart = suggestion[len(currentWord):]
175
- cursorPos = self.textWidget.index(tk.INSERT)
176
- self.textWidget.insert(cursorPos, missingPart)
177
-
178
- self.hideSuggestions()
179
-
180
- def handleNavigation(self, direction):
181
- """Handle up/down navigation in suggestions."""
182
- if not self.suggestions:
183
- return
184
-
185
- if direction == "down":
186
- self.selectedSuggestion = min(self.selectedSuggestion + 1, len(self.suggestions) - 1)
187
- else: # up
188
- self.selectedSuggestion = max(self.selectedSuggestion - 1, 0)
189
-
190
- self.suggestionListbox.selection_clear(0, tk.END)
191
- self.suggestionListbox.selection_set(self.selectedSuggestion)
192
-
193
-
194
- class CommandHistory:
195
- """Manages command history and navigation."""
196
-
197
- def __init__(self):
198
- self.history = []
199
- self.index = -1
200
- self.tempCommand = ""
201
-
202
- def add(self, command):
203
- """Add a command to history."""
204
- if command.strip():
205
- self.history.append(command)
206
- self.index = len(self.history)
207
-
208
- def navigateUp(self):
209
- """Get previous command from history."""
210
- if self.index > 0:
211
- self.index -= 1
212
- return self.history[self.index]
213
- return None
214
-
215
- def navigateDown(self):
216
- """Get next command from history."""
217
- if self.index < len(self.history) - 1:
218
- self.index += 1
219
- return self.history[self.index]
220
- elif self.index == len(self.history) - 1:
221
- self.index = len(self.history)
222
- return self.tempCommand
223
- return None
224
-
225
- def setTemp(self, command):
226
- """Store temporary command while navigating history."""
227
- self.tempCommand = command
228
-
229
-
230
- class InteractiveConsoleText(tk.Text):
231
- """A tk.Text widget with Python syntax highlighting for interactive console."""
232
-
233
- PROMPT = ">>> "
234
- PROMPT_LENGTH = 4
235
-
236
- def __init__(self, master, userLocals=None, userGlobals=None, **kwargs):
237
- super().__init__(master, **kwargs)
238
-
239
- # Initialize components
240
- self.suggestionManager = CodeSuggestionManager(self, userLocals=userLocals, userGlobals=userGlobals)
241
-
242
- self.navigatingHistory = False
243
- self.history = CommandHistory()
244
-
245
- # Syntax highlighting setup
246
- self.lexer = PythonLexer()
247
- self.style = get_style_by_name("monokai")
248
-
249
- # Track current command
250
- self.currentCommandLine = 1
251
- self.isExecuting = False
252
-
253
- # Setup tags and bindings
254
- self._setupTags()
255
- self._setupBindings()
256
-
257
- # Initialize with first prompt
258
- self.addPrompt()
259
-
260
- def _setupTags(self):
261
- """Configure text tags for different output types."""
262
- self.tag_configure("prompt", foreground="#00ff00", font=("Consolas", 12, "bold"))
263
- self.tag_configure("output", foreground="#ffffff", font=("Consolas", 12))
264
- self.tag_configure("error", foreground="#ff6666", font=("Consolas", 12))
265
- self.tag_configure("result", foreground="#66ccff", font=("Consolas", 12))
266
-
267
- # Configure syntax highlighting tags
268
- for token, style in self.style:
269
- if style["color"]:
270
- fg = f"#{style['color']}"
271
- font = ("Consolas", 12, "bold" if style["bold"] else "normal")
272
- self.tag_configure(str(token), foreground=fg, font=font)
273
-
274
- def _setupBindings(self):
275
- """Setup all key and mouse bindings."""
276
- self.bind("<Return>", self.onEnter)
277
- self.bind("<Shift-Return>", self.onShiftEnter)
278
- self.bind("<Control-c>", self.cancel)
279
- self.bind("<Tab>", self.onTab)
280
- self.bind("<BackSpace>", self.onBackspace)
281
- self.bind("<KeyRelease>", self.onKeyRelease)
282
- self.bind("<KeyPress>", self.onKeyPress)
283
- self.bind("<Button-1>", self.onClick)
284
- self.bind("<Up>", self.onUp)
285
- self.bind("<Down>", self.onDown)
286
-
287
- def getCurrentLineNumber(self):
288
- """Get the line number where current command starts."""
289
- return int(self.index("end-1c").split(".")[0])
290
-
291
- def getPromptPosition(self):
292
- """Get the position right after the prompt on current command line."""
293
- return f"{self.currentCommandLine}.{self.PROMPT_LENGTH}"
294
-
295
- def getCommandStartPosition(self):
296
- """Get the starting position of the current command."""
297
- return f"{self.currentCommandLine}.0"
298
-
299
- def getCurrentCommand(self):
300
- """Extract the current command text (without prompt)."""
301
- if self.isExecuting:
302
- return ""
303
-
304
- start = self.getPromptPosition()
305
- end = "end-1c"
306
- return self.get(start, end)
307
-
308
- def replaceCurrentCommand(self, newCommand):
309
- """Replace the current command with new text."""
310
- if self.isExecuting:
311
- return
312
-
313
- start = self.getPromptPosition()
314
- end = "end-1c"
315
-
316
- self.delete(start, end)
317
- self.insert(start, newCommand)
318
- self.see("end")
319
-
320
- def isCursorInEditableArea(self):
321
- """Check if cursor is in the editable command area."""
322
- if self.isExecuting:
323
- return False
324
-
325
- cursorLine = int(self.index("insert").split(".")[0])
326
- cursorCol = int(self.index("insert").split(".")[1])
327
-
328
- return (cursorLine >= self.currentCommandLine and
329
- (cursorLine > self.currentCommandLine or cursorCol >= self.PROMPT_LENGTH))
330
-
331
- def onEnter(self, event):
332
- """Handle Enter key - execute command."""
333
- self.suggestionManager.hideSuggestions()
334
-
335
- if self.isExecuting:
336
- return "break"
337
-
338
- command = self.getCurrentCommand()
339
-
340
- if not command.strip():
341
- return "break"
342
-
343
- # Check if statement is incomplete
344
- if self.isIncompleteStatement(command):
345
- return self.onShiftEnter(event)
346
-
347
- # Execute the command
348
- self.history.add(command)
349
- self.mark_set("insert", "end")
350
- self.insert("end", "\n")
351
- self.see("end")
352
-
353
- # Execute in thread
354
- self.isExecuting = True
355
- threading.Thread(
356
- target=self.executeCommandThreaded,
357
- args=(command,),
358
- daemon=True
359
- ).start()
360
-
361
- return "break"
362
-
363
- def onShiftEnter(self, event):
364
- """Handle Shift+Enter - new line with auto-indent."""
365
- self.suggestionManager.hideSuggestions()
366
-
367
- if self.isExecuting:
368
- return "break"
369
-
370
- # Get current line for indent calculation
371
- cursorPos = self.index("insert")
372
- lineStart = self.index(f"{cursorPos} linestart")
373
- lineEnd = self.index(f"{cursorPos} lineend")
374
- currentLine = self.get(lineStart, lineEnd)
375
-
376
- # Calculate indentation
377
- indent = self.calculateIndent(currentLine)
378
-
379
- # Insert newline with indent
380
- self.insert("insert", "\n" + " " * indent)
381
- self.see("end")
382
-
383
- return "break"
384
-
385
- def onTab(self, event):
386
- """Handle Tab key for autocompletion."""
387
- if self.isExecuting:
388
- return "break"
389
-
390
- if self.suggestionManager.suggestionWindow and \
391
- self.suggestionManager.suggestionWindow.winfo_viewable():
392
- self.suggestionManager.applySuggestion()
393
- else:
394
- self.suggestionManager.showSuggestions()
395
-
396
- return "break"
397
-
398
- def onBackspace(self, event):
399
- """Prevent backspace from deleting the prompt."""
400
- if not self.isCursorInEditableArea():
401
- return "break"
402
-
403
- # Check if we're at the prompt boundary
404
- cursorPos = self.index("insert")
405
- promptPos = self.getPromptPosition()
406
-
407
- if self.compare(cursorPos, "<=", promptPos):
408
- return "break"
409
-
410
- def onClick(self, event):
411
- """Handle mouse clicks - prevent clicking before prompt."""
412
- self.suggestionManager.hideSuggestions()
413
- return None
414
-
415
- def onKeyPress(self, event):
416
- """Handle key press events."""
417
- # print(event.keysym)
418
- if self.suggestionManager.suggestionWindow and \
419
- self.suggestionManager.suggestionWindow.winfo_viewable():
420
- if event.keysym == "Escape":
421
- self.suggestionManager.hideSuggestions()
422
- return "break"
423
-
424
- # Prevent editing outside command area
425
- if not event.keysym in ["Shift_L", "Shift_R", "Control_L", "Control_R"]:
426
- self.navigatingHistory = False
427
- if not self.isCursorInEditableArea():
428
- self.mark_set("insert", "end")
429
-
430
- if event.keysym in ["Left", "Right"]:
431
- if self.index("insert") == self.getPromptPosition():
432
- self.mark_set("insert", "1.4")
433
- return "break"
434
-
435
- def onKeyRelease(self, event):
436
- """Handle key release events."""
437
- if event.keysym in ["Return", "Escape", "Left", "Right", "Home", "End"]:
438
- self.suggestionManager.hideSuggestions()
439
- elif event.keysym not in ["Up", "Down", "Shift_L", "Shift_R", "Control_L", "Control_R"]:
440
- if not self.isExecuting:
441
- self.after_idle(self.suggestionManager.showSuggestions)
442
- self.after_idle(self.highlightCurrentCommand)
443
-
444
- def cancel(self, event):
445
- self.history.add(self.getCurrentCommand())
446
- self.replaceCurrentCommand("")
447
-
448
- def historyReplace(self, command):
449
- if self.getCurrentCommand() == "" or self.navigatingHistory:
450
- if self.isExecuting:
451
- return "break"
452
-
453
- if self.history.index == len(self.history.history):
454
- self.history.setTemp(self.getCurrentCommand())
455
-
456
- if command is not None:
457
- self.replaceCurrentCommand(command)
458
- self.navigatingHistory = True
459
- return("break")
460
-
461
- def onUp(self, event):
462
- if self.suggestionManager.suggestionWindow and \
463
- self.suggestionManager.suggestionWindow.winfo_viewable():
464
- if event.keysym == "Up":
465
- self.suggestionManager.handleNavigation("up")
466
- return "break"
467
- command = self.history.navigateUp()
468
- return(self.historyReplace(command))
469
- # self.mark_set("insert", "insert -1 line")
470
-
471
- def onDown(self, event):
472
- if self.suggestionManager.suggestionWindow and \
473
- self.suggestionManager.suggestionWindow.winfo_viewable():
474
- if event.keysym == "Down":
475
- self.suggestionManager.handleNavigation("down")
476
- return "break"
477
- command = self.history.navigateDown()
478
- return(self.historyReplace(command))
479
-
480
- def isIncompleteStatement(self, code):
481
- """Check if the code is an incomplete statement."""
482
- lines = code.split("\n")
483
- if not lines[-1].strip():
484
- return False
485
-
486
- # Check for line ending with colon
487
- for line in lines:
488
- if line.strip().endswith(":"):
489
- return True
490
-
491
- return False
492
-
493
- def calculateIndent(self, line):
494
- """Calculate the indentation level for the next line."""
495
- currentIndent = len(line) - len(line.lstrip())
496
-
497
- # If line ends with colon, increase indent
498
- if line.strip().endswith(":"):
499
- return currentIndent + 4
500
-
501
- return currentIndent
502
-
503
- def highlightCurrentCommand(self):
504
- """Apply syntax highlighting to the current command."""
505
- if self.isExecuting:
506
- return
507
-
508
- # Clear existing highlighting
509
- start = self.getPromptPosition()
510
- end = "end-1c"
511
-
512
- for token, _ in self.style:
513
- self.tag_remove(str(token), start, end)
514
-
515
- # Get and highlight the command
516
- command = self.getCurrentCommand()
517
- if not command:
518
- return
519
-
520
- self.mark_set("highlight_pos", start)
521
-
522
- for token, content in pygments.lex(command, self.lexer):
523
- if content:
524
- endPos = f"highlight_pos + {len(content)}c"
525
- if content.strip(): # Only highlight non-whitespace
526
- self.tag_add(str(token), "highlight_pos", endPos)
527
- self.mark_set("highlight_pos", endPos)
528
-
529
- def writeOutput(self, text, tag="output"):
530
- """Write output to the console (thread-safe)."""
531
- def _write():
532
- self.insert("end", text + "\n", tag)
533
- self.see("end")
534
-
535
- self.after(0, _write)
536
-
537
- def addPrompt(self):
538
- """Add a new command prompt."""
539
- def _add():
540
- # Store the line number for the new command
541
- self.currentCommandLine = self.getCurrentLineNumber()
542
-
543
- # Insert prompt
544
- self.insert("end", self.PROMPT)
545
- promptStart = f"{self.currentCommandLine}.0"
546
- promptEnd = f"{self.currentCommandLine}.{self.PROMPT_LENGTH}"
547
- self.tag_add("prompt", promptStart, promptEnd)
548
-
549
- self.mark_set("insert", "end")
550
- self.see("end")
551
- self.isExecuting = False
552
-
553
- if self.isExecuting:
554
- self.after(0, _add)
555
- else:
556
- _add()
557
-
558
- def executeCommandThreaded(self, command):
559
- """Execute a command in a separate thread."""
560
- try:
561
- # Try eval first for expressions
562
- result = eval(command, self.master.userGlobals, self.master.userLocals)
563
- if result is not None:
564
- self.writeOutput(str(result), "result")
565
- self.master.userLocals["_"] = result
566
- except SyntaxError:
567
- try:
568
- # Try exec for statements
569
- exec(command, self.master.userGlobals, self.master.userLocals)
570
- except Exception:
571
- self.writeOutput(traceback.format_exc(), "error")
572
- except Exception:
573
- self.writeOutput(traceback.format_exc(), "error")
574
-
575
- # Add new prompt after execution
576
- self.addPrompt()
30
+ def readline(self, *args, **kwargs):
31
+ return(self.readCallback())
577
32
 
578
33
 
579
34
  class InteractiveConsole(ctk.CTk):
@@ -605,16 +60,28 @@ class InteractiveConsole(ctk.CTk):
605
60
 
606
61
  # Redirect stdout/stderr
607
62
  self._setupOutputRedirect()
63
+ self._setupInputRedirect()
608
64
 
609
65
  def _createUi(self):
610
- """Create the user interface."""
611
- # Main frame
66
+ """Create UI with console and help tab."""
612
67
  frame = ctk.CTkFrame(self)
613
68
  frame.pack(padx=10, pady=10, fill="both", expand=True)
614
-
615
- # Console text widget
69
+
70
+ # Horizontal frame
71
+ self.horizFrame = ctk.CTkFrame(frame)
72
+ self.horizFrame.pack(fill="both", expand=True)
73
+
74
+ # Right: Help Tab
75
+ self.helpTab = HelpTab(self.horizFrame, width=500)
76
+
77
+ # Left: Console
78
+ self.consoleFrame = ctk.CTkFrame(self.horizFrame, width=600)
79
+ self.consoleFrame.pack(side="left", fill="both", expand=True)
80
+ self.consoleFrame.pack_propagate(False) # prevent shrinking to fit contents
81
+
616
82
  self.console = InteractiveConsoleText(
617
- frame,
83
+ self.consoleFrame,
84
+ self.helpTab,
618
85
  userGlobals=self.userGlobals,
619
86
  userLocals=self.userLocals,
620
87
  wrap="word",
@@ -624,9 +91,8 @@ class InteractiveConsole(ctk.CTk):
624
91
  font=("Consolas", 12)
625
92
  )
626
93
  self.console.pack(fill="both", expand=True, padx=5, pady=5)
627
-
628
- # Give console access to namespace
629
94
  self.console.master = self
95
+
630
96
 
631
97
  def _setupOutputRedirect(self):
632
98
  """Setup stdout/stderr redirection to console."""
@@ -634,6 +100,10 @@ class InteractiveConsole(ctk.CTk):
634
100
  sys.stderr = StdoutRedirect(
635
101
  lambda text, tag: self.console.writeOutput(text, "error")
636
102
  )
103
+
104
+ def _setupInputRedirect(self):
105
+ """Setup stdin redirection to console."""
106
+ sys.stdin = StdinRedirect(self.console.readInput)
637
107
 
638
108
  def probe(self, *args, **kwargs):
639
109
  """Start the console main loop."""
@@ -647,7 +117,7 @@ if __name__ == "__main__":
647
117
 
648
118
  def greet(name):
649
119
  print(f"Hello {name}!")
650
- return f"Greeted {name}"
120
+ return(f"Greeted {name}")
651
121
 
652
122
  # Create the list for testing autocomplete
653
123
  exampleList = [1, 2, 3, 4, 5]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: liveConsole
3
- Version: 1.3.2
3
+ Version: 1.4.1
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
@@ -29,6 +29,9 @@ A fully-featured, **live Python console GUI** built with **CustomTkinter** and *
29
29
 
30
30
  * Clickable history of previous commands
31
31
 
32
+ * **Integrated Help Panel** for quick access to Python object documentation
33
+
34
+
32
35
  ## Installation
33
36
 
34
37
  `pip install liveConsole`
@@ -77,6 +80,17 @@ A fully-featured, **live Python console GUI** built with **CustomTkinter** and *
77
80
  * Click to copy them back to the prompt for editing or re-execution.
78
81
 
79
82
 
83
+ ### Help Panel
84
+
85
+ * A resizable right-hand panel that displays Python documentation (help()) for any object.
86
+
87
+ * Opens when clicking ctrl+click on a function/method and can be closed with the "X" button.
88
+
89
+ * Scrollable and syntax-styled.
90
+
91
+ * Perfect for quick reference without leaving the console.
92
+
93
+
80
94
  ### Easy Integration
81
95
 
82
96
  * Automatically grabs **caller frame globals and locals** if not provided.
@@ -0,0 +1,12 @@
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=9Gwevh3q52Qirig9VJ_qnbTuCda2ydyZnUR29kl7Zlw,3839
5
+ mainConsole.py,sha256=MxgJJihgbPtKZ8FqXSFczj5neRVGRiM8RX4lcm0ewk0,12973
6
+ styledTextbox.py,sha256=pc-7gaq_pGTZGZmtr_ARbPuKlKgJYqzD6HTR7tFhx7k,1989
7
+ suggestionManager.py,sha256=GRde3c1gFAWt_3rvBoFt_-Xl0aOzzloBMD7MHJA2W8U,6256
8
+ liveconsole-1.4.1.dist-info/licenses/LICENSE,sha256=7dZ0zL72aGaFE0C9DxacOpnaSkC5jajhG6iL7lqhWmU,1064
9
+ liveconsole-1.4.1.dist-info/METADATA,sha256=fg47oHebYtfDRKCZaFvYOaSHqCTxzTYYWWq87W5_WO0,3648
10
+ liveconsole-1.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ liveconsole-1.4.1.dist-info/top_level.txt,sha256=QkTpA-HOQwPQRHYwLKYJOhu_zwarar6tdVSuPwYPvl8,88
12
+ liveconsole-1.4.1.dist-info/RECORD,,
@@ -0,0 +1,7 @@
1
+ __init__
2
+ commandHistory
3
+ helpTab
4
+ liveConsole
5
+ mainConsole
6
+ styledTextbox
7
+ suggestionManager