The TV quick-connect was rendered as standalone cards inserted between two
sliced block lists, which broke the panel's chosen style (e.g. split the
timeline rail) and dropped a step. Now it renders THROUGH the active renderer:
- Blocks gain an optional customNode; every renderer (cards/timeline/accordion/
minimal) drops it into the block body, so it inherits that exact style.
- InstallationGuide renders the FULL block list in the active style and injects
the TV connect widget as customNode on the "add subscription" step (no more
slicing / dropped steps) — only for the Happ Android TV app.
- TvQuickConnect is now content-only (no one-off cards/titles); the block step is
the wrapper. Buttons already use the shared blockButtonClass.
Verified against Happ docs: the sendtv 5-digit-code / Web-Import API
(check.happ.su/sendtv) is Android-TV-only. Apple TV (tvOS) uses a different
mechanism (tv.happ.su temporary code), so POSTing the code to check.happ.su
would hit the wrong endpoint. The block was gated on isTvPlatform (androidTV ||
appleTV); narrow it to androidTV. Apple TV now renders its normal config blocks.
The TV quick-connect block (Подключить TV / Сканировать QR) is a Happ-only
feature (check.happ.su/sendtv), but it rendered for ANY app on a TV platform and
used one-off button styles (solid btn-primary + grey btn-secondary) that clashed
with the outlined-accent buttons that come from the subscription-page config.
- Show TvQuickConnect only for the Happ app (detected by its happ:// deep-link
scheme, name as fallback).
- Extract the config-block button style into a shared blockButtonClass and use it
for the TV buttons too, so they adapt to exactly the same visual language as the
panel-config blocks (no divergent one-off styling).
- Align the code input with the Happ Android TV API doc (5-digit numeric):
placeholder 12345 + inputMode numeric. The send call already matches the spec
(POST check.happ.su/sendtv/{code}, body {"data": base64(subscriptionUrl)}).
A personal glyph-by-glyph re-review of the migration (the "steering wheel"
guard) found two wrong matches:
- MaintenanceScreen: a wrench/maintenance svg had been mapped to AdjustmentsIcon
(sliders) → use a proper WrenchIcon (PiWrench).
- InstallationGuide tutorial button: an open-book svg had been mapped to
DocumentIcon (file) → use BookOpenIcon (PiBookOpen).
Add WrenchIcon and BookOpenIcon to the central barrel. All other replacements
were verified correct (paper-plane→Send, server→HardDrives, warning-triangle→
Warning, etc.).
Audit-driven sweep: replaced 168 hand-written inline <svg> icons across 77
files with the central Phosphor (react-icons/pi) components from
@/components/icons, preserving each icon's size classes and colour (dynamic
stroke colours via the parent's currentColor, RefreshIcon's spinning state,
conditional rotate-180 chevrons).
Verified: tsc + vite build + eslint clean; an adversarial diff review of all
changed files found the replacements correct (70 files clean, 0 blockers).
Remaining inline <svg> dropped from 262 to ~95 — the survivors are legitimate
non-icons (brand/provider logos, loading spinners & framer-motion animations,
charts/sparklines, background decoration) plus a few ambiguous glyphs.
Replace the cabinet's hand-written heroicons-style SVG icon components with
the Remnawave panel's own icon family — Phosphor via react-icons/pi (Duotone).
All icons now live in a single central barrel (src/components/icons:
index + extended-icons + editor-icons, 144 icons); every feature file imports
from it instead of redefining inline SVGs (~440 icon defs removed).
- Add react-icons dependency
- Desktop nav (AppShell) now uses the central Phosphor icons, removing a
stroke-vs-duotone inconsistency the partial migration introduced
- Icons with custom props (SortIcon's direction, expandable chevrons) kept as
thin Phosphor wrappers; the RemnawaveIcon brand logo and animated SVGs
(checkmarks, spinners) are intentionally left as-is
- Restore the original per-call-site icon sizes wherever the centralized
default differed (211 call sites)
Subscription copy-URL button only had a title attribute; back and QR-open buttons in
InstallationGuide had no accessible name. Add aria-label so screen readers announce
the action (WCAG 2.4.6).
Sweep all amber-N color classes to warning-N (uniformly pending/warning semantics).
Yellow and orange left for per-file review (mixed Stars brand + semantic uses).
When user selects Android TV or Apple TV platform (from non-TV device),
shows 5-char code input + QR scanner to send subscription to TV via
Happ TV API. Hybrid QR strategy: native Telegram scanner on mobile,
html5-qrcode CDN fallback on desktop. i18n keys for ru/en/zh/fa.
Based on PR #415 by @smediainfo.
- Fix stale initData comparison in clearStaleSessionIfNeeded that destroyed
valid refresh tokens on mobile WebView reopens
- Restrict X-Telegram-Init-Data header to auth endpoints only
- Close Mini App on auth retry to force fresh initData from Telegram
- Merge Connection page error/not-configured states for better UX
- Remove unnecessary comments across 40+ files (section dividers,
restating comments, noise catch block comments)
- Configure ESLint allowEmptyCatch to properly handle intentional
empty catch blocks (62 warnings resolved)
- New ConnectionQR page with fullscreen QR display (no modal)
- QR button in InstallationGuide header next to platform selector
- Respects hideLink setting: shows URL below QR only when visible
- i18n translations for ru, en, zh, fa