---
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/id/plugins/markdown-editor
---

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

## The Philosophy: "Native-First"

Most editors break the standard web workflow. **MarkdownEditor** embraces it.
Because it sits directly on top of a `<textarea>`, you don't need to learn a new way to handle data.

- **No Data Binding Needed:** Works with `<form method="POST">` out of the box
- **Standard Access:** Use `document.getElementById('editor').value` just like a normal input.
- **Backend Agnostic:** Works with any backend (Python, Node.js, PHP, etc.) just like a normal form field

## Key Features

### 🖼️ Advanced Image Upload (SEO Optimized)

Don't bloat your database with heavy Base64 strings. Configure our API to upload images directly to your server or S3 bucket. The editor receives the URL, keeping your Markdown files light and your site's SEO ranking high.

### 🔀 Hybrid & Plain Modes

Give your users the best of both worlds. Switch between a **Visual (WYSIWYG) Hybrid mode** for easy formatting and a **Plain Markdown mode** for distraction-free, raw syntax editing.

### 🌍 Global-Ready with RTL Support

Full native support for Right-to-Left (RTL) languages. Perfect for projects requiring **Arabic, Urdu, or Farsi** support with automatic text-direction alignment.

### ⚡ Performance at Scale

- **Lightweight**: A tiny ~116KB footprint that won't slow down your page load
- **Large Document Support**: Optimized to handle thousands of lines of text without input lag or browser freezing
- **Smart Rendering**: Debounced preview updates, cached style calculations, and conflict-free keyboard handling between list continuation and indentation — so Tab and Enter always do exactly one thing

### ♿ Accessible by Default

Full ARIA support is built in and requires no extra configuration. The toolbar is a proper `role="toolbar"` landmark, the preview pane is a labelled `role="region"`, all SVG icons are hidden from screen readers, the preview toggle exposes its on/off state via `aria-pressed`, and disabled toolbar buttons use both `disabled` and `aria-disabled` so assistive technology is never misled. Modals return focus to the triggering button when closed.

### 🛡️ Zero CSS Conflicts

All editor styles are fully scoped to the `.markdown-editor-wrapper` element. Tailwind's global preflight (element resets for `h1`–`h6`, `a`, `button`, etc.) is excluded, so the editor can live alongside Bootstrap, Tailwind, or any other CSS framework on the same page without breaking a single style.

### 🔒 XSS Safe Preview

The rendered preview is sanitized via [DOMPurify](https://github.com/cure53/DOMPurify) before being written to the DOM. Script tags, inline event handlers, and malicious URLs in crafted markdown input are stripped automatically — no configuration required.

## Markdown Editor Demo

# Markdown Editor

## Quick Implementation

### 1. Installation

You can install it using NPM, import the JavaScript file directly, or use a CDN for rapid deployment.

#### Install via NPM

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

OR

#### Using a CDN

Alternatifnya, sertakan tautan CDN berikut dalam HTML Anda

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

### 2. Struktur HTML

Cukup tempatkan `<textarea>` di dalam formulir standar Anda. Tidak diperlukan pembungkus khusus.

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

### 3. Impor JS dan CSS dan inisialisasi plugin pada elemen textarea Anda

```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. Konfigurasi & Kustomisasi

Anda dapat sepenuhnya menyesuaikan perilaku dan antarmuka editor dengan meneruskan objek `options`. Jika Anda menghilangkan opsi, nilai **default** akan digunakan.

| Milik         | Jenis            | Bawaan         | Purpose                                                                                                                                                                                                  |
| ------------- | ---------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode`        | `string`         | `'plain'`      | Mengatur tampilan awal. Gunakan `'hybrid'` untuk pengalaman WYSIWYG atau `'plain'` untuk sintaksis mentah.                                                                                               |
| `placeholder` | `string`         | `'Write...'`   | Teks ditampilkan saat editor kosong.                                                                                                                                                                     |
| `toolbar`     | `array`          | `[...]`        | Menentukan alat mana yang muncul dan dalam urutan apa.                                                                                                                                                   |
| `footer`      | `false | object` | semua terlihat | Mengontrol bilah status yang ditampilkan di bawah editor. Setel ke `false` untuk menyembunyikannya sepenuhnya, atau teruskan objek untuk mengubah statistik individual.                                  |
| `theme`       | `string`         | diwariskan     | Menetapkan tema editor secara eksplisit (`'light'`, `'dark'`, `'snowberry'`, `'darkberry'`). Jika dihilangkan, editor akan mewarisi `data-theme` dari elemen leluhur terdekat atau `<textarea>` itu sendiri. |
| `minHeight`   | `number`         | `200`          | Tinggi minimum dalam piksel yang akan diperkecil oleh editor ketika kontennya pendek. Pasangkan dengan `maxHeight` untuk menyetel rentang pertumbuhan otomatis.                                          |
| `maxHeight`   | `number`         | `500`          | Tinggi maksimum dalam piksel yang dapat ditingkatkan editor dalam mode non-layar penuh. Setelah konten melebihi ketinggian ini, bilah gulir akan muncul di dalam editor. Editor juga memiliki pegangan tarik sehingga pengguna dapat mengubah ukurannya secara manual melebihi batas ini. |
| `onChange`    | `function`       | `undefined`    | Callback diaktifkan pada setiap perubahan konten — pengetikan, tindakan toolbar, undo/redo, dan kelanjutan daftar. Menerima string penurunan harga saat ini sebagai satu-satunya argumennya.             |

#### 🛠 Kustomisasi Bilah Alat

Bilah alatnya bersifat modular. Anda dapat menciptakan pengalaman minimal atau power suite berfitur lengkap dengan memodifikasi array.

##### Alat yang Tersedia

| Kategori   | Kunci Alat                                                 |
| ---------- | ---------------------------------------------------------- |
| Typography | `heading`, `bold`, `italic`, `strikethrough`, `blockquote` |
| Daftar     | `ul` (peluru), `ol` (bernomor), `checklist`                |
| Kode       | `code` (sebaris), `codeblock` (blok berpagar)              |
| Sisipan    | `hr` (aturan horisontal), `table` (templat tabel)          |
| Media      | `link`, `image`                                            |
| Mengedit   | `undo`, `redo`, `indent`, `outdent`                        |
| Melihat    | `preview`                                                  |

###### 💡 Kiat Penerapan:

- **Pengurutan ulang**: Tombol muncul sesuai urutan yang Anda cantumkan dalam larik
- **Menghapus**: Cukup hilangkan kunci apa pun (seperti `gambar`) dari array untuk menonaktifkan fitur tersebut sepenuhnya bagi pengguna
- **Pengganti Asli**: Jika Anda tidak memberikan `placeholder` di JS, plugin akan secara otomatis menggunakan atribut `placeholder` dari `<textarea>` HTML Anda

#### 📊 Catatan Kaki (Bilah Status)

Footer berada di bawah editor dan menampilkan **baris**, **kolom** kursor, **jumlah karakter** dokumen, dan **jumlah kata** opsional — semuanya diperbarui secara real-time. Ini terlihat secara default dan setiap stat dapat diubah secara independen.

| Kunci   | Jenis     | Bawaan  | Keterangan                                                                                 |
| ------- | --------- | ------- | ------------------------------------------------------------------------------------------ |
| `line`  | `boolean` | `true`  | Tampilkan nomor baris saat ini.                                                            |
| `col`   | `boolean` | `true`  | Tampilkan nomor kolom saat ini.                                                            |
| `chars` | `boolean` | `true`  | Tampilkan jumlah karakter total.                                                           |
| `words` | `boolean` | `false` | Tunjukkan jumlah kata total. Nonaktif secara default — setel ke `true` untuk mengaktifkan. |

##### Contoh Penggunaan

```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. Mendapatkan, Mengatur, dan Mengirimkan Konten

Salah satu kekuatan inti **MarkdownEditor** adalah menjaga `<textarea>` tetap tersinkronisasi dengan sempurna. Baik Anda menggunakan kerangka kerja JavaScript modern atau backend tradisional seperti **Django, PHP, atau Laravel**, alur kerjanya tetap sederhana dan asli.

#### 1. Cara Asli (Disarankan)

Karena editor menyempurnakan area teks standar, Anda dapat menggunakan metode DOM yang sudah dikenal. Ini adalah cara tercepat untuk berinteraksi dengan data Anda tanpa mempelajari API baru.

```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. Menggunakan Referensi Variabel

Jika Anda memiliki referensi ke elemen textarea, Anda dapat menggunakannya secara langsung — tidak diperlukan API khusus perpustakaan.

```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. Menetapkan dan membaca konten

Cara yang disarankan untuk menyetel konten awal adalah langsung di HTML `<textarea>` — ini bekerja secara alami dengan setiap kerangka backend (Django, Laravel, Rails, PHP, dll.) dan editor merendernya secara otomatis di init.

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

Untuk membaca atau memperbarui konten saat runtime, gunakan nilai `textarea` asli. Panggil `editor.render()` setelah pembaruan untuk menyegarkan pratinjau dan lapisan hibrid.

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

// Read
const markdown = textarea.value;

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

#### 4. Menghancurkan editor — `destroy()`

Panggil `editor.destroy()` untuk menghapus pembungkus DOM editor dan mengembalikan `<textarea>` asli ke posisinya dalam dokumen. Berguna dalam aplikasi satu halaman saat melepas tampilan.

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

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

##### React terhadap perubahan dengan `onChange`

Teruskan callback `onChange` untuk mendapatkan notifikasi setiap ada perubahan konten. Menerima string penurunan harga saat ini.

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

###### Simpan otomatis draf dengan `localStorage`

Gunakan `onChange` untuk menyimpan draf pada setiap penekanan tombol. Pulihkan dengan mengisi terlebih dahulu area teks sebelum menginisialisasi editor.

```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. Pengiriman Formulir Otomatis yang Mudah

Karena **MarkdownEditor** dibangun langsung pada `<textarea>` asli, ia kompatibel dengan setiap kerangka backend (Django, Laravel, PHP, Ruby on Rails, dll.) langsung dari kotaknya.

Di sinilah filosofi “Pribumi-Pertama” bersinar. Anda tidak perlu menyinkronkan data secara manual sebelum mengirimkan formulir. Browser memperlakukan editor persis seperti kolom input standar.

```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>
```

**Catatan:** Inisialisasi plugin MarkdownEditor wajib dilakukan

Cukup gunakan `<form>` HTML standar. Atribut `name` pada textarea adalah apa yang akan digunakan server Anda untuk mengidentifikasi konten.

##### 🚀 Mengapa ini merupakan Game-Changer untuk Backend

Karena editor mempertahankan perilaku `<textarea>` asli, server Anda menangani data sebagai string standar. Tidak diperlukan logika tambahan—tidak ada `preventDefault()` dan tidak ada konstruksi `FormData` manual.

##### 💡 Mengapa ini disebut "Fitur Pembunuh":

Kebanyakan editor (seperti Quill, Editor.js, simpleMDE, easyMDE) menyimpan data dalam struktur JSON yang kompleks. Jika pengembang menggunakannya, mereka harus menulis ulang skema database dan logika renderingnya.

Dengan **MarkdownEditor**, pengembang dapat mengambil situs web lama, mengganti `<textarea>` biasa dengan editor Anda, dan **backend bahkan tidak mengetahui perubahannya**. Itu hanya menerima teks mentah yang sama seperti biasanya, tetapi pengguna mendapatkan pengalaman 10x lebih baik.

| Kerangka / Bahasa | Cara mengakses konten penurunan harga |
| ----------------- | ------------------------------------- |
| PHP               | $\_POST['content']                    |
| Django            | request.POST.get('content')           |
| Node.js (Express) | req.body.content                      |
| Laravel           | $request->input('content')            |
| Ruby on Rails     | params[:content]                      |

## Opsi Konfigurasi

### 🖼️ Unggah gambar tingkat lanjut

Menangani unggahan gambar secara asli—daripada mengandalkan string Base64 yang lambat dan banyak memori—merupakan kemenangan yang signifikan baik untuk performa maupun SEO.

#### Opsi Konfigurasi

Alat gambar mendukung konfigurasi `fileInput` untuk menangani unggahan server langsung.

- `accept`: Menentukan array format gambar yang diperbolehkan (misalnya, 'webp', 'avif')
- `uploadUrl`: Tentukan titik akhir backend tempat objek `File` akan dikirim melalui `POST`
- `params`: Objek opsional untuk mengirim data tambahan (seperti token CSRF, ID pengguna, atau nama folder) bersama file gambar

#### Contoh penggunaan (Konfigurasi Lengkap)

```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);
```

#### 📡 Detail Integrasi Server

##### 1. Permintaan

Editor mengirimkan permintaan `POST` sebagai `multipart/form-data`. Secara default, ini mencakup:

- `image_file`: Objek file sebenarnya
- `image_alt`: Teks alternatif yang dimasukkan oleh pengguna
- ...ditambah data khusus apa pun yang ditentukan dalam objek `params`

##### 2. Respon yang Diperlukan

Untuk mengonfirmasi keberhasilan upload dan menyisipkan gambar ke editor, server Anda **harus** mengembalikan struktur JSON berikut:

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

**Catatan**: Pastikan Anda menggunakan kunci `image_path` untuk URL gambar yang diunggah.

#### Validasi Teks Alt Gambar (`altInput`)

Untuk memastikan konten Anda tetap dapat diakses dan SEO-friendly, `MarkdownEditor` menerapkan validasi teks alternatif secara default. Anda dapat mengonfigurasi perilaku ini menggunakan singkatan boolean atau objek detail.

- **Perilaku Default**: Jika `altInput` tidak ditentukan, defaultnya adalah `{ diperlukan: true }`
- **Terapkan Aksesibilitas**: Pengguna akan dilarang menyisipkan gambar sampai deskripsi alternatif diberikan

##### Contoh Konfigurasi:

###### 1. Default (Tidak perlu konfigurasi)

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

###### 2. Singkatan (Nonaktifkan Validasi)

Jika Anda ingin mengizinkan gambar tanpa deskripsi, cukup setel boolean ke `false`.

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

###### 3. Berbasis objek (Eksplisit)

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

#### Penggunaan Gambar Standar (Tanpa `fileInput`):

Jika `fileInput` tidak dikonfigurasi, editor akan menggunakan modal sederhana berbasis URL secara default. Ini ideal jika sebagian besar pengguna Anda terhubung ke host gambar eksternal.

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

##### 💡 Mengapa menggunakan params?

Dalam kerangka kerja seperti Laravel atau Django, Anda tidak dapat mengunggah berkas tanpa token CSRF. Dengan menambahkan `_token` ke objek `params`, permintaan Anda akan melewati middleware keamanan backend dengan lancar, mempertahankan filosofi "Zero Logic" untuk pengontrol sisi server Anda.

### 🔀 Mode Pengeditan

**MarkdownEditor** menawarkan dua cara berbeda untuk menulis dan memformat konten Anda. Anda dapat beralih antara tampilan tradisional yang berfokus pada sintaksis atau pengalaman modern yang mengutamakan visual.

#### Konfigurasi

Anda dapat menentukan mode awal menggunakan properti `mode` selama inisialisasi.

- `plain` (**Default**): Lingkungan penurunan harga yang bersih dan berkinerja tinggi tempat sintaksis (seperti `**bold**` atau `# heading`) terlihat. Ideal untuk pengembang dan penganut penurunan harga
- `hibrida`: Pengalaman yang terinspirasi WYSIWYG yang menampilkan pemformatan (Tebal, Miring, Judul) secara real-time saat Anda mengetik, sambil tetap mempertahankan struktur penurunan harga yang mendasarinya.

##### Pelaksanaan:

```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'
});
```

##### Pratinjau Mode Hibrid dan Biasa:

###### Hybrid Mode

Pemformatan visual ditampilkan secara real-time saat Anda mengetik.

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

###### Mode Biasa (Default)

Berfokus pada sintaks penurunan harga mentah untuk pengalaman yang ringan.

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

| Konfigurasi                                                                                                                                                                                              | Properti / Alat                                                                                                                                                                                 | Keterangan                                                                                                                                                                                               |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Objek pilihan                                                                                                                                                                                            | `placeholder`                                                                                                                                                                                   | Menetapkan teks placeholder untuk textarea (opsional, karena Anda juga dapat menggunakan atribut textarea HTML standar)                                                                                  |
|                                                                                                                                                                                                          | `mode: 'hybrid'`                                                                                                                                                                                | Memungkinkan pengalaman yang terinspirasi WYSIWYG yang menampilkan pemformatan (tebal, miring, judul) secara real-time saat Anda mengetik                                                                |
| `toolbar`: Menentukan alat mana    yang muncul di toolbar dan urutannya.                                                                                                                                 | `heading`                                                                                                                                                                                       | Membuka tarik-turun untuk memilih tingkat judul H1–H6                                                                                                                                                    |
|                                                                                                                                                                                                          | `bold`                                                                                                                                                                                          | Mengaktifkan pemformatan teks tebal.                                                                                                                                                                     |
|                                                                                                                                                                                                          | `italic`                                                                                                                                                                                        | Mengaktifkan pemformatan teks miring.                                                                                                                                                                    |
|                                                                                                                                                                                                          | `strikethrough`                                                                                                                                                                                 | Memungkinkan teks dicoret.                                                                                                                                                                               |
|                                                                                                                                                                                                          | `ol`                                                                                                                                                                                            | (Daftar Terurut): Mengubah teks menjadi format daftar bernomor.                                                                                                                                          |
|                                                                                                                                                                                                          | `ul`                                                                                                                                                                                            | (Daftar Tidak Berurutan): Mengubah teks menjadi daftar poin-poin.                                                                                                                                        |
|                                                                                                                                                                                                          | `checklist`                                                                                                                                                                                     | Menambahkan kotak centang ke teks Anda, sehingga cocok untuk tugas, daftar tugas, atau melacak status penyelesaian.                                                                                      |
|                                                                                                                                                                                                          | `blockquote`                                                                                                                                                                                    | Sorot teks yang dikutip atau ditekankan.                                                                                                                                                                 |
|                                                                                                                                                                                                          | `code`                                                                                                                                                                                          | Membungkus teks yang dipilih dalam satu backtick untuk kode sebaris. Mengklik lagi akan menghapus backticks.                                                                                             |
|                                                                                                                                                                                                          | `codeblock`                                                                                                                                                                                     | Membungkus teks yang dipilih dalam blok kode berpagar triple-backtick. Mengklik lagi akan menghilangkan pagar.                                                                                           |
|                                                                                                                                                                                                          | `hr`                                                                                                                                                                                            | Menyisipkan aturan horizontal `---` pada posisi kursor pada barisnya sendiri.                                                                                                                            |
|                                                                                                                                                                                                          | `table`                                                                                                                                                                                         | Menyisipkan templat tabel penurunan harga 2x3 awal pada posisi kursor.                                                                                                                                   |
|                                                                                                                                                                                                          | `image`                                                                                                                                                                                         | Memungkinkan Anda menyisipkan gambar melalui sintaks penurunan harga.                                                                                                                                    |
|                                                                                                                                                                                                          | `link`                                                                                                                                                                                          | Memungkinkan Anda menambahkan hyperlink ke teks Anda.                                                                                                                                                    |
|                                                                                                                                                                                                          | `undo`                                                                                                                                                                                          | Untuk membalikkan perubahan terakhir.                                                                                                                                                                    |
| `redo`                                                                                                                                                                                                   | Untuk menerapkan kembali perubahan terakhir yang dibatalkan.                                                                                                                                    |
| `indent`                                                                                                                                                                                                 | Untuk meningkatkan tingkat lekukan.                                                                                                                                                             |
| `outdent`                                                                                                                                                                                                | Untuk mengurangi tingkat lekukan.                                                                                                                                                               |
| `preview`                                                                                                                                                                                                | Mengalihkan pratinjau layar penuh berdampingan. Kotak centang di panel pratinjau dapat diklik dan memperbarui sumber penurunan harga secara instan. Tekan Escape untuk keluar dari layar penuh. |
| [**Fitur pengunggahan gambar tingkat lanjut**](#advanced-image-upload):    Memungkinkan konfigurasi pengunggahan gambar ke     server Anda sendiri dan menyetel jalur     gambar melalui API. Ini meningkatkan kinerja dan SEO. | `fileInput`                                                                                                                                                                                     | `accept`: Kumpulan jenis file gambar yang diperbolehkan (misalnya `'webp'`, `'avif'`).   `uploadUrl`: Titik akhir backend tempat file dikirim melalui `POST`.   `params`: Objek opsional untuk data tambahan (token CSRF, ID pengguna, nama folder) |
|                                                                                                                                                                                                          | `altInput`                                                                                                                                                                                      | `wajib: salah`: Menonaktifkan validasi input teks alternatif (standarnya adalah benar)                                                                                                                   |

### ⌨️ Pintasan Papan Ketik

Tindakan pemformatan umum dapat dipicu langsung dari keyboard tanpa menyentuh toolbar. Setiap pintasan juga ditampilkan di tooltip tombol toolbar yang sesuai.

| Jalan pintas                 | Tindakan                                                |
| ---------------------------- | ------------------------------------------------------- |
| `Ctrl + B` / `⌘ B`           | Alihkan **Tebal**                                       |
| `Ctrl + I` / `⌘ I`           | Alihkan *Miring*                                        |
| `Ctrl + K` / `⌘ K`           | Sisipkan Tautan                                         |
| `` Ctrl + ` `` / `` ⌘ ` ``   | Alihkan `Kode` sebaris                                  |
| `Ctrl + Shift + S` / `⌘ ⇧ S` | Alihkan ~~Dicoret~~                                     |
| `Ctrl + Z` / `⌘ Z`           | Membuka                                                 |
| `Ctrl + Shift + Z` / `⌘ ⇧ Z` | Mengulangi                                              |
| `Tab`                        | Indentasi baris yang dipilih                            |
| `Shift + Tab`                | Garis yang dipilih keluar                               |
| `Ctrl + F` / `⌘ F`           | Buka panel Temukan                                      |
| `Ctrl + H` / `⌘ H`           | Buka panel Temukan & Ganti                              |
| `Escape`                     | Tutup panel Temukan / Keluar dari pratinjau layar penuh |

### 🔍 Temukan & Mengganti

Panel temukan dan ganti bawaan tersedia di dalam editor — tidak diperlukan ekstensi browser atau alat terpisah.

- Tekan `Ctrl + F` (atau `⌘ F`) untuk membuka panel **Temukan**
- Tekan `Ctrl + H` (atau `⌘ H`) untuk membuka **Temukan & Ganti** panel
- Penelusuran **tidak peka huruf besar-kecil** dan menampilkan penghitung pertandingan langsung (misalnya *3 dari 12*)
- Navigasikan kecocokan dengan tombol **▲ / ▼** atau `Enter` / `Shift + Enter`
- **Ganti** menggantikan kecocokan yang disorot saat ini; **Ganti Semua** menggantikan setiap kejadian sekaligus
- Tekan `Escape` untuk menutup panel dan mengembalikan fokus ke editor

Panel mengapung di sudut kanan atas area konten editor dan tidak mengganggu penulisan.

### 🌙 Tema

**MarkdownEditor** secara otomatis mewarisi temanya dari halaman sekitarnya — tidak diperlukan konfigurasi. Editor membaca `data-theme` dari leluhur terdekat saat inisialisasi, sehingga tetap sinkron dengan tema situs Anda.

#### Bagaimana tema diselesaikan (urutan prioritas)

1. Opsi **`tema`** — penggantian eksplisit yang diteruskan pada objek opsi
2. **`data-theme` pada `<textarea>`** — disetel langsung pada elemen
3. **`data-theme` pada leluhur mana pun** — mis. `<html>`, `<body>`, atau pembungkus `<div>`

#### Tema yang tersedia

`'light'` (default), `'dark'`, `'snowberry'`, `'darkberry'`

##### Opsi 1 — mewarisi dari `<html>` atau leluhur mana pun (konfigurasi nol)

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

##### Opsi 2 — atur `data-theme` langsung di `<textarea>`

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

##### Opsi 3 — opsi `tema` eksplisit (mengganti semuanya)

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

#### 🎨 Tema Khusus melalui Variabel CSS

Anda dapat sepenuhnya menyesuaikan tampilan editor dengan mengganti variabel CSS-nya pada elemen `.markdown-editor-wrapper` atau pemilih `[data-theme]` mana pun. Semua warna menggunakan [ruang warna OKLCH](https://oklch.com) untuk hasil yang seragam.

| Variabel             | Purpose                                | Standar ringan          | Defaultnya gelap        |
| -------------------- | -------------------------------------- | ----------------------- | ----------------------- |
| `--color-base`       | Latar belakang penyunting              | `oklch(100% 0 0)`       | `oklch(10.9% 0 0)`      |
| `--color-on-base`    | Warna teks utama                       | `oklch(22% 0 0)`        | `oklch(98% 0 0)`        |
| `--color-primary`    | Aksen utama (bilah alat aktif, tautan) | `oklch(51.1% .262 277)` | `oklch(66.4% .184 286)` |
| `--color-on-primary` | Teks pada permukaan berwarna primer    | `oklch(96.2% .018 272)` | `oklch(10% .01 270)`    |
| `--color-secondary`  | Aksen sekunder                         | `oklch(59.1% .293 323)` | `oklch(65% .18 220)`    |
| `--color-accent`     | Aksen highlight (kode sebaris, miring) | `oklch(54.1% .281 293)` | `oklch(75% .18 50)`     |
| `--color-neutral`    | Permukaan netral (batas, pemisah)      | `oklch(15% 0 0)`        | `oklch(85% 0 0)`        |
| `--color-error`      | Warna status kesalahan                 | `oklch(57.7% .245 27)`  | `oklch(60% .22 30)`     |
| `--border-radius`    | Pembulatan sudut bingkai editor        | `0.25rem`               | |

##### Contoh tema khusus

Ganti variabel apa pun di `.markdown-editor-wrapper` setelah editor menginisialisasi, atau tentukan blok `[data-theme]` khusus di stylesheet Anda:

```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' });
```

- **Pratinjau Waktu Nyata:** Lihat penurunan harga yang ditampilkan secara instan saat Anda mengetik.
- **Penyorotan Sintaks:** Peningkatan keterbacaan dengan kode yang jelas dan format penurunan harga.
- **Integrasi Mudah**: Integrasikan secara lancar ke dalam proyek web apa pun dengan penyiapan minimal.
- **Bilah Alat yang Dapat Disesuaikan:** Konfigurasikan dan susun ulang opsi bilah alat secara dinamis seperti huruf tebal, miring, dan lainnya.

## Fitur

## 🔌 Integrasi Formulir Asli

Bekerja persis seperti `<textarea>` standar. Tidak ada API yang rumit—cukup gunakan atribut `value` atau `name`. Ini "hanya berfungsi" dengan pengiriman formulir HTML standar di PHP, Django, atau Node.js.

## 🖼️ Unggah Gambar Tingkat Lanjut

Konfigurasikan unggahan server asli melalui API. Hindari string Base64 yang berat untuk memastikan pemuatan halaman lebih cepat dan SEO unggul dengan menghosting gambar di CDN Anda sendiri.

## 🔀 Hybrid & Plain Modes

Beralih antara pengalaman **Hybrid (WYSIWYG)** untuk pengeditan visual atau mode **Penurunan Harga Biasa** untuk nuansa pengkodean tradisional.

## 🚀 Performa Tinggi

Paket kecil **~116 KB** yang dioptimalkan untuk "Konten Berat". Menangani dokumen berukuran besar dan file besar tanpa jeda input atau penurunan kinerja.

## 🌍 Dukungan RTL bawaan

Dukungan asli untuk bahasa Kanan-Kiri seperti Arab, Urdu, dan Farsi. Sempurna untuk membangun aplikasi yang dapat diakses secara global.

## 🌙 Tema Adaptif

Termasuk dukungan Mode Gelap otomatis. Ini disinkronkan dengan pengaturan sistem Anda atau perpustakaan **Frutjam UI** untuk pengalaman visual yang lancar.

## 📝 Pengeditan Cerdas

Kelanjutan daftar otomatis gaya GitHub untuk daftar terurut, daftar tak terurut, dan daftar periksa — tekan `Enter` dan editor melanjutkan polanya. Kotak centang di panel pratinjau dapat diklik dan langsung disinkronkan kembali ke sumber penurunan harga.

## 📱 Sepenuhnya Responsif

UI yang lancar dan mengutamakan seluler yang beradaptasi sempurna dengan desktop, tablet, dan ponsel cerdas untuk mengedit saat bepergian.

## 📦 Dukungan Universal

Kompatibel dengan **ESM, UMD, CommonJS, dan IIFE**. Berfungsi langsung melalui CDN (`<script src>`), npm, atau bundler apa pun (Vite, webpack, Rollup) — tidak memerlukan konfigurasi tambahan.

## ♿ Accessible by Default

Dukungan penuh ARIA bawaan — landmark toolbar, wilayah pratinjau berlabel, tombol ramah pembaca layar, `aria-pressed` pada tombol pratinjau, `disabled` dan `aria-disabled` pada alat yang tidak aktif, dan memperbaiki pemulihan fokus ketika modals ditutup.

## 🛡️ Zero CSS Conflicts

Gaya editor sepenuhnya tercakup dalam `.markdown-editor-wrapper`. Pra-penerbangan global Tailwind dikecualikan sehingga editor dapat bekerja dengan aman bersama Bootstrap, Tailwind, atau kerangka kerja lainnya tanpa merusak gayanya.

## ⌨️ Pintasan Papan Ketik

`Ctrl+B`, `Ctrl+I`, `Ctrl+K`, `` Ctrl+` ``, `Ctrl+Shift+S` — tindakan pemformatan umum tanpa menyentuh mouse. Setiap pintasan ditampilkan di tooltip tombol toolbar.

## 🔍 Temukan & Mengganti

Tekan `Ctrl+F` untuk mencari atau `Ctrl+H` untuk membuka cari & mengganti. Pencarian tidak peka huruf besar-kecil dengan penghitung pencocokan langsung, navigasi berikutnya/sebelumnya, penggantian tunggal, dan penggantian semua — tanpa meninggalkan editor.

## Contoh Konfigurasi Lengkap

Gunakan contoh komprehensif ini untuk menginisialisasi **MarkdownEditor** dengan semua fitur utama, termasuk pengurutan toolbar khusus dan penanganan unggahan gambar tingkat lanjut.

```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();
```

