Categorized tags were capped at 50 per category and flat tags at
100. Tags area is already inside a QScrollArea so there's no layout
reason for the limit. All tags now render.
behavior change: posts with 50+ tags per category now show all of
them instead of silently truncating.
Replaces the inline f-string concatenation of post.source into the
RichText document with a call through build_source_html(), which
escapes both the href value and the visible display text.
Also escapes the filetype field for defense-in-depth — the value
comes from a parsed URL suffix (effectively booru-controlled) and
the previous code interpolated it raw.
Removes the dead duplicate setText() call that wrote a plain-text
version before being overwritten by the RichText version on the
next line.
Audit-Ref: SECURITY_AUDIT.md finding #6
Severity: Medium
When a category fetch is about to fire (Rule34/Safebooru.org/
Moebooru on first click), the info panel was rendering the full
flat tag list, then ~200ms later re-rendering with categorized
tags. The re-layout from flat→categorized looked like a visual
hitch.
Fix: new _categories_pending flag on InfoPanel. When True, the
flat-tag fallback branch is skipped — the tags area stays empty
until categories arrive and render in one clean pass.
_ensure_post_categories_async sets _categories_pending = True
before scheduling the fetch (or False if no fetcher = Danbooru)
_on_categories_updated clears _categories_pending = False
Visual result:
Danbooru/e621: instant (inline, no flag)
Gelbooru with auth: instant (background prefetch beat the click)
Rule34/SB.org/Moebooru: empty ~200ms → categories appear cleanly
(no flat→categorized re-layout)
Step 11 of the gui/app.py + gui/preview.py structural refactor. Pure
copy: the toggleable info panel widget with category-coloured tag
list moves to its own module. The new module gets its own
`log = logging.getLogger("booru")` at module level — same logger
instance the rest of the app uses (logging.getLogger is idempotent
by name), matching the existing per-module convention used by
grid.py / bookmarks.py / library.py. All six tag-color Qt Properties
preserved verbatim. app.py grows another shim line. Shim removed
in commit 14.