This commit is contained in:
2026-01-11 03:34:35 -08:00
parent 5985a8306a
commit df2bb76390
2 changed files with 74 additions and 40 deletions

View File

@@ -98,6 +98,8 @@ class ConfigModal(ModalScreen):
self.editing_item_type = None # 'store' or 'provider'
self.editing_item_name = None
self.workspace_root = workspace_root
self._button_id_map = {}
self._input_id_map = {}
def compose(self) -> ComposeResult:
with Container(id="config-container"):
@@ -127,6 +129,8 @@ class ConfigModal(ModalScreen):
def refresh_view(self) -> None:
container = self.query_one("#fields-container", ScrollableContainer)
self._button_id_map.clear()
self._input_id_map.clear()
# Clear existing synchronously
for child in list(container.children):
@@ -157,10 +161,14 @@ class ConfigModal(ModalScreen):
def render_globals(self, container: ScrollableContainer) -> None:
container.mount(Label("General Configuration", classes="config-label"))
idx = 0
for k, v in self.config_data.items():
if not isinstance(v, dict) and not k.startswith("_"):
inp_id = f"global-{idx}"
self._input_id_map[inp_id] = k
container.mount(Label(k))
container.mount(Input(value=str(v), id=f"global-{k}", classes="config-input"))
container.mount(Input(value=str(v), id=inp_id, classes="config-input"))
idx += 1
def render_stores(self, container: ScrollableContainer) -> None:
container.mount(Label("Configured Stores", classes="config-label"))
@@ -169,6 +177,7 @@ class ConfigModal(ModalScreen):
container.mount(Static("No stores configured."))
else:
# stores is structured as: {type: {name_key: config}}
idx = 0
for stype, instances in stores.items():
if isinstance(instances, dict):
for name_key, conf in instances.items():
@@ -182,10 +191,16 @@ class ConfigModal(ModalScreen):
or name_key
)
edit_id = f"edit-store-{idx}"
del_id = f"del-store-{idx}"
self._button_id_map[edit_id] = ("edit", f"store-{stype}", name_key)
self._button_id_map[del_id] = ("del", f"store-{stype}", name_key)
idx += 1
row = Horizontal(
Static(f"{display_name} ({stype})", classes="item-label"),
Button("Edit", id=f"edit-store-{stype}-{name_key}"),
Button("Delete", variant="error", id=f"del-store-{stype}-{name_key}"),
Button("Edit", id=edit_id),
Button("Delete", variant="error", id=del_id),
classes="item-row"
)
container.mount(row)
@@ -196,11 +211,16 @@ class ConfigModal(ModalScreen):
if not providers:
container.mount(Static("No providers configured."))
else:
for name, _ in providers.items():
for i, (name, _) in enumerate(providers.items()):
edit_id = f"edit-provider-{i}"
del_id = f"del-provider-{i}"
self._button_id_map[edit_id] = ("edit", "provider", name)
self._button_id_map[del_id] = ("del", "provider", name)
row = Horizontal(
Static(name, classes="item-label"),
Button("Edit", id=f"edit-provider-{name}"),
Button("Delete", variant="error", id=f"del-provider-{name}"),
Button("Edit", id=edit_id),
Button("Delete", variant="error", id=del_id),
classes="item-row"
)
container.mount(row)
@@ -246,6 +266,7 @@ class ConfigModal(ModalScreen):
# Show all existing keys
existing_keys_upper = set()
idx = 0
for k, v in section.items():
if k.startswith("_"): continue
@@ -267,10 +288,13 @@ class ConfigModal(ModalScreen):
is_secret = True
container.mount(Label(label_text))
inp = Input(value=str(v), id=f"item-{k}", classes="config-input")
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)
idx += 1
# Add required/optional fields from schema that are missing
for k_upper, field_def in provider_schema_map.items():
@@ -282,7 +306,10 @@ class ConfigModal(ModalScreen):
label_text += " *"
default_val = str(field_def.get("default") or "")
inp = Input(value=default_val, id=f"item-{key}", classes="config-input")
inp_id = f"item-{idx}"
self._input_id_map[inp_id] = key
inp = Input(value=default_val, id=inp_id, classes="config-input")
idx += 1
if field_def.get("secret"):
inp.password = True
if field_def.get("placeholder"):
@@ -301,7 +328,10 @@ class ConfigModal(ModalScreen):
# Case-insensitive deduplication (fix path vs PATH)
if rk.upper() not in existing_keys_upper:
container.mount(Label(rk))
container.mount(Input(value="", id=f"item-{rk}", classes="config-input"))
inp_id = f"item-{idx}"
self._input_id_map[inp_id] = rk
container.mount(Input(value="", id=inp_id, classes="config-input"))
idx += 1
# If it's a provider, we might have required keys (legacy check fallback)
if item_type == "provider":
@@ -314,7 +344,10 @@ class ConfigModal(ModalScreen):
for rk in required_keys:
if rk.upper() not in existing_keys_upper:
container.mount(Label(rk))
container.mount(Input(value="", id=f"item-{rk}", classes="config-input"))
inp_id = f"item-{idx}"
self._input_id_map[inp_id] = rk
container.mount(Input(value="", id=inp_id, classes="config-input"))
idx += 1
except Exception:
pass
@@ -355,17 +388,22 @@ class ConfigModal(ModalScreen):
self.editing_item_name = None
self.editing_item_type = None
self.refresh_view()
elif bid.startswith("edit-store-"):
parts = bid.replace("edit-store-", "").split("-", 1)
if len(parts) == 2:
stype, name = parts
self.editing_item_type = f"store-{stype}"
elif bid in self._button_id_map:
action, itype, name = self._button_id_map[bid]
if action == "edit":
self.editing_item_type = itype
self.editing_item_name = name
self.refresh_view()
elif bid.startswith("edit-provider-"):
self.editing_item_name = bid.replace("edit-provider-", "")
self.editing_item_type = "provider"
self.refresh_view()
elif action == "del":
if itype.startswith("store-"):
stype = itype.replace("store-", "")
if "store" in self.config_data and stype in self.config_data["store"]:
if name in self.config_data["store"][stype]:
del self.config_data["store"][stype][name]
elif itype == "provider":
if "provider" in self.config_data and name in self.config_data["provider"]:
del self.config_data["provider"][name]
self.refresh_view()
elif bid == "add-store-btn":
all_classes = _discover_store_classes()
options = []
@@ -390,19 +428,6 @@ class ConfigModal(ModalScreen):
except Exception:
pass
self.app.push_screen(SelectionModal("Select Provider Type", options), callback=self.on_provider_type_selected)
elif bid.startswith("del-store-"):
parts = bid.replace("del-store-", "").split("-", 1)
if len(parts) == 2:
stype, name = parts
if "store" in self.config_data and stype in self.config_data["store"]:
if name in self.config_data["store"][stype]:
del self.config_data["store"][stype][name]
self.refresh_view()
elif bid.startswith("del-provider-"):
name = bid.replace("del-provider-", "")
if "provider" in self.config_data and name in self.config_data["provider"]:
del self.config_data["provider"][name]
self.refresh_view()
def on_store_type_selected(self, stype: str) -> None:
if not stype: return
@@ -474,11 +499,16 @@ class ConfigModal(ModalScreen):
def on_input_changed(self, event: Input.Changed) -> None:
if not event.input.id:
return
if event.input.id.startswith("global-"):
key = event.input.id.replace("global-", "")
bid = event.input.id
if bid not in self._input_id_map:
return
key = self._input_id_map[bid]
if bid.startswith("global-"):
self.config_data[key] = event.value
elif event.input.id.startswith("item-") and self.editing_item_name:
key = event.input.id.replace("item-", "")
elif bid.startswith("item-") and self.editing_item_name:
it = str(self.editing_item_type or "")
inm = str(self.editing_item_name or "")