This commit is contained in:
2026-01-11 03:47:25 -08:00
parent df2bb76390
commit 722eedc3c3
4 changed files with 64 additions and 29 deletions

View File

@@ -306,7 +306,7 @@ class OpenLibrary(Provider):
"key": "quality",
"label": "Image Quality",
"default": "medium",
"placeholder": "high, medium, low"
"choices": ["high", "medium", "low"]
}
]

View File

@@ -152,7 +152,8 @@ class Provider(ABC):
"label": "API Key",
"default": "",
"required": True,
"secret": True
"secret": True,
"choices": ["Option 1", "Option 2"]
}
"""
return []

View File

@@ -21,7 +21,8 @@ class Store(ABC):
"key": "PATH",
"label": "Store Location",
"default": "",
"required": True
"required": True,
"choices": ["/mnt/media", "/srv/data"]
}
"""
return []

View File

@@ -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)