تخطَّ إلى المحتوى
Oleaflyv0.4.7

بنية الواجهة الأمامية

كيف يوزّع Oleafly تطبيق سطح المكتب بين مساحة عمل الواجهة الأمامية، وخدمات Rust، والبرامج المرافقة ذات الإصدارات المثبّتة.

الواجهة الأمامية لـ Oleafly هي مساحة عمل pnpm: يعيش هيكل التطبيق في جذر المستودع (src/)، ويعيش رمز الواجهة الأمامية القابل لإعادة الاستخدام في packages/* على هيئة اثنتي عشرة حزمة مركّزة. وتعيش خلفية Tauri في src-tauri/، بينما يعيش رمز مزوّدي الذكاء الاصطناعي والوكيل القابل لإعادة الاستخدام في crates/oleafly-agent/. انظر مخطط البنية في README (يفتح في علامة تبويب جديدة) للاطلاع على صورة الواجهة الأمامية/Rust/البرامج المرافقة، وانظر التطوير لأوامر العمل اليومية.

يغطّي هذا المستند: ما هي كل حزمة، وقواعد عكس التبعيات التي تحافظ على نقائها، وسجل المساهمات الذي يربط الميزات بالهيكل، وكيفية عمل تحليل الوحدات، وكيفية إضافة حزمة أو استخراجها.

كانت كل ميزة في السابق تصل مباشرة إلى مخازن Zustand في التطبيق، وعميل Tauri IPC، وطقم واجهة shadcn. وقد جعل ذلك الميزات غير قابلة للفصل عن الهيكل: فلم يعد بالإمكان اختبار أي شيء أو إعادة استخدامه أو التفكير فيه على نحو معزول. تقلب مساحة العمل هذا الوضع. تحمل الحزم منطق الميزات وتصرّح بواجهات ضيقة تُسمى المنافذ لكل ما تحتاجه. ينفّذ التطبيق تلك المنافذ فوق مخازنه وعميل Tauri ثم يحقنها. يعتمد التطبيق على الحزم، ولا يحدث العكس أبدًا.

تُستهلك الحزم كمصدر TypeScript (يشير حقل main في كل منها إلى src/index.ts). لا توجد خطوة بناء، ولا نشر، ولا انحراف في الإصدارات: يحلّ كل من Vite وVitest وtsc الرموز @oleafly/* مباشرة إلى مصادر الحزم عبر الأسماء البديلة (انظر تحليل الوحدات).

الحزمةما هيالمنافذ المحقونةتبعيات بارزة
@oleafly/latexمنطق LaTeX والأشكال النقي: نموذج المخطط، ومسلسل TikZ (modelToTikz، ودورة ذهاب وعودة للنماذج المضمّنة)، وباني المستند المستقل (buildStandaloneDoc)، ومساعدات أسماء الأشكال وأحجامها بالبايتاتلا شيء (نقي)لا شيء
@oleafly/ai-coreبيانات عرض المزوّدين واختيار النماذج في الواجهة الأمامية، مع الحفاظ على تطابقها مع فهرس Rustلا شيء (نقي)لا شيء
@oleafly/latex-intelligenceأنواع متحقَّق منها ووصول إلى البيانات لمدونة LaTeX التي تستخدمها ذكاء المحررلا شيء (نقي)لا شيء
@oleafly/registryسجل المساهمات: علامات تبويب الشريط، وأوامر لوحة الأوامر/شريط البحث الشامل، وأطقم أدوات الذكاء الاصطناعي (التفاصيل أدناه)لا شيء (المساهمات تحمل سلوكها معها)react (الأنواع)
@oleafly/preflightمحرك الفحص المسبق: تصنيف المستندات، وقواعد المصدر/PDF/المراجع، وتحليل ATS، وتجهيز التصدير الميسّر، والتقييم. يُشحن pdf-extract كمسار فرعي @oleafly/preflight/pdf-extract ليبقى pdf.js خارج مخططات اختبار nodeلا شيء (نقي)pdfjs-dist (المسار الفرعي فقط)
@oleafly/pdf-to-latexمسار إعادة بناء PDF إلى LaTeX المحلي: تجميع الأسطر، وكشف العناوين والأعمدة، واستنباط الأنماط، وربط الرموز، واستخراج الأشكال، وتهريب المحارف، وتجميع المستند. يُشحن محوّل pdf.js كمسار فرعي @oleafly/pdf-to-latex/pdf-adapter للسبب نفسه المتعلق بمخطط اختبار nodeلا شيء (نقي)pdfjs-dist (المسار الفرعي فقط)
@oleafly/wysiwygنواة المحرر المرئي المقيّد: المخطط المشترك، وتحليل LaTeX وMarkdown وتسلسلهما، وعقد السطر والكتلة الخام التي تُبقي المصدر غير المدعوم مرئيًا بدلًا من إهمالهلا شيء (نقي)لا شيء
@oleafly/editorنواة CodeMirror لـ LaTeX وTypst وMarkdown: CodeMirrorEditor، ومتحكم عرض المحرر (insertAtCursor، gotoLine، …)، واللغات مع الإكمالات، والمظهر، والطي، والعناوين اللاصقة، وأدوات الفحص، وقناع latex-mask، ومعاينة الرياضيات (KaTeX لـ LaTeX، وTypst لـ Typst)، ومساعدات تحرير Typst وتحويل الرياضيات من LaTeX إلى Typst، ووضع Visual في الموضع لـ LaTeX وTypst (@oleafly/editor/visual)، ولوحة البحث، وأدوات فحص الإملاء والقواعدEditorHost (نموذج المستند + الإعدادات، بشكل خطافات)، SpellHost (المدقق الإملائي/Harper/القاموس)، setBibKeysProvider، extraExtensions/extraKeymap@codemirror/*, @replit/codemirror-vim, @replit/codemirror-emacs, katex
@oleafly/previewعارض pdf.js الافتراضي (PdfViewer)، ومتحكم صفحات SyncTeX (gotoRect، pageClickToBp)، وعامل pdf.js، وسدّادات WebView (@oleafly/preview/polyfills، تُستورد أولًا في main.tsx)خاصية onOpenLink (روابط متصفح النظام)، setPdfLoggerpdfjs-dist
@oleafly/diagramمؤلّف المخططات المرئي لـ TikZ وTypst (fletcher) وMermaid: لوحة React Flow، ومفتّش الأشكال، وعلامتا تبويب الرسم/الشيفرة، وتدفق التجميع ثم المعاينة ثم الإدراج. لكل لغة في src/languages/ كاتب وقارئ وصيغتا إدراج وملف خاصتان بها، وتبقى اللوحة والشيفرة متزامنتين في الاتجاهينDiagramHost (تجميع LaTeX، وعرض Typst وMermaid، وإدخال/إخراج الملفات، والإدراج في المحرر، والإصلاح بالذكاء الاصطناعي)، وسياق DiagramKit (Button/Tooltip/Select/toast/theme)@xyflow/react, @codemirror/*, @oleafly/latex, @oleafly/editor
@oleafly/ai-toolsتعريفات أدوات المشروع والبحث والذاكرة والأشكال مع تعديلات واعية بالموافقةAiToolsHost (الملفات، والتجميع، وفهرس الرموز، ومسار الأشكال، والمحرر)@oleafly/latex
@oleafly/templatesمعرض قوالب المشاريع الجديدة (معالج من خطوتين، وفئات، ومعاينات، وتنزيلات لمرة واحدة للموارد مع إظهار التقدم)TemplatesHost (المعاينات، وتنزيل الموارد، والتسجيل)، TemplatesKit (Button/Tooltip)، وخيارات الألوان كخصائصلا شيء عدا أدوات واجهة المستخدم

اتجاه التبعيات، مفروض بصرامة:

كيف يعتمد هيكل التطبيق والحزم بعضهما على بعضيضم هيكل التطبيق في src/ مخازن Zustand ومجموعة عناصر الواجهة وعميل Tauri ومحوّلات المنافذ. تنفّذ المحوّلات المنافذ التي تعلنها حزم الميزات @oleafly، مثل EditorHost، ويحقنها التطبيق. ويجوز لحزم الميزات أن تعتمد على حزم طرفية نقية مثل latex وai-core وregistry. ويتحدث عميل Tauri إلى الواجهة الخلفية بلغة Rust عبر Tauri IPC. ولا تستورد الحزم التطبيق أبدًا، ولا مخازنه، ولا واجهات Tauri البرمجية.هيكل التطبيق، src/حزم @oleafly/*المخازنZustandالمحوّلاتتنفّذ المنافذأدوات الواجهةshadcnعميل Taurisrc/lib/tauri.tsحزم الميزاتتعلن منافذ مثل EditorHostالحزم الطرفيةlatex, ai-core, registryالواجهة الخلفية بـ Rustsrc-tauri, crates/oleafly-agentيحقنTauri IPCلا تستورد الحزم التطبيق ولا مخازنه ولا واجهات Tauri البرمجية أبدًا.كيف يعتمد هيكل التطبيق والحزم بعضهما على بعضيضم هيكل التطبيق في src/ مخازن Zustand ومجموعة عناصر الواجهة وعميل Tauri ومحوّلات المنافذ. تنفّذ المحوّلات المنافذ التي تعلنها حزم الميزات @oleafly، مثل EditorHost، ويحقنها التطبيق. ويجوز لحزم الميزات أن تعتمد على حزم طرفية نقية مثل latex وai-core وregistry. ويتحدث عميل Tauri إلى الواجهة الخلفية بلغة Rust عبر Tauri IPC. ولا تستورد الحزم التطبيق أبدًا، ولا مخازنه، ولا واجهات Tauri البرمجية.هيكل التطبيق، src/حزم @oleafly/*المخازنأدوات الواجهةعميل Tauriالمحوّلاتالواجهة الخلفية بـ Rustsrc-tauri, cratesحزم الميزاتتعلن المنافذالحزم الطرفيةlatex, ai-core, registryTauri IPCيحقنلا تستورد الحزم التطبيقولا مخازنه ولا واجهات Tauri البرمجية.
يعتمد التطبيق على الحزم ويحقن محوّلات للمنافذ التي تعلنها. ولا تستورد الحزم التطبيق أبدًا.

يجب ألا تستورد أي حزمة أبدًا من src/ (@/…)، ولا من مخزن Zustand، ولا من @tauri-apps/*، ولا من مكونات واجهة التطبيق. فحص النقاء جزء من نظافة الاستخراج:

نافذة المحطة الطرفية
grep -rn 'from "@/\|@tauri\|zustand' packages/*/src # must return nothing

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

منافذ المضيف: واجهة interface XHost واحدة لكل حزمة تغطّي الخدمات التي يحتاجها. يبني التطبيق المهايئ بقراءات أمرية من المخازن:

// src/lib/ai-tools.ts (app adapter, abridged)
const HOST: AiToolsHost = {
getProjectId: () => useFilesStore.getState().projectId,
readFileContent, // Tauri client passthrough
recompile: () => useCompileStore.getState().recompile(),
insertAtCursor, // editor controller
// ...
};
export const createOleaflyTools = (opts?) => createOleaflyToolsCore(HOST, opts);

إبقاء الشواغل الخاصة بالتطبيق وحده داخل المهايئ يُبقي التبعيات خارج الحزم أيضًا. تبقى بيانات اعتماد المزوّدين والطلبات في Rust، فلا تتلقى حزم الواجهة الأمامية إلا الحالة والنتائج التي تتطلبها واجهاتها.

أطقم واجهة المستخدم: سياق React (أو خاصية) من مكونات ذات أنواع بنيوية: Button: ComponentType<{ variant?: …; onClick?: () => void }>. يمرّر التطبيق مكونات shadcn دون تغيير لأن النوع البنيوي الأصغر متوافق. تحمل الأطقم أيضًا toast وخطاف useThemeMode().

منافذ بشكل خطافات: للحزم التي يجب أن تشترك في حالة التطبيق. يملك EditorHost الخاص بالمحرر أعضاء على هيئة خطافات (useActivePath()، useSettings()) إضافة إلى توائم أمرية (getActivePath()) لمستمعي الأحداث. القواعد: يجب أن يكون كائن المضيف على مستوى الوحدة (بهوية مستقرة)، ويجب أن تستدعي أعضاء use* فيه الخطافات دون قيد.

مفردات على مستوى الوحدة: للرابط العابر للمجالات؛ تملك setPdfLogger(fn) وsetSpellHost(host) وsetBibKeysProvider(fn) قيمًا افتراضية لا تفعل شيئًا، ويثبّتها مهايئ التطبيق كأثر جانبي للاستيراد.

تركت كل عملية استخراج ملفًا رقيقًا عند مسار @/… القديم يعيد تصدير الحزمة (أو يغلّفها)، بحيث لم يضطر المستهلكون ومسارات vi.mock(...) القائمة إلى التغيير مطلقًا. أمثلة: src/components/editor/cm/controller.ts (إعادة تصدير)، وsrc/components/pdf/PdfViewer.tsx (غلاف يحقن فاتح الروابط الخاص بملحق الهيكل)، وsrc/lib/ai-tools.ts (مهايئ + تواقيع المصنع الأصلية). يمكن للشيفرة الجديدة استيراد @oleafly/* مباشرة. توجد الملفات البديلة لأغراض التوافق، وليست المسار المفضّل.

@oleafly/registry هي الطريقة التي تتصل بها الميزات بهيكل التطبيق من دون أن يعرفها الهيكل. ثلاثة أنواع من المساهمات:

  • علامات تبويب الشريط (registerRailTab): الأيقونة، والقسم (explore / review / assist، مع فواصل تُرسم بين الأقسام غير الفارغة)، وإظهار when(ctx) الاختياري، وخطاف useBadge() الاختياري (مثل عدد تغييرات Git)، ومكوّن panel للشريط الجانبي.
  • الأوامر (registerCommand): تُقدَّم إلى لوحة الأوامر (Cmd+K، مجمّعة حسب group) و/أو شريط البحث الشامل (Cmd+P) عبر surfaces. وقد تكون التسميات والأيقونات دوال للسياق (مثل «التبديل إلى المظهر الفاتح»).
  • أطقم أدوات الذكاء الاصطناعي (registerAiToolset): وضع محادثة ("chat"، "figure") يقترن بمصنع طقم أدوات. تبحث لوحة المحادثة عن طقم أدوات الوضع النشط بدلًا من تثبيته في الشيفرة.

يبني كل سطح عرض AppContext ({ projectId, projectKind, theme }) من اشتراكاته الخاصة، فتبقى شروط when/label تفاعلية.

التسجيل ثابت: يسجّل src/contributions/ كل ما هو مدمج (علامات التبويب والأوامر وأطقم الأدوات)، وتستدعي main.tsx الدالة registerContributions() مرة واحدة قبل تحميل الهيكل. ولا تعرض Rail وSidebar وCommandPalette وSearchOmnibar وChatPanel إلا ما هو مسجّل.

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

// src/contributions/my-feature.tsx (called from src/contributions/index.ts)
registerRailTab({
id: "review", label: "Review", icon: MessageSquare,
section: "review", order: 55,
when: (ctx) => ctx.projectKind !== "image",
panel: ReviewPanel,
});
registerCommand({
id: "omnibar.review", surfaces: ["omnibar"], order: 45,
label: "Open review", keywords: "review comments",
run: () => useSettingsStore.getState().setRailTab("review"),
});

هذه واجهة برمجية داخلية، وليست حزمة تطوير إضافات: قد تتغير الأشكال بحرية، ولا يُحمَّل أي شيء ديناميكيًا. وأُجِّل إلى حين وجود مستهلك حقيقي: تنسيقات قائمة التصدير، ومخططات الإعدادات، وسلوكيات أنواع المشاريع بوصفها مساهمات.

لأن الحزم تُستهلك كمصدر، يُصرَّح بالاسم البديل نفسه في ثلاثة أماكن، ويجب أن تبقى الثلاثة متزامنة عند إضافة حزمة:

  1. tsconfig.json → compilerOptions.paths. تحتاج المسارات الفرعية مثل @oleafly/preflight/pdf-extract إلى مدخل صريح خاص بها.
  2. vite.config.ts → resolve.alias. الأسماء البديلة النصية تطابق البادئات، فتُحلّ المسارات الفرعية دون مدخل آخر.
  3. vitest.config.ts → اسم resolve.alias نفسه، إضافةً إلى أن test.include يجب أن يغطي packages/**/*.test.ts. وإلا انسحبت الاختبارات المنقولة من التشغيل بصمت.

أيضًا: يعتمد package.json في الجذر على كل حزمة عبر "workspace:*"، ويحتوي src/styles/globals.css على @source "../../packages" ليفحص Tailwind v4 مصادر الحزم بحثًا عن أسماء الأصناف.

تعمل الاستيرادات الخاصة بـ Vite وحده (العمّال بصيغة ?worker&url، واستيرادات CSS) دون تغيير من مصادر الحزم لأن الأسماء البديلة تشير إلى المصدر. أبقِ أي شيء يجذب pdf.js خارج ملفات barrel التي تستوردها اختبارات بيئة node: لهذا لا يمكن الوصول إلى pdf-extract وعارض حزمة المعاينة إلا حيث توجد DOM.

  • الاختبارات تعيش بجوار المصدر (packages/*/src/*.test.ts) وتعمل ضمن أمر pnpm test نفسه مع اختبارات التطبيق. راقب عدد الاختبارات عند نقل الملفات.
  • التبعيات يُصرَّح بها بتساهل ("react": "*"). يزيل pnpm التكرار مقابل الجذر، حيث توجد الحدود الدنيا الحقيقية للإصدارات. لا تصرّح بتبعيات لا تستوردها الحزمة.
  • بوابات أي تغيير في حزمة: pnpm exec tsc --noEmit، وpnpm build، وpnpm test (بالعدد نفسه)، وفحص grep للنقاء أعلاه.
  • استخراج شيء جديد: انقل الملفات عبر git mv (يتتبعها السجل)، وحدّد المنافذ، واترك ملفات بديلة عند المسارات القديمة، واربط الأسماء البديلة الثلاثة، ثم شغّل البوابات. لا تستخرج ميزة أثناء تطويرك النشط عليها.
  • الترخيص: كل حزمة AGPL-3.0-or-later، مثل التطبيق نفسه.

يحتفظ src/ بكل ما هو حقًا تطبيق الواجهة الأمامية: مخازن Zustand، وعميل Tauri IPC (src/lib/tauri.ts)، وطقم واجهة shadcn، ومكونات التخطيط/الهيكل، ومهايئات المنافذ والملفات البديلة، وsrc/contributions/، والشيفرة الرابطة للميزات المرتبطة بالمخازن بطبيعتها (واجهة لوحة Preflight، وملحق التحرير بالذكاء الاصطناعي المضمّن، وذكاء الشيفرة أو التحويم المحقون في المحرر بوصفه extraExtensions). تبقى أوامر Tauri وتكامل نظام التشغيل في src-tauri/. أما عملاء المزوّدين والبث وتنسيق الوكيل فمكانها crates/oleafly-agent/ لتبقى الأسرار وطلبات المزوّدين خارج الـ webview.

معاينة الصورة

تعذّر تحميل إجابات الذكاء الاصطناعي

حاول مرة أخرى، أو ابحث في الوثائق.