current state

This commit is contained in:
Ben Lipsey
2025-04-12 21:19:44 -07:00
parent ccc1399644
commit 8779297424
6 changed files with 70 additions and 29 deletions
+25 -7
View File
@@ -38,6 +38,9 @@ config_folder = os.path.join(locals_dir, "node-configs")
# Load translations
field_mapping, help_text = parse_ini_file(translation_file)
# Aliases
Segment = tuple[str, str, bool, bool]
WrappedLine = list[Segment]
def display_menu(state: MenuState) -> tuple[object, object]: # curses.window or pad types
@@ -183,7 +186,7 @@ def get_wrapped_help_text(
selected_option: str | None,
width: int,
max_lines: int
):
) -> list[WrappedLine]:
"""Fetches and formats help text for display, ensuring it fits within the allowed lines."""
full_help_key = '.'.join(transformed_path + [selected_option]) if selected_option else None
@@ -202,7 +205,7 @@ def get_wrapped_help_text(
r'\\033\[4m(.*?)\\033\[0m': ('settings_default', False, True) # Underline
}
def extract_ansi_segments(text):
def extract_ansi_segments(text: str) -> list[Segment]:
"""Extracts and replaces ANSI color codes, ensuring spaces are preserved."""
matches = []
last_pos = 0
@@ -232,7 +235,7 @@ def get_wrapped_help_text(
return matches
def wrap_ansi_text(segments, wrap_width):
def wrap_ansi_text(segments: list[Segment], wrap_width: int) -> list[WrappedLine]:
"""Wraps text while preserving ANSI formatting and spaces."""
wrapped_lines = []
line_buffer = []
@@ -275,7 +278,17 @@ def get_wrapped_help_text(
return wrapped_help
def move_highlight(old_idx, options, menu_win, menu_pad, help_win, help_text, max_help_lines, state):
def move_highlight(
old_idx: int,
options: list[str],
menu_win: object,
menu_pad: object,
help_win: object,
help_text: dict[str, str],
max_help_lines: int,
state: MenuState
) -> None:
if old_idx == state.selected_index: # No-op
return
@@ -323,7 +336,12 @@ def move_highlight(old_idx, options, menu_win, menu_pad, help_win, help_text, ma
draw_arrows(menu_win, visible_height, max_index, state)
def draw_arrows(win, visible_height, max_index, state):
def draw_arrows(
win: object,
visible_height: int,
max_index: int,
state: MenuState
) -> None:
# vh = visible_height + (1 if show_save_option else 0)
mi = max_index - (2 if state.show_save_option else 0)
@@ -340,7 +358,7 @@ def draw_arrows(win, visible_height, max_index, state):
win.addstr(visible_height + 3, 2, " ", get_color("settings_default"))
def settings_menu(stdscr, interface):
def settings_menu(stdscr: object, interface: object) -> None:
curses.update_lines_cols()
menu = generate_menu_from_protobuf(interface)
@@ -674,7 +692,7 @@ def settings_menu(stdscr, interface):
menu_win.refresh()
break
def set_region(interface):
def set_region(interface: object) -> None:
node = interface.getNode('^local')
device_config = node.localConfig
lora_descriptor = device_config.lora.DESCRIPTOR
+5 -5
View File
@@ -11,11 +11,11 @@ json_file_path = os.path.join(parent_dir, "config.json")
log_file_path = os.path.join(parent_dir, "client.log")
db_file_path = os.path.join(parent_dir, "client.db")
def format_json_single_line_arrays(data, indent=4):
def format_json_single_line_arrays(data: dict[str, object], indent: int = 4) -> str:
"""
Formats JSON with arrays on a single line while keeping other elements properly indented.
"""
def format_value(value, current_indent):
def format_value(value: object, current_indent: int) -> str:
if isinstance(value, dict):
items = []
for key, val in value.items():
@@ -31,7 +31,7 @@ def format_json_single_line_arrays(data, indent=4):
return format_value(data, indent)
# Recursive function to check and update nested dictionaries
def update_dict(default, actual):
def update_dict(default: dict[str, object], actual: dict[str, object]) -> bool:
updated = False
for key, value in default.items():
if key not in actual:
@@ -42,7 +42,7 @@ def update_dict(default, actual):
updated = update_dict(value, actual[key]) or updated
return updated
def initialize_config():
def initialize_config() -> dict[str, object]:
COLOR_CONFIG_DARK = {
"default": ["white", "black"],
"background": [" ", "black"],
@@ -161,7 +161,7 @@ def initialize_config():
return loaded_config
def assign_config_variables(loaded_config):
def assign_config_variables(loaded_config: dict[str, object]) -> None:
# Assign values to local variables
global db_file_path, log_file_path, message_prefix, sent_message_prefix
+1 -1
View File
@@ -1,7 +1,7 @@
import curses
from contact.ui.colors import get_color
def dialog(stdscr, title, message):
def dialog(stdscr: curses.window, title: str, message: str) -> None:
height, width = stdscr.getmaxyx()
# Calculate dialog dimensions
+17 -6
View File
@@ -1,19 +1,27 @@
from collections import OrderedDict
from meshtastic.protobuf import config_pb2, module_config_pb2, channel_pb2
import logging
import base64
import logging
import os
from collections import OrderedDict
from typing import Any
from google.protobuf.message import Message
from meshtastic.protobuf import channel_pb2, config_pb2, module_config_pb2
locals_dir = os.path.dirname(os.path.abspath(__file__))
translation_file = os.path.join(locals_dir, "localisations", "en.ini")
def encode_if_bytes(value):
def encode_if_bytes(value: Any) -> str | Any:
"""Encode byte values to base64 string."""
if isinstance(value, bytes):
return base64.b64encode(value).decode('utf-8')
return value
def extract_fields(message_instance, current_config=None):
def extract_fields(
message_instance: Message,
current_config: Message | dict[str, Any] | None = None
) -> dict[str, Any]:
if isinstance(current_config, dict): # Handle dictionaries
return {key: (None, encode_if_bytes(current_config.get(key, "Not Set"))) for key in current_config}
@@ -47,7 +55,10 @@ def extract_fields(message_instance, current_config=None):
menu[field.name] = (field, encode_if_bytes(current_value))
return menu
def generate_menu_from_protobuf(interface):
def generate_menu_from_protobuf(interface: object) -> dict[str, Any]:
"""
Builds the full settings menu structure from the protobuf definitions.
"""
menu_structure = {"Main Menu": {}}
# Add User Settings
+1 -1
View File
@@ -1,7 +1,7 @@
import curses
from contact.ui.colors import get_color
def draw_splash(stdscr):
def draw_splash(stdscr: object) -> None:
curses.curs_set(0)
stdscr.clear()
+21 -9
View File
@@ -1,6 +1,7 @@
import os
import json
import curses
from typing import Any
from contact.ui.colors import get_color, setup_colors, COLOR_MAP
from contact.ui.default_config import format_json_single_line_arrays, loaded_config
from contact.utilities.input_handlers import get_list_input
@@ -9,8 +10,7 @@ width = 80
save_option = "Save Changes"
sensitive_settings = []
def edit_color_pair(key, current_value):
def edit_color_pair(key: str, current_value: list[str]) -> list[str]:
"""
Allows the user to select a foreground and background color for a key.
"""
@@ -20,7 +20,7 @@ def edit_color_pair(key, current_value):
return [fg_color, bg_color]
def edit_value(key, current_value, state):
def edit_value(key: str, current_value: str) -> str:
height = 10
input_width = width - 16 # Allow space for "New Value: "
@@ -96,7 +96,7 @@ def edit_value(key, current_value, state):
return user_input if user_input else current_value
def display_menu(state):
def display_menu(state: Any) -> tuple[Any, Any, list[str]]:
"""
Render the configuration menu with a Save button directly added to the window.
"""
@@ -167,7 +167,14 @@ def display_menu(state):
return menu_win, menu_pad, options
def move_highlight(old_idx, options, menu_win, menu_pad, state):
def move_highlight(
old_idx: int,
options: list[str],
menu_win: curses.window,
menu_pad: curses.window,
state: Any
) -> None:
if old_idx == state.selected_index: # No-op
return
@@ -209,7 +216,12 @@ def move_highlight(old_idx, options, menu_win, menu_pad, state):
draw_arrows(menu_win, visible_height, max_index, state)
def draw_arrows(win, visible_height, max_index, state):
def draw_arrows(
win: curses.window,
visible_height: int,
max_index: int,
state: any
) -> None:
mi = max_index - (2 if state.show_save_option else 0)
@@ -225,7 +237,7 @@ def draw_arrows(win, visible_height, max_index, state):
win.addstr(visible_height + 3, 2, " ", get_color("settings_default"))
def json_editor(stdscr, state):
def json_editor(stdscr: curses.window, state: Any) -> None:
state.selected_index = 0 # Track the selected option
@@ -351,13 +363,13 @@ def json_editor(stdscr, state):
break
def save_json(file_path, data):
def save_json(file_path: str, data: dict[str, Any]) -> None:
formatted_json = format_json_single_line_arrays(data)
with open(file_path, "w", encoding="utf-8") as f:
f.write(formatted_json)
setup_colors(reinit=True)
def main(stdscr):
def main(stdscr: curses.window) -> None:
from contact.ui.ui_state import MenuState
state = MenuState()