"""APIClient - Agent — Environment Manager Dialog.""" from PyQt6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QListWidget, QListWidgetItem, QPushButton, QTableWidget, QTableWidgetItem, QHeaderView, QLabel, QInputDialog, QMessageBox, QSplitter, QWidget ) from PyQt6.QtCore import Qt from PyQt6.QtGui import QBrush, QColor from app.ui.theme import Colors from app.core import storage from app.models import Environment class EnvironmentDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("Manage Environments") self.setMinimumSize(760, 520) self._current_env: Environment | None = None self._dirty = False layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) # ── Title bar ───────────────────────────────────────────────────────── title_bar = QWidget() title_bar.setObjectName("panelHeader") title_bar.setFixedHeight(48) tl = QHBoxLayout(title_bar) tl.setContentsMargins(16, 0, 16, 0) title = QLabel("Manage Environments") title.setObjectName("panelTitle") tl.addWidget(title) layout.addWidget(title_bar) # ── Splitter ────────────────────────────────────────────────────────── splitter = QSplitter(Qt.Orientation.Horizontal) splitter.setHandleWidth(1) # Left: environment list left = QWidget() left.setObjectName("sidebarPanel") left.setFixedWidth(220) ll = QVBoxLayout(left) ll.setContentsMargins(0, 0, 0, 0) ll.setSpacing(0) list_header = QWidget() list_header.setObjectName("sectionHeader") list_header.setFixedHeight(36) lh = QHBoxLayout(list_header) lh.setContentsMargins(12, 0, 8, 0) env_heading = QLabel("ENVIRONMENTS") env_heading.setObjectName("sectionLabel") lh.addWidget(env_heading) lh.addStretch() add_env_btn = QPushButton("+") add_env_btn.setObjectName("ghost") add_env_btn.setFixedSize(26, 26) add_env_btn.setToolTip("Add Environment") add_env_btn.clicked.connect(self._add_env) lh.addWidget(add_env_btn) ll.addWidget(list_header) self.env_list = QListWidget() self.env_list.setObjectName("sidebarList") self.env_list.currentItemChanged.connect(self._on_env_selected) ll.addWidget(self.env_list) btn_row = QHBoxLayout() btn_row.setContentsMargins(8, 6, 8, 6) btn_row.setSpacing(6) self.activate_btn = QPushButton("Set Active") self.activate_btn.clicked.connect(self._set_active) self.del_btn = QPushButton("Delete") self.del_btn.setObjectName("danger") self.del_btn.clicked.connect(self._delete_env) btn_row.addWidget(self.activate_btn) btn_row.addWidget(self.del_btn) ll.addLayout(btn_row) splitter.addWidget(left) # Right: variable table right = QWidget() right.setObjectName("panelBody") rl = QVBoxLayout(right) rl.setContentsMargins(0, 0, 0, 0) rl.setSpacing(0) var_header = QWidget() var_header.setObjectName("panelHeader") var_header.setFixedHeight(36) vh = QHBoxLayout(var_header) vh.setContentsMargins(16, 0, 12, 0) var_label = QLabel("Variables") var_label.setObjectName("fieldLabel") vh.addWidget(var_label) vh.addStretch() add_var_btn = QPushButton("+ Add Variable") add_var_btn.setObjectName("ghost") add_var_btn.clicked.connect(self._add_var_row) vh.addWidget(add_var_btn) rl.addWidget(var_header) self.var_table = QTableWidget(0, 2) self.var_table.setHorizontalHeaderLabels(["Variable", "Value"]) self.var_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) self.var_table.verticalHeader().setVisible(False) self.var_table.setAlternatingRowColors(True) self.var_table.itemChanged.connect(self._on_var_changed) rl.addWidget(self.var_table, 1) splitter.addWidget(right) splitter.setSizes([220, 520]) layout.addWidget(splitter, 1) # ── Bottom bar ──────────────────────────────────────────────────────── bottom = QWidget() bottom.setObjectName("panelFooter") bottom.setFixedHeight(52) bl = QHBoxLayout(bottom) bl.setContentsMargins(16, 0, 16, 0) bl.addStretch() save_btn = QPushButton("Save & Close") save_btn.setObjectName("accent") save_btn.setFixedWidth(120) save_btn.clicked.connect(self._save_and_close) bl.addWidget(save_btn) layout.addWidget(bottom) self._load_envs() # ── Data loading ────────────────────────────────────────────────────────── def _load_envs(self): self.env_list.clear() for env in storage.get_environments(): label = f"{'● ' if env.is_active else ' '} {env.name}" item = QListWidgetItem(label) item.setData(Qt.ItemDataRole.UserRole, env) if env.is_active: item.setForeground(QBrush(QColor(Colors.ACCENT))) self.env_list.addItem(item) def _on_env_selected(self, current, _previous): if self._current_env and self._dirty: self._current_env.variables = self._get_vars() if not current: return self._current_env = current.data(Qt.ItemDataRole.UserRole) self._dirty = False self._load_vars(self._current_env.variables) def _load_vars(self, variables: dict): self.var_table.blockSignals(True) self.var_table.setRowCount(0) for k, v in variables.items(): row = self.var_table.rowCount() self.var_table.insertRow(row) self.var_table.setItem(row, 0, QTableWidgetItem(k)) self.var_table.setItem(row, 1, QTableWidgetItem(str(v))) self.var_table.blockSignals(False) def _on_var_changed(self): if self._current_env: self._dirty = True def _get_vars(self) -> dict: result = {} for row in range(self.var_table.rowCount()): k = self.var_table.item(row, 0) v = self.var_table.item(row, 1) if k and k.text().strip(): result[k.text().strip()] = v.text() if v else "" return result # ── Actions ─────────────────────────────────────────────────────────────── def _add_var_row(self): if not self._current_env: QMessageBox.information( self, "No Environment Selected", "Select or create an environment first." ) return row = self.var_table.rowCount() self.var_table.insertRow(row) self.var_table.setItem(row, 0, QTableWidgetItem("")) self.var_table.setItem(row, 1, QTableWidgetItem("")) self.var_table.editItem(self.var_table.item(row, 0)) def _add_env(self): name, ok = QInputDialog.getText(self, "New Environment", "Name:") if not ok or not name.strip(): return env = Environment(name=name.strip()) env_id = storage.save_environment(env) env.id = env_id self._load_envs() for i in range(self.env_list.count()): item = self.env_list.item(i) if item.data(Qt.ItemDataRole.UserRole).id == env_id: self.env_list.setCurrentItem(item) break def _delete_env(self): item = self.env_list.currentItem() if not item: return env = item.data(Qt.ItemDataRole.UserRole) reply = QMessageBox.question( self, "Delete Environment", f"Delete '{env.name}'? This cannot be undone.", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel ) if reply == QMessageBox.StandardButton.Yes: storage.delete_environment(env.id) self._current_env = None self._dirty = False self.var_table.setRowCount(0) self._load_envs() def _set_active(self): item = self.env_list.currentItem() if not item: return env = item.data(Qt.ItemDataRole.UserRole) storage.set_active_environment(env.id) self._load_envs() def _save_and_close(self): if self._current_env: self._current_env.variables = self._get_vars() storage.save_environment(self._current_env) self.accept()