deff889f0c
WordPress Gutenberg + Notion karışımı blok editor. 4 admin formunda markdown textarea yerine gerçek WYSIWYG editor. RichEditor component (components/admin/rich-editor.tsx): - TipTap v3 (@tiptap/react + starter-kit + link + image + placeholder + underline) - Üst toolbar (her zaman görünür): - B / I / U (bold, italic, underline) - H1 / H2 / H3 - Bullet list / Ordered list / Quote / Code block - Link (URL prompt) - Görsel ekle (MediaPicker modal) - Undo / Redo - Slash menu: '/' yazınca blok seçim menüsü açılır - Notion tarzı keyboard navigation (↓↑ Enter Esc) - 8 blok tipi: H1/H2/H3/ul/ol/quote/code/hr - Image picker modal (toolbar görsel butonundan) - Mevcut MediaPicker'ı kullanır - 'Yeni görsel yükle' (progress bar ile) + 'Kütüphaneden seç' grid - HTML çıktı (hidden input ile form'a) - Mevcut content alanlarıyla backward compat Formlarda değişiklik (4 dosya): - app/admin/(protected)/blog/form.tsx → content - app/admin/(protected)/hizmetler/form.tsx → content - app/admin/(protected)/projeler/form.tsx → content - app/admin/(protected)/sektorler/form.tsx → content Public render (lib/content-render.ts): - renderContent() yardımcısı: - İçerik '<' ile başlıyorsa → HTML (direkt döner) - Aksi halde → markdown (marked.parse) - 4 detay sayfası bu helper'ı kullanıyor (blog/[slug], projeler/[slug], hizmetler/[slug], sektor/[slug]) - Eski markdown içerikler hala çalışıyor, yeni içerikler HTML olarak gelir 37 route, build temiz.
18 lines
609 B
TypeScript
18 lines
609 B
TypeScript
import { marked } from "marked";
|
||
|
||
/**
|
||
* Akıllı içerik render — RichEditor HTML üretir, eski içerikler markdown olabilir.
|
||
* - HTML işareti (`<p>`, `<h1>` vs ile başlıyor) varsa direkt döner
|
||
* - Aksi halde markdown olarak parse eder
|
||
*/
|
||
export function renderContent(content?: string | null): string {
|
||
if (!content) return "";
|
||
const trimmed = content.trim();
|
||
if (!trimmed) return "";
|
||
// HTML: ilk karakter '<' ise ve içinde HTML tag varsa
|
||
if (trimmed.startsWith("<") && /<\w+[^>]*>/.test(trimmed)) {
|
||
return trimmed;
|
||
}
|
||
return marked.parse(trimmed, { async: false }) as string;
|
||
}
|