Files
dreport/CLAUDE.md
2026-03-29 22:35:57 +03:00

29 KiB
Raw Permalink Blame History

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)

// 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'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)

{
  "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.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)

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

  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:

{
  "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 + <script setup> kullan, Options API KULLANMA.
  • Pinia store'lar defineStore ile.
  • Tip guvenligi: strict: true tsconfig'de. any kullanma, gerekirse unknown + type guard.
  • Composable isimlendirme: useXxx pattern.
  • 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: thiserror ile typed errors, handler'larda anyhow kabul edilebilir.
  • Layout engine dependency: dreport-layout crate.
  • 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

  1. Veritabani yok (ilk asama). Template'ler JSON dosyasi olarak import/export edilir.
  2. Kullanici auth yok. Tek kullanicili yerel kullanim senaryosu.
  3. Sadece PDF cikti. Ileride PNG/SVG eklenebilir.
  4. Tekrarlayan bolge (repeating region) yok — sadece tekrarlayan tablo. Array binding yalnizca tablo bileseni ile yapilir. Serbest form repeating region ilerideki fazlarda degerlendirilir.
  5. 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.
  6. Canvas kutuphanesi (fabric.js / konva.js) kullanilmiyor. Etkilesim katmani saf Vue bilesenleri + pointer event'ler ile yapilir. Render LayoutRenderer.vue ile HTML div'lerdir.