İçeriğe geç
Docs

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ı.

Son güncelleme:

@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 read_type_definition aracını kullanabilirsiniz.

Neler sunar#

ParçaNe yapar
TecofEditor / TecofStudioiframe kanvaslı görsel editör: sürükle-bırak, katmanlar, Inspector, stil ve tema panelleri, komut paleti, geri al/yinele, otomatik kayıt
TecofRenderKaydedilmiş 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, <Slot>, <TecofRoot>Tema bileşeni yazarken slot, varsayılan çocuk ve kök className sözleşmesini otomatikleştirir
TecofApiClientSayfa, medya, çeviri, CMS, ortak bileşen ve e-ticaret uçları için istemci
@tecof/theme-editor/emailPazarlama 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.

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
Diyagram

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#

GereksinimSürümNot
react, react-dom^18 || ^19Peer bağımlılık
@tiptap/core, @tiptap/pm, @tiptap/react^3Zengin metin alanı (EditorField) için peer bağımlılık
lucide-react>=0.408.0 <2İkonlar; peer bağımlılık
Next.jsApp Router önerilirEditö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#

Terminal
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:

app/editor/layout.tsx
import "@tecof/theme-editor/styles.css";

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ıç).

.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.

components/TecofProviderWrapper.tsx
"use client";

import { TecofProvider } from "@tecof/theme-editor";

export function TecofProviderWrapper({ children }: { children: React.ReactNode }) {
  return (
    <TecofProvider
      apiUrl={process.env.NEXT_PUBLIC_BASE_URL!}
      secretKey={process.env.NEXT_PUBLIC_MERCHANT_SECRET_KEY!}
      cdnUrl={process.env.NEXT_PUBLIC_CDN_URL}
      themeId={process.env.NEXT_PUBLIC_THEME_ID}
    >
      {children}
    </TecofProvider>
  );
}
PropTipAçıklama
apiUrlstringTecof API adresi (sondaki / temizlenir)
secretKeystringMağazanın vitrin anahtarı; her istekte x-secret-key başlığı
cdnUrlstring?Medya adresleri için CDN kökü; verilmezse apiUrl
themeIdstring?Çok temalı mağazada sayfa okuma/kaydetme isteklerinin doğru temaya gitmesi için
childrenReactNode—

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ı#

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) => <a className="rounded-full bg-black px-5 py-2.5 text-white">{p.text}</a>,
});

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) => (
    <section className="py-24 text-center">
      <h1 className="text-5xl font-semibold">{p.title}</h1>
      <div className="mt-8 flex justify-center">{slots.actions}</div>
    </section>
  ),
});

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ı sayfasında. Gerçek temalarda metinler genelde çok dilli alanlarla (createLanguageField, değer [{ code, value }]) tutulur: Alanlar.

2. Editör sayfası#

Tecof paneli tema editörünü iframe içinde /<editör rotası>/<pageId>?token=<JWT> biçiminde açar. Tema bu token’ı accessToken olarak editöre verir.

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 <EditorClient pageId={id} accessToken={token} />;
}
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
      pageId={pageId}
      config={editorConfig}
      accessToken={accessToken}
      hostOrigin="https://app.tecof.com"
    />
  );
}

TecofEditor, TecofStudio’nun diğer adıdır; ikisi aynı editörü açar. Tüm prop’lar: Studio arayüzü.

3. Yayın sayfası#

TecofRender veri getirmez. Sayfayı sunucuda çekip renderer’a verin; editörle aynı editorConfig’i kullanın.

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<string, unknown> | null }) {
  return <TecofRender data={data} config={editorConfig} cmsData={cmsData} />;
}

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’ı ve Tema mimarisi.

Veri modeli#

Editörün kaydettiği doküman Puck uyumlu bir şekildir (PuckPageData):

TypeScript
interface PuckPageData {
  content: Array<{ type: string; props: { id: string } & Record<string, unknown> }>; // kök düğümler
  root: { props: Record<string, unknown> };                                          // sayfa geneli (tema, şema sürümü…)
  zones: Record<string, Array<{ type: string; props: { id: string } & Record<string, unknown> }>>;
}
  • 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-editorEditör, renderer, alanlar, API istemcisi, stil/tema yardımcıları, tipler
@tecof/theme-editor/styles.cssEditör ve alan stilleri (./puck.css ve ./dist/styles.css eski takma adlardır)
@tecof/theme-editor/under-constructionUnderConstruction bileşeni (title, description, subtitle?, logoUrl?, accentColor?)
@tecof/theme-editor/emailE-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.

TypeScript
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#