From 4d9a0e594367a0bfb1abb53e50eff91638c9f527 Mon Sep 17 00:00:00 2001 From: rob Date: Thu, 15 Jan 2026 22:18:00 -0400 Subject: [PATCH] Add CmdForge welcome page to sidebar - Create welcome_page.py with branded landing page - Add quick action cards for common tasks (Create Tool, Registry, etc.) - Add "CmdForge" entry at top of sidebar navigation - Update page indices for navigation Co-Authored-By: Claude Opus 4.5 --- src/cmdforge/gui/main_window.py | 21 ++- src/cmdforge/gui/pages/__init__.py | 3 +- src/cmdforge/gui/pages/welcome_page.py | 175 +++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 src/cmdforge/gui/pages/welcome_page.py diff --git a/src/cmdforge/gui/main_window.py b/src/cmdforge/gui/main_window.py index ce92e09..d45cab0 100644 --- a/src/cmdforge/gui/main_window.py +++ b/src/cmdforge/gui/main_window.py @@ -71,6 +71,7 @@ class MainWindow(QMainWindow): def _setup_sidebar(self): """Set up sidebar navigation items.""" items = [ + ("CmdForge", "Welcome to CmdForge"), ("Tools", "Manage your tools"), ("Registry", "Browse and install tools"), ("Providers", "Configure AI providers"), @@ -80,26 +81,34 @@ class MainWindow(QMainWindow): font = QFont() font.setPointSize(11) - for name, tooltip in items: + for i, (name, tooltip) in enumerate(items): item = QListWidgetItem(name) item.setFont(font) item.setToolTip(tooltip) item.setSizeHint(QSize(180, 48)) + # Style the CmdForge item differently + if i == 0: + bold_font = QFont(font) + bold_font.setBold(True) + item.setFont(bold_font) self.sidebar.addItem(item) def _setup_pages(self): """Set up content pages.""" # Import pages here to avoid circular imports + from .pages.welcome_page import WelcomePage from .pages.tools_page import ToolsPage from .pages.registry_page import RegistryPage from .pages.providers_page import ProvidersPage from .pages.profiles_page import ProfilesPage + self.welcome_page = WelcomePage(self) self.tools_page = ToolsPage(self) self.registry_page = RegistryPage(self) self.providers_page = ProvidersPage(self) self.profiles_page = ProfilesPage(self) + self.pages.addWidget(self.welcome_page) self.pages.addWidget(self.tools_page) self.pages.addWidget(self.registry_page) self.pages.addWidget(self.providers_page) @@ -121,10 +130,12 @@ class MainWindow(QMainWindow): def navigate_to(self, page_name: str): """Navigate to a specific page by name.""" page_map = { - "tools": 0, - "registry": 1, - "providers": 2, - "profiles": 3, + "welcome": 0, + "cmdforge": 0, + "tools": 1, + "registry": 2, + "providers": 3, + "profiles": 4, } if page_name.lower() in page_map: self.sidebar.setCurrentRow(page_map[page_name.lower()]) diff --git a/src/cmdforge/gui/pages/__init__.py b/src/cmdforge/gui/pages/__init__.py index 2a45535..5d76351 100644 --- a/src/cmdforge/gui/pages/__init__.py +++ b/src/cmdforge/gui/pages/__init__.py @@ -1,8 +1,9 @@ """GUI pages.""" +from .welcome_page import WelcomePage from .tools_page import ToolsPage from .tool_builder_page import ToolBuilderPage from .registry_page import RegistryPage from .providers_page import ProvidersPage -__all__ = ["ToolsPage", "ToolBuilderPage", "RegistryPage", "ProvidersPage"] +__all__ = ["WelcomePage", "ToolsPage", "ToolBuilderPage", "RegistryPage", "ProvidersPage"] diff --git a/src/cmdforge/gui/pages/welcome_page.py b/src/cmdforge/gui/pages/welcome_page.py new file mode 100644 index 0000000..cad9681 --- /dev/null +++ b/src/cmdforge/gui/pages/welcome_page.py @@ -0,0 +1,175 @@ +"""Welcome page - CmdForge landing page.""" + +from PySide6.QtWidgets import ( + QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, + QFrame, QGridLayout, QSizePolicy +) +from PySide6.QtCore import Qt + + +class WelcomePage(QWidget): + """CmdForge welcome/landing page.""" + + def __init__(self, main_window): + super().__init__() + self.main_window = main_window + self._setup_ui() + + def _setup_ui(self): + """Set up the UI.""" + layout = QVBoxLayout(self) + layout.setContentsMargins(48, 48, 48, 48) + layout.setSpacing(32) + + # Top spacer + layout.addStretch(1) + + # Logo/title + title = QLabel("CmdForge") + title.setStyleSheet(""" + font-size: 48px; + font-weight: bold; + color: #667eea; + """) + title.setAlignment(Qt.AlignCenter) + layout.addWidget(title) + + # Tagline + tagline = QLabel("Build AI-powered command-line tools") + tagline.setStyleSheet(""" + font-size: 18px; + color: #718096; + """) + tagline.setAlignment(Qt.AlignCenter) + layout.addWidget(tagline) + + layout.addSpacing(24) + + # Quick actions + actions_container = QWidget() + actions_container.setMaximumWidth(600) + actions_layout = QGridLayout(actions_container) + actions_layout.setSpacing(16) + + # Create tool card + create_card = self._create_action_card( + "Create Tool", + "Build a new AI-powered command-line tool with prompts, code, and data flow", + "New Tool", + self._on_create_tool + ) + actions_layout.addWidget(create_card, 0, 0) + + # Browse registry card + registry_card = self._create_action_card( + "Browse Registry", + "Discover and install tools shared by the community", + "Open Registry", + self._on_browse_registry + ) + actions_layout.addWidget(registry_card, 0, 1) + + # Manage tools card + tools_card = self._create_action_card( + "My Tools", + "View, edit, and run your existing tools", + "Open Tools", + self._on_open_tools + ) + actions_layout.addWidget(tools_card, 1, 0) + + # Providers card + providers_card = self._create_action_card( + "AI Providers", + "Configure AI backends like Claude, GPT, and custom providers", + "Configure", + self._on_open_providers + ) + actions_layout.addWidget(providers_card, 1, 1) + + # Center the actions container + actions_wrapper = QHBoxLayout() + actions_wrapper.addStretch() + actions_wrapper.addWidget(actions_container) + actions_wrapper.addStretch() + layout.addLayout(actions_wrapper) + + # Bottom spacer + layout.addStretch(2) + + # Footer + footer = QLabel("CmdForge - AI Tool Builder") + footer.setStyleSheet("color: #a0aec0; font-size: 12px;") + footer.setAlignment(Qt.AlignCenter) + layout.addWidget(footer) + + def _create_action_card(self, title: str, description: str, button_text: str, callback) -> QFrame: + """Create an action card widget.""" + card = QFrame() + card.setStyleSheet(""" + QFrame { + background-color: white; + border: 1px solid #e2e8f0; + border-radius: 8px; + padding: 16px; + } + QFrame:hover { + border-color: #667eea; + } + """) + card.setMinimumSize(260, 140) + + layout = QVBoxLayout(card) + layout.setContentsMargins(16, 16, 16, 16) + layout.setSpacing(8) + + # Title + title_label = QLabel(title) + title_label.setStyleSheet("font-size: 16px; font-weight: 600; color: #2d3748; border: none;") + layout.addWidget(title_label) + + # Description + desc_label = QLabel(description) + desc_label.setWordWrap(True) + desc_label.setStyleSheet("font-size: 13px; color: #718096; border: none;") + layout.addWidget(desc_label, 1) + + # Button + btn = QPushButton(button_text) + btn.clicked.connect(callback) + btn.setStyleSheet(""" + QPushButton { + background-color: #667eea; + color: white; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-weight: 500; + } + QPushButton:hover { + background-color: #5a67d8; + } + """) + layout.addWidget(btn) + + return card + + def _on_create_tool(self): + """Handle create tool action.""" + self.main_window.open_tool_builder() + + def _on_browse_registry(self): + """Handle browse registry action.""" + self.main_window.navigate_to("registry") + + def _on_open_tools(self): + """Handle open tools action.""" + self.main_window.navigate_to("tools") + + def _on_open_providers(self): + """Handle open providers action.""" + self.main_window.navigate_to("providers") + + def refresh(self): + """Refresh the page (nothing to refresh).""" + pass