---
title: "Markdown editor"
type: plugin
version: "2.2.2"
doc_version: "2.7.3"
status: stable
date: 2026-07-05
library: Frutjam
stack: tailwind_css
compatibility: universal
framework_agnostic: true
runtime_requirement: none
description: "JavaScript markdown editor plugin for Tailwind CSS projects. WYSIWYG and plain Markdown modes, live preview, RTL support, dark mode. Works with Django, HTMX, Laravel, React."
url: https://frutjam.com/pl/plugins/markdown-editor
---

# The Native-First, High-Performance Markdown Plugin for JavaScript

## Filozofia: „Najpierw rodzimy”

Większość redaktorów łamie standardowy przepływ pracy w sieci. **MarkdownEditor** to uwzględnia.
Ponieważ znajduje się on bezpośrednio nad `<textarea>`, nie musisz uczyć się nowego sposobu obsługi danych.

- **Nie jest potrzebne wiązanie danych:** od razu po wyjęciu z pudełka współpracuje z `<form method="POST">`
- **Dostęp standardowy:** użyj `document.getElementById('editor').value` tak jak normalnego wejścia.
- **Niezależny od backendu:** działa z dowolnym backendem (Python, Node.js, PHP itp.), tak jak normalne pole formularza

## Kluczowe funkcje

### 🖼️ Zaawansowane przesyłanie obrazów (zoptymalizowane pod kątem SEO)

Nie przeciążaj swojej bazy danych ciężkimi ciągami Base64. Skonfiguruj nasze API, aby przesyłać obrazy bezpośrednio na serwer lub do segmentu S3. Edytor otrzymuje adres URL, dzięki czemu pliki Markdown są lekkie, a ranking SEO Twojej witryny wysoki.

### 🔀 Tryby hybrydowe i zwykłe

Daj swoim użytkownikom to, co najlepsze z obu światów. Przełączaj między **trybem hybrydowym wizualnym (WYSIWYG)**, który ułatwia formatowanie, a **trybem zwykłego znacznika**, który pozwala na niezakłóconą, surową edycję składni.

### 🌍 Gotowy na cały świat z obsługą RTL

Pełna natywna obsługa języków pisanych od prawej do lewej (RTL). Idealny do projektów wymagających obsługi **arabskiego, urdu lub farsi** z automatycznym wyrównywaniem kierunku tekstu.

### ⚡ Wydajność na dużą skalę

- **Lekki**: niewielki rozmiar ~116 KB, który nie spowalnia ładowania strony
- **Obsługa dużych dokumentów**: zoptymalizowana pod kątem obsługi tysięcy linii tekstu bez opóźnień wprowadzania danych i zawieszania się przeglądarki
- **Inteligentne renderowanie**: pozbawione błędów aktualizacje podglądu, obliczenia stylu w pamięci podręcznej i bezkonfliktowa obsługa klawiatury między kontynuacją listy a wcięciem — dzięki czemu Tab i Enter zawsze wykonują dokładnie jedną czynność

### ♿ Dostępne domyślnie

Pełna obsługa ARIA jest wbudowana i nie wymaga dodatkowej konfiguracji. Pasek narzędzi jest właściwym punktem orientacyjnym `role="toolbar"`, panel podglądu ma etykietę `role="region"`, wszystkie ikony SVG są ukryte przed czytnikami ekranu, przełącznik podglądu pokazuje jego stan włączenia/wyłączenia poprzez `aria-pressed`, a wyłączone przyciski paska narzędzi używają zarówno `disabled`, jak i `aria-disabled`, więc technologia wspomagająca nigdy nie jest wprowadzana w błąd. Modale po zamknięciu przywracają ostrość do przycisku wyzwalającego.

### 🛡️ Zero konfliktów CSS

Wszystkie style edytora są w pełni ograniczone do elementu `.markdown-editor-wrapper`. Globalna inspekcja wstępna Tailwinda (resetowanie elementów dla `h1`–`h6`, `a`, `button` itp.) jest wykluczona, więc edytor może działać obok Bootstrap, Tailwind lub dowolnego innego frameworka CSS na tej samej stronie bez przerywania jednego stylu.

### 🔒 Podgląd bezpiecznego XSS

Wyrenderowany podgląd jest oczyszczany za pomocą [DOMPurify](https://github.com/cure53/DOMPurify) przed zapisaniem w DOM. Tagi skryptów, wbudowane procedury obsługi zdarzeń i złośliwe adresy URL w spreparowanych danych wejściowych Markdown są usuwane automatycznie — nie jest wymagana żadna konfiguracja.

## Wersja demonstracyjna edytora Markdown

# Markdown Editor

## Szybka realizacja

### 1. Instalacja

Możesz go zainstalować za pomocą NPM, bezpośrednio zaimportować plik JavaScript lub użyć CDN do szybkiego wdrożenia.

#### Zainstaluj przez NPM

```bash
npm install markdown-text-editor
```

LUB

#### Korzystanie z CDN

Alternatywnie możesz dołączyć następujące linki CDN do swojego kodu HTML

```html
<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>
```

### 2. Struktura HTML

Po prostu umieść `<textarea>` w standardowym formularzu. Nie są wymagane żadne specjalne opakowania.

```html
<form action="/api/save" method="POST">
  <textarea id="markdown-editor" name="content"># Hello World</textarea>
  <button type="submit">Save Content</button>
</form>
```

### 3. Zaimportuj JS i CSS oraz zainicjuj wtyczkę w swoim elemencie tekstowym

```javascript
import MarkdownEditor from "markdown-text-editor";

const editor = new MarkdownEditor('#markdown-editor', {
  placeholder: 'Write your markdown...',
  toolbar: ['heading', 'bold', 'italic', 'strikethrough', 'ul', 'ol', 'checklist', 'blockquote', 'link', 'preview'],
});
```

### 4. Konfiguracja i dostosowywanie

Możesz w pełni dostosować zachowanie i interfejs edytora, przekazując obiekt `options`. Jeśli pominiesz opcję, zostanie użyta wartość **domyślna**.

| Nieruchomość  | Typ              | Domyślny          | Zamiar                                                                                                                                                                                                   |
| ------------- | ---------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode`        | `string`         | `'plain'`         | Ustawia widok początkowy. Użyj `'hybrid'`, aby uzyskać obsługę WYSIWYG lub `'plain'`, aby uzyskać surową składnię.                                                                                       |
| `placeholder` | `string`         | `'Write...'`      | Tekst wyświetlany, gdy edytor jest pusty.                                                                                                                                                                |
| `toolbar`     | `array`          | `[...]`           | Określa, które narzędzia pojawiają się i w jakiej kolejności.                                                                                                                                            |
| `footer`      | `false | object` | wszystko widoczne | Steruje paskiem stanu widocznym pod edytorem. Ustaw wartość `false`, aby całkowicie ją ukryć, lub przekaż obiekt, aby przełączać poszczególne statystyki.                                                |
| `theme`       | `string`         | dziedziczny       | Jawnie ustawia motyw edytora (`'jasny'`, `'ciemny'`, `'snowberry'`, `'ciemny'`). Jeśli zostanie pominięty, edytor dziedziczy `data-theme` od najbliższego elementu nadrzędnego lub samego `<textarea>`.  |
| `minHeight`   | `number`         | `200`             | Minimalna wysokość w pikselach, do której edytor się zmniejszy, gdy treść będzie krótka. W połączeniu z `maxHeight` ustawia zakres automatycznego wzrostu.                                               |
| `maxHeight`   | `number`         | `500`             | Maksymalna wysokość w pikselach, do jakiej może urosnąć edytor w trybie innym niż pełnoekranowy. Gdy treść przekroczy tę wysokość, w edytorze pojawi się pasek przewijania. Edytor posiada również uchwyt przeciągania, dzięki czemu użytkownicy mogą ręcznie zmieniać jego rozmiar powyżej tego limitu. |
| `onChange`    | `function`       | `undefined`       | Wywołanie zwrotne uruchamiane przy każdej zmianie treści — pisaniu, akcjach na pasku narzędzi, cofaniu/ponawianiu i kontynuacji listy. Odbiera bieżący ciąg przeceny jako jedyny argument.               |

#### 🛠 Dostosowywanie paska narzędzi

Pasek narzędzi jest modułowy. Modyfikując tablicę, możesz stworzyć minimalne środowisko lub w pełni funkcjonalny pakiet mocy.

##### Dostępne narzędzia

| Kategoria           | Klucze narzędziowe                                         |
| ------------------- | ---------------------------------------------------------- |
| Typografia          | `heading`, `bold`, `italic`, `strikethrough`, `blockquote` |
| Listy               | `ul` (kula), `ol` (numerowane), `checklist`                |
| Kod                 | `code` (wbudowane), `codeblock` (ogrodzony blok)           |
| Wkładki             | `hr` (reguła pozioma), `table` (szablon tabeli)            |
| Głoska bezdźwięczna | `link`, `image`                                            |
| Redagowanie         | `undo`, `redo`, `indent`, `outdent`                        |
| Pogląd              | `preview`                                                  |

###### 💡 Wskazówki dotyczące wdrożenia:

- **Zmiana kolejności**: przyciski pojawiają się dokładnie w takiej kolejności, w jakiej zostały wymienione w tablicy
- **Usuwanie**: po prostu pomiń dowolny klucz (np. `image`) z tablicy, aby całkowicie wyłączyć tę funkcję dla użytkownika
- **Natywna rezerwa**: jeśli nie podasz `placeholdera` w JS, wtyczka automatycznie użyje atrybutu `placeholder` z Twojego `<textarea>` kodu HTML

#### 📊 Stopka (pasek stanu)

Stopka znajduje się pod edytorem i pokazuje **linię**, **kolumnę** kursora, **liczbę znaków** dokumentu i opcjonalnie **liczbę słów** – wszystko aktualizowane w czasie rzeczywistym. Jest ona domyślnie widoczna i każdą statystykę można przełączać niezależnie.

| Klawisz | Typ       | Domyślny | Opis                                                                             |
| ------- | --------- | -------- | -------------------------------------------------------------------------------- |
| `line`  | `boolean` | `true`   | Pokaż bieżący numer linii.                                                       |
| `col`   | `boolean` | `true`   | Pokaż bieżący numer kolumny.                                                     |
| `chars` | `boolean` | `true`   | Pokaż całkowitą liczbę znaków.                                                   |
| `words` | `boolean` | `false`  | Pokaż całkowitą liczbę słów. Domyślnie wyłączone — ustaw na `true`, aby włączyć. |

##### Przykłady użycia

```javascript
// Default — line, col, and chars visible
new MarkdownEditor('#editor');

// Disable the footer entirely
new MarkdownEditor('#editor', { footer: false });

// Hide only character count
new MarkdownEditor('#editor', { footer: { chars: false } });

// Hide line and column, keep character count
new MarkdownEditor('#editor', { footer: { line: false, col: false } });

// Show only line number
new MarkdownEditor('#editor', { footer: { col: false, chars: false } });

// Enable word count alongside the defaults
new MarkdownEditor('#editor', { footer: { words: true } });

// Show word count only
new MarkdownEditor('#editor', { footer: { line: false, col: false, chars: false, words: true } });
```

### 5. Pobieranie, ustawianie i przesyłanie treści

Jedną z głównych zalet **MarkdownEditor** jest to, że zapewnia doskonałą synchronizację podstawowego `<textarea>`. Niezależnie od tego, czy używasz nowoczesnego frameworku JavaScript, czy tradycyjnego backendu, takiego jak **Django, PHP czy Laravel**, przepływ pracy pozostaje prosty i natywny.

#### 1. Sposób rodzimy (zalecany)

Ponieważ edytor rozszerza standardowy obszar tekstowy, możesz używać znanych metod DOM. To najszybszy sposób na interakcję z danymi bez konieczności poznawania nowego API.

```javascript
// Retrieve content via ID
const markdown = document.getElementById('markdown-editor').value;

// Set content via ID (The editor UI updates automatically)
document.getElementById('markdown-editor').value = "# New Heading Content";
```

#### 2. Używanie odniesienia do zmiennej

Jeśli masz odwołanie do elementu Textarea, możesz go użyć bezpośrednio — nie jest potrzebny żaden interfejs API specyficzny dla biblioteki.

```javascript
const textarea = document.getElementById('markdown-editor');

// Retrieve content
const markdown = textarea.value;

// Set content (the editor UI reflects this immediately)
textarea.value = "## Updated via JS";
```

#### 3. Ustawianie i odczytywanie treści

Zalecany sposób ustawiania treści początkowej odbywa się bezpośrednio w kodzie `<textarea>` HTML — działa to naturalnie z każdym frameworkiem zaplecza (Django, Laravel, Rails, PHP itp.), a edytor renderuje ją automatycznie podczas init.

```html
<!-- Recommended: set content server-side -->
<textarea id="markdown-editor"># Hello World</textarea>
```

Aby przeczytać lub zaktualizować treść w czasie wykonywania, użyj natywnej wartości `textarea`. Po aktualizacji wywołaj `editor.render()`, aby odświeżyć podgląd i warstwę hybrydową.

```javascript
const textarea = document.getElementById('markdown-editor');

// Read
const markdown = textarea.value;

// Update at runtime
textarea.value = '# New content';
editor.render();
```

#### 4. Zburzenie edytora — `destroy()`

Wywołaj funkcję `editor.destroy()`, aby usunąć opakowanie DOM edytora i przywrócić oryginalny `<textarea>` do jego pozycji w dokumencie. Przydatne w aplikacjach jednostronicowych podczas odmontowywania widoku.

```javascript
const editor = new MarkdownEditor('#markdown-editor');

// Remove the editor and restore the plain textarea
editor.destroy();
```

##### Reagowanie na zmiany za pomocą `onChange`

Przekaż wywołanie zwrotne `onChange`, aby otrzymywać powiadomienia o każdej zmianie treści. Odbiera bieżący ciąg przeceny.

```javascript
const editor = new MarkdownEditor('#markdown-editor', {
    onChange(value) {
        console.log('Content changed:', value.length, 'characters');
    }
});
```

###### Wersja robocza jest automatycznie zapisywana w `localStorage`

Użyj `onChange`, aby zapisać wersję roboczą przy każdym naciśnięciu klawisza. Przywróć go, wypełniając obszar tekstowy przed inicjalizacją edytora.

```javascript
const DRAFT_KEY = 'my-page-draft';

// Restore saved draft before init (only if textarea starts empty)
const textarea = document.getElementById('markdown-editor');
const saved = localStorage.getItem(DRAFT_KEY);
if (saved && !textarea.value) textarea.value = saved;

// Save on every change
const editor = new MarkdownEditor('#markdown-editor', {
    onChange(value) {
        localStorage.setItem(DRAFT_KEY, value);
    }
});

// Clear draft after successful form submission
document.querySelector('form').addEventListener('submit', () => {
    localStorage.removeItem(DRAFT_KEY);
});
```

#### 4. Łatwe i automatyczne przesyłanie formularzy

Ponieważ **MarkdownEditor** jest zbudowany bezpośrednio na natywnym `<textarea>`, jest kompatybilny z każdym frameworkiem backendowym (Django, Laravel, PHP, Ruby on Rails itp.) od razu po wyjęciu z pudełka.

Tutaj błyszczy filozofia „Native-First”. Nie musisz ręcznie synchronizować danych przed przesłaniem formularza. Przeglądarka traktuje edytor dokładnie tak, jak standardowe pole wejściowe.

```html
<form method="POST" action="/api/submit">
  <textarea id="markdown-editor" name="content" class="h-48" rows="5">
    # Initial Content
  </textarea>

  <button type="submit">Submit to Server</button>
</form>

<script>
  // Just initialize it. That's it.
  new MarkdownEditor('#markdown-editor');
</script>
```

**Uwaga:** inicjalizacja wtyczki MarkdownEditor jest obowiązkowa

Po prostu użyj standardowego `<formularz>` HTML. Atrybut `name` w obszarze tekstowym jest tym, czego Twój serwer użyje do identyfikacji treści.

##### 🚀 Dlaczego to zmienia zasady gry dla backendów

Ponieważ edytor zachowuje natywne zachowanie `<textarea>`, serwer obsługuje dane jako standardowy ciąg znaków. Nie jest wymagana żadna dodatkowa logika — nie ma `preventDefault()` ani ręcznej konstrukcji `FormData`.

##### 💡 Dlaczego jest to „funkcja zabójcza”:

Większość edytorów (np. Quill, Editor.js, simpleMDE, easyMDE) zapisuje dane w złożonych strukturach JSON. Jeśli programista z nich korzysta, musi przepisać schemat bazy danych i logikę renderowania.

Dzięki **MarkdownEditor** programista może wykorzystać starą witrynę i zastąpić zwykły `<textarea>` Twoim edytorem, a **backend nawet nie zauważy, że się to zmieniło**. Po prostu otrzymuje ten sam surowy tekst, co zawsze, ale użytkownik otrzymuje 10 razy lepsze wrażenia.

| Ramy / język      | Jak uzyskać dostęp do treści Markdown |
| ----------------- | ------------------------------------- |
| PHP               | $\_POST['content']                    |
| Django            | request.POST.get('content')           |
| Node.js (Express) | req.body.content                      |
| Laravel           | $request->input('content')            |
| Ruby on Rails     | params[:content]                      |

## Opcje konfiguracji

### 🖼️ Zaawansowane przesyłanie obrazów

Natywna obsługa przesyłania obrazów — zamiast polegania na wolnych, obciążających pamięć ciągach Base64 — to znacząca korzyść zarówno pod względem wydajności, jak i SEO.

#### Opcje konfiguracji

Narzędzie do tworzenia obrazów obsługuje konfigurację `fileInput` do obsługi bezpośredniego przesyłania na serwer.

- `accept`: zdefiniuj tablicę dozwolonych formatów obrazów (np. „webp”, „avif”)
- `uploadUrl`: Określ punkt końcowy zaplecza, do którego obiekt `File` zostanie wysłany za pośrednictwem `POST`
- `params`: opcjonalny obiekt do wysyłania dodatkowych danych (takich jak tokeny CSRF, identyfikatory użytkowników lub nazwy folderów) wraz z plikiem obrazu

#### Przykład użycia (pełna konfiguracja)

```javascript
const options = {
  placeholder: 'Start writing...',
  toolbar: [
    'link',
    {
      image: {
        fileInput: {
            accept: ['webp', 'avif'], // restrict the image upload format
            uploadUrl: '/api/upload', // Your upload endpoint
            params: {
                _token: 'your_csrf_token_here', // Essential for Laravel/Django
                folder: 'blog_posts'
            }
        },
        // Supports boolean: true/false OR object: { required: true }
        altInput: { required: true }
      }
    },
    'preview'
  ],
}
const editor = new MarkdownEditor('#markdown-editor', options);
```

#### 📡 Szczegóły integracji serwera

##### 1. Prośba

Edytor wysyła żądanie `POST` jako `multipart/form-data`. Domyślnie zawiera:

- `image_file`: Rzeczywisty obiekt pliku
- `image_alt`: tekst alternatywny wprowadzony przez użytkownika
- ...plus wszelkie niestandardowe dane zdefiniowane w obiekcie `params`

##### 2. Wymagana odpowiedź

Aby potwierdzić pomyślne przesłanie i wstawić obraz do edytora, Twój serwer **musi** zwrócić następującą strukturę JSON:

```json
{
  "success": true,
  "image_path": "https://cdn.yourdomain.com/uploads/image.webp"
}
```

**Uwaga**: upewnij się, że używasz klucza `image_path` jako adresu URL przesłanego obrazu.

#### Walidacja tekstu alternatywnego obrazu (`altInput`)

Aby mieć pewność, że Twoje treści pozostaną dostępne i przyjazne dla SEO, `MarkdownEditor` domyślnie wymusza sprawdzanie poprawności tekstu alternatywnego. Możesz skonfigurować to zachowanie, używając skrótu logicznego lub szczegółowego obiektu.

- **Zachowanie domyślne**: jeśli nie zdefiniowano `altInput`, domyślnie jest to `{ wymagane: true `
- **Wymuś dostępność**: użytkownicy nie będą mogli wstawić obrazu, dopóki nie zostanie podany opis alternatywny

##### Przykłady konfiguracji:

###### 1. Domyślne (nie wymaga konfiguracji)

```javascript
// Alt text is REQUIRED by default
image: {
  fileInput: { uploadUrl: '/api/upload' }
}
```

###### 2. Skrót (wyłącz weryfikację)

Jeśli chcesz zezwolić na obrazy bez opisów, po prostu ustaw wartość logiczną na `false`.

```javascript
image: {
  altInput: false // Users can now skip the alt text field
}
```

###### 3. Oparte na obiektach (jawne)

```javascript
image: {
    altInput: {
        required: false // Disables alt text validation — users can skip the alt field
    }
}
```

#### Standardowe użycie obrazu (brak `fileInput`):

Jeśli `fileInput` nie jest skonfigurowane, edytor domyślnie korzysta z prostego modalu opartego na adresie URL. Jest to idealne rozwiązanie, jeśli Twoi użytkownicy głównie łączą się z zewnętrznymi hostami obrazów.

```javascript
const options = {
  toolbar: [
    'link',
    'image',
    'preview'
  ],
}
const editor = new MarkdownEditor('#markdown-editor', options);
```

##### 💡 Po co używać parametrów?

W frameworkach takich jak Laravel czy Django nie można przesyłać plików bez tokena CSRF. Dodając `_token` do obiektu `params`, Twoje żądanie bezproblemowo przejdzie przez oprogramowanie pośredniczące bezpieczeństwa zaplecza, zachowując filozofię „Zero Logic” dla kontrolerów po stronie serwera.

### 🔀 Tryby edycji

**MarkdownEditor** oferuje dwa różne sposoby pisania i formatowania treści. Możesz przełączać się między tradycyjnym widokiem skupionym na składni lub nowoczesnym, opartym na wizualizacjach.

#### Konfiguracja

Tryb początkowy można zdefiniować za pomocą właściwości `mode` podczas inicjalizacji.

- `plain` (**Domyślne**): czyste, wydajne środowisko Markdown, w którym widoczna jest składnia (taka jak `**bold**` lub `# nagłówek`). Idealny dla programistów i purystów Markdown
- `hybrid`: środowisko inspirowane WYSIWYG, które renderuje formatowanie (pogrubienie, kursywa, nagłówki) w czasie rzeczywistym podczas pisania, zachowując jednocześnie podstawową strukturę Markdown.

##### Realizacja:

```javascript
// Default initialization (Plain Mode)
new MarkdownEditor('#markdown-editor');

// Explicit Plain Mode
new MarkdownEditor('#markdown-editor', {
    mode: 'plain'
});

// Hybrid (Visual) Mode
new MarkdownEditor('#markdown-editor', {
    mode: 'hybrid'
});
```

##### Podgląd trybu hybrydowego i zwykłego:

###### Tryb hybrydowy

Formatowanie wizualne jest renderowane w czasie rzeczywistym podczas pisania.

# Hybrid mode markdown editor
This will render visual styles immediately.

###### Tryb zwykły (domyślny)

Koncentruje się na surowej składni Markdown, aby zapewnić lekkość.

# Plain mode markdown editor
This shows the raw **syntax**.

| Konfig                                                                                                                                                                                                   | Właściwość / narzędzie                                                                                                                                                                    | Opis                                                                                                                                                                                                     |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Obiekt opcji                                                                                                                                                                                             | `placeholder`                                                                                                                                                                             | Ustawia tekst zastępczy dla obszaru tekstowego (opcjonalnie, ponieważ można również użyć standardowego atrybutu pola tekstowego HTML)                                                                    |
|                                                                                                                                                                                                          | `mode: 'hybrid'`                                                                                                                                                                          | Włącza funkcję inspirowaną WYSIWYG, która renderuje formatowanie (pogrubienie, kursywa, nagłówki) w czasie rzeczywistym podczas pisania                                                                  |
| `toolbar`: Określa, które narzędzia    pojawiają się na pasku narzędzi i ich kolejność.                                                                                                                  | `heading`                                                                                                                                                                                 | Otwiera menu rozwijane umożliwiające wybranie poziomu nagłówka H1–H6                                                                                                                                     |
|                                                                                                                                                                                                          | `bold`                                                                                                                                                                                    | Włącza pogrubione formatowanie tekstu.                                                                                                                                                                   |
|                                                                                                                                                                                                          | `italic`                                                                                                                                                                                  | Umożliwia formatowanie tekstu kursywą.                                                                                                                                                                   |
|                                                                                                                                                                                                          | `strikethrough`                                                                                                                                                                           | Umożliwia przekreślenie tekstu.                                                                                                                                                                          |
|                                                                                                                                                                                                          | `ol`                                                                                                                                                                                      | (Lista uporządkowana): Konwertuje tekst na format listy numerowanej.                                                                                                                                     |
|                                                                                                                                                                                                          | `ul`                                                                                                                                                                                      | (Lista nieuporządkowana): Konwertuje tekst na listę punktowaną.                                                                                                                                          |
|                                                                                                                                                                                                          | `checklist`                                                                                                                                                                               | Dodaje pola wyboru do tekstu, dzięki czemu doskonale nadaje się do zadań, list rzeczy do zrobienia lub śledzenia stanu ukończenia.                                                                       |
|                                                                                                                                                                                                          | `blockquote`                                                                                                                                                                              | Zaznacz cytowany lub podkreślony tekst.                                                                                                                                                                  |
|                                                                                                                                                                                                          | `code`                                                                                                                                                                                    | Zawija zaznaczony tekst pojedynczymi znacznikami dla kodu wbudowanego. Ponowne kliknięcie usuwa tylne znaczniki.                                                                                         |
|                                                                                                                                                                                                          | `codeblock`                                                                                                                                                                               | Zawija zaznaczony tekst w ogrodzonym bloku kodu z potrójnym znacznikiem. Ponowne kliknięcie usuwa ogrodzenia.                                                                                            |
|                                                                                                                                                                                                          | `hr`                                                                                                                                                                                      | Wstawia linijkę poziomą `---` w miejscu kursora, w osobnej linii.                                                                                                                                        |
|                                                                                                                                                                                                          | `table`                                                                                                                                                                                   | Wstawia startowy szablon tabeli przecen 2x3 w pozycji kursora.                                                                                                                                           |
|                                                                                                                                                                                                          | `image`                                                                                                                                                                                   | Umożliwia wstawianie obrazów za pomocą składni przecen.                                                                                                                                                  |
|                                                                                                                                                                                                          | `link`                                                                                                                                                                                    | Umożliwia dodawanie hiperłączy do tekstu.                                                                                                                                                                |
|                                                                                                                                                                                                          | `undo`                                                                                                                                                                                    | Aby cofnąć ostatnie zmiany.                                                                                                                                                                              |
| `redo`                                                                                                                                                                                                   | Aby ponownie zastosować ostatnio cofnięte zmiany.                                                                                                                                         |
| `indent`                                                                                                                                                                                                 | Aby zwiększyć poziom wcięcia.                                                                                                                                                             |
| `outdent`                                                                                                                                                                                                | Aby zmniejszyć poziom wcięcia.                                                                                                                                                            |
| `preview`                                                                                                                                                                                                | Przełącza pełnoekranowy podgląd obok siebie. Pola wyboru w panelu podglądu można kliknąć i natychmiast zaktualizować źródło przeceny. Naciśnij Escape, aby wyjść z trybu pełnoekranowego. |
| [**Zaawansowana funkcja przesyłania obrazów**](#advanced-image-upload):    Umożliwia konfigurowanie przesyłania obrazów na     własny serwer i ustawianie ścieżki     obrazu za pośrednictwem interfejsu API. Poprawia to wydajność i SEO. | `fileInput`                                                                                                                                                                               | `accept`: tablica dozwolonych typów plików graficznych (np. `'webp'`, `'avif'`).   `uploadUrl`: punkt końcowy zaplecza, do którego plik jest wysyłany za pośrednictwem `POST`.   `params`: Opcjonalny obiekt dla dodatkowych danych (tokeny CSRF, identyfikatory użytkowników, nazwy folderów) |
|                                                                                                                                                                                                          | `altInput`                                                                                                                                                                                | `required: false`: Wyłącza sprawdzanie poprawności wprowadzania tekstu alternatywnego (wartość domyślna to true)                                                                                         |

### ⌨️ Skróty klawiaturowe

Typowe czynności formatowania można uruchomić bezpośrednio z klawiatury, bez dotykania paska narzędzi. Każdy skrót jest także pokazany w podpowiedzi odpowiedniego przycisku na pasku narzędzi.

| Skrót                        | Działanie                                               |
| ---------------------------- | ------------------------------------------------------- |
| `Ctrl + B` / `⌘ B`           | Przełącz opcję **Pogrubienie**                          |
| `Ctrl + I` / `⌘ I`           | Przełącz opcję *Kursywa*                                |
| `Ctrl + K` / `⌘ K`           | Wstaw łącze                                             |
| `` Ctrl + ` `` / `` ⌘ ` ``   | Przełącz wbudowany `Kod`                                |
| `Ctrl + Shift + S` / `⌘ ⇧ S` | Przełącz opcję ~~Przekreślenie~~                        |
| `Ctrl + Z` / `⌘ Z`           | Anulować                                                |
| `Ctrl + Shift + Z` / `⌘ ⇧ Z` | Przerobić                                               |
| `Tab`                        | Wcięcie wybranych linii                                 |
| `Shift + Tab`                | Zmniejsz wybrane linie                                  |
| `Ctrl + F` / `⌘ F`           | Otwórz panel Znajdź                                     |
| `Ctrl + H` / `⌘ H`           | Otwórz panel Znajdź i zamień                            |
| `Escape`                     | Zamknij panel Znajdź / Wyjdź z podglądu pełnoekranowego |

### 🔍 Znajdź i amp; Zastępować

W edytorze dostępny jest wbudowany panel wyszukiwania i zamiany — nie jest potrzebne żadne rozszerzenie przeglądarki ani osobne narzędzie.

- Naciśnij `Ctrl + F` (lub `⌘ F`), aby otworzyć panel **Znajdź**
- Naciśnij `Ctrl + H` (lub `⌘ H`), aby otworzyć **Find & Wymień** panel
- Wyszukiwanie **nie uwzględnia wielkości liter** i wyświetla licznik meczów na żywo (np. *3 z 12*)
- Nawiguj po dopasowaniach za pomocą przycisków **▲ / ▼** lub `Enter` / `Shift + Enter`
- Opcja **Zamień** zastępuje aktualnie podświetlone dopasowanie; Opcja **Zamień wszystko** zastępuje wszystkie wystąpienia na raz
- Naciśnij `Escape`, aby zamknąć panel i powrócić do edytora

Panel unosi się w prawym górnym rogu obszaru zawartości edytora i nie zakłóca pisania.

### 🌙 Tematyka

**MarkdownEditor** automatycznie dziedziczy motyw z otaczającej strony — nie jest wymagana żadna konfiguracja. Edytor odczytuje `data-theme` od najbliższego przodka podczas inicjalizacji, dzięki czemu pozostaje zsynchronizowany z motywem Twojej witryny od razu po wyjęciu z pudełka.

#### Sposób rozwiązania tematu (kolejność priorytetów)

1. Opcja **`theme`** — jawne zastąpienie przekazane w obiekcie opcji
2. **`data-theme` w `<textarea>`** — ustawiany bezpośrednio na elemencie
3. **`motyw danych` dowolnego przodka** — np. `<html>`, `<body>` lub opakowanie `<div>`

#### Dostępne motywy

`'jasny'` (domyślnie), `'ciemny'`, `'jasny'`, `'ciemny'`

##### Opcja 1 — dziedzicz z `<html>` lub dowolnego przodka (konfiguracja zerowa)

```html
<html data-theme="dark">
  ...
  <textarea id="markdown-editor"></textarea>
  <script>
    new MarkdownEditor('#markdown-editor'); // picks up dark automatically
  </script>
```

##### Opcja 2 — ustaw `data-theme` bezpośrednio w `<textarea>`

```html
<textarea id="markdown-editor" data-theme="dark"></textarea>
<script>
  new MarkdownEditor('#markdown-editor');
</script>
```

##### Opcja 3 — wyraźna opcja `theme` (zastępuje wszystko)

```javascript
new MarkdownEditor('#markdown-editor', {
    theme: 'dark'
});
```

#### 🎨 Motyw niestandardowy za pomocą zmiennych CSS

Możesz w pełni dostosować wygląd edytora, zastępując jego zmienne CSS w elemencie `.markdown-editor-wrapper` lub dowolnym selektorze `[data-theme]`. Wszystkie kolory wykorzystują [przestrzeń kolorów OKLCH](https://oklch.com), aby uzyskać spójne percepcyjnie rezultaty.

| Zmienny              | Zamiar                                            | Światło domyślne        | Ciemny domyślny         |
| -------------------- | ------------------------------------------------- | ----------------------- | ----------------------- |
| `--color-base`       | Tło redaktora                                     | `oklch(100% 0 0)`       | `oklch(10.9% 0 0)`      |
| `--color-on-base`    | Podstawowy kolor tekstu                           | `oklch(22% 0 0)`        | `oklch(98% 0 0)`        |
| `--color-primary`    | Akcent podstawowy (aktywny pasek narzędzi, linki) | `oklch(51.1% .262 277)` | `oklch(66.4% .184 286)` |
| `--color-on-primary` | Tekst na powierzchniach w kolorze podstawowym     | `oklch(96.2% .018 272)` | `oklch(10% .01 270)`    |
| `--color-secondary`  | Akcent wtórny                                     | `oklch(59.1% .293 323)` | `oklch(65% .18 220)`    |
| `--color-accent`     | Podświetl akcent (kod wbudowany, kursywa)         | `oklch(54.1% .281 293)` | `oklch(75% .18 50)`     |
| `--color-neutral`    | Powierzchnie neutralne (granice, przegrody)       | `oklch(15% 0 0)`        | `oklch(85% 0 0)`        |
| `--color-error`      | Kolor stanu błędu                                 | `oklch(57.7% .245 27)`  | `oklch(60% .22 30)`     |
| `--border-radius`    | Zaokrąglenie narożników ramki edytora             | `0.25rem`               | |

##### Przykład niestandardowego motywu

Zastąp dowolną zmienną w `.markdown-editor-wrapper` po zainicjowaniu edytora lub zdefiniuj niestandardowy blok `[data-theme]` w swoim arkuszu stylów:

```css
/* Override individual variables */
.markdown-editor-wrapper {
    --color-primary: oklch(60% 0.2 30);   /* orange accent */
    --border-radius: 0.5rem;
}

/* Or define a full custom theme */
[data-theme="brand"] .markdown-editor-wrapper,
.markdown-editor-wrapper[data-theme="brand"] {
    --color-base:       oklch(15% 0.01 250);
    --color-on-base:    oklch(95% 0 0);
    --color-primary:    oklch(65% 0.22 145);   /* green */
    --color-on-primary: oklch(10% 0 0);
    --color-accent:     oklch(75% 0.18 60);
    --color-neutral:    oklch(80% 0 0);
    --border-radius:    0.75rem;
}
```

```javascript
new MarkdownEditor('#markdown-editor', { theme: 'brand' });
```

- **Podgląd w czasie rzeczywistym:** zobacz, jak przeceny są renderowane natychmiast podczas pisania.
- **Podświetlanie składni:** zwiększona czytelność dzięki przejrzystemu kodowi i formatowaniu przecen.
- **Łatwa integracja**: bezproblemowo integruj się z dowolnym projektem internetowym przy minimalnej konfiguracji.
- **Dostosowywany pasek narzędzi:** dynamicznie konfiguruj i zmieniaj kolejność opcji paska narzędzi, takich jak pogrubienie, kursywa i inne.

## Cechy

## 🔌 Integracja formularzy natywnych

Działa dokładnie tak, jak standardowy `<textarea>`. Żadnych skomplikowanych interfejsów API — wystarczy użyć atrybutu `value` lub `name`. To „po prostu działa” ze standardowymi formularzami HTML w PHP, Django lub Node.js.

## 🖼️ Zaawansowane przesyłanie obrazu

Skonfiguruj przesyłanie natywnego serwera za pośrednictwem interfejsu API. Unikaj ciężkich ciągów Base64, aby zapewnić szybsze ładowanie strony i lepsze SEO, hostując obrazy we własnej sieci CDN.

## 🔀 Tryby hybrydowe i zwykłe

Przełączaj się między trybem **Hybrid (WYSIWYG)** do edycji wizualnej lub trybem **Plain Markdown**, aby uzyskać tradycyjne wrażenia z kodowania.

## 🚀 Wysoka wydajność

Mały pakiet **~116 KB** zoptymalizowany pod kątem „Ciężkich treści”. Obsługuje ogromne dokumenty i duże pliki bez opóźnień wejściowych i spadków wydajności.

## 🌍 Wbudowana obsługa RTL

Natywna obsługa języków pisanych od prawej do lewej, takich jak arabski, urdu i perski. Idealny do tworzenia aplikacji dostępnych na całym świecie.

## 🌙 Motywy adaptacyjne

Zawiera automatyczną obsługę trybu ciemnego. Synchronizuje się z ustawieniami systemu lub biblioteką **Frutjam UI**, zapewniając płynne wrażenia wizualne.

## 📝 Inteligentna edycja

Automatyczna kontynuacja listy w stylu GitHuba dla list uporządkowanych, list nieuporządkowanych i list kontrolnych — naciśnij `Enter`, a edytor będzie kontynuował wzorzec. Pola wyboru w panelu podglądu można kliknąć i natychmiast synchronizować ze źródłem przecen.

## 📱 W pełni responsywny

Płynny, dostosowany do urządzeń mobilnych interfejs użytkownika, który idealnie dopasowuje się do komputerów stacjonarnych, tabletów i smartfonów, umożliwiając edycję w dowolnym miejscu.

## 📦 Uniwersalne wsparcie

Kompatybilny z **ESM, UMD, CommonJS i IIFE**. Działa od razu po uruchomieniu za pośrednictwem CDN (`<script src>`), npm lub dowolnego programu pakującego (Vite, webpack, Rollup) — nie jest wymagana dodatkowa konfiguracja.

## ♿ Dostępne domyślnie

Wbudowana pełna obsługa ARIA — punkt orientacyjny na pasku narzędzi, oznaczony obszar podglądu, przyciski przyjazne dla czytnika ekranu, `wciśnięte aria` na przełączniku podglądu, `wyłączone` i `aria-wyłączone` w nieaktywnych narzędziach oraz prawidłowe przywracanie fokusu po zamknięciu modów.

## 🛡️ Zero konfliktów CSS

Style edytora są w pełni ograniczone do `.markdown-editor-wrapper`. Globalna inspekcja wstępna Tailwinda jest wykluczona, więc edytor może bezpiecznie współpracować z Bootstrapem, Tailwindem lub jakimkolwiek innym frameworkiem, nie naruszając ich stylu.

## ⌨️ Skróty klawiaturowe

`Ctrl+B`, `Ctrl+I`, `Ctrl+K`, `` Ctrl+` ``, `Ctrl+Shift+S` — typowe operacje formatowania bez dotykania myszy. Każdy skrót jest pokazany w etykiecie przycisku paska narzędzi.

## 🔍 Znajdź i amp; Zastępować

Naciśnij `Ctrl+F`, aby znaleźć, lub `Ctrl+H`, aby otworzyć funkcję find & zastępować. Wyszukiwanie bez rozróżniania wielkości liter z licznikiem dopasowań na żywo, nawigacją następny/poprzedni, pojedynczym zastępowaniem i zastępowaniem wszystkich — bez opuszczania edytora.

## Przykład pełnej konfiguracji

Użyj tego obszernego przykładu, aby zainicjować **MarkdownEditor** ze wszystkimi podstawowymi funkcjami, w tym niestandardową kolejnością pasków narzędzi i zaawansowaną obsługą przesyłania obrazów.

```javascript
const editor = new MarkdownEditor('#markdown-editor', {
    mode: 'hybrid',
    placeholder: 'Start writing...',
    footer: {
        line: true,
        col: true,
        chars: true,
        words: true,
    },
    onChange(value) {
        console.log('Content updated:', value.length, 'characters');
    },
    toolbar: [
        'heading', 'bold', 'italic', 'strikethrough', 'blockquote',
        'ul', 'ol', 'checklist',
        'code', 'codeblock', 'hr', 'table',
        {
            image: {
                fileInput: {
                    accept: ['webp', 'avif', 'png'],
                    uploadUrl: '/api/upload'
                }
            }
        },
        'link', 'undo', 'redo', 'indent', 'outdent', 'preview'
    ],
});

// Read content natively
const markdown = document.getElementById('markdown-editor').value;

// Update content programmatically (dispatch input to keep preview in sync)
// const ta = document.getElementById('markdown-editor');
// ta.value = '# New content';
// ta.dispatchEvent(new Event('input', { bubbles: true }));

// destroy() when the view unmounts (SPAs)
// editor.destroy();
```

