tetes
Możliwości
Historia
Home App
Warstwy
Mapa homelaba
Blog
Studia przypadków
Współpraca
Kontakt
tetes

Docker homelab. 40+ serwisów. 0 subskrypcji chmurowych.

mariuszbinczyk.com

Nawigacja

  • Możliwości
  • Historia
  • Home App
  • Warstwy
  • Współpraca
  • Kontakt

Kontakt

  • GitHub
  • Email
  • PL / EN / DE

© 2026 tetes.pl — Wszelkie prawa zastrzeżone

0d98bb6
O projekciePrywatność
>|
Język:ENPLDE
Home App·29 maja 2026·6 min czytania·Aktualizacja: 13 czerwca 2026

Notatnik z aparatem, głosem i OCR — jak zbudowałem to lokalnie

Dodałem do domowej aplikacji wgrywanie zdjęć, dyktowanie głosowe po polsku i rozpoznawanie tekstu ze zdjęć (OCR). Wszystko działa lokalnie, bez zewnętrznych API.

#homelab#homeapp#notatki#ocr#self-hosted
Notatnik z aparatem, głosem i OCR — jak zbudowałem to lokalnie
ℹ
Note

Artykuł opisuje funkcje wdrożone w Home App v2.12 — domowej aplikacji na prywatnym serwerze. Kod nie jest publicznie dostępny, ale opisane wzorce (TipTap Image, Web Speech API, Tesseract.js) są ogólnie stosowalne.

Tło

Przepis na kartce, paragon do rozliczenia, pomysł podczas gotowania — wcześniej lądowały w aparacie telefonu albo po prostu ginęły.

Od kilku miesięcy intensywnie używam Notatnika w Home App — własnej aplikacji webowej działającej na domowym serwerze. Brakowało mi jednej rzeczy: możliwości wklejenia zdjęcia, zrobienia notatki aparatem telefonu, szybkiego podyktowania pomysłu głosem albo dołączenia pliku (np. PDF) do notatki.

Korzystam z komercyjnych aplikacji (Notion, Obsidian, Evernote) i każda ma coś z tych rzeczy — ale moje dane zostają u dostawcy. Chciałem to samo, lokalnie.

Cztery sposoby na wstawienie zdjęcia

Przed wdrożeniem notatnik obsługiwał wyłącznie tekst ( z kilkoma rozszerzeniami: listy zadań, nagłówki, linki). Dodałem cztery sposoby dodawania obrazów:

1. Drag & drop — przeciągnij plik obrazka na edytor. Pojawia się fioletowa przerywana ramka jako drop zone. Upuść → obraz trafia na serwer i wstawia się w edytorze.

2. Wybór pliku — przycisk w pasku narzędzi otwiera systemowy wybierak plików. Na Androidzie pokazuje galerię. Na iPhone obsługuje też format HEIC (native format aparatu Apple).

3. Aparat — drugi przycisk w pasku narzędzi (capture=environment) otwiera bezpośrednio tylny aparat telefonu. Zero kroków pośrednich.

4. Wklejanie ze schowka — Ctrl+V (lub przytrzymanie → Wklej na telefonie) po screenshocie lub skopiowaniu obrazka wkleja go bezpośrednio. W przeglądarce obsługuje to zdarzenie wklejania w TipTap.

Jak to działa po stronie serwera

Obrazy trafiają przez multer (Express, in-memory, limit 25 MB) do serwisu, który używa sharp:

  • Oryginał zapisywany z pełnymi danymi EXIF
  • Wariant display: max 1920px, JPEG q85, rotacja EXIF zastosowana
  • Thumbnail: max 400px, JPEG q80

Deduplikacja po SHA-256 — ten sam plik wgrany dwa razy = jeden plik na dysku, dwie referencje. Storage na bind-mountowanym katalogu hostowym, objęty standardowym backupem homelaba.

Poza obrazami ten sam endpoint przyjmuje też zwykłe pliki (np. PDF) — te pomijają obróbkę sharp i pojawiają się w notatce jako pobieralny załącznik (przycisk spinacza w toolbarze). Lista dozwolonych typów MIME jest pilnowana po stronie serwera.

Pipeline uploadu: przeglądarka → upload → obrazy przez sharp / pliki jako załącznik → SHA-256 → PostgreSQL i lokalny storage → backup

Dyktowanie głosowe po polsku

Web Speech API (SpeechRecognition) jest wbudowane w Chrome i Edge — zero zależności, zero kosztu serwera. Dodałem przycisk mikrofonu w toolbarze notatnika:

  • Kliknięcie start → pulsujący czerwony dot + "Słucham…"
  • Każde rozpoznane zdanie jest wstawiane bezpośrednio do edytora
  • continuous: true + interimResults: true → streaming tekstu w czasie rzeczywistym
  • Język: pl-PL hardcoded

Działa zaskakująco dobrze dla codziennych notatek. Dla specjalistycznego słownictwa (np. terminy techniczne po angielsku pośrodku polskiego zdania) jest gorzej — ale do notowania pomysłów, zadań i szybkich uwag — wystarczy w zupełności.

Safari iOS nie obsługuje Web Speech API — przycisk mikrofonu jest wtedy automatycznie ukrywany.

OCR ze zdjęcia (Tesseract.js)

Trzeci nowy przycisk uruchamia rozpoznawanie tekstu:

  1. Wybierasz zdjęcie (aparat lub plik)
  2. Zdjęcie trafia na serwer (standardowa ścieżka z sharp)
  3. Tesseract.js uruchamia się jako Web Worker w przeglądarce
  4. Model pol+eng (~15 MB, zapisany w IndexedDB po pierwszym pobraniu)
  5. OCR zwraca tekst → pojawia się modal z:
    • podglądem zdjęcia (zwijany)
    • edytowalnym textarea z rozpoznanym tekstem
    • trzema przyciskami: tylko zdjęcie / tylko tekst / oba (domyślnie)

Kluczowy szczegół: dostaje wariant display z serwera (JPEG po EXIF-rotacji), nie oryginalny HEIC z iPhone — eliminuje potrzebę heic2any w przeglądarce.

Dokładność: świetna dla wydrukowanego tekstu, akceptowalna dla czytelnego odręcznego pisma, słaba dla nieczytelnych bazgrołów. Na przyszłość zaplanowałem podpięcie vision jako opcji "Lepsze OCR (AI)" — endpoint jest już przygotowany, czeka na model.

FAB Quick-Capture

Pływający przycisk + na stronie głównej (dolny prawy róg) + skrót n — tworzysz notatkę bez otwierania sekcji Notatnik. Cztery akcje w panelu wysuwanym od dołu:

  • Notatka → pusty edytor
  • Foto → aparat → upload → notatka z obrazkiem
  • Głos → pusta notatka + mikrofon startuje automatycznie
  • OCR → aparat → upload → OCR modal

Quick-Capture: cztery szybkie akcje wokół przycisku „+" — notatka, zdjęcie, głos, OCR

Notatki trafiają do domyślnego notatnika użytkownika. Jeśli użytkownik nie ma jeszcze domyślnego notatnika, backend tworzy „Szybkie notatki" i ustawia je jako domyślne.

Linki między notatkami

Po zdjęciach, OCR i szybkim przechwytywaniu brakowało mi jeszcze jednej rzeczy: możliwości połączenia notatek ze sobą. Nie przez zwykły link URL, tylko tak jak w Obsidianie: wpisuję [[, zaczynam tytuł, wybieram notatkę z podpowiedzi i w treści pojawia się mały fioletowy chip z linkiem.

Kliknięcie przenosi od razu do powiązanej notatki. A kiedy otwieram tę notatkę, widzę też panel „Linkujące" — listę miejsc, które do niej prowadzą. Dzięki temu notatnik przestaje być tylko chronologicznym zbiorem wpisów. Zaczyna działać bardziej jak mała, prywatna wiki: jeden temat może mieć własną notatkę, a inne wpisy mogą do niego wracać bez kopiowania kontekstu.

Technicznie zrobiłem to bez osobnej tabeli relacji. Link jest węzłem TipTap zapisanym w content_json, a backlinki są wyliczane przez skan JSON-a w PostgreSQL (jsonb_path_exists). Przy skali domowego notatnika to prostsze niż utrzymywanie dodatkowej tabeli krawędzi i synchronizowanie jej przy każdym autosave. Jeśli kiedyś linków będzie dużo, wtedy będzie sens przenieść to do normalnej tabeli relacji.

Przy okazji domknąłem też kilka rzeczy wokół pracy z obrazami:

  • Galeria obrazków — notatka z kilkoma zdjęciami pokazuje pasek miniatur i pełnoekranowy podgląd.
  • Zmiana rozmiaru obrazów — obraz można złapać za róg, zmienić rozmiar i zostawić w notatce tak, jak ma wyglądać.
  • Eksport do Markdown — gotową notatkę można pobrać jako .md, razem z tekstową reprezentacją linków i załączników.

Czego się nauczyłem

nginx client_max_body_size — domyślny limit to 1 MB. Zapomniałem go ustawić i upload zdjęć z telefonu (typowo 3–5 MB) kończył się ciszą. Teraz client_max_body_size 30m.

useLayoutEffect zamiast useEffect dla scroll — miałem komponent z poziomym snap-scroll (strony dashboardu). Próbowałem ustawić pozycję startową przez useEffect + requestAnimationFrame, ale clientWidth był wtedy 0. useLayoutEffect + scrollLeft = index * window.innerWidth działa synchronicznie przed pierwszym renderem.

prev_content_json — po wdrożeniu odkryłem, że autosave może nadpisać treść notatki, gdy użytkownik ma ją otwartą w czasie restartu aplikacji. Dodałem kolumnę prev_content_json — przy każdym update zapisuję poprzednią wersję treści. To miało być jedno awaryjne cofnięcie, a przy późniejszym wdrażaniu linków między notatkami faktycznie uratowało mi dane.

Co dalej

  • Ollama vision dla trudnych OCR-ów (kiedy model będzie dostępny)
  • Historia wersji (więcej niż jedno cofnięcie)
  • PWA Share Target — "Udostępnij do Notatnika" z systemu Android

Notatnik to tylko jeden moduł Home App. Jak z domowego problemu z zakupami wyrosła cała rodzinna aplikacja, opisałem w studium przypadku o rodzinnym dashboardzie.


Home App działa na prywatnym serwerze x86. Stack: React 19 + TipTap 3 + Express + PostgreSQL.

Najczęstsze pytania

Czy moje zdjęcia i notatki trafiają do chmury?

Nie. Upload zdjęć, dyktowanie i OCR działają lokalnie na domowym serwerze — dane nie opuszczają domu.

Czy potrzebuję drogiego sprzętu, żeby uruchomić OCR i dyktowanie?

Nie. OCR działa w przeglądarce (Tesseract.js), a dyktowanie korzysta z wbudowanego Web Speech API. Wystarczy zwykły serwer domowy.

Czy mogę dołączać pliki PDF i robić zdjęcia telefonem?

Tak. Notatnik obsługuje upload plików, zrzuty ekranu, zdjęcia z aparatu telefonu i przeciąganie obrazów wprost do edytora.

Tagi

#homelab#homeapp#notatki#ocr#self-hosted
← Powrót do bloga

← Poprzedni

Lokalny LLM bez GPU: czego nauczył mnie rok z Ollama na starym i5