صفحهٔ اصلی: تفاوت میان نسخه‌ها

از shakiba
پرش به ناوبری پرش به جستجو
برچسب: برگردانده‌شده
 
(۳ نسخهٔ میانیِ ایجادشده توسط همین کاربر نشان داده نشد)
خط ۱: خط ۱:
'''مدیاویکی با موفقیت نصب شد.'''
== به ویکی شکیبا خوش آمدید ==


برای اطلاعات بیشتر در مورد به‌کارگیری نرم‌افزار ویکی به [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents فهرست راهنمای کاربر] مراجعه کنید.
این ویکی، مرجع شخصی من برای ثبت و مرور آموخته‌هایی است که در مسیر یادگیری برنامه‌نویسی و طراحی وب جمع‌آوری کرده‌ام.


<!DOCTYPE html>
== موضوعات ==
<html lang="fa" dir="rtl">
{| class="wikitable" style="direction:rtl; width:100%;"
<head>
|-
  <meta charset="UTF-8">
! موضوع !! توضیح کوتاه
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
|-
  <title>ویکی شخصی من</title>
| [[Git]] || سیستم کنترل نسخه؛ دستورات پایه، شاخه‌ها، ریموت، rebase و حل تعارض
  <style>
|-
    @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700&display=swap');
| [[WordPress]] || طراحی سایت با وردپرس؛ قالب، افزونه، سئو، امنیت و مدیریت محتوا
|-
| [[CMD]] || خط فرمان ویندوز؛ ناوبری، مدیریت فایل، شبکه و فایل‌های Batch
|-
| [[Prompt Engineering]] || اصول و تکنیک‌های نوشتن پرامپت برای مدل‌های هوش مصنوعی
|-
| [[HTML CSS JavaScript]] || طراحی سایت با HTML، CSS و JavaScript؛ ساختار، استایل‌دهی و تعامل صفحه
|}


    * { margin: 0; padding: 0; box-sizing: border-box; }
== دربارهٔ این ویکی ==


    :root {
* هدف: ثبت منظم و قابل‌جستجوی آموخته‌ها به‌جای پراکنده بودن در یادداشت‌های مختلف
      --bg: #f8f9fa;
* زبان محتوا: فارسی
      --surface: #ffffff;
* ساختار هر صفحه: مفاهیم پایه، سپس دستورات/تکنیک‌ها در قالب جدول، و در پایان نکات و بهترین شیوه‌ها
      --sidebar-bg: #f0f2f5;
      --border: #a2a9b1;
      --border-light: #c8ccd1;
      --text: #202122;
      --text-muted: #54595d;
      --text-light: #72777d;
      --accent: #3366cc;
      --accent-hover: #2a4b8d;
      --accent-visited: #795cb2;
      --link: #0645ad;
      --link-hover: #0b0080;
      --link-visited: #0b0080;
      --tab-bg: #eaecf0;
      --tab-active: #ffffff;
      --toc-bg: #f8f9fa;
      --toc-border: #a2a9b1;
      --danger: #b32424;
      --success: #14866d;
      --warning: #e07d10;
      --tag-green: #14866d;
      --tag-blue: #3366cc;
      --tag-orange: #b7570a;
      --tag-purple: #795cb2;
      --shadow: 0 1px 3px rgba(0,0,0,0.08);
    }


    body {
== آخرین تغییرات ==
      font-family: 'Vazirmatn', sans-serif;
      background: var(--bg);
      color: var(--text);
      font-size: 14px;
      line-height: 1.7;
    }


    a { color: var(--link); text-decoration: none; }
برای مشاهدهٔ آخرین ویرایش‌های انجام‌شده در ویکی، به [[Special:RecentChanges|تغییرات اخیر]] مراجعه کنید.
    a:hover { text-decoration: underline; color: var(--link-hover); }
 
    /* === Personal Bar (top) === */
    .personal-bar {
      background: var(--surface);
      border-bottom: 1px solid var(--border-light);
      padding: 0.25rem 1rem;
      font-size: 0.82rem;
      display: flex;
      justify-content: flex-start;
      align-items: center;
      gap: 1rem;
      color: var(--text-muted);
    }
 
    .personal-bar a { color: var(--link); font-size: 0.82rem; }
 
    /* === Main Header === */
    .wiki-header {
      background: var(--surface);
      padding: 0.8rem 1.5rem;
      display: flex;
      align-items: center;
      justify-content: space-between;
      border-bottom: 3px solid var(--accent);
    }
 
    .wiki-logo {
      display: flex;
      align-items: center;
      gap: 0.75rem;
    }
 
    .wiki-logo-icon {
      width: 50px;
      height: 50px;
      background: var(--accent);
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      color: white;
      font-size: 1.6rem;
      font-weight: 700;
    }
 
    .wiki-logo h1 {
      font-size: 1.5rem;
      font-weight: 400;
      color: var(--text);
    }
 
    .wiki-logo h1 span {
      font-weight: 700;
    }
 
    .header-search {
      display: flex;
      align-items: center;
      gap: 0.4rem;
    }
 
    .header-search input {
      padding: 0.45rem 0.8rem;
      border: 1px solid var(--border);
      border-radius: 2px;
      font-family: inherit;
      font-size: 0.9rem;
      width: 300px;
      direction: rtl;
    }
 
    .header-search input:focus {
      outline: none;
      border-color: var(--accent);
      box-shadow: 0 0 0 2px rgba(51,102,204,0.15);
    }
 
    .header-search button {
      padding: 0.45rem 0.8rem;
      background: var(--accent);
      color: white;
      border: 1px solid var(--accent);
      font-family: inherit;
      font-size: 0.85rem;
      cursor: pointer;
      border-radius: 2px;
    }
 
    .header-search button:hover { background: var(--accent-hover); }
 
    /* === Body Layout === */
    .wiki-body {
      display: flex;
      max-width: 1400px;
      margin: 0 auto;
      min-height: calc(100vh - 120px);
    }
 
    /* === Sidebar === */
    .wiki-sidebar {
      width: 220px;
      min-width: 220px;
      background: var(--surface);
      border-left: 1px solid var(--border-light);
      padding: 1rem 0;
      font-size: 0.88rem;
    }
 
    .sidebar-section {
      margin-bottom: 0.5rem;
    }
 
    .sidebar-section h4 {
      padding: 0.4rem 1rem;
      font-size: 0.78rem;
      font-weight: 700;
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: 0.05em;
      background: var(--tab-bg);
      border-top: 1px solid var(--border-light);
      border-bottom: 1px solid var(--border-light);
    }
 
    .sidebar-section ul {
      list-style: none;
      padding: 0.3rem 0;
    }
 
    .sidebar-section li a {
      display: block;
      padding: 0.3rem 1rem;
      color: var(--accent);
      transition: background 0.15s;
    }
 
    .sidebar-section li a:hover {
      background: var(--tab-bg);
      text-decoration: none;
    }
 
    .sidebar-section li a.active-page {
      background: var(--accent);
      color: white;
      font-weight: 500;
    }
 
    .sidebar-search {
      padding: 0.5rem 1rem;
    }
 
    .sidebar-search input {
      width: 100%;
      padding: 0.35rem 0.6rem;
      border: 1px solid var(--border);
      font-family: inherit;
      font-size: 0.85rem;
      direction: rtl;
    }
 
    .sidebar-search input:focus {
      outline: none;
      border-color: var(--accent);
    }
 
    .sidebar-search button {
      width: 100%;
      margin-top: 0.3rem;
      padding: 0.3rem;
      background: var(--tab-bg);
      border: 1px solid var(--border);
      font-family: inherit;
      font-size: 0.82rem;
      cursor: pointer;
      color: var(--text-muted);
    }
 
    .sidebar-search button:hover { background: var(--border-light); }
 
    /* === Content Area === */
    .wiki-content {
      flex: 1;
      min-width: 0;
    }
 
    /* Tabs */
    .content-tabs {
      display: flex;
      background: var(--tab-bg);
      border-bottom: 1px solid var(--border-light);
      padding: 0 1rem;
      gap: 0;
    }
 
    .content-tab {
      padding: 0.5rem 1rem;
      font-size: 0.88rem;
      color: var(--text-muted);
      cursor: pointer;
      border: 1px solid transparent;
      border-bottom: none;
      background: none;
      font-family: inherit;
      border-radius: 2px 2px 0 0;
      position: relative;
      top: 1px;
      transition: background 0.15s;
    }
 
    .content-tab:hover {
      background: var(--surface);
      color: var(--text);
    }
 
    .content-tab.active {
      background: var(--surface);
      color: var(--text);
      border-color: var(--border-light);
      border-bottom-color: var(--surface);
      font-weight: 500;
    }
 
    /* Article Content */
    .article-body {
      background: var(--surface);
      border: 1px solid var(--border-light);
      border-top: none;
      padding: 1.5rem 2rem;
      min-height: 400px;
    }
 
    .article-body h1.article-title {
      font-size: 1.8rem;
      font-weight: 400;
      border-bottom: 1px solid var(--border-light);
      padding-bottom: 0.3rem;
      margin-bottom: 1rem;
      line-height: 1.4;
    }
 
    .article-intro {
      margin-bottom: 1.5rem;
    }
 
    /* Table of Contents */
    .toc {
      background: var(--toc-bg);
      border: 1px solid var(--toc-border);
      padding: 0.8rem 1rem;
      display: inline-block;
      margin: 0 0 1.5rem;
      min-width: 200px;
      font-size: 0.9rem;
    }
 
    .toc-title {
      font-weight: 700;
      text-align: center;
      margin-bottom: 0.3rem;
    }
 
    .toc-toggle {
      font-size: 0.82rem;
      color: var(--link);
      cursor: pointer;
      margin-right: 0.3rem;
    }
 
    .toc ol {
      margin: 0;
      padding-right: 1.2rem;
      list-style-type: decimal;
    }
 
    .toc ol ol {
      list-style-type: decimal;
    }
 
    .toc li {
      margin: 0.2rem 0;
    }
 
    .toc li a {
      color: var(--link);
    }
 
    /* Section Headings */
    .wiki-heading {
      display: flex;
      align-items: baseline;
      border-bottom: 1px solid var(--border-light);
      margin: 1.5rem 0 0.8rem;
      padding-bottom: 0.2rem;
    }
 
    .wiki-heading h2 {
      font-size: 1.4rem;
      font-weight: 400;
      flex: 1;
    }
 
    .wiki-heading .edit-section {
      font-size: 0.82rem;
      color: var(--text-light);
    }
 
    .wiki-heading h3 {
      font-size: 1.15rem;
      font-weight: 700;
      flex: 1;
    }
 
    .wiki-heading h3::after {
      content: '';
      display: none;
    }
 
    .article-body p {
      margin-bottom: 0.8rem;
    }
 
    .article-body ul, .article-body ol {
      margin: 0.5rem 0 0.8rem 2rem;
    }
 
    .article-body li {
      margin-bottom: 0.3rem;
    }
 
    /* Category Box */
    .catlinks {
      background: var(--toc-bg);
      border: 1px solid var(--border-light);
      padding: 0.5rem 1rem;
      margin-top: 2rem;
      font-size: 0.85rem;
      display: flex;
      gap: 0.5rem;
      flex-wrap: wrap;
      align-items: center;
    }
 
    .catlinks strong { margin-left: 0.3rem; }
 
    .catlinks a {
      background: var(--tab-bg);
      padding: 0.15rem 0.5rem;
      border-radius: 2px;
      font-size: 0.82rem;
    }
 
    /* Page Footer */
    .page-footer {
      background: var(--surface);
      border-top: 1px solid var(--border-light);
      padding: 1rem 2rem;
      font-size: 0.82rem;
      color: var(--text-light);
      text-align: center;
    }
 
    /* === Wiki Text Formatting === */
    .wiki-bold { font-weight: 700; }
    .wiki-italic { font-style: italic; }
 
    code {
      background: var(--toc-bg);
      padding: 0.1rem 0.35rem;
      border: 1px solid var(--border-light);
      font-size: 0.92em;
      border-radius: 2px;
      font-family: monospace;
    }
 
    pre {
      background: var(--toc-bg);
      border: 1px solid var(--border-light);
      padding: 0.8rem 1rem;
      overflow-x: auto;
      font-size: 0.9rem;
      margin: 0.8rem 0;
      border-radius: 2px;
    }
 
    pre code {
      background: none;
      border: none;
      padding: 0;
    }
 
    blockquote {
      border-right: 3px solid var(--accent);
      padding-right: 1rem;
      margin: 0.8rem 0;
      color: var(--text-muted);
      background: var(--toc-bg);
      padding: 0.5rem 1rem;
    }
 
    /* Info Box */
    .infobox {
      float: left;
      clear: right;
      margin: 0 0 1rem 1.5rem;
      border: 1px solid var(--border-light);
      border-collapse: collapse;
      background: var(--surface);
      font-size: 0.88rem;
      width: 280px;
    }
 
    .infobox-header {
      background: var(--accent);
      color: white;
      padding: 0.5rem 0.8rem;
      text-align: center;
      font-weight: 700;
      font-size: 1rem;
    }
 
    .infobox td, .infobox th {
      padding: 0.3rem 0.6rem;
      border: 1px solid var(--border-light);
      vertical-align: top;
    }
 
    .infobox th {
      background: var(--toc-bg);
      text-align: right;
      font-weight: 500;
      width: 35%;
    }
 
    /* Alert Boxes */
    .ambox {
      border: 1px solid var(--border-light);
      background: var(--toc-bg);
      padding: 0.6rem 1rem;
      margin: 0.8rem 0;
      font-size: 0.88rem;
      display: flex;
      align-items: center;
      gap: 0.6rem;
    }
 
    .ambox-icon { font-size: 1.3rem; }
 
    /* Editor (Modal) */
    .modal-overlay {
      display: none;
      position: fixed;
      inset: 0;
      background: rgba(0,0,0,0.5);
      z-index: 200;
      align-items: center;
      justify-content: center;
      padding: 1rem;
    }
 
    .modal-overlay.show { display: flex; }
 
    .modal {
      background: var(--surface);
      border: 1px solid var(--border);
      width: 100%;
      max-width: 800px;
      max-height: 90vh;
      overflow-y: auto;
      border-radius: 2px;
    }
 
    .modal-header {
      background: var(--tab-bg);
      padding: 0.6rem 1rem;
      border-bottom: 1px solid var(--border-light);
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
 
    .modal-header h3 { font-size: 1rem; font-weight: 500; }
 
    .modal-body { padding: 1.2rem; }
 
    .form-group { margin-bottom: 1rem; }
 
    .form-group label {
      display: block;
      font-size: 0.85rem;
      font-weight: 500;
      margin-bottom: 0.3rem;
      color: var(--text-muted);
    }
 
    .form-group input,
    .form-group textarea,
    .form-group select {
      width: 100%;
      padding: 0.5rem 0.7rem;
      border: 1px solid var(--border);
      font-family: inherit;
      font-size: 0.95rem;
      direction: rtl;
      border-radius: 2px;
    }
 
    .form-group textarea {
      min-height: 300px;
      resize: vertical;
      font-family: monospace;
      font-size: 0.9rem;
      line-height: 1.8;
      direction: ltr;
      text-align: left;
    }
 
    .form-group input:focus,
    .form-group textarea:focus,
    .form-group select:focus {
      outline: none;
      border-color: var(--accent);
    }
 
    .modal-actions {
      display: flex;
      gap: 0.5rem;
      padding: 0.6rem 1rem;
      background: var(--tab-bg);
      border-top: 1px solid var(--border-light);
    }
 
    .modal-actions button {
      padding: 0.4rem 1rem;
      font-family: inherit;
      font-size: 0.85rem;
      cursor: pointer;
      border-radius: 2px;
    }
 
    .btn-save {
      background: var(--accent);
      color: white;
      border: 1px solid var(--accent);
    }
 
    .btn-save:hover { background: var(--accent-hover); }
 
    .btn-cancel {
      background: var(--tab-bg);
      color: var(--text);
      border: 1px solid var(--border);
    }
 
    .btn-cancel:hover { background: var(--border-light); }
 
    .btn-delete {
      background: var(--danger);
      color: white;
      border: 1px solid var(--danger);
      margin-right: auto;
    }
 
    .btn-delete:hover { background: #9b1b1b; }
 
    /* Footer nav */
    .footer-nav {
      background: var(--surface);
      border-top: 1px solid var(--border-light);
      padding: 0.5rem 1rem;
      display: flex;
      justify-content: center;
      gap: 1.5rem;
      font-size: 0.82rem;
      color: var(--text-light);
    }
 
    .footer-nav a { color: var(--link); }
 
    /* Special pages list */
    .special-list {
      list-style: none;
      padding: 0;
    }
 
    .special-list li {
      padding: 0.4rem 0;
      border-bottom: 1px dotted var(--border-light);
    }
 
    .special-list li:last-child { border-bottom: none; }
 
    .special-list a {
      font-weight: 500;
    }
 
    .special-list .desc {
      color: var(--text-muted);
      font-size: 0.88rem;
      margin-right: 0.5rem;
    }
 
    /* Responsive */
    @media (max-width: 900px) {
      .wiki-sidebar { display: none; }
      .header-search input { width: 180px; }
      .article-body { padding: 1rem; }
      .infobox { float: none; width: 100%; margin: 0 0 1rem; }
    }
  </style>
</head>
<body>
 
  <!-- Personal Toolbar -->
  <div class="personal-bar">
    <span>👤 کاربر مهمان</span>
    <a href="#" onclick="openCreateModal(); return false;">صفحه جدید</a>
    <a href="#" onclick="showAllPages(); return false;">همه صفحات</a>
    <a href="#" onclick="exportData(); return false;">خروجی JSON</a>
    <a href="#" onclick="importData(); return false;">ورودی JSON</a>
  </div>
 
  <!-- Header -->
  <header class="wiki-header">
    <div class="wiki-logo">
      <div class="wiki-logo-icon">W</div>
      <h1><span>ویکی</span> شخصی من</h1>
    </div>
    <div class="header-search">
      <input type="text" id="globalSearch" placeholder="جستجو..." oninput="searchPages()">
      <button onclick="searchPages()">جستجو</button>
    </div>
  </header>
 
  <!-- Body -->
  <div class="wiki-body">
    <!-- Sidebar -->
    <nav class="wiki-sidebar" id="sidebar">
      <div class="sidebar-section">
        <h4>ابزارها</h4>
        <div class="sidebar-search">
          <input type="text" id="sidebarSearch" placeholder="جستجو در صفحات..." oninput="searchPages()">
          <button onclick="searchPages()">برو</button>
        </div>
      </div>
      <div class="sidebar-section">
        <h4>ناوبری</h4>
        <ul id="navPages">
          <li><a href="#" class="active-page" onclick="showMainPage(); return false;">صفحه اصلی</a></li>
        </ul>
      </div>
      <div class="sidebar-section">
        <h4>صفحات ویژه</h4>
        <ul>
          <li><a href="#" onclick="showAllPages(); return false;">همه صفحات</a></li>
          <li><a href="#" onclick="openCreateModal(); return false;">ایجاد صفحه جدید</a></li>
        </ul>
      </div>
      <div class="sidebar-section" id="categorySidebar">
        <h4>رده‌ها</h4>
        <ul id="categoryList"></ul>
      </div>
    </nav>
 
    <!-- Content -->
    <div class="wiki-content">
      <div class="content-tabs" id="contentTabs">
        <button class="content-tab active" id="tab-read" onclick="switchTab('read')"><strong>خواندن</strong></button>
        <button class="content-tab" id="tab-edit" onclick="switchTab('edit')"><strong>ویرایش</strong></button>
      </div>
 
      <!-- Read View -->
      <div class="article-body" id="readView"></div>
 
      <!-- Edit View (hidden) -->
      <div class="article-body" id="editView" style="display:none;"></div>
 
      <!-- Footer -->
      <div class="page-footer">
        صفحه آخرین بار در <span id="footerDate"></span> ویرایش شده است.
      </div>
    </div>
  </div>
 
  <!-- Bottom nav -->
  <div class="footer-nav">
    <a href="#" onclick="showMainPage(); return false;">صفحه اصلی</a>
    <span>•</span>
    <a href="#" onclick="showAllPages(); return false;">فهرست صفحات</a>
    <span>•</span>
    <a href="#" onclick="openCreateModal(); return false;">صفحه جدید</a>
    <span>•</span>
    <span>ذخیره شده در مرورگر شما ❤️</span>
  </div>
 
  <!-- Create/Edit Modal -->
  <div class="modal-overlay" id="editorModal">
    <div class="modal">
      <div class="modal-header">
        <h3 id="modalTitle">ایجاد صفحه جدید</h3>
        <button class="btn-cancel" onclick="closeModal('editorModal')" style="border:none;background:none;font-size:1.2rem;cursor:pointer;">✕</button>
      </div>
      <div class="modal-body">
        <div class="form-group">
          <label>نام صفحه (عنوان)</label>
          <input type="text" id="pageTitle" placeholder="مثال: React Hooks">
        </div>
        <div class="form-group">
          <label>رده (دسته‌بندی)</label>
          <input type="text" id="pageCategory" placeholder="مثال: برنامه‌نویسی، طراحی، یادداشت">
        </div>
        <div class="form-group">
          <label>محتوای ویکی (Wiki markup ساده)</label>
          <textarea id="pageContent" placeholder="== عنوان بخش ==&#10;&#10;متن مقاله اینجا...&#10;&#10;=== زیرعنوان ===&#10;&#10;* مورد اول&#10;* مورد دوم&#10;&#10;{{یادآوری|متن مهم}}&#10;&#10;[[پیوند به صفحه دیگر]]&#10;&#10;---"></textarea>
        </div>
      </div>
      <div class="modal-actions">
        <button class="btn-save" onclick="savePage()">ذخیره صفحه</button>
        <button class="btn-cancel" onclick="closeModal('editorModal')">لغو</button>
        <button class="btn-delete" id="modalDeleteBtn" style="display:none;" onclick="deleteCurrentPage()">حذف صفحه</button>
      </div>
    </div>
  </div>
 
  <!-- Hidden file input for import -->
  <input type="file" id="importInput" accept=".json" style="display:none;" onchange="handleImport(event)">
 
  <script>
    // ===== Data Layer =====
    let pages = JSON.parse(localStorage.getItem('mediawiki_pages') || '[]');
    let currentPageId = null;
    let editingPageId = null;
 
    function save() {
      localStorage.setItem('mediawiki_pages', JSON.stringify(pages));
    }
 
    function genId() {
      return Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
    }
 
    function getPageBySlug(slug) {
      return pages.find(p => p.slug === slug);
    }
 
    function slugify(text) {
      return text.trim().replace(/\s+/g, '-').replace(/[^\w\-\u0600-\u06FF]/g, '').toLowerCase();
    }
 
    // ===== Wiki Markup Parser =====
    function parseWikiMarkup(text) {
      let html = text;
 
      // Escape HTML first
      html = html.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
 
      // Headings: == Title == → <h2>, === Title === → <h3>, etc.
      html = html.replace(/^======\s*(.+?)\s*======/gm, '<h5>$1</h5>');
      html = html.replace(/^=====\s*(.+?)\s*=====/gm, '<h4>$1</h4>');
      html = html.replace(/^====\s*(.+?)\s*====/gm, '<h4>$1</h4>');
      html = html.replace(/^===\s*(.+?)\s*===/gm, '<h3>$1</h3>');
      html = html.replace(/^==\s*(.+?)\s*==/gm, '</div><div class="wiki-heading"><h2>$1</h2></div><div class="wiki-para">');
 
      // Bold & Italic
      html = html.replace(/'{5}(.+?)'{5}/g, '<b><i>$1</i></b>');
      html = html.replace(/'{3}(.+?)'{3}/g, '<b>$1</b>');
      html = html.replace(/'{2}(.+?)'{2}/g, '<i>$1</i>');
 
      // Internal links: [[Page Name]] or [[Page Name|display text]]
      html = html.replace(/\[\[([^\]|]+)\|([^\]]+)\]\]/g, '<a href="#" onclick="navigateToPage(\'$1\'); return false;" class="wiki-link">$2</a>');
      html = html.replace(/\[\[([^\]]+)\]\]/g, '<a href="#" onclick="navigateToPage(\'$1\'); return false;" class="wiki-link">$1</a>');
 
      // External links: [http://... text]
      html = html.replace(/\[(https?:\/\/[^\s]+)\s+([^\]]+)\]/g, '<a href="$1" target="_blank">$2</a>');
      // Bare external: [http://...]
      html = html.replace(/\[(https?:\/\/[^\s]+)\]/g, '<a href="$1" target="_blank">$1</a>');
 
      // Templates: {{یادآوری|text}} or {{tip|text}}
      html = html.replace(/\{\{یادآوری\|(.+?)\}\}/g, '<div class="ambox"><span class="ambox-icon">💡</span><span>$1</span></div>');
      html = html.replace(/\{\{tip\|(.+?)\}\}/g, '<div class="ambox"><span class="ambox-icon">✅</span><span>$1</span></div>');
      html = html.replace(/\{\{هشدار\|(.+?)\}\}/g, '<div class="ambox" style="border-color:var(--danger);"><span class="ambox-icon">⚠️</span><span>$1</span></div>');
      html = html.replace(/\{\{note\|(.+?)\}\}/g, '<div class="ambox"><span class="ambox-icon">📝</span><span>$1</span></div>');
 
      // Code blocks: <code>...</code> or indented lines
      html = html.replace(/<code>([\s\S]*?)<\/code>/g, '<pre><code>$1</code></pre>');
 
      // Blockquote: lines starting with :
      html = html.replace(/^:\s*(.+)$/gm, '<blockquote>$1</blockquote>');
 
      // Horizontal rule: ----
      html = html.replace(/^----+$/gm, '<hr style="border:none;border-top:1px solid var(--border-light);margin:1rem 0;">');
 
      // Unordered lists
      html = html.replace(/^\* (.+)$/gm, '<li style="list-style:disc;margin-right:1.5rem;">$1</li>');
 
      // Ordered lists
      html = html.replace(/^# (.+)$/gm, '<li style="list-style:decimal;margin-right:1.5rem;">$1</li>');
 
      // Wrap consecutive <li> in <ul>
      html = html.replace(/(<li[^>]*>.*?<\/li>\n?)+/g, (match) => {
        if (match.includes('list-style:decimal')) return '<ol style="margin:0.5rem 0;">' + match + '</ol>';
        return '<ul style="margin:0.5rem 0;">' + match + '</ul>';
      });
 
      // Line breaks: double newline = paragraph
      html = html.replace(/\n\n+/g, '</p><p>');
      html = html.replace(/\n/g, '<br>');
 
      // Wrap in paragraphs
      html = '<div class="wiki-para"><p>' + html + '</p></div>';
 
      // Remove empty paragraphs
      html = html.replace(/<p><\/p>/g, '');
      html = html.replace(/<p>\s*<\/p>/g, '');
 
      return html;
    }
 
    // ===== Rendering =====
    function showMainPage() {
      currentPageId = null;
      updateSidebarActive('صفحه اصلی');
 
      const defaultContent = pages.length === 0
        ? getWelcomeContent()
        : getRecentChangesContent();
 
      document.getElementById('readView').innerHTML = defaultContent;
      document.getElementById('tab-read').click();
      document.getElementById('footerDate').textContent = formatDate(new Date().toISOString());
      window.scrollTo(0, 0);
    }
 
    function getWelcomeContent() {
      return `
        <h1 class="article-title">صفحه اصلی</h1>
        <div class="ambox">
          <span class="ambox-icon">📖</span>
          <span>به ویکی شخصی خود خوش آمدید! اینجا می‌توانید هر چیزی که یاد می‌گیرید را ذخیره کنید.</span>
        </div>
        <h2 style="font-weight:400;font-size:1.4rem;margin:1.5rem 0 0.5rem;border-bottom:1px solid var(--border-light);padding-bottom:0.2rem;">نحوه استفاده</h2>
        <p>برای شروع کار با این ویکی، از امکانات زیر استفاده کنید:</p>
        <ul style="margin:0.5rem 0 1rem 2rem;">
          <li><b>ایجاد صفحه جدید:</b> از لینک «صفحه جدید» در بالای صفحه استفاده کنید.</li>
          <li><b>ویرایش صفحه:</b> روی تب «ویرایش» کلیک کنید.</li>
          <li><b>پیوند بین صفحات:</b> از <code>[[نام صفحه]]</code> استفاده کنید.</li>
          <li><b>قالب‌بندی:</b> از <code>== عنوان ==</code> برای تیتر و <code>'''متن'''</code> برای بولد استفاده کنید.</li>
        </ul>
        <h2 style="font-weight:400;font-size:1.4rem;margin:1.5rem 0 0.5rem;border-bottom:1px solid var(--border-light);padding-bottom:0.2rem;">شروع کنید</h2>
        <p>اکنون <a href="#" onclick="openCreateModal(); return false;"><b>اولین صفحه خود را بسازید</b></a> و شروع به یادداشت کنید!</p>
        <h2 style="font-weight:400;font-size:1.4rem;margin:1.5rem 0 0.5rem;border-bottom:1px solid var(--border-light);padding-bottom:0.2rem;">راهنمای Wiki markup</h2>
        <table class="infobox" style="width:100%;float:none;">
          <tr><td class="infobox-header" colspan="2">نمونه‌های قالب‌بندی</td></tr>
          <tr><th>عنوان بخش</th><td><code>== عنوان ==</code> یا <code>=== زیرعنوان ===</code></td></tr>
          <tr><th>متن بولد</th><td><code>'''متن بولد'''</code></td></tr>
          <tr><th>متن ایتالیک</th><td><code>''متن ایتالیک''</code></td></tr>
          <tr><th>لیست</th><td><code>* مورد اول</code> (ترتیبی: <code># مورد اول</code>)</td></tr>
          <tr><th>پیوند داخلی</th><td><code>[[نام صفحه]]</code> یا <code>[[نام|متن نمایشی]]</code></td></tr>
          <tr><th>پیوند خارجی</th><td><code>[https://example.com متن لینک]</code></td></tr>
          <tr><th>یادآوری</th><td><code>{{یادآوری|متن مهم}}</code></td></tr>
          <tr><th>کد</th><td><code>&lt;code&gt;کد&lt;/code&gt;</code></td></tr>
          <tr><th>خط جداکننده</th><td><code>----</code></td></tr>
        </table>
        <div style="clear:both;"></div>`;
    }
 
    function getRecentChangesContent() {
      const sorted = [...pages].sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt));
      const recent = sorted.slice(0, 10);
 
      let html = `
        <h1 class="article-title">صفحه اصلی</h1>
        <div class="ambox">
          <span class="ambox-icon">🏠</span>
          <span>به ویکی شخصی خود خوش آمدید. <a href="#" onclick="openCreateModal(); return false;">صفحه جدید ایجاد کنید</a>.</span>
        </div>
        <h2 style="font-weight:400;font-size:1.4rem;margin:1.5rem 0 0.5rem;border-bottom:1px solid var(--border-light);padding-bottom:0.2rem;">تغییرات اخیر</h2>
        <p>آخرین صفحات ویرایش شده:</p>
        <table style="width:100%;border-collapse:collapse;margin:0.5rem 0;">
          <tr style="background:var(--tab-bg);">
            <th style="padding:0.4rem 0.6rem;border:1px solid var(--border-light);text-align:right;">صفحه</th>
            <th style="padding:0.4rem 0.6rem;border:1px solid var(--border-light);text-align:right;">رده</th>
            <th style="padding:0.4rem 0.6rem;border:1px solid var(--border-light);text-align:right;">آخرین ویرایش</th>
          </tr>`;
 
      recent.forEach(p => {
        const cats = p.categories.length > 0 ? p.categories.join(', ') : '—';
        html += `
          <tr style="cursor:pointer;" onclick="navigateToPage('${p.title}')">
            <td style="padding:0.4rem 0.6rem;border:1px solid var(--border-light);"><a href="#" onclick="event.preventDefault();navigateToPage('${p.title}')">${escapeHtml(p.title)}</a></td>
            <td style="padding:0.4rem 0.6rem;border:1px solid var(--border-light);color:var(--text-muted);">${escapeHtml(cats)}</td>
            <td style="padding:0.4rem 0.6rem;border:1px solid var(--border-light);color:var(--text-light);">${formatDate(p.updatedAt)}</td>
          </tr>`;
      });
 
      html += `</table>`;
 
      // Stats
      html += `
        <h2 style="font-weight:400;font-size:1.4rem;margin:1.5rem 0 0.5rem;border-bottom:1px solid var(--border-light);padding-bottom:0.2rem;">آمار ویکی</h2>
        <ul style="margin:0.5rem 0 1rem 2rem;">
          <li>تعداد کل صفحات: <b>${pages.length}</b></li>
          <li>تعداد رده‌ها: <b>${new Set(pages.flatMap(p => p.categories)).size}</b></li>
          <li>بزرگ‌ترین صفحه: <b>${pages.length > 0 ? pages.reduce((a, b) => a.content.length > b.content.length ? a : b).title : '—'}</b></li>
        </ul>`;
 
      return html;
    }
 
    function showAllPages() {
      currentPageId = null;
      updateSidebarActive('همه صفحات');
 
      const sorted = [...pages].sort((a, b) => a.title.localeCompare(b.title, 'fa'));
 
      let html = `<h1 class="article-title">همه صفحات</h1>`;
 
      if (sorted.length === 0) {
        html += `
          <div class="ambox">
            <span class="ambox-icon">📭</span>
            <span>هنوز هیچ صفحه‌ای وجود ندارد. <a href="#" onclick="openCreateModal(); return false;">اولین صفحه را بسازید</a>.</span>
          </div>`;
      } else {
        html += `<p>تعداد کل صفحات: <b>${sorted.length}</b></p>`;
        html += `<ul class="special-list">`;
        sorted.forEach(p => {
          const catText = p.categories.length > 0 ? ` — ${p.categories.join(', ')}` : '';
          html += `<li><a href="#" onclick="navigateToPage('${escapeHtml(p.title)}'); return false;">${escapeHtml(p.title)}</a> <span class="desc">${catText} (${formatDate(p.updatedAt)})</span></li>`;
        });
        html += `</ul>`;
      }
 
      document.getElementById('readView').innerHTML = html;
      document.getElementById('tab-read').click();
      document.getElementById('footerDate').textContent = formatDate(new Date().toISOString());
      window.scrollTo(0, 0);
    }
 
    function navigateToPage(title) {
      const page = pages.find(p => p.title === title || p.slug === slugify(title));
      if (page) {
        showPage(page.id);
      } else {
        // Page doesn't exist — offer to create
        if (confirm(`صفحه «${title}}» وجود ندارد. آیا می‌خواهید آن را بسازید؟`)) {
          editingPageId = null;
          document.getElementById('modalTitle').textContent = `ایجاد صفحه: ${title}`;
          document.getElementById('pageTitle').value = title;
          document.getElementById('pageCategory').value = '';
          document.getElementById('pageContent').value = '';
          document.getElementById('modalDeleteBtn').style.display = 'none';
          document.getElementById('editorModal').classList.add('show');
        }
      }
    }
 
    function showPage(id) {
      const page = pages.find(p => p.id === id);
      if (!page) return showMainPage();
 
      currentPageId = id;
      updateSidebarActive(page.title);
 
      const parsedContent = parseWikiMarkup(page.content);
 
      // Build TOC from headings
      const headingMatches = page.content.match(/^==+\s*(.+?)\s*==+$/gm) || [];
      let tocHtml = '';
      if (headingMatches.length >= 2) {
        tocHtml = `<div class="toc"><div class="toc-title">فهرست مطالب <span class="toc-toggle" onclick="this.parentElement.querySelector('ol').classList.toggle('hidden');">[隐蔽]</span></div><ol>`;
        headingMatches.forEach((h, i) => {
          const depth = (h.match(/^=+/) || ['=='])[0].length - 2;
          const text = h.replace(/^=+\s*/, '').replace(/\s*=+$/, '');
          const indent = depth > 0 ? `style="margin-right:${depth * 1.5}rem;"` : '';
          tocHtml += `<li ${indent}><a href="#section-${i}">${text}</a></li>`;
        });
        tocHtml += `</ol></div>`;
      }
 
      // Build categories
      let catHtml = '';
      if (page.categories.length > 0) {
        catHtml = `<div class="catlinks"><strong>رده‌ها:</strong> ${page.categories.map(c => `<a href="#" onclick="filterByCategory('${escapeHtml(c)}'); return false;">${escapeHtml(c)}</a>`).join(' ')}</div>`;
      }
 
      // Infobox
      const wordCount = page.content.split(/\s+/).length;
      const charCount = page.content.length;
 
      const infobox = `
        <div class="infobox">
          <div class="infobox-header">${escapeHtml(page.title)}</div>
          <table>
            <tr><th>تاریخ ایجاد</th><td>${formatDate(page.createdAt)}</td></tr>
            <tr><th>آخرین ویرایش</th><td>${formatDate(page.updatedAt)}</td></tr>
            <tr><th>تعداد کلمات</th><td>${wordCount.toLocaleString('fa-IR')}</td></tr>
            <tr><th>تعداد حروف</th><td>${charCount.toLocaleString('fa-IR')}</td></tr>
            ${page.categories.length > 0 ? `<tr><th>رده‌ها</th><td>${page.categories.join('، ')}</td></tr>` : ''}
          </table>
        </div>`;
 
      // Assemble
      let fullHtml = `<h1 class="article-title">${escapeHtml(page.title)}</h1>`;
      fullHtml += infobox;
      fullHtml += tocHtml;
      fullHtml += parsedContent;
      fullHtml += `<div style="clear:both;"></div>`;
      fullHtml += catHtml;
 
      // See also — links to other pages
      const otherPages = pages.filter(p => p.id !== page.id).slice(0, 5);
      if (otherPages.length > 0) {
        fullHtml += `<h2 style="font-weight:400;font-size:1.3rem;margin:1.5rem 0 0.5rem;border-bottom:1px solid var(--border-light);padding-bottom:0.2rem;">همچنین ببینید</h2>`;
        fullHtml += `<ul style="margin:0.5rem 0;">`;
        otherPages.forEach(p => {
          fullHtml += `<li><a href="#" onclick="navigateToPage('${escapeHtml(p.title)}'); return false;">${escapeHtml(p.title)}</a></li>`;
        });
        fullHtml += `</ul>`;
      }
 
      document.getElementById('readView').innerHTML = fullHtml;
      document.getElementById('tab-read').click();
      document.getElementById('footerDate').textContent = formatDate(page.updatedAt);
      window.scrollTo(0, 0);
 
      updateSidebar();
    }
 
    function renderEditView(id) {
      const page = id ? pages.find(p => p.id === id) : null;
 
      let html = `<h1 class="article-title" style="margin-bottom:1rem;">${page ? 'ویرایش: ' + escapeHtml(page.title) : 'ایجاد صفحه جدید'}</h1>`;
 
      html += `
        <div style="background:var(--toc-bg);border:1px solid var(--border-light);padding:0.8rem;margin-bottom:1rem;font-size:0.88rem;">
          <b>راهنمای سریع Wiki markup:</b><br>
          <code>== عنوان ==</code> · <code>'''بولد'''</code> · <code>''ایتالیک''</code> · <code>[[پیوند]]</code> · <code>{{یادآوری|متن}}</code> · <code>* لیست</code> · <code>&lt;code&gt;kod&lt;/code&gt;</code>
        </div>
        <div class="form-group">
          <label>عنوان صفحه</label>
          <input type="text" id="editTitle" value="${page ? escapeHtml(page.title) : ''}" placeholder="عنوان صفحه">
        </div>
        <div class="form-group">
          <label>رده‌ها (با کاما جدا کنید)</label>
          <input type="text" id="editCategories" value="${page ? page.categories.join(', ') : ''}" placeholder="برنامه‌نویسی, یادداشت">
        </div>
        <div class="form-group">
          <label>محتوا</label>
          <textarea id="editContent" placeholder="محتوای صفحه را اینجا بنویسید...">${page ? page.content : ''}</textarea>
        </div>
        <div style="display:flex;gap:0.5rem;">
          <button class="btn-save" onclick="saveFromEditor(${id ? `'${id}'` : 'null'})">ذخیره صفحه</button>
          <button class="btn-cancel" onclick="cancelEdit()">لغو</button>
          ${page ? `<button class="btn-delete" onclick="deletePage('${id}')">حذف صفحه</button>` : ''}
        </div>`;
 
      document.getElementById('editView').innerHTML = html;
    }
 
    function switchTab(tab) {
      if (tab === 'read') {
        document.getElementById('tab-read').classList.add('active');
        document.getElementById('tab-edit').classList.remove('active');
        document.getElementById('readView').style.display = '';
        document.getElementById('editView').style.display = 'none';
      } else {
        document.getElementById('tab-edit').classList.add('active');
        document.getElementById('tab-read').classList.remove('active');
        document.getElementById('readView').style.display = 'none';
        document.getElementById('editView').style.display = '';
        renderEditView(currentPageId);
      }
    }
 
    function cancelEdit() {
      if (currentPageId) {
        switchTab('read');
      } else {
        showMainPage();
      }
    }
 
    function saveFromEditor(id) {
      const title = document.getElementById('editTitle').value.trim();
      const cats = document.getElementById('editCategories').value.split(',').map(c => c.trim()).filter(Boolean);
      const content = document.getElementById('editContent').value;
 
      if (!title) { document.getElementById('editTitle').focus(); return; }
      if (!content) { document.getElementById('editContent').focus(); return; }
 
      if (id) {
        const page = pages.find(p => p.id === id);
        if (page) {
          page.title = title;
          page.slug = slugify(title);
          page.categories = cats;
          page.content = content;
          page.updatedAt = new Date().toISOString();
        }
      } else {
        const newPage = {
          id: genId(),
          title,
          slug: slugify(title),
          categories: cats,
          content,
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
        };
        pages.push(newPage);
        currentPageId = newPage.id;
      }
 
      save();
      showPage(currentPageId);
      updateSidebar();
    }
 
    // ===== Modal helpers =====
    function openCreateModal() {
      editingPageId = null;
      document.getElementById('modalTitle').textContent = 'ایجاد صفحه جدید';
      document.getElementById('pageTitle').value = '';
      document.getElementById('pageCategory').value = '';
      document.getElementById('pageContent').value = '';
      document.getElementById('modalDeleteBtn').style.display = 'none';
      document.getElementById('editorModal').classList.add('show');
      document.getElementById('pageTitle').focus();
    }
 
    function savePage() {
      const title = document.getElementById('pageTitle').value.trim();
      const cats = document.getElementById('pageCategory').value.split(',').map(c => c.trim()).filter(Boolean);
      const content = document.getElementById('pageContent').value;
 
      if (!title) { document.getElementById('pageTitle').focus(); return; }
      if (!content) { document.getElementById('pageContent').focus(); return; }
 
      const newPage = {
        id: genId(),
        title,
        slug: slugify(title),
        categories: cats,
        content,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
      };
 
      pages.push(newPage);
      save();
      closeModal('editorModal');
      currentPageId = newPage.id;
      showPage(newPage.id);
      updateSidebar();
    }
 
    function deletePage(id) {
      if (!confirm('آیا مطمئن هستید که می‌خواهید این صفحه را حذف کنید؟')) return;
      pages = pages.filter(p => p.id !== id);
      save();
      currentPageId = null;
      showMainPage();
      updateSidebar();
    }
 
    function deleteCurrentPage() {
      if (currentPageId) deletePage(currentPageId);
    }
 
    function closeModal(id) {
      document.getElementById(id).classList.remove('show');
    }
 
    // ===== Sidebar =====
    function updateSidebar() {
      const navPages = document.getElementById('navPages');
      const sorted = [...pages].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
      const recent = sorted.slice(0, 8);
 
      let html = `<li><a href="#" class="${!currentPageId ? 'active-page' : ''}" onclick="showMainPage(); return false;">صفحه اصلی</a></li>`;
      recent.forEach(p => {
        html += `<li><a href="#" class="${currentPageId === p.id ? 'active-page' : ''}" onclick="navigateToPage('${escapeHtml(p.title)}'); return false;">${escapeHtml(p.title)}</a></li>`;
      });
      navPages.innerHTML = html;
 
      // Categories
      const allCats = [...new Set(pages.flatMap(p => p.categories))].sort();
      const catList = document.getElementById('categoryList');
      catList.innerHTML = allCats.map(c =>
        `<li><a href="#" onclick="filterByCategory('${escapeHtml(c)}'); return false;">${escapeHtml(c)}</a></li>`
      ).join('');
    }
 
    function updateSidebarActive(title) {
      document.querySelectorAll('.sidebar-section li a').forEach(a => {
        a.classList.remove('active-page');
        if (a.textContent.trim() === title) a.classList.add('active-page');
      });
    }
 
    function filterByCategory(cat) {
      const matching = pages.filter(p => p.categories.includes(cat));
      let html = `<h1 class="article-title">رده: ${escapeHtml(cat)}</h1>`;
      html += `<p>تعداد صفحات در این رده: <b>${matching.length}</b></p>`;
 
      if (matching.length > 0) {
        html += `<ul class="special-list">`;
        matching.forEach(p => {
          html += `<li><a href="#" onclick="navigateToPage('${escapeHtml(p.title)}'); return false;">${escapeHtml(p.title)}</a> <span class="desc">(${formatDate(p.updatedAt)})</span></li>`;
        });
        html += `</ul>`;
      }
 
      document.getElementById('readView').innerHTML = html;
      document.getElementById('tab-read').click();
      window.scrollTo(0, 0);
    }
 
    // ===== Search =====
    function searchPages() {
      const query = document.getElementById('globalSearch').value.trim().toLowerCase() ||
                    document.getElementById('sidebarSearch').value.trim().toLowerCase();
 
      if (!query) {
        if (currentPageId) return showPage(currentPageId);
        return showMainPage();
      }
 
      const results = pages.filter(p =>
        p.title.toLowerCase().includes(query) ||
        p.content.toLowerCase().includes(query) ||
        p.categories.some(c => c.toLowerCase().includes(query))
      );
 
      let html = `<h1 class="article-title">نتایج جستجو: «${escapeHtml(query)}»</h1>`;
      html += `<p>${results.length} نتیجه یافت شد.</p>`;
 
      if (results.length > 0) {
        html += `<ul class="special-list">`;
        results.forEach(p => {
          // Show context snippet
          const idx = p.content.toLowerCase().indexOf(query);
          let snippet = '';
          if (idx !== -1) {
            const start = Math.max(0, idx - 40);
            const end = Math.min(p.content.length, idx + query.length + 60);
            snippet = (start > 0 ? '...' : '') + p.content.substring(start, end) + (end < p.content.length ? '...' : '');
          }
          html += `<li><a href="#" onclick="navigateToPage('${escapeHtml(p.title)}'); return false;">${escapeHtml(p.title)}</a>`;
          if (snippet) html += `<br><span class="desc">${escapeHtml(snippet)}</span>`;
          html += `</li>`;
        });
        html += `</ul>`;
      }
 
      document.getElementById('readView').innerHTML = html;
      document.getElementById('tab-read').click();
      window.scrollTo(0, 0);
    }
 
    // ===== Import/Export =====
    function exportData() {
      const data = JSON.stringify(pages, null, 2);
      const blob = new Blob([data], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `wiki-backup-${new Date().toISOString().slice(0, 10)}.json`;
      a.click();
      URL.revokeObjectURL(url);
    }
 
    function importData() {
      document.getElementById('importInput').click();
    }
 
    function handleImport(event) {
      const file = event.target.files[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = (e) => {
        try {
          const data = JSON.parse(e.target.result);
          if (!Array.isArray(data)) throw new Error('Invalid format');
          if (confirm(`آیا می‌خواهید ${data.length} صفحه را وارد کنید؟ (صفحات فعلی حفظ می‌شوند)`)) {
            data.forEach(p => {
              if (p.title && p.content) {
                pages.push({
                  id: p.id || genId(),
                  title: p.title,
                  slug: p.slug || slugify(p.title),
                  categories: p.categories || [],
                  content: p.content,
                  createdAt: p.createdAt || new Date().toISOString(),
                  updatedAt: p.updatedAt || new Date().toISOString(),
                });
              }
            });
            save();
            showMainPage();
            updateSidebar();
            alert(`${data.length} صفحه با موفقیت وارد شد.`);
          }
        } catch (err) {
          alert('خطا در خواندن فایل. لطفاً فایل JSON معتبر وارد کنید.');
        }
      };
      reader.readAsText(file);
      event.target.value = '';
    }
 
    // ===== Helpers =====
    function formatDate(iso) {
      if (!iso) return '—';
      return new Date(iso).toLocaleDateString('fa-IR', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
    }
 
    function escapeHtml(text) {
      const d = document.createElement('div');
      d.textContent = text;
      return d.innerHTML;
    }
 
    // ===== Keyboard Shortcuts =====
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape') {
        document.querySelectorAll('.modal-overlay.show').forEach(m => m.classList.remove('show'));
      }
      if (e.key === 'n' && (e.ctrlKey || e.metaKey)) {
        e.preventDefault();
        openCreateModal();
      }
    });
 
    // Close modal on overlay click
    document.querySelectorAll('.modal-overlay').forEach(o => {
      o.addEventListener('click', (e) => { if (e.target === o) o.classList.remove('show'); });
    });
 
    // ===== Init =====
    updateSidebar();
    showMainPage();
  </script>
</body>
</html>

نسخهٔ کنونی تا ۶ سپتامبر ۲۰۲۶، ساعت ۰۷:۳۴

به ویکی شکیبا خوش آمدید

این ویکی، مرجع شخصی من برای ثبت و مرور آموخته‌هایی است که در مسیر یادگیری برنامه‌نویسی و طراحی وب جمع‌آوری کرده‌ام.

موضوعات

موضوع توضیح کوتاه
Git سیستم کنترل نسخه؛ دستورات پایه، شاخه‌ها، ریموت، rebase و حل تعارض
WordPress طراحی سایت با وردپرس؛ قالب، افزونه، سئو، امنیت و مدیریت محتوا
CMD خط فرمان ویندوز؛ ناوبری، مدیریت فایل، شبکه و فایل‌های Batch
Prompt Engineering اصول و تکنیک‌های نوشتن پرامپت برای مدل‌های هوش مصنوعی
HTML CSS JavaScript طراحی سایت با HTML، CSS و JavaScript؛ ساختار، استایل‌دهی و تعامل صفحه

دربارهٔ این ویکی

  • هدف: ثبت منظم و قابل‌جستجوی آموخته‌ها به‌جای پراکنده بودن در یادداشت‌های مختلف
  • زبان محتوا: فارسی
  • ساختار هر صفحه: مفاهیم پایه، سپس دستورات/تکنیک‌ها در قالب جدول، و در پایان نکات و بهترین شیوه‌ها

آخرین تغییرات

برای مشاهدهٔ آخرین ویرایش‌های انجام‌شده در ویکی، به تغییرات اخیر مراجعه کنید.