From 14a7b196f291b62b3888de3cb53362300e70ad3a Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:23:54 +0330 Subject: [PATCH] Import source: app/src/admin/ArticleEditor Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- app/src/admin/ArticleEditor.jsx | 742 ++++++++++++++++++++++++++++++++ 1 file changed, 742 insertions(+) create mode 100644 app/src/admin/ArticleEditor.jsx diff --git a/app/src/admin/ArticleEditor.jsx b/app/src/admin/ArticleEditor.jsx new file mode 100644 index 0000000..94ccebf --- /dev/null +++ b/app/src/admin/ArticleEditor.jsx @@ -0,0 +1,742 @@ +import { useState, useEffect, useRef, useCallback } from 'react'; +import { useToast, TypeBadge, CategoryBadge, pillBtn, inputStyle } from '../../teknav-ui.jsx'; +import { LazyDiagram } from '../components/lazy-diagram.jsx'; +import { adminApi } from '../lib/admin-api.js'; +import { revisionsApi, presenceApi } from '../lib/engagement-api.js'; +import { iconNode } from '../lib/icon.jsx'; +import linkIcon from '../assets/icons/link.svg'; +import imageIcon from '../assets/icons/image.svg'; +import squarePenIcon from '../assets/icons/square-pen.svg'; +import pencilIcon from '../assets/icons/pencil.svg'; +import lockIcon from '../assets/icons/lock.svg'; +import starIcon from '../assets/icons/star.svg'; +import triangleAlertIcon from '../assets/icons/triangle-alert.svg'; +import { BRAND, DIAGRAM_CHOICES, PanelState, actionBtn } from './Shared.jsx'; + +function ArtifactStudio({ selectedDiagram, onSelectDiagram, onInsert }) { + const [mode, setMode] = useState('library'); + const [codeType, setCodeType] = useState('mermaid'); + const [code, setCode] = useState(''); + const [uploadState, setUploadState] = useState({ busy: false, message: '' }); + + const insertCodeArtifact = () => { + if (!code.trim()) return; + const safeCode = code.replace(//g, '>'); + onInsert?.(`\n
دیاگرام کدنویسی‌شده (${codeType})
${safeCode}
\n`); + }; + const fileToBase64 = (file) => new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(String(reader.result).split(',')[1] || ''); + reader.onerror = () => reject(reader.error); + reader.readAsDataURL(file); + }); + const insertUploadedFile = (file, url) => { + const safeName = file.name.replace(//g, '>'); + const isImage = file.type.startsWith('image/'); + onInsert?.(isImage + ? `\n
${safeName}
${safeName}
\n` + : `\n

${safeName}

\n`); + }; + const handleFileUpload = async (file) => { + if (!file) return; + if (file.size > 12 * 1024 * 1024) { + setUploadState({ busy: false, message: 'حداکثر حجم فایل برای بارگذاری مستقیم ۱۲ مگابایت است.' }); + return; + } + setUploadState({ busy: true, message: 'در حال بارگذاری فایل...' }); + try { + const dataBase64 = await fileToBase64(file); + const result = await adminApi.upload({ filename: file.name, mimeType: file.type || 'application/octet-stream', dataBase64 }); + insertUploadedFile(file, result.url); + setUploadState({ busy: false, message: 'فایل روی سرور محلی ذخیره و به مقاله اضافه شد.' }); + } catch { + setUploadState({ busy: false, message: 'اتصال upload در دسترس نبود؛ فایل درج نشد و داده نمونه یا محلی نمایش داده نمی‌شود.' }); + } + }; + + return ( +
+
+
+
+
Artifact Writing
+

کتابخانه دیاگرام و ابزار کدنویسی

+
+
+ {[['library', 'قالب‌ها'], ['code', 'کد اختصاصی']].map(([id, label]) => ( + + ))} +
+
+
+ +
+
بارگذاری فایل از همین سرور انجام می‌شود و به CDN خارجی وابسته نیست.
+ + {uploadState.message &&
{uploadState.message}
} +
+ + {mode === 'library' ? ( +
+
+
+ {DIAGRAM_CHOICES.map(t => ( + + ))} +
+
+
پیش‌نمایش زنده
+
+ +
+

فقط دیاگرام‌های واقعی پشتیبانی‌شده در کد نمایش داده می‌شوند؛ قالب نمونه یا محتوای آزمایشی درج نمی‌شود.

+
+
+
+ ) : ( +
+
+
+ {['mermaid', 'plantuml', 'd2'].map(t => ( + + ))} +
+