-
-
Notifications
You must be signed in to change notification settings - Fork 52
feat(ui): Rich-based terminal UI module with modern interaction patterns #581
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| """Cortex UI - Modern terminal interface components.""" | ||
|
|
||
| from .console import console, CortexConsole | ||
| from .theme import COLORS, SYMBOLS, CORTEX_THEME, PANEL_STYLES | ||
| from .prompts import CommandPrompt, MenuAction, PromptResult, confirm, select | ||
| from .progress import spinner, progress_bar, steps, indeterminate_progress, download_progress | ||
| from .errors import show_error, show_conflict, show_warning, auto_suggest_fix, SuggestedFix | ||
| from .explain import explain_command, explain_pipeline, CommandPart | ||
| from .trust import TrustManager, TrustScope, trust_manager | ||
| from .panels import ai_thinking, ai_response, status_panel, code_panel, diff_panel, summary_panel, welcome_banner, help_footer | ||
|
Check failure on line 10 in cortex/ui/__init__.py
|
||
|
|
||
| __all__ = [ | ||
| "console", "CortexConsole", "COLORS", "SYMBOLS", "CORTEX_THEME", "PANEL_STYLES", | ||
| "CommandPrompt", "MenuAction", "PromptResult", "confirm", "select", | ||
| "spinner", "progress_bar", "steps", "indeterminate_progress", "download_progress", | ||
| "show_error", "show_conflict", "show_warning", "auto_suggest_fix", "SuggestedFix", | ||
| "explain_command", "explain_pipeline", "CommandPart", | ||
| "TrustManager", "TrustScope", "trust_manager", | ||
| "ai_thinking", "ai_response", "status_panel", "code_panel", "diff_panel", "summary_panel", "welcome_banner", "help_footer", | ||
| ] | ||
| __version__ = "0.1.0" | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,78 @@ | ||||||||||||
| """Cortex Console - Themed console singleton with semantic message methods.""" | ||||||||||||
|
|
||||||||||||
| from typing import Optional | ||||||||||||
| from rich.console import Console as RichConsole | ||||||||||||
| from rich.panel import Panel | ||||||||||||
| from rich.syntax import Syntax | ||||||||||||
| from rich.markdown import Markdown | ||||||||||||
| from .theme import CORTEX_THEME, SYMBOLS, PANEL_STYLES | ||||||||||||
|
Check failure on line 8 in cortex/ui/console.py
|
||||||||||||
|
Comment on lines
+3
to
+8
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix import sorting and use modern type annotation syntax. Static analysis flagged unsorted imports (I001) and outdated Proposed fix-from typing import Optional
-from rich.console import Console as RichConsole
-from rich.panel import Panel
-from rich.syntax import Syntax
-from rich.markdown import Markdown
-from .theme import CORTEX_THEME, SYMBOLS, PANEL_STYLES
+from rich.console import Console as RichConsole
+from rich.markdown import Markdown
+from rich.panel import Panel
+from rich.syntax import Syntax
+
+from .theme import CORTEX_THEME, PANEL_STYLES, SYMBOLS🧰 Tools🪛 GitHub Check: lint[failure] 3-8: Ruff (I001) 🪛 GitHub Check: Lint[failure] 3-8: Ruff (I001) 🤖 Prompt for AI Agents |
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| class CortexConsole: | ||||||||||||
| """Themed console with semantic message methods.""" | ||||||||||||
|
|
||||||||||||
|
Check failure on line 13 in cortex/ui/console.py
|
||||||||||||
| _instance: Optional['CortexConsole'] = None | ||||||||||||
|
|
||||||||||||
|
Check failure on line 15 in cortex/ui/console.py
|
||||||||||||
| def __new__(cls) -> 'CortexConsole': | ||||||||||||
| if cls._instance is None: | ||||||||||||
| cls._instance = super().__new__(cls) | ||||||||||||
| cls._instance._console = RichConsole(theme=CORTEX_THEME) | ||||||||||||
| return cls._instance | ||||||||||||
|
|
||||||||||||
|
Check failure on line 21 in cortex/ui/console.py
|
||||||||||||
| @property | ||||||||||||
| def rich(self) -> RichConsole: | ||||||||||||
| return self._console | ||||||||||||
|
|
||||||||||||
|
Check failure on line 25 in cortex/ui/console.py
|
||||||||||||
| def print(self, *args, **kwargs) -> None: | ||||||||||||
| self._console.print(*args, **kwargs) | ||||||||||||
|
Comment on lines
+11
to
+27
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove trailing whitespace and add docstrings to public methods. Multiple blank lines contain whitespace (W293 on lines 13, 15, 21, 25, 28, 31, 36). Additionally, public methods lack docstrings per coding guidelines. The singleton pattern implemented via Fix whitespace and update type annotation class CortexConsole:
"""Themed console with semantic message methods."""
-
+
_instance: Optional['CortexConsole'] = None
-
+
def __new__(cls) -> 'CortexConsole':
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._console = RichConsole(theme=CORTEX_THEME)
return cls._instance
-
+
@property
def rich(self) -> RichConsole:
+ """Return the underlying Rich Console instance."""
return self._console🧰 Tools🪛 GitHub Check: lint[failure] 25-25: Ruff (W293) [failure] 21-21: Ruff (W293) [failure] 15-15: Ruff (W293) [failure] 13-13: Ruff (W293) 🪛 GitHub Check: Lint[failure] 25-25: Ruff (W293) [failure] 21-21: Ruff (W293) [failure] 15-15: Ruff (W293) [failure] 13-13: Ruff (W293) 🤖 Prompt for AI Agents |
||||||||||||
|
|
||||||||||||
|
Check failure on line 28 in cortex/ui/console.py
|
||||||||||||
| def success(self, message: str) -> None: | ||||||||||||
| self._console.print(f"[success_symbol]{SYMBOLS['success']}[/] [success]{message}[/]") | ||||||||||||
|
|
||||||||||||
|
Check failure on line 31 in cortex/ui/console.py
|
||||||||||||
| def error(self, message: str, details: Optional[str] = None) -> None: | ||||||||||||
|
Check failure on line 32 in cortex/ui/console.py
|
||||||||||||
| self._console.print(f"[error_symbol]{SYMBOLS['error']}[/] [error]{message}[/]") | ||||||||||||
| if details: | ||||||||||||
| self._console.print(f" [secondary]{details}[/]") | ||||||||||||
|
Comment on lines
+34
to
+35
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
Comment on lines
+32
to
+35
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update type annotation syntax. Use Proposed fix- def error(self, message: str, details: Optional[str] = None) -> None:
+ def error(self, message: str, details: str | None = None) -> None:
self._console.print(f"[error_symbol]{SYMBOLS['error']}[/] [error]{message}[/]")
if details:
self._console.print(f" [secondary]{details}[/]")🧰 Tools🪛 GitHub Check: lint[failure] 32-32: Ruff (UP045) 🪛 GitHub Check: Lint[failure] 32-32: Ruff (UP045) 🤖 Prompt for AI Agents |
||||||||||||
|
|
||||||||||||
|
Check failure on line 36 in cortex/ui/console.py
|
||||||||||||
| def warning(self, message: str) -> None: | ||||||||||||
| self._console.print(f"[warning_symbol]{SYMBOLS['warning']}[/] [warning]{message}[/]") | ||||||||||||
|
|
||||||||||||
| def info(self, message: str) -> None: | ||||||||||||
| self._console.print(f"[info_symbol]{SYMBOLS['info']}[/] [info]{message}[/]") | ||||||||||||
|
|
||||||||||||
| def command(self, cmd: str) -> None: | ||||||||||||
| self._console.print(f"[command]{SYMBOLS['command']} {cmd}[/]") | ||||||||||||
|
|
||||||||||||
| def step(self, message: str, current: int, total: int) -> None: | ||||||||||||
| self._console.print(f"[info_symbol]{SYMBOLS['step']}[/] [info]{message}[/] [secondary]({current}/{total})[/]") | ||||||||||||
|
|
||||||||||||
| def thinking(self, message: str) -> None: | ||||||||||||
| self._console.print(f"[cortex]{SYMBOLS['thinking']} {message}[/]") | ||||||||||||
|
|
||||||||||||
| def secondary(self, message: str) -> None: | ||||||||||||
| self._console.print(f" [secondary]{message}[/]") | ||||||||||||
|
|
||||||||||||
| def blank(self) -> None: | ||||||||||||
| self._console.print() | ||||||||||||
|
|
||||||||||||
| def rule(self, title: str = "") -> None: | ||||||||||||
| self._console.rule(title, style="panel_border") | ||||||||||||
|
|
||||||||||||
| def code(self, code: str, language: str = "bash") -> None: | ||||||||||||
| syntax = Syntax(code, language, theme="monokai", line_numbers=False) | ||||||||||||
| self._console.print(syntax) | ||||||||||||
|
|
||||||||||||
| def markdown(self, text: str) -> None: | ||||||||||||
| self._console.print(Markdown(text)) | ||||||||||||
|
|
||||||||||||
| def cortex_panel(self, content: str, title: str = "CORTEX") -> None: | ||||||||||||
| panel = Panel(content, title=f"[cortex]─ {title} [/]", **PANEL_STYLES["cortex"]) | ||||||||||||
| self._console.print(panel) | ||||||||||||
|
|
||||||||||||
| def panel(self, content: str, title: str = "", style: str = "default") -> None: | ||||||||||||
| panel_style = PANEL_STYLES.get(style, PANEL_STYLES["default"]) | ||||||||||||
| panel = Panel(content, title=f"─ {title} " if title else None, **panel_style) | ||||||||||||
| self._console.print(panel) | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| console = CortexConsole() | ||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,97 @@ | ||||||||||||||||||||
| """Cortex Errors - Structured error display with context and fixes.""" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from typing import Optional, Dict, List | ||||||||||||||||||||
| from dataclasses import dataclass | ||||||||||||||||||||
| import re | ||||||||||||||||||||
| from rich.panel import Panel | ||||||||||||||||||||
| from .theme import SYMBOLS | ||||||||||||||||||||
| from .console import console | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| @dataclass | ||||||||||||||||||||
| class SuggestedFix: | ||||||||||||||||||||
| command: str | ||||||||||||||||||||
| description: Optional[str] = None | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| COMMON_ERRORS = { | ||||||||||||||||||||
| "container_conflict": {"pattern": "container name .* already in use", "title": "Container Name Conflict", "fix_template": "docker rm -f {container_name}", "fix_description": "Remove the existing container"}, | ||||||||||||||||||||
| "port_in_use": {"pattern": "port .* already in use", "title": "Port Already In Use", "fix_template": "lsof -ti:{port} | xargs kill -9", "fix_description": "Kill the process using the port"}, | ||||||||||||||||||||
| "permission_denied": {"pattern": "permission denied", "title": "Permission Denied", "fix_template": "sudo {original_command}", "fix_description": "Run with elevated privileges"}, | ||||||||||||||||||||
| "not_found": {"pattern": "command not found|not found", "title": "Command Not Found", "fix_template": "apt install {package}", "fix_description": "Install the missing package"}, | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
Comment on lines
+17
to
+22
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The 🔧 Option 1: Add extraction logic elif error_type == "permission_denied":
fix_cmd = fix_cmd.format(original_command=command)
+ elif error_type == "not_found":
+ # Try to extract package name from error
+ match = re.search(r"(\w+): command not found", error_lower)
+ if match:
+ fix_cmd = fix_cmd.format(package=match.group(1))
+ else:
+ fix_cmd = fix_cmd.format(package="<package>")
return SuggestedFix(command=fix_cmd, description=info["fix_description"])🔧 Option 2: Use generic placeholder in template- "not_found": {"pattern": "command not found|not found", "title": "Command Not Found", "fix_template": "apt install {package}", "fix_description": "Install the missing package"},
+ "not_found": {"pattern": "command not found|not found", "title": "Command Not Found", "fix_template": "apt install <package-name>", "fix_description": "Install the missing package"},🤖 Prompt for AI Agents |
||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| def show_error(title: str, message: str, context: Optional[Dict[str, str]] = None, suggested_fix: Optional[str] = None, fix_description: Optional[str] = None, actions: Optional[List[str]] = None) -> Optional[str]: | ||||||||||||||||||||
| lines = [f"[error]{SYMBOLS['error']} FAILED:[/] [primary]{title}[/]", "", f" [error]Error:[/] {message}"] | ||||||||||||||||||||
| if context: | ||||||||||||||||||||
| lines.append("") | ||||||||||||||||||||
| for key, value in context.items(): | ||||||||||||||||||||
| display_value = value if len(value) < 50 else value[:47] + "..." | ||||||||||||||||||||
| lines.append(f" [secondary]{key}:[/] {display_value}") | ||||||||||||||||||||
|
Comment on lines
+30
to
+31
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The truncation logic for |
||||||||||||||||||||
|
|
||||||||||||||||||||
| if suggested_fix: | ||||||||||||||||||||
| fix_content = [] | ||||||||||||||||||||
| if fix_description: | ||||||||||||||||||||
| fix_content.append(f"[muted]{fix_description}[/]") | ||||||||||||||||||||
| fix_content.append("") | ||||||||||||||||||||
| fix_content.append(f"[command]{suggested_fix}[/]") | ||||||||||||||||||||
| console.print(Panel("\n".join(lines), border_style="error", padding=(1, 2))) | ||||||||||||||||||||
| console.print(Panel("\n".join(fix_content), title="[success]─ SUGGESTED FIX [/]", border_style="success", padding=(0, 2))) | ||||||||||||||||||||
| else: | ||||||||||||||||||||
| console.print(Panel("\n".join(lines), border_style="error", padding=(1, 2))) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if actions: | ||||||||||||||||||||
| action_text = [f"[highlight][{a[0].upper()}][/][muted]{a[1:]}[/]" for a in actions] | ||||||||||||||||||||
| console.print(" " + " ".join(action_text)) | ||||||||||||||||||||
| console.print() | ||||||||||||||||||||
| while True: | ||||||||||||||||||||
| response = input(" > ").strip().lower() | ||||||||||||||||||||
| for action in actions: | ||||||||||||||||||||
| if response == action[0].lower() or response == action.lower(): | ||||||||||||||||||||
| return action | ||||||||||||||||||||
| console.warning(f"Please enter one of: {', '.join(a[0] for a in actions)}") | ||||||||||||||||||||
|
Comment on lines
+48
to
+53
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||||||||||||||||||||
| return None | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| def show_conflict(title: str, description: str, options: List[Dict[str, str]]) -> str: | ||||||||||||||||||||
| lines = [f"[warning]{SYMBOLS['warning']} CONFLICT DETECTED[/]", "", f"[primary]{description}[/]", "", "[muted]Options:[/]"] | ||||||||||||||||||||
| for opt in options: | ||||||||||||||||||||
| lines.append(f" [highlight][{opt['key']}][/] {opt['label']}") | ||||||||||||||||||||
| if opt.get('description'): | ||||||||||||||||||||
| lines.append(f" [secondary]{opt['description']}[/]") | ||||||||||||||||||||
| console.print(Panel("\n".join(lines), title=f"[warning]─ {title} [/]", border_style="warning", padding=(1, 2))) | ||||||||||||||||||||
| valid_keys = [opt['key'].lower() for opt in options] | ||||||||||||||||||||
| while True: | ||||||||||||||||||||
| response = input(f"\n {SYMBOLS['prompt']} What would you like to do? ").strip().lower() | ||||||||||||||||||||
| if response in valid_keys: | ||||||||||||||||||||
| return response | ||||||||||||||||||||
| console.warning(f"Please enter one of: {', '.join(valid_keys)}") | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| def show_warning(title: str, message: str, details: Optional[List[str]] = None) -> None: | ||||||||||||||||||||
| lines = [f"[warning]{SYMBOLS['warning']} {title}[/]", "", f"[primary]{message}[/]"] | ||||||||||||||||||||
| if details: | ||||||||||||||||||||
| lines.append("") | ||||||||||||||||||||
| for detail in details: | ||||||||||||||||||||
| lines.append(f" [secondary]• {detail}[/]") | ||||||||||||||||||||
| console.print(Panel("\n".join(lines), border_style="warning", padding=(1, 2))) | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| def auto_suggest_fix(error_message: str, command: str = "") -> Optional[SuggestedFix]: | ||||||||||||||||||||
| error_lower = error_message.lower() | ||||||||||||||||||||
| for error_type, info in COMMON_ERRORS.items(): | ||||||||||||||||||||
| if re.search(info["pattern"], error_lower): | ||||||||||||||||||||
| fix_cmd = info["fix_template"] | ||||||||||||||||||||
| if error_type == "container_conflict": | ||||||||||||||||||||
| match = re.search(r'name ["\']?/?(\w+)', error_lower) | ||||||||||||||||||||
| if match: | ||||||||||||||||||||
| fix_cmd = fix_cmd.format(container_name=match.group(1)) | ||||||||||||||||||||
|
Comment on lines
+86
to
+89
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Container name regex may miss names with hyphens. The regex 🔧 Suggested fix if error_type == "container_conflict":
- match = re.search(r'name ["\']?/?(\w+)', error_lower)
+ match = re.search(r'name ["\']?/?([\w-]+)', error_lower)
if match:
fix_cmd = fix_cmd.format(container_name=match.group(1))📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| elif error_type == "port_in_use": | ||||||||||||||||||||
| match = re.search(r'port (\d+)', error_lower) | ||||||||||||||||||||
| if match: | ||||||||||||||||||||
| fix_cmd = fix_cmd.format(port=match.group(1)) | ||||||||||||||||||||
| elif error_type == "permission_denied": | ||||||||||||||||||||
| fix_cmd = fix_cmd.format(original_command=command) | ||||||||||||||||||||
|
Comment on lines
+94
to
+95
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||||||||
| return SuggestedFix(command=fix_cmd, description=info["fix_description"]) | ||||||||||||||||||||
| return None | ||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| """Cortex Explain - Command explanation and breakdown.""" | ||
|
|
||
| from typing import List, Tuple | ||
| from dataclasses import dataclass | ||
| import shlex | ||
| from rich.panel import Panel | ||
| from .console import console | ||
|
|
||
|
|
||
| @dataclass | ||
| class CommandPart: | ||
| text: str | ||
| explanation: str | ||
| is_flag: bool = False | ||
|
|
||
|
|
||
| COMMAND_EXPLANATIONS = { | ||
| "docker": { | ||
| "run": "Creates and starts a new container", "-d": "Runs in background (detached)", "--detach": "Runs in background", | ||
| "-v": "Mounts a volume (host:container)", "--volume": "Mounts a volume", "-p": "Maps a port (host:container)", | ||
| "--publish": "Maps a port", "--name": "Names the container", "--gpus": "Grants GPU access", | ||
| "--rm": "Auto-removes when exits", "-it": "Interactive with TTY", "-e": "Sets environment variable", | ||
| "pull": "Downloads an image", "stop": "Stops container", "rm": "Removes container", "-f": "Force operation", | ||
| }, | ||
| "apt": { | ||
| "install": "Installs packages", "update": "Refreshes package index", "upgrade": "Upgrades all packages", | ||
| "remove": "Removes package", "purge": "Removes package + config", "-y": "Auto-yes to prompts", | ||
| }, | ||
| "pip": { | ||
| "install": "Installs Python packages", "-e": "Editable/dev mode", "-r": "From requirements file", | ||
| "--upgrade": "Upgrades to latest", "-U": "Upgrades to latest", "--break-system-packages": "Allows system Python changes", | ||
| }, | ||
| "git": { | ||
| "clone": "Downloads repository", "pull": "Fetches and merges", "push": "Uploads commits", | ||
| "add": "Stages files", "commit": "Records changes", "-m": "Commit message", "checkout": "Switches branches", "-b": "Creates new branch", | ||
| }, | ||
| } | ||
|
|
||
|
|
||
| def parse_command(cmd: str) -> Tuple[str, List[str]]: | ||
| try: | ||
| parts = shlex.split(cmd) | ||
| except ValueError: | ||
| parts = cmd.split() | ||
| return (parts[0], parts[1:]) if parts else ("", []) | ||
|
|
||
|
|
||
| def get_explanation(base_cmd: str, part: str) -> str: | ||
| cmd_exp = COMMAND_EXPLANATIONS.get(base_cmd, {}) | ||
| if part in cmd_exp: | ||
| return cmd_exp[part] | ||
| if part.lstrip('-') in cmd_exp: | ||
| return cmd_exp[part.lstrip('-')] | ||
| if part.startswith('-'): | ||
| return "Option flag" | ||
| if ':' in part: | ||
| return "Mapping (source:destination)" | ||
| if '/' in part and not part.startswith('/'): | ||
| return "Image or path reference" | ||
| return "Argument or value" | ||
|
|
||
|
|
||
| def explain_command(cmd: str, show_panel: bool = True) -> List[CommandPart]: | ||
| base_cmd, args = parse_command(cmd) | ||
| parts = [CommandPart(text=base_cmd, explanation=f"The {base_cmd} command")] | ||
|
|
||
| i = 0 | ||
| while i < len(args): | ||
| arg = args[i] | ||
| exp = get_explanation(base_cmd, arg) | ||
| if arg.startswith('-') and i + 1 < len(args) and not args[i + 1].startswith('-'): | ||
| parts.append(CommandPart(text=f"{arg} {args[i+1]}", explanation=f"{exp}: {args[i+1]}", is_flag=True)) | ||
| i += 2 | ||
| else: | ||
|
Comment on lines
+71
to
+74
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for parsing command arguments and flags in |
||
| parts.append(CommandPart(text=arg, explanation=exp, is_flag=arg.startswith('-'))) | ||
| i += 1 | ||
|
|
||
| if show_panel: | ||
| lines = [f"[command]{cmd}[/]", ""] | ||
| for j, part in enumerate(parts): | ||
| lines.append(f"[{'cortex' if j == 0 else 'info'}]{part.text}[/]") | ||
| lines.append(f"└─ [secondary]{part.explanation}[/]") | ||
| if j < len(parts) - 1: | ||
| lines.append("") | ||
| console.print(Panel("\n".join(lines), title="[cortex]─ EXPLANATION [/]", border_style="cortex", padding=(1, 2))) | ||
| console.print("\n [secondary]Press Enter to return to options[/]") | ||
| input() | ||
|
Comment on lines
+86
to
+87
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the |
||
| return parts | ||
|
|
||
|
|
||
| def explain_pipeline(cmd: str) -> None: | ||
| import re | ||
| parts = re.split(r'\s*(\||\&\&|\|\|)\s*', cmd) | ||
| lines = ["[muted]Pipeline breakdown:[/]", ""] | ||
| step = 1 | ||
| for part in parts: | ||
| if part in ('|', '&&', '||'): | ||
| ops = {'|': 'pipes output to', '&&': 'then (if successful)', '||': 'or (if failed)'} | ||
| lines.append(f" [warning]{ops.get(part, part)}[/]") | ||
| elif part.strip(): | ||
| lines.append(f"[info]{step}.[/] [command]{part.strip()}[/]") | ||
| step += 1 | ||
| console.print(Panel("\n".join(lines), title="[cortex]─ PIPELINE [/]", border_style="cortex", padding=(1, 2))) | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,67 @@ | ||||||||||||||||||||||||||||||||||||||||
| """Cortex Panels - AI response and status panels.""" | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| from typing import Optional, List | ||||||||||||||||||||||||||||||||||||||||
| from rich.panel import Panel | ||||||||||||||||||||||||||||||||||||||||
| from rich.syntax import Syntax | ||||||||||||||||||||||||||||||||||||||||
| from rich.text import Text | ||||||||||||||||||||||||||||||||||||||||
| from rich.console import Group | ||||||||||||||||||||||||||||||||||||||||
| from .console import console | ||||||||||||||||||||||||||||||||||||||||
| from .theme import SYMBOLS | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def ai_thinking(message: str, animated: bool = False) -> None: | ||||||||||||||||||||||||||||||||||||||||
| content = f"[cortex]{SYMBOLS['thinking']} {message}[/]" | ||||||||||||||||||||||||||||||||||||||||
| console.print(Panel(content, title="[cortex]─ CORTEX [/]", border_style="cortex", padding=(1, 2))) | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+12
to
+14
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused parameter: The 🔧 Option 1: Remove unused parameter-def ai_thinking(message: str, animated: bool = False) -> None:
+def ai_thinking(message: str) -> None:🔧 Option 2: Add TODO for future implementation def ai_thinking(message: str, animated: bool = False) -> None:
+ """Display an AI thinking indicator.
+
+ Args:
+ message: The thinking message to display.
+ animated: If True, show animated spinner. (Not yet implemented)
+ """
+ # TODO: Implement animated spinner using Rich Live
content = f"[cortex]{SYMBOLS['thinking']} {message}[/]"📝 Committable suggestion
Suggested change
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def ai_response(message: str, title: str = "CORTEX", show_commands: Optional[List[str]] = None, show_code: Optional[str] = None, code_language: str = "bash") -> None: | ||||||||||||||||||||||||||||||||||||||||
| content_parts = [Text(message)] | ||||||||||||||||||||||||||||||||||||||||
| if show_commands: | ||||||||||||||||||||||||||||||||||||||||
| content_parts.append(Text()) | ||||||||||||||||||||||||||||||||||||||||
| content_parts.append(Text("Commands to execute:", style="muted")) | ||||||||||||||||||||||||||||||||||||||||
| for cmd in show_commands: | ||||||||||||||||||||||||||||||||||||||||
| content_parts.append(Text(f" {cmd}", style="command")) | ||||||||||||||||||||||||||||||||||||||||
| if show_code: | ||||||||||||||||||||||||||||||||||||||||
| content_parts.append(Text()) | ||||||||||||||||||||||||||||||||||||||||
| content_parts.append(Syntax(show_code, code_language, theme="monokai", line_numbers=False)) | ||||||||||||||||||||||||||||||||||||||||
| content = Group(*content_parts) if len(content_parts) > 1 else content_parts[0] | ||||||||||||||||||||||||||||||||||||||||
| console.print(Panel(content, title=f"[cortex]─ {title} [/]", border_style="cortex", padding=(1, 2))) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def status_panel(title: str, items: List[str], style: str = "default") -> None: | ||||||||||||||||||||||||||||||||||||||||
| content = "\n".join(f" {item}" for item in items) | ||||||||||||||||||||||||||||||||||||||||
| border = {"default": "panel_border", "success": "success", "warning": "warning", "error": "error"}.get(style, "panel_border") | ||||||||||||||||||||||||||||||||||||||||
| console.print(Panel(content, title=f"─ {title} ", border_style=border, padding=(1, 2))) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def code_panel(code: str, language: str = "bash", title: str = "Code") -> None: | ||||||||||||||||||||||||||||||||||||||||
| console.print(Panel(Syntax(code, language, theme="monokai", line_numbers=True), title=f"─ {title} ", border_style="panel_border", padding=(0, 1))) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def diff_panel(additions: List[str], deletions: List[str], title: str = "Changes") -> None: | ||||||||||||||||||||||||||||||||||||||||
| lines = [f"[error]- {line}[/]" for line in deletions] + [f"[success]+ {line}[/]" for line in additions] | ||||||||||||||||||||||||||||||||||||||||
| console.print(Panel("\n".join(lines), title=f"─ {title} ", border_style="panel_border", padding=(0, 2))) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def summary_panel(title: str, summary: str, details: Optional[dict] = None) -> None: | ||||||||||||||||||||||||||||||||||||||||
| lines = [summary] | ||||||||||||||||||||||||||||||||||||||||
| if details: | ||||||||||||||||||||||||||||||||||||||||
| lines.append("") | ||||||||||||||||||||||||||||||||||||||||
| for key, value in details.items(): | ||||||||||||||||||||||||||||||||||||||||
| lines.append(f"[muted]{key}:[/] {value}") | ||||||||||||||||||||||||||||||||||||||||
| console.print(Panel("\n".join(lines), title=f"[cortex]─ {title} [/]", border_style="cortex", padding=(1, 2))) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def welcome_banner() -> None: | ||||||||||||||||||||||||||||||||||||||||
| banner = """[cortex] ____ _ | ||||||||||||||||||||||||||||||||||||||||
| / ___|___ _ __| |_ _____ __ | ||||||||||||||||||||||||||||||||||||||||
| | | / _ \\| '__| __/ _ \\ \\/ / | ||||||||||||||||||||||||||||||||||||||||
| | |__| (_) | | | || __/> < | ||||||||||||||||||||||||||||||||||||||||
| \\____\\___/|_| \\__\\___/_/\\_\\[/] | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| [muted]The AI Layer for Linux[/]""" | ||||||||||||||||||||||||||||||||||||||||
| console.print(banner) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| def help_footer() -> None: | ||||||||||||||||||||||||||||||||||||||||
| console.print("\n [secondary]esc[/] [muted]Cancel[/] · [secondary]tab[/] [muted]Add instructions[/] · [secondary]?[/] [muted]Help[/]") | ||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix unsorted imports to pass CI.
The pipeline is failing due to Ruff I001 (unsorted imports). Reorder imports alphabetically by module name.
🔧 Sorted imports
📝 Committable suggestion
🧰 Tools
🪛 GitHub Actions: CI
[error] 3-3: Import block is un-sorted or un-formatted
🪛 GitHub Check: lint
[failure] 3-10: Ruff (I001)
cortex/ui/init.py:3:1: I001 Import block is un-sorted or un-formatted
🪛 GitHub Check: Lint
[failure] 3-10: Ruff (I001)
cortex/ui/init.py:3:1: I001 Import block is un-sorted or un-formatted
🤖 Prompt for AI Agents