From 5be93186f2a8ee04aa1fdc392a7e4bf0f513155e Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 20 Oct 2026 11:43:02 +0200 Subject: Hints: Delete hint acts on the ticked packages, then offers Revert Rows with only a stanza have no hint and are skipped. The deletes open the same dialog as a hint job: each removed hint's diff and Revert, which had no place in the GUI before. Co-Authored-By: Claude Opus 5.5 --- README.md | 5 +++-- sb_builder/hints_tab.py | 30 ++++++++++++++++++++---------- sb_builder/mainwindow.py | 1 + tests/test_gui.py | 12 ++++++++++++ 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0659a12..366b740 100644 --- a/README.md +++ b/README.md @@ -65,9 +65,10 @@ Every key is optional. `File > Reload config` rereads the GUI config. token) and ticks the outdated packages. **Update selected** rewrites their hints on the VM (version, URLs, md5 of the downloaded source; **no download** writes `DOWNLOAD="no"` instead; a hint sbshadow wrote, `DELREQUIRES` only, - gets the `.info` version lines added). **Delete hint** keeps a `.bak`. The Packages detail pane has **New hint** for a package + gets the `.info` version lines added). **Delete hint** removes the ticked + packages' hints, each kept as a `.bak`. The Packages detail pane has **New hint** for a package without one. -- After a hint job a dialog shows the diff of each changed hint, with +- After a hint job or a delete a dialog shows the diff of each changed hint, with **Revert** per package and **Build these** (a slackrepo update preview). neovim and openvino are refused for now: their bundled deps still need mkhint on the VM. diff --git a/sb_builder/hints_tab.py b/sb_builder/hints_tab.py index bf2011a..b78ec69 100644 --- a/sb_builder/hints_tab.py +++ b/sb_builder/hints_tab.py @@ -38,6 +38,7 @@ HINT_JOBS = ("hint-update", "hint-new") class HintsTab(QWidget): start_requested = pyqtSignal(list) # job-start arguments + result_ready = pyqtSignal(dict) # {"ok", "failed"} of hint changes made here, for the dialog def __init__(self, remote, cfg, stanzas=upstream.STANZAS, parent=None): super().__init__(parent) @@ -201,17 +202,26 @@ class HintsTab(QWidget): if pairs: self.start_requested.emit(["hint-update", *pairs] + (["--no-dl"] if self.no_dl.isChecked() else [])) - def delete(self): - name = self.current() - if name and QMessageBox.question(self, "Delete hint", f"Delete the hint of {name}? " - "(kept as .bak, Revert restores it)") \ - == QMessageBox.StandardButton.Yes: - self.remote.call(self._changed, "hint-delete", name) + def confirm(self, title, text): + return QMessageBox.question(self, title, text) == QMessageBox.StandardButton.Yes - def _changed(self, _data, err): - if err: - QMessageBox.warning(self, "Hint", err) - self.reload() + def delete(self): + """Delete the hints of the ticked packages, then the dialog to revert them.""" + ticked = set(self.checked()) + names = [r["name"] for r in self.rows if r["name"] in ticked and r["hint_path"]] + if not names or not self.confirm( + "Delete hint", f"Delete the hint of {len(names)} package(s): {', '.join(names)}?\n" + "Each is kept as .bak; the next dialog can revert it."): + return + result = {"ok": [], "failed": []} + + def done(name, err): + (result["failed"].append([name, err]) if err else result["ok"].append(name)) + if len(result["ok"]) + len(result["failed"]) == len(names): + self.reload() + self.result_ready.emit(result) + for name in names: + self.remote.call(lambda _d, err, n=name: done(n, err), "hint-delete", name) class DiffColors(QSyntaxHighlighter): diff --git a/sb_builder/mainwindow.py b/sb_builder/mainwindow.py index 0331eb9..eeac61a 100644 --- a/sb_builder/mainwindow.py +++ b/sb_builder/mainwindow.py @@ -337,6 +337,7 @@ class MainWindow(QMainWindow): if self.hints_tab is None: self.hints_tab = HintsTab(self.remote, self.cfg) self.hints_tab.start_requested.connect(lambda args: self.open_jobs().start(*args)) + self.hints_tab.result_ready.connect(self.show_hint_result) self.tabs.addTab(self.hints_tab, "Hints") self.hints_tab.reload() if name: diff --git a/tests/test_gui.py b/tests/test_gui.py index d45fffd..1f3f7b0 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -513,6 +513,18 @@ class SmokeTest(unittest.TestCase): self.assertEqual(tab.table.item(row, 5).toolTip(), "HTTPError(404)") self.assertIs(w.open_hints("foo"), tab) self.assertEqual(tab.current(), "foo") + tab.check_rows(False) + tab.confirm = lambda title, text: self.fail("asked with nothing to delete") + tab.delete() # nothing ticked + tab.check_rows(True) # foo has a hint; nuclei and curl have only a stanza + asked = [] + tab.confirm = lambda title, text: asked.append(text) or True + remote.answers["hint-diff"] = ({"bak": "A=1\n", "current": None}, None) + tab.delete() + self.assertIn("1 package(s): foo", asked[0]) + self.assertEqual([c for c in remote.calls if c[0] == "hint-delete"], [("hint-delete", "foo")]) + self.assertEqual(len(w.hint_dialogs), 1) # Revert lives there + self.assertIn("-A=1", w.hint_dialogs[0].diff.toPlainText()) def test_hint_job_end_opens_diff(self): job = dict(JOB, id="20260101-000000-hint-update", kind="hint-update", args=["nuclei=3.12.0"]) -- cgit v1.2.3