29 KiB
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:
- Kullanicinin tasarimi bir Template JSON olarak tutulur.
- Template JSON + Data JSON, layout-engine WASM modulu ile tarayicide islenir → LayoutResult uretilir.
- LayoutResult, her elemanin mutlak pozisyonunu (x, y, width, height mm cinsinden) icerir.
- LayoutRenderer.vue bu sonuclari HTML div'ler olarak render eder.
- InteractionOverlay.vue ayni pozisyon bilgisiyle secim, drag, resize handle'lar koyar.
- 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)
// 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):
// 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'inpadding'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)
{
"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:
{
"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.
{
"$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.rsJSON path'i cozumler, text icerigini uretir.
Array Binding (Tekrarlayan Tablo)
Array verisi icin ozel tablo bileseni. Kullanici:
- Arac kutusundan "Tekrarlayan Tablo" bilesenini surukler.
dataSourceolarak schema'daki bir array alani secer (or:kalemler).- Sutun tanimlarinda array'in alt alanlarini secer (or:
kalemler[].adi). - 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)
pub struct LayoutResult {
pub pages: Vec<PageLayout>,
}
pub struct PageLayout {
pub width_mm: f64,
pub height_mm: f64,
pub elements: Vec<ElementLayout>,
}
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<ResolvedContent>,
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)
// 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
- LayoutResult'taki her eleman icin pozisyon ve boyut bilgisi (mm) alinir.
- mm → px donusumu yapilir:
px = mm * scale * zoomLevel - Her eleman icin CSS
position: absolute+left/top/width/heightile handle yerlestirilir. - Tikla ile secim → mavi kenarlik (container ise mor kenarlik) + resize handle'lar.
- Absolute elemanlar suruklenebilir — drag sirasinda CSS transform, birakinca layout engine re-compute.
- 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:
{
"template": {},
"data": {}
}
Response: Content-Type: application/pdf — binary PDF
Akis:
- Template + Data JSON parse edilir.
compute_layout(template, data, fonts)→LayoutResultrender_pdf(layout_result, fonts)→ PDF bytes- 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
textelemani olusur, binding ayarlanir. - Schema'dan array alani surukle-birak:
repeating_tableolusur, 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)
imageeleman 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
childrendizisinin sirasi = cizim sirasi (sonraki ustte). - Kullanici "One Getir" / "Arkaya Gonder" yapabilmeli → dizi sirasi degisir.
Kod Stili ve Konvansiyonlar
Frontend (TypeScript / Vue)
- Composition API +
<script setup>kullan, Options API KULLANMA. - Pinia store'lar
defineStoreile. - Tip guvenligi:
strict: truetsconfig'de.anykullanma, gerekirseunknown+ type guard. - Composable isimlendirme:
useXxxpattern. - Bilesen isimleri: PascalCase, en az iki kelime (or:
EditorCanvas,SchemaTreePanel). - CSS: Scoped styles veya CSS modules. Global CSS minimum.
Backend (Rust)
- Axum handler'lar async.
- Serde ile JSON serialize/deserialize (
#[derive(Serialize, Deserialize)]). - Hata yonetimi:
thiserrorile typed errors, handler'lardaanyhowkabul edilebilir. - Layout engine dependency:
dreport-layoutcrate. - Clippy uyarilari temiz tutulacak.
Genel
- Commit mesajlari: conventional commits (
feat:,fix:,refactor:,docs:vs.). - Turkce yorum yazilabilir, kod ve degisken isimleri Ingilizce.
- Template JSON field isimleri Ingilizce (or:
position,size,binding). - UI etiketleri ve kullaniciya gosterilen metinler Turkce.
Kisitlamalar ve Bilincli Tercihler
- Veritabani yok (ilk asama). Template'ler JSON dosyasi olarak import/export edilir.
- Kullanici auth yok. Tek kullanicili yerel kullanim senaryosu.
- Sadece PDF cikti. Ileride PNG/SVG eklenebilir.
- Tekrarlayan bolge (repeating region) yok — sadece tekrarlayan tablo. Array binding yalnizca tablo bileseni ile yapilir. Serbest form repeating region ilerideki fazlarda degerlendirilir.
- WYSIWYG garantisi layout engine uzerinden. Ayni layout engine (taffy + cosmic-text) hem editorde hem PDF'te kullanilir. Editor HTML div render, PDF krilla render — ama pozisyonlar ayni engine'den gelir.
- Canvas kutuphanesi (fabric.js / konva.js) kullanilmiyor. Etkilesim katmani saf Vue bilesenleri + pointer event'ler ile yapilir. Render LayoutRenderer.vue ile HTML div'lerdir.