from textual_window import Window from textual.widgets import RichLog from textual import work from lupa import LuaRuntime, lua51 import os, json, asyncio class PluginLoader(Window): def __init__(self): super().__init__( id="Plugin Loader", mode="permanent", icon="⚙️", starting_horizontal="right", starting_vertical="bottom", start_open=True ) @work async def find_plugins(self): log = self.query_one(RichLog) log.write("[b]Setting up LUA runtime..[/]") self.lua_runtime = lua51.LuaRuntime() lua_runtime_stuff = { "ui": { "notify": self.notify } } log.write("[b]Finding plugins...[/]") # Find all plugins (they're just in the plugins folder for now) folders = [ os.path.join(os.path.dirname(__file__), "plugins") ] # path to the folder of all correctly formatted plugins plugin_paths = [] for folder in folders: log.write(f"Searching {folder}...") plugin_folders = os.listdir(folder) for plugin_folder in plugin_folders: plugin_folder = os.path.join(folder, plugin_folder) if not os.path.isdir(plugin_folder): log.write(f"[d]Ignoring {plugin_folder} because it is not a folder.[/]") continue if not os.path.isdir(os.path.join(plugin_folder, "lua")): log.write(f"[d]Ignoring {plugin_folder} because it has no \"lua\" folder.[/]") continue if not os.path.isfile(os.path.join(plugin_folder, "plugin.json")): log.write(f"[d]Ignoring {plugin_folder} because it has no plugin.json file.[/]") continue with open(os.path.join(plugin_folder, "plugin.json"), "r") as f: try: plugin_json = json.loads(f.read()) plugin_json["name"] plugin_json["version"] plugin_json["author"] plugin_json["dependencies"] except UnicodeDecodeError: log.write(f"[d]Ignoring {plugin_folder} because its plugin.json file is unreadable.[/]") continue except json.JSONDecodeError: log.write(f"[d]Ignoring {plugin_folder} because its plugin.json file is malformed.[/]") continue except KeyError as e: log.write(f"[d]Ignoring {plugin_folder} because its plugin.json file is missing the field {e}.") continue except Exception as e: log.write(f"[d]Ignoring {plugin_folder} because of error: {e}.[/]") continue log.write(f"[b green]FOUND[/] {plugin_json['name']} ({plugin_json['version']})") for lua_file in os.listdir(os.path.join(plugin_folder, "lua")): lua_file_path = os.path.join(plugin_folder, f"lua/{lua_file}") with open(lua_file_path, "r") as f: code = f.read() sandbox = self.lua_runtime.eval("{}") setfenv = self.lua_runtime.eval("setfenv") sandbox.print = self.log #self.lua_runtime.globals().print sandbox.math = self.lua_runtime.globals().math sandbox.string = self.lua_runtime.globals().string sandbox.berry = lua_runtime_stuff setfenv(0, sandbox) executed_code = self.lua_runtime.execute(code) if executed_code.init: executed_code.init() if executed_code.main: self.run_worker(thread=True, work=executed_code.main) plugin_paths.append(plugin_folder) log.write("\n[d]Window will automatically close in 5 seconds.[/]") await asyncio.sleep(5.0) self.close_window() async def on_mount(self): self.find_plugins() def compose(self): yield RichLog(markup=True, id="plugins-log")