Frontend mimarisi
Oleafly'ın masaüstü uygulamasını frontend çalışma alanı, Rust servisleri ve sabit sürümlü sidecar'lar arasında nasıl böldüğü.
Oleafly’ın frontend’i bir pnpm çalışma alanıdır: uygulama kabuğu depo kökünde (src/), yeniden kullanılabilir frontend kodu ise packages/* altında odaklanmış on iki paket olarak yer alır. Tauri backend’i src-tauri/ içinde, yeniden kullanılabilir AI sağlayıcı ve agent kodu ise crates/oleafly-agent/ içinde bulunur. Frontend/Rust/sidecar genel görünümü için README’deki mimari diyagrama (yeni sekmede açılır), günlük komutlar için Geliştirme sayfasına bakın.
Bu belge şunları ele alır: her paketin ne olduğu, paketleri saf tutan bağımlılık tersine çevirme kuralları, özellikleri kabuğa bağlayan katkı kayıt sistemi, modül çözümlemesinin nasıl çalıştığı ve bir paketin nasıl eklenip çıkarılacağı.
Neden bir çalışma alanı
Bölüm başlığı “Neden bir çalışma alanı”Eskiden her özellik, uygulamanın Zustand store’larına, Tauri IPC istemcisine ve shadcn UI kitine doğrudan erişiyordu. Bu, özellikleri kabuktan ayrılmaz hale getiriyordu: hiçbir şey izole biçimde test edilemiyor, yeniden kullanılamıyor ya da tek başına değerlendirilemiyordu. Çalışma alanı bunu tersine çevirir. Paketler özellik mantığını barındırır ve ihtiyaç duydukları her şey için port adı verilen dar arayüzler tanımlar. Uygulama bu portları kendi store’ları ve Tauri istemcisi üzerinden uygular ve enjekte eder. Uygulama paketlere bağımlıdır, asla tersi değil.
Paketler TypeScript kaynağı olarak tüketilir (main alanları src/index.ts dosyasını gösterir). Derleme adımı yoktur, yayınlama yoktur, sürüm kayması yoktur: Vite, Vitest ve tsc, @oleafly/* paketlerini takma adlar (alias) aracılığıyla doğrudan paket kaynaklarına çözümler (bkz. Modül çözümlemesi).
Paketler
Bölüm başlığı “Paketler”| Paket | Ne olduğu | Enjekte edilen portlar | Önemli bağımlılıklar |
|---|---|---|---|
@oleafly/latex | Saf LaTeX/şekil mantığı: diyagram modeli, TikZ serileştiricisi (modelToTikz, gömülü model gidiş-gelişi), bağımsız belge oluşturucu (buildStandaloneDoc), şekil adı/bayt yardımcıları | yok (saf) | yok |
@oleafly/ai-core | Rust kataloğuyla eşitlik içinde tutulan sağlayıcı görüntüleme metaverisi ve frontend model seçimi | yok (saf) | yok |
@oleafly/latex-intelligence | Editör zekasının kullandığı LaTeX derlemi için doğrulanmış tipler ve veri erişimi | yok (saf) | yok |
@oleafly/registry | Katkı kayıt sistemi: ray sekmeleri, palet/omnibar komutları, AI araç takımları (ayrıntılar aşağıda) | yok (katkılar kendi davranışlarını taşır) | react (tipler) |
@oleafly/preflight | Ön denetim motoru: belge türü belirleme, kaynak kod/PDF/kaynakça kuralları, ATS ayrıştırma, erişilebilir dışa aktarma hazırlığı, puanlama. pdf-extract, @oleafly/preflight/pdf-extract alt yolu olarak dağıtılır; böylece pdf.js node test grafiklerinin dışında kalır | yok (saf) | pdfjs-dist (yalnızca alt yol) |
@oleafly/pdf-to-latex | Yerel PDF’den LaTeX’e yeniden yapılandırma işlem hattı: satır gruplama, başlık ve sütun algılama, stil çıkarımı, sembol eşleme, şekil çıkarma, kaçış işleme ve belge birleştirme. pdf.js adaptörü, aynı node-test-grafiği nedeniyle @oleafly/pdf-to-latex/pdf-adapter alt yolu olarak dağıtılır | yok (saf) | pdfjs-dist (yalnızca alt yol) |
@oleafly/wysiwyg | Kısıtlı görsel editör çekirdeği: paylaşılan şema, LaTeX ve Markdown ayrıştırma/serileştirme ve desteklenmeyen kaynağı atmak yerine görünür tutan ham satır içi/blok düğümler | yok (saf) | yok |
@oleafly/editor | LaTeX, Typst ve Markdown için CodeMirror çekirdeği: CodeMirrorEditor, editör görünümü denetleyicisi (insertAtCursor, gotoLine, …), diller + tamamlamalar, tema, katlama, yapışkan başlıklar, linter’lar, latex-mask, matematik önizlemesi (LaTeX için KaTeX, Typst için Typst), Typst düzenleme yardımcıları ve LaTeX’ten Typst’a matematik dönüştürmesi, LaTeX ve Typst için yerinde Görsel mod (@oleafly/editor/visual), arama paneli, yazım/dil bilgisi linter’ları | EditorHost (belge modeli + ayarlar, hook biçimli), SpellHost (yazım denetleyici/Harper/sözlük), setBibKeysProvider, extraExtensions/extraKeymap | @codemirror/*, @replit/codemirror-vim, @replit/codemirror-emacs, katex |
@oleafly/preview | Sanallaştırılmış pdf.js görüntüleyicisi (PdfViewer), SyncTeX sayfa denetleyicisi (gotoRect, pageClickToBp), pdf.js worker’ı ve WebView polyfill’leri (@oleafly/preview/polyfills, main.tsx içinde ilk içe aktarılan) | onOpenLink prop’u (sistem tarayıcısı bağlantıları), setPdfLogger | pdfjs-dist |
@oleafly/diagram | TikZ, Typst (fletcher) ve Mermaid için görsel diyagram oluşturucu: React Flow tuvali, şekil denetçisi, Çiz/Kod sekmeleri, derle-önizle-ekle akışı. src/languages/ içindeki her dilin bir yazıcısı, bir okuyucusu ve kendi ekleme ve dosya biçimleri vardır; tuval ve kod iki yönde de eşzamanlı yürür | DiagramHost (LaTeX derleme, Typst ve Mermaid işleme, dosya GÇ’si, editöre ekleme, AI düzeltme), DiagramKit bağlamı (Button/Tooltip/Select/toast/theme) | @xyflow/react, @codemirror/*, @oleafly/latex, @oleafly/editor |
@oleafly/ai-tools | Onay farkındalı mutasyonlarla proje, araştırma, bellek ve şekil araç tanımları | AiToolsHost (dosyalar, derleme, sembol dizini, şekil işlem hattı, editör) | @oleafly/latex |
@oleafly/templates | Yeni proje şablon galerisi (iki adımlı sihirbaz, kategoriler, önizlemeler, ilerleme gösteren tek seferlik varlık indirmeleri) | TemplatesHost (önizlemeler, varlık indirmeleri, günlükleme), TemplatesKit (Button/Tooltip), prop olarak renk seçenekleri | UI yardımcıları dışında yok |
Bağımlılık yönü, katı biçimde zorunlu kılınır:
Bir paket asla src/ konumundan (@/…), bir Zustand store’undan, @tauri-apps/* paketlerinden veya uygulamanın UI bileşenlerinden içe aktarma yapmamalıdır. Saflık denetimi, çıkarma temizliğinin bir parçasıdır:
grep -rn 'from "@/\|@tauri\|zustand' packages/*/src # must return nothingPort deseni
Bölüm başlığı “Port deseni”Paketler arayüzleri tanımlar. Uygulama bunları genellikle kendi store’larını sarmalayan modül düzeyinde bir adaptör nesnesi olarak bir kez uygular. Sık görülen dört biçim:
Host portları: her paket için, ihtiyaç duyduğu servisleri kapsayan tek bir interface XHost. Uygulama, adaptörü store’lara ilişkin imperatif okumalarla kurar:
// 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);Yalnızca uygulamaya ait kaygıları adaptörün içinde tutmak, bağımlılıkların paketlere sızmasını da önler. Sağlayıcı kimlik bilgileri ve istekleri Rust tarafında kalır; böylece frontend paketleri yalnızca arayüzlerinin gerektirdiği durumu ve sonuçları alır.
UI kitleri: yapısal tipli bileşenlerden oluşan bir React bağlamı (veya prop): Button: ComponentType<{ variant?: …; onClick?: () => void }>. Uygulama, shadcn bileşenlerini değiştirmeden geçirir, çünkü daha küçük yapısal tip uyumludur. Kitler ayrıca toast ve bir useThemeMode() hook’u taşır.
Hook biçimli portlar: uygulama durumuna abone olması gereken paketler için. Editörün EditorHost’u, olay dinleyicileri için hook üyeleri (useActivePath(), useSettings()) ve bunların imperatif eşlerini (getActivePath()) içerir. Kurallar: host nesnesi modül düzeyinde olmalıdır (kararlı kimlik) ve use* üyeleri hook’ları koşulsuz çağırmalıdır.
Modül tekil örnekleri: yatay kesen yapıştırıcı kod için setPdfLogger(fn), setSpellHost(host), setBibKeysProvider(fn) hiçbir şey yapmayan varsayılanlara sahiptir ve uygulama shim’i tarafından bir içe aktarma yan etkisi olarak kurulur.
Shim’ler
Bölüm başlığı “Shim’ler”Her çıkarma işlemi, paketi yeniden dışa aktaran (veya sarmalayan) eski @/… yolunda ince bir dosya bıraktı; böylece tüketiciler ve mevcut vi.mock(...) yolları hiç değişmedi. Örnekler: src/components/editor/cm/controller.ts (yeniden dışa aktarma), src/components/pdf/PdfViewer.tsx (kabuk eklentisinin bağlantı açıcısını enjekte eden sarmalayıcı), src/lib/ai-tools.ts (adaptör + özgün fabrika imzaları). Yeni kod @oleafly/* paketlerini doğrudan içe aktarabilir. Shim’ler uyumluluk için vardır, tercih edilen yol olarak değil.
Katkı kayıt sistemi
Bölüm başlığı “Katkı kayıt sistemi”@oleafly/registry, özelliklerin kabuğun onları tanımasına gerek kalmadan uygulama kabuğuna takılmasını sağlar. Üç katkı türü vardır:
- Ray sekmeleri (
registerRailTab): simge, bölüm (explore/review/assist, boş olmayan bölümler arasına ayraç çizilir), isteğe bağlıwhen(ctx)görünürlüğü, isteğe bağlıuseBadge()hook’u (ör. Git değişiklik sayısı) ve kenar çubuğupanelbileşeni. - Komutlar (
registerCommand): komut paletine (Cmd+K,groupile gruplanır) ve/veyasurfacesaracılığıyla omnibara (Cmd+P) sunulur. Etiketler ve simgeler bağlamın işlevleri olabilir (ör. “Açık temaya geç”). - AI araç takımları (
registerAiToolset): bir sohbet modu ("chat","figure") bir araç takımı fabrikasına eşlenir. Sohbet paneli, sabit kodlanmış bir takım yerine etkin modun araç takımını arar.
AppContext ({ projectId, projectKind, theme }), her işleme yüzeyi tarafından kendi aboneliklerinden oluşturulur; böylece when/label koşulları tepkisel kalır.
Kayıt statiktir: src/contributions/ tüm yerleşikleri (sekmeler, komutlar, araç takımları) kaydeder ve main.tsx, kabuk bağlanmadan önce registerContributions() işlevini bir kez çağırır. Rail, Sidebar, CommandPalette, SearchOmnibar ve ChatPanel yalnızca kayıtlı olanı işler.
Bir özelliğin sekmesini veya komutunu eklemek için onu kaydedin. Kabukta değişiklik gerekmez:
// 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"),});Bu dahili bir API’dir, eklenti SDK’sı değildir: biçimler serbestçe değişebilir ve hiçbir şey dinamik olarak yüklenmez. Gerçek bir tüketici ortaya çıkana dek ertelenenler: dışa aktarma menüsü biçimleri, ayar şemaları ve proje türü davranışlarının katkı olarak sunulması.
Modül çözümlemesi
Bölüm başlığı “Modül çözümlemesi”Paketler kaynak olarak tüketildiği için aynı takma ad üç yerde bildirilir ve bir paket eklendiğinde üçünün de eşitlemiş olması gerekir:
tsconfig.json→compilerOptions.paths.@oleafly/preflight/pdf-extractgibi alt yolların kendi açık girdisi olmalıdır.vite.config.ts→resolve.alias. Dize takma adları önek olarak eşleşir; bu sayede alt yollar ek bir girdi olmadan çözümlenir.vitest.config.ts→ aynıresolve.alias, ayrıcatest.includeyolupackages/**/*.test.tsdosyalarını kapsamalıdır. Aksi takdirde taşınan testler sessizce çalışmanın dışında kalır.
Ayrıca: kök package.json, her pakete "workspace:*" üzerinden bağımlıdır ve src/styles/globals.css içinde @source "../../packages" bulunduğundan Tailwind v4 sınıf adları için paket kaynaklarını tarar.
Yalnızca Vite’e özgü içe aktarımlar (?worker&url worker’ları, CSS içe aktarımları), takma adlar kaynağı gösterdiği için paket kaynaklarından değişmeden çalışır. pdf.js’yi peşine takan her şeyi, node ortamındaki testlerin içe aktardığı barrel dosyalarının dışında tutun: pdf-extract ile önizleme paketinin görüntüleyicisinin yalnızca DOM’un var olduğu yerden erişilebilir olmasının nedeni budur.
Paketler üzerinde çalışmak
Bölüm başlığı “Paketler üzerinde çalışmak”- Testler kaynağın yanında yer alır (
packages/*/src/*.test.ts) ve uygulama testleriyle aynıpnpm testkomutunda çalışır. Dosya taşırken test sayısını izleyin. - Bağımlılıklar gevşek bildirilir (
"react": "*"). pnpm, gerçek sürüm tabanlarının bulunduğu köke göre tekilleştirme yapar. Paketin içe aktarmadığı bağımlılıkları bildirmeyin. - Bir pakette yapılan her değişiklik için geçitler:
pnpm exec tsc --noEmit,pnpm build,pnpm test(değişmeyen test sayısı) ve yukarıdaki saflık grep’i. - Yeni bir şey çıkarmak: dosyaları
git mvile taşıyın (geçmiş izler), portları tanımlayın, eski yollarda shim’ler bırakın, üç takma adı bağlayın, ardından geçitleri çalıştırın. Üzerinde etkin olarak iterasyon yaptığınız bir özelliği o sırada çıkarmayın. - Lisanslama: her paket, uygulama gibi
AGPL-3.0-or-laterlisanslıdır.
Uygulamada kalanlar
Bölüm başlığı “Uygulamada kalanlar”src/, gerçekten frontend uygulaması olan her şeyi barındırır: Zustand store’ları, Tauri IPC istemcisi (src/lib/tauri.ts), shadcn UI kiti, düzen/kabuk bileşenleri, port adaptörleri ve shim’ler, src/contributions/ ve doğası gereği store’a bağlı özellik yapıştırıcıları (ön denetim paneli arayüzü, satır içi AI düzenleme eklentisi ve editöre extraExtensions olarak enjekte edilen kod ya da üzerine gelme zekası). Tauri komutları ve işletim sistemi entegrasyonu src-tauri/ içinde kalır. Sağlayıcı istemcileri, akış ve agent orkestrasyonu crates/oleafly-agent/ içinde yer almalıdır; böylece gizli bilgiler ve sağlayıcı istekleri webview dışında kalır.