From 13ac3cbead6318d2c65ed734c2e6f988f826cca6 Mon Sep 17 00:00:00 2001 From: mia Date: Wed, 17 Jun 2026 11:00:32 +0200 Subject: [PATCH] (sophia) improved ui --- route_planning.py | 212 +++++++++++++++++++++++++++++++++------------- 1 file changed, 151 insertions(+), 61 deletions(-) diff --git a/route_planning.py b/route_planning.py index 0e93350..6d6dff6 100644 --- a/route_planning.py +++ b/route_planning.py @@ -15,24 +15,30 @@ Note: This module is a minimal TUI stub. Many features are not implemented yet (station lookup, route rendering). Add TODOs where appropriate. """ - import datetime -import time -import json -from prompt_toolkit import Application +from prompt_toolkit import Application, prompt from prompt_toolkit.buffer import Buffer from prompt_toolkit.layout.containers import HSplit, VSplit, Window from prompt_toolkit.formatted_text import HTML from prompt_toolkit.layout.controls import BufferControl, FormattedTextControl from prompt_toolkit.layout.layout import Layout from prompt_toolkit.key_binding import KeyBindings -from prompt_toolkit.shortcuts import message_dialog from prompt_toolkit.shortcuts import yes_no_dialog -from prompt_toolkit.shortcuts import input_dialog -from prompt_toolkit import prompt import backend +# ── Colour palette ──────────────────────────────────────────────────────────── +BLUE_DARK = "#0d1f3c" # header / footer background +BLUE_MID = "#1b4b9a" # accent / separator +BLUE_LIGHT = "#add4ff" # secondary text (clock date, labels) +WHITE = "#e8f0fe" # primary text +GREY = "#4a5568" # muted text / disabled +PURPLE = "#a78bfa" # journey-path dot trail +BG_MAIN = "#0a0f1e" # main body background +BG_ROW = "#111827" # alternating row tint +# ───────────────────────────────────────────────────────────────────────────── + + def run_route_planning(hafas: backend.HafasClient | None = None): """Run the route planning TUI. @@ -43,84 +49,168 @@ def run_route_planning(hafas: backend.HafasClient | None = None): if hafas is None: hafas = backend.HafasClient() - # TODO: integrate actual station search + routing using `hafas`. kb = KeyBindings() - - start = prompt("Start: ") - end = prompt("Ende: ") - station1 = hafas.getStationNames(start) - station2 = hafas.getStationNames(end) + # ── Input phase (before TUI) ────────────────────────────────────────────── + start_input = prompt("Von: ") + end_input = prompt("Nach: ") - startBuffer = Buffer() - startBuffer.text = start + station1 = hafas.getStationNames(start_input) + station2 = hafas.getStationNames(end_input) + # Resolved station names (fall back to raw input if lookup fails) + start_name = station1[0][0] if station1 else start_input + end_name = station2[0][0] if station2 else end_input - #departure = #current time, muss noch abgesprochen werden. + # TODO: fetch actual departure / arrival times from `hafas`. + # TODO: fetch actual journey duration from `hafas`. + # ── Buffers ─────────────────────────────────────────────────────────────── + start_buf = Buffer() + start_buf.text = start_name - etdBuffer = Buffer() - etdBuffer.text = "etd" #muss noch alles definiert werden + etd_buf = Buffer() + etd_buf.text = "──:──" # TODO: real departure time + end_buf = Buffer() + end_buf.text = end_name + eta_buf = Buffer() + eta_buf.text = "──:──" # TODO: real arrival time - endBuffer = Buffer() - endBuffer.text = end - - etaBuffer = Buffer() - etaBuffer.text = "eta" #--//-- - - - - drivetimeBuffer = Buffer() - #drivetimeBuffer.text = Fahrzeit von Hafas. - - infoBuffer = Buffer() - infoBuffer.text = f"Routenplanung von {station1[0][0]} nach {station2[0][0]}" - - def get_clock_text(): + # ── Dynamic text helpers ────────────────────────────────────────────────── + def clock_text(): now = datetime.datetime.now() return HTML( - f' 🕐 {now.strftime("%H:%M:%S")} ' - f'' + f'' + f'' ) - root_container = HSplit(children=[ - - HSplit(children=[ - Window(height=2, content=BufferControl(buffer=infoBuffer, focusable=False), style="fg:#2A71D5"), #informationen über Route - Window(width=1, height = 1, char='-', style="fg:#2A71D5"), #Trennlinie - Window(content=BufferControl(buffer=startBuffer, focusable=True)), #start Station - Window(content=BufferControl(buffer=etdBuffer, focusable=False)), #abfahrtszeit - Window(width = 1, height = 2, char= ".", style= "fg:#A86FD6"), #Trennlinie - Window(content=BufferControl(buffer=endBuffer, focusable=True)), #end Station - Window(content=BufferControl(buffer=etaBuffer, focusable=False)), #ankunftszeit + def header_text(): + return HTML( + f'' + f'' + ) - ]), - Window(height=1, char=' ', style="bg:#2A71D5 fg:black"), + def hint_text(): + return HTML( + f'' + f'' + ) + + def label(text): + """Left-aligned label in the narrow column.""" + return FormattedTextControl( + HTML(f'') + ) + + def sublabel(text): + """Muted sublabel for times.""" + return FormattedTextControl( + HTML(f'') + ) + + # ── Layout ──────────────────────────────────────────────────────────────── + LABEL_W = 6 # width of the label column + + root_container = HSplit([ + + # ── Header bar ──────────────────────────────────────────────────────── Window( - content=FormattedTextControl(get_clock_text), - height=1, - style="bg:#163D7A fg:#aaddff",), + height=1, + content=FormattedTextControl(header_text), + style=f"bg:{BLUE_DARK}", + ), + Window(height=1, char="─", style=f"fg:{BLUE_MID} bg:{BG_MAIN}"), + + # ── Spacer ──────────────────────────────────────────────────────────── + Window(height=1, char=" ", style=f"bg:{BG_MAIN}"), + + # ── Departure row ───────────────────────────────────────────────────── + VSplit([ + Window(width=LABEL_W, content=label("Von"), style=f"bg:{BG_ROW}"), + Window( + content=BufferControl(buffer=start_buf, focusable=True), + style=f"fg:{WHITE} bg:{BG_ROW}", + ), + ], height=1), + + VSplit([ + Window(width=LABEL_W, content=sublabel("Ab"), style=f"bg:{BG_ROW}"), + Window( + content=BufferControl(buffer=etd_buf, focusable=False), + style=f"fg:{GREY} bg:{BG_ROW}", + ), + ], height=1), + + # ── Journey path (dot trail) ─────────────────────────────────────────── + Window(height=1, char=" ", style=f"bg:{BG_MAIN}"), + Window( + height=1, + content=FormattedTextControl( + HTML(f'') + ), + style=f"bg:{BG_MAIN}", + ), + Window(height=1, char=" ", style=f"bg:{BG_MAIN}"), + + # ── Arrival row ─────────────────────────────────────────────────────── + VSplit([ + Window(width=LABEL_W, content=label("Nach"), style=f"bg:{BG_ROW}"), + Window( + content=BufferControl(buffer=end_buf, focusable=True), + style=f"fg:{WHITE} bg:{BG_ROW}", + ), + ], height=1), + + VSplit([ + Window(width=LABEL_W, content=sublabel("An"), style=f"bg:{BG_ROW}"), + Window( + content=BufferControl(buffer=eta_buf, focusable=False), + style=f"fg:{GREY} bg:{BG_ROW}", + ), + ], height=1), + + # ── Spacer ──────────────────────────────────────────────────────────── + Window(char=" ", style=f"bg:{BG_MAIN}"), + + # ── Clock bar ───────────────────────────────────────────────────────── + Window(height=1, char="─", style=f"fg:{BLUE_MID} bg:{BG_MAIN}"), + Window( + content=FormattedTextControl(clock_text), + height=1, + style=f"bg:{BLUE_DARK}", + ), + + # ── Keybinding hint bar ─────────────────────────────────────────────── + Window( + height=1, + content=FormattedTextControl(hint_text), + style=f"bg:{BLUE_DARK}", + ), ]) - - @kb.add('c-e') + # ── Key bindings ────────────────────────────────────────────────────────── + @kb.add("c-e") def _(event): - result = yes_no_dialog( - title='Programm beenden', - text='Fenster schließen?').run() - if result == True: + confirmed = yes_no_dialog( + title="Programm beenden", + text="haTerm schließen?", + ).run() + if confirmed: event.app.exit() - - + # ── Run ─────────────────────────────────────────────────────────────────── layout = Layout(root_container) - app = Application(layout=layout, key_bindings=kb, full_screen=True) + app = Application( + layout=layout, + key_bindings=kb, + full_screen=True, + refresh_interval=1.0, # keeps the clock ticking + mouse_support=False, + ) try: app.run() except KeyboardInterrupt: - # graceful exit on Ctrl-C return -