NABI NOTE
محرّر WYSIWYG مفتوح المصدر
NABI NOTE محرّر WYSIWYG مفتوح المصدر، فُصلت فيه كل وظيفة أساسية — التنسيق والمحاذاة والجداول والرفع وما بقي — عن النواة على هيئة وحدة مستقلة تُسمّى wing، حتى يستطيع المطوّر أن يوسّعه بلا حدّ. وهو مكتوب بلغة Vanilla JS الصافية ويقصد صفرًا من الاعتماد على أطر العمل، فيدخل في React وفي Vue وفي أي شيء آخر كما هو، ومعه مكتبة على CDN للمشاريع التي لا نظام بناء لها. وله صيغته الخاصة بـ JSON، وهي NABI TREE، فيمكن تجهيز التحويل بين HTML والنص حيث لا يوجد DOM أصلًا (Node.js، SSR)؛ ولأنه يعيد تركيب المستند من مفردات مسموح بها بدل أن يرقّعه، فهو يضمن منع شيفرات XSS من الأساس من دون مكتبة منفصلة لتنقية المحتوى. أما في التصميم فقد أخذ بنظام متغيّرات CSS، فيسهل تبديل لون العلامة التجارية، وبـتخطيط قائم على rem، فيبقى شكل الهاتف سلسًا مهما كبّرت أو صغّرت؛ والألوان الموافقة للداكن والفاتح وأقلام التظليل والخطوط متعددة اللغات كلها حاضرة. ويضاف إلى ذلك فرز أعمدة الجدول مع تمييز النوع، وسجل محلي، ودعم البرمجة بالإحساس.
الخط
بلا تذييل (المبدئي)، وبتذييل، وثابت العرض، ومكتوب باليد — كل عائلة تُرصّ فيها الخطوط بحسب نظام الكتابة، فأيّ لغة كتبتَ يبقى لها وجه تلك العائلة؛ وما لا يد له في تلك العائلة من أنظمة الكتابة يعود إلى خط المتصفح المبدئي. الخط المبدئي يقرّره المضيف.
وفي ما يلي كل عائلة معروضة بلغات عدة.
بلا زخارف · सैन्स सेरिफ़ · স্যান্স সেরিফ · سانس سیرف · Tanpa serif · بدون سریف · सॅन्स सेरिफ · Không chân · శాన్స్ సెరిఫ్ · Mara seref · Sans serif · Bila serif · சான்ஸ் செரிஃப் · ไร้เชิง · Senza grazie · 산세리프 · ゴシック体 · 无衬线 · Serifenlos · Sans empattement · Sin serifa · Sem serifa · Без засечек
بزخارف · सेरिफ़ · সেরিফ · سیرف · Berserif · سریف · सेरिफ · Có chân · సెరిఫ్ · Mai seref · Serif · Yenye serif · செரிஃப் · มีเชิง · Con grazie · 세리프 · 明朝体 · 衬线 · Avec empattement · Con serifa · Com serifa · С засечками
ثابت العرض · मोनोस्पेस · মনোস্পেস · یکساں چوڑائی · Lebar tetap · تکفاصله · Đơn cách · మోనోస్పేస్ · Tazara ɗaya · Eş aralıklı · Nafasi moja · ஒற்றையகலம் · ความกว้างคงที่ · Monospaziato · Monospace · 고정폭 · 等幅 · 等宽 · Dicktengleich · Chasse fixe · Monoespaciada · Monoespaçada · Моноширинный
خط اليد · हस्तलिपि · হস্তলিপি · رواں خط · Tulisan tangan · دستنویس · हस्ताक्षर · Chữ viết tay · చేతిరాత · Rubutun hannu · El yazısı · Mwandiko · கையெழுத்து · ลายมือ · Corsivo · Cursive · 필기체 · 筆記体 · 手写体 · Schreibschrift · Cursiva · Рукописный
حجم الخط
صغير جدًا
صغير
كبير
كبير جدًا
عنوان
اكتب # في سطر فارغ ثم اضغط المسافة — يصير عنوانًا في الحال.
H1
H2
H3
H4
H5
H6
عريض · مائل · تحته خط · مشطوب
عريض مائل تحته خط مشطوب — هذا مثال.
ويمكن جمعها بعضها فوق بعض.
الرفع والخفض
المساحة 3.5 م2، والحاشية تُوضع هكذا1.
الماء هو H2O.
لون النص · التظليل
اختيرت الألوان لتبقى مريحة للقراءة في الفاتح والداكن معًا.
لون النص أخضر · مرجاني · بنفسجي · كهرماني · أزرق
التظليل أصفر · أخضر · سماوي · وردي · أرجواني · برتقالي
رابط
ضع عنوانًا فيصير رابطًا.
لا يُقبل إلا http:// و https://؛ وما كان مثل javascript: فلا يمرّ.
اكتب مثلًا https://nabi.saro.me ثم اضغط المسافة أو الإدخال — فيتحوّل من تلقاء نفسه، كما ترى هنا.
فتح الرابط
مبدئيًا يُفتح رابط الأصل نفسه في هذه النافذة، وأي موقع آخر في نافذة جديدة؛ وتُضبط هذه القاعدة عند تعريف المحرّر.
رابط المرفق
إن رفعتَ غير صورة بقي رابط على هيئة ملف كالذي في الأسفل.
مرفق هكذا يبقى.
المحاذاة
إلى اليسار
إلى الوسط
إلى اليمين
والعناوين أيضًا تُحاذى.
القوائم
قائمة نقطية
اكتب - في سطر فارغ ثم اضغط المسافة — تصير قائمة نقطية في الحال.
هذا بند
Tab / Shift+Tab للإزاحة إلى الداخل والخارج.
قائمة مرقّمة
اكتب 1. في سطر فارغ ثم اضغط المسافة — تصير قائمة مرقّمة.
الأول
الثاني
الثالث
قائمة مهام
اكتب [ ] أو [x] في سطر فارغ ثم اضغط المسافة — تصير قائمة مهام.
هذه المهمة منجزة.
وهذه لم تُنجز بعد.
جدول
أنشئه من زر الجدول في شريط الأدوات؛ ويمكن إضافة الصفوف والأعمدة وحذفها ودمجها.
فرز الأعمدة
اضغط معاينة، ثم انقر ترويسة المخزون والسعر واحدة بعد الأخرى.
الطراز | المخزون | السعر | الوزن |
|---|---|---|---|
NB-7 | 1,200 | 349 | 1.2 kg |
NB-9 | 20,000 | 99 | 0.9 kg |
NB-12 | 3,500 | 1,299 | 1.4 kg |
NB-80 | 900 | 8,900 | 2.1 kg |
NB-100 | لم يُحدَّد | 12,999 | 2.4 kg |
السعر كله أرقام، فيُفرز فرز الأرقام.
المخزون يُفرز فرز النصوص لأن في الخانة الأخيرة حروفًا.
فاصل
اكتب --- ثم اضغط الإدخال — يصير خطًا فاصلًا.
صورة
العرض يُضبط من 30% إلى 100%، وتقف يمينًا أو وسطًا أو يسارًا.
YouTube
الرفع
جرّب أن تسحب صورة أو ملفًا إلى المحرّر.
الرفع في هذا العرض تمثيلي؛ وبإعداد واحد يتصل بخادمك.
وإن أخفق الرفع أُخرجت تلك الصورة أو ذلك الملف من المحرّر.
اقتباس
اكتب > في سطر فارغ ثم اضغط المسافة — يصير صندوق اقتباس.
ويمكن أن يمتدّ على أسطر عدة.
شيفرة
اكتب ``` في سطر فارغ ثم اضغط المسافة أو الإدخال — يصير صندوق شيفرة.
واكتب اللغة معها، مثل ```java، ثم المسافة أو الإدخال — فيأخذ الصندوق تلك اللغة.
import { createNabiWith, defaultWings } from 'nabi-note'
const { nabi } = createNabiWith(defaultWings)
const html = nabi.getHtml()طيّ
الطيّ يتكوّن من عنوان ومحتوى.
ولك أن تحدّد أيُحفظ مطويًّا أم مفتوحًا.
الاختصارات
اضغط Shift مرتين سريعًا فيُظهر شريط الأدوات اختصار كل وظيفة.
التنسيق التلقائي
مثال | المفتاح | النتيجة |
|---|---|---|
# | مسافة | عنوان |
- | مسافة | قائمة نقطية |
1. | مسافة | قائمة مرقّمة |
[ ] · [x] | مسافة | قائمة مهام |
> | مسافة | اقتباس |
``` · ```ts | مسافة · إدخال | صندوق شيفرة |
--- | إدخال | فاصل |
https://… | مسافة · إدخال | رابط |
دوال الإخراج
الدالة | النتيجة |
|---|---|
getHtml() | HTML |
getJson() | JSON |
يعمل بغير DOM
تحويل JSON إلى HTML لا يحتاج DOM.
فيستطيع الخادم (Node.js) أن يقرأ شجرة نابي المحفوظة كما هي ويبني HTML وهو يصدّ XSS.
ودود للهاتف
واجهة الهاتف — تخطيط متجاوب يحمل واجهة الهاتف.
تعويض لوحة المفاتيح — إذا ظهرت لوحة المفاتيح عُوّض ارتفاعها.
أحجام مرنة — كل الأحجام مكتوبة بوحدة rem.
متعدد اللغات — يدعم لغات متعددة.
التخصيص
wing من صنعك — إن نقصتك وظيفة فاصنعها بنفسك وسجّلها.
CSS من صنعك — الألوان والزوايا والفراغات كلها معرّفة بـ --nabi-*، فالداكن والفاتح إليك.
مفتوح المصدر — متاح مفتوح المصدر على GitHub.
اطّلع على التوثيق → nabi.saro.me
npm install nabi-noteimport {
createNabiWith, mountSurface, mountToolbar, mountContextToolbar,
mountHints, watchSettle, defaultWings, mountUpload, mountUploadView,
browserFileStore, mountFile, parseNodes, mountViewTools,
browserHistoryStorage, mountLocalHistory, openHistoryPanel,
} from 'nabi-note'
import { mountDiffWing } from 'nabi-note/diff'
// The box that asks — without it the core answers "no" to everything (plug in your own)
const ask = { message: (t: string) => alert(t), confirm: (t: string) => confirm(t) }
const selected = [
...defaultWings,
]
const { nabi, registry } = createNabiWith(selected, { ask })
// Bring your own highlighter (Prism, highlight.js, Shiki, …) — the wing stays as it is
// and only the attach is swapped (`makeCodeAttach` ships from the same door)
// { ...codeWing, attach: makeCodeAttach({ highlight: (source, lang) => [{ text: source }] }) }
const root = document.querySelector('.nabi')!
const content = document.querySelector('.nabi-content')!
const view = mountUploadView({ nabi, surface: content })
const upload = mountUpload({
nabi, root: content,
// your upload goes here — report progress with task.onProgress(0–100)
uploader: async (task) => ({ uri: 'https://cdn.example/uploaded' }),
extensions: ['png', 'jpg', 'pdf'], maxFileSize: 10 * 1024 * 1024,
onStart: (tasks) => view.start(tasks),
onProgress: (id, percent) => view.progress(id, percent),
onSettle: () => view.settle(),
onDone: () => view.done(),
})
const file = mountFile({ nabi, registry, store: browserFileStore(document), parse: parseNodes, locale: 'ar', name: () => 'note' })
const history = mountLocalHistory({ nabi, storage: browserHistoryStorage(window) })
const diff = mountDiffWing({ nabi, registry, surface: content, locale: 'ar' })
// The locale also sets the direction — Arabic and Urdu run right to left
mountSurface({ nabi, registry, root: content, locale: 'ar', fileSink: upload.take })
const settle = watchSettle(document, { surface: content })
const shared = { nabi, registry, surface: content, settle, locale: 'ar' }
// A tool that needs a panel comes back to the host — without this the button is dead
const toolbar = mountToolbar({
...shared, root: document.querySelector('#toolbar')!,
file,
onFiles: upload.take,
onHost: (w) => {
if (w === 'diff') { diff.open(); return }
if (w !== 'localHistory') return
openHistoryPanel({
history, surface: content, locale: 'ar', sessionId: history.sessionId,
render: (record) => createNabiWith(selected, { doc: JSON.parse(record.body) }).nabi.getHtml(),
})
},
})
const context = mountContextToolbar({ ...shared, root: document.querySelector('#context')! })
mountHints({ toolbar, context, root, surface: content })
// The preview and fullscreen buttons — they stand their own box at the end of the row
mountViewTools({ ...shared, root, container: document.querySelector('#toolbar')! })
// on every change — hook up your own code here
// nabi.onChange(() => user_callback(nabi.getHtml()))