diff --git a/app/routes/api.py b/app/routes/api.py index 04253a3..952b0e2 100644 --- a/app/routes/api.py +++ b/app/routes/api.py @@ -72,6 +72,13 @@ def send_message(): 'error': 'Message text cannot be empty' }), 400 + # MeshCore message length limit (~180-200 bytes for LoRa) + if len(text) > 200: + return jsonify({ + 'success': False, + 'error': f'Message too long ({len(text)} chars). Maximum 200 characters allowed due to LoRa constraints.' + }), 400 + reply_to = data.get('reply_to') # Send message via meshcli diff --git a/app/static/js/app.js b/app/static/js/app.js index 3d4cc37..5fee59b 100644 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -45,6 +45,11 @@ function setupEventListeners() { } }); + // Character counter + input.addEventListener('input', function() { + updateCharCounter(); + }); + // Manual refresh button document.getElementById('refreshBtn').addEventListener('click', function() { loadMessages(); @@ -183,6 +188,7 @@ async function sendMessage() { if (data.success) { input.value = ''; + updateCharCounter(); showNotification('Message sent', 'success'); // Reload messages after short delay @@ -205,6 +211,7 @@ async function sendMessage() { function replyTo(username) { const input = document.getElementById('messageInput'); input.value = `@[${username}] `; + updateCharCounter(); input.focus(); } @@ -362,6 +369,30 @@ function formatTime(timestamp) { } } +/** + * Update character counter + */ +function updateCharCounter() { + const input = document.getElementById('messageInput'); + const counter = document.getElementById('charCounter'); + const length = input.value.length; + const maxLength = 200; + + counter.textContent = `${length} / ${maxLength}`; + + // Visual warning when approaching limit + if (length >= maxLength * 0.9) { + counter.classList.remove('text-muted', 'text-warning'); + counter.classList.add('text-danger', 'fw-bold'); + } else if (length >= maxLength * 0.75) { + counter.classList.remove('text-muted', 'text-danger'); + counter.classList.add('text-warning', 'fw-bold'); + } else { + counter.classList.remove('text-warning', 'text-danger', 'fw-bold'); + counter.classList.add('text-muted'); + } +} + /** * Escape HTML to prevent XSS */ diff --git a/app/templates/index.html b/app/templates/index.html index 9e83c67..c8a53bf 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -31,13 +31,17 @@ class="form-control" placeholder="Type a message..." rows="2" + maxlength="200" required > - Press Shift+Enter for new line, Enter to send +
+ Press Shift+Enter for new line, Enter to send + 0 / 200 +
diff --git a/technotes/limity.md b/technotes/limity.md new file mode 100644 index 0000000..2fe0a15 --- /dev/null +++ b/technotes/limity.md @@ -0,0 +1,53 @@ +W **MeshCore** obowiązuje **dość rygorystyczny limit długości pojedynczej wiadomości**, wynikający bezpośrednio z ograniczeń LoRa. + +### 🔹 Limit długości wiadomości + +* **Maksymalnie ~200–240 bajtów payloadu** +* W praktyce **bezpiecznie przyjmuj ~180–200 bajtów**, bo część danych zajmują: + + * nagłówki protokołu MeshCore, + * adresowanie, + * metadane routingu, + * CRC / kontrola integralności. + +To oznacza: + +* **kilkadziesiąt znaków tekstu** (zależnie od kodowania), +* raczej **krótkie komunikaty**, nie „SMS-y” ani tym bardziej bloki tekstu. + +### 🔹 Co się dzieje przy dłuższej wiadomości? + +* MeshCore **tnie ją na fragmenty (fragmentation)**, +* fragmenty są wysyłane osobno i składane u odbiorcy, +* **każdy fragment zwiększa czas transmisji i ryzyko utraty**, szczególnie: + + * przy niskim SF, + * w zatłoczonej sieci, + * na dalekich hopach. + +Dlatego: + +> **zalecenie praktyczne:** traktuj MeshCore jak **pager / radio tekstowe**, a nie komunikator. + +### 🔹 Porównanie (dla intuicji) + +* 1 znak ASCII ≈ 1 bajt +* 180 bajtów ≈ + + * ~180 znaków ASCII + * ~90–120 znaków UTF-8 (polskie znaki, emoji → szybciej zjadają limit) + +### 🔹 Dobre praktyki + +* używaj **krótkich, rzeczowych komunikatów**, +* unikaj emoji i znaków narodowych, jeśli zasięg/stabilność jest krytyczna, +* jeśli musisz przesłać więcej danych: + + * podziel treść logicznie (kilka wiadomości), + * rozważ **out-of-band** (np. MeshCore tylko do „powiadomień”). + +Jeśli chcesz, mogę: + +* rozpisać **dokładną strukturę ramki MeshCore (ile bajtów na co)**, +* porównać to z **Meshtastic** (tam limity są trochę inne), +* albo pomóc dobrać **SF/BW/CR**, żeby fragmentacja była możliwie bezpieczna.