mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-11-02 02:42:04 +00:00
feat(tui-v3): implement Command Palette system (Ctrl+P)
Added top-docked CommandPalette widget with status labeling. Implemented CommandProcessor to route palette strings to SeedPass services. Wired Ctrl+P shortcut to toggle the palette. Integrated command execution into the main app message loop. Supported v3 commands: help, stats, lock, refresh, settings, maximize.
This commit is contained in:
1 parent
af177a8516
commit
d5b418711b
2 files changed
+121
-2
No files matched your search
@@ -13,11 +13,52 @@ from .widgets.header import RibbonHeader
|
||||
from .widgets.sidebar import SidebarContainer
|
||||
from .widgets.grid import GridContainer
|
||||
from .widgets.inspector import BoardContainer
|
||||
from .widgets.palette import CommandPalette
|
||||
from .screens.settings import SettingsScreen
|
||||
from .screens.inspector import MaximizedInspectorScreen
|
||||
|
||||
class CommandProcessor:
|
||||
"""Handles logic for palette commands in TUI v3."""
|
||||
def __init__(self, app: SeedPassTuiV3):
|
||||
self.app = app
|
||||
|
||||
def execute(self, raw: str) -> None:
|
||||
import shlex
|
||||
try:
|
||||
parts = shlex.split(raw)
|
||||
except Exception as e:
|
||||
self.app.notify(f"Parse error: {e}", severity="error")
|
||||
return
|
||||
|
||||
if not parts: return
|
||||
cmd = parts[0].lower()
|
||||
args = parts[1:]
|
||||
|
||||
if cmd == "help":
|
||||
self.app.notify("v3 commands: stats, lock, refresh, settings, maximize")
|
||||
elif cmd == "stats":
|
||||
self.app.notify("Calculating stats...")
|
||||
# We reuse the existing stats logic
|
||||
if "vault" in self.app.services:
|
||||
stats = self.app.services["vault"].stats()
|
||||
self.app.notify(f"Total entries: {stats.get('total_entries', 0)}")
|
||||
elif cmd == "lock":
|
||||
if "vault" in self.app.services:
|
||||
self.app.services["vault"].lock()
|
||||
self.app.session_locked = True
|
||||
self.app.notify("Vault locked")
|
||||
elif cmd == "refresh":
|
||||
self.app.action_refresh()
|
||||
elif cmd == "settings":
|
||||
self.app.action_toggle_settings()
|
||||
elif cmd == "maximize":
|
||||
self.app.action_maximize_inspector()
|
||||
else:
|
||||
self.app.notify(f"Unknown v3 command: {cmd}", severity="warning")
|
||||
|
||||
class MainScreen(Screen):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield CommandPalette(id="palette")
|
||||
yield Static("SeedPass ◈ UI v3", id="brand-strip")
|
||||
yield RibbonHeader(id="top-ribbon")
|
||||
with Vertical(id="body"):
|
||||
@@ -108,6 +149,7 @@ class SeedPassTuiV3(App[None]):
|
||||
|
||||
def on_mount(self) -> None:
|
||||
"""Initialize services and push the main screen."""
|
||||
self.processor = CommandProcessor(self)
|
||||
for name, factory in self.factories.items():
|
||||
if factory:
|
||||
try:
|
||||
@@ -120,6 +162,10 @@ class SeedPassTuiV3(App[None]):
|
||||
# Global UI Heartbeat (for 2FA ticking etc)
|
||||
self.set_interval(1.0, self.action_refresh_ui_quiet)
|
||||
|
||||
def on_command_palette_command_executed(self, message: CommandPalette.CommandExecuted) -> None:
|
||||
"""Handle command from palette."""
|
||||
self.processor.execute(message.command)
|
||||
|
||||
def action_refresh_ui_quiet(self) -> None:
|
||||
"""Background refresh for dynamic elements (2FA)."""
|
||||
try:
|
||||
@@ -157,8 +203,11 @@ class SeedPassTuiV3(App[None]):
|
||||
self.notify("UI Refreshed")
|
||||
|
||||
def action_open_palette(self) -> None:
|
||||
# Placeholder for unified command palette
|
||||
self.notify("Palette coming soon in v3")
|
||||
"""Toggle the command palette."""
|
||||
try:
|
||||
self.query_one("#palette").toggle()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def action_toggle_settings(self) -> None:
|
||||
"""Push the full-screen settings screen."""
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
from __future__ import annotations
|
||||
from typing import Any
|
||||
from textual.app import ComposeResult
|
||||
from textual.widgets import Input, Static
|
||||
from textual.containers import Vertical
|
||||
from textual.message import Message
|
||||
|
||||
class CommandPalette(Vertical):
|
||||
"""
|
||||
The TUI v3 Command Palette.
|
||||
Docks at the top and provides a unified entry point for all actions.
|
||||
"""
|
||||
|
||||
DEFAULT_CSS = """
|
||||
CommandPalette {
|
||||
dock: top;
|
||||
height: 4;
|
||||
background: #0b0f13;
|
||||
color: #daf2e5;
|
||||
border-bottom: solid #2abf75;
|
||||
display: none;
|
||||
padding: 0 1;
|
||||
}
|
||||
CommandPalette.visible {
|
||||
display: block;
|
||||
}
|
||||
#palette-label {
|
||||
height: 1;
|
||||
color: #58f29d;
|
||||
text-style: bold;
|
||||
margin-left: 1;
|
||||
}
|
||||
#palette-input {
|
||||
background: #0d1114;
|
||||
border: solid #1a3024;
|
||||
color: #e4fff2;
|
||||
}
|
||||
#palette-input:focus {
|
||||
border: heavy #58f29d;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Static("COMMAND PALETTE (type 'help' for list)", id="palette-label")
|
||||
yield Input(placeholder="enter command...", id="palette-input")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.input = self.query_one("#palette-input", Input)
|
||||
|
||||
def toggle(self) -> None:
|
||||
if self.has_class("visible"):
|
||||
self.remove_class("visible")
|
||||
self.app.set_focus(None)
|
||||
else:
|
||||
self.add_class("visible")
|
||||
self.input.focus()
|
||||
|
||||
def on_input_submitted(self, event: Input.Submitted) -> None:
|
||||
"""Forward the command to the main app."""
|
||||
cmd = event.value.strip()
|
||||
if cmd:
|
||||
self.app.post_message(self.CommandExecuted(cmd))
|
||||
self.input.value = ""
|
||||
self.toggle() # Close after execution
|
||||
|
||||
class CommandExecuted(Message):
|
||||
"""Sent when a command is submitted."""
|
||||
def __init__(self, command: str) -> None:
|
||||
self.command = command
|
||||
super().__init__()
|
||||
Reference in new issue
Block a user