Devlog #38 – Nieprecyzyjne wyszukiwanie, mobilny UX i niezawodność PWA

Fala 18 przynosi trzy pogłębione wpisy o fizyce plazmy, topologii i nauce o materiałach, a pod maską wdrażamy usprawnienia wyszukiwania zapowiedziane w Devlogu #37: dopasowanie nieprecyzyjne Levenshteina, korektę fonetyczną soundex oraz pełną rundę poprawek mobilnego UX.

Platforma w skrócie

345
Symulacje
75
Kategorie
110
Wpisy na blogu
18
Fale treści
36
Spotlighty
28
Wpisy edukacyjne

Retrospektywa Fali 18

Nieprecyzyjne wyszukiwanie (fuzzy search)

Devlog #37 odłożył dopasowanie nieprecyzyjne jako pracę do wykonania później. Po ukończeniu treści Fali 18 wdrożyliśmy pełną warstwę nieprecyzyjnego wyszukiwania na bazie istniejącej infrastruktury indeksu odwróconego. Kluczowym ograniczeniem projektowym było utrzymanie się poniżej 15 ms opóźnienia p95 na telefonie ze średniej półki, bez żadnych zapytań sieciowych.

Wyszukiwanie odległością edycyjną Levenshteina

Algorytm: programowanie dynamiczne Wagnera-Fischera (czas O(m·n), pamięć O(m))
  koszty: wstawienie=1, usunięcie=1, podstawienie=1
  Transpozycje (rozszerzenie Damerau): +1 za zamianę sąsiednich znaków

Polityka dopasowania:
  Długość zapytania 1–3: tylko dopasowanie dokładne (bardzo krótkie zapytania, bez fuzzy)
  Długość zapytania 4–6: akceptowana odległość edycyjna ≤ 1
  Długość zapytania ≥ 7: akceptowana odległość edycyjna ≤ 2

Filtrowanie kandydatów (wstępne przycinanie):
  Wspólne trigramy: kandydat musi dzielić ≥ 1 trigram z zapytaniem
  Redukuje liczbę wywołań DP o ~95% względem naiwnego podejścia wszystkie-do-wszystkich

Ranking "czy chodziło Ci o":
  Kandydaci sortowani wg: (odległość edycyjna, wynik TF-IDF)
  Wyświetlane jako sugestia inline: "Wyniki dla bernoulli — czy chodziło Ci o bernoulli?"

Wpływ na rozmiar indeksu:
  Oryginalny indeks odwrócony: 28 KB gzip
  + Indeks trigramów do generowania kandydatów: +22 KB gzip
  Razem: 50 KB gzip  (cel to było 54 KB; 7% poniżej budżetu)

Opóźnienie (odpowiednik Moto G Power, 2× throttling CPU):
  Zapytanie dokładne:  6 ms p95
  Zapytanie z 1 edycją: 11 ms p95
  Zapytanie z 2 edycjami: 14 ms p95  (w granicach celu 15 ms)
          

Dopasowanie fonetyczne soundex

Algorytm Soundex (wariant US NIST):
  1. Zachowaj pierwszą literę
  2. Zmapuj pozostałe spółgłoski: B,F,P,V → 1;  C,G,J,K,Q,S,X,Z → 2;
     D,T → 3;  L → 4;  M,N → 5;  R → 6
  3. Usuń kolejne duplikaty; usuń samogłoski
  4. Przytnij lub uzupełnij do 4 znaków

Przykład:
  "bernoulli" → B654
  "bernuly"   → B654   (ten sam kod → dopasowanie fonetyczne)

Pokrycie:
  Dopasowania fonetyczne uruchamiają się tylko, gdy odległość Levenshteina > próg
  Dodaje ~8% recall przy błędnie wpisanych zapytaniach względem samego Levenshteina
  Odsetek fałszywych trafień: 0,3% zapytań pokazuje jako pierwszy niezwiązany wynik

Połączony potok na każde naciśnięcie klawisza:
  1. Tokenizacja zapytania
  2. Dokładne wyszukanie w indeksie odwróconym (< 1ms)
  3. Kandydaci Levenshteina filtrowani trigramami (< 8ms)
  4. Fallback soundex, jeśli < 3 wyniki (< 3ms)
  5. Scalenie, punktacja, renderowanie sugestii
          

Usprawnienia mobilnego UX

Ruch mobilny odpowiada za 48% sesji, ale historycznie miał gorszy czas zaangażowania z powodu sterowania zaprojektowanego z myślą o desktopie. Ta fala wzmocniła interakcje dotykowe we wszystkich 345 symulacjach bez zmiany logiki którejkolwiek z nich.

Suwaki przeciągane palcem

Problem: <input type="range"> na urządzeniach mobilnych jest niewygodny dla kciuka i ignoruje pointer-lock
Nowa implementacja:
  - Niestandardowy <div role="slider"> z minimalnym obszarem dotyku 44px (WCAG 2.5.5)
  - Kontrakt zdarzeń pointerdown → pointermove → pointerup (obsługuje mysz + dotyk + rysik)
  - setPointerCapture() zapobiega utracie śledzenia podczas szybkich przesunięć
  - Przyspieszenie zależne od prędkości: |dx| > 8px/klatkę → 2× wielkość kroku
Migracja: 1 współdzielony moduł slider.js, zero zmian specyficznych dla symulacji
          

Pinch-zoom w symulacjach na canvasie

Dotyczy: symulacji 3D Three.js (OrbitControls) oraz symulacji 2D na canvasie

Symulacje Three.js (100+ symulacji):
  OrbitControls już obsługiwał pinch; problemem było domyślne touch-action ustawione na pan
  Poprawka: element canvas otrzymuje touch-action: none; zapobiega przewijaniu strony podczas orbitowania kamerą 3D
  Efekt: zero przypadkowych przewinięć strony podczas orbitowania kamerą 3D na urządzeniach mobilnych

Symulacje 2D na canvasie (~80 symulacji):
  Nowe współdzielone narzędzie pinch.js:
    Śledzenie euklidesowej odległości dwóch dotknięć → emisja syntetycznego zdarzenia "wheel" (deltaY = −Δdist)
    Symulacje nasłuchujące zoomu przez wheel działają automatycznie
    Współczynnik skali = new_dist / old_dist, ograniczony do [0.2, 10×]

Reset przez podwójne stuknięcie:
  Sekwencja TouchEnd < 300ms i < 5px ruchu → wywołuje niestandardowe zdarzenie "resetView"
  Wszystkie symulacje z przyciskiem resetu reagują teraz też na podwójne stuknięcie
          

Panele parametrów w stylu bottom-sheet

Wcześniej: panel boczny unosił się nad canvasem → zasłaniał 40% małego ekranu
Teraz: bottom-sheet (wzorzec Material Design) przy viewport < 640px

Implementacja:
  - position: fixed; bottom: 0;  transition: transform 300ms ease
  - przeciąganie za "uchwyt": próg > 60px przesunięcie w górę → otwiera; w dół → zwija
  - Stan zwinięty: 48px podglądu (pokazuje pierwsze 2 kontrolki wg liczby lub ważności)
  - Pełne otwarcie: maks. wysokość 55vh, overflow-y: auto
  - Tło: przyciemnienie głównej treści po otwarciu, dotknięcie zamyka

Wdrożenie: 22 symulacje z największą liczbą parametrów zmigrowano jako pierwsze
Symulacje typu ławka laboratoryjna (beam-deflection, bridge-truss itd.): specjalny układ siatki 2-kolumnowej przy <480px
          

Niezawodność PWA

Service Worker v18 – Stale-While-Revalidate

Zmiany strategii cache:
  Żądania nawigacyjne: NetworkFirst (wcześniej CacheFirst)
    → Zawsze najpierw próbuj sieci; w razie braku połączenia użyj cache
  Zasoby statyczne (JS/CSS/WASM): StaleWhileRevalidate
    → Serwuj natychmiast z cache; odświeżaj w tle
  API / CDN Three.js: CacheFirst z TTL 7 dni
    → Three.js r160 (1,2 MB) nigdy nie jest pobierane ponownie bez potrzeby

Strony fallback offline:
  75 stron kategorii wstępnie zbuforowanych (wcześniej 0)
  Niestandardowy offline.html z listą Ostatnio oglądane z IndexedDB
  Wskaźnik offline w pasku nawigacji: "Jesteś offline — wyświetlane treści z cache"

Synchronizacja w tle:
  Odroczone zapisy "ostatnio oglądane" w trybie offline
  Wykorzystuje BackgroundSync API (tag sync "rv-write")
  Fallback: beacon przy visibilitychange dla przeglądarek bez BackgroundSync
  Przechowywanie: IndexedDB, kluczowane po slugu symulacji, maks. 50 wpisów, eviction FIFO

Wyniki audytu Lighthouse (mobile, throttled):
  Wydajność:        94  (+6 względem fali 17)
  Dostępność:       99  (bez zmian)
  Najlepsze praktyki: 100
  SEO:              100
  PWA:              100  (nowość — InstallPrompt + fallback offline)
          

Lista kontrolna audytu platformy — Fala 18

Zapowiedź Fali 19

⭐ Spotlight #38

Kosmologia i ciemna materia

Piki akustyczne CMB, inflacja, symulacje N-ciałowe ciemnej materii, filamenty wielkoskalowej struktury i barionowe oscylacje akustyczne.

📖 Learning #29

Ogólna teoria względności i zakrzywiona czasoprzestrzeń

Tensor Riemanna, równania pola Einsteina, metryka Schwarzschilda, precesja geodezyjna, fale grawitacyjne i czułość LIGO.

⭐ Spotlight #39

Chemia fizyczna i dynamika reakcji

Teoria stanu przejściowego, równanie Eyringa, transfer elektronu Marcusa, spektroskopia femtosekundowa i kwantowe wydajności fotochemiczne.

🛠️ Devlog #39

Tryb offline PWA i podświetlanie wyszukiwania

Pełna architektura offline-first, podświetlanie fraz wyszukiwania na stronie oraz audyt danych strukturalnych (JSON-LD) dla wszystkich 345 symulacji.

Zapraszamy do współtworzenia: system mobilnych paneli bottom-sheet wymaga testów na prawdziwych urządzeniach w szerszym zakresie telefonów — zwłaszcza Samsung Galaxy Fold (zagnieżdżone przewijanie) i iPad mini (breakpoint średniej wielkości). Jeśli testujesz na fizycznym sprzęcie i znajdziesz błąd w układzie, daj nam znać przez formularz kontaktowy. Minimalne przypadki reprodukcji (urządzenie + przeglądarka + zrzut ekranu) są szczególnie pomocne.