diff --git a/CHANGELOG.md b/CHANGELOG.md index d5065b4..80c7e87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Fixed - `category_fetcher._do_ensure` no longer permanently flips `_batch_api_works` to False when a transient network error drops a tag-API request mid-call; the unprobed path now routes through `_probe_batch_api`, which distinguishes clean 200-with-zero-matches (structurally broken, flip) from timeout/HTTP-error (transient, retry next call) - Bookmark→library save and bookmark Save As now plumb the active site's `CategoryFetcher` through to the filename template, so `%artist%`/`%character%` tokens render correctly instead of silently dropping out when saving a post that wasn't previewed first +- Info panel no longer silently drops tags that failed to land in a cached category — any tag from `post.tag_list` not rendered under a known category section now appears in an "Other" bucket, so partial cache coverage can't make individual tags invisible ### Refactored - `category_fetcher` batch tag-API params are now built by a shared `_build_tag_api_params` helper instead of duplicated across `fetch_via_tag_api` and `_probe_batch_api` diff --git a/booru_viewer/gui/info_panel.py b/booru_viewer/gui/info_panel.py index dbb9bab..d7b2441 100644 --- a/booru_viewer/gui/info_panel.py +++ b/booru_viewer/gui/info_panel.py @@ -136,6 +136,7 @@ class InfoPanel(QWidget): # Display tags grouped by category. Colors come from the # tag*Color Qt Properties so a custom.qss can override any of # them via `InfoPanel { qproperty-tagCharacterColor: ...; }`. + rendered: set[str] = set() for category, tags in post.tag_categories.items(): color = self._category_color(category) header = QLabel(f"{category}:") @@ -145,6 +146,7 @@ class InfoPanel(QWidget): ) self._tags_flow.addWidget(header) for tag in tags: + rendered.add(tag) btn = QPushButton(tag) btn.setFlat(True) btn.setCursor(Qt.CursorShape.PointingHandCursor) @@ -155,6 +157,27 @@ class InfoPanel(QWidget): btn.setStyleSheet(style) btn.clicked.connect(lambda checked, t=tag: self.tag_clicked.emit(t)) self._tags_flow.addWidget(btn) + # Safety net: any tag in post.tag_list that didn't land in + # a cached category (batch tag API returned partial results, + # HTML scrape fell short, cache stale, etc.) is still shown + # under an "Other" bucket so tags can't silently disappear + # from the info panel. + leftover = [t for t in post.tag_list if t and t not in rendered] + if leftover: + header = QLabel("Other:") + header.setStyleSheet( + "font-weight: bold; margin-top: 6px; margin-bottom: 2px;" + ) + self._tags_flow.addWidget(header) + for tag in leftover: + btn = QPushButton(tag) + btn.setFlat(True) + btn.setCursor(Qt.CursorShape.PointingHandCursor) + btn.setStyleSheet( + "QPushButton { text-align: left; padding: 1px 4px; border: none; }" + ) + btn.clicked.connect(lambda checked, t=tag: self.tag_clicked.emit(t)) + self._tags_flow.addWidget(btn) elif not self._categories_pending: # Flat tag fallback — only when no category fetch is # in-flight. When a fetch IS pending, leaving the tags