#!/usr/bin/env -S uv run python3 import tkinter as tk from tkinter import N, S, E, W, ttk import textwrap # Complex transforms go here, # Simple transforms are just lambdas def _wordcount(s: str): "Returns a tuple of linecount, wordcount, charcount" return (len(s.splitlines()), len(s.split()), len(s)) def _markdown_quote(s: str): "Returns every line prefixed with >" return "\n".join(["> " + line for line in s.splitlines()]) transforms = [ # Transforms go here, for example {"name": "One line", "transform": lambda x: " ".join(x.splitlines())} ,{"name": "Line/Word/Char", "transform": _wordcount} ,{"name": "Dedent", "transform": textwrap.dedent} ,{"name": "Markdown Quote", "transform": _markdown_quote} ] class GUI(): def __init__(self) -> None: self.root = tk.Tk() self.active_transform = lambda x: x # start with no transform self.layout_gui() def layout_gui(self) -> None: self.mainframe = ttk.Frame(self.root, padding="3 3 12 12") self.root.title("Text Tools") self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1) self.content_box = tk.Text(self.mainframe, undo=True) self.content_box.grid(column=2, row=2) self.output_box = tk.Text(self.mainframe, undo=True) self.output_box.grid(column=2, row=3) self.transform_box = tk.Listbox(self.mainframe) self.transform_box.grid(column=1, row=2, rowspan=2) for t in transforms: self.transform_box.insert(tk.END, t["name"]) # Keyboard bindings self.root.bind("", lambda _: self.root.quit()) self.content_box.bind("", lambda _: self.transform_box.focus()) # vvv makes clicking or pressing enter change the transform self.transform_box.bind("", self.select_transform) self.transform_box.bind("", self.select_transform) # vvv makes anything typed in update the output self.content_box.bind("", lambda _: self.root.after(1, self.update)) self.content_box.focus() def update(self): content = self.content_box.get('1.0', tk.END) content = self.active_transform(content) self.output_box.delete('1.0', tk.END) self.output_box.insert('1.0', content) def select_transform(self, _): try: selection = self.transform_box.curselection()[0] self.active_transform = transforms[selection]["transform"] self.update() except (ValueError, IndexError): pass def main(): # Entry point for pyproject.toml gui = GUI() gui.root.mainloop() if __name__ == "__main__": main()