diff --git a/build.ps1 b/build.ps1 index 170ff08..55e6916 100644 --- a/build.ps1 +++ b/build.ps1 @@ -14,6 +14,7 @@ pyinstaller ` --add-data "venv\Lib\site-packages\irc\codes.txt;irc" ` --add-data "icon.ico;." ` --add-data "src\channel.ico;." ` + --add-data "src\server.ico;." ` --icon "icon.ico" ` "src/main.py" diff --git a/src/LocalServer.py b/src/LocalServer.py index ac2542b..2214c62 100644 --- a/src/LocalServer.py +++ b/src/LocalServer.py @@ -211,3 +211,65 @@ class LocalServerManager: except Exception: logger.info(message) +if __name__ == "__main__": + import argparse + import sys + import signal + + def main(): + parser = argparse.ArgumentParser( + description="Run a local-only IRC server." + ) + parser.add_argument( + "--host", type=str, default="0.0.0.0", + help="Bind host (default: 0.0.0.0)" + ) + parser.add_argument( + "--port", type=int, default=6667, + help="Bind port (default: 6667)" + ) + parser.add_argument( + "--channels", type=str, default="#lobby", + help="Comma-separated list of channels (default: #lobby)" + ) + parser.add_argument( + "--verbose", action="store_true", + help="Enable verbose logging" + ) + + args = parser.parse_args() + + # Set up logging + logging.basicConfig( + level=logging.DEBUG if args.verbose else logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s" + ) + + # Initialize the server manager + manager = LocalServerManager( + listen_host=args.host, + listen_port=args.port + ) + manager.set_channels([ch.strip() for ch in args.channels.split(",") if ch.strip()]) + + # Handle Ctrl+C gracefully + def signal_handler(sig, frame): + print("\nStopping server...") + manager.stop() + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + + try: + manager.start() + print(f"IRC server running on {args.host}:{args.port}") + # Keep the main thread alive while server runs + while manager.is_running(): + import time + time.sleep(1) + except Exception as e: + print(f"Error: {e}") + manager.stop() + sys.exit(1) + + main() diff --git a/src/main.py b/src/main.py index ea010f0..9ebb56a 100644 --- a/src/main.py +++ b/src/main.py @@ -566,11 +566,18 @@ class IRCFrame(wx.Frame): # Bind close event self.notebook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_notebook_page_close) - # Server panel server_panel = IRCPanel(self.notebook, self) server_panel.set_target("SERVER") + + idx = self.notebook.GetPageCount() self.notebook.AddPage(server_panel, "Server") + + # THIS is the missing line + if self.icon_server != -1: + self.notebook.SetPageImage(idx, self.icon_server) + self.channels["SERVER"] = server_panel + # Right sidebar - Users - light gray for contrast right_panel = wx.Panel(panel) @@ -1283,37 +1290,54 @@ Available commands: def setup_tab_icons(self): try: - self.tab_image_list = wx.ImageList(16, 16) + self.tab_image_list = wx.ImageList(32, 32) - # Possible icon locations - icon_paths = [ - os.path.join(os.path.dirname(__file__), "channel.ico"), - get_resource_path("channel.ico"), # PyInstaller bundle - os.path.join(os.getcwd(), "src", "channel.ico"), + # Icon file names to search for + icon_files = { + 'server': 'server.ico', + 'channel': 'channel.ico', + 'query': 'channel.ico' # Reuse channel.ico for queries if no query.ico exists + } + + # Search paths for icons + base_paths = [ + os.path.dirname(__file__), + get_resource_path(""), + os.path.join(os.getcwd(), "src"), ] - icon_path = None - for path in icon_paths: - if path and os.path.exists(path): - icon_path = path - break + # Try to load each icon + loaded_icons = {} + for icon_type, filename in icon_files.items(): + icon_path = None + for base_path in base_paths: + test_path = os.path.join(base_path, filename) + if os.path.exists(test_path): + icon_path = test_path + break + + if icon_path: + try: + img = wx.Image(icon_path, wx.BITMAP_TYPE_ICO) + img = img.Scale(32, 32, wx.IMAGE_QUALITY_HIGH) + bmp = wx.Bitmap(img) + loaded_icons[icon_type] = self.tab_image_list.Add(bmp) + logger.info(f"Loaded {icon_type} icon from {icon_path}") + except Exception as e: + logger.warning(f"Failed to load {icon_type} icon: {e}") + loaded_icons[icon_type] = -1 + else: + logger.info(f"{filename} not found for {icon_type}") + loaded_icons[icon_type] = -1 - if icon_path: - try: - img = wx.Image(icon_path, wx.BITMAP_TYPE_ICO) - img = img.Scale(16, 16, wx.IMAGE_QUALITY_HIGH) - bmp = wx.Bitmap(img) + # Assign icon indices + self.icon_server = loaded_icons.get('server', -1) + self.icon_channel = loaded_icons.get('channel', -1) + self.icon_query = loaded_icons.get('query', -1) - self.icon_server = self.tab_image_list.Add(bmp) - self.icon_channel = self.tab_image_list.Add(bmp) - self.icon_query = self.tab_image_list.Add(bmp) - - logger.info(f"Loaded tab icons from {icon_path}") - except Exception as e: - logger.warning(f"Icon load failed: {e}") - self._setup_fallback_icons() - else: - logger.info("channel.ico not found, using fallback icons") + # Use fallback icons if any failed to load + if self.icon_server == -1 or self.icon_channel == -1 or self.icon_query == -1: + logger.info("Using fallback icons for missing icon files") self._setup_fallback_icons() self.notebook.SetImageList(self.tab_image_list) diff --git a/src/server.ico b/src/server.ico new file mode 100644 index 0000000..04115f4 Binary files /dev/null and b/src/server.ico differ