# Tecof Geliştirici Dokümantasyonu — Tam Metin > Tecof üzerinde tema, uygulama ve entegrasyon geliştirmek için tek kaynak: CLI, @tecof paketleri, Tecof Studio (theme-editor), API referansları, MCP ve AI araçları. Bu dosya https://tecof-app-dev.vercel.app adresindeki tüm sayfaların içerikten otomatik üretilmiş birleşimidir. Bölüm sırası site navigasyonuyla aynıdır. Tek sayfa için `.md` kullanın. --- # Genel Bakış > Tecof geliştirici platformuna genel bakış — tema, uygulama, CLI, API ve AI yüzeyleri, hangi yoldan başlanacağı, kimlik doğrulama katmanları ve ortam değişkenleri. > > Kaynak: https://tecof-app-dev.vercel.app/getting-started · Bölüm: Başlangıç · Son güncelleme: 2018-10-20 Tecof’ta iki tür şey geliştirirsiniz: mağazanın ziyaretçiye görünen **teması** (Next.js + Tecof Studio) ve mağaza panelinde çalışan **uygulamalar** (partner uygulamaları). İkisi de `@tecof/cli` ile terminalden geliştirilir, numaralı sürümlerle yayınlanır ve anında geri alınabilir. Bu doküman sitesi bu işlerin tamamının tek kaynağıdır; içerik kaynak koda göre yazılır ve her sayfa AI araçları için Markdown olarak da sunulur. ## Nereden başlamalı | Yapmak istediğiniz | Başlangıç | Sonra | | :--- | :--- | :--- | | Mağazanın temasını kodla değiştirmek | [Tema hızlı başlangıç](https://tecof-app-dev.vercel.app/theme-quickstart) | [Tema mimarisi](https://tecof-app-dev.vercel.app/storefronts), [Tecof Studio](https://tecof-app-dev.vercel.app/theme-editor) | | Temaya Studio’da düzenlenebilir bileşen eklemek | [Bileşen yapılandırması](https://tecof-app-dev.vercel.app/editor-components) | [Alanlar](https://tecof-app-dev.vercel.app/editor-fields), [Slotlar](https://tecof-app-dev.vercel.app/editor-slots), [Stil editörü](https://tecof-app-dev.vercel.app/editor-styles) | | Mağazalara kurulan bir uygulama yazmak | [Uygulama hızlı başlangıç](https://tecof-app-dev.vercel.app/app-quickstart) | [Tasarım kimliği](https://tecof-app-dev.vercel.app/app-ui), [Webhook’lar](https://tecof-app-dev.vercel.app/app-webhooks), [App Bridge](https://tecof-app-dev.vercel.app/app-bridge) | | Vitrin verisini kendi önyüzünüzde kullanmak | [Public API](https://tecof-app-dev.vercel.app/public-apis) | [Headless CMS](https://tecof-app-dev.vercel.app/headless-cms) | | Bir AI ajanını Tecof’a bağlamak | [Tecof MCP](https://tecof-app-dev.vercel.app/mcp) | [Dokümanı AI ile kullanın](https://tecof-app-dev.vercel.app/ai-docs), [Alışveriş ajanları](https://tecof-app-dev.vercel.app/agents) | | CI’dan yayın yapmak | [Güvenlik ve CI](https://tecof-app-dev.vercel.app/dev-security) | [CLI başvurusu](https://tecof-app-dev.vercel.app/cli-reference) | | Paket sürümlerini görmek | [Paketler](https://tecof-app-dev.vercel.app/packages) | — | ## Platform yüzeyleri ### Tema ve vitrin Temalar Next.js App Router projeleridir. Sayfa verisini, CMS içeriğini, mağaza ayarlarını ve e-ticaret verisini backend’in `/api/store/*` katmanından vitrin anahtarıyla (`x-secret-key`) okur; sayfaları `@tecof/theme-editor`’ün `TecofRender`’ı ile çizer. Panel aynı sayfaları temanın editör rotasında **Tecof Studio** ile düzenletir. Özel temanın kodu bir **Space**’te yaşar: `tecof theme push` yalnız değişen dosyaları yükleyip staged bir sürüm açar, `tecof theme promote` onu yeniden derlemeden canlıya alır ([Spaces](https://tecof-app-dev.vercel.app/spaces)). ### Partner uygulamaları Uygulamalar mağazaya kurulur; istemci kimliği + sırrıyla alınan kurulum token’ı (`tcfi_…`) ile `/api/v1`’i kullanır, imzalı webhook’lar ve panel aksiyonları alır, panelde iframe içinde `@tecof/app-ui` ile Tecof görünümünde çalışır, ayarlarını Vault ve metafield’larda tutar ve ücretli planlar sunabilir ([Uygulama geliştirme](https://tecof-app-dev.vercel.app/app-development)). ### Vitrin ve CMS API’leri Canlı mağazanın güvenli API yüzeyi `/api/store/*` altındadır: ürün listesi, sepet doğrulama, checkout, müşteri hesabı, CMS okuma, bülten ve tema render verisi ([Public API](https://tecof-app-dev.vercel.app/public-apis)). Panel (yönetim) uçları `/api/merchant/*` altında JWT ile korunur ve panel içindir; üçüncü taraflar `/api/v1` kullanır. ### AI ve MCP Tecof’un uzak MCP sunucusu (`https://api.tecof.com/mcp`) mağaza verisini, sayfaları, CMS’i, ürünleri ve tema kodunu kapsamlı yetkilerle ajanlara açar; `@tecof/mcp` aynı araçları tema reposunun içinden sunar ([Tecof MCP](https://tecof-app-dev.vercel.app/mcp)). Mağazalar ayrıca AI arama motorları için `llms.txt` ve `/ai/*.json` dosyaları yayınlar ([GEO: AI Storefront](https://tecof-app-dev.vercel.app/ai-storefront)). ## Kimlik doğrulama modeli | Katman | Kullanım | Kimlik doğrulama | | :--- | :--- | :--- | | Vitrin (okuma ve güvenli yazma) | Canlı tema, ürün, CMS, checkout | `x-secret-key` | | Müşteri hesabı | Profil, adres, siparişler, yorum/favori | `x-secret-key` + müşteri `Authorization` | | Tema editörü yazma | Sayfa kaydetme, yükleme, ortak bileşen | `x-secret-key` + mağaza/ekip JWT’si | | Panel (yönetim) | Satıcı paneli ve yönetim API’leri | JWT, rol/ekip yetkisi | | Analytics toplama | Vitrin olayları | Public `merchantId` | | Partner uygulaması (sunucu) | Kurulu uygulamanın `/api/v1` çağrıları | `Authorization: Bearer tcfi_…` (kurulum token’ı) | | Partner uygulaması (iframe) | Panel içindeki uygulama arayüzü → uygulama sunucusu | 60 sn’lik oturum JWT’si (App Bridge) | | CLI ve CI | Spaces, push, promote, rollback | Cihaz akışı token’ı (`tcfd_…`) ya da kapsamlı CI token’ı (`TECOF_TOKEN`) | | MCP | Ajanların mağaza araçları | OAuth 2.1 ya da kişisel erişim anahtarı (`tcf_…`) | `NEXT_PUBLIC_MERCHANT_SECRET_KEY` temanın public bundle’ında bulunur. Bu yüzden backend, yazma yetkisi gerektiren editör işlemlerinde ayrıca JWT ister; taslak önizleme de imzalı `previewToken` ister. ## Ortam değişkenleri Tema projelerinin temel değişkenleri (`tecof theme dev` bunları `.env.local` dosyasına yazar): ```dotenv title=".env.local" NEXT_PUBLIC_BASE_URL=https://api.tecof.com NEXT_PUBLIC_CDN_URL=https://cdn.tecof.com NEXT_PUBLIC_MERCHANT_SECRET_KEY=merchant_secret_key NEXT_PUBLIC_THEME_ID=merchant_theme_id ``` `tecof-theme-core` bu değerlerle `/api/store/render`, `/api/store/merchant-info`, `/api/store/pages` ve `/api/store/ecommerce/*` uçlarını çağırır. Uygulama projelerinin değişkenleri (`TECOF_CLIENT_ID`, `TECOF_CLIENT_SECRET`, `TECOF_API_URL`) [uygulama hızlı başlangıçta](https://tecof-app-dev.vercel.app/app-quickstart#ortam-degiskenleri). ## Doğruluk notu Bu belgeler kaynak koda göre yazılır: `tecof-app-backend` (API sözleşmeleri), `tecof-theme-core` (tema runtime’ı), `tecof-theme-editor` (Studio ve renderer), `tecof-app-sdk` (CLI, uygulama paketleri) ve `tecof-mcp`. Mevcut kodda doğrulanan yol REST tabanlı `/api/store/*`, `/api/v1` ve MCP sözleşmeleridir; GraphQL ya da ayrı bir storefront SDK’sı birincil yol değildir. Henüz kodda olmayan özellikler “plan” olarak belirtilir. Bir çelişki görürseniz paketlerin gerçek tip tanımları ve API yanıtları son doğruluk kaynağıdır. --- # Tema Geliştirme: Hızlı Başlangıç > Özel temayı terminalden geliştirip canlıya alma: tecof login, theme init, dev (yerel/bulut), push, önizleme, promote, rollback ve base-moved çözümü. > > Kaynak: https://tecof-app-dev.vercel.app/theme-quickstart · Bölüm: Tema geliştirme · Son güncelleme: 2018-10-20 Bu sayfa bir özel temayı terminalden geliştirip canlıya almayı adım adım anlatır: giriş, kodu indirme, yerel ya da bulut geliştirme, push, önizleme, canlıya alma ve geri alma. Kavramlar için [Geliştirici Alanları (Spaces)](https://tecof-app-dev.vercel.app/spaces) sayfasına bakın. **Gerekenler** | Gereksinim | Not | | :--- | :--- | | Node.js 20+ | CLI'ın çalışma zamanı bağımlılığı yoktur; `tecof doctor` sürümü denetler | | Tecof hesabı ve mağaza ekibi üyeliği | Temanın ait olduğu mağazanın ekibinde olmanız yeterli; partner hesabı gerekmez | | Özel tema | Katalog temaları salt okunurdur; önce özel temaya kopyalayın ([ayrıntı](https://tecof-app-dev.vercel.app/spaces#salt-okunur-alanlar)) | | Paket | Push, canlıya alma, geri alma ve bulut geliştirme için mağaza paketinde Özel Temalar ve Kod Erişimi açık olmalı ([ayrıntı](https://tecof-app-dev.vercel.app/spaces#paket-gereksinimi)) | | Paket yöneticisi | Temanın kilit dosyasına göre `npm`, `pnpm`, `yarn` ya da `bun` | --- ## 1. CLI'ı Kurun ```bash npm install -g @tecof/cli tecof --version tecof doctor ``` `tecof doctor` Node sürümünü, API erişimini, oturumu, kimlik deposunu, klasör bağını, git'i ve paket yöneticisini denetler; zorunlu bir denetim başarısızsa çıkış kodu 1 olur. > **Not:** `tecof theme --help` tema komutlarını listelemiyorsa eski bir sürüm kuruludur; `npm install -g @tecof/cli@latest` ile güncelleyin. --- ## 2. Giriş Yapın ```bash tecof login ``` ```text Tecof'a giriş yapılıyor (https://api.tecof.com)… Doğrulama kodu: KQ7M-4XRT Tarayıcıda açın ve kodu onaylayın: https://app.tecof.com/cli?code=KQ7M-4XRT Onay bekleniyor… ✔ Giriş yapıldı: ayse@ornek-ajans.com (Örnek Ajans) Token deposu: keychain ``` 1. CLI onay sayfasını tarayıcıda açar. Açamazsa (ör. SSH oturumunda) adresi kendiniz açın; `--no-browser` tarayıcıyı hiç denemez. 2. Panelde oturumunuz açıkken sayfa doğrulama kodunu, CLI'ın adını, IP adresini, istek zamanını ve yetkilendirilecek hesabı gösterir. Kod terminaldekiyle aynıysa **Onayla ve giriş yap**; isteği siz başlatmadıysanız **Reddet**. 3. Kod 10 dakika geçerlidir. CLI onayı 5 saniyede bir yoklar ve onay gelince token'ı işletim sisteminin anahtarlığına kaydeder ([Kimlik bilgisi deposu](https://tecof-app-dev.vercel.app/dev-security#kimlik-bilgisi-deposu)). ```bash tecof whoami ``` Cihaz oturumu 30 gün geçerlidir; 14 gün kullanılmazsa düşer. Onayladığınız cihazları panelde `https://app.tecof.com/cli?tab=tokens` (**CLI anahtarları**) sayfasında görüp iptal edebilirsiniz. `tecof logout` token'ı sunucuda da iptal eder. --- ## 3. Tema Alanınızı Bulun ```bash tecof theme ls ``` ```text id kind name merchant/app live last rolled-back writable ---------------------------- ----- ----- ------------------ ---- ---- ----------- ------------------ thm_66f1a2b3c4d5e6f7a8b9c0d1 theme Butik Butik Mağaza (BTK) v11 v11 yes thm_66f1a2b3c4d5e6f7a8b9c0d2 theme Vita Butik Mağaza (BTK) - - no (catalog-theme) ``` Tema ve uygulama alanlarının hepsini `tecof spaces ls` listeler. Alan kimliğini panelde de bulabilirsiniz: **Temalar** → tema kartındaki ⋯ menüsü → **Sürümler** ekranının başlığında, kopyalama düğmesiyle. --- ## 4. Kodu İndirin ```bash tecof theme init butik --space thm_66f1a2b3c4d5e6f7a8b9c0d1 ``` ```text Kaynak indiriliyor (main)… ✔ Hazır: butik → Butik (312 dosya) Sonraki adımlar: cd butik npm install tecof theme dev ``` `init` klasörü açar, `main` dalının son hâlini indirir (çalıştırılabilir dosya izinleri korunur) ve iki dosya yazar: | Dosya | İçerik | Git | | :--- | :--- | :--- | | `.tecof/space.json` | Klasörün bağlı olduğu alan: `{ "space": "thm_…", "kind": "theme" }` | Commit'leyin; CI alanı bu dosyadan bulur | | `.tecof/state.json` | Yerel taban: indirilen `main` commit'i, dosya özetleri ve bekleyen staged sürüm | Yerel önbellek; CLI'ın yazdığı `.tecof/.gitignore` onu git dışında tutar | `.tecof/` klasörü Tecof'a hiçbir zaman gönderilmez. Etkileşimli terminalde `--space` vermezseniz yazılabilir temalar arasından seçim yaparsınız. Klasör boş değilse `--force` gerekir. Klasör zaten varsa — örneğin başka bir makinede kendi git deponuzdan geldiyse — `.tecof/space.json` sizi alana bağlar ama yerel taban (`state.json`) yoktur. `tecof theme pull` güncel `main` dalını indirip tabanı kaydeder; yerelde farklı olan dosyaların üzerine `--force` olmadan yazmaz. Bağ dosyası hiç yoksa önce `tecof theme link --space thm_…` çalıştırın. --- ## 5. Yerelde Geliştirin ```bash cd butik npm install tecof theme dev ``` ```text ✔ .env.local güncellendi (4 değişken: NEXT_PUBLIC_BASE_URL, NEXT_PUBLIC_CDN_URL, NEXT_PUBLIC_MERCHANT_SECRET_KEY, NEXT_PUBLIC_THEME_ID); diğer satırlar korundu. Başlatılıyor: npm run dev -- --port 3000 (port 3000) ``` `tecof theme dev` temanın canlıda kullandığı genel ortam değişkenlerini `.env.local` dosyasına yazar ve temanın `dev` betiğini başlatır: | Değişken | Değer | | :--- | :--- | | `NEXT_PUBLIC_BASE_URL` | Tecof API adresi | | `NEXT_PUBLIC_CDN_URL` | Tecof CDN adresi | | `NEXT_PUBLIC_MERCHANT_SECRET_KEY` | Mağazanın vitrin anahtarı (genel bir değerdir; tarayıcı paketine zaten girer) | | `NEXT_PUBLIC_THEME_ID` | Tema kimliği | - `.env.local` içindeki diğer satırlar korunur; dosya `0600` izinle yazılır ve hiçbir zaman push edilmez. - Paket yöneticisi kilit dosyasından seçilir (`pnpm-lock.yaml`, `yarn.lock`, `bun.lock`, `package-lock.json`). Port `--port` ile değişir; varsayılan `PORT` ortam değişkeni ya da `3000`. > **Dikkat:** Yerel sunucu, canlı sitenin kullandığı API'ye gerçek mağaza verisiyle bağlanır. Sayfalar, ürünler ve CMS içeriği gerçektir; sepet, form gönderimi ya da sipariş gibi yazma işlemleri gerçek mağazaya gider. ### Bulutta Geliştirme Yerel ortam kurmadan çalışmak ya da önizlemeyi başkasıyla paylaşmak için: ```bash tecof theme dev --cloud ``` ```text Bulut geliştirme oturumu açılıyor (thm_66f1a2b3c4d5e6f7a8b9c0d1)… ✔ İlk eşitleme: 2 dosya yazıldı, 0 silindi. Önizleme: https://sb-….vercel.run Değişiklikler izleniyor (Ctrl+C ile çıkış). Sürüme dönüştürmek için: tecof theme push ``` Bulut oturumu Cloud IDE'nin sandbox'ını kullanır: kod `main` dalından açılır, CLI yerel farkınızı yazar, sonra klasörü izleyip değişiklikleri yaklaşık 300 ms'lik gruplar hâlinde oturuma gönderir. Önizleme adresi sıcak yenilemeyle (HMR) güncellenir. - Yalnız metin dosyaları eşitlenir; görsel ya da font gibi ikili dosyalar `binary-not-supported` ile reddedilir. Onları `tecof theme push` ile gönderin. - Yerel taban `main` ile aynıysa yalnız farklı dosyalar gönderilir. Taban eskiyse CLI uyarır ve tüm dosyaları gönderir; projede ikili dosya varsa eşitleme durur. Bu uyarıyı görürseniz önce `tecof theme pull` ile tabanı güncelleyin. - Oturum Cloud IDE ile aynı kredi kurallarına tabidir: açılışta ve açık kaldığı sürece mağazanın kredisinden düşer ([MCP → Kredi](https://tecof-app-dev.vercel.app/mcp#kredi)). Oturum 45 dakika sonra kapanır; CLI süreyi uzatmaz, oturum bitince uyarıp çıkar. - Bir temada aynı anda tek oturum açık olabilir. Aynı temada başka bir kullanıcının Cloud IDE oturumu açıksa `409 session-busy` döner. - Ctrl+C'de CLI oturumu kapatmayı sorar (varsayılan evet); `--keep` oturumu açık bırakır. Oturum kapanırken taslak kaydedilmez: kaynak gerçeği sizin klasörünüzdür. - Bulut geliştirme yalnız `tecof login` oturumuyla çalışır; kapsamlı CI token'ı `403 token-scope-insufficient` alır. - Oturumdaki değişiklikler yalnız `tecof theme push` ile sürüme dönüşür. --- ## 6. Push Edin ```bash tecof theme push -m "Yeni ana sayfa" ``` ```text Push: thm_66f1a2b3c4d5e6f7a8b9c0d1 → staged 312 dosya tarandı (4.21 MB). Plan: 3 yüklenecek, 1 silinecek, 309 aynı. Yükleniyor 3/3 dosya (17.8 KB / 17.8 KB) Sürüm açıldı: v12 (staged) Durum: queued Durum: building Durum: ready ✔ v12 hazır: https://tecof-theme-butik-3k9x2a1b7-tecof.vercel.app Önizleme: tecof theme open v12 Canlıya almak için: tecof theme promote v12 ``` - Yalnız depoda olmayan içerik yüklenir; değişmeyen dosyalar ve depoda zaten bulunan içerik (ör. taşınan bir dosya) yeniden gönderilmez. Yerelde sildiğiniz dosyalar depodan da silinir. - Varsayılan hedef **staged**: üretim derlemesi yapılır, alan adı atanmaz, canlı site değişmez. - `-m` vermezseniz son git commit başlığı, yoksa `tecof theme push` kullanılır. Tecof deposundaki commit mesajı `Yeni ana sayfa [tecof v12 · Ayşe Yılmaz]` biçimindedir. - CLI derleme bitene kadar bekler (varsayılan 900 saniye; `--wait-timeout` ile değişir, `--no-wait` beklemez). Derleme başarısız olursa son günlük satırlarını basar ve çıkış kodu 1 olur; tüm günlük için `tecof theme logs v12`. - Değişiklik yoksa sürüm açılmaz: "Değişiklik yok; sürüm açılmadı." - Gönderilmeyen dosyalar ve sınırlar: [CLI Başvurusu → Yok sayma kuralları](https://tecof-app-dev.vercel.app/cli-reference#yok-sayma-kurallari). Staged adımını atlayıp doğrudan yayınlamak için: ```bash tecof theme push --prod -m "Acil düzeltme" ``` `--prod` commit'i doğrudan `main` dalına hızlı ileri yazar; derleme bitince sürüm canlıya çıkar ve tek derleme yapılır. Önizleme adımı olmadığı için yıkıcı sayılır ve onay ister. --- ## 7. Önizleyin ```bash tecof theme open v12 ``` ```text Açılıyor: https://tecof-theme-butik-3k9x2a1b7-tecof.vercel.app/?x-vercel-protection-bypass=***&x-vercel-set-bypass-cookie=true Bağlantıdaki erişim sırrı maskelendi; tam bağlantı için --show-secret kullanın (yalnız yetkili kişilerle paylaşın). ``` - Sürüm adresleri korumalıdır. CLI erişim sırrı taşıyan bir bağlantı alır ve tarayıcıda tam hâliyle açar; terminalde ve `--json` makbuzunda sır maskelenir. Tam bağlantı için `--show-secret`. - Sürüm vermezseniz (`tecof theme open`) canlı adres açılır. - Panelde aynı iş **Sürümler** ekranındaki **Önizle** düğmesidir. Bağlantıyı paylaşmadan önce [Önizleme bağlantıları](https://tecof-app-dev.vercel.app/versions-and-rollback#onizleme-baglantilari) bölümünü okuyun: içindeki sır, alanın bütün sürüm adreslerini açar. --- ## 8. Canlıya Alın ```bash tecof theme promote v12 ``` ```text v12 thm_66f1a2b3c4d5e6f7a8b9c0d1 için canlıya alınacak. Devam edilsin mi? (e/H) e ✔ v12 canlıda: https://www.butik-ornek.com ``` - Yeniden derleme yapılmaz; alan adları saniyeler içinde v12'yi gösterir. - `main` dalı v12'nin commit'ine hızlı ileri alınır; bu klasörün yerel tabanı da v12 olur. - Vercel `main` dalının bu commit'ini bir kez daha derler. İçerik aynıdır, canlı site etkilenmez ve yeni sürüm numarası açılmaz. - Vercel isteği sıraya aldıysa sonuç `Aktivasyon: pending` olarak görünür; durumu `tecof theme versions v12` ile izleyin. --- ## 9. Gerekirse Geri Alın ```bash tecof theme rollback # bir önceki canlı sürüme tecof theme rollback v10 # daha önce canlıda olmuş belirli bir sürüme ``` ```text thm_66f1a2b3c4d5e6f7a8b9c0d1 bir önceki canlı sürüme geri alınacak. Devam edilsin mi? (e/H) e ✔ Geri alındı; canlı sürüm: v11 ! Yeni değişiklikler canlıda değil; ileri almak için `tecof theme promote vM` ya da yeni yayın. ``` Geri alma yalnız daha önce canlıda olmuş sürümlere yapılır, anlıktır ve `main` dalına dokunmaz. Geri almadan sonra yeni yayınların nasıl canlıya çıktığını [Geri Almadan Sonra Yayınlar](https://tecof-app-dev.vercel.app/versions-and-rollback#geri-almadan-sonra-yayinlar) anlatır. Sürüm geçmişi: ```bash tecof theme versions ``` ```text ref status target live source created message url --- ------ ---------- ---- ------ ---------------- ------------------------------- ---------------------------------------------------- v12 ready staged cli 2026-10-11 09:41 Yeni ana sayfa https://tecof-theme-butik-3k9x2a1b7-tecof.vercel.app v11 ready production ● ide 2026-10-10 17:05 Footer bağlantıları güncellendi https://tecof-theme-butik-8h2m4q6r1-tecof.vercel.app v10 ready staged cli 2026-10-09 14:22 Ürün kartı https://tecof-theme-butik-5t7w9y2c4-tecof.vercel.app ``` --- ## Başkası Temayı Değiştirdiyse (base-moved) Siz çalışırken `main` dalına başka bir değişiklik gidebilir: bir Cloud IDE kaydı, bir AI işi, başka bir geliştiricinin push'u ya da CI. Staged push eski tabanınızın üstüne kurulur ve başarılı olur; fark canlıya alma anında ortaya çıkar: ```text $ tecof theme promote v12 ✖ main ilerledi (şu an 9f8e7d6); bu push eski tabana dayanıyor (base-moved). ``` Çıkış kodu 4'tür; `--json` makbuzunda `next.action` `pull` olur. Canlı siteye ve `main` dalına dokunulmaz. v12'nin tabanı değişmeyeceği için aynı sürümü yeniden denemek işe yaramaz: değişikliklerinizi güncel `main` üstüne taşıyıp yeni bir sürüm açın. 1. `tecof status` ile tabanınızı ve yerel değişikliklerinizi görün. 2. `tecof theme pull` güncel `main` dalını indirir. Birleştirme yapmaz: yerelde değiştirdiğiniz bir dosya `main`dekinden farklıysa hiçbir şey yazmadan durur, dosyaları nedenleriyle listeler ve çıkış kodu 4 olur. 3. Değişikliklerinizi güncel `main` üstüne uygulayın (aşağıdaki örnek), `tecof theme push` ile yeni sürümü (ör. v13) açın, önizleyin ve `tecof theme promote v13` ile canlıya alın. `--prod` push'ta aynı denetim commit anında yapılır: `main` ilerlediyse sürüm açılmaz, çözüm aynıdır. **Örnek: yerel git ile güncelleme.** Değişiklikleri birleştirmenin en kolay yolu klasörü kendi git deponuz olarak tutmaktır; `.git/` hiçbir zaman Tecof'a gönderilmez. Aşağıdaki örnek, klasörü `tecof theme init` sonrasında git deposu yaptığınızı, her `pull`dan sonra commit'lediğinizi ve değişikliklerinizin henüz commit'lenmediğini varsayar: ```bash git stash -u # değişikliklerinizi kenara alın; klasör yerel tabana döner tecof theme pull # güncel main'i indirir git add -A && git commit -m "tecof: main güncellendi" git stash pop # değişikliklerinizi yeniden uygulayın; çakışma varsa çözün tecof theme push -m "Yeni ana sayfa (güncel main üstüne)" ``` Yerel değişikliklerinizi bırakmayı göze alıyorsanız `tecof theme pull --force` onların üzerine yazar ve uzakta silinmiş dosyaları siler. --- ## Sonraki Adımlar - [Sürümler, Canlıya Alma ve Geri Alma](https://tecof-app-dev.vercel.app/versions-and-rollback) — durumlar, önizleme bağlantıları, canlıya alma ve geri alma kuralları - [Güvenlik ve CI](https://tecof-app-dev.vercel.app/dev-security) — CI token'ları ve GitHub Actions ile otomatik yayın - [CLI Başvurusu](https://tecof-app-dev.vercel.app/cli-reference) — tüm komutlar, çıkış kodları ve makbuzlar --- # Tema Mimarisi ve Runtime > Tecof temalarının yapısı — Next.js runtime akışı, ortam değişkenleri, temel dosyalar, bileşen kaydı (registry), vitrin API’leri ve analytics entegrasyonu. > > Kaynak: https://tecof-app-dev.vercel.app/storefronts · Bölüm: Tema geliştirme · Son güncelleme: 2018-10-20 Tecof temaları Next.js App Router, `@tecof/theme-editor` ve backend’in `/api/store/*` katmanı üzerine kuruludur. Tema sayfa JSON’unu backend’den alır, `TecofRender` ile render eder; panel aynı sayfayı temanın editör rotasında `TecofEditor` ile düzenletir. Yeni bir tema genelde `tecof-theme-core`’dan türetilir ve [CLI ile](https://tecof-app-dev.vercel.app/theme-quickstart) geliştirilip yayınlanır. --- ## Tema Runtime Akışı ```mermaid sequenceDiagram participant Theme as Next.js Theme participant StoreAPI as /api/store participant Backend as Tecof Backend participant Editor as Tecof Editor Theme->>StoreAPI: POST /render?themeId&locale StoreAPI->>Backend: merchant + theme + slug çözümle Backend-->>Theme: publishedData + CMS data Theme->>Theme: TecofRender(data, config) Editor->>StoreAPI: GET /editor/:pageId Editor->>StoreAPI: PUT /editor/:pageId ``` Tema tarafındaki temel dosyalar: | Dosya | Görev | | :--- | :--- | | `lib/api/store.ts` | Sayfa, merchant info ve sayfa listesi fetch fonksiyonları | | `lib/api/ecommerce.ts` | Ürün, sepet, checkout, müşteri hesabı ve push API helper'ları | | `app/components.tsx` | Tema bileşen registry'si | | `app/editor-config.tsx` | Editor config ve component map | | `components/RenderClient.tsx` | `TecofRender` sarmalı | | `components/global/TecofAnalyticsWrapper.tsx` | Analytics runtime script enjeksiyonu | --- ## Zorunlu Ortam Değişkenleri ```dotenv NEXT_PUBLIC_BASE_URL=https://api.tecof.com NEXT_PUBLIC_CDN_URL=https://cdn.tecof.com NEXT_PUBLIC_MERCHANT_SECRET_KEY=merchant_secret_key NEXT_PUBLIC_THEME_ID=merchant_theme_id ``` `tecof theme dev` bu değerleri mağazanızdan alıp `.env.local` dosyasına yazar. `NEXT_PUBLIC_MERCHANT_SECRET_KEY` public bundle içinde yer alır. Bu nedenle canlı storefront için uygundur, fakat editör yazma işlemlerinde tek başına yeterli değildir; backend `PUT /api/store/editor/:id` için JWT/team doğrulaması da ister. --- ## Tecof Studio entegrasyonu Tema üç noktada `@tecof/theme-editor` kullanır: tüm uygulamayı saran `TecofProvider`, panelin iframe ile açtığı editör rotası (`app/editor/[id]/page.tsx` → `TecofEditor`, `?token=` ile gelen JWT `accessToken` olur) ve yayın sayfalarındaki `TecofRender`. Kurulum, prop’lar ve örnek kodlar: [Tecof Studio’ya giriş](https://tecof-app-dev.vercel.app/theme-editor). --- ## Bileşen kaydı (registry) Her tema bileşeni registry'de bir key ile yayınlanır. Kaydedilmiş sayfa verisindeki `content[].type` değeri bu key ile eşleşmelidir. ```tsx import { Header } from "@/components/sections/Header"; import { HeroSection } from "@/components/sections/HeroSection"; export const components = { Header, HeroSection, }; ``` Bileşen config'i en az şu yapıyı taşımalıdır: ```tsx export const HeroSection = { label: "Hero", fields: { title: { type: "text", label: "Başlık" }, }, defaultProps: { title: "Yeni başlık", }, // className kök elemana basılmalı; yoksa bileşen editörde seçilemez render: (props: any) =>
{props.title}
, }; ``` Alan tiplerinin tamamı [Alanlar](https://tecof-app-dev.vercel.app/editor-fields), bileşen yapılandırmasının tamamı (`defineSection`, slotlar, kök `className` sözleşmesi) [Bileşen yapılandırması](https://tecof-app-dev.vercel.app/editor-components) sayfasındadır. --- ## Public API'ler Tema oluştururken verilmesi gereken temel public API yüzeyi: | Amaç | Endpoint | | :--- | :--- | | Yayındaki sayfayı render et | `POST /api/store/render?themeId=:themeId&locale=:locale` | | Editör için sayfa oku | `GET /api/store/editor/:pageId` | | Editör için sayfa kaydet | `PUT /api/store/editor/:pageId` | | Merchant dil/logo/script bilgisi | `GET /api/store/merchant-info` | | Link alanı için sayfa listesi | `GET /api/store/pages` | | Upload | `POST /api/store/upload` | | Medya listesi | `GET /api/store/uploads` | | Otomatik çeviri | `POST /api/store/translate` | | CMS canlı içerik | `POST /api/store/cms/collections/:slug/items` | | E-ticaret ürün listesi | `GET /api/store/ecommerce/products` | | Checkout session | `POST /api/store/ecommerce/checkout-session` | Tam tablo için [Public API Referansı](https://tecof-app-dev.vercel.app/public-apis) sayfasına bakın. --- ## Analytics Entegrasyonu `tecof-theme-core`, `TecofAnalyticsWrapper` ile backend’in servis ettiği runtime betiğini yükler (betik ve olay adresi `NEXT_PUBLIC_BASE_URL`’den türetilir). Aynı işi bir pakette isterseniz [`@tecof/analytics`](https://tecof-app-dev.vercel.app/packages#tecofanalytics) `TecofAnalyticsProvider` ve `useTecofAnalytics().track()` sunar: ```tsx ``` Script kaynağı: ```text /public/analytics/client.js ``` Event endpoint'i: ```text POST /api/analytics/event ``` Varsayılan event tipleri `pageview`, `web-vital`, `cart_add`, `cart_remove` ve `checkout_start` olarak doğrulanmıştır. Tema bileşenleri sepet ve checkout etkileşimlerinde `trackCartAdd`, `trackCartRemove`, `trackCheckoutStart` helper'larını çağırabilir. --- ## Tema Oluşturma Kontrol Listesi 1. `NEXT_PUBLIC_BASE_URL`, `NEXT_PUBLIC_MERCHANT_SECRET_KEY`, `NEXT_PUBLIC_THEME_ID` değerlerini tanımla. 2. `TecofProvider` ile app'i sar. 3. `app/components.tsx` içinde tüm render edilebilir component key'lerini kaydet. 4. `app/editor-config.tsx` içinde component config'i export et. 5. Dynamic route'larda `getPageData(slug, locale, showDraftData)` ile `/api/store/render` çağır. 6. Public sayfada `TecofRender`, editör sayfasında `TecofEditor` kullan; ikisi aynı `editorConfig`’i paylaşsın ve her bileşen `className`’i kökte bassın. 7. E-ticaret componentleri için `lib/api/ecommerce.ts` helper'larını kullan. 8. Analytics için `TecofAnalyticsWrapper` ve checkout event helper'larını ekle. --- # Tecof Studio’ya Giriş (@tecof/theme-editor) > @tecof/theme-editor ile Tecof Studio: kurulum, TecofProvider, editör ve yayın sayfası, doküman veri modeli ve paketin alt giriş noktaları. > > Kaynak: https://tecof-app-dev.vercel.app/theme-editor · Bölüm: Tema geliştirme · Son güncelleme: 2018-10-20 `@tecof/theme-editor`, Tecof temalarının görsel sayfa editörünü (**Tecof Studio**), kaydedilmiş sayfaları yayınlayan renderer’ı, editör alanlarını (field factory’ler), API istemcisini, stil ve tema motorunu tek pakette toplar. Bu bölüm paketin güncel sürümünü (`0.0.96`) kaynak koduna göre anlatır; tip tanımlarını doğrudan okumak için [doküman MCP’sinin](https://tecof-app-dev.vercel.app/ai-docs) `read_type_definition` aracını kullanabilirsiniz. > [!NOTE] Bu bölüm eski editör dokümantasyon sitesinin yerini alır > Daha önce `tecof-theme-editor` deposundaki ayrı sitede (playground) duran içerik buraya taşındı ve kaynak koda göre güncellendi. Paketin README’si artık yalnız kurulum ve kısa kullanım içerir. ## Neler sunar | Parça | Ne yapar | | :--- | :--- | | `TecofEditor` / `TecofStudio` | iframe kanvaslı görsel editör: sürükle-bırak, katmanlar, Inspector, stil ve tema panelleri, komut paleti, geri al/yinele, otomatik kayıt | | `TecofRender` | Kaydedilmiş sayfayı yayında render eder; stil CSS’ini, tema değişkenlerini ve fontları kendisi basar | | `create*Field` | Çok dilli metin, zengin metin, medya, bağlantı, renk, kod, ikon, tekrar eden satırlar, CMS ve e-ticaret seçicileri | | `defineSection` / `defineElement`, ``, `` | Tema bileşeni yazarken slot, varsayılan çocuk ve kök `className` sözleşmesini otomatikleştirir | | `TecofApiClient` | Sayfa, medya, çeviri, CMS, ortak bileşen ve e-ticaret uçları için istemci | | `@tecof/theme-editor/email` | Pazarlama e-postası doküman modeli ve tablo tabanlı HTML render’ı | Editör ile yayın aynı `StudioConfig` nesnesini ve aynı sayfa veri modelini kullanır. Editörde gördüğünüz bileşenler yayında yeniden yorumlanmaz; aynı registry üzerinden render edilir. ```mermaid flowchart LR A["StudioConfig: bileşenler ve alanlar"] --> B["TecofEditor: draftData yazar"] B -->|"PUT /api/store/editor/:id"| C[("Tecof backend")] C -->|"panelde yayınla"| D["publishedData"] D -->|"POST /api/store/render"| E["TecofRender: yayın sayfası"] A --> E ``` Editör **yalnız taslağı** (`draftData`) yazar. Yayınlama (taslağın `publishedData`’ya kopyalanması) panelin ve backend’in işidir; paket `publishedData`’ya hiç dokunmaz. iframe’e gönderilen `puck:publish` mesajı da yalnız taslak kaydı tetikler. ## Gereksinimler | Gereksinim | Sürüm | Not | | :--- | :--- | :--- | | `react`, `react-dom` | `^18 \|\| ^19` | Peer bağımlılık | | `@tiptap/core`, `@tiptap/pm`, `@tiptap/react` | `^3` | Zengin metin alanı (EditorField) için peer bağımlılık | | `lucide-react` | `>=0.408.0 <2` | İkonlar; peer bağımlılık | | Next.js | App Router önerilir | Editör ve renderer’ı içeren dosyalar istemci bileşenidir (`"use client"`) | Paket derlemesi her JS dosyasının başına `'use client'` ekler; sunucu bileşeninden doğrudan içe aktarabilirsiniz ama editörü saran kendi bileşeninizi de istemci bileşeni yapın. ## Kurulum ```bash npm2yarn npm install @tecof/theme-editor @tiptap/core @tiptap/pm @tiptap/react lucide-react ``` Editör arayüzü ve alanların stilleri tek CSS dosyasındadır. Editörün çalıştığı layout’ta bir kez içe aktarın: ```tsx title="app/editor/layout.tsx" import "@tecof/theme-editor/styles.css"; ``` > [!TIP] Sürüm aralığı > `0.0.x` sürümlerde `^0.0.96` yalnız `0.0.96`’yı kabul eder. Yeni sürüme geçmek için `package.json`’daki sürümü elle değiştirin. Güncel sürüm: [Paketler](https://tecof-app-dev.vercel.app/packages#tecoftheme-editor). ## Ortam değişkenleri Tecof temaları aşağıdaki genel değişkenlerle çalışır. `tecof theme dev` bunları `.env.local` dosyasına kendisi yazar ([Tema hızlı başlangıç](https://tecof-app-dev.vercel.app/theme-quickstart#5-yerelde-gelistirin)). ```dotenv title=".env.local" NEXT_PUBLIC_BASE_URL=https://api.tecof.com NEXT_PUBLIC_CDN_URL=https://cdn.tecof.com NEXT_PUBLIC_MERCHANT_SECRET_KEY=mağazanın-vitrin-anahtarı NEXT_PUBLIC_THEME_ID=tema-kimliği ``` Vitrin anahtarı (`x-secret-key`) genel bir değerdir; tarayıcı paketine girer. Bu yüzden backend editörün yazma uçlarında ayrıca kullanıcı JWT’si ister (aşağıdaki `accessToken`). ## Provider `TecofProvider` bir `TecofApiClient` oluşturup alt bileşenlere taşır. Editör, alanlar (dil listesi, medya kütüphanesi, sayfa seçici) ve CMS’e bağlı tekrar eden slotlar bu bağlamı kullanır. ```tsx title="components/TecofProviderWrapper.tsx" "use client"; import { TecofProvider } from "@tecof/theme-editor"; export function TecofProviderWrapper({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` | Prop | Tip | Açıklama | | :--- | :--- | :--- | | `apiUrl` | `string` | Tecof API adresi (sondaki `/` temizlenir) | | `secretKey` | `string` | Mağazanın vitrin anahtarı; her istekte `x-secret-key` başlığı | | `cdnUrl` | `string?` | Medya adresleri için CDN kökü; verilmezse `apiUrl` | | `themeId` | `string?` | Çok temalı mağazada sayfa okuma/kaydetme isteklerinin doğru temaya gitmesi için | | `children` | `ReactNode` | — | Bileşen içinden `useTecof()` ile `{ apiClient, apiUrl, secretKey, cdnUrl, themeId }` okunur; provider dışında çağrılırsa hata fırlatır. ## İlk entegrasyon: üç dosya ### 1. Bileşen yapılandırması ```tsx title="app/editor-config.tsx" "use client"; import { defineElement, defineSection, type StudioConfig } from "@tecof/theme-editor"; const Button = defineElement({ label: "Buton", fields: { text: { type: "text", label: "Metin" } }, defaultProps: { text: "Keşfet" }, render: (p) => {p.text}, }); const Hero = defineSection({ label: "Hero", category: "sections", fields: { title: { type: "text", label: "Başlık" } }, slots: { actions: { label: "Butonlar", layout: "row-wrap", gap: "md", default: ["Button"] }, }, defaultProps: { title: "Yeni sezon" }, render: (p, slots) => (

{p.title}

{slots.actions}
), }); export const editorConfig: StudioConfig = { categories: { sections: { title: "Bölümler", components: ["Hero"] }, elements: { title: "Elementler", components: ["Button"] }, }, components: { Hero, Button }, }; ``` `defineSection` slot alanlarını, varsayılan çocukları ve kökteki `className` sözleşmesini sizin yerinize kurar; ayrıntılar [Bileşen yapılandırması](https://tecof-app-dev.vercel.app/editor-components) sayfasında. Gerçek temalarda metinler genelde çok dilli alanlarla (`createLanguageField`, değer `[{ code, value }]`) tutulur: [Alanlar](https://tecof-app-dev.vercel.app/editor-fields#languagefield-cok-dilli-metin). ### 2. Editör sayfası Tecof paneli tema editörünü iframe içinde `//?token=` biçiminde açar. Tema bu token’ı `accessToken` olarak editöre verir. ```tsx title="app/editor/[id]/page.tsx" import EditorClient from "@/components/EditorClient"; export default async function EditorPage({ params, searchParams, }: { params: Promise<{ id: string }>; searchParams: Promise<{ token?: string }>; }) { const { id } = await params; const { token } = await searchParams; return ; } ``` ```tsx title="components/EditorClient.tsx" "use client"; import { TecofEditor } from "@tecof/theme-editor"; import { editorConfig } from "@/app/editor-config"; export default function EditorClient({ pageId, accessToken }: { pageId: string; accessToken?: string }) { return ( ); } ``` `TecofEditor`, `TecofStudio`’nun diğer adıdır; ikisi aynı editörü açar. Tüm prop’lar: [Studio arayüzü](https://tecof-app-dev.vercel.app/editor-studio#tecofeditor-proplari). ### 3. Yayın sayfası `TecofRender` veri getirmez. Sayfayı sunucuda çekip renderer’a verin; editörle **aynı** `editorConfig`’i kullanın. ```tsx title="components/RenderClient.tsx" "use client"; import { TecofRender, type PuckPageData } from "@tecof/theme-editor"; import { editorConfig } from "@/app/editor-config"; export function RenderClient({ data, cmsData }: { data: PuckPageData; cmsData?: Record | null }) { return ; } ``` `tecof-theme-core` sayfa verisini `POST /api/store/render` ile alır ve taslak önizlemede (`?showDraftData`) `draftData`, yayında `publishedData` kullanır. Ayrıntı: [Yayın render’ı](https://tecof-app-dev.vercel.app/editor-render) ve [Tema mimarisi](https://tecof-app-dev.vercel.app/storefronts). ## Veri modeli Editörün kaydettiği doküman Puck uyumlu bir şekildir (`PuckPageData`): ```ts interface PuckPageData { content: Array<{ type: string; props: { id: string } & Record }>; // kök düğümler root: { props: Record }; // sayfa geneli (tema, şema sürümü…) zones: Record }>>; } ``` - Kök düğümler `content` dizisinde, iç içe düğümler `zones` nesnesindedir. Zone anahtarı her zaman `parentId:slotAdı` biçimindedir. - `props.id` düğümün kimliğidir; kopyalama, çoğaltma, yapıştırma ve şablon ekleme her zaman **yeni** id üretir. - `zones` nesnesindeki anahtar sırası görsel sırayı temsil etmez; kardeş sırası zone dizisinin kendi sırasıdır. - Bir düğüm silinince alt zone ağacı da temizlenir. - `_` ile başlayan prop’lar motora ayrılmıştır, bileşen prop’u olarak kullanmayın: `_tecofStyles`, `_tecofTheme`, `_interactions`, `_startHidden`, `_locked`, `_hidden`, `_symbolOverrides`, `_variant`, `_schemaVersion`, `_layerName`, `_tecofStyleSync`. - Aynı `sharedComponentId` değerini taşıyan düğümler bir **ortak bileşenin** (symbol) örnekleridir: birinde yapılan değişiklik diğerlerine canlı yansır; örneğe özel alanlar `_symbolOverrides` ile ayrılır. ## Alt giriş noktaları | Giriş | İçerik | | :--- | :--- | | `@tecof/theme-editor` | Editör, renderer, alanlar, API istemcisi, stil/tema yardımcıları, tipler | | `@tecof/theme-editor/styles.css` | Editör ve alan stilleri (`./puck.css` ve `./dist/styles.css` eski takma adlardır) | | `@tecof/theme-editor/under-construction` | `UnderConstruction` bileşeni (`title`, `description`, `subtitle?`, `logoUrl?`, `accentColor?`) | | `@tecof/theme-editor/email` | E-posta doküman modeli: `createEmailDocument`, `createEmailBlock`, `validateEmailDocument`, `renderEmailHtml` | E-posta modeli sürümlü bir blok dokümanıdır (`kind: "tecof-email"`, `version: 1`). Yaprak bloklar `logo, heading, text, button, image, divider, spacer, social, coupon, product, video, footer, menu, html`; kap bloklar `section` (yalnız en üst düzey) ve `columns` (`layout`: `1`, `1:1`, `1:1:1`, `1:1:1:1`, `1:2`, `2:1`, `1:3`, `3:1`; yalnız yaprak alır). Doğruluk kaynağı backend derleyicisidir; paket aynı sözleşmeyi taşır. ```ts import { createEmailBlock, createEmailDocument, renderEmailHtml, validateEmailDocument } from "@tecof/theme-editor/email"; const doc = createEmailDocument({ blocks: [ createEmailBlock("columns", { layout: "1:2", columns: [[createEmailBlock("image")], [createEmailBlock("text")]] }), createEmailBlock("footer"), ], }); validateEmailDocument(doc); // [] → geçerli const html = renderEmailHtml(doc, { mergeData: {}, preserveMergeTags: false }); ``` ## Sonraki adımlar - [Studio arayüzü](https://tecof-app-dev.vercel.app/editor-studio): paneller, modlar, `TecofEditor` prop’ları. - [Bileşen yapılandırması](https://tecof-app-dev.vercel.app/editor-components): `ComponentConfig`, `defineSection`, slot sözleşmesi. - [Alanlar](https://tecof-app-dev.vercel.app/editor-fields): tüm field factory’ler ve değer biçimleri. - [Stil editörü](https://tecof-app-dev.vercel.app/editor-styles): token modeli, masaüstü-öncelikli kırılımlar, canlı önizleme. --- # Studio Arayüzü > Tecof Studio arayüzü — kanvas, paneller, Inspector sekmeleri, düzenleme/önizleme modları, otomatik kayıt, canlı katılım ve TecofEditor prop’larının tamamı. > > Kaynak: https://tecof-app-dev.vercel.app/editor-studio · Bölüm: Tema geliştirme · Son güncelleme: 2018-10-20 Tecof Studio, sayfayı gerçek tasarım genişliğinde bir iframe kanvasında gösterir; bileşenleri sürükle-bırak ile ekler, Inspector’dan düzenler ve taslağı backend’e kaydeder. Bu sayfa arayüzün parçalarını ve `TecofEditor` prop’larını anlatır. ## Arayüz | Bölge | Ne yapar | | :--- | :--- | | Üst çubuk | Geri al / yinele, Düzenleme ↔ Önizleme modu, Masaüstü / Tablet / Mobil görünüm ve masaüstü genişliği, düzenlenen dil, kılavuz ızgarası (`G`), boyutlandırma (`R`) ve boşluk tutamaçları (`B`), yardım, sol/sağ paneli aç-kapa | | Sol panel | Bileşen kütüphanesi (kategoriler, varsa önizleme görselleri), sayfa şablonları ve katman ağacı | | Kanvas | Host CSS’inden yalıtılmış iframe; seçim, hover, sürükleme ve satır içi metin düzenleme | | Sağ panel (Inspector) | Seçim yokken **Sayfa** ve **Tema** sekmeleri; bir düğüm seçiliyken **İçerik**, **Stil** ve **Etkileşimler** | | Komut paleti (`⌘K`) | Eylemler, bileşen ekleme ve (yapılandırıldıysa) AI ile bölüm üretme | ### Kanvas - **Gerçek genişlikte kanvas.** Masaüstü görünümü seçilen tasarım genişliğinde (1280 / 1440 / 1680 / 1920 px ya da özel; varsayılan 1440) çizilir ve alana sığması için ölçeklenir. Tablet 768, mobil 375 px’tir. Üst çubuktaki çip etkin genişliği ve ölçek yüzdesini gösterir. - **Düzenleme modu.** Tıklama düğümü seçer, çift tıklama satır içi metin düzenlemeyi başlatır, sürükleme düğümü taşır. Bağlantı, buton ve formların tarayıcı davranışı (gezinme, gönderme) bilerek engellenir. - **Önizleme modu.** Editör çerçeveleri gizlenir; sayfa ziyaretçinin gördüğü gibi çalışır. - **Satır içi düzenleme.** Metne çift tıklayın; `Enter` kaydeder, `Esc` vazgeçer. Bileşenler düzenlenebilir metni `data-tecof-prop="propAdı"` (çok dilli için ek olarak `data-tecof-lang`) ile işaretleyebilir; işaret yoksa metin eşleştirmesine düşülür. - **Kanvastan panele odak.** Bir öğeye tıklamak Inspector’da İçerik sekmesini açar ve ilgili alana kaydırıp kısa bir vurgu yapar. - **Boşluk görünümü.** Hover’da padding (yeşil) ve margin (amber) devtools tarzında gösterilir. - **Dayanıklılık.** Render hatası veren bir bileşen kanvası düşürmez; düğüm bazlı error boundary yalnız o düğümü izole eder ve prop düzelince kendiliğinden toparlar. - **Dokunmatik sürükle-bırak.** Tablet ve telefonda bir bileşene, palet bloğuna ya da katman satırına ~300 ms basılı tutup sürükleyin; fare ise HTML5 sürükle-bırak yolunu kullanır. ### Array kartları (`data-tecof-item`) Bir bileşen `type: "array"` prop’unu kendi render’ında map’liyorsa (slot kullanmadan) kartları editöre tanıtabilir: ```tsx {services.map((s, i) => (
  • {s.title}

    {s.description}

  • ))} ``` Kart hover çerçevesi alır, tıklama Inspector’da o satırı açar, çift tıklama satır alanını yerinde düzenler (tek geri al adımı). Satır kayıt anında silinmiş ya da taşınmışsa yazım sessizce iptal edilir; yanlış satıra yazılmaz. ### Etkileşimli kontroller (`registerOverlayPortal`) Düzenleme modunda kanvas her tıklamayı editöre yönlendirir. Sekme başlığı, slider oku ya da akordeon gibi kontrollerin düzenlerken de çalışması gerekiyorsa elemanı `puck.registerOverlayPortal` ile işaretleyin (React ref callback’i olarak, `null`’a dayanıklı): ```tsx render: ({ puck, tabs }) => (
    {tabs.map((tab, i) => ( ))}
    ) ``` İşaretli eleman ve alt elemanları seçim, çift tıklama ve sürükleme işleyicilerinden muaf tutulur. `router.push` gibi JS yönlendirmeleri tarayıcı davranışı olmadığı için engellenemez; bu tür bileşenler `editMode` / `puck.isEditing` değerine bakmalıdır. | `puck` alanı | Tip | Açıklama | | :--- | :--- | :--- | | `registerOverlayPortal` | `(el: HTMLElement \| null) => () => void` | Elemanı düzenleme modunda canlı bırakır | | `isEditing` | `boolean` | Düzenleme modunda mı (`editMode` prop’u ile aynı) | | `dragRef` | `(el: HTMLElement \| null) => void` | Yalnız `inline` bileşenlerde sürükleme tutamacı | | `renderDropZone` | `(opts) => ReactNode` | Slot’u elle çizmek için (bkz. [Slotlar](https://tecof-app-dev.vercel.app/editor-slots)) | ### Inspector - **İçerik.** Bileşenin alanları. Kapsayıcı bir düğüm seçildiğinde bölümün kendi alanlarının yanında tüm alt elementlerin alanları da girintili, katlanabilir gruplar hâlinde listelenir. Bileşen `fieldsGroups` tanımlarsa alanlar akordeon gruplarına ayrılır. `variants` tanımlıysa varyant çipleri görünür. - **Stil.** Görsel stil editörü: kırılım ve durum bazlı token’lar, sayfalar arası stil uygulama. Bkz. [Stil editörü](https://tecof-app-dev.vercel.app/editor-styles). - **Etkileşimler.** Tetikleyici → eylem kuralları (`click` / `hover` → `scrollTo`, `toggleClass`, `show`, `hide`, `toggle`). Kurallar düğümün `_interactions` prop’unda saklanır ve yayında `TecofRender` tarafından çalıştırılır. - **Sayfa / Tema.** Seçim yokken sayfa geneli (`config.root.fields`) ve canlı tema düzenleyici. Bkz. [Canlı tema](https://tecof-app-dev.vercel.app/editor-theme). ### Katmanlar Katman ağacında her satırda kilit ve gizle düğmeleri vardır (`_locked` / `_hidden`). Kilitli düğüm seçilebilir ama taşınamaz, silinemez ve alanları salt okunur olur. Gizli düğüm editörde soluk görünür; önizlemede ve yayında hiç render edilmez. ### Ortak bileşenler (symbol) Bir bölüm ortak bileşene dönüştürülünce aynı `sharedComponentId` değerini taşıyan tüm örnekler tek kaynaktan beslenir: bir örnekte yapılan değişiklik diğerlerine canlı yansır. Örneğe özel alanlar `_symbolOverrides` ile ayrılır. Kayıtta backend örneği referansa indirger ve okurken asıl kayıttan çözer; ortak bileşen silinirken referanslar önce sayfalara gerçek içerik olarak yazılır, içerik kaybolmaz. Ortak bileşenler kanvasta mor rozetle işaretlenir. ### Canlı katılım Aynı sayfayı aynı anda açan başka bir ekip üyesi üst çubukta görünür. Amaç eşzamanlı düzenleme değil **çakışma uyarısıdır**: iki kişi aynı taslağı kaydederse son kaydeden diğerinin işini ezer. Bağlantı yalnız editörde ve `accessToken` varken açılır; kimlik bilgisi sunucuda token’dan çözülür. ### AI ile bölüm üretme `config.ai` tanımlıysa komut paletinde **AI ile bölüm üret** görünür. Kütüphane bileşen kataloğundan istemi kurar ve modelin yanıtını savunmacı biçimde doğrular (uydurma tip reddi, id temizliği, derinlik sınırı); LLM çağrısını host yapar: ```ts export const editorConfig: StudioConfig = { components, ai: { complete: async ({ system, prompt }) => { const res = await fetch("/api/ai/section", { method: "POST", body: JSON.stringify({ system, prompt }) }); return res.text(); // modelin ham metni: JSON bölüm yükünü içermeli }, }, }; ``` `config.ai` yoksa AI arayüzü hiç görünmez. ### Revizyon önizlemesi Editör adresine `?revision=` eklenirse taslak yerine o revizyonun anlık görüntüsü açılır ve editör **salt okunur** olur (kaydetme ve içe aktarma kilitlenir; iframe’de kayıt isteği `puck:saveError` döner). ## TecofEditor prop’ları | Prop | Tip | Varsayılan | Açıklama | | :--- | :--- | :--- | :--- | | `pageId` | `string` | zorunlu | Düzenlenecek sayfanın backend kimliği | | `config` | `StudioConfig` | zorunlu | Yayınla aynı bileşen kaydı | | `accessToken` | `string?` | — | Kayıt ve yazma isteklerinde `Authorization` başlığı (panelin verdiği JWT) | | `hostOrigin` | `string?` | `'*'` | iframe’e gömülü kullanımda izin verilen üst pencere origin’i; gelen ve giden `postMessage` bu origin’e kısıtlanır | | `autoSave` | `boolean?` | `false` | Düzenlemeler durulunca taslağı otomatik kaydet | | `autoSaveDelay` | `number?` | `2000` | Otomatik kayıt bekleme süresi (ms) | | `warnOnUnsavedChanges` | `boolean?` | `true` | Kaydedilmemiş değişiklik varken tarayıcının yerel `beforeunload` uyarısı | | `onChange` | `(data) => void` | — | ~300 ms debounce’lu değişiklik bildirimi | | `onSave` | `(data) => void` | — | Başarılı taslak kaydından sonra | | `onLanguageChange` | `(code: string) => void` | — | Aktif düzenleme dili ilk çözüldüğünde ve her değiştiğinde | | `panelUrl` | `string?` | `https://app.tecof.com` | Alanlardaki “Panelde yönet / Panelde ekle” bağlantılarının kökü (yeni sekmede açılır) | | `className` | `string?` | — | Studio kök elemanına ek sınıf | | `overrides`, `plugins` | — | — | Ayrılmış; şu an kullanılmıyor | `onLanguageChange` sözleşmesi: merchant bilgisi gelmeden boş dil (`""`) gönderilmez, aynı kod art arda iki kez gönderilmez ve her render’da yeni fonksiyon verseniz de ek çağrı olmaz. Tema bunu kendi i18n sağlayıcısına bağlayarak kanvas içeriğini o dilde gösterebilir; editör arayüzü Türkçe kalır. Kanvastaki bileşenler aynı dili `useActiveLanguage()` ile okuyabilir (Studio dışında `null` döner). ```tsx title="components/EditorClient.tsx" "use client"; import { useState } from "react"; import { NextIntlClientProvider, useMessages } from "next-intl"; import { TecofEditor } from "@tecof/theme-editor"; import { editorConfig } from "@/app/editor-config"; export default function EditorClient({ pageId, accessToken }: { pageId: string; accessToken?: string }) { const messages = useMessages(); const [canvasLocale, setCanvasLocale] = useState("tr"); return ( ); } ``` ## Pencereler ve onaylar Editördeki her pencere (ayarlar, yardım, bölüm ekleme, seçiciler, onaylar) aynı kabuğu kullanır: `StudioDrawer` (Radix Dialog, `modal={false}`; içinden açılan renk/ikon/CMS popover’ları tıklanabilir kalır). Host eklentileri aynı kabuğu ve onay kuyruğunu kullanabilir: ```tsx import { StudioDrawer, studioDialog } from "@tecof/theme-editor"; const ok = await studioDialog.confirm({ title: "Ortak bileşeni sil", description: "Bu bileşeni kullanan tüm sayfalar etkilenir.", confirmLabel: "Sil", danger: true, }); await studioDialog.alert({ title: "İçe aktarma tamamlandı" }); ``` | `StudioDrawer` prop’u | Açıklama | | :--- | :--- | | `open` / `onOpenChange` | Açıklık durumu | | `title` | Başlık (zorunlu; erişilebilirlik için de kullanılır) | | `description`, `icon` | Alt başlık ve rozet ikonu | | `tone` | `default` · `primary` · `danger` | | `size` | `sm` 400 · `md` 560 · `lg` 820 · `xl` 1180 px (varsayılan `md`) | | `busy` | İş sürerken kapatma yollarını kilitler | | `footer`, `headerActions`, `hideClose`, `elevated`, `inert` | Alt eylem satırı, baş satırı kontrolleri, X’i gizle, başka drawer’ın üstünde aç, etkileşimi kapat | Bir drawer açıkken Studio’nun genel kısayolları (`Delete`, `⌘Z`, `G` / `R` / `B` …) çalışmaz; denetim `isStudioDrawerOpen()` ile yapılır. ## Performans Monaco (kod), TipTap (zengin metin) ve FilePond/Doka (medya) gibi ağır alanlar ayrı chunk’lara bölünüp ihtiyaç anında yüklenir. Bileşen kütüphanesindeki önizleme görselleri tembel yüklenir. --- # Bileşen Yapılandırması > StudioConfig ve ComponentConfig başvurusu, defineSection/defineElement, ve , kök className sözleşmesi, dinamik alanlar, sürükle-bırak kuralları, varyantlar ve varsayılan çocuklar. > > Kaynak: https://tecof-app-dev.vercel.app/editor-components · Bölüm: Tema geliştirme · Son güncelleme: 2018-10-20 Studio’nun hangi blokları göstereceğini, blokların hangi alanlarla düzenleneceğini ve nasıl render edileceğini `StudioConfig` belirler. Editör ve yayın renderer’ı **aynı** nesneyi kullanmalıdır; kayıtlı bir `type` yayındaki config’te yoksa o düğüm sessizce render edilmez. ## StudioConfig | Alan | Tip | Açıklama | | :--- | :--- | :--- | | `components` | `Record` | Zorunlu. Anahtar, dokümanda `type` olarak saklanır | | `categories` | `Record` | Bileşen kütüphanesindeki kategoriler ve sırası | | `root` | `{ fields?, render? }` | Sayfa geneli alanlar (Inspector → Sayfa) ve sayfa sarmalayıcısı (`render`, `children` alır) | | `templates` | `SectionTemplate[]` | Tek bölüm şablonları ([Slotlar ve şablonlar](https://tecof-app-dev.vercel.app/editor-slots#bolum-sablonlari)) | | `pageTemplates` | `PageTemplate[]` | Çok bölümlü sayfa şablonları | | `theme` | `DeepPartialThemeConfig` | Temanın varsayılan renk/tipografi/boşluk değerleri ([Canlı tema](https://tecof-app-dev.vercel.app/editor-theme)) | | `themePresets` | `ThemePreset[]` | Tema panelinde hazır renk paletleri | | `darkMode` | `boolean \| { defaultMode?, storageKey? }` | Karanlık modu açar ([Canlı tema](https://tecof-app-dev.vercel.app/editor-theme#karanlik-mod)) | | `permissions` | `Partial` | Genel izinler ([İzinler](https://tecof-app-dev.vercel.app/editor-permissions)) | | `migrations` | `MigrationConfig` | Eski kayıtları yükseltme ([İzinler ve migration](https://tecof-app-dev.vercel.app/editor-permissions#veri-migrationi)) | | `ai` | `{ complete({ system, prompt }) }` | AI ile bölüm üretme ([Studio](https://tecof-app-dev.vercel.app/editor-studio#ai-ile-bolum-uretme)) | | `nodeInfoButton` | `boolean` | Seçim araç çubuğunda ⓘ bileşen bilgisi düğmesi; varsayılan kapalı | ## ComponentConfig Yalnız `render` zorunludur. | Alan | Tip | Açıklama | | :--- | :--- | :--- | | `render` | `(props) => ReactNode` | Editörde ve yayında kullanılan render | | `label` | `string?` | Kütüphanede görünen ad | | `category` | `string?` | Kategori anahtarı | | `fields` | `Record` | Inspector alanları ([Alanlar](https://tecof-app-dev.vercel.app/editor-fields)) | | `fieldsGroups` | `{ name, fields[] }[]` | Alanları akordeon gruplarına ayırır; listelenmeyenler grupların ardından düz gösterilir | | `defaultProps` | `object?` | Yeni düğümün başlangıç değerleri | | `defaultChildren` | `Record>` | Eklenince slotlara otomatik konan çocuklar (özyinelemeli, döngü korumalı) | | `variants` | `Record` | İsimli prop ön ayarları: Bölüm Ekle’de ayrı kart, Inspector’da çip | | `inline` | `boolean?` | Editörün sarmalayıcı `div`’ini kaldırır; bileşen `puck.dragRef`’i köke bağlamalıdır | | `acceptsChildren` | `boolean \| string[] \| Record` | Hangi çocuk tiplerini kabul ettiği (bileşen ya da slot bazında) | | `maxItems` | `number \| Record` | Doğrudan çocuk üst sınırı (bileşen ya da slot bazında) | | `allowedParents` | `string[]` | Bu bileşenin bırakılabileceği üst tipler; doluysa kök içeriğe bırakılamaz | | `resizable` | `boolean?` | Boyutlandırma modunda genişlik/yükseklik tutamaçları (varsayılan `true`) | | `permissions` / `resolvePermissions` | — | Bileşen ve düğüm bazlı izinler | | `resolveFields` | `(props, ctx) => fields` | Prop’lara göre dinamik alan seti (sync/async) | | `resolveData` | `(props, ctx) => { props?, readOnly? }` | Türetilmiş prop’lar ve salt okunur alanlar (sync/async) | | `metadata` | `object?` | Host’a özel veri; render’da `puck.metadata` içinde | Render fonksiyonuna gelen prop’lar: düğüm prop’ları, editörün birleştirdiği `className`, slot alanları için hazır slot elemanları, `editMode` ve `puck` (`renderDropZone`, `isEditing`, `metadata`, düzenlemede `registerOverlayPortal` / `dragRef`). ## Kök className sözleşmesi Editör seçim, hover ve stil sınıflarını bileşenin **kök DOM elemanına** `className` üzerinden ulaştırır. Bir bileşen `props.className`’i kökte basmazsa kanvasta **seçilemez** ve Stil sekmesinde verilen stiller görünmez. ```tsx // Yanlış: className kaybolur, düğüm seçilemez render: (p) =>
    {p.title}
    // Doğru render: (p) =>
    {p.title}
    ``` Bileşenin her dalı (ör. erken `return` ile boş durum) aynı sözleşmeye uymalıdır. `defineSection` / `defineElement` bunu otomatik yapar; elle yazarken `TecofRoot` kullanabilirsiniz: ```tsx import { TecofRoot } from "@tecof/theme-editor"; render: (p) => ( {p.title} ) ``` ## defineSection ve defineElement Bildirimsel bir tanımdan düz bir `ComponentConfig` üretir: - `slots` tek yerde tanımlanır; hem `type: "slot"` alanları hem `defaultChildren` buradan türetilir, - `render`’a ikinci argüman olarak hazır `` elemanları (`slots.`) verilir, - render’ın döndürdüğü kök elemanın `className`’ine `p.className` eklenir. ```tsx import { createLanguageField, defineSection } from "@tecof/theme-editor"; export const FAQSection = defineSection({ label: "Sıkça Sorulan Sorular", category: "sections", slots: { content: { label: "Başlık ve açıklama", layout: "col", gap: "md", default: ["Title", "Paragraph"] }, items: { label: "Sorular", layout: "col", gap: "sm", hideIfEmpty: true, className: (p) => p.compact && "max-w-2xl mx-auto", default: [{ type: "FAQItem" }, { type: "FAQItem" }], }, }, fields: { compact: { type: "toggle", label: "Dar düzen" }, eyebrow: createLanguageField({ label: "Üst başlık" }), }, defaultProps: { compact: false }, render: (p, slots) => (
    {slots.content}
    {slots.items}
    ), }); ``` | `slots.` seçeneği | Açıklama | | :--- | :--- | | `label` | Inspector’daki slot adı | | `layout` | `row` · `row-wrap` · `col` · `contents` · `none`; `row`/`row-wrap` slot alanını yatay (`orientation: "horizontal"`) yapar | | `gap` | `none` · `xs` .25 · `sm` .5 · `md` .75 · `lg` 1 · `xl` 1.5 rem ya da ham değer (`"1.25rem"`) | | `className` | Kapsayıcıya ek sınıf; fonksiyon verilirse her render’da prop’larla çağrılır | | `hideIfEmpty` | Yayında slot boşsa kapsayıcıyı hiç basma (editörde boş slot “Bileşen ekle” ipucuyla görünür kalır) | | `default` | Eklenince otomatik doğan çocuklar | | `fieldExtra` | Slot alanına eklenecek ek ayarlar (ör. `{ repeatSource: "items" }`) | | `allow`, `maxItems` | Slot alanına yazılır — aşağıdaki nota bakın | > [!WARNING] Slot alanındaki `allow` ve `maxItems` motor tarafından uygulanmaz > 0.0.96 sürümünde sürükle-bırak kuralları **bileşen düzeyindeki** `acceptsChildren`, `maxItems` ve çocuğun `allowedParents` alanlarından okunur. Slot alanına yazılan `allow` ya da `maxItems` editörde kısıtlama yapmaz. Kısıtlama gerekiyorsa `defineSection` çıktısına bileşen düzeyinde ekleyin: > > ```tsx > export const FAQSection = { > ...defineSection({ /* … */ }), > acceptsChildren: { items: ["FAQItem"] }, > maxItems: { items: 12 }, > }; > ``` > [!CAUTION] Bileşen elemanı döndüren render > `render` bir DOM elemanı yerine bileşen elemanı döndürürse (``), `className` o bileşene **prop** olarak geçer. Bileşen bunu kendi kök DOM’una basmazsa düğüm yine seçilemez. Hook gerektiren gövdeyi ayrı bileşene alırken `className` prop’unu kabul edip köke `cn(...)` ile geçirin. ## Slot bileşeni `` bir slot değerini tek satırda çizer ve yerleşimi tek kaynaktan (`layout` + `gap`) yönetir. `defineSection` kullanmayan bileşenlerde doğrudan kullanılır: ```tsx import { Slot } from "@tecof/theme-editor"; render: (p) => (
    ) ``` | Prop | Açıklama | | :--- | :--- | | `value` | Motorun enjekte ettiği slot değeri (`p.`) | | `layout`, `gap`, `className`, `style`, `hideIfEmpty` | Yukarıdaki tabloyla aynı | | diğerleri | `data-*`, `aria-*`, `id` gibi prop’lar slot kapsayıcısına aynen geçer | `` motorun varsayılan yatay yerleşimini (inline `flex`/`gap`) basmasını kapatır; böylece temanın Tailwind sınıfları ezilmez. Yerleşim sınıfları (`.tecof-slot-row`, `.tecof-slot-row-wrap`, `.tecof-slot-col`, `.tecof-slot-contents`) `@layer base` içindedir: editörde paketin `styles.css`’inden gelir, **yayında ise temanın global CSS’inde bulunmalıdır** (yayın sayfası paket CSS’ini yüklemez). ## Dinamik alanlar ve türetilmiş veri `resolveFields` prop’lara göre alan setini değiştirir; `resolveData` türetilmiş prop yazar ve alanları salt okunur yapar. İkisi de senkron ya da asenkron olabilir; geç gelen eski sonuçlar elenir, geri yazım yalnız gerçek farkı commit eder (idempotent olmalı). ```tsx ProductCard: { fields: { source: { type: "select", options: [{ label: "Elle", value: "manual" }, { label: "CMS", value: "cms" }] }, title: { type: "text" }, slug: { type: "text" }, }, resolveFields: (props, { fields }) => ({ ...fields, ...(props.source === "cms" ? { collection: createCmsCollectionField({ label: "Koleksiyon" }) } : {}), }), resolveData: (props) => ({ props: { slug: slugify(props.title ?? "") }, readOnly: { slug: true }, }), render: (props) =>
    {props.title}
    , } ``` `resolveFields` hata fırlatırsa statik `fields` kullanılır. ## Sürükle-bırak kuralları Kurallar hem kanvasta hem katman panelinde, dokunmatik sürüklemede de aynı şekilde uygulanır. Hiçbiri tanımlı değilse her bırakma serbesttir. ```tsx const config: StudioConfig = { components: { Slider: { acceptsChildren: { slides: ["Slide"] }, // slides slotu yalnız Slide alır maxItems: { slides: 8 }, fields: { slides: { type: "slot", orientation: "horizontal" } }, render: ({ slides, className }) =>
    {slides}
    , }, Slide: { allowedParents: ["Slider"], // yalnız Slider içine bırakılabilir render: ({ className }) =>
    , }, }, }; ``` ## Inline bileşenler Sarmalayıcı `div` düzeni bozuyorsa `inline: true` verin ve `puck.dragRef`’i kök elemana bağlayın: ```tsx InlineButton: { inline: true, fields: { text: { type: "text" } }, render: ({ puck, text, className }) => ( ), } ``` ## Varyantlar ```tsx Button: { fields: { text: { type: "text" }, tone: { type: "radio", options: [{ label: "Ana", value: "primary" }, { label: "Çizgili", value: "outline" }] } }, variants: { primary: { label: "Ana buton", props: { tone: "primary" } }, outline: { label: "Çizgili buton", props: { tone: "outline" } }, }, render: (p) => {p.text}, } ``` Her varyant Bölüm Ekle penceresinde kendi önizlemeli kartı olarak görünür; Inspector’daki çip varyantı mevcut düğüme uygular (geri alınabilir, `_variant` işaretiyle). ## Kategoriler ve sayfa kökü ```tsx export const editorConfig: StudioConfig = { components, categories: { sections: { title: "Sayfa bölümleri", components: ["Header", "Hero", "Footer"] }, elements: { title: "Elementler", components: ["Title", "Paragraph", "Button"] }, }, root: { fields: { enableEcommerce: { type: "radio", label: "E-ticaret özellikleri", options: [{ label: "Aktif", value: "yes" }, { label: "Pasif", value: "no" }] }, }, }, }; ``` Sayfa geneli değerler dokümanın `root.props` alanında saklanır; tema yayın tarafında bunları `data.root.props` üzerinden okur. Editör 0.0.96 `root.defaultProps` alanını okumaz: değer hiç kaydedilmemiş sayfalar için varsayılanı temada verin (ör. `data.root.props.enableEcommerce !== "no"`). --- # Alanlar > Tecof Studio alan başvurusu — yerleşik tipler, çok dilli metin, zengin metin, medya, bağlantı, renk, kod, ikon, tekrar eden satırlar, harici/API kaynakları, CMS ve e-ticaret seçicileri; saklanan değer biçimleri. > > Kaynak: https://tecof-app-dev.vercel.app/editor-fields · Bölüm: Tema geliştirme · Son güncelleme: 2018-10-20 Alanlar Inspector’da düzenlenen prop’ları tanımlar. Yerleşik tipler doğrudan `{ type: "…" }` olarak yazılır; gelişmiş alanlar `create*Field()` fabrikalarıyla üretilir. Fabrikalar `{ type: "custom", _fieldType, label, render }` döndürür, yani Puck uyumlu config’e doğrudan konur. ## Yerleşik tipler | `type` | Değer | Önemli seçenekler | | :--- | :--- | :--- | | `text`, `textarea` | `string` | `bindable` (CMS bağlama düğmesi, varsayılan `true`), `readOnly` | | `select`, `radio` | seçilen `value` | `options: [{ label, value }]` | | `number` | `number` | — | | `boolean`, `toggle` | `boolean` | `onLabel`, `offLabel` (varsayılan `Açık` / `Kapalı`) | | `range` | `number` | `min` (0), `max` (100), `step` (1), `unit` (ör. `"px"`) | | `array` | `object[]` | `arrayFields` | | `object` | `object` | `objectFields` | | `slot` | (zone) | `orientation`, `repeatSource`, `itemSchema` — bkz. [Slotlar](https://tecof-app-dev.vercel.app/editor-slots) | Her alan `label`, `defaultValue` ve `readOnly` alabilir. Dinamik salt okunurluk `resolveData().readOnly` ile gelir; ikisi VEYA’lanır. ## Ortak seçenekler Tüm fabrikalar şunları destekler: | Seçenek | Tip | Açıklama | | :--- | :--- | :--- | | `label` | `string` | Inspector etiketi | | `labelIcon` | `ReactElement` | Etiketin yanındaki ikon (ör. ``) | | `visible` | `boolean` | Alanın panelde görünüp görünmediği | ## LanguageField — çok dilli metin Merchant dillerini `merchant-info`’dan alır ve dil başına değer tutar. Studio içinde üst çubuktaki **global dil** kullanılır ve alan içindeki dil sekmeleri gizlenir; Studio dışında sekmeli moda döner. ```tsx import { createLanguageField } from "@tecof/theme-editor"; fields: { title: createLanguageField({ label: "Başlık" }), description: createLanguageField({ label: "Açıklama", isTextarea: true, textareaRows: 4 }), html: createLanguageField({ label: "HTML içerik", isHtml: true }), } ``` | Seçenek | Varsayılan | Açıklama | | :--- | :--- | :--- | | `isTextarea` | `false` | Çok satırlı giriş | | `textareaRows` | `3` | Satır sayısı | | `placeholder` | `''` | Yer tutucu | | `isHtml` | `false` | Çeviride HTML etiketlerini korur | **Değer:** `[{ code: "tr", value: "Merhaba" }, { code: "en", value: "Hello" }]`. Temada aktif dilin değerini seçin; değer yoksa varsayılan dile düşmek temanın sorumluluğundadır: ```ts const pick = (value: Array<{ code: string; value: string }> | string | undefined, locale: string, fallback = "tr") => typeof value === "string" ? value : (value?.find((v) => v.code === locale)?.value || value?.find((v) => v.code === fallback)?.value || ""); ``` **Dil araçları:** Birden fazla dil varken alan üstünde **Hızlı Doldur** (aktif sekmedeki metni tüm dillere kopyalar) ve **Çevir** (API üzerinden diğer dillere çevirir; `isHtml` ile etiketler korunur) düğmeleri görünür. Aynı çubuk `EditorField`, `LinkField` (yalnız Hızlı Doldur, yalnız boş diller) ve `LocalizedUploadField`’da da vardır. Kendi çok dilli alanınızda aynı araçları kullanabilirsiniz: ```tsx import { LanguageToolsBar, useLanguageToolsStatus, normalizeLocalizedValues, fillLanguages, translateLanguages, isEmptyText, LANGUAGE_TOOL_MESSAGES, } from "@tecof/theme-editor"; const { status, translating, setTranslating, flash } = useLanguageToolsStatus(); const values = normalizeLocalizedValues(value, languages, () => ""); const fill = () => { const res = fillLanguages(values, languages, activeTab, { isEmpty: isEmptyText }); if (!res) return; // kaynak boş ya da doldurulacak dil yok onChange(res.values); flash({ text: LANGUAGE_TOOL_MESSAGES.filledAll, type: "success" }, 2000); }; const translate = async () => { setTranslating(true); try { const out = await translateLanguages({ values: () => valuesRef.current, // getter: yanıt gelene kadar yazılanlar korunur languages, sourceCode: activeTab, translate: apiClient.translate.bind(apiClient), }); if (out.ok) onChange(out.values); flash(out.ok ? { text: LANGUAGE_TOOL_MESSAGES.translated, type: "success" } : { text: out.message, type: "error" }, 3000); } finally { setTranslating(false); } }; ; ``` `fillLanguages(values, languages, sourceCode, { onlyEmpty, isEmpty, clone })` → `{ values, filled } | null`. `onlyEmpty: true` dolu dilleri ezmez ve listede olmayan dil kodlarını korur. Boşluk tanımları: `isEmptyText`, `isEmptyHtml` (medya etiketi dolu sayılır), `isEmptyLink`. ## EditorField — zengin metin TipTap tabanlı, çok dilli WYSIWYG editör (kalın, italik, bağlantı, liste, başlık…). Hızlı Doldur boş `

    ` içerikte pasiftir; Çevir yalnız `TecofProvider` varken görünür. Araçlar aktif dilin içeriğini değiştirmediği için imleç korunur. ```tsx fields: { content: createEditorField({ label: "İçerik" }) } ``` ## UploadField — medya Seçili medya kare önizleme kutuları olarak görünür; “ekle” kutusu sekmeli bir medya penceresi açar: **Kütüphane** (sunucudaki dosyalar), **Yükle** (FilePond), **Referans** (CMS değişkeni, `{{ data.alan }}`). Doka görsel düzenleyici (kırp, döndür, ayarlar), istemci tarafı WebP sıkıştırma ve **odak noktası** seçimi içerir. ```tsx fields: { gallery: createUploadField({ label: "Ürün galerisi", allowMultiple: true, maxFiles: 10, maxFileSize: "50MB", acceptedTypes: ["image/*"], folder: "/products", }), document: createUploadField({ label: "Katalog", allowMultiple: false, acceptedTypes: ["application/pdf"] }), } ``` | Seçenek | Varsayılan | Açıklama | | :--- | :--- | :--- | | `allowMultiple` | `true` | Çoklu dosya | | `maxFiles` | `100` | En çok dosya | | `maxFileSize` | `"100MB"` | Tek dosya sınırı | | `maxTotalFileSize` | `"200MB"` | Toplam sınır | | `acceptedTypes` | tümü | MIME türleri (`["image/*"]`, `["application/pdf"]`) | | `folder` | `"/"` | Yükleme klasörü | | `allowReorder` | `true` | Sürükleyerek sıralama | | `imageCompressionEnabled` | `true` | Yüklemeden önce sıkıştırma | | `imageCompressionOptions` | `{ maxSizeMB: 1, maxWidthOrHeight: 1920 }` | Sıkıştırma ayarları (`fileType` de verilebilir) | | `imagePreviewHeight` | — | FilePond önizleme yüksekliği | | `showUploadedFiles` | `false` | Kullanımdan kalktı; yeni arayüzde etkisiz | **Değer:** `UploadedFile[]` — `{ _id, name, size, type, mimeType?, url?, folder?, provider?, focalPoint?, meta? }`. Görseli temada `TecofPicture` ile basın ([Yayın render’ı](https://tecof-app-dev.vercel.app/editor-render#tecofpicture)). **Odak noktası:** görsel kutusundaki nişangâh düğmesi odak penceresini açar (tıkla/sürükle, ok tuşlarıyla 1’er, `Shift` ile 10’ar adım; 16:9, 1:1, 9:16 kırpma önizlemeleri). Seçim dosyanın içine `focalPoint: { x, y }` (yüzde, 50/50 merkez) olarak yazılır; `TecofPicture` bunu `object-position` olarak uygular. Backend’de şema değişikliği gerekmez. ## LocalizedUploadField — dile göre medya `UploadField`’ın dil sekmeli sürümü; seçenekleri aynıdır. Her dil için ayrı dosya listesi tutar. ```tsx fields: { banner: createLocalizedUploadField({ label: "Kampanya görseli", allowMultiple: false }), } defaultProps: { banner: [] } ``` **Değer:** `[{ code: "tr", value: UploadedFile[] }, { code: "en", value: [] }]`. Yayında istenen dil boşsa varsayılan dile, o da boşsa ilk dolu dile düşülür: ```tsx import { resolveLocalizedUploadFile, TecofPicture } from "@tecof/theme-editor"; const file = resolveLocalizedUploadFile(props.banner, locale, "tr"); // UploadedFile | null return file ? : null; ``` Çoklu dosya için `resolveLocalizedUpload(value, locale, defaultLanguage)` → `UploadedFile[]`. Eski düz `UploadedFile[]` kayıtlar varsayılan dil altında açılır ve ilk düzenlemede yeni biçimde kaydedilir. Hızlı Doldur yalnız boş dillere kopyalar; Çevir yoktur. Ters yön (yerelleştirilmiş alanı tekrar `createUploadField`’a çevirmek) desteklenmez. ## LinkField — sayfa veya URL Mağaza sayfalarından seçim (aranabilir pencere, yayın durumu göstergesi) ya da elle URL girişi. Dil sekmelidir; değer dil başına saklanır. ```tsx fields: { cta: createLinkField({ label: "CTA bağlantısı", showTarget: true, placeholder: "https://…" }), } ``` | Seçenek | Varsayılan | Açıklama | | :--- | :--- | :--- | | `showTarget` | `true` | Aynı / yeni sekme seçimi | | `placeholder` | `https://...` | URL yer tutucusu | **Değer:** `[{ code: "tr", value: LinkFieldValue }, …]`; `LinkFieldValue = { url, label?, target?: "_self" | "_blank", type?: "page" | "custom" | "category" | "brand" | "product" | "cms" }`. Render yalnız `url` ve `target` okur. Eski düz `LinkFieldValue` kayıtlar mağaza dillerine göre normalize edilir. ## ColorField — renk Popover renk seçici: doygunluk/parlaklık alanı, ton ve opaklık kaydırıcıları, HEX/RGB/HSL girdileri, ekrandan renk seçme (EyeDropper), canlı tema paleti, 22 tonluk Tailwind paleti, son kullanılanlar ve WCAG kontrast önizlemesi. Tam klavye erişimi vardır. ```tsx fields: { bgColor: createColorField({ label: "Arka plan" }), overlay: createColorField({ label: "Kaplama", showOpacity: true, defaultColor: "#00000080" }), accent: createColorField({ label: "Vurgu", themeVars: true }), brand: createColorField({ label: "Marka", swatches: ["#18181b", "#74b500", "#ffffff"], palette: false }), } ``` | Seçenek | Varsayılan | Açıklama | | :--- | :--- | :--- | | `showOpacity` | `false` | Opaklık; değer `#rrggbbaa` | | `defaultColor` | `''` | Sıfırlama hedefi | | `placeholder` | `'#000000'` | HEX girdisi yer tutucusu | | `showReset` | `true` | Değer varsayılandan farklıyken sıfırla düğmesi | | `swatches` | `[]` | “Hızlı seçim” satırı | | `themeColors` | `true` | Canlı tema renkleri (yalnız Studio içinde) | | `themeVars` | `false` | Tema rengini `var(--theme-color-)` olarak bağlar; kapalıyken açık paletteki hex kopyalanır | | `palette` | `true` | Tailwind paleti | | `contrast` | `true` | Kontrast çipleri (WCAG AA/AAA) | | `contrastAgainst` | — | Kontrast zemini (hex) | **Değer sözleşmesi:** `''`, `#rrggbb`, `#rrggbbaa` (yalnız `showOpacity`) ya da `var(--theme-color-*)` (yalnız `themeVars`). `rgb()/hsl()/transparent` girdileri hex’e çevrilir; tanınmayan değerler korunur. Hex matematiği yapan tema kodu `var()` beklememelidir. Saf yardımcılar da dışa aktarılır: `parseColor`, `formatHex`, `normalizeColorValue`, `classifyColorValue`, `contrastRatio`, `wcagLevel`, `themeColorVar`, `parseThemeColorVar`, `THEME_COLOR_KEYS`, `toThemeCssKey`. ## CodeEditorField — kod Monaco tabanlı kod editörü (ilk kullanımda tembel yüklenir). ```tsx fields: { customHtml: createCodeEditorField({ label: "Özel kod", defaultLanguage: "html" }), json: createCodeEditorField({ label: "Yapılandırma", defaultLanguage: "json", height: "240px" }), } ``` Seçenekler: `defaultLanguage`, `height`, `theme`. ## RepeaterField — tekrar eden satırlar Alt alan setini (`subFields`) satırlar hâlinde düzenler: ekle, sil, çoğalt, sürükleyerek sırala, satırı katla. ```tsx fields: { items: createRepeaterField({ label: "Öğeler", subFields: { title: createLanguageField({ label: "Başlık" }), link: createLinkField({ label: "Bağlantı" }), }, minItems: 1, maxItems: 6, defaultRow: { title: "", link: null }, }), } ``` **Değer:** `Record[]`. Bir slot’un `repeatSource`’u olarak kullanılabilir ([Repeat zone](https://tecof-app-dev.vercel.app/editor-slots#repeat-zone-oge-sablonu)). ## IconField — ikon `lucide-react` setinden aranabilir ikon seçici (arama yoksa ilk 120 ikon). **Değer** ikonun adıdır (`"ShoppingCart"`); bileşeni render’da siz çözersiniz: ```tsx import { icons } from "lucide-react"; const Icon = icons[props.icon as keyof typeof icons]; return Icon ? : null; ``` ## ExternalField — harici tek kayıt Kendi API’nizden aranabilir bir pencerede **tek kayıt** seçtirir. ```tsx fields: { product: createExternalField({ label: "Ürün", fetchList: async ({ query }) => (await fetch(`/api/products?q=${query ?? ""}`)).json(), mapProp: (row) => ({ id: row.id, name: row.name }), mapRow: (row) => ({ Ad: row.name, SKU: row.sku }), getItemSummary: (value) => value?.name ?? "", placeholder: "Ürün seçin", }), } ``` | Seçenek | Açıklama | | :--- | :--- | | `fetchList({ query?, filters? })` | Zorunlu. Satırları getirir | | `mapProp(row)` | Prop’a yazılacak değer (varsayılan satırın kendisi) | | `mapRow(row)` | Pencere tablosunun sütunları | | `getItemSummary(value)` | Seçili değerin etiketi | | `showSearch` | Arama kutusu (varsayılan `true`) | ## ApiListField — API’den liste Kendi API’nizden **liste** akıtır; bir slot’un `repeatSource`’una bağlanınca öğe şablonu her satır için tekrarlanır. ```tsx fields: { source: createApiListField({ label: "Ürünler", fetchList: async ({ query, limit }) => (await fetch(`/api/products?q=${query ?? ""}&limit=${limit ?? 8}`)).json(), itemSchema: [{ key: "name", label: "Ad", type: "text" }, { key: "image", label: "Görsel", type: "image" }], defaultLimit: 8, }), card: { type: "slot", orientation: "horizontal", repeatSource: "source" }, } ``` **Değer:** `{ query?: string; limit?: number }` — dokümana yalnız sorgu parametreleri yazılır; satırlar render anında çözülür (oturum içi önbellek, eşzamanlı istek tekleme). Seçenekler: `showQuery`, `showLimit` (varsayılan `true`), `defaultLimit`. ## E-ticaret seçicileri Mağazanın kendi kayıtlarından seçim yaptırır; slug ya da ObjectId elle yazdırmaz. ```tsx import { createCategoryField, createCategoryListField, createProductField, createProductListField, createBrandField, createBrandListField, createTagField, createTagListField, createAttributeField, createAttributeListField, createVariantTypeField, createVariantField, createFlashSaleField, createCampaignField, createDiscountField, } from "@tecof/theme-editor"; fields: { category: createCategoryField({ label: "Kategori" }), featured: createProductField({ label: "Öne çıkan ürün" }), tags: createTagListField({ label: "Etiketler", max: 6 }), coupon: createDiscountField({ label: "Kupon" }), } ``` Saklanan değer seçim anında donan bir **anlık görüntüdür**; yayın ek istek atmadan render edebilir: | Alan | Değer | | :--- | :--- | | `createCategoryField` | `{ id, slug, name, path }` | | `createProductField` | `{ id, slug, name, image }` | | `createBrandField` | `{ id, slug, name, image }` | | `createTagField` | `{ id, slug, name }` | | `createAttributeField` | `{ id, slug, name, type, isFilterable, options: [{ value, label, colorCode }] }` | | `createVariantTypeField` | `{ id, name, selectionType, values: [{ label, colorCode }] }` | | `createVariantField` | `{ productId, productSlug, productName, variantId, sku, title, price }` | | `createFlashSaleField` | `{ id, name, startDate, endDate, showCountdown, bannerText, highlightColor, discountType, discountValue }` | | `createCampaignField` | `{ id, name, status, startDate, endDate, discountCode }` | | `createDiscountField` | `{ id, code, title, type, value, minCartAmount, maxDiscountAmount, startDate, endDate }` | - `*ListField` varyantlarında değer bu nesnelerin dizisidir; editördeki sıra korunur. Seçenekler: `label`, `placeholder`, `visible`, listelerde `max`. - **Neden hem `id` hem `slug`:** ürün listeleme ucu markayı ve etiketi **ObjectId**, kategoriyi **slug** ile filtreler. Temada filtreyi kimlikle gönderin: `getProducts({ brand: props.brand?.id, tag: props.tag?.id })`. - **Veri kaynağı:** kategori/ürün/marka vitrin uçlarından (`/api/store/ecommerce/*`); etiket, özellik, varyant tipi, flaş satış, kampanya ve kupon panel uçlarından (`/api/merchant/ecommerce/*`) gelir. Panel uçları JWT’ye ek olarak `x-merchant-id` ister; `TecofApiClient` bunu `merchant-info`’dan bir kez çözer. Kimlik çözülemezse liste boş değil **hata** gösterir. - **Anlık görüntü donar:** markanın adı ya da kampanyanın tarihi panelde sonradan değişirse sayfa eski değeri gösterir; alanı yeniden seçin. - **Boş durum:** liste boşsa “Panelde ürün ekle” gibi bir bağlantı, doluysa başlıkta “Panelde yönet” görünür; adresler `panelUrl` üzerine kurulur ve yeni sekmede açılır. Panelde kayıt ekledikten sonra seçicideki **Yenile** listeyi tazeler. - **Geriye uyum:** var olan bir metin alanının yerine koyarken eski prop’u okumaya devam edin: `props.brand?.id ?? props.brandIdText ?? ""`. ## CmsCollectionField — koleksiyon bağlama Bir bileşeni CMS koleksiyonuna bağlar: koleksiyon, adet ve sıralama seçilir; bileşenin veri yuvaları koleksiyon alanlarına eşlenir. ```tsx fields: { source: createCmsCollectionField({ label: "Veri kaynağı", defaultLimit: 6, slots: { title: { label: "Başlık", fieldTypes: ["text"] }, image: { label: "Görsel", fieldTypes: ["image"] }, link: { label: "Bağlantı" }, }, }), } ``` | Seçenek | Varsayılan | Açıklama | | :--- | :--- | :--- | | `defaultLimit` | `10` | Varsayılan öğe sayısı | | `showLimit`, `showSort` | `true` | Adet ve sıralama (özel / yeni → eski / eski → yeni) kontrolleri | | `slots` | — | Bileşenin ihtiyaç duyduğu veri yuvaları | **Değer:** `{ collectionSlug, collectionName?, limit?, sort?: "newest" | "oldest" | "custom", fieldMap?: Record }`. Bir repeat slot’a kaynak olarak bağlanırsa satırlar koleksiyondan otomatik çekilir (ağaçta `TecofProvider` gerekir). ## CMS veri bağlama (metin alanları) `text` ve `textarea` alanlarının yanında bir **`{ }`** düğmesi görünür; koleksiyon → alan seçilince alana `{{ data. }}` token’ı eklenir. Yayında token, `TecofRender`’a verilen `cmsData` kaydıyla çözülür. Kapatmak için `bindable: false`: ```tsx fields: { title: { type: "text", label: "Başlık" }, // bağlama açık staticLabel: { type: "text", label: "Sabit etiket", bindable: false }, } ``` Repeat zone şablonunun içindeyken aynı düğme **Öğe alanları** bölümünü de gösterir (`{{ item. }}`). ## Hata yalıtımı Fabrikaların ürettiği alanlar `FieldErrorBoundary` içinde render edilir; bir alanın hatası Inspector’ı düşürmez. Kendi `render` alanlarınızda da aynı bileşeni kullanabilirsiniz. --- # Slotlar ve Şablonlar > Zone modeli, dikey ve yatay slotlar, repeat zone (öğe şablonu) ve veri kaynakları, slider deseni, bölüm ve sayfa şablonları, id güvenliği — canlı repeat zone önizlemesiyle. > > Kaynak: https://tecof-app-dev.vercel.app/editor-slots · Bölüm: Tema geliştirme · Son güncelleme: 2018-10-20 Slotlar bir bileşenin içine başka bileşenlerin bırakılabildiği bölgelerdir. Bu sayfa slotların saklanma biçimini, yatay/dikey yerleşimi, verinin her satırı için tekrarlanan **repeat zone**’ları ve tek tıkla eklenen şablonları anlatır. ## Zone modeli Kök düğümler dokümanın `content` dizisinde, iç içe düğümler `zones` nesnesinde tutulur. Zone anahtarı `parentId:slotAdı` biçimindedir: ```json { "content": [{ "type": "Hero", "props": { "id": "hero-1", "title": "Yeni sezon" } }], "zones": { "hero-1:actions": [ { "type": "Button", "props": { "id": "btn-1", "text": "Keşfet" } }, { "type": "Button", "props": { "id": "btn-2", "text": "İncele" } } ] }, "root": { "props": {} } } ``` Üst düğüm silindiğinde alt zone ağacı da temizlenir. Zone anahtarı düğüm **id**’sine bağlı olduğu için bir bileşen tipinin adını değiştirmek zone anahtarlarını etkilemez. ## Dikey ve yatay slotlar Slot alanları varsayılan olarak çocukları alt alta dizer. `orientation: "horizontal"` verildiğinde editör bırakma eksenini sol/sağ olarak hesaplar ve yayın aynı düzeni korur. ```tsx fields: { columns: { type: "slot", label: "Kolonlar", orientation: "horizontal" }, blocks: { type: "slot", label: "Bloklar" }, // dikey } ``` Slot’u render’da çizmenin üç yolu vardır; yeni kodda ilk ikisini kullanın: ```tsx // 1) defineSection: hazır elemanı render: (p, slots) =>
    {slots.columns}
    // 2) bileşeni: yerleşim tek kaynaktan render: (p) => (
    ) // 3) Düşük seviye: puck.renderDropZone render: ({ puck, className }) => (
    {puck.renderDropZone({ zone: "columns", orientation: "horizontal" })}
    ) ``` Düz `renderDropZone` ile yatay slot yayında inline `display:flex; flex-wrap:wrap; gap:8px` alır; `` ise bu varsayılanı kapatıp yerleşimi `layout` / `gap` / `className`’den kurar ([Slot bileşeni](https://tecof-app-dev.vercel.app/editor-components#slot-bileseni)). ## Repeat zone (öğe şablonu) Bir slot alanına `repeatSource` verildiğinde slot bir **öğe şablonuna** dönüşür: kart bir kez tasarlanır ve veri listesindeki her satır için tekrarlanır (ürün ızgarası, koleksiyon listesi, SSS…). Şablondaki prop’lar satıra `{{ item. }}` token’larıyla bağlanır. ```tsx title="ProductGrid.tsx" ProductGrid: { label: "Ürün ızgarası", fields: { items: createRepeaterField({ label: "Ürünler", subFields: { name: { type: "text", label: "Ad" }, price: { type: "text", label: "Fiyat" }, image: createUploadField({ label: "Görsel", allowMultiple: false }) }, }), card: { type: "slot", label: "Kart şablonu", orientation: "horizontal", repeatSource: "items" }, }, render: ({ card, className }) =>
    {card}
    , } ``` Şablondaki bir Text düğümünün `text` prop’u `"{{ item.name }}"`, fiyat satırı `"Fiyat: {{ item.price }} TL"` olabilir. - **Tam token ham değeri geçirir:** prop’un değeri yalnız `"{{ item.image }}"` ise görsel/bağlantı nesnesi olduğu gibi iletilir; metne gömülü token metne çevrilir. - **Özel yollar:** `{{ item._index }}` (0 tabanlı), `{{ item._position }}` (1 tabanlı). - **Token’ları kütüphane çözer:** `{{ item.* }}` hem editör kanvasında hem `TecofRender`’da çözülür; host tarafında iş gerekmez. (`{{ data.* }}` ise CMS kaydından, `cmsData` ile çözülür.) - **Editörde** şablon ilk satırın verisiyle bir kez düzenlenir; kalan satırlar etkileşimsiz, soluk kopyalar olarak aynı grid/flex akışında görünür. - **Yayında** döngü gerçek veriyle döner; satır yoksa şablon **hiç** render edilmez — canlı sitede ham token görünmez. > [!WARNING] Bağlı metni kanvasta düzenlemeyin > Şablonda bağlı bir metne çift tıklayıp satır içi düzenlemek token’ı literal değerle ezer ve bağı koparır. Bağlı alanları Inspector’dan düzenleyin; Inspector her zaman ham token’ı gösterir. ### Veri kaynakları `repeatSource`’un gösterdiği prop’un değerine göre satırlar otomatik çözülür: | Kaynak | Satırlar nereden gelir | | :--- | :--- | | Dizi prop / `createRepeaterField` | Dokümanda kayıtlı satırlar | | `createApiListField` | Host’un `fetchList` fonksiyonu; editörde ve yayında (istemcide) kütüphane çağırır | | `createCmsCollectionField` | CMS koleksiyonu; ağaçta `TecofProvider` gerekir (yoksa satırlar boş kalır) | | `renderDropZone({ zone, repeatItems })` | Bileşen satırları render anında kendisi verir (tam kontrol) | ```tsx // Manuel yol: veriyi bileşen kendisi getirir render: ({ puck }) => { const { rows } = useMyProducts(); return puck.renderDropZone({ zone: "card", repeatItems: rows }); }, ``` Bağlama penceresindeki **Öğe alanları** listesi sırasıyla slot’un `itemSchema`’sından, kaynak alanın `itemSchema`’sından ya da çözülen ilk satırdan çıkarılır. Yalnız manuel `repeatItems` yolunda `itemSchema` bildirmeniz gerekir: ```tsx card: { type: "slot", itemSchema: [ { key: "name", label: "Ürün adı", type: "text" }, { key: "image", label: "Görsel", type: "image" }, ], } ``` | Dışa aktarım | Ne işe yarar | | :--- | :--- | | `useRepeatItem()` | Şablon bileşeni içinde `{ item, index, count }`; şablon dışında `null` | | `resolveItemTokens(value, item, index)` | Renderer’ların kullandığı token çözücü (host’un kendi render’ı için) | | `resolveRepeatRows(value, sourceFieldDef, apiClient?)` | Satırları React dışında çözer (ör. SSR ısıtma) | | `useRepeatRows(value, sourceFieldDef)` | Aynı çözümleme, React kancası olarak | | `clearRepeatRowsCache()` | API/CMS satır önbelleğini temizler | | `findRepeatScope(doc, config, nodeId)` | Bir düğümün içinde bulunduğu repeat şablonunu bulur | > [!NOTE] Repeat slotlarda yayın yerleşimi `orientation`’dan gelir > 0.0.96 sürümünde `TecofRender`, `repeatSource`’lu bir slotu kendi iç bileşeniyle çizer ve ``’un `layout` / `gap` / `className` değerlerini bu bileşene **iletmez**. Yayında yatay repeat slot `orientation: "horizontal"` varsayılanını (inline `flex-wrap`, `gap: 8px`) kullanır. Kart aralığını kartın kendi kenar boşluğuyla ya da kapsayıcı bölümün sınıflarıyla verin; yukarıdaki önizleme bu davranışı gösterir. ## Slider / carousel deseni Slaytlar ayrı bir bileşen olarak slota bırakılır. Kritik nokta `editMode` ayrımıdır: editörde tüm slaytlar yan yana görünüp sürükle-bırak almalı, yayında aynı zone kaydırılabilir bir carousel olmalıdır. ```tsx title="Slider.tsx" const Slider = { label: "Slider", acceptsChildren: { slides: ["Slide"] }, maxItems: { slides: 8 }, fields: { slides: { type: "slot", label: "Slaytlar", orientation: "horizontal" } }, render: ({ puck, editMode, className }) => (
    {puck.renderDropZone({ zone: "slides", orientation: "horizontal", className: editMode ? "gap-4" : "snap-x snap-mandatory overflow-x-auto gap-4", style: editMode ? undefined : { flexWrap: "nowrap" }, })}
    ), }; const Slide = { label: "Slayt", allowedParents: ["Slider"], fields: { image: createUploadField({ label: "Görsel", allowMultiple: false }), caption: { type: "text", label: "Başlık" } }, render: ({ image, caption, className }) => (
    {caption &&
    {caption}
    }
    ), }; ``` - `snap-*` sınıfları slaydın kendi kökündedir; yayında zone çocukları doğrudan render edildiği için scroll-snap birebir çalışır. - Swiper/Embla gibi DOM’u klonlayan ya da ölçen kütüphaneleri yalnız `editMode === false` iken bağlayın; editörde zone düz düzende kalmalıdır ki seçim ve bırakma hedefleri doğru çalışsın. Çoğu senaryoda CSS scroll-snap yeterlidir. ## Bölüm şablonları `config.templates`, **Bölüm Ekle** penceresinde ayrı sekmede gösterilen hazır bölümlerdir. Şablon bir alt ağaçtır: kök düğüm ve alt zone’ları. ```tsx templates: [ { id: "hero-cta", label: "Hero + iki buton", category: "Dönüşüm", thumbnail: "/templates/hero-cta.webp", payload: { node: { type: "Hero", props: { id: "t-hero", title: "Başlık" } }, zones: { "t-hero:actions": [ { type: "Button", props: { id: "t-b1", text: "Başla" } }, { type: "Button", props: { id: "t-b2", text: "İncele" } }, ], }, }, }, ], ``` | Alan | Tip | Açıklama | | :--- | :--- | :--- | | `id` | `string` | Kararlı kimlik | | `label` | `string` | Kartta görünen ad | | `category` | `string?` | Kategori | | `thumbnail` | `string?` | Önizleme görseli (yoksa ikon) | | `payload.node` | `TecofNode` | Eklenecek kök düğüm | | `payload.zones` | `Record?` | Kök düğümün alt zone’ları (`nodeId:slotAdı`) | ## Sayfa şablonları `config.pageTemplates` tüm sayfayı kuran bölüm dizileridir (Ana Sayfa, Hakkımızda, İletişim…). Sol paneldeki **Sayfa şablonları** bölümünde ve Bölüm Ekle penceresinde görünür. ```tsx pageTemplates: [ { id: "core-home", label: "Ana sayfa", description: "Hero, hizmetler, referanslar ve iletişim çağrısı", keywords: ["kurumsal", "landing"], previewUrl: "/preview-template/core-home", sections: [ { node: { type: "Hero", props: { id: "t-hero", title: "Başlık" } } }, { node: { type: "Services", props: { id: "t-svc" } }, zones: { "t-svc:items": [] } }, ], }, ], ``` Şablona tıklamak bölümleri doğrudan eklemez; önce bir onay penceresi açılır. `previewUrl` varsa sayfa 1280 px referans genişlikte bir iframe’de (tembel, sandbox’lı, tıklanamaz) gösterilir; göreli adres editörün çalıştığı origin’e göre çözülür. Yoksa bölümlerin canlı önizlemesi üst üste dizilir. Onaydan sonra mevcut içerik silinmez; bölümler eklenir ve tamamı **tek geri al** adımıdır. ## Id güvenliği Şablondaki id’ler yalnız ilişki kurmak için örnek kimliklerdir. Studio her eklemede kök ve tüm alt düğümler için yeni id üretir ve zone anahtarlarını buna göre yeniden yazar; aynı şablonu güvenle birden çok kez ekleyebilirsiniz. Kopyala/yapıştır ve çoğaltma için de aynı kural geçerlidir. --- # Stil Editörü > Studio stil editörü — _tecofStyles token modeli, 54 kontrol, masaüstü-öncelikli kırılımlar, hover/focus/active, serbest değerler, renk kaynakları, TecofRender’ın ürettiği CSS, safelist ve sayfalar arası stil senkronu. > > Kaynak: https://tecof-app-dev.vercel.app/editor-styles · Bölüm: Tema geliştirme · Son güncelleme: 2018-10-20 Inspector’daki **Stil** sekmesi seçili düğüme görsel stil uygular. Stiller ham class dizisi olarak değil, düğümün `_tecofStyles` prop’unda **yapısal token**’lar olarak saklanır; editör ve `TecofRender` bunları aynı yoldan sınıflara ve CSS’e çevirir. ## Token modeli ```json { "base": { "p": "8", "bg": "primary-500", "radius": "2xl" }, "sm": { "p": "4" }, "states": { "hover": { "bg": "primary-600" }, "md:hover": { "bg": "primary-700" } } } ``` `compileStyles()` bu modeli sınıf dizisine çevirir; her sınıf `!` (important) son ekiyle üretilir ki bileşenin kendi sınıflarını yensin: ```text p-8! bg-primary-500! rounded-2xl! sm:p-4! hover:bg-primary-600! md:hover:bg-primary-700! ``` - Değer bir **token**’dır, Tailwind sınıfı değil: `p: "6"`, `bg: "primary-600"`, `radius: "xl"`. - Serbest değerler köşeli parantezle yazılır: `"[40px]"`, `"[#ff6b00]"`, `"[var(--theme-color-primary)]"`. - Model `node.props._tecofStyles` içinde kayıpsız gidip gelir; editör her kontrolün mevcut değerini gösterebilir. - Kontrol listesi, preset’ler ve sınıf derlemesinin tek kaynağı paketin `STYLE_CONTROLS` dizisidir (54 kontrol). ## Kırılımlar masaüstü önceliklidir > [!IMPORTANT] Tailwind’in mobil-öncelikli `md:` anlamı burada geçerli değil > Sınıf adları Tailwind’e benzese de Studio’nun ürettiği CSS **masaüstü önceliklidir** (`max-width` medya sorguları). `base` ana (masaüstü) tasarımdır; her kırılım ekran **küçüldükçe** devreye giren bir geçersiz kılmadır. Bu sayede 375 px’lik mobil kanvas `sm` katmanını gerçekten düzenler. | Katman | Uygulandığı ekran | Önek | | :--- | :--- | :--- | | `base` | Tüm ekranlar | — | | `xl` | ≤ 1280 px | `xl:` | | `lg` | ≤ 1024 px | `lg:` | | `md` | ≤ 768 px (tablet ve altı) | `md:` | | `sm` | ≤ 640 px (mobil) | `sm:` | Dar bir ekran birden çok sorguya uyduğu için CSS büyükten küçüğe sırayla basılır; en dar katman kazanır. Editörde üst katmanda değer yoksa daha geniş katmandan **miras alınan** değer soluk ipucu olarak gösterilir; değer içeren katmanlar küçük bir noktayla işaretlenir. **Durumlar:** `hover`, `focus`, `active`. Durum anahtarı çıplaksa (`hover`) tüm ekranlarda, `md:hover` biçimindeyse yalnız o kırılımda uygulanır (`base:hover` geçersizdir). ## Kontroller | Grup | Kontroller (`*` serbest değer alır) | | :--- | :--- | | Yerleşim | `display`, `flexDir`, `justify`, `items`, `flexWrap`, `gap*`, `alignSelf`, `position`, `zIndex`, `overflow` | | Boşluk | `p*`, `px*`, `py*`, `pt*`, `pb*`, `pl*`, `pr*`, `m*`, `mx*`, `my*`, `mt*`, `mb*`, `ml*`, `mr*`, `marginAlign` | | Boyut | `w*`, `h*`, `maxW*` | | Arka plan | `bg*`, `bgGradient`, `gradientFrom*`, `gradientTo*` | | Tipografi | `fontFamily*`, `text*`, `fontSize`, `fontWeight`, `align`, `leading`, `tracking`, `textTransform`, `decoration` | | Kenarlık | `radius`, `border`, `borderColor*` | | Efektler | `shadow`, `opacity`, `blur`, `scale`, `rotate`, `transition`, `anim`, `animDelay`, `reveal`, `parallax` | Serbest uzunluklar `[12px]`, `[1.5rem]`, `[50%]`, `[80vh]` biçimindedir (negatif yalnız margin’de); serbest renkler `[#hex]`, `[rgb(…)]`, `[rgba(…)]` ya da `[var(--theme-color-)]`; yazı tipi yalnız yerleşik `[var(--font-)]`. `reveal` (görününce animasyon) ve `parallax` kontrolleri yayında `TecofRender`’ın küçük bir çalışma zamanıyla uygulanır; `prefers-reduced-motion`’a uyar. ## Renk kaynakları Arka plan, metin ve kenarlık renkleri tek bir seçici panelinde beş kaynaktan seçilir: | Kaynak | Saklanan token | Derlenen sınıf | | :--- | :--- | :--- | | Hızlı seçim | `white`, `black`, `transparent` | `bg-white!` | | Tema renkleri (canlı) | `[var(--theme-color-primary)]` | `bg-[var(--theme-color-primary)]!` | | Marka skalası (temanın `--color-primary-*` değişkenleri) | `primary-600` | `bg-primary-600!` | | Tailwind paleti (22 renk × 11 ton) | `red-500` | `bg-red-500!` | | Özel renk (seçici / hex) | `[#ff6b00]` | `bg-[#ff6b00]!` | Tema renkleri `--theme-color-*` değişkenlerine bağlanır: tema panelinde rengi değiştirmek bu düğümü de değiştirir ([Canlı tema](https://tecof-app-dev.vercel.app/editor-theme)). Palet verisi `TAILWIND_PALETTE` ve `TAILWIND_SHADES` olarak dışa aktarılır. ## CSS’i kim üretir `TecofRender`, dokümanda kullanılan her stil sınıfının CSS’ini **kendisi üretip** sayfaya `