Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1412ced7a8 | |||
| 54241a9e4e |
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import litellm
|
||||
from prompt_toolkit import PromptSession
|
||||
from prompt_toolkit.completion import WordCompleter
|
||||
from prompt_toolkit.history import FileHistory
|
||||
|
||||
from pyra.chat.history import ConversationHistory
|
||||
@@ -159,12 +160,15 @@ def start_chat() -> None:
|
||||
))
|
||||
|
||||
history = ConversationHistory(cfg, registry)
|
||||
session: PromptSession = PromptSession(
|
||||
history=FileHistory(str(_HISTORY_FILE)),
|
||||
multiline=False,
|
||||
)
|
||||
|
||||
plugin_slash = registry.get_slash_commands()
|
||||
all_commands = list(_STATIC_COMMANDS) + list(plugin_slash)
|
||||
session: PromptSession = PromptSession(
|
||||
history=FileHistory(str(_HISTORY_FILE)),
|
||||
completer=WordCompleter(all_commands, sentence=True),
|
||||
complete_while_typing=False,
|
||||
multiline=False,
|
||||
)
|
||||
|
||||
provider = get_provider(cfg.ai.provider_id)
|
||||
render_system(
|
||||
|
||||
+92
-41
@@ -7,7 +7,7 @@ from textual.binding import Binding
|
||||
from textual.containers import Horizontal, VerticalScroll
|
||||
from textual.coordinate import Coordinate
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Button, DataTable, Input, Label, Switch, TabbedContent, TabPane
|
||||
from textual.widgets import Button, DataTable, Footer, Header, Input, Label, Switch, TabbedContent, TabPane
|
||||
|
||||
from pyra.config.manager import load_config, save_config
|
||||
from pyra.plugins.base import BasePlugin, ConfigField
|
||||
@@ -67,24 +67,32 @@ def _pfid(plugin_name: str, key: str) -> str:
|
||||
|
||||
# ── Tab widgets ───────────────────────────────────────────────────────────────
|
||||
|
||||
class _GeneralTab(Widget):
|
||||
class _GeneralTab(VerticalScroll):
|
||||
BINDINGS = [Binding("ctrl+s", "save", "Save")]
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
cfg = load_config()
|
||||
with VerticalScroll():
|
||||
for f in GENERAL_FIELDS:
|
||||
current = _get_nested(cfg, f.path)
|
||||
with Horizontal(classes="row"):
|
||||
yield Label(f.label)
|
||||
if f.type == "bool":
|
||||
yield Switch(value=bool(current), id=_fid(f.path))
|
||||
else:
|
||||
yield Input(value=str(current), id=_fid(f.path))
|
||||
with Horizontal(classes="actions"):
|
||||
yield Button("Save", id="save-general", variant="primary")
|
||||
for f in GENERAL_FIELDS:
|
||||
current = _get_nested(cfg, f.path)
|
||||
with Horizontal(classes="row"):
|
||||
yield Label(f.label)
|
||||
if f.type == "bool":
|
||||
yield Switch(value=bool(current), id=_fid(f.path))
|
||||
else:
|
||||
yield Input(value=str(current), id=_fid(f.path))
|
||||
with Horizontal(classes="actions"):
|
||||
yield Button("Save", id="save-general", variant="primary")
|
||||
|
||||
def action_save(self) -> None:
|
||||
self._do_save()
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
if event.button.id != "save-general":
|
||||
return
|
||||
self._do_save()
|
||||
event.stop()
|
||||
|
||||
def _do_save(self) -> None:
|
||||
cfg = load_config()
|
||||
for f in GENERAL_FIELDS:
|
||||
wid = _fid(f.path)
|
||||
@@ -95,10 +103,15 @@ class _GeneralTab(Widget):
|
||||
_set_nested(cfg, f.path, cfg_val)
|
||||
save_config(cfg)
|
||||
self.app.notify("General settings saved.")
|
||||
event.stop()
|
||||
|
||||
|
||||
class _PluginsTab(Widget):
|
||||
DEFAULT_CSS = "_PluginsTab { height: 1fr; width: 1fr; }"
|
||||
BINDINGS = [
|
||||
Binding("e", "enable_plugin", "Enable"),
|
||||
Binding("d", "disable_plugin", "Disable"),
|
||||
]
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
cfg = load_config()
|
||||
enabled = set(cfg.plugins.enabled)
|
||||
@@ -114,26 +127,35 @@ class _PluginsTab(Widget):
|
||||
)
|
||||
yield table
|
||||
with Horizontal(classes="actions"):
|
||||
yield Button("Enable", id="btn-enable", variant="success")
|
||||
yield Button("Disable", id="btn-disable")
|
||||
yield Button("Enable [e]", id="btn-enable", variant="success")
|
||||
yield Button("Disable [d]", id="btn-disable")
|
||||
|
||||
def action_enable_plugin(self) -> None:
|
||||
self._toggle_plugin("enable")
|
||||
|
||||
def action_disable_plugin(self) -> None:
|
||||
self._toggle_plugin("disable")
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
btn_id = event.button.id
|
||||
if btn_id not in ("btn-enable", "btn-disable"):
|
||||
return
|
||||
if event.button.id == "btn-enable":
|
||||
self._toggle_plugin("enable")
|
||||
event.stop()
|
||||
elif event.button.id == "btn-disable":
|
||||
self._toggle_plugin("disable")
|
||||
event.stop()
|
||||
|
||||
def _toggle_plugin(self, action: str) -> None:
|
||||
table = self.query_one("#plugins-table", DataTable)
|
||||
if table.row_count == 0:
|
||||
event.stop()
|
||||
return
|
||||
plugin_name = str(table.get_cell_at(Coordinate(table.cursor_coordinate.row, 0)))
|
||||
cfg = load_config()
|
||||
if btn_id == "btn-enable" and plugin_name not in cfg.plugins.enabled:
|
||||
if action == "enable" and plugin_name not in cfg.plugins.enabled:
|
||||
cfg.plugins.enabled.append(plugin_name)
|
||||
elif btn_id == "btn-disable" and plugin_name in cfg.plugins.enabled:
|
||||
elif action == "disable" and plugin_name in cfg.plugins.enabled:
|
||||
cfg.plugins.enabled.remove(plugin_name)
|
||||
save_config(cfg)
|
||||
self._refresh_table()
|
||||
event.stop()
|
||||
|
||||
def _refresh_table(self) -> None:
|
||||
cfg = load_config()
|
||||
@@ -150,7 +172,9 @@ class _PluginsTab(Widget):
|
||||
)
|
||||
|
||||
|
||||
class _PluginConfigTab(Widget):
|
||||
class _PluginConfigTab(VerticalScroll):
|
||||
BINDINGS = [Binding("ctrl+s", "save", "Save")]
|
||||
|
||||
def __init__(self, name: str, plugin: Any) -> None:
|
||||
super().__init__()
|
||||
self._name = name
|
||||
@@ -159,23 +183,29 @@ class _PluginConfigTab(Widget):
|
||||
def compose(self) -> ComposeResult:
|
||||
cfg = load_config()
|
||||
settings = cfg.plugin_settings.get(self._name, {})
|
||||
with VerticalScroll():
|
||||
for f in self._plugin.config_fields():
|
||||
current = settings.get(f.key, f.default)
|
||||
with Horizontal(classes="row"):
|
||||
yield Label(f.label)
|
||||
if f.type == "bool":
|
||||
yield Switch(value=bool(current), id=_pfid(self._name, f.key))
|
||||
else:
|
||||
yield Input(value=str(current), id=_pfid(self._name, f.key))
|
||||
if f.description:
|
||||
yield Label(f.description, classes="hint")
|
||||
with Horizontal(classes="actions"):
|
||||
yield Button("Save", id=f"save-{self._name}", variant="primary")
|
||||
for f in self._plugin.config_fields():
|
||||
current = settings.get(f.key, f.default)
|
||||
with Horizontal(classes="row"):
|
||||
yield Label(f.label)
|
||||
if f.type == "bool":
|
||||
yield Switch(value=bool(current), id=_pfid(self._name, f.key))
|
||||
else:
|
||||
yield Input(value=str(current), id=_pfid(self._name, f.key))
|
||||
if f.description:
|
||||
yield Label(f.description, classes="hint")
|
||||
with Horizontal(classes="actions"):
|
||||
yield Button("Save", id=f"save-{self._name}", variant="primary")
|
||||
|
||||
def action_save(self) -> None:
|
||||
self._do_save()
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
if event.button.id != f"save-{self._name}":
|
||||
return
|
||||
self._do_save()
|
||||
event.stop()
|
||||
|
||||
def _do_save(self) -> None:
|
||||
cfg = load_config()
|
||||
settings: dict[str, Any] = dict(cfg.plugin_settings.get(self._name, {}))
|
||||
for f in self._plugin.config_fields():
|
||||
@@ -187,7 +217,6 @@ class _PluginConfigTab(Widget):
|
||||
cfg.plugin_settings[self._name] = settings
|
||||
save_config(cfg)
|
||||
self.app.notify(f"{self._name} settings saved.")
|
||||
event.stop()
|
||||
|
||||
|
||||
# ── App ───────────────────────────────────────────────────────────────────────
|
||||
@@ -195,19 +224,22 @@ class _PluginConfigTab(Widget):
|
||||
class ConfigApp(App):
|
||||
TITLE = "Pyra Configuration"
|
||||
BINDINGS = [
|
||||
Binding("q", "quit", "Quit"),
|
||||
Binding("escape", "quit", "Quit"),
|
||||
Binding("q", "quit", "Quit"),
|
||||
Binding("escape", "quit", "Quit", show=False),
|
||||
Binding("ctrl+right", "next_tab", "Next tab"),
|
||||
Binding("ctrl+left", "prev_tab", "Prev tab"),
|
||||
]
|
||||
CSS = """
|
||||
Screen { background: $surface; }
|
||||
.row { height: 3; margin: 0 2; }
|
||||
.row Label { width: 26; content-align: left middle; }
|
||||
.hint { color: $text-muted; margin: 0 2 1 28; }
|
||||
.hint { color: $foreground 50%; margin: 0 2 1 28; }
|
||||
.actions { height: 3; align: right middle; margin: 1 2; }
|
||||
DataTable { height: 1fr; }
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Header()
|
||||
plugins = _installed_plugins()
|
||||
with TabbedContent():
|
||||
with TabPane("General"):
|
||||
@@ -218,6 +250,25 @@ class ConfigApp(App):
|
||||
if plugin is not None and plugin.config_fields():
|
||||
with TabPane(name):
|
||||
yield _PluginConfigTab(name, plugin)
|
||||
yield Footer()
|
||||
|
||||
def action_next_tab(self) -> None:
|
||||
tc = self.query_one(TabbedContent)
|
||||
panes = list(tc.query("TabPane"))
|
||||
ids = [p.id for p in panes]
|
||||
try:
|
||||
tc.active = ids[(ids.index(tc.active) + 1) % len(ids)]
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
def action_prev_tab(self) -> None:
|
||||
tc = self.query_one(TabbedContent)
|
||||
panes = list(tc.query("TabPane"))
|
||||
ids = [p.id for p in panes]
|
||||
try:
|
||||
tc.active = ids[(ids.index(tc.active) - 1) % len(ids)]
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
|
||||
def launch_config_tui() -> None:
|
||||
|
||||
Reference in New Issue
Block a user