Frontend webowy

repartee zawiera wbudowany frontend webowy, który działa obok interfejsu terminalowego. Uzyskaj dostęp do sesji IRC z dowolnej przeglądarki -- na komputerze lub urządzeniu mobilnym -- z dwukierunkową synchronizacją w czasie rzeczywistym.

Repartee Web Frontend Demo

TUI (po lewej) | Mobilny web (na środku) | Desktopowy web (po prawej) -- pełna synchronizacja stanu 1:1.

Włączanie

Frontend webowy jest domyślnie wyłączony. Aby go włączyć, ustaw hasło w ~/.repartee/.env i włącz go w config.toml:

1. Ustaw hasło logowania:

echo 'WEB_PASSWORD=your-secret-password' >> ~/.repartee/.env

2. Włącz w konfiguracji:

[web]
enabled = true
port = 8443

repartee automatycznie generuje samopodpisany certyfikat TLS przy pierwszym uruchomieniu. Otwórz https://localhost:8443 w przeglądarce i zaakceptuj ostrzeżenie o certyfikacie.

Konfiguracja

Wszystkie ustawienia webowe znajdują się w sekcji [web] w config.toml i mogą być zmieniane w czasie działania za pomocą /set:

UstawienieDomyślnaOpis
web.enabledfalseWłącz serwer webowy
web.bind_address127.0.0.1Adres nasłuchiwania (użyj 0.0.0.0 dla dostępu z sieci lokalnej)
web.port8443Port HTTPS
web.tls_cert(auto)Ścieżka do certyfikatu TLS (PEM). Pusty = samopodpisany
web.tls_key(auto)Ścieżka do klucza prywatnego TLS (PEM). Pusty = samopodpisany
web.password(z .env)Hasło logowania (ustawiane przez WEB_PASSWORD w .env)
web.session_days90Czas życia ciasteczka w dniach; sesje są zapisywane na dysku i przeżywają restart przeglądarki
web.usernamereparteeNazwa użytkownika podpowiadana w formularzu logowania (dekoracyjna — weryfikowane jest tylko hasło)
web.image_previewsfalseOpcjonalne podglądy obrazków pod wiadomościami czatu
web.image_previews_max_per_msg4Maksymalna liczba miniatur podglądu renderowanych na wiadomość
web.thumbnail_cache_mb200Maksymalny rozmiar ~/.repartee/web_thumbnails/ (LRU po mtime)
web.themenightfallDomyślny motyw (nightfall, catppuccin-mocha, tokyo-storm, gruvbox-light, catppuccin-latte)
web.timestamp_format%H:%MFormat znacznika czasu (składnia chrono strftime)
web.line_height1.35Wysokość linii CSS dla wiadomości czatu
web.nick_column_width12Szerokość kolumny nicka w znakach
web.nick_max_length9Maksymalna wyświetlana długość nicka przed obcięciem

Ustawienia kolorowania nicków znajdują się w sekcji [display], ale są również synchronizowane z klientami webowymi:

UstawienieDomyślnaOpis
display.nick_colorstrueWłącz deterministyczne kolorowanie per nick
display.nick_colors_in_nicklisttrueKoloruj również nicki na liście nicków na pasku bocznym
display.nick_color_saturation0.65Nasycenie HSL (0.0-1.0)
display.nick_color_lightness0.65Jasność HSL (0.0-1.0), niższa dla jasnych motywów

Ustawienia zmienione za pomocą /set web.* lub /set display.nick_color* są natychmiast stosowane do wszystkich podłączonych klientów webowych.

Funkcje

Frontend webowy zapewnia pełną zgodność 1:1 z interfejsem terminalowym:

  • Wszystkie typy buforów -- serwer, kanał, query, czat DCC
  • Synchronizacja w czasie rzeczywistym -- wiadomości, zmiany nicków, joiny, party, quity, zmiany topiku, zmiany trybów
  • Dwukierunkowe przełączanie buforów -- przełącz bufor w przeglądarce, a TUI podąża za nim i odwrotnie
  • Wykonywanie poleceń -- uruchamiaj dowolne /polecenie z pola wprowadzania w przeglądarce (wynik widoczny w przeglądarce)
  • Uzupełnianie Tab -- nicki, /polecenia i ścieżki ustawień /set
  • Popup uzupełniania -- wpisanie @nick, /cmd lub :emote otwiera przyjazną dotykowi listę podpowiedzi (emote pokazują animowane miniatury). Strzałki przesuwają zaznaczenie, Tab akceptuje, Escape zamyka; Enter akceptuje tylko wtedy, gdy wiersz jest zaznaczony, więc w przeciwnym razie nadal wysyła wiadomość. Dotknięcie wiersza zatwierdza wybór już na mousedown, więc przyjęcie podpowiedzi nie chowa klawiatury na urządzeniu mobilnym
  • Dotknij nicka, aby o nim wspomnieć -- dotknięcie nicka w logu czatu wstawia go do composera (nick: na początku linii, nick w środku linii); podwójne kliknięcie nadal zaznacza tekst do skopiowania. Dotknięcie nicka na liście nicków otwiera query, tak jak dotychczas
  • @nick przepisywany przy wysyłce -- linia zaczynająca się od @nick jest wysyłana w tradycyjnej ircowej formie adresowania nick: , z kanoniczną wielkością liter z listy nicków. Jeśli nazwa nie pasuje do nikogo w buforze, linia jest wysyłana bez zmian, a @nick w środku linii nigdy nie jest przepisywany
  • Historia wpisywania -- naciśnij ↑/↓, aby przywołać to, co wysłałeś, tak jak w TUI (ostatnie 100 wpisów; niewysłany szkic jest odkładany i przywracany). ↑ cofa się w historii tylko wtedy, gdy karetka jest w pierwszej linii szkicu, więc w szkicu wieloliniowym nadal porusza się normalnie
  • Wskaźniki pisania -- zobacz, kto komponuje wiadomość w bieżącym buforze, zgodnie z paskiem statusu TUI (alice and bob are typing…). Twoje własne pisanie jest zgłaszane w trakcie komponowania, zgodnie z przełącznikami wysyłania [typing]
  • Kolorowanie nicków -- deterministyczne kolory per nick synchronizowane z ustawień serwera, ten sam algorytm HSL co TUI
  • Lista nicków -- grupowana według trybu (opy, voice, zwykli), status away, kolorowanie per nick (osobno przełączalne)
  • Wskaźniki aktywności -- kolorowe poziomy aktywności oraz plakietka nieprzeczytanych na każdym buforze (ograniczona do 99+, ukryta dla aktywnego bufora). Bufory z rozłączonej sieci są przygaszone
  • Regulowany rozmiar tekstu i odstęp między liniami -- menu Aa (dolny pasek na komputerze, wysuwany panel na urządzeniach mobilnych) ustawia rozmiar czcionki (10–24 px) i odstęp między liniami (1.00–2.20), wraz z przywróceniem wartości domyślnych. Oba ustawienia są zapisywane w localStorage, więc przeżywają przeładowanie strony
  • Wzmianki -- śledzenie podświetleń z plakietką licznika wzmianek
  • Wybór motywu -- zmiana motywu na żywo (5 wbudowanych motywów)
  • Wieloliniowe wprowadzanie -- komponuj z podziałami wierszy lub wklej blok tekstu; wysyłane jako jedna wiadomość draft/multiline, gdy serwer ją obsługuje, w przeciwnym razie linia po linii
  • Trwałe sesje -- domyślnie 90-dniowe ciasteczko, magazyn tokenów na dysku; restart przeglądarki, instalacja PWA i przejście z WiFi na sieć komórkową nie wylogowują użytkownika
  • Formularz logowania z polem użytkownika -- prawidłowy <form> z oddzielnymi polami username + password, dzięki czemu 1Password, iCloud Keychain i Bitwarden rozpoznają formularz
  • Klikalne linki -- URL-e w wiadomościach renderowane są jako odnośniki otwierające się w nowej karcie, z natywnym menu kontekstowym przeglądarki ("Otwórz w nowym oknie", "Kopiuj adres linku")
  • Podglądy obrazków -- opcjonalne (web.image_previews = true); pobieranie po stronie serwera z zabezpieczeniem SSRF i generowaniem miniatury (400x300 JPEG, cache lokalny), zarezerwowana ramka układu chroni przed przeładowywaniem czatu, dyskretne odrzucanie podglądu per wiadomość zapisywane w localStorage
  • Automatyczny reconnect -- WebSocket automatycznie łączy ponownie z wykładniczym opóźnieniem (2s-30s) po utracie połączenia
  • Odporne pierwsze połączenie -- chwilowy problem sieciowy przy ładowaniu strony powtarza próby z wykładniczym opóźnieniem do pięciu razy, zanim spadnie do formularza logowania
  • Separatory dat -- wizualne separatory między wiadomościami z różnych dni dla łatwiejszego czytania historii
  • Wbudowana powłoka -- pełny terminal w przeglądarce przez renderer beamterm 1.0 WebGL2. Otwiera się w karcie przeglądarki inicjującej polecenie, z oddzielnym PTY na sesję webową (bez konfliktu zmiany rozmiaru z TUI). Czcionka FiraCode Nerd Font, poprawne renderowanie szerokich glifów 2-kolumnowych, zaznaczanie myszą ze schowkiem, zmiana rozmiaru czcionki Ctrl+/-, Ctrl+C (SIGINT), wklejanie ze schowka w trybie bracketed paste
  • Inline emotes -- 183 wbudowane emote GG7 :name: renderowane jako inline animowane GIF-y serwowane prosto z binarki; wpisz :smile: (lub polskie :usmiech:) i pojawia się w miejscu
  • Emote & emoji pickers -- GG emote picker (animowane miniatury + filtr na żywo) oraz Unicode emoji picker (zakładki kategorii + wyszukiwanie) otwierane z przycisków paska narzędzi, polecenia /emoji lub skrótem Ctrl+G; :name: uzupełnia się również przez tab-complete w composerze. Oba wstawiają w miejscu caret, na komputerze i urządzeniach mobilnych
  • Formatowane topiki -- pasek topiku renderuje kolory mIRC/irssi, pogrubienie, kursywę, podkreślenie i klikalne URL-e (pełna 99-kolorowa paleta), zgodnie z parserem czatu
  • Backlog scroll-back i synchronizacja read-marker -- przewiń kanał/query w stronę góry, aby stronicować starsze wiadomości prosto z logu SQLite (oraz z historii serwera, gdy bouncer obsługuje draft/chathistory); pozycja przewijania jest zakotwiczona, a znaczniki przeczytania (MARKREAD) są synchronizowane między TUI, webem i innymi klientami bouncera
  • Wspólny panel ustawień (/wizard) -- kliknij ikonę koła zębatego w lewym górnym rogu (lub wpisz /wizard), aby otworzyć pełny 10-sekcyjny panel ustawień współdzielony z TUI (Networks & Connections, Appearance, Messages, Notifications, History & Privacy, Keyboard, Extensions, E2E Encryption, Translation oraz Advanced), z wyszukiwarką Ctrl+F, edytorami list oraz kreatorami kanałów per sieć dla szyfrowania E2E i tłumaczeń; hasła i klucze trafiają do .env, nigdy do config.toml
  • Własne skróty klawiszowe (/bind) -- skróty klawiszowe i sekwencje wieloklawiszowe skonfigurowane przez /bind lub sekcję [keyboard] (w tym skok do aktywności Alt+A, Alt+1–9/0, Alt+Lewo/Prawo oraz nawigacja po highlightach) działają w interfejsie webowym i aktualizują się na żywo
  • Powiadomienia WebPush -- powiadomienia push w przeglądarce dzięki obsłudze draft/webpush / soju.im/webpush na bouncerze
  • Ikony sieci -- wyświetla ikony sieci rozgłaszane przez IRCv3 ISUPPORT draft/ICON na pasku bocznym i liście serwerów
  • Auto-revealing scrollbars -- cienkie, dopasowane do motywu scrollbary, które pojawiają się przy najechaniu/przewijaniu i poza tym pozostają niewidoczne, w Chrome, Firefox i Safari

Układ desktopowy

Układ desktopowy odzwierciedla interfejs terminalowy:

┌─────────────────────────────────────────────────────┐
│ Topic bar                                           │
├──────────┬─────────────────────────────┬────────────┤
│ Buffers  │ Chat area                   │ Nick list  │
│          │ 14:23 @ferris❯ Hello!       │ @ferris    │
│ (status) │ 14:24  alice❯ Hey there     │  alice     │
│ 1.#rust  │                             │  bob       │
│ 2.#help  │                             │            │
├──────────┴─────────────────────────────┴────────────┤
│ [kofany(+i)] [#rust(+nt)] [Lag: 42ms] [Act: 3,4]   │
│ ❯ [Message input...                           ] [➤] │
│ [● ● ● ● ●] theme picker                           │
└─────────────────────────────────────────────────────┘

Układ mobilny

Na ekranach węższych niż 768px układ przełącza się na widok zoptymalizowany dla urządzeń mobilnych:

┌──────────────────────────┐
│ ☰  #rust (+nt) — Welc… 👥│  top bar
├──────────────────────────┤
│ 14:23 @ferris❯ Has any…  │  inline nicks
│ 14:24 alice❯ Yeah, it's… │
├──────────────────────────┤
│ [kofany|Act: 3,4,7]      │  compact status
│ [Message...          ] ➤  │  input
└──────────────────────────┘

Funkcje mobilne:

  • Czat inline -- nicki pojawiają się w linii z wiadomością (bez wyrównanej w prawo kolumny), aby zmaksymalizować przestrzeń poziomą
  • Wysuwana lista buforów -- dotknij menu hamburger lub przesuń palcem w prawo z dowolnego miejsca, aby otworzyć listę kanałów/buforów
  • Wysuwana lista nicków -- dotknij przycisku listy nicków lub przesuń palcem w lewo z dowolnego miejsca, aby otworzyć listę nicków
  • Automatyczne zamykanie paneli -- dotknięcie bufora na wysuwanej liście przełącza na niego i automatycznie zamyka panel
  • Przyjazny dotykowi -- duże cele dotykowe, gesty przesuwania, brak przypadkowego przewijania poziomego
  • Dopasowanie do widoku -- używa 100dvh do prawidłowego wypełnienia ekranu w iOS Safari i Android Chrome (uwzględnia chrome przeglądarki)
  • Bez automatycznego powiększania -- skupienie na polu wprowadzania nie wywołuje automatycznego powiększenia w iOS Safari
  • Bezpieczny obszar wycięcia -- respektuje safe-area-inset-bottom na iPhone'ach ze wskaźnikami home
  • Stabilne pinowanie do dołu -- wzorzec "pin to bottom" znany z The Lounge: dopóki najnowsza wiadomość jest widoczna, nowe wiadomości pozostają przyklejone do dołu; przewiń w górę o dowolny kawałek i widok zostaje zaparkowany, a historia spokojnie spływa pod spodem. Wcześniej widoczny "skok w górę i z powrotem" został wyeliminowany.
  • Bez skoków, gdy treść się doładuje -- czat jest przypinany ponownie za każdym razem, gdy zmienia się jego ramka, a nie tylko przy zmianie rozmiaru okna, więc rosnący composer nie zasłania już ostatniej linii podczas pisania. Podglądy obrazków rezerwują swoją ramkę od pierwszego renderu i jedynie płynnie się pojawiają, więc obrazek kończący ładowanie w trakcie przewijania nie przebuduje widoku.
  • Gęstsze panele -- ciaśniejsze odstępy i większe cele dotykowe na mobilnym pasku górnym oraz w wysuwanych panelach, a Escape zamyka otwarte okno modalne lub panel.

Własny TLS

Do użytku produkcyjnego (lub aby uniknąć ostrzeżeń o certyfikacie w przeglądarce) podaj własny certyfikat TLS:

[web]
tls_cert = "/path/to/fullchain.pem"
tls_key  = "/path/to/privkey.pem"

Certyfikaty Let's Encrypt działają od razu.

Zdalny dostęp

Aby uzyskać dostęp do frontendu webowego z innych urządzeń w sieci:

[web]
bind_address = "0.0.0.0"   # listen on all interfaces
port = 8443

Następnie otwórz https://adres-ip-twojego-komputera:8443 ze swojego telefonu lub innego komputera.

Bezpieczeństwo

  • Tylko HTTPS -- cały ruch jest szyfrowany przez TLS
  • Uwierzytelnianie hasłem -- logowanie weryfikowane HMAC-SHA256
  • Ograniczanie prób -- ochrona przed atakami brute-force z progresywną blokadą
  • Tokeny sesji -- 32-bajtowe losowe tokeny przekazywane jako ciasteczko HttpOnly; Secure; SameSite=Strict; zapisywane w ~/.repartee/web_sessions.bin (uprawnienia 0600) wyłącznie jako HMAC-SHA256(token, WEB_SESSION_SECRET), więc surowy token nigdy nie trafia na dysk. WEB_SESSION_SECRET jest generowany do .env przy pierwszym uruchomieniu; jego rotacja unieważnia wszystkie istniejące sesje.
  • Brak powiązania z IP/User-Agent -- ciasteczko jest granicą bezpieczeństwa, więc telefon przechodzący między WiFi a siecią komórkową nie wylogowuje się
  • Ochrona SSRF dla podglądów -- /api/preview serwuje wyłącznie URL-e wcześniej wyłuskane przez sam serwer, wyszukiwane po hashu HMAC; cele prywatne, loopback, link-local i metadane chmury są blokowane na poziomie bezpośrednich URL-i, przekierowań i podążania za og:image
  • Brak zewnętrznych zależności -- interfejs webowy jest skompilowany do WASM i osadzony w binarce; brak żądań do CDN, brak zewnętrznych skryptów