From 722eedc3c379888717cc77e2475a6d6dabc24efa Mon Sep 17 00:00:00 2001 From: Nose Date: Sun, 11 Jan 2026 03:47:25 -0800 Subject: [PATCH] h --- Provider/openlibrary.py | 2 +- ProviderCore/base.py | 3 +- Store/_base.py | 3 +- TUI/modalscreen/config_modal.py | 85 +++++++++++++++++++++++---------- 4 files changed, 64 insertions(+), 29 deletions(-) diff --git a/Provider/openlibrary.py b/Provider/openlibrary.py index c441813..5cd5c1c 100644 --- a/Provider/openlibrary.py +++ b/Provider/openlibrary.py @@ -306,7 +306,7 @@ class OpenLibrary(Provider): "key": "quality", "label": "Image Quality", "default": "medium", - "placeholder": "high, medium, low" + "choices": ["high", "medium", "low"] } ] diff --git a/ProviderCore/base.py b/ProviderCore/base.py index b86e698..1cd3066 100644 --- a/ProviderCore/base.py +++ b/ProviderCore/base.py @@ -152,7 +152,8 @@ class Provider(ABC): "label": "API Key", "default": "", "required": True, - "secret": True + "secret": True, + "choices": ["Option 1", "Option 2"] } """ return [] diff --git a/Store/_base.py b/Store/_base.py index 4993461..d99703f 100644 --- a/Store/_base.py +++ b/Store/_base.py @@ -21,7 +21,8 @@ class Store(ABC): "key": "PATH", "label": "Store Location", "default": "", - "required": True + "required": True, + "choices": ["/mnt/media", "/srv/data"] } """ return [] diff --git a/TUI/modalscreen/config_modal.py b/TUI/modalscreen/config_modal.py index 8191e01..ccc8e94 100644 --- a/TUI/modalscreen/config_modal.py +++ b/TUI/modalscreen/config_modal.py @@ -1,7 +1,7 @@ from textual.app import ComposeResult from textual.screen import ModalScreen from textual.containers import Container, Horizontal, Vertical, ScrollableContainer -from textual.widgets import Static, Button, Input, Label, ListView, ListItem, Rule, OptionList, Footer +from textual.widgets import Static, Button, Input, Label, ListView, ListItem, Rule, OptionList, Footer, Select from textual import on from textual.message import Message from typing import Dict, Any, List, Optional @@ -84,6 +84,11 @@ class ConfigModal(ModalScreen): content-align: left middle; } + .item-row Button { + width: 15; + margin-left: 1; + } + Button { margin: 0 1; } @@ -279,6 +284,7 @@ class ConfigModal(ModalScreen): # Determine display props from schema label_text = k is_secret = False + choices = None schema = provider_schema_map.get(k_upper) if schema: label_text = schema.get("label") or k @@ -286,14 +292,27 @@ class ConfigModal(ModalScreen): label_text += " *" if schema.get("secret"): is_secret = True + choices = schema.get("choices") container.mount(Label(label_text)) inp_id = f"item-{idx}" self._input_id_map[inp_id] = k - inp = Input(value=str(v), id=inp_id, classes="config-input") - if is_secret: - inp.password = True - container.mount(inp) + + if choices: + # Select takes a list of (label, value) tuples + select_options = [(str(c), str(c)) for c in choices] + # If current value not in choices, add it or stay blank + current_val = str(v) + if current_val not in [str(c) for c in choices]: + select_options.insert(0, (current_val, current_val)) + + sel = Select(select_options, value=current_val, id=inp_id) + container.mount(sel) + else: + inp = Input(value=str(v), id=inp_id, classes="config-input") + if is_secret: + inp.password = True + container.mount(inp) idx += 1 # Add required/optional fields from schema that are missing @@ -306,17 +325,24 @@ class ConfigModal(ModalScreen): label_text += " *" default_val = str(field_def.get("default") or "") + choices = field_def.get("choices") + + container.mount(Label(label_text)) inp_id = f"item-{idx}" self._input_id_map[inp_id] = key - inp = Input(value=default_val, id=inp_id, classes="config-input") + + if choices: + select_options = [(str(c), str(c)) for c in choices] + sel = Select(select_options, value=default_val, id=inp_id) + container.mount(sel) + else: + inp = Input(value=default_val, id=inp_id, classes="config-input") + if field_def.get("secret"): + inp.password = True + if field_def.get("placeholder"): + inp.placeholder = field_def.get("placeholder") + container.mount(inp) idx += 1 - if field_def.get("secret"): - inp.password = True - if field_def.get("placeholder"): - inp.placeholder = field_def.get("placeholder") - - container.mount(Label(label_text)) - container.mount(inp) # If it's a store, we might have required keys (legacy check fallback) if item_type.startswith("store-"): @@ -495,20 +521,15 @@ class ConfigModal(ModalScreen): self.editing_item_name = ptype self.refresh_view() - @on(Input.Changed) - def on_input_changed(self, event: Input.Changed) -> None: - if not event.input.id: - return - - bid = event.input.id - if bid not in self._input_id_map: + def _update_config_value(self, widget_id: str, value: Any) -> None: + if widget_id not in self._input_id_map: return - key = self._input_id_map[bid] + key = self._input_id_map[widget_id] - if bid.startswith("global-"): - self.config_data[key] = event.value - elif bid.startswith("item-") and self.editing_item_name: + if widget_id.startswith("global-"): + self.config_data[key] = value + elif widget_id.startswith("item-") and self.editing_item_name: it = str(self.editing_item_type or "") inm = str(self.editing_item_name or "") @@ -521,14 +542,26 @@ class ConfigModal(ModalScreen): self.config_data["store"][stype] = {} if inm not in self.config_data["store"][stype]: self.config_data["store"][stype][inm] = {} - self.config_data["store"][stype][inm][key] = event.value + self.config_data["store"][stype][inm][key] = value else: # Provider or other top-level sections if it not in self.config_data: self.config_data[it] = {} if inm not in self.config_data[it]: self.config_data[it][inm] = {} - self.config_data[it][inm][key] = event.value + self.config_data[it][inm][key] = value + + @on(Input.Changed) + def on_input_changed(self, event: Input.Changed) -> None: + if event.input.id: + self._update_config_value(event.input.id, event.value) + + @on(Select.Changed) + def on_select_changed(self, event: Select.Changed) -> None: + if event.select.id: + # Select value can be the 'Select.BLANK' sentinel + if event.value != Select.BLANK: + self._update_config_value(event.select.id, event.value) def save_all(self) -> None: save_config(self.config_data, config_dir=self.workspace_root)