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;
Enterkaydeder,Escvazgeçer. Bileşenler düzenlenebilir metnidata-tecof-prop="propAdı"(çok dilli için ek olarakdata-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:
{services.map((s, i) => (
<li key={i} data-tecof-item={`services:${i}`}>
<h3 data-tecof-item-prop="title">{s.title}</h3>
<p data-tecof-item-prop="description">{s.description}</p>
</li>
))}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ı):
render: ({ puck, tabs }) => (
<div>
{tabs.map((tab, i) => (
<button key={i} ref={puck.registerOverlayPortal} onClick={() => setActive(i)}>
{tab.title}
</button>
))}
</div>
)İş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) |
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
fieldsGroupstanımlarsa alanlar akordeon gruplarına ayrılır.variantstanı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ü.
- Etkileşimler. Tetikleyici → eylem kuralları (
click/hover→scrollTo,toggleClass,show,hide,toggle). Kurallar düğümün_interactionsprop’unda saklanır ve yayındaTecofRendertarafından çalıştırılır. - Sayfa / Tema. Seçim yokken sayfa geneli (
config.root.fields) ve canlı tema düzenleyici. Bkz. Canlı tema.
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:
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=<id> 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).
"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 (
<NextIntlClientProvider locale={canvasLocale} messages={messages}>
<TecofEditor pageId={pageId} config={editorConfig} accessToken={accessToken} onLanguageChange={setCanvasLocale} />
</NextIntlClientProvider>
);
}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:
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.