Skip to content

Instantly share code, notes, and snippets.

@Ismael-VC
Created November 3, 2025 06:25
Show Gist options
  • Save Ismael-VC/e860e94d885bc25e3f300ea649d2dfa8 to your computer and use it in GitHub Desktop.
Save Ismael-VC/e860e94d885bc25e3f300ea649d2dfa8 to your computer and use it in GitHub Desktop.
Reflector
#!/usr/bin/env python
import sys
from code import InteractiveConsole
from io import StringIO
from rich.syntax import Syntax
from textual.app import App, ComposeResult, RenderResult
from textual.containers import Container, Horizontal
from textual.widget import Widget
from textual.widgets import Footer, Header, RichLog, TextArea
class Reflector(Widget):
CSS = """
#input {
padding: 1 2 1 2;
border: none;
}
#output {
padding: 1 2 1 2;
}
#input-container {
border: solid;
height: 0.4fr;
margin: 0 2 1 2;
}
#output-container {
border: solid;
margin: 1 2 1 2;
height: 0.6fr;
}
"""
BINDINGS = [
("ctrl+enter,ctrl+r", "eval", "eval"),
("ctrl+n", "dir", "namespace"),
("ctrl+l", "clear_output", "clear"),
("ctrl+u", "clear_input", "clear input"),
]
def render(self) -> RenderResult:
self.header = Header(id="header", icon="🐍")
self.input = TextArea.code_editor(id="input", language="python")
self.input_container = Container(
self.input, id="input-container", placeholder="^⏎ to evaluate"
)
self.output = RichLog(id="output", markup=True, highlight=True)
self.output_container = Container(self.output, id="output-container")
self.footer = Footer(id="footer")
yield self.header
yield self.output_container
yield self.input_container
yield self.footer
def on_mount(self) -> None:
self.title = "Reflector"
self.sub_title = self.app.title
self.footer.show_command_palette = False
self.input_container.border_title = "Input"
self.output_container.border_title = "Output"
self.namespace = {"app": self, "__builtins__": __builtins__}
self.repl = InteractiveConsole(locals=self.namespace)
self.input.focus()
def action_dir(self) -> None:
self.action_eval("dir()")
def action_clear_output(self) -> None:
self.output.clear()
def action_clear_input(self) -> None:
self.input.text = ""
def action_eval(self, code="") -> None:
if not code:
code = self.input.text
if not code:
return
split_code = code.split("\n")
self.output.write(Syntax(f">>> {split_code[0]}", "python", indent_guides=True))
if len(split_code) > 1:
for line in split_code[1:]:
self.output.write(Syntax(f"... {line}", "python", indent_guides=True))
self.output.write(Syntax("... ", "python", indent_guides=True))
old_stdout, old_stderr = sys.stdout, sys.stderr
sys.stdout, sys.stderr = StringIO(), StringIO()
self.repl.push(code + "\n")
captured_output = sys.stdout.getvalue().strip()
captured_error = sys.stderr.getvalue().strip()
if captured_output:
self.output.write(Syntax(captured_output, "python", indent_guides=True))
if captured_error:
self.output.write(Syntax(captured_error, "python", indent_guides=True))
sys.stdout, sys.stderr = old_stdout, old_stderr
self.input.text = ""
self.input.focus()
class TerminalApp(App):
def compose(self):
yield Reflector()
if __name__ == "__main__":
app = TerminalApp()
app.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment