195 lines
7.1 KiB
Python
195 lines
7.1 KiB
Python
"""
|
|
Module: ui.py
|
|
-------------
|
|
Obsahuje interaktivní rozhraní Robovojtíka založené na knihovně curses.
|
|
"""
|
|
|
|
import curses
|
|
import time
|
|
import threading
|
|
import logging
|
|
|
|
import api_interface
|
|
import shell_functions
|
|
import markdown_parser
|
|
|
|
logger = logging.getLogger("robovojtik.ui")
|
|
|
|
# Definujeme globální proměnnou automode
|
|
automode = False
|
|
|
|
def main_curses(stdscr):
|
|
curses.start_color()
|
|
curses.use_default_colors()
|
|
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) # Hlavička
|
|
curses.init_pair(2, curses.COLOR_WHITE, -1) # Chat
|
|
curses.init_pair(3, curses.COLOR_GREEN, -1) # Výstup
|
|
curses.init_pair(4, curses.COLOR_YELLOW, -1) # Vstup
|
|
curses.init_pair(5, curses.COLOR_CYAN, -1) # Spinner
|
|
curses.init_pair(6, curses.COLOR_BLACK, curses.COLOR_WHITE) # Oddělovač
|
|
|
|
curses.curs_set(1)
|
|
stdscr.nodelay(False)
|
|
stdscr.clear()
|
|
height, width = stdscr.getmaxyx()
|
|
|
|
header_height = 3
|
|
prompt_height = 2
|
|
left_width = width // 2
|
|
right_width = width - left_width - 1 # oddělovač = 1 sloupec
|
|
chat_height = height - header_height - prompt_height
|
|
|
|
header_win = curses.newwin(header_height, left_width, 0, 0)
|
|
chat_win = curses.newwin(chat_height, left_width, header_height, 0)
|
|
prompt_win = curses.newwin(prompt_height, width, height - prompt_height, 0)
|
|
output_win = curses.newwin(height - prompt_height, right_width, 0, left_width + 1)
|
|
divider_win = curses.newwin(height - prompt_height, 1, 0, left_width)
|
|
|
|
header_win.bkgd(' ', curses.color_pair(1))
|
|
chat_win.bkgd(' ', curses.color_pair(2))
|
|
prompt_win.bkgd(' ', curses.color_pair(4))
|
|
output_win.bkgd(' ', curses.color_pair(3))
|
|
divider_win.bkgd(' ', curses.color_pair(6))
|
|
|
|
d_height, _ = divider_win.getmaxyx()
|
|
for y in range(d_height):
|
|
try:
|
|
divider_win.addch(y, 0, curses.ACS_VLINE)
|
|
except curses.error:
|
|
pass
|
|
divider_win.refresh()
|
|
|
|
header_text = [
|
|
"Vítejte v Robovojtikovi!",
|
|
"Zadejte dotaz, příkaz (prefix 'cmd:'), 'automat',",
|
|
"nebo napiš 'napiš mi skript, ...' pro generování skriptu.",
|
|
"Pro ukončení zadejte 'vypni' nebo 'exit'."
|
|
]
|
|
max_chars = max(0, left_width - 2)
|
|
for idx, line in enumerate(header_text):
|
|
try:
|
|
header_win.addnstr(idx, 1, line, max_chars)
|
|
except curses.error:
|
|
pass
|
|
header_win.refresh()
|
|
|
|
chat_win.scrollok(True)
|
|
output_win.scrollok(True)
|
|
prompt_win.scrollok(True)
|
|
output_win.box()
|
|
output_win.refresh()
|
|
|
|
spinner_chars = ["|", "/", "-", "\\"]
|
|
|
|
def spinner_func(ch):
|
|
mid_x = left_width // 2 - 5
|
|
header_win.addnstr(1, mid_x, f"Čekám... {ch}", left_width - mid_x - 1, curses.color_pair(5) | curses.A_BOLD)
|
|
header_win.refresh()
|
|
|
|
chat_history = []
|
|
def add_to_chat(text):
|
|
# Použijeme markdown_parser pro formátování
|
|
segments = markdown_parser.parse_markdown(text)
|
|
for seg_text, attr in segments:
|
|
chat_win.addstr(seg_text, attr)
|
|
chat_win.addstr("\n")
|
|
chat_win.refresh()
|
|
|
|
add_to_chat("Historie chatu:")
|
|
|
|
while True:
|
|
prompt_win.erase()
|
|
prompt_win.addstr(">> ", curses.A_BOLD)
|
|
prompt_win.refresh()
|
|
curses.echo()
|
|
try:
|
|
user_input = prompt_win.getstr().decode("utf-8").strip()
|
|
except Exception:
|
|
continue
|
|
curses.noecho()
|
|
|
|
if not user_input:
|
|
continue
|
|
|
|
add_to_chat("Ty: " + user_input)
|
|
|
|
if user_input.lower() in ["vypni", "exit"]:
|
|
add_to_chat("Ukončuji Robovojtíka...")
|
|
time.sleep(1)
|
|
break
|
|
|
|
if user_input.lower() == "automat":
|
|
global automode
|
|
automode = not automode
|
|
stav = "zapnut" if automode else "vypnut"
|
|
add_to_chat(f"Automód byl nyní {stav}.")
|
|
continue
|
|
|
|
if user_input.lower().startswith("skript:"):
|
|
try:
|
|
_, rest = user_input.split("skript:", 1)
|
|
parts = rest.split(";", 1)
|
|
if len(parts) < 2:
|
|
add_to_chat("Pro vytvoření skriptu uveďte název a popis oddělené středníkem.")
|
|
continue
|
|
file_name = parts[0].strip()
|
|
description = parts[1].strip()
|
|
add_to_chat(f"Vytvářím skript '{file_name}' na základě popisu: {description}")
|
|
generated_content = api_interface.posli_dotaz_do_assistenta("Vygeneruj skript podle popisu: " + description)
|
|
add_to_chat("Generovaný obsah skriptu:\n" + generated_content)
|
|
result = shell_functions.vytvor_skript(file_name, generated_content)
|
|
add_to_chat(result)
|
|
except Exception as e:
|
|
add_to_chat(f"Chyba při vytváření skriptu: {str(e)}")
|
|
continue
|
|
|
|
if user_input.startswith("cmd:"):
|
|
command = user_input[4:].strip()
|
|
add_to_chat(f"Rozpoznán přímý příkaz: {command}")
|
|
if not automode:
|
|
prompt_win.erase()
|
|
prompt_win.addstr("Spustit tento příkaz? (y/n): ", curses.A_BOLD)
|
|
prompt_win.refresh()
|
|
potvrzeni = prompt_win.getstr().decode("utf-8").strip().lower()
|
|
add_to_chat("Ty: " + potvrzeni)
|
|
if potvrzeni != "y":
|
|
add_to_chat("Příkaz nebyl spuštěn.")
|
|
continue
|
|
output, response = shell_functions.run_command_locally_and_report(command, api_interface)
|
|
output_win.erase()
|
|
output_win.addstr(1, 1, output)
|
|
output_win.box()
|
|
output_win.refresh()
|
|
add_to_chat("Robovojtík odpovídá:\n" + response)
|
|
continue
|
|
|
|
assistant_response = api_interface.volani_asistenta(user_input, spinner_func=spinner_func)
|
|
add_to_chat("Robovojtík odpovídá:\n" + assistant_response)
|
|
|
|
if assistant_response.strip().lower().startswith("navrhovaný příkaz:"):
|
|
lines = assistant_response.splitlines()
|
|
proposal_line = lines[0]
|
|
navrhovany_prikaz = proposal_line[len("Navrhovaný příkaz:"):].strip()
|
|
add_to_chat(f"Navrhovaný příkaz: {navrhovany_prikaz}")
|
|
if not automode:
|
|
prompt_win.erase()
|
|
prompt_win.addstr("Spustit tento příkaz? (y/n): ", curses.A_BOLD)
|
|
prompt_win.refresh()
|
|
potvrzeni = prompt_win.getstr().decode("utf-8").strip().lower()
|
|
add_to_chat("Ty: " + potvrzeni)
|
|
if potvrzeni != "y":
|
|
add_to_chat("Příkaz nebyl spuštěn.")
|
|
continue
|
|
output, response = shell_functions.run_command_locally_and_report(navrhovany_prikaz, api_interface)
|
|
output_win.erase()
|
|
output_win.addstr(1, 1, output)
|
|
output_win.box()
|
|
output_win.refresh()
|
|
add_to_chat("Robovojtík odpovídá:\n" + response)
|
|
|
|
prompt_win.erase()
|
|
prompt_win.refresh()
|
|
|
|
def main_ui():
|
|
curses.wrapper(main_curses)
|