- برای پروژه بعدی آمادهاید؟
+ برای طراحی لوگوی حرفهای آمادهاید؟
- با ما در تماس باشید تا درباره نیازهای برند و محتوای شما صحبت کنیم.
+ با استودیو نورو در تماس باشید تا لوگویی متناسب با هویت برند شما طراحی کنیم.
تماس با ما
diff --git a/src/utils/blogContent.ts b/src/utils/blogContent.ts
new file mode 100644
index 0000000..7f62d8d
--- /dev/null
+++ b/src/utils/blogContent.ts
@@ -0,0 +1,83 @@
+export type BlogContentBlock =
+ | { type: 'h2'; text: string }
+ | { type: 'h3'; text: string }
+ | { type: 'h4'; text: string }
+ | { type: 'p'; text: string }
+ | { type: 'ul'; items: string[] }
+ | { type: 'img'; src: string; alt: string }
+
+const imagePattern = /^!\[(.*?)\]\((.*?)\)$/
+const headingPattern = /^(#{2,4})\s+(.*)$/
+
+export function parseBlogContent(content: string, apiBase = ''): BlogContentBlock[] {
+ const blocks: BlogContentBlock[] = []
+ const lines = content.replace(/\r\n/g, '\n').split('\n')
+ let paragraph: string[] = []
+ let listItems: string[] = []
+
+ const resolve = (url: string) => {
+ if (!url) return ''
+ if (/^https?:\/\//.test(url) || url.startsWith('data:')) return url
+ const base = apiBase.replace(/\/$/, '')
+ return url.startsWith('/') ? `${base}${url}` : `${base}/${url}`
+ }
+
+ const flushParagraph = () => {
+ const text = paragraph.join('\n').trim()
+ if (text) blocks.push({ type: 'p', text })
+ paragraph = []
+ }
+
+ const flushList = () => {
+ if (listItems.length) blocks.push({ type: 'ul', items: [...listItems] })
+ listItems = []
+ }
+
+ for (const rawLine of lines) {
+ const line = rawLine.trimEnd()
+ const trimmed = line.trim()
+
+ if (!trimmed) {
+ flushParagraph()
+ flushList()
+ continue
+ }
+
+ const imageMatch = trimmed.match(imagePattern)
+ if (imageMatch) {
+ flushParagraph()
+ flushList()
+ blocks.push({
+ type: 'img',
+ alt: imageMatch[1] || '',
+ src: resolve(imageMatch[2] || ''),
+ })
+ continue
+ }
+
+ const headingMatch = trimmed.match(headingPattern)
+ if (headingMatch) {
+ flushParagraph()
+ flushList()
+ const level = headingMatch[1].length
+ const text = headingMatch[2].trim()
+ if (level === 2) blocks.push({ type: 'h2', text })
+ else if (level === 3) blocks.push({ type: 'h3', text })
+ else blocks.push({ type: 'h4', text })
+ continue
+ }
+
+ if (trimmed.startsWith('* ')) {
+ flushParagraph()
+ listItems.push(trimmed.slice(2).trim())
+ continue
+ }
+
+ flushList()
+ paragraph.push(trimmed)
+ }
+
+ flushParagraph()
+ flushList()
+ return blocks
+}