<!--
 * MD.Viewer — Documentation
 * Version: 2.11.3
-->

# MD.Viewer

[![PHP](https://img.shields.io/badge/PHP-8.3%2B-777BB4?logo=php&logoColor=white)](https://php.net)
[![md.php](https://img.shields.io/badge/md.php-v2.18.0-success)](https://github.com/paulmann/MD.Viewer)
[![updater.php](https://img.shields.io/badge/updater.php-v3.14.0-4f46e5)](https://github.com/paulmann/MD.Viewer)

<!-- md-i18n:switcher -->
**अन्य भाषाओं में:** [English](README.md) · [Deutsch](README.de.md) · [中文](README.zh.md) · हिन्दी · [Español](README.es.md) · [Русский](README.ru.md)

> एक सुरक्षित, स्वयं-होस्टेड PHP Markdown व्यूअर और ब्राउज़र, जो सादी `.md` फ़ाइलों को खोजने योग्य फ़ाइल ब्राउज़र, स्वतः बनने वाली विषय-सूची, शीर्षक क्रमांकन, Mermaid आरेख, शब्दावली टूलटिप, अनुकूलनीय पठन नियंत्रण, डार्क मोड, अंतर्निहित स्वयं-अपडेटर, फ़ाइल अपलोड, क्लिपबोर्ड पूर्वावलोकन और सर्वर-साइड कॉन्फ़िगरेशन के साथ परिष्कृत दस्तावेज़ीकरण पृष्ठों में बदल देता है। कोई बिल्ड चरण नहीं। कोई डेटाबेस नहीं। कोई फ़्रेमवर्क नहीं।

---

## परिचय

MD.Viewer **`md.php`** के इर्द-गिर्द बना है — एक ही PHP प्रवेश-बिंदु, जो मेल खाती Markdown फ़ाइल रेंडर करता है, और मेल खाती फ़ाइल न होने पर पुनरावर्ती Markdown फ़ाइल ब्राउज़र पर चला जाता है। परियोजना सरल परिनियोजन के लिए बनाई गई है: फ़ाइलें किसी PHP होस्ट पर कॉपी करें, अपने `.md` दस्तावेज़ पास ही रखें, और ब्राउज़र में `md.php` खोलें।

व्यूअर का ध्यान बिना टूलचेन वाले स्वयं-होस्टेड दस्तावेज़ीकरण पर है। यह पठनीय टाइपोग्राफ़ी, शीर्षक क्रमांकन, विषय-सूची का स्वतः निर्माण, स्वतः-मरम्मत और अंग्रेज़ी कंसोल निदान के साथ Mermaid रेंडरिंग, कोड और उद्धरण के लिए साझा प्रतिलिपि नियंत्रण, शब्दावली टूलटिप, डार्क मोड, मोबाइल-अनुकूल नियंत्रण, और एक सेटिंग्स पैनल समर्थित करता है जो व्यूअर की प्राथमिकताएँ स्थानीय रूप से संग्रहीत करता है।

**`updater.php`** एक सहयोगी स्वयं-अपडेट और रखरखाव इंजन है। यह ETag और SHA-256 का उपयोग करके GitHub की raw फ़ाइलें जाँचता है, बदलने से पहले संस्करण-बद्ध बैकअप बनाता है, सेटिंग्स पैनल से या सीधे URL के ज़रिए पिछले संस्करण बहाल करता है, वैकल्पिक `index.php` हार्ड लिंक का प्रबंधन करता है, `.md` फ़ाइल अपलोड संभालता है, और ब्राउज़र से सीधे अपडेट तथा रोलबैक समर्थित करता है।

सर्वर-साइड व्यवहार **`.md.ini`** कॉन्फ़िगरेशन फ़ाइल से नियंत्रित होता है, जो `md.php` के पास रखी जाती है। यह फ़ाइल फ़ीचर फ़्लैग कठोरता से लॉक करती है, जिन्हें ब्राउज़र या सेटिंग्स पैनल से बदला नहीं जा सकता।

---

## त्वरित शुरुआत

```bash
# Clone into your web root or any subdirectory
git clone https://github.com/paulmann/MD.Viewer.git /var/www/html/docs

# Create a Markdown file that matches the script name
echo "# Hello, World\n\nThis is my documentation." > /var/www/html/docs/md.md

# Open in a browser
# https://your-domain.com/docs/md.php
```

न्यूनतम सेटअप:

- **`md.php`** को (`js/md/` और `css/md/` फ़ोल्डरों के साथ) **`md.md`** के पास रखें।
- ब्राउज़र में **`md.php`** खोलें।
- स्क्रिप्ट स्वतः उसी आधार-नाम वाली Markdown फ़ाइल खोजती है।

वैकल्पिक:

- इन-ऐप अपडेट जाँच, अपडेट लागू करना, बैकअप, पुनर्स्थापना, फ़ाइल अपलोड और `index.php` लिंक प्रबंधन सक्षम करने के लिए **`updater.php`** जोड़ें।
- अपडेट सिस्टम सक्षम करने के लिए `.md.ini` में **`ALLOW_UPDATE = true`** सेट करें (डिफ़ॉल्ट रूप से बंद)।

### एक-फ़ाइल बूटस्ट्रैप इंस्टॉल

इंस्टॉल करने का सबसे तेज़ तरीका है कि सर्वर पर **केवल `updater.php`** अपलोड करें, फिर उसे ब्राउज़र में `?update=true` के साथ खोलें। यह:

1. `.md.ini` को सुरक्षित डिफ़ॉल्ट के साथ स्वतः बना देगा।
2. GitHub से सभी ट्रैक की गई फ़ाइलें (`md.php`, `js/md/*`, `css/md/*`, `README.md`, `LICENSE`) डाउनलोड करेगा।
3. सभी आवश्यक उप-निर्देशिकाएँ (`js/md/`, `css/md/`) न होने पर `0755` अनुमतियों के साथ बनाएगा।
4. स्वयं को अंतिम सूची में रखेगा और पूरा परिणाम पृष्ठ दिखाएगा।

**आवश्यकता:** ब्राउज़र मोड के लिए चलाने से पहले `.md.ini` में `ALLOW_UPDATE = true` सेट होना चाहिए (या इसे स्वतः बनी `.md.ini` में हाथ से जोड़ें और पुनः लोड करें)। मशीन API के लिए इसके बजाय `.md.ini` की `API_KEY` ही क्रेडेंशियल है — देखें [मशीन API](#4-मशीन-api-json-बाहरी-क्लाइंट-के-लिए)।

```bash
# Upload only updater.php, then:
curl -o updater.php https://raw.githubusercontent.com/paulmann/MD.Viewer/main/updater.php
# Edit .md.ini (auto-created on first hit) and set ALLOW_UPDATE = true
# Then open: https://your-domain.com/updater.php?update=true
#
# Or drive it over the machine API (no ALLOW_UPDATE needed, key from .md.ini):
curl "https://your-domain.com/updater.php?api_key=<key>&action=conflicts&format=json"
curl -X POST "https://your-domain.com/updater.php?api_key=<key>&action=install&format=json&dry_run=1"
```

---

## आवश्यकताएँ

- PHP **8.3+**।
- PHP एक्सटेंशन: `mbstring` और `curl` (अपडेटर और क्लिपबोर्ड के लिए आवश्यक)।
- एक वेब सर्वर, जैसे Apache, Nginx, Caddy या PHP का अंतर्निहित सर्वर।
- CDN एसेट (Mermaid, सिंटैक्स हाइलाइटिंग, Tailwind) के लिए ब्राउज़र-साइड इंटरनेट पहुँच, जब तक उन्हें स्थानीय प्रतियों से न बदला जाए।

---

## इंस्टॉलेशन

### Git क्लोन

```bash
git clone https://github.com/paulmann/MD.Viewer.git
cd MD.Viewer
```

### मैन्युअल इंस्टॉल

1. रिपॉज़िटरी या रिलीज़ आर्काइव डाउनलोड करें।
2. `md.php`, `updater.php`, `js/`, `css/` और वैकल्पिक रूप से `README.md` को अपनी लक्ष्य निर्देशिका में कॉपी करें।
3. अपनी Markdown फ़ाइलें उसी निर्देशिका वृक्ष में जोड़ें।
4. ब्राउज़र में `md.php` खोलें। पहली बार लोड होने पर `.md.ini` स्वतः बन जाती है।

### updater के ज़रिए एक-फ़ाइल इंस्टॉल

1. अपनी सर्वर निर्देशिका में केवल `updater.php` अपलोड करें।
2. `updater.php` को एक बार ब्राउज़र में खोलें — यह `ALLOW_UPDATE = false` के साथ `.md.ini` स्वतः बनाता है।
3. सर्वर पर `.md.ini` संपादित करें: `ALLOW_UPDATE = true` सेट करें।
4. `https://your-domain.com/updater.php?update=true` खोलें।
5. अपडेटर सभी फ़ाइलें डाउनलोड करता है, आवश्यकतानुसार `js/md/` और `css/md/` निर्देशिकाएँ बनाता है, और परिणाम पृष्ठ दिखाता है।
6. `md.php` खोलें — आपका MD.Viewer तैयार है।
7. वैकल्पिक: `ALLOW_UPDATE` को छुए बिना भी इंस्टॉल करें — `.md.ini` की
   `API_KEY` के साथ मशीन API कॉल करके (देखें [मशीन API](#4-मशीन-api-json-बाहरी-क्लाइंट-के-लिए))।

### PHP अंतर्निहित सर्वर

```bash
cd /path/to/MD.Viewer
php -S localhost:8080
# then open http://localhost:8080/md.php
```

### Nginx उदाहरण

```nginx
server {
    listen 80;
    server_name docs.example.com;
    root /var/www/html/docs;
    index index.php md.php;

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}
```

---

## फ़ाइल संरचना

```text
MD.Viewer/
├── md.php                     # Main viewer / browser script       v2.18.0
├── updater.php                # Self-updater, backup, upload        v3.14.0
├── .md.ini                    # Server-side config (auto-created)
├── md.md                      # Default Markdown file for md.php
├── uploads.md/                # Created automatically for uploaded/saved files
├── md.backup/                 # Created automatically when updates/restores run
│   ├── 2.8.2/
│   ├── 2.8.2-pre-restore/     # Auto-created before each rollback
│   └── .state/                # ETag + SHA-256 per-file cache
├── css/
│   └── md/
│       ├── md.css             # v2.2.2
│       ├── settings.css       # v2.9.0
│       └── tooltips.css       # v2.4.5
├── js/
│   └── md/
│       ├── md.js              # v2.7.0
│       ├── settings.js        # v2.11.0
│       ├── tooltips.js        # v2.4.5
│       └── upload.js          # v2.9.1
├── LICENSE                    # v1.0.0
└── README.md                  # v2.11.3
```

नामकरण का मुख्य नियम: `md.php` `md.md` खोजता है, `docs.php` `docs.md` खोजता है। कई स्वतंत्र व्यूअर इंस्टेंस बिना अतिरिक्त रूटिंग के एक ही निर्देशिका वृक्ष साझा कर सकते हैं।

---

## सर्वर-साइड कॉन्फ़िगरेशन — `.md.ini`

पहले अनुरोध पर `md.php` (या `updater.php`) उसी निर्देशिका में `.md.ini` बनाता है। यह फ़ाइल फ़ीचर फ़्लैग कठोरता से लॉक करती है, ताकि वे ब्राउज़र या सेटिंग्स पैनल से **नहीं** बदले जा सकें। अनुपलब्ध कुंजियाँ हर लोड पर सुरक्षित डिफ़ॉल्ट के साथ स्वतः जोड़ दी जाती हैं।

```ini
; MD.Viewer server-side configuration

; Disable the "Upload .md" button in the file browser (recommended: true)
DISABLE_UPLOAD    = true

; Disable the "Preview Clipboard" button in the file browser
DISABLE_CLIPBOARD = false

; Disable the "Save to File" button in clipboard preview
DISABLE_SAVE_CLIPBOARD_TO_FILE = true

; Allow updating files via updater.php?update=true or the Settings panel
ALLOW_UPDATE = false

; Allow restoring a backup via updater.php?restore=latest or ?restore=[version]
ALLOW_RESTORE = false

; Allow creating/removing the index.php hard link from the Settings panel
ALLOW_CREATE_INDEX_PHP_LINK = true

; Machine API key used by external clients (a control panel, an installer)
; Generated automatically on first run. Send it as ?api_key=<value>.
API_KEY = mdv_your_generated_key_here
```

| कुंजी | डिफ़ॉल्ट | प्रभाव |
|---|---|---|
| `DISABLE_UPLOAD` | `true` | Upload बटन छिपाता और अक्षम करता है |
| `DISABLE_CLIPBOARD` | `false` | Clipboard Preview बटन छिपाता और अक्षम करता है |
| `API_KEY` | auto-generated | Secret key for the machine API (`?api_key=`); required for JSON installs |
| `DISABLE_SAVE_CLIPBOARD_TO_FILE` | `true` | क्लिपबोर्ड पूर्वावलोकन में Save to File छिपाता है |
| `ALLOW_UPDATE` | `false` | अपडेट सिस्टम सक्षम करता है (सेटिंग्स पैनल + `?update=true`) |
| `ALLOW_RESTORE` | `false` | Backup & Restore अनुभाग और `?restore=` URL मोड सक्षम करता है |
| `ALLOW_CREATE_INDEX_PHP_LINK` | `true` | सेटिंग्स पैनल में Index File अनुभाग सक्षम करता है |

**सभी कुंजियाँ स्वतः जोड़ी जाती हैं** (अपने डिफ़ॉल्ट मानों के साथ) मौजूदा `.md.ini` फ़ाइलों में अगली बार लोड होने पर — अपग्रेड के लिए कभी `.md.ini` में हाथ से बदलाव ज़रूरी नहीं।

### प्रति-निर्देशिका `.md.ini` (v2.12.0 / v3.12.0)

`md.php` के पास की `.md.ini` अपनी इंस्टॉलेशन-स्तरीय फ़ाइल की भूमिका रखती है
(`BROWSE_DIR`, `API_KEY`, `ALLOW_*`, और `DISABLE_*` फ़्लैग के डिफ़ॉल्ट)।
इसके ऊपर, **ब्राउज़ रूट के भीतर कोई भी निर्देशिका अपनी `.md.ini` रख सकती है**।
किसी दस्तावेज़ पर लागू होने वाली सेटिंग्स बेस फ़ाइल हैं, जिनके ऊपर ब्राउज़ रूट से
लेकर दस्तावेज़ की निर्देशिका तक रास्ते में मिली हर `.md.ini` लागू होती है - गहराई में
पड़ी फ़ाइल हमेशा ऊपर वाली को हरा देती है:

```text
ext/mdviewer/.md.ini     base file: installation-level keys, API key
share/.md.ini            applies to everything below share/
share/sub/.md.ini        applies to share/sub/ and below, and wins
```

निर्देशिका फ़ाइल में केवल ये तीन कुंजियाँ मानी जाती हैं:

| कुंजी | डिफ़ॉल्ट | उस निर्देशिका में प्रभाव |
|---|---|---|
| `DISABLE_UPLOAD` | `true` | **Upload .md** की अनुमति है या नहीं और अपलोड कहाँ जाता है |
| `DISABLE_CLIPBOARD` | `false` | Clipboard Preview बटन सक्रिय है या नहीं |
| `DISABLE_SAVE_CLIPBOARD_TO_FILE` | `true` | **Save to File** की अनुमति है या नहीं और फ़ाइल कहाँ जाती है |

नियम:

- कुंजियाँ `BROWSE_DIR`, `API_KEY`, `ALLOW_UPDATE`, `ALLOW_RESTORE` और
  `ALLOW_CREATE_INDEX_PHP_LINK` इंस्टॉलेशन-स्तरीय हैं। निर्देशिका फ़ाइल में वे अनदेखी
  की जाती हैं, और PHP त्रुटि लॉग में एक टिप्पणी लिखी जाती है।
- मान उतनी ही कड़ाई से पढ़े जाते हैं जितनी बेस फ़ाइल में (ट्रिम किए जाते हैं, वैकल्पिक
  जोड़ीदार उद्धरण हटाए जाते हैं, null बाइट, नई पंक्ति या 255 बाइट से अधिक वाला मान
  छोड़ दिया जाता है)। अपठनीय फ़ाइल या अनुपयोगी मान त्रुटि-लॉग प्रविष्टि के साथ छोड़ा
  जाता है - इससे पृष्ठ कभी नहीं टूटता और कभी विशेषाधिकार-प्राप्त डिफ़ॉल्ट पर वापस नहीं जाता।
- निर्देशिका की `.md.ini` तभी पढ़ी जाती है जब उसका वास्तविक पथ ब्राउज़ रूट के भीतर हो,
  इसलिए `..` के साथ बाहर निकलने की कोशिश करने वाला पथ अस्वीकार हो जाता है।
- व्यूअर मोड (`?file=sub/doc.md`) में दस्तावेज़ की निर्देशिका लागू होती है; फ़ाइल
  ब्राउज़र मोड में ब्राउज़ रूट लागू होता है।
- **Upload .md** और **Save to File** उसी निर्देशिका में लिखते हैं जिसे देखा जा रहा है।
  अनुरोध इसे `dir` पैरामीटर में भेजता है (ब्राउज़ रूट के सापेक्ष); ट्रैवर्सल खंडों वाले,
  निरपेक्ष पथ वाले या null बाइट वाले `dir` मान का उत्तर `400` से दिया जाता है, और
  किसी अन्य तरीके से भी निर्देशिका से बाहर नहीं जाया जा सकता।
- निर्देशिका की `.md.ini` फ़ाइलें वैकल्पिक हैं। उनके बिना व्यवहार पहले जैसा ही है।

---

## उपयोग के तरीके

### व्यूअर मोड

जब मेल खाती Markdown फ़ाइल मौजूद हो, तो MD.Viewer उसे एक स्टाइल वाले दस्तावेज़ पृष्ठ के रूप में रेंडर करता है। यह पहले शीर्ष-स्तरीय शीर्षक से पृष्ठ का शीर्षक निकालता है, पहले अनुच्छेद से विवरण बनाता है, विषय-सूची रेंडर करता है, और व्यूअर की स्थानीय प्राथमिकताएँ लागू करता है।

### फ़ाइल ब्राउज़र मोड

जब मेल खाती Markdown फ़ाइल न हो, तो MD.Viewer पुनरावर्ती Markdown ब्राउज़र पर चला जाता है। ब्राउज़र वर्तमान निर्देशिका वृक्ष को स्कैन करता है, Markdown फ़ाइलें खोजने योग्य तालिका में सूचीबद्ध करता है, और चुनी गई फ़ाइल को `?file=relative/path.md` के साथ खोलता है।

### क्लिपबोर्ड पूर्वावलोकन मोड

फ़ाइल ब्राउज़र में **Clipboard Preview** बटन होता है। टेक्स्टएरिया में कोई भी Markdown टेक्स्ट चिपकाएँ और Preview पर क्लिक करें — सामग्री व्यूअर मोड में रेंडर हो जाती है। यदि `DISABLE_SAVE_CLIPBOARD_TO_FILE = false` है, तो फ़ाइलनाम इनपुट और Save बटन वाली **Save to File** पट्टी दिखाई देती है, जो `BROWSE_DIR` में लिखती है (वह कुंजी सेट न होने पर `md.php` के पास की `uploads.md/` में)।

### URL पैरामीटर

| पैरामीटर | अर्थ | उदाहरण |
|---|---|---|
| `?file=path/to/doc.md` | विशिष्ट Markdown फ़ाइल रेंडर करें | `md.php?file=docs/api.md` |

---

## विशेषताएँ

### Markdown रेंडरिंग

- शीर्षक, अनुच्छेद, ज़ोर, मोटा टेक्स्ट, इनलाइन कोड, ब्लॉककोट, क्षैतिज रेखाएँ, फ़ेंस किए गए कोड ब्लॉक।
- Mermaid स्वतः-मरम्मत PlantUML-शैली के क्लास नोट्स, शाब्दिक `\n`, बिना बंद किया गया `<br>`, PlantUML `return` और बिना उद्धरण वाले कोष्ठकों को बदल देती है; निदान केवल अंग्रेज़ी में हैं।
- क्रमबद्ध और अक्रमबद्ध सूचियाँ, नेस्टेड सूचियों सहित।
- स्टाइल वाले आउटपुट के साथ तालिकाएँ।
- कार्य सूचियाँ, फ़ुटनोट, संदर्भ-शैली लिंक।
- चित्र और मानक लिंक।
- इमोजी शॉर्टकोड समर्थन।
- परिष्कृत दस्तावेज़ स्वरूपण के लिए सार्वभौमिक इनलाइन पैटर्न।

### नेविगेशन और संरचना

- स्वतः बनने वाली विषय-सूची।
- वैकल्पिक स्वचालित शीर्षक क्रमांकन।
- स्वच्छ हीरो शीर्षकों के लिए कोलन पर शीर्षक का विभाजन।
- खोजने योग्य फ़ाइल ब्राउज़र मोड।
- पढ़े जा रहे दस्तावेज़ की स्रोत `.md` की प्रतिलिपि और डाउनलोड, तथा
  ब्राउज़र की हर फ़ाइल की भी (दोनों टॉगल डिफ़ॉल्ट रूप से चालू)।

### कोड, आरेख और समृद्ध सामग्री

- प्रतिलिपि बटन सहित सिंटैक्स-हाइलाइट किए गए फ़ेंस कोड ब्लॉक।
- Mermaid आरेख।
- सुपरस्क्रिप्ट/सबस्क्रिप्ट समर्थन।

### शब्दावली टूलटिप

`js/md/tooltips.js` और `css/md/tooltips.css` के ज़रिए इनलाइन शब्दावली टूलटिप। शब्दों पर हॉवर करने से टूलटिप दिखता है; इंजन अलग-अलग वर्तनी रूपों को एक ही परिभाषा तक ले जाता है। इसमें टच समर्थन, 2 सेकंड की छूट अवधि और व्यूपोर्ट के अनुसार स्मार्ट स्थिति निर्धारण शामिल है।

### सेटिंग्स पैनल — फ़ीचर टूलटिप

सेटिंग्स पैनल के **Features** अनुभाग में हर टॉगल एक विस्तृत हॉवर टूलटिप दिखाता है, जो बताता है कि फ़ीचर क्या करता है और रेंडरिंग को कैसे प्रभावित करता है। टूलटिप वही `.g-tooltip` बुलबुला और स्थिति-निर्धारण इंजन उपयोग करते हैं जो शब्दावली टूलटिप करते हैं — वही दृश्य शैली, वही डार्क-मोड समर्थन, वही टच/कीबोर्ड व्यवहार। कोई अतिरिक्त CSS आवश्यक नहीं।

### रूप और पठन नियंत्रण

- डार्क मोड।
- समायोज्य पृष्ठ चौड़ाई, फ़ॉन्ट आकार और पंक्ति ऊँचाई।
- हेडर टूलबार की दृश्यता सेटिंग्स (डेस्कटॉप/मोबाइल के लिए अलग-अलग)।
- ब्राउज़र कुकीज़ में संग्रहीत टिकाऊ प्राथमिकताएँ।

### फ़ाइल अपलोड

यदि `.md.ini` में `DISABLE_UPLOAD = false` है, तो फ़ाइल ब्राउज़र **Upload .md** बटन दिखाता है। अपलोड की गई फ़ाइलें `BROWSE_DIR` में सहेजी जाती हैं, या वह कुंजी सेट न होने पर `md.php` के पास की `uploads.md/` में। प्रति-निर्देशिका सेटिंग्स के साथ फ़ाइल इसके बजाय उसी निर्देशिका में जाती है जिसे देखा जा रहा है, और वह निर्देशिका की अपनी `.md.ini` तय करती है कि क्रिया की अनुमति है या नहीं। अपलोड सुरक्षा तीन स्तरों की है:

1. `DISABLE_UPLOAD = true` होने पर UI बटन छिपा/अक्षम।
2. `MDV_CONFIG.disableUpload` true होने पर JS क्लिक हैंडलर शीघ्र लौट जाता है।
3. PHP `updater.php`: `.md.ini` स्वतंत्र रूप से पढ़ता है, अक्षम होने पर `403` लौटाता है।

### स्रोत `.md` की प्रतिलिपि और डाउनलोड

तीन वैकल्पिक नियंत्रण पाठक को रेंडर किए गए पृष्ठ के बजाय स्वयं फ़ाइल देते हैं।
तीनों डिफ़ॉल्ट रूप से चालू हैं, तीनों PHP-साइड फ़ीचर टॉगल हैं (सेटिंग्स पैनल और
`.md.ini` कुंजी तालिका देखें), और दोनों **एक्सेस नियंत्रण नहीं, इंटरफ़ेस गेट** हैं: एक ही
फ़ाइल का अनुरोध किसी भी स्थिति में उत्तर-योग्य रहता है, क्योंकि व्यूअर में कोई
प्रमाणीकरण नहीं है और कभी था भी नहीं।

| टॉगल | कहाँ दिखता है | डिफ़ॉल्ट |
|---|---|---|
| `FEATURE_DOWNLOAD_ICON` | फ़ाइल ब्राउज़र की हर पंक्ति पर डाउनलोड आइकन | on |
| `FEATURE_DOWNLOAD_BUTTON` | दस्तावेज़ टूलबार में डाउनलोड बटन, चौड़ाई और फ़ॉन्ट नियंत्रणों के बाएँ | on |
| `FEATURE_COPY_BUTTON` | दस्तावेज़ टूलबार में प्रतिलिपि बटन, डाउनलोड बटन के बाएँ | on |

तीनों एक ही अनुरोध तक ले जाते हैं - `md.php?file=<relative path>&download=1` - और वह
अनुरोध ठीक उसी फ़ंक्शन से सत्यापित होता है जिससे व्यूअर पृष्ठ, इसलिए जो पथ दिखाने के
लिए अस्वीकार है वह यहाँ भी अस्वीकार है। उत्तर फ़ाइल के बाइट्स
`application/octet-stream` के रूप में `X-Content-Type-Options: nosniff` और
`Content-Disposition: attachment` के साथ भेजता है: `.md` टेक्स्ट है, और दस्तावेज़ के
लेखक द्वारा चुने गए फ़ाइलनाम के नीचे उसे टेक्स्ट घोषित करना ही उसे ब्राउज़र की नज़र में
स्क्रिप्ट बना देगा। प्रतिलिपि बटन क्लिक पर दस्तावेज़ को पृष्ठ में दूसरी बार जोड़ने के
बजाय वही अनुरोध करता है, और फिर परिणाम क्लिपबोर्ड पर रखता है। क्लिपबोर्ड
पूर्वावलोकन के पीछे कोई फ़ाइल नहीं होती, इसलिए उसे टूलबार के ये दोनों नियंत्रण नहीं मिलते।

### क्लिपबोर्ड पूर्वावलोकन और Save to File
Markdown टेक्स्ट चिपकाएँ और बिना फ़ाइल सहेजे उसे तुरंत रेंडर करें। यदि `DISABLE_SAVE_CLIPBOARD_TO_FILE = false` है, तो फ़ाइलनाम इनपुट, Save बटन और इनलाइन स्थिति प्रतिक्रिया वाली Save to File पट्टी दिखाई देती है। पूर्वावलोकन खुलने पर स्रोत टेक्स्ट `sessionStorage` में संग्रहीत होता है। फ़ाइल उसी निर्देशिका में लिखी जाती है जिसे देखा जा रहा है, और प्रति-निर्देशिका `.md.ini` वहाँ सहेजना अक्षम या पुनः सक्षम कर सकती है।

### सेटिंग्स पैनल

सेटिंग्स पैनल (हेडर के गियर आइकन से खुलता है) में शामिल हैं:

- व्यूअर प्राथमिकता टॉगल (थीम, चौड़ाई, फ़ॉन्ट, पंक्ति ऊँचाई, विषय-सूची, क्रमांकन)।
- प्रत्येक डिवाइस प्रकार के लिए हेडर टूलबार दृश्यता नियंत्रण।
- वर्णनात्मक हॉवर टूलटिप के साथ PHP-साइड फ़ीचर टॉगल (अगली बार पृष्ठ लोड होने पर कुकीज़ के ज़रिए लागू)।
- अपडेट जाँच और लागू करने के नियंत्रण (केवल तभी दिखते हैं जब `ALLOW_UPDATE = true`)।
- बैकअप सूची और पुनर्स्थापना नियंत्रण (केवल तभी दिखते हैं जब `ALLOW_RESTORE = true`)।
- `index.php` हार्ड-लिंक प्रबंधन (केवल तभी दिखता है जब `ALLOW_CREATE_INDEX_PHP_LINK = true`)।

---

## अंतर्निहित अपडेटर

`updater.php` फ़ाइलों को अपडेट और प्रबंधित करने के चार तरीके देता है।

### अपडेटर का लैंडिंग पेज

`updater.php` को बिना किसी पैरामीटर के खोलने पर एक **सूचना पृष्ठ** दिखता है, जिसमें:

- हर ट्रैक की गई फ़ाइल के लिए संस्करण संख्याओं के साथ मौजूद / अनुपलब्ध स्थिति।
- हर फ़ाइलनाम GitHub पर raw फ़ाइल से लिंक है (नए टैब में खुलता है)।
- `.md.ini` स्थिति: `ALLOW_UPDATE = false` होने पर संपादन निर्देशों के साथ चेतावनी दिखाती है।
- क्रिया बटन (**Check & Apply Updates**, **↺ Force Reinstall All**, **⟲ Restore Latest Backup**) — केवल तभी दिखते हैं जब `.md.ini` में संबंधित फ़्लैग सक्षम हो।

### 1. सेटिंग्स पैनल (AJAX)

- सभी ट्रैक की गई फ़ाइलों में अपडेट जाँचें, प्रति-फ़ाइल संस्करण प्रदर्शन के साथ।
- अपडेट लागू करें — वर्तमान फ़ाइलों का बैकअप लेता है, नए संस्करण डाउनलोड करता है।
- संस्करण की परवाह किए बिना सभी फ़ाइलें बलपूर्वक पुनः इंस्टॉल करें (ETag/SHA-256 कैश को बायपास करता है)।
- बैकअप संस्करण देखें और बहाल करें (केवल तभी दिखता है जब `ALLOW_RESTORE = true`)।

### 2. सीधा अपडेट (`?update=true`)

`.md.ini` में `ALLOW_UPDATE = true` आवश्यक है।

```
https://your-domain.com/updater.php?update=true
https://your-domain.com/updater.php?update=true&force=true   ← force reinstall
```

**दो-चरणीय बूटस्ट्रैप:**

- **चरण 1** — सबसे पहले `updater.php` को स्वयं अपडेट करता है। फ़ाइल बदली हो तो `?_phase=2` पर रीडायरेक्ट करता है (`&force=true` सेट हो तो सुरक्षित रखा जाता है), ताकि बाकी काम **नया** अपडेटर करे।
- **चरण 2** — बाकी सभी ट्रैक की गई फ़ाइलें संस्करण-अंतर प्रदर्शन के साथ अपडेट करता है।

**`&force=true` फ़्लैग:**

- ETag और SHA-256 कैश पूरी तरह छोड़ देता है — हर फ़ाइल हमेशा GitHub से डाउनलोड होती है।
- असफल आंशिक अपडेट के बाद या स्थानीय फ़ाइलों के खराब होने पर उपयोगी।
- रीडायरेक्ट URL के ज़रिए चरण 1 से चरण 2 तक स्वतः प्रसारित होता है।
- परिणाम पृष्ठ के फ़ुटर में एम्बर रंग का **↺ Force reinstall all** बटन दिखता है।

**निर्देशिकाओं का स्वतः निर्माण:**

अपडेटर फ़ाइलें लिखने से पहले कोई भी अनुपलब्ध उप-निर्देशिका (`js/md/`, `css/md/`) `0755` अनुमतियों के साथ बना देता है। इसी से `?update=true` और मशीन API एक-फ़ाइल इंस्टॉल से काम करते हैं।

**परिणाम पृष्ठ:**

- प्रति-फ़ाइल स्थिति बैज: `updated` / `force-updated` / `created` / `current` / `error`
- संस्करण अंतर: `2.7.0 → 2.8.2`
- हर फ़ाइलनाम GitHub पर raw फ़ाइल का क्लिक करने योग्य लिंक है (नए टैब में खुलता है)
- फ़ुटर: **↺ Force reinstall all** बटन + **← Back** बटन

### 3. सीधा रोलबैक (`?restore=`)

`.md.ini` में `ALLOW_RESTORE = true` आवश्यक है।

```
https://your-domain.com/updater.php?restore=latest
https://your-domain.com/updater.php?restore=2.7.0
```

| URL | व्यवहार |
|---|---|
| `?restore=latest` | `md.backup/` स्कैन करता है, सबसे ऊँचा semver संस्करण स्वतः चुनता है |
| `?restore=X.Y.Z` | ठीक उसी नाम वाला बैकअप संस्करण बहाल करता है |

**रोलबैक प्रक्रिया:**

1. `ALLOW_RESTORE = true` सत्यापित करता है — false होने पर त्रुटि पृष्ठ दिखाता है।
2. `?restore=latest` `md.backup/` स्कैन करता है, semver से क्रमबद्ध करता है, नवीनतम चुनता है।
3. संस्करण स्ट्रिंग सत्यापित करता है (केवल अक्षरांकीय, बिंदु, डैश)।
4. अधिलेखन से पहले वर्तमान फ़ाइलों का बैकअप `md.backup/[version]-pre-restore/` में लेता है — रोलबैक का रोलबैक हमेशा संभव है।
5. प्रति-फ़ाइल ETag कैश (`.state/`) अमान्य करता है, ताकि अगली अपडेट जाँच पूरा फ़ेच करे।
6. `restored` / `skipped` / `error` बैज, संस्करण अंतर और क्लिक करने योग्य फ़ाइल लिंक वाला HTML परिणाम पृष्ठ देता है।

### 4. मशीन API (JSON, बाहरी क्लाइंट के लिए)

कुंजी-संरक्षित JSON API किसी अन्य प्रोग्राम (कंट्रोल पैनल, इंस्टॉलर) को इंस्टॉल चलाने और
उसकी प्रगति चरण-दर-चरण रेंडर करने देता है। यह कभी HTML नहीं लौटाता, और PHP notices या warnings कभी
रिस्पॉन्स बॉडी में नहीं लीक होते।

`API_KEY` पहले अनुरोध पर बनती है और `.md.ini` में संग्रहीत होती है। यदि आप बाद में फ़ाइल पढ़े
बिना बूटस्ट्रैप को स्वचालित करना चाहते हैं, तो डिप्लॉय करने से पहले वहाँ अपना मान सेट करें।

```
GET  updater.php?api_key=<key>&action=status&format=json
GET  updater.php?api_key=<key>&action=conflicts&format=json
POST updater.php?api_key=<key>&action=install&format=json[&dry_run=1][&force=1]
```

| क्रिया | विधि | उद्देश्य |
|---|---|---|
| `status` | GET | प्रति फ़ाइल स्थानीय इंस्टॉल स्थिति (मौजूद / अनुपलब्ध / संस्करण)। कोई नेटवर्क I/O नहीं। |
| `conflicts` | GET | ड्राई-रन टकराव रिपोर्ट। कभी कुछ नहीं लिखता। |
| `install` | POST | पूर्व-लेखन टकराव गार्ड के ज़रिए फ़ाइलें फ़ेच कर लिखता है। |

रिस्पॉन्स लिफ़ाफ़ा:

```json
{
  "ok": true,
  "action": "install",
  "version": "2.16.0",
  "steps":     [{"step": "install:css/md/md.css", "status": "ok", "ms": 143, "detail": "2.2.2 → 2.2.2"}],
  "files":     [{"path": "css/md/md.css", "status": "current", "version": "2.2.2"}],
  "conflicts": [{"path": "css/md/settings.css", "status": "same_name_elsewhere",
                 "existing_path": "legacy/components/settings.css", "size_bytes": 1932}],
  "errors":    []
}
```

- `conflicts[].status`: `ok` | `exists` (ठीक वही पथ पहले से है) | `same_name_elsewhere`
- `files[].status`: `present` | `missing` | `updated` | `created` | `force-updated` | `current` | `skipped` | `would_write` | `error`
- `steps[].status`: `ok` | `dry-run` | `skip` | `warn` | `error`
- HTTP कोड: `200` पूरा हुआ (प्रति-फ़ाइल विफलताएँ बॉडी में `"ok": false` रखती हैं), `400` अज्ञात क्रिया, `403` `unauthorized` या `api_key_unavailable`, `405` POST के बिना `install`

`dry_run=1` टकराव रिपोर्ट के बाद रुक जाता है: कुछ फ़ेच नहीं होता और कुछ लिखा नहीं जाता।
`force=1` ट्रैक किए गए पथ पर पड़ी उस फ़ाइल को भी बदल देता है जो MD.Viewer फ़ाइल नहीं दिखती
(अन्यथा वह छोड़ दी जाती है और `conflicts` में रिपोर्ट होती है) और ETag/SHA-256 कैश को बायपास करता है।
ऊपर के ब्राउज़र मोड `ALLOW_UPDATE` / `ALLOW_RESTORE` का पालन करते रहते हैं; API कुंजी JSON क्रियाओं
के लिए अपना क्रेडेंशियल है।
---

## ट्रैक की गई फ़ाइलें

अपडेटर द्वारा प्रबंधित सभी फ़ाइलें (जाँची, डाउनलोड की, बैकअप ली गई):

```
md.php                    v2.18.0
updater.php               v3.14.0
js/md/md.js               v2.7.0
js/md/settings.js         v2.11.0
js/md/tooltips.js         v2.4.5
js/md/upload.js           v2.9.1
css/md/md.css             v2.2.2
css/md/settings.css       v2.9.0
css/md/tooltips.css       v2.4.5
README.md                 v2.11.3
LICENSE                   v1.0.0
```

---

## बैकअप और पुनर्स्थापना

हर फ़ाइल बदलने से पहले (अपडेट या बलपूर्वक पुनः इंस्टॉल), वर्तमान स्थानीय फ़ाइल `md.backup/[version]/` में कॉपी की जाती है। सेटिंग्स पैनल बैकअप संस्करण, तारीख़ें और फ़ाइल संख्याएँ सूचीबद्ध करता है (केवल तभी दिखता है जब `ALLOW_RESTORE = true`)। पुनर्स्थापना पहले वर्तमान संस्करण का बैकअप लेती है (`[version]-pre-restore` के रूप में), फिर चुनी गई फ़ाइलें वापस लिखती है। `README.md` और `LICENSE` डाउनलोड और अपडेट होते हैं पर उनका बैकअप **नहीं** लिया जाता (वे दस्तावेज़ हैं, कोड नहीं)।

---

## सुरक्षा

सभी सुरक्षा-संवेदी क्रियाएँ **तीन स्वतंत्र स्तरों** पर संरक्षित हैं: सर्वर `.md.ini` फ़्लैग, PHP API गार्ड और क्लाइंट-साइड UI दमन।

| फ़ीचर | `.md.ini` फ़्लैग | PHP गार्ड | UI दमन |
|---|---|---|---|
| अपडेट (लागू/जाँच) | `ALLOW_UPDATE` | `requireAllowUpdate()` | false होने पर छिपा |
| बैकअप बहाल करना | `ALLOW_RESTORE` | `requireAllowRestore()` | false होने पर छिपा |
| बैकअप सूचीबद्ध करना | `ALLOW_RESTORE` | `requireAllowRestore()` | false होने पर कॉल नहीं होता |
| index लिंक बनाना | `ALLOW_CREATE_INDEX_PHP_LINK` | `requireAllowIndexLink()` | false होने पर छिपा |
| index लिंक हटाना | `ALLOW_CREATE_INDEX_PHP_LINK` | `requireAllowIndexLink()` | false होने पर छिपा |
| Index स्थिति | `ALLOW_CREATE_INDEX_PHP_LINK` | `{disabled:true}` लौटाता है | false होने पर कॉल नहीं होता |
| फ़ाइल अपलोड | `DISABLE_UPLOAD` | true होने पर 403 | बटन छिपा |
| क्लिपबोर्ड सहेजना | `DISABLE_SAVE_CLIPBOARD_TO_FILE` | true होने पर 403 | बटन छिपा |
| मशीन API (`status`, `conflicts`, `install`) | `API_KEY` | 403 `unauthorized` | कुंजी के बिना कॉल नहीं हो सकता |

अतिरिक्त सुरक्षा:

- **`.md.ini`** ब्राउज़र को कभी नहीं दी जाती — यह केवल सर्वर-साइड पढ़ी जाती है।
- **प्रति-निर्देशिका `.md.ini`** फ़ाइलें केवल ब्राउज़ रूट के भीतर की निर्देशिकाओं से पढ़ी जाती हैं, और उनसे केवल `DISABLE_*` कुंजियाँ ली जाती हैं; इंस्टॉलेशन-स्तरीय कुंजियाँ अनदेखी और लॉग की जाती हैं।
- **पथ ट्रैवर्सल** सभी फ़ाइल-सर्विंग, अपलोड और सहेजने की क्रियाओं में रोका गया है।
- **Upload / Save to File** फ़ाइलनाम सर्वर-साइड सत्यापित करते हैं (केवल `.md` एक्सटेंशन, कोई स्लैश नहीं, कोई null बाइट नहीं)।
- **CORS गार्ड** `updater.php` में क्रॉस-ओरिजिन अनुरोध अस्वीकार करता है।
- **सभी परिवर्तनकारी क्रियाओं** के लिए `POST` आवश्यक है (सीधे URL मोड को छोड़कर, जिनके लिए उनके अपने `ALLOW_*` फ़्लैग आवश्यक हैं)।
- **संस्करण स्ट्रिंग** `?restore=` में केवल `[a-zA-Z0-9.\-]` के विरुद्ध सत्यापित होती हैं।
- **मशीन API** केवल JSON उत्तर देता है (`format=json`), `API_KEY` माँगता है, और हर
  छोड़ी गई बाहरी फ़ाइल को अधिलेखित करने के बजाय रिपोर्ट करता है।

---

## लाइसेंस

PolyForm Noncommercial License 1.0.0 के अंतर्गत जारी। देखें [LICENSE](https://github.com/paulmann/MD.Viewer/blob/main/LICENSE)।
