# 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](https://github.com/Oleafly/Oleafly/blob/main/README.md#architecture), günlük komutlar için [Geliştirme](/docs/engineering/development/) 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ı

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](#module-resolution)).

## 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](#the-contribution-registry)) | 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:

```bash
grep -rn 'from "@/\|@tauri\|zustand' packages/*/src   # must return nothing
```

## 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:

```ts
// 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

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

`@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ğu `panel` bileşeni.
- **Komutlar** (`registerCommand`): komut paletine (Cmd+K, `group` ile gruplanır) ve/veya `surfaces` aracı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:

```ts
// 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

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:

1. `tsconfig.json` → `compilerOptions.paths`. `@oleafly/preflight/pdf-extract` gibi alt yolların kendi açık girdisi olmalıdır.
2. `vite.config.ts` → `resolve.alias`. Dize takma adları önek olarak eşleşir; bu sayede alt yollar ek bir girdi olmadan çözümlenir.
3. `vitest.config.ts` → aynı `resolve.alias`, **ayrıca** `test.include` yolu `packages/**/*.test.ts` dosyaları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

- **Testler** kaynağın yanında yer alır (`packages/*/src/*.test.ts`) ve uygulama testleriyle aynı `pnpm test` komutunda ç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 mv` ile 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-later` lisanslıdır.

## 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.
