# 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](/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`, `<Slot>`, `<TecofRoot>` | 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](/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ıç](/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 (
    <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>
  );
}
```

| 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) => <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ı](/editor-components) sayfasında. Gerçek temalarda metinler genelde çok dilli alanlarla (`createLanguageField`, değer `[{ code, value }]`) tutulur: [Alanlar](/editor-fields#languagefield-cok-dilli-metin).

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

```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 <EditorClient pageId={id} accessToken={token} />;
}
```

```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
      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ü](/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<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’ı](/editor-render) ve [Tema mimarisi](/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<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-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ü](/editor-studio): paneller, modlar, `TecofEditor` prop’ları.
- [Bileşen yapılandırması](/editor-components): `ComponentConfig`, `defineSection`, slot sözleşmesi.
- [Alanlar](/editor-fields): tüm field factory’ler ve değer biçimleri.
- [Stil editörü](/editor-styles): token modeli, masaüstü-öncelikli kırılımlar, canlı önizleme.
