Merge pull request 'fix: block duplicate env/header keys at entry instead of silently dropping one (#38)' (#49) from fix/38-duplicate-kv-keys into main
CI / Tests (py3.12 / windows-latest) (push) Successful in 18s
CI / Lint (ruff) (push) Successful in 13s
CI / Tests (py3.10 / ubuntu-latest) (push) Successful in 12s
CI / Tests (py3.12 / ubuntu-latest) (push) Successful in 13s
CI / Tests (py3.13 / ubuntu-latest) (push) Successful in 12s
CI / Tests (py3.12 / windows-latest) (push) Successful in 18s
CI / Lint (ruff) (push) Successful in 13s
CI / Tests (py3.10 / ubuntu-latest) (push) Successful in 12s
CI / Tests (py3.12 / ubuntu-latest) (push) Successful in 13s
CI / Tests (py3.13 / ubuntu-latest) (push) Successful in 12s
This commit was merged in pull request #49.
This commit is contained in:
@@ -19,6 +19,7 @@ from PySide6.QtCore import QRect, QSettings, QSize, Qt, QThread, QTimer, QUrl, S
|
|||||||
from PySide6.QtGui import (
|
from PySide6.QtGui import (
|
||||||
QAction,
|
QAction,
|
||||||
QColor,
|
QColor,
|
||||||
|
QCursor,
|
||||||
QDesktopServices,
|
QDesktopServices,
|
||||||
QGuiApplication,
|
QGuiApplication,
|
||||||
QIcon,
|
QIcon,
|
||||||
@@ -51,6 +52,7 @@ from PySide6.QtWidgets import (
|
|||||||
QStyledItemDelegate,
|
QStyledItemDelegate,
|
||||||
QTableWidget,
|
QTableWidget,
|
||||||
QTableWidgetItem,
|
QTableWidgetItem,
|
||||||
|
QToolTip,
|
||||||
QVBoxLayout,
|
QVBoxLayout,
|
||||||
QWidget,
|
QWidget,
|
||||||
)
|
)
|
||||||
@@ -271,10 +273,31 @@ class KeyValueTable(QWidget):
|
|||||||
self.reveal_btn.setText("Hide secrets" if on else "Show secrets")
|
self.reveal_btn.setText("Hide secrets" if on else "Show secrets")
|
||||||
self.table.viewport().update()
|
self.table.viewport().update()
|
||||||
|
|
||||||
def _changed(self, *_):
|
def _changed(self, item=None, *_):
|
||||||
|
if item is not None and item.column() == 0:
|
||||||
|
new_key = item.text().strip()
|
||||||
|
row = item.row()
|
||||||
|
if new_key and self._is_duplicate_key(new_key, row):
|
||||||
|
prev_key = item.data(Qt.ItemDataRole.UserRole)
|
||||||
|
prev_key = prev_key if prev_key is not None else ""
|
||||||
|
self.table.blockSignals(True)
|
||||||
|
item.setText(prev_key)
|
||||||
|
self.table.blockSignals(False)
|
||||||
|
QToolTip.showText(QCursor.pos(), f"Duplicate key '{new_key}' — reverted.")
|
||||||
|
return
|
||||||
|
item.setData(Qt.ItemDataRole.UserRole, new_key)
|
||||||
if self._on_change:
|
if self._on_change:
|
||||||
self._on_change()
|
self._on_change()
|
||||||
|
|
||||||
|
def _is_duplicate_key(self, key: str, ignore_row: int) -> bool:
|
||||||
|
for r in range(self.table.rowCount()):
|
||||||
|
if r == ignore_row:
|
||||||
|
continue
|
||||||
|
other = self.table.item(r, 0)
|
||||||
|
if other and other.text().strip() == key:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def _add_row(self):
|
def _add_row(self):
|
||||||
dlg = QDialog(self.window())
|
dlg = QDialog(self.window())
|
||||||
dlg.setWindowTitle(f"Add {self._key_label}")
|
dlg.setWindowTitle(f"Add {self._key_label}")
|
||||||
@@ -301,6 +324,13 @@ class KeyValueTable(QWidget):
|
|||||||
grid.addWidget(val_edit, 1, 1)
|
grid.addWidget(val_edit, 1, 1)
|
||||||
v.addLayout(grid)
|
v.addLayout(grid)
|
||||||
|
|
||||||
|
# Shown when the typed key already exists in the table.
|
||||||
|
dup_warn = QLabel("")
|
||||||
|
dup_warn.setStyleSheet(f"color: {WARN};")
|
||||||
|
dup_warn.setWordWrap(True)
|
||||||
|
dup_warn.hide()
|
||||||
|
v.addWidget(dup_warn)
|
||||||
|
|
||||||
# Type an API_KEY/TOKEN-style name and the value field masks itself.
|
# Type an API_KEY/TOKEN-style name and the value field masks itself.
|
||||||
def _sync_echo(text):
|
def _sync_echo(text):
|
||||||
secret = core.is_secret_key(text)
|
secret = core.is_secret_key(text)
|
||||||
@@ -316,7 +346,15 @@ class KeyValueTable(QWidget):
|
|||||||
ok_btn = btns.button(QDialogButtonBox.StandardButton.Ok)
|
ok_btn = btns.button(QDialogButtonBox.StandardButton.Ok)
|
||||||
ok_btn.setObjectName("primary")
|
ok_btn.setObjectName("primary")
|
||||||
ok_btn.setEnabled(False)
|
ok_btn.setEnabled(False)
|
||||||
key_edit.textChanged.connect(lambda t: ok_btn.setEnabled(bool(t.strip())))
|
|
||||||
|
def _validate(text):
|
||||||
|
k = text.strip()
|
||||||
|
dup = bool(k) and self._is_duplicate_key(k, ignore_row=-1)
|
||||||
|
ok_btn.setEnabled(bool(k) and not dup)
|
||||||
|
dup_warn.setText(f"'{k}' already exists" if dup else "")
|
||||||
|
dup_warn.setVisible(dup)
|
||||||
|
|
||||||
|
key_edit.textChanged.connect(_validate)
|
||||||
btns.accepted.connect(dlg.accept)
|
btns.accepted.connect(dlg.accept)
|
||||||
btns.rejected.connect(dlg.reject)
|
btns.rejected.connect(dlg.reject)
|
||||||
v.addWidget(btns)
|
v.addWidget(btns)
|
||||||
@@ -330,13 +368,15 @@ class KeyValueTable(QWidget):
|
|||||||
return
|
return
|
||||||
k = key_edit.text().strip()
|
k = key_edit.text().strip()
|
||||||
val = val_edit.text()
|
val = val_edit.text()
|
||||||
if not k:
|
if not k or self._is_duplicate_key(k, ignore_row=-1):
|
||||||
return
|
return
|
||||||
if self._before_change:
|
if self._before_change:
|
||||||
self._before_change()
|
self._before_change()
|
||||||
r = self.table.rowCount()
|
r = self.table.rowCount()
|
||||||
self.table.insertRow(r)
|
self.table.insertRow(r)
|
||||||
self.table.setItem(r, 0, QTableWidgetItem(k))
|
key_item = QTableWidgetItem(k)
|
||||||
|
key_item.setData(Qt.ItemDataRole.UserRole, k)
|
||||||
|
self.table.setItem(r, 0, key_item)
|
||||||
self.table.setItem(r, 1, QTableWidgetItem(val))
|
self.table.setItem(r, 1, QTableWidgetItem(val))
|
||||||
self._changed()
|
self._changed()
|
||||||
|
|
||||||
@@ -355,7 +395,9 @@ class KeyValueTable(QWidget):
|
|||||||
for k, v in (d or {}).items():
|
for k, v in (d or {}).items():
|
||||||
r = self.table.rowCount()
|
r = self.table.rowCount()
|
||||||
self.table.insertRow(r)
|
self.table.insertRow(r)
|
||||||
self.table.setItem(r, 0, QTableWidgetItem(str(k)))
|
key_item = QTableWidgetItem(str(k))
|
||||||
|
key_item.setData(Qt.ItemDataRole.UserRole, str(k))
|
||||||
|
self.table.setItem(r, 0, key_item)
|
||||||
self.table.setItem(r, 1, QTableWidgetItem(str(v)))
|
self.table.setItem(r, 1, QTableWidgetItem(str(v)))
|
||||||
self.table.blockSignals(False)
|
self.table.blockSignals(False)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user