# CLAUDE.md — dreport ## Proje Ozeti **dreport**, kullanicilarin fatura, irsaliye, rapor gibi belge sablonlarini gorsel bir drag & drop editor ile tasarlayip, JSON veri ile birlestirerek PDF cikti almasini saglayan bir belge tasarim aracidir. Temel fark: Editorde ayri bir canvas render engine (fabric.js, konva.js vb.) KULLANILMAZ. Bunun yerine custom bir layout engine (taffy + cosmic-text) kullanilir. Ayni layout engine hem editorde onizleme (HTML div'ler) hem backend'de PDF uretimi (krilla) icin calisir. Bu sayede "editorde gordugum ile PDF'te aldigim farkli" sorunu ortadan kalkar. --- ## Teknoloji Kararlari | Katman | Teknoloji | Gerekce | | ----------------- | ------------------------------------ | ---------------------------------------------------------------- | | Frontend | Vue 3 (Composition API) + TypeScript | Kullanici tercihi | | Layout Engine | taffy (flexbox) + cosmic-text | Template JSON → hesaplanmis pozisyonlar; hem WASM hem native | | Editor Render | HTML div'ler (LayoutRenderer.vue) | Layout engine sonuclarina gore CSS ile render | | Etkilesim Katmani | DOM overlay (Vue bilesenleri) | Layout sonuclari uzerine secim, surekleme, yeniden boyutlandirma | | Backend | Rust + Axum | Layout engine'i dogrudan kullanabilme; performans | | PDF Render | krilla (server-side) | LayoutResult → PDF; font tutarliligi garantisi | | Veri Formati | JSON (sablon tanimi + veri) | Evrensel, kolay serialize/deserialize | | Paket Yonetimi | bun (frontend), cargo (backend) | — | --- ## Mimari Genel Bakis ``` ┌──────────────────────────────────────────────────────┐ │ VUE FRONTEND │ │ │ │ ┌───────────────┐ ┌───────────────────────────┐ │ │ │ Sol Panel │ │ Editor Canvas │ │ │ │ - Bilesenler │ │ │ │ │ │ - Schema Tree │ │ ┌─────────────────────┐ │ │ │ │ - Ozellikler │ │ │ LayoutRenderer.vue │ │ │ │ │ │ │ │ (HTML div render) │ │ │ │ │ │ │ └─────────────────────┘ │ │ │ │ │ │ ┌─────────────────────┐ │ │ │ │ │ │ │ InteractionOverlay │ │ │ │ │ │ │ │ secim / drag / resize│ │ │ │ │ │ │ └─────────────────────┘ │ │ │ └───────────────┘ └───────────────────────────┘ │ │ │ │ Template JSON → layout-engine WASM → LayoutResult │ │ → LayoutRenderer (HTML) + Overlay │ └──────────────────────────┬───────────────────────────┘ │ POST /api/render ▼ ┌──────────────────────────────────────────────────────┐ │ RUST BACKEND (Axum) │ │ │ │ Template JSON + Data JSON │ │ → compute_layout() (taffy + cosmic-text) │ │ → render_pdf() (krilla) │ │ → PDF bytes │ └──────────────────────────────────────────────────────┘ ``` --- ## Render Pipeline ### Temel Prensip Typst KULLANILMAZ. Bunun yerine custom layout engine: 1. Kullanicinin tasarimi bir **Template JSON** olarak tutulur. 2. Template JSON + Data JSON, **layout-engine** WASM modulu ile tarayicide islenir → **LayoutResult** uretilir. 3. LayoutResult, her elemanin mutlak pozisyonunu (x, y, width, height mm cinsinden) icerir. 4. **LayoutRenderer.vue** bu sonuclari HTML div'ler olarak render eder. 5. **InteractionOverlay.vue** ayni pozisyon bilgisiyle secim, drag, resize handle'lar koyar. 6. Kullanici bir elemani suruklediqinde → Template JSON guncellenir → layout-engine yeniden calisir → HTML guncellenir. ### Performans Stratejisi - **Drag sirasinda:** Overlay katmaninda sadece CSS transform ile gorsel geri bildirim ver. Layout engine calistirma. - **Drag bittiginde (mouseup/pointerup):** Template JSON'i guncelle, layout engine calistir, HTML'i yenile. - **Debounce:** Ozellik panelinden yapilan degisikliklerde 150-300ms debounce ile hesapla. - **Web Worker:** Layout engine WASM'i bir Web Worker icinde calistir. Ana thread'i ASLA bloklamayacak. ### Layout Engine (layout-engine crate) ```rust // Temel kullanim use dreport_layout::{compute_layout, LayoutResult}; let layout: LayoutResult = compute_layout(&template, &data, &fonts); // layout.pages[0].elements → her elemanin x, y, width, height (mm) ``` WASM tarafinda (frontend): ```typescript // layout.worker.ts icinde import init, { computeLayout, loadFonts } from "dreport-layout-wasm"; await init(); await loadFonts(fontBytes); const layoutJson = computeLayout(templateJson, dataJson); ``` - WASM modulu: ~1-2 MB (vs eski Typst 8MB) - Fontlar: Ayni Noto Sans seti (~4-5 MB), font olcum icin gerekli. --- ## Veri Modeli ### Layout Sistemi: Container-Based CSS Flexbox mantigina benzeyen container-based layout: - **Sayfa = kok container.** `page.margins` → kok container'in `padding`'i olur. - **Container'lar ic ice gecebilir.** `direction: "row" | "column"` ile yatay/dikey dizilim. - **Elemanlar varsayilan olarak flow icindedir** — otomatik pozisyonlanir. - **Opsiyonel absolute positioning:** Kullanici isterse bir elemani `position: "absolute"` yapabilir. Bu sayede: - Tablo satirlari artarsa alttaki elemanlar otomatik kayar. - Ayni satira iki kolon koymak icin ic ice container yeterlidir. - Absolute mod ile serbest pozisyonlama da mumkundur. ### Boyut Sistemi (SizeValue) Her eleman ve container icin `width` ve `height` su tiplerden biri olabilir: | Tip | Aciklama | Taffy karsiligi | | ------- | -------------------------- | ----------------------------- | | `fixed` | Sabit boyut (mm) | `Dimension::Length(pt)` | | `auto` | Iceriqe gore otomatik | `Dimension::Auto` | | `fr` | Kalan alani oransal doldur | `flex_grow: n, flex_basis: 0` | Ek olarak `minWidth`, `maxWidth`, `minHeight`, `maxHeight` (mm) desteklenir. ### Template JSON (Sablon Tanimi) ```jsonc { "id": "tpl_fatura_001", "name": "Standart Fatura", "page": { "width": 210, "height": 297 }, "fonts": ["Noto Sans", "Noto Sans Mono"], "root": { "id": "root", "type": "container", "position": { "type": "flow" }, "size": { "width": { "type": "auto" }, "height": { "type": "auto" } }, "direction": "column", "gap": 5, "padding": { "top": 15, "right": 15, "bottom": 15, "left": 15 }, "align": "stretch", "justify": "start", "style": {}, "children": [ { "id": "c_header", "type": "container", "position": { "type": "flow" }, "size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" }, }, "direction": "row", "gap": 5, "padding": { "top": 0, "right": 0, "bottom": 0, "left": 0 }, "align": "start", "justify": "start", "style": {}, "children": [ { "id": "el_firma", "type": "text", "position": { "type": "flow" }, "size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" }, }, "style": { "fontSize": 14, "fontWeight": "bold" }, "binding": { "type": "scalar", "path": "firma.unvan" }, }, { "id": "el_fatura_baslik", "type": "static_text", "position": { "type": "flow" }, "size": { "width": { "type": "auto" }, "height": { "type": "auto" }, }, "style": { "fontSize": 12, "fontWeight": "bold", "align": "right" }, "content": "FATURA", }, ], }, { "id": "el_cizgi", "type": "line", "position": { "type": "flow" }, "size": { "width": { "type": "fr", "value": 1 }, "height": { "type": "auto" }, }, "style": { "strokeColor": "#000000", "strokeWidth": 0.5 }, }, ], }, } ``` ### Eleman Tipleri | Tip | Aciklama | Binding | | ----------------- | ----------------------------------------- | ---------------- | | `container` | Duzen kutusu, cocuk elemanlari barindirir | Yok | | `static_text` | Sabit metin, veri baglantisi yok | Yok | | `text` | Dinamik metin, schema'dan veri ceker | Scalar | | `repeating_table` | Array verisinden tekrarlayan tablo | Array | | `line` | Yatay/dikey cizgi | Yok | | `image` | Statik veya dinamik gorsel | Opsiyonel scalar | | `page_number` | Sayfa numarasi (cok sayfali belgeler) | Otomatik | ### Container Ozellikleri | Ozellik | Tip | Aciklama | | ----------- | ------------------------------------------------------------- | ------------------------------- | | `direction` | `"row"` \| `"column"` | Cocuklari yatay mi dikey mi diz | | `gap` | number (mm) | Cocuklar arasi bosluk | | `padding` | `{ top, right, bottom, left }` (mm) | Ic bosluk | | `align` | `"start"` \| `"center"` \| `"end"` \| `"stretch"` | Cross-axis hizalama | | `justify` | `"start"` \| `"center"` \| `"end"` \| `"space-between"` | Main-axis dagilim | | `style` | `{ backgroundColor, borderColor, borderWidth, borderRadius }` | Gorsel stil | ### Positioning Modlari | Mod | Aciklama | Taffy karsiligi | | ---------- | -------------------------------------------- | ------------------------------------- | | `flow` | Parent container'in flow'una katil (default) | `Position::Relative` | | `absolute` | Parent container icinde sabit konum | `Position::Absolute, inset: top/left` | ### Fatura Ornegi — Container Agaci ``` Sayfa (kok container, column, padding: 15mm) ├── Header (container, row, gap: 5mm) │ ├── Logo alani (container, column, width: 60mm) │ │ ├── image (logo) │ │ └── text (firma unvani) │ └── Fatura bilgi (container, column, width: fill, align: end) │ ├── static_text ("FATURA") │ ├── text (fatura no) │ └── text (tarih) ├── line (ayirici cizgi) ├── repeating_table (kalemler) └── Footer (container, row) ├── bos alan (width: fill) └── Toplamlar (container, column, width: 80mm) ├── text (ara toplam) ├── text (KDV) └── text (genel toplam) ``` ### Data JSON (Gercek Veri) Render zamaninda sablonla birlestirilen veri: ```jsonc { "firma": { "unvan": "Acme Teknoloji A.S.", "vergiNo": "1234567890", "logo": "data:image/png;base64,...", }, "fatura": { "no": "FTR-2026-001", "tarih": "2026-03-29", }, "kalemler": [ { "siraNo": 1, "adi": "Web Gelistirme Hizmeti", "miktar": 1, "birim": "Adet", "birimFiyat": 15000, "tutar": 15000, }, { "siraNo": 2, "adi": "SSL Sertifikasi", "miktar": 2, "birim": "Adet", "birimFiyat": 500, "tutar": 1000, }, ], "toplamlar": { "araToplam": 16000, "kdv": 2880, "genelToplam": 18880, }, } ``` ### JSON Schema (Veri Yapisi Tanimi) Editorun sol panelinde kullaniciya sunulan, baglanabilir alanlarin agac yapisi. Kullanici bu agactan surukleyerek elemanlari baglar. ```jsonc { "$id": "fatura-schema", "type": "object", "properties": { "firma": { "type": "object", "properties": { "unvan": { "type": "string", "title": "Firma Unvani" }, "vergiNo": { "type": "string", "title": "Vergi No" }, "logo": { "type": "string", "title": "Logo", "format": "image" }, }, }, "fatura": { "type": "object", "properties": { "no": { "type": "string", "title": "Fatura No" }, "tarih": { "type": "string", "title": "Tarih", "format": "date" }, }, }, "kalemler": { "type": "array", "title": "Fatura Kalemleri", "items": { "type": "object", "properties": { "siraNo": { "type": "integer", "title": "Sira No" }, "adi": { "type": "string", "title": "Urun / Hizmet Adi" }, "miktar": { "type": "number", "title": "Miktar" }, "birim": { "type": "string", "title": "Birim" }, "birimFiyat": { "type": "number", "title": "Birim Fiyat", "format": "currency", }, "tutar": { "type": "number", "title": "Tutar", "format": "currency" }, }, }, }, "toplamlar": { "type": "object", "properties": { "araToplam": { "type": "number", "title": "Ara Toplam", "format": "currency", }, "kdv": { "type": "number", "title": "KDV", "format": "currency" }, "genelToplam": { "type": "number", "title": "Genel Toplam", "format": "currency", }, }, }, }, } ``` --- ## Binding Mekanizmasi ### Scalar Binding Basit alan baglama — bir eleman, JSON'daki tek bir degere baglanir. - Editorde: Kullanici schema agacindan bir alani surukleyip text elemanina birakir. - Template JSON'da: `"binding": { "type": "scalar", "path": "firma.vergiNo" }` - Layout engine'de: `data_resolve.rs` JSON path'i cozumler, text icerigini uretir. ### Array Binding (Tekrarlayan Tablo) Array verisi icin ozel tablo bileseni. Kullanici: 1. Arac kutusundan "Tekrarlayan Tablo" bilesenini surukler. 2. `dataSource` olarak schema'daki bir array alani secer (or: `kalemler`). 3. Sutun tanimlarinda array'in alt alanlarini secer (or: `kalemler[].adi`). 4. Tablo stili (header rengi, zebra satirlar vs.) ayarlar. Layout engine'de: `table_layout.rs` repeating_table'i satir/sutun container agacina acar, taffy ile layout hesaplar. ### Format Fonksiyonlari Schema'daki `format` alanina gore formatlama yapilir: - `currency` → para birimi formatlama (binlik ayiraci, kurus, ₺ sembolu) - `date` → tarih formatlama (gun.ay.yil) - `percentage` → yuzde formatlama --- ## Layout Engine Detaylari ### LayoutResult (engine ciktisi) ```rust pub struct LayoutResult { pub pages: Vec, } pub struct PageLayout { pub width_mm: f64, pub height_mm: f64, pub elements: Vec, } pub struct ElementLayout { pub id: String, pub x_mm: f64, // Sayfa sol ustten mutlak pozisyon pub y_mm: f64, pub width_mm: f64, pub height_mm: f64, pub element_type: String, pub content: Option, pub style: ResolvedStyle, } ``` ### Taffy Mapping | dreport | taffy | | ----------------------------------------- | -------------------------------------------- | | `container(direction: row)` | `FlexDirection::Row` | | `container(direction: column)` | `FlexDirection::Column` | | `gap` | `gap: Size { width, height }` | | `padding` | `padding: Rect { top, right, bottom, left }` | | `align: start/center/end/stretch` | `align_items` | | `justify: start/center/end/space-between` | `justify_content` | | `SizeValue::Fixed(mm)` | `Dimension::Length(pt)` | | `SizeValue::Auto` | `Dimension::Auto` | | `SizeValue::Fr(n)` | `flex_grow: n, flex_basis: 0` | | `PositionMode::Absolute` | `Position::Absolute, inset: top/left` | Text leaf node'lari → taffy `MeasureFunc` callback'i ile cosmic-text'ten olcum alir. ### PDF Render (Backend) ```rust // render.rs akisi let layout = dreport_layout::compute_layout(&template, &data, &fonts); let pdf_bytes = dreport_layout::pdf_render::render_pdf(&layout, &fonts)?; // → Response olarak dondurulur ``` krilla kutuphanesi ile her ElementLayout'u PDF sayfasina cizer: text, line, rect, image, table. --- ## Interaction Overlay Layout engine pozisyon bilgisi uretiyor — bu pozisyonlar hem gorsel render (LayoutRenderer.vue) hem etkilesim katmani (InteractionOverlay.vue) tarafindan kullanilir. ### Overlay Nasil Calisir 1. LayoutResult'taki her eleman icin pozisyon ve boyut bilgisi (mm) alinir. 2. mm → px donusumu yapilir: `px = mm * scale * zoomLevel` 3. Her eleman icin CSS `position: absolute` + `left/top/width/height` ile handle yerlestirilir. 4. Tikla ile secim → mavi kenarlik (container ise mor kenarlik) + resize handle'lar. 5. Absolute elemanlar suruklenebilir — drag sirasinda CSS transform, birakinca layout engine re-compute. 6. Flow elemanlar suruklenemez — sira degisikligi drag-to-reorder ile yapilir. ### En Buyuk Kazanim Eski mimari: Typst SVG (opak) + Vue overlay (pozisyon sync'i sorunlu) Yeni mimari: Layout engine pozisyon verir → DOM div'ler hem gorsel hem etkilesim katmani. Ayri SVG/overlay senkronizasyonu sorunu yok. --- ## Proje Yapisi (Monorepo) ``` dreport/ ├── CLAUDE.md ├── Cargo.toml # Workspace: core, backend, layout-engine ├── justfile ├── frontend/ # Vue 3 + TypeScript │ ├── package.json │ ├── vite.config.ts │ ├── tsconfig.json │ ├── public/ │ │ └── fonts/ # Layout engine WASM icin gomulu fontlar │ ├── src/ │ │ ├── main.ts │ │ ├── App.vue │ │ ├── stores/ # Pinia │ │ │ ├── template.ts # Template JSON state │ │ │ ├── schema.ts # JSON Schema state │ │ │ └── editor.ts # Editor UI state (secili eleman, zoom vs.) │ │ ├── components/ │ │ │ ├── editor/ │ │ │ │ ├── EditorCanvas.vue # Ana editor alani (LayoutRenderer + overlay) │ │ │ │ ├── LayoutRenderer.vue # LayoutResult → HTML div render │ │ │ │ ├── InteractionOverlay.vue # Etkilesim katmani (secim/drag/resize) │ │ │ │ ├── ElementHandle.vue # Tekil eleman secim/drag/resize handle │ │ │ │ ├── SnapGuides.vue # Hizalama cizgileri │ │ │ │ └── RulerBar.vue # Cetvel │ │ │ ├── panels/ │ │ │ │ ├── ToolboxPanel.vue # Sol: bilesen arac kutusu │ │ │ │ ├── SchemaTreePanel.vue # Sol: JSON schema agaci (drag source) │ │ │ │ └── PropertiesPanel.vue # Sag: secili elemanin ozellikleri │ │ │ ├── properties/ │ │ │ │ ├── TextProperties.vue │ │ │ │ ├── TableProperties.vue │ │ │ │ ├── ImageProperties.vue │ │ │ │ └── StyleProperties.vue │ │ │ └── common/ │ │ │ ├── ColorPicker.vue │ │ │ ├── FontSelector.vue │ │ │ └── UnitInput.vue # mm/pt girisi │ │ ├── composables/ │ │ │ ├── useLayoutEngine.ts # Layout engine WASM yonetimi (Web Worker iletisimi) │ │ │ ├── useDragDrop.ts # Surukle-birak mantigi │ │ │ ├── useElementSelection.ts # Eleman secimi │ │ │ ├── useSnapGuides.ts # Miknatisli hizalama │ │ │ ├── useUndoRedo.ts # Geri al / yinele │ │ │ └── useZoomPan.ts # Zoom ve kaydirma │ │ ├── workers/ │ │ │ └── layout.worker.ts # Layout engine WASM Web Worker │ │ ├── core/ │ │ │ ├── types.ts # Ortak TypeScript tip tanimlari │ │ │ ├── layout-types.ts # LayoutResult TypeScript tipleri │ │ │ ├── schema-parser.ts # JSON Schema → agac yapisi (panel icin) │ │ │ └── mock-data-generator.ts # Schema'dan ornek veri uretme │ │ └── styles/ │ │ └── editor.css │ └── tests/ │ └── schema-parser.test.ts │ ├── core/ # Ortak Rust modelleri │ ├── Cargo.toml │ └── src/ │ ├── lib.rs │ └── models.rs # Template JSON serde modelleri (tum crate'ler kullanir) │ ├── layout-engine/ # Custom layout engine (taffy + cosmic-text) │ ├── Cargo.toml │ └── src/ │ ├── lib.rs # Public API: compute_layout() │ ├── tree.rs # Template → taffy node tree │ ├── sizing.rs # SizeValue → taffy Style mapping │ ├── text_measure.rs # cosmic-text ile text olcum │ ├── table_layout.rs # RepeatingTable → container agacina expand │ ├── data_resolve.rs # Binding'leri cozumle (gercek text content uret) │ ├── page_break.rs # Cok sayfali belgeler icin icerik bolme │ ├── pdf_render.rs # LayoutResult → PDF (krilla, sadece native) │ ├── wasm_api.rs # wasm_bindgen exports (loadFonts, computeLayout) │ └── font.rs # Font yukleme (WASM fetch vs native file read) │ ├── backend/ # Rust + Axum │ ├── Cargo.toml │ ├── src/ │ │ ├── main.rs │ │ ├── routes/ │ │ │ ├── mod.rs │ │ │ ├── render.rs # POST /api/render → PDF (layout-engine kullanir) │ │ │ └── health.rs # GET /api/health │ │ └── models/ │ │ ├── mod.rs │ │ ├── template.rs # Template JSON serde modelleri │ │ └── schema.rs # JSON Schema modelleri │ ├── fonts/ # Gomulu font dosyalari │ │ ├── NotoSans-Regular.ttf │ │ ├── NotoSans-Bold.ttf │ │ ├── NotoSans-Italic.ttf │ │ └── NotoSansMono-Regular.ttf │ └── tests/ │ └── render_test.rs │ └── shared/ # Ortak sema tanimlari └── schemas/ ├── fatura.schema.json └── irsaliye.schema.json ``` --- ## API Endpoints ### `POST /api/render` Template JSON + Data JSON alir, PDF doner. **Request:** ```json { "template": {}, "data": {} } ``` **Response:** `Content-Type: application/pdf` — binary PDF **Akis:** 1. Template + Data JSON parse edilir. 2. `compute_layout(template, data, fonts)` → `LayoutResult` 3. `render_pdf(layout_result, fonts)` → PDF bytes 4. Response olarak dondurulur. ### `GET /api/health` Sunucu saglik kontrolu. --- ## Editor UI/UX Davranislari ### Eleman Ekleme - **Arac kutusundan surukle-birak:** Container, statik metin, cizgi, gorsel, tekrarlayan tablo. - **Container'a birakma:** Eleman hedef container'in flow'una eklenir. Drop pozisyonuna gore sira belirlenir. - **Schema agacindan surukle-birak:** Scalar alan container'a birakilinca `text` elemani olusur, binding ayarlanir. - **Schema'dan array alani surukle-birak:** `repeating_table` olusur, sutunlari ayarlama diyalogu acilir. ### Eleman Secimi ve Manipulasyon - Tiklama ile secim → mavi kenarlik (container ise mor kenarlik) + resize handle'lar. - **Flow elemanlar:** Suruklenemez (pozisyon otomatik). Sira degisikligi drag-to-reorder ile. - **Absolute elemanlar:** Drag ile tasinir. Surukleme sirasinda CSS transform, birakinca layout engine re-compute. - **Container secimi:** Tiklayinca sag panelde direction, gap, align, padding ayarlari. - **Positioning modu degisikligi:** Sag panelde flow ↔ absolute gecisi. - Delete/Backspace ile silme. - Shift+tiklama ile coklu secim. ### Undo/Redo - Template JSON uzerinde immutable snapshot stack. - Ctrl+Z / Ctrl+Shift+Z. ### Zoom ve Pan - Ctrl+scroll ile zoom. - Space+drag veya orta fare tusu ile pan. - Zoom araligi: %25 – %400. --- ## Roadmap - [ ] Tablo stili ayarlari (header, zebra, border) - [ ] Format fonksiyonlari (currency, date) - [ ] `image` eleman tipi (statik + dinamik) --- ## Onemli Teknik Notlar ### Font Stratejisi Layout engine (hem WASM hem native) cosmic-text ile text olcum yapar — bu nedenle font dosyalarina ihtiyac duyar. Font dosyalari projeye dahil edilmeli ve hem WASM'a hem backend'e yuklenmelidir. Baslangicta minimal bir set: - Noto Sans (Regular, Bold, Italic, Bold Italic) — genel metin - Noto Sans Mono (Regular) — tablo sayilari, monospace ihtiyaclari - Toplam ~4-5 MB **Kritik:** Backend'de (native Rust) ve frontend'de (WASM) birebir ayni font dosyalari kullanilmalidir. Farkli font = farkli metrik = layout uyumsuzlugu. ### Koordinat Sistemi - Tum pozisyonlar **milimetre (mm)** cinsindendir. - Template JSON'daki degerler mm, taffy'ye point'e cevrilerek verilir. - LayoutResult'taki degerler mm cinsindendir. - Editor canvas'ta mm → px donusumu: `px = mm * scale * zoomLevel` - Referans: A4 = 210mm × 297mm. ### Hata Yonetimi - Layout engine hatasi olursa → editorde kirmizi banner ile hata mesaji goster. - Hesaplama basarisiz oldugunda son basarili LayoutResult'i koru, kullanicinin calsimasini bozma. - Web Worker crash olursa → yeniden baslat, state'i koru. ### Eleman Sirasi (Z-Order) - Template JSON'daki `children` dizisinin sirasi = cizim sirasi (sonraki ustte). - Kullanici "One Getir" / "Arkaya Gonder" yapabilmeli → dizi sirasi degisir. --- ## Kod Stili ve Konvansiyonlar ### Frontend (TypeScript / Vue) - Composition API + `