حُمرَة. جُزْس ديب ديف: كيف تعمل وكيف تمددها؟ (العربية (Arabic))

حُمرَة. جُزْس ديب ديف: كيف تعمل وكيف تمددها؟

Sunday, 09 November 2025

//

15 minute read

أولاً

ملاحظة: هذا جزء من تجاربي مع AI/ طريقة لإنفاق 1000 دولار كرمز ائتمانات الويب. لقد أطعمت هذه مجموعة من الأوراق، فهمي، أسئلة كان علي توليد هذه المقالة. إنها ممتعة وتملأ فجوة لم أرها ملأها في أي مكان آخر.

ويستند هذا المقال إلى مواد سابقة: إذا لم تكن قد فعلت بالفعل، تحقق من إضافة حوريّة. js مع, مُدِّر المواضيع للمُحْرَمَة، و ، ، ، ، ، ، ، ، ، ، ، تعزيز حورية الحورية مع البان/الزوم والتصديرهذا الغوص العميق يفسّر الداخليات وراء تلك التنفيذات.

حورية البحر. js رائعة حقاً. اكتب نصاً بسيطاً ، احصل على رسوم بيانية جميلة. لا مزيد من التعثر مع Visio أو رسم.io ، فقدان ملفات المصدر ، أو الحفاظ على ملفات صور منفصلة. كل شيء يعيش في علامة ، تحكم النسخة جنباً إلى جنب مع رمزك.

ولكن أردت أن أعرف كيف في الواقع يعمل تحت الغطاء. كيف يفعل هذا:

graph LR
    A[Text] --> B[Magic?]
    B --> C[Beautiful Diagram]

و الأهم من ذلك، كيف يمكنك ربطه به لإضافة سمات مثل بان/زوم، التبديل المواضيعي، و خاصية التصدير التي بنيتها لهذا الموقع (متاحة الآن كما يلي: @medorlymedlyluccid/memed-enhancements)?

بعد الكثير من البحث من خلال شفرة مصدر حورية البحر وبناء الامتدادات الحقيقية، هنا كل شيء تعلمته عن كيفية عمل حورية البحر داخليا وكيفية توسيعه بشكل صحيح.

ما هو (ميرميدج) ؟

تقوم حورية البحر بتحويل تعاريف النص إلى رسوم بيانية. فكر في "ماركداون للرسميات."

الطريقة القديمة:

1. 2. 3. كصادرات 4. مُخْطَطَة في الطُّقق 5. ابحث المصدر ملفّ حرّر إعادة تصدير استبدل صورة...

طريقة الحورية:

1. 2. م أنجز

تحديث الإيطالية فقط حرّر نص النسخة تحكم هو فقط نص يعمل بوصة هدف? نعم!

تدعم الحوريات عدداً سخيفاً من أنواع الرسوم البيانية:

graph LR
    A[Flowcharts] --> B[Sequence Diagrams]
    B --> C[Class Diagrams]
    C --> D[State Diagrams]
    D --> E[ER Diagrams]
    E --> F[Gantt Charts]
    F --> G[Pie Charts]
    G --> H[Git Graphs]
    H --> I[User Journeys]
    I --> J[And many more...]

انظر S انظر « فآ » م للقائمة الكاملة.

كيف تعمل الحَمَمَة : الخط

هذا ما يحدث عندما تقوم حورية البحر بتقديم رسم بياني:

graph TD
    A[Text Definition] --> B[Lexer/Tokenizer]
    B --> C[Parser]
    C --> D[AST Built]
    D --> E[Diagram Type Detected]
    E --> F[Type-Specific Renderer]
    F --> G[SVG Generated]
    G --> H[Inserted into DOM]
    H --> I[Your Enhancements Run]

    style A stroke:#059669,stroke-width:3px,color:#10b981
    style D stroke:#2563eb,stroke-width:3px,color:#3b82f6
    style G stroke:#7c3aed,stroke-width:3px,color:#8b5cf6
    style I stroke:#d97706,stroke-width:3px,color:#f59e0b

دعونا نكسر كل خطوة.

الخطوة 1: النص التعريف

يبدأ كل شيء بالنص. تكتب الرسوم البيانية في DSL (اللغة الخاصة بالمجال):

// Flowchart
const diagram = `
graph TD
    A[Start] --> B{Is it working?}
    B -->|Yes| C[Great!]
    B -->|No| D[Debug time]
`;

الخطوة 2: التحليل البرمجي

الـ elder يكسر نص بوصة رمزيّات. على سبيل المثال، هذا سطر:

A[Start] --> B{Decision}

مثل:

[
    { type: 'NODE_ID', value: 'A' },
    { type: 'NODE_TEXT', value: 'Start' },
    { type: 'ARROW', value: '-->' },
    { type: 'NODE_ID', value: 'B' },
    { type: 'NODE_TEXT', value: 'Decision' },
    { type: 'NODE_SHAPE', value: 'diamond' }  // from { }
]

الخطوة 3: التنازل والجيل

يستهلك المحلل الرسومات ويبني شجرة لصياغة الملخصات (AST):

// Simplified AST structure
{
    type: 'flowchart',
    direction: 'TD',
    nodes: [
        { id: 'A', text: 'Start', shape: 'rect' },
        { id: 'B', text: 'Decision', shape: 'diamond' }
    ],
    edges: [
        { from: 'A', to: 'B', type: 'arrow' }
    ]
}

تستخدم حورية الحورية حوريات مختلفة لـ كل نوع تخطيطي. هذه غالباً ما يتم توليدها من الملفات التي تستخدم J (مثل Yacc/Bison for JavaScript).

الخطوة 4: المخطط

تقوم الحورية بكشف نوع الرسم البياني من السطر الأول:

// Simplified detection logic
if (text.match(/^\s*graph/)) return 'flowchart';
if (text.match(/^\s*sequenceDiagram/)) return 'sequence';
if (text.match(/^\s*classDiagram/)) return 'class';
// ... etc

خطوة 5: خطوة خطوة 5: منح

ولكل نوع من أنواع الرسوم البيانية مُصَنِّع خاص به. يأخذ المُعَدِّل AST ويولِّد عناصر SVG.

فيما يتعلق بتدفقات الصور التجارية، تستخدم حَرِمَة الحَرْمَمَة لمكتبة تخطيط الرسوم البيانية. بالنسبة للآخرين، تستخدم خوارزميات مخصصة أو مكتبات مثل Cytoscape.

// Simplified flowchart renderer
export const draw = function (text, id, version, diagObj) {
    const graph = diagObj.db;  // The AST
    const svg = d3.select(`#${id}`);

    // Render nodes
    graph.getVertices().forEach(vertex => {
        drawNode(svg, vertex);
    });

    // Render edges
    graph.getEdges().forEach(edge => {
        drawEdge(svg, edge);
    });

    // Apply layout algorithm
    dagre.layout(graph);
};

الخطوة 6: توليد SVG

المُجَرِّر يُنتِج رَمْز SVG:

<svg xmlns="http://www.w3.org/2000/svg">
    <g class="node">
        <rect x="0" y="0" width="100" height="50"/>
        <text x="50" y="25">Start</text>
    </g>
    <g class="edge">
        <path d="M 100 25 L 200 25" stroke="#333"/>
    </g>
</svg>

الخطوة 7: إدراج

المُحْوَلَة تُفِد كُل .mermaid ويستبدلها بـ SVG translated SVG:

// From mermaid.ts
export const init = async function (config, nodes) {
    const nodesToProcess = nodes || document.querySelectorAll('.mermaid');

    for (const node of nodesToProcess) {
        const id = `mermaid-${Date.now()}-${Math.random()}`;
        const txt = node.textContent;

        const { svg } = await render(id, txt);
        node.innerHTML = svg;
    }
};

الخطوة 8: مرحلة ما بعد المعالجة (حيث تدخل)

بعد إدخال الحوريّة SVG، يمكنك تعزيزها. هنا حيث كلّ مؤازراتي في:

  • دالة البان/المازوم
  • القدرات التصديرية
  • نقل السممة

المزيد حول هذا أدناه.

مُمَدْدِد الحَحْمَة: نقاط التمدّد

الآن بما أننا نعرف كيف تعمل حورية البحر، دعونا نستكشف كيف نوسعها.

الدالة

وأهم عملية تمديد أساسية هي التشكيلة التالية:

import mermaid from 'mermaid';

mermaid.initialize({
    startOnLoad: true,
    theme: 'dark',
    securityLevel: 'loose',
    flowchart: {
        curve: 'basis',
        padding: 15
    }
});

2 - تكييف الموضوع

لقد غطيت هذا بشكل مكثف في مُدِّر المواضيع للمُحْرَمَةلكن هذا هو التنفيذ الرئيسي:

المشكلة مع مُحوِّل الموضوع

و لا يمكنك تغييره بعد ذلك. ولكن إذا أردت إعادة إدخال الرسوم التخطيطية مع سمة جديدة، تحتاج إلى مصدر الرسم الأصلي - أي حورية لا يخزن في DOM.

الإحلال

يخزن المحتوى الأصلي قبل التصريف، ثم يسترجع ويعاد الاسترجاع عند التبديل

// From my theme-switcher implementation
const originalData = new Map();

// Save original content before first render
const saveOriginalData = async () => {
    const elements = document.querySelectorAll('.mermaid');
    elements.forEach(element => {
        const id = element.id || `mermaid-${Date.now()}`;
        element.id = id;

        // Store the original diagram source
        if (!originalData.has(id)) {
            originalData.set(id, element.textContent?.trim());
        }
    });
};

// When theme changes, restore and re-render
const loadMermaid = async (theme) => {
    mermaid.initialize({
        startOnLoad: false,
        theme: theme
    });

    const elements = document.querySelectorAll('.mermaid');
    for (const element of elements) {
        const source = originalData.get(element.id);
        if (source) {
            element.innerHTML = '';  // Clear
            element.removeAttribute('data-processed');

            const { svg } = await mermaid.render(
                `mermaid-svg-${element.id}`,
                source
            );
            element.innerHTML = svg;
        }
    }
};

طرق الكشف عن مواضيع متعددة (المواقع التي تعالج فيها المواضيع بشكل مختلف):

function detectTheme() {
    // Check various sources
    if (typeof window.__themeState !== 'undefined') {
        return window.__themeState;
    }
    if (localStorage.theme) {
        return localStorage.theme;
    }
    if (document.documentElement.classList.contains('dark')) {
        return 'dark';
    }
    if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
        return 'dark';
    }
    return 'light';
}

انظر S انظر كود مُزِّر الموضوع الكامل للتفاصيل.

3- تعزيزات ما بعد المناقصة

هنا حيث يحدث السحر الحقيقي. بعد أن تقوم حورية البحر بترجمة، يمكنك إضافة خصائص تفاعلية.

لقد غطيت هذا بشكل مكثف في تعزيز حورية الحورية مع البان/الزوم والتصديرلذا سأسلط الضوء على التقنيات الرئيسية هنا

أنشئ حاوية غلاف للضوابط:

function wrapDiagram(element) {
    if (element.closest('.mermaid-wrapper')) {
        return element.closest('.mermaid-wrapper');
    }

    const wrapper = document.createElement('div');
    wrapper.className = 'mermaid-wrapper';
    wrapper.id = `wrapper-${element.id}`;

    element.parentNode.insertBefore(wrapper, element);
    wrapper.appendChild(element);

    return wrapper;
}

مضافاً بان/ زوم

& بوصة Svg-pan- zoom:

import svgPanZoom from 'svg-pan-zoom';

const panZoomInstances = new Map();

function initPanZoom(svgElement, diagramId) {
    // Clean up existing instance
    if (panZoomInstances.has(diagramId)) {
        panZoomInstances.get(diagramId).destroy();
        panZoomInstances.delete(diagramId);
    }

    const instance = svgPanZoom(svgElement, {
        zoomEnabled: true,
        controlIconsEnabled: false,
        fit: true,
        center: true,
        minZoom: 0.1,
        maxZoom: 10
    });

    panZoomInstances.set(diagramId, instance);
    return instance;
}

% مُنتفر المُنتفر

لا يمكن إنشاء هذا النظام:

function createControlButtons(container, diagramId) {
    const controlsDiv = document.createElement('div');
    controlsDiv.className = 'mermaid-controls';

    const buttons = [
        { icon: 'bx-fullscreen', title: 'Fullscreen', action: 'fullscreen' },
        { icon: 'bx-zoom-in', title: 'Zoom In', action: 'zoomIn' },
        { icon: 'bx-zoom-out', title: 'Zoom Out', action: 'zoomOut' },
        { icon: 'bx-reset', title: 'Reset', action: 'reset' },
        { icon: 'bx-move', title: 'Pan', action: 'pan' },
        { icon: 'bx-image', title: 'Export PNG', action: 'exportPng' },
        { icon: 'bx-code-alt', title: 'Export SVG', action: 'exportSvg' }
    ];

    buttons.forEach(btn => {
        const button = document.createElement('button');
        button.className = `mermaid-control-btn bx ${btn.icon}`;
        button.setAttribute('data-action', btn.action);
        button.setAttribute('data-diagram-id', diagramId);
        controlsDiv.appendChild(button);
    });

    container.appendChild(controlsDiv);
}

أي شيء حدث (الصفعة!)

لا ترفق المستمعين على كل زر. استخدم مصدر الحدث:

document.addEventListener('click', (e) => {
    const target = e.target;
    if (!target.classList.contains('mermaid-control-btn')) return;

    const action = target.getAttribute('data-action');
    const diagramId = target.getAttribute('data-diagram-id');
    const panZoom = panZoomInstances.get(diagramId);

    switch (action) {
        case 'zoomIn': panZoom?.zoomIn(); break;
        case 'zoomOut': panZoom?.zoomOut(); break;
        case 'reset': panZoom?.reset(); break;
        // ... etc
    }
});

التحدي: عناصر SVG لها خصائص ديناميكية، تحويلات شاملة/زومية، وأساليب موروثة. لكي يتم التصدير بشكل صحيح، تحتاج إلى:

  1. مُنْقل الـSVG
  2. _اخل
  3. حوّل إلى PNG أو SVG

& بوصة html إلى صورة:

import { toPng, toSvg } from 'html-to-image';

async function exportDiagram(container, format, diagramId) {
    const svgElement = container.querySelector('svg');
    if (!svgElement) return;

    // Clone to avoid modifying original
    const clonedSvg = svgElement.cloneNode(true);

    // Get or calculate viewBox
    let viewBox = clonedSvg.getAttribute('viewBox');
    if (!viewBox) {
        const bbox = svgElement.getBBox();
        viewBox = `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`;
        clonedSvg.setAttribute('viewBox', viewBox);
    }

    // Set explicit dimensions
    const [, , width, height] = viewBox.split(' ').map(Number);
    clonedSvg.setAttribute('width', width);
    clonedSvg.setAttribute('height', height);

    // Remove pan-zoom transforms
    clonedSvg.removeAttribute('style');

    // Create off-screen container
    const temp = document.createElement('div');
    temp.style.position = 'absolute';
    temp.style.left = '-9999px';
    temp.appendChild(clonedSvg);
    document.body.appendChild(temp);

    // Export
    const dataUrl = format === 'png'
        ? await toPng(clonedSvg, { pixelRatio: 2 })
        : await toSvg(clonedSvg);

    downloadFile(dataUrl, `diagram-${Date.now()}.${format}`);

    // Cleanup
    document.body.removeChild(temp);
}

التفاصيل الجوهرية:

  • دعم العرض - تلتقط الرسم البياني الكامل، ليس فقط الجزء الظاهري
  • التــي عــد الت عــد الت عــل الت عــل الت - تجنب التأثير على الرسم البياني المعروض
  • بكيكل الراتيثيو: 2 - كبار كبار كبار المسؤولين من أجل صادرات بابوا غينيا الجديدة

انظر S انظر (رمز التصدير الكامل) لمزيد من التفاصيل.

المجموعة الكاملة من عناصر التعزيز

لقد حزمت كل هذه التعزيزات @medorlymedlyluccid/memed-enhancementsانظر: إدخالات مُحسنات مُنْشِشِشِر كحزمة Nnpm لتفاصيل كاملة.

هذه هي الطريقة التي تنسجم بها مع بعضها البعض:

graph TB
    A[User Initializes] --> B[init Function]
    B --> C[initMermaid]
    B --> D[enhanceMermaidDiagrams]

    C --> E[Theme Detection]
    C --> F[Event Listeners]
    C --> G[Mermaid Rendering]

    E --> E1[Global State]
    E --> E2[LocalStorage]
    E --> E3[DOM Class]
    E --> E4[OS Preference]

    F --> F1[Custom Events]
    F --> F2[Media Query]

    G --> H[Apply Enhancements]
    D --> H

    H --> I[Wrap Diagrams]
    H --> J[Init Pan/Zoom]
    H --> K[Add Controls]

    I --> L[Interactive Diagram]
    J --> L
    K --> L

    L --> M[User Interactions]
    M --> M1[Zoom In/Out]
    M --> M2[Pan]
    M --> M3[Fullscreen]
    M --> M4[Export PNG/SVG]

    style A stroke:#059669,stroke-width:3px,color:#10b981
    style L stroke:#2563eb,stroke-width:3px,color:#3b82f6
    style M stroke:#7c3aed,stroke-width:3px,color:#8b5cf6

npm install @mostlylucid/mermaid-enhancements
import { init } from '@mostlylucid/mermaid-enhancements';
import '@mostlylucid/mermaid-enhancements/styles.css';

await init();

هذه هي! رسومات حورية البحر الخاصة بك الآن:

  • الأمم المتحدة
  • ● صندوق ضوئي كامل الفرز
  • □ بابوا غينيا الجديدة/تصدير SVG
  • □ تحوّل محور تلقائي
  • التصميم الرجعي

التكامل مع HTMX

كما أنا مغطى في إضافة حوريّة. js مععليك إعادة تأريخ الحوريّة بعد مبادلة المحتوى HTMX:

// On page load
document.addEventListener('DOMContentLoaded', function () {
    mermaid.initialize({ startOnLoad: true });
});

// After HTMX swaps content
document.body.addEventListener('htmx:afterSwap', function(evt) {
    mermaid.run();
});

مع حزمة التعزيزات:

import { init, enhanceMermaidDiagrams } from '@mostlylucid/mermaid-enhancements';

// Initial load
await init();

// After HTMX swap
document.body.addEventListener('htmx:afterSwap', async function() {
    await init();  // Re-init Mermaid with current theme
    enhanceMermaidDiagrams();  // Re-apply enhancements
});

أفضل الممارسات

بعد بناء هذه الأشياء وتصحيح حالات الحافة الغريبة، وهنا ما يعمل:

1 - يجب أن يكون المخزن الأصلي

الحوريّة لا تحافظ على مصدر الرسم التخطيطيّ الأصليّ بعد التخلّص منه، يجب أن تخزنه بنفسك:

const originalData = new Map();

// Before first render
element.setAttribute('data-original-code', element.textContent);
originalData.set(element.id, element.textContent);

// When re-rendering
element.innerHTML = originalData.get(element.id);

2- حالات النظافة

تسربات الذاكرة حقيقية. تدمير الحالات قبل إنشاء حالات جديدة:

if (panZoomInstances.has(id)) {
    try {
        panZoomInstances.get(id).destroy();
    } catch (e) {
        console.warn('Failed to destroy:', e);
    }
    panZoomInstances.delete(id);
}

لا شيء

لا ترفق مستمعات إلى فرادى الأزرار:

// ❌ Don't do this
buttons.forEach(btn => {
    btn.addEventListener('click', handler);
});

// ✅ Do this
document.addEventListener('click', (e) => {
    if (e.target.matches('.mermaid-control-btn')) {
        handleClick(e.target);
    }
});

قاذفة قاذفة قاذفة قاذفة سحابة سحابة سحابة

المُحمّل الصاروخي يُؤخّر جافاScript تنفيذ. انتظر للمُتَوَقّعات:

function waitForDependencies(maxAttempts = 50) {
    return new Promise((resolve) => {
        let attempts = 0;

        const check = () => {
            if (window.mermaid && window.htmx && window.Alpine) {
                resolve();
            } else if (attempts >= maxAttempts) {
                resolve();  // Give up
            } else {
                attempts++;
                setTimeout(check, Math.min(50 * Math.pow(1.2, attempts), 500));
            }
        };

        check();
    });
}

واستبعد نصك الرئيسي من تحميل Rocket:

<script src="main.js" data-cfasync="false"></script>

5- التوقيت هو كل شيء

requestAnimationFrame أفضل توقيتاً من التعسف setTimeout:

// After Mermaid renders
await mermaid.run();

// Wait for paint before enhancing
await new Promise(resolve => {
    requestAnimationFrame(() => {
        requestAnimationFrame(() => {
            enhanceMermaidDiagrams();
            resolve();
        });
    });
});

6 - المناولة الدفاعية SVG

الـ SVGs يمكن أن تكون غريبة.

const svgElement = container.querySelector('svg');
if (!svgElement) {
    console.warn('No SVG found');
    return;
}

// Clone before modifying
const cloned = svgElement.cloneNode(true);

// Ensure viewBox exists
let viewBox = cloned.getAttribute('viewBox');
if (!viewBox) {
    const bbox = svgElement.getBBox();
    viewBox = `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`;
}

م_ م_ م_ م_ م_ م_ م_ م_ م_ م_ م_ م_ م

المُسند المُنتج المُنتج

مع التمهيد المناسب، يجب أن ترى:

Saving original data
Loading mermaid with theme: dark
Mermaid initialized
Enhanced 3 diagrams

أولاً - المؤهلات

بعد التنفيذ:

  • [ [المخططات التي يجري تقديمها في تحميل الصفحة
  • [ العمل المتعلق بضوابط السيطرة على البان/الزوم
  • [ (X، انقر خارج، ESC)
  • [ رسوم بيانية كاملة
  • [ [محافظات تصدير SVG
  • [ [تعمل بعد مقايضة المحتوى HTMX
  • [ / theme تحويل إعادة استخدامات بشكل صحيح / them en en them re- rethers بشكل صحيح
  • [ *
  • [ / / / / / / / نمط نمط نمط نمط
  • [ واو - إمكانية الاطلاع على المفتاح

الـمُشَكِّل:

-

  • مُحمِلwindow.mermaid)

لا يعمل بان/زوم لا يعمل:

  • تحقق من Svg- an- zoom مسقط
  • افحص لـ متضارب CSSSpointer-events: none)

المصدرات فقط زاوية:

  • حفظ
  • نقل
  • 1 م ع (ر م)

السمة ليس التبديل:

  • لم يتم حفظ
  • data-processed
  • المستمعون لأحداث غير مسجلون

لجنة البلدان

& التمهيد

لا تُحمِل التعزيزات قبل الحاجة:

let enhancementsLoaded = false;

async function loadEnhancements() {
    if (enhancementsLoaded) return;

    const { enhanceMermaidDiagrams } = await import('./enhancements.js');
    enhanceMermaidDiagrams();
    enhancementsLoaded = true;
}

// Load on first interaction
document.addEventListener('click', (e) => {
    if (e.target.closest('.mermaid')) {
        loadEnhancements();
    }
}, { once: true });

مراقب المراقبة

مُسندة مُسندة إلى مُنتَزَع:

const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            renderDiagram(entry.target);
            observer.unobserve(entry.target);
        }
    });
}, { rootMargin: '100px' });

document.querySelectorAll('.mermaid').forEach(el => {
    observer.observe(el);
});

ثانياً - النفـفـس

عند إعادة تحجيم أو تغيير الموضوع:

let timeout;
window.addEventListener('resize', () => {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
        panZoomInstances.forEach(instance => {
            instance.resize();
            instance.fit();
        });
    }, 250);
});

في الإستنتاج

لكن فهم كيفية عملها داخلياً يسمح لك ببناء بعض التعزيزات الرائعة حقاً. الرؤى الرئيسية:

  1. جاري البحث: النص / اليكسر / Parser / AST / Render / SVG
  2. نقاط الاتصالالموضوع، المواضيع، تعزيزات ما بعد التقديم
  3. مجلدالحوريّة لا تفعل ذلك من أجلك
  4. ****:: أن تكون التسريبات في الذاكرة حقيقية
  5. لا شيء:: تحسين الأداء
  6. مراس: تُعالج المواقع مواضيع مختلفة عن بعضها البعض:
  7. المسائلالاستخدام: الاستخدام: requestAnimationFrame

كل التقنيات التي غطيتها هنا تستخدم في الإنتاج في هذا الموقع @medorlymedlyluccid/memed-enhancementsالمصدر الكامل متاح في الناويـب اللـكـس غالبيـاً منهـا/معظمهـا من العارضات.

الوظائف المتصلة بها

الموارد الخارجة عن

جرب الضوابط على الرسوم البيانية في الأعلى!

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.