---
url: https://nsh.dev/ar/docs/architecture
description: "كيف يعمل تطبيق NSH: مسار البناء، وخيوط المحرّك والخيط الرئيسي، والمسار من تغيّر الإشارة (signal) إلى العروض الأصلية (native views)، والرسم البياني التفاعلي الأصلي، وتخطيط النصوص، والحزم (crates)."
---

# البنية المعمارية

هذه الصفحة موجهة لمطوري التطبيقات الذين يريدون معرفة ما يحدث بين `count.set(1)` وتغيّر نص على الشاشة،
في تطبيق مكتوب بلغة Rust أو بـ TSX.
لا تحتاج إليها لكتابة التطبيقات، لكنها تشرح لماذا يتصرف NSH على النحو الذي يتصرف به: لماذا تعمل المكوّنات مرة واحدة، ولماذا
تبقى الرسوم المتحركة سلسة بينما JS مشغول، ولماذا لا ينتظر الخيط الرئيسي شيفرتك أبدًا.

<a id="the-big-picture"></a>

## الصورة الكبيرة

![How an nsh app runs: the nsh CLI builds App.tsx into bytecode; on the engine thread, QuickJS runs a handler, the reactive graph in nsh-js queues ops, and nsh-core commits one MountBatch; it crosses to the main thread in one hop, which applies it to UIKit or Android views; touches go back as events. Native animations run on the main thread without JavaScript.](https://nsh.dev/diagrams/architecture-light.svg)

<a id="building-only-your-code-becomes-js"></a>

## البناء: شيفرتك وحدها تتحول إلى JS

`nsh build` و`nsh dev` وبقية الأوامر تجمّع تطبيقك في حزمة واحدة بسلسلة أدوات مكتوبة بلغة Rust (دون Node.js):

1. **Oxc** يزيل أنواع TypeScript.
2. **مترجم JSX الخاص بـ NSH** يحوّل JSX إلى استدعاءات مباشرة للمُصيِّر الأصلي، في المرحلة نفسها. الخصائص الثابتة
   والعناصر الأبناء تُضبط مرة واحدة؛ أما التعبيرات التي قد تتغير (التي تستدعي دالة أو تقرأ خاصية) فتصبح روابط (bindings) يتابعها
   المُصيِّر. وتتحول المكوّنات إلى استدعاءات دوال عادية مع كائن خصائص تكون فيه الخصائص المتغيرة دوال getter.
3. **rolldown** يجمّع وحداتك في وحدة ES واحدة. استيرادات وحدات NSH (`nsh` و`nsh/mobile` و
   `nsh/base` ...) تبقى استيرادات: فهذه الوحدات أصلية ويقدّمها وقت التشغيل. **الحزمة تحتوي على شيفرة تطبيقك
   فقط**؛ فمثال العدّاد أقل من 1 كيلوبايت من JS.
4. تُصرَّف الحزمة إلى **شيفرة بايتية** (`bundle.nshc`)، وهي تُحمَّل أسرع من المصدر، وتُركَّب **خريطة مصدر**
   عبر جميع الخطوات بحيث تشير تتبّعات المكدس إلى أسطر `.tsx` الخاصة بك.

تُجمع الصور والخطوط التي تستوردها كأصول (assets)، وتُكتب تصريحات الأنواع لمحررك في
`.nsh/types`.

التطبيق المكتوب بلغة Rust لا يحتوي على حزمة: إذ يبني Cargo مُشغِّل كل هدف مع ربط حزمتك (crate) به، ويعمل
المكوّن الجذر الذي يسمّيه `nsh.json` على المحرك أصليًا. وكل ما يلي ينطبق عليه أيضًا، مع فارق أن شيفرة مكوّناتك
تعمل في Rust حيث تعمل شيفرة تطبيق TSX في QuickJS.

<a id="running-two-threads-per-app"></a>

## التشغيل: خيطان لكل تطبيق

كل تطبيق قيد التشغيل (`NshAppView` على iOS، و`NshView` على Android، ونافذة التطبيق المُحزَّم) هو **محرك** واحد
له **خيط محرك** خاص به، ولتطبيقات TSX جهاز QuickJS افتراضي (VM) خاص به. لا توجد كائنات مفردة عامة (singletons)، لذلك يمكن تشغيل عدة تطبيقات جنبًا إلى جنب
داخل تطبيق مضيف واحد.

<a id="the-engine-thread"></a>

### خيط المحرك

كل ما يتعلق بمنطق تطبيقك يحدث هنا:

- **شيفرتك** تعمل هنا: المكوّنات ومعالجات الأحداث والمؤقتات والتأثيرات (effects). مكوّنات Rust تعمل أصليًا، وTSX يعمل في
  **QuickJS**.
- **الرسم البياني التفاعلي** (الإشارات signals، والقيم المحسوبة computed، والتأثيرات effects، والمالكون owners، والسياق context) أصلي. عُقده كائنات QuickJS
  من أصناف أصلية، فيرى مُجمِّع النفايات المراجع عبرها، لكن تتبّع الاعتماديات
  وجدولة التحديثات يتمّان في Rust (وتتبع خوارزمية Solid 1.9).
- **المُصيِّر** الذي تستدعيه شيفرة JSX المترجمة أصلي. ينشئ العُقد ويحوّل كل تغيير إلى **عملية (op)** تُدفع
  مباشرة إلى طابور أصلي: بلا JSON وبلا رسائل لكل خاصية.
- **النواة** تحتفظ بشجرة ظلّية لجميع العُقد. عند انتهاء دورة JS تأخذ كل العمليات المصطفّة دفعة واحدة و**تُنفِّذ الالتزام (commit)**:
  تُصيِّر مكوّنات Rust (مكوّنات `nsh/base` ومكوّنات التطبيق نفسه)، وتحلّ هوامش `env()` في الحشو (padding) والهامش (margin)، وتعيد بناء النصوص المتغيرة، وتجري التخطيط باستخدام **Taffy** (flexbox وgrid، مع
  اتجاه عمودي افتراضي وبلا انكماش)، وتقارن الإطارات والأنماط، وتنتج **MountBatch** واحدة: قائمة العمليات التي تحتاجها
  الواجهات الأصلية.

<a id="the-main-thread"></a>

### الخيط الرئيسي

خيط المنصة الرئيسي (خيط الواجهة) يطبّق كل MountBatch في قفزة واحدة: ينشئ الواجهات ويُدرجها ويضبط الأنماط والإطارات
ويتسلّم النص بعد تخطيطه. وبخلاف ذلك لا يفعل إلا ما يجب أن يحدث عليه:

- **رسم النص** الذي خُطِّط مسبقًا على خيط المحرك؛
- **الرسوم المتحركة الأصلية**: القيم المتحركة من `nsh` تُدار إطارًا بإطار (`CADisplayLink` على iOS، ومُحرِّك
  على خيط الواجهة على Android) وتُكتب مباشرة في الواجهات؛
- **الإيماءات والتمرير**: تتحول اللمسات إلى أحداث تُرسل إلى خيط المحرك؛ و`scrollValue` و`pan` تغذّيان القيم
  المتحركة في الإطار نفسه.

**الخيط الرئيسي لا ينتظر JS أبدًا.** عندما يحتاج النظام إلى جواب فوري (هل يجوز تغيير هذا النص، وهل يجوز لزر
الرجوع إغلاق هذه الشاشة، وهل يجوز إغلاق هذه الورقة)، يكون الجواب قد رُكِّب مسبقًا كخاصية، ويعلم JS به
لاحقًا عبر حدث. قد يؤخر خيط المحرك المشغول التحديثات، لكنه لا يستطيع تجميد التمرير أو الرسوم المتحركة أو
إيماءات النظام.

<a id="from-a-tap-to-the-screen"></a>

## من اللمسة إلى الشاشة

```text
 main thread               engine thread                                              main thread
 ───────────               ─────────────────────────────────────────────────────      ───────────
 tap on a view ─ event ─▶  handler runs: count.set(count() + 1)
                           reactive graph: the bindings that read count() run again
                           renderer: one op per changed binding → native queue
                           JS turn ends → core commit: render, text, layout, diff
                           → MountBatch (e.g. 1 Text item)                 ─ hop ─▶   apply: draw the new text
```

في مثال العدّاد، تكلّف اللمسة نحو 0.12 ms في إصدار release على جهاز Mac، شاملةً JS والالتزام معًا، وتنتج
عددًا قليلًا من عناصر التركيب (انظر [الاختبار](https://nsh.dev/translations/ar/docs/testing-and-trace.md)).

في تطبيق Rust يكون المعالج إغلاقًا (closure) في Rust (`count.set(count.get() + 1)`)، ويتبع ذلك الرسم البياني نفسه والالتزام والدفعة نفسها،
دون أي مفسِّر في الطريق.

تنتج ثلاث خصائص عن هذا التصميم:

- **دورة واحدة تعني التزامًا واحدًا.** مهما بلغ عدد الإشارات التي يضبطها المعالج، تتغير الواجهات مرة واحدة، معًا.
- **المكوّنات تعمل مرة واحدة.** الروابط وحدها تُعاد تشغيلها؛ لا إعادة تصيير للمكوّن ولا مقارنة DOM افتراضي. والمقارنة
  الوحيدة المتبقية هي مقارنة النواة للإطارات والأنماط، في Rust.
- **التغييرات وحدها تنتقل.** الأنماط والإطارات غير المتغيرة لا تُرسل؛ والواجهات الجديدة لا تُعطى قيمًا افتراضية.

<a id="text-laid-out-once-off-the-main-thread"></a>

## النص: يُخطَّط مرة واحدة، بعيدًا عن الخيط الرئيسي

النص غالبًا هو الجزء الأكثر كلفة في الواجهة. يخطّطه NSH على خيط المحرك باستخدام محرك النصوص الخاص بالمنصة، فيبدو
تمامًا كالنص الأصلي:

1. **التحضير**: يتحول `<Text>` وما بداخله من نطاقات (spans) متداخلة إلى سلسلة منسّقة واحدة (CoreText على منصات Apple،
   و`PrecomputedText` / `StaticLayout` على Android)؛
2. **القياس** عند العروض التي يطلبها التخطيط؛ وتُخزَّن القياسات مؤقتًا بحسب المحتوى والعرض، فلا يعيد تغيير اللون وحده
   القياس؛
3. **التخطيط** بالحجم النهائي.

يستلم الخيط الرئيسي النتيجة غير القابلة للتغيير ويرسمها فقط. النص المتداخل في `<Text>` هو نطاق (span) وليس واجهة؛
والسلاسل النصية خارج `<Text>` لا تُصيَّر شيئًا.

<a id="lists"></a>

## القوائم

`List` مُفتَرَضة (virtualized) أصليًا. تقرر النواة أي الصفوف قريبة من منفذ العرض، وتحسب المواضع والأعمدة
والرؤوس الثابتة، وتعيد تدوير الخلايا. وعندما تحتاج خلية إلى عنصر مختلف، يربطها وقت التشغيل (يتغير الوصولان `item()` و`index()`
في دالة الصف الخاصة بك)؛ أما الإطارات التي لا تكشف صفًا جديدًا فلا تستدعي JS إطلاقًا. انظر [القوائم](https://nsh.dev/translations/ar/docs/lists.md).

<a id="everything-else-the-runtime-does"></a>

## كل ما يفعله وقت التشغيل أيضًا

- **الشبكات**: `fetch` وواجهات API العامة الأخرى أصناف أصلية. تذهب الطلبات إلى حزمة HTTP الخاصة بالمنصة
  (`NSURLSession`، `HttpURLConnection`)، مع البث في الاتجاهين.
- **الصور** تُنزَّل وتُفكّ ترميزها بعيدًا عن الخيط الرئيسي، بحجم الواجهة، مع ذاكرة تخزين مؤقت.
- **الحدود والصلاحيات** يفرضها وقت التشغيل، في Rust: انظر [العزل والصلاحيات](https://nsh.dev/translations/ar/docs/sandbox-and-capabilities.md).
- **استبدال الوحدات الفوري (Hot module replacement)** أصلي أيضًا: يطبّق وقت التشغيل التصحيحات في أقل بكثير من ميلي ثانية (انظر
  [خادم التطوير وتصحيح الأخطاء](https://nsh.dev/translations/ar/docs/dev-server-and-debugging.md)).

<a id="three-layers"></a>

## ثلاث طبقات

تُبنى واجهة NSH على ثلاث طبقات، كما هي الحال في GPUI Kit:

| الطبقة | NSH | GPUI Kit | ما هي |
|---|---|---|---|
| العناصر الأولية (Primitives) | `nsh-core` ووقت التشغيل (`nsh`، `nsh/mobile`، `nsh/desktop`) | `gpui` | العناصر المضيفة والأنماط والتخطيط والتصيير والتفاعلية والبيئة |
| المكوّنات الأساسية | `nsh-base` (`nsh/base`) | `gpui-base` | مكوّنات بلا تنسيق تملك السلوك والحالة والتركيز ولوحة المفاتيح وإمكانية الوصول، وتأخذ مظهرها من التطبيق |
| مكتبة المكوّنات | مكتبة تطبيقك، أو مكتبة المجتمع | `gpui-component` | مكوّنات منسّقة: أزرار وبطاقات وصفوف ذات مظهر |

الطبقتان الأوليان هما Native Shell. أما الثالثة فهي لك: يبني التطبيق مكتبة المكوّنات الخاصة به على `nsh/base` وعلى
العناصر الأولية، بلغة TSX أو كمكوّنات Rust في [حزمته الخاصة](https://nsh.dev/translations/ar/docs/rust.md)، أو يستخدم مكتبة ينشرها المجتمع، أو
ينسّق واجهته مباشرة داخل التطبيق.

<a id="the-crates"></a>

## الحزم (crates)

NSH هو مساحة عمل (workspace) بلغة Rust. لا تحتاج إلى بنائه لكتابة التطبيقات (فالتوزيعة تأتي مع CLI وأوقات تشغيل مبنية مسبقًا)، لكن
الأسماء تظهر في السجلات والوثائق:

| الحزمة | ما هي |
|---|---|
| `nsh-core` | النواة المستقلة عن المنصة: الشجرة الظلّية، والأنماط، وتخطيط Taffy، ودمج النصوص، وافتراض القوائم، وعناصر التركيب؛ وقيم CSS وTailwind؛ ورسم الرسوم المتحركة الأصلية (القيم، والاستيفاء، وtiming / spring / decay)؛ وإطار مكوّنات Rust والسمة (theme)؛ ووحدات Rust الخاصة بوقت التشغيل (`BackHandler` الخاصة بـ `nsh/mobile`، ...). |
| `nsh-base` | `nsh/base`، المكوّنات الأساسية بلا تنسيق (`gpui-base` الخاصة بـ NSH): التنقل والحوارات وعناصر التحكم وشريط الحالة؛ وتستخدمها تطبيقات Rust باسم `nsh_base`. |
| `nsh-js` | مضيف QuickJS ووقت تشغيل NSH: الرسم البياني التفاعلي، والمُصيِّر، وكل وحدة أصلية، وواجهات API العامة، واستبدال الوحدات الفوري، والحدود؛ وتصريحات TypeScript لكل ذلك. |
| `nsh-engine` | خيط المحرك: يدير `nsh-js` و`nsh-core` ويسلّم دفعة واحدة لكل إطار إلى المنصة. |
| `nsh-package` | الحزم الموقّعة: المفاتيح والشهادات والإبطال ومخزن الثقة. |
| `nsh-bundler` | أداة التجميع: rolldown وOxc ومترجم JSX الخاص بـ NSH وخرائط المصدر وفحوص أصناف Tailwind. |
| `nsh-dev` | خادم التطوير وعميله. |
| `nsh-platform-ios` | واجهة UIKit الخلفية (iOS والمحاكي وMac Catalyst): تركيب الواجهات، ومحرك النصوص CoreText، والإدخال؛ وحزمة التضمين SDK. |
| `nsh-platform-android` | الواجهة الخلفية لـ Android: `libnsh.so` ووقت تشغيل Java (`NshView`، والواجهات، والصور، والشبكات). |
| `nsh-cli` | الأمر `nsh`. |

اتجاه الاعتمادية هو `nsh-core` ← `nsh-js` ← `nsh-engine` ← المنصات؛ و`nsh-base` مبنية على `nsh-core`. لا تعتمد النواة على أي منصة ولا على أي محرك JS،
وهذا ما يتيح لـ `nsh test` تشغيل وقت التشغيل الحقيقي دون واجهة على أي جهاز.

<a id="see-also"></a>

## انظر أيضًا

- [الأداء](https://nsh.dev/translations/ar/docs/performance.md)
- [التفاعلية](https://nsh.dev/translations/ar/docs/reactivity.md)
