Browse Source

bug-fixes

master
Silberengel 2 weeks ago
parent
commit
38ae34001a
  1. 67
      src/imwald/core/author_html.py
  2. 38
      src/imwald/ui/feed_page.py
  3. 3
      src/imwald/ui/main_window.py
  4. 38
      src/imwald/ui/theme.py

67
src/imwald/core/author_html.py

@ -69,6 +69,7 @@ def format_nip05_chips_html( @@ -69,6 +69,7 @@ def format_nip05_chips_html(
bad: str,
chip_bg: str | None = None,
chip_border: str | None = None,
container_margin_top: str | None = None,
) -> str:
"""
Horizontal row of NIP-05 chips: favicon (per domain), identifier, verification mark.
@ -89,30 +90,39 @@ def format_nip05_chips_html( @@ -89,30 +90,39 @@ def format_nip05_chips_html(
f'style="vertical-align:middle;border-radius:3px;opacity:0.92" />'
)
if okv is True:
mark = f'<span style="color:{ok}" title="NIP-05 verified">✓</span>'
mark = f'<span style="color:{ok};font-size:15px;line-height:1" title="NIP-05 verified">✓</span>'
elif okv is False:
mark = f'<span style="color:{bad}" title="NIP-05 not verified">✗</span>'
mark = f'<span style="color:{bad};font-size:15px;line-height:1" title="NIP-05 not verified">✗</span>'
else:
mark = f'<span style="color:{dim}" title="NIP-05 not verified yet">○</span>'
mark = f'<span style="color:{dim};font-size:15px;line-height:1" title="NIP-05 not verified yet">○</span>'
if chip_bg:
brd = chip_border or dim
wrap = (
"display:inline-flex;align-items:center;gap:8px;padding:8px 16px;"
"margin:0 18px 12px 0;"
"display:inline-flex;align-items:center;gap:10px;padding:10px 18px;"
"margin:0 12px 10px 0;"
f"border-radius:999px;background:{chip_bg};border:1px solid {brd}"
)
else:
wrap = "display:inline-flex;align-items:center;gap:6px;padding:2px 6px;margin:0 12px 8px 0"
wrap = (
"display:inline-flex;align-items:center;gap:10px;padding:4px 4px 4px 0;"
"margin:0 16px 6px 0"
)
parts.append(
f'<span style="{wrap}">'
f"{icon}"
f'<span style="color:{muted};font-size:13px">{esc}</span>{mark}'
f'<span style="color:{muted};font-size:14px;line-height:1.45">{esc}</span>{mark}'
f"</span>"
)
if not parts:
return ""
mt = "14px" if chip_bg else "6px"
return f'<div style="display:flex;flex-wrap:wrap;align-items:center;margin-top:{mt}">{"".join(parts)}</div>'
if container_margin_top is not None:
mt = container_margin_top
else:
mt = "14px" if chip_bg else "4px"
return (
f'<div style="display:flex;flex-wrap:wrap;align-items:flex-start;'
f'gap:10px 14px;margin-top:{mt}">{"".join(parts)}</div>'
)
def feed_op_author_compact_html(
@ -122,37 +132,48 @@ def feed_op_author_compact_html( @@ -122,37 +132,48 @@ def feed_op_author_compact_html(
*,
status_inner_html: str,
nip05_chips_html: str,
muted: str,
event_meta_inner_html: str,
header_bg: str,
border: str,
text: str,
dim: str,
) -> str:
"""Feed OP header: larger avatar, display name, compact npub line, NIP-38 + NIP-05."""
"""Feed OP header: name + optional NIP-38, then avatar beside npub / NIP-05 / event meta in a card."""
pk_l = pubkey_hex.strip().lower()
href = html.escape(f"imwald://pub/{pk_l}", quote=True)
av = avatar_img_or_placeholder(parsed, 64, border_hex=border, profile_href=href)
name_e = html.escape(display_name_from_profile_or_hex(parsed, pubkey_hex))
npub_line, npub_title = _npub_header_lines(npub_bech)
status_block = ""
nip38_block = ""
if status_inner_html.strip():
status_block = (
f'<div style="margin-top:12px;padding-top:12px;border-top:1px solid {border}" class="md">'
nip38_block = (
f'<div style="margin-top:10px;padding-top:10px;border-top:1px solid {border}" class="md">'
f"{status_inner_html}</div>"
)
meta_block = ""
if event_meta_inner_html.strip():
meta_block = (
f'<div style="color:{dim};font-size:13px;line-height:1.5;margin:0">{event_meta_inner_html}</div>'
)
row_mt = "12px" if nip38_block else "8px"
return (
f'<div style="display:flex;align-items:flex-start;gap:20px;margin:0 0 24px 0;'
f'padding:4px 4px 22px 2px;border-bottom:1px solid {border}">'
f"{av}"
f'<div style="flex:1;min-width:0;padding-top:2px">'
f'<div style="background:{header_bg};border:1px solid {border};border-radius:10px;'
f'padding:14px 16px 18px;margin:0 0 22px 0">'
f'<a href="{href}" style="text-decoration:none;color:inherit" title="View profile">'
f'<div style="color:{text};font-size:22px;font-weight:700;letter-spacing:-0.03em;line-height:1.2">'
f'<div style="color:{text};font-size:22px;font-weight:700;letter-spacing:-0.03em;line-height:1.25">'
f"{name_e}</div>"
f'<div style="margin-top:10px;font-size:13px;color:{dim};font-family:ui-monospace,Cascadia Mono,Consolas,'
f'monospace;line-height:1.45;word-break:break-all" title="{npub_title}">{npub_line}</div>'
f"</a>"
f"{status_block}"
f"{nip38_block}"
f'<div style="display:flex;align-items:flex-start;gap:14px;margin-top:{row_mt}">'
f"{av}"
f'<div style="flex:1;min-width:0;display:flex;flex-direction:column;gap:12px">'
f'<a href="{href}" style="text-decoration:none;color:inherit" title="View profile">'
f'<div style="font-size:13px;color:{dim};font-family:ui-monospace,Cascadia Mono,Consolas,monospace;'
f'line-height:1.5;word-break:break-all" title="{npub_title}">{npub_line}</div>'
f"</a>"
f"{nip05_chips_html}"
f"</div></div>"
f"{meta_block}"
f"</div></div></div>"
)

38
src/imwald/ui/feed_page.py

@ -47,7 +47,7 @@ from imwald.core.nostr_engine import NostrEngine @@ -47,7 +47,7 @@ from imwald.core.nostr_engine import NostrEngine
from imwald.core.ranker import Ranker
from imwald.ui.media_viewer_dialog import try_open_media_url_in_app
from imwald.ui.note_text_browser import NoteTextBrowser
from imwald.ui.theme import ACCENT, BG_CODE, BORDER, FEED_DOC_CSS, TEXT, TEXT_DIM, TEXT_MUTED
from imwald.ui.theme import ACCENT, BG_CARD, BORDER, FEED_DOC_CSS, TEXT, TEXT_DIM, TEXT_MUTED
FEED_KINDS = (1, 20, 21, 30023, 9802, 11)
@ -497,8 +497,9 @@ class FeedPage(QWidget): @@ -497,8 +497,9 @@ class FeedPage(QWidget):
dim=TEXT_DIM,
ok=ACCENT,
bad="#b86a6a",
chip_bg=BG_CODE,
chip_border=BORDER,
chip_bg=None,
chip_border=None,
container_margin_top="0",
)
st_ev = get_active_user_status_event(self._db, pk)
st_html = ""
@ -509,13 +510,27 @@ class FeedPage(QWidget): @@ -509,13 +510,27 @@ class FeedPage(QWidget):
nip30_tags=_nip30_tags(st_ev),
nip30_author_pubkey=pk,
)
try:
cts = int(ev["created_at"])
rel_time, abs_time = relative_event_time_labels(cts)
except (TypeError, ValueError, OSError):
rel_time = str(ev.get("created_at") or "")
abs_time = rel_time
rel_e = html.escape(rel_time, quote=False)
abs_title = html.escape(abs_time, quote=True)
meta_inner = (
f"<span style='color:{TEXT_MUTED};font-size:12px;font-weight:600'>Kind {int(ev['kind'])}</span>"
f"<span style='color:{TEXT_MUTED};font-size:12px;font-weight:600'>&nbsp;&middot;&nbsp;</span>"
f"<span style='color:{TEXT_DIM};font-size:13px;cursor:default' title=\"{abs_title}\">{rel_e}</span>"
)
author_block = feed_op_author_compact_html(
parsed,
npub,
pk,
status_inner_html=st_html,
nip05_chips_html=chips,
muted=TEXT_MUTED,
event_meta_inner_html=meta_inner,
header_bg=BG_CARD,
border=BORDER,
text=TEXT,
dim=TEXT_DIM,
@ -527,14 +542,6 @@ class FeedPage(QWidget): @@ -527,14 +542,6 @@ class FeedPage(QWidget):
f"<div style='color:{TEXT_MUTED};font-size:13px;margin:0 0 12px 0;font-style:italic'>"
f"Trending slice (nostrarchives)</div>"
)
try:
cts = int(ev["created_at"])
rel_time, abs_time = relative_event_time_labels(cts)
except (TypeError, ValueError, OSError):
rel_time = str(ev.get("created_at") or "")
abs_time = rel_time
rel_e = html.escape(rel_time, quote=False)
abs_title = html.escape(abs_time, quote=True)
eid_raw = str(ev["id"])
if len(eid_raw) > 52:
eid_display = html.escape(eid_raw[:22] + "" + eid_raw[-18:])
@ -551,13 +558,8 @@ class FeedPage(QWidget): @@ -551,13 +558,8 @@ class FeedPage(QWidget):
"<!DOCTYPE html><html><head><meta charset=\"utf-8\">"
f"{FEED_DOC_CSS}</head><body style=\"padding:16px 22px 28px 22px\">"
f"{author_block}"
f"<div style='color:{TEXT_DIM};font-size:14px;margin:14px 0 16px 0;line-height:1.55'>"
f"<span style='color:{TEXT_MUTED};font-size:12px;font-weight:600'>Kind {int(ev['kind'])}</span>"
f"<span style='color:{TEXT_MUTED};font-size:12px;font-weight:600'>&nbsp;&middot;&nbsp;</span>"
f"<span style='color:{TEXT_DIM};font-size:13px;cursor:default' title=\"{abs_title}\">{rel_e}</span>"
f"</div>"
f"{tr}"
f'<div class="md" style="margin-top:4px">{md_body}</div>'
f'<div class="md" style="margin-top:6px;padding-top:4px;border-top:1px solid {BORDER}">{md_body}</div>'
f'<p style="color:{TEXT_DIM};font-size:12px;margin:22px 0 0 0;font-family:ui-monospace,monospace;'
f'line-height:1.4" title="{eid_title}">{eid_display}</p>'
"</body></html>"

3
src/imwald/ui/main_window.py

@ -24,6 +24,7 @@ from PySide6.QtWidgets import ( @@ -24,6 +24,7 @@ from PySide6.QtWidgets import (
QSpinBox,
QSplitter,
QStackedWidget,
QTabBar,
QTabWidget,
QToolBar,
QVBoxLayout,
@ -124,6 +125,8 @@ class MainWindow(QMainWindow): @@ -124,6 +125,8 @@ class MainWindow(QMainWindow):
self._browser_tabs.setMovable(True)
self._browser_tabs.tabCloseRequested.connect(self._on_browser_tab_close)
self._browser_tabs.addTab(self._feed, "Feed")
# Feed is not closable (_on_browser_tab_close ignores index 0); hide the redundant control.
self._browser_tabs.tabBar().setTabButton(0, QTabBar.ButtonPosition.RightSide, None)
self._profile_tabs_by_pubkey: dict[str, ProfilePage] = {}
self._search = SearchPage(db)
self._notif = NotificationsPage(db, self._accounts)

38
src/imwald/ui/theme.py

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
from __future__ import annotations
import base64
from urllib.parse import quote
from PySide6.QtWidgets import QApplication
@ -21,13 +21,13 @@ BG_CARD = "#151f1a" @@ -21,13 +21,13 @@ BG_CARD = "#151f1a"
BORDER = "#2a3d34"
BG_CODE = "#0a100d"
# Fusion’s default tab-close pixmap is nearly invisible on our dark tabs; use an explicit “×”.
_TAB_CLOSE_ICON_B64 = base64.standard_b64encode(
b'<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14">'
b'<path d="M3 3l8 8M11 3l-8 8" stroke="#c5e0d4" stroke-width="2" stroke-linecap="round" fill="none"/>'
b"</svg>"
).decode("ascii")
_TAB_CLOSE_ICON_URL = f'url("data:image/svg+xml;base64,{_TAB_CLOSE_ICON_B64}")'
# Fusion often fails to paint base64 SVG ``image:`` URLs on tab close buttons; UTF-8 data URL is more reliable.
_TAB_CLOSE_SVG = (
'<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">'
'<path d="M5 5l8 8M13 5l-8 8" stroke="#f2fff8" stroke-width="2.4" stroke-linecap="round" fill="none"/>'
"</svg>"
)
_TAB_CLOSE_ICON_URL = "url(\"data:image/svg+xml;charset=utf-8," + quote(_TAB_CLOSE_SVG, safe="") + "\")"
_W = IMAGE_DISPLAY_MAX_WIDTH_PX
FEED_DOC_CSS = f"""
@ -147,7 +147,7 @@ QTabBar::tab {{ @@ -147,7 +147,7 @@ QTabBar::tab {{
color: {TEXT_DIM};
font-size: 15px;
min-height: 22px;
padding: 10px 36px 11px 18px;
padding: 10px 52px 11px 18px;
margin: 8px 5px 0 0;
border: 1px solid {BORDER};
border-bottom: none;
@ -158,7 +158,7 @@ QTabBar::tab:selected {{ @@ -158,7 +158,7 @@ QTabBar::tab:selected {{
background-color: {BG_FIELD};
color: {TEXT};
font-weight: 600;
padding: 10px 36px 12px 18px;
padding: 10px 52px 12px 18px;
margin-bottom: -1px;
border-color: {BORDER};
border-bottom-color: {BG_FIELD};
@ -171,21 +171,23 @@ QTabBar::close-button {{ @@ -171,21 +171,23 @@ QTabBar::close-button {{
subcontrol-origin: padding;
subcontrol-position: right;
image: {_TAB_CLOSE_ICON_URL};
width: 26px;
height: 26px;
margin: 0 4px 0 6px;
width: 30px;
height: 30px;
min-width: 30px;
min-height: 30px;
margin: 0 6px 0 4px;
padding: 5px;
border-radius: 8px;
background-color: {BG_CODE};
border: 1px solid {BORDER};
background-color: {TEXT_DIM};
border: 2px solid {BORDER};
}}
QTabBar::close-button:hover {{
background-color: {BG_CARD};
border: 1px solid {ACCENT};
background-color: {TEXT_MUTED};
border: 2px solid {ACCENT};
}}
QTabBar::close-button:pressed {{
background-color: {BG_FIELD};
border: 1px solid {ACCENT_SOFT};
border: 2px solid {ACCENT_SOFT};
}}
QComboBox {{
background-color: {BG_FIELD};

Loading…
Cancel
Save