NABI NOTE
Trình soạn thảo WYSIWYG mã nguồn mở
NABI NOTE là trình soạn thảo WYSIWYG mã nguồn mở; định dạng, căn chỉnh, bảng, tải tệp và các tính năng khác được tách khỏi lõi thành những mô-đun độc lập gọi là wing. Bạn chỉ chọn các wing cần thiết để lắp ráp trình soạn thảo và có thể kết nối wing tự tạo. NABI NOTE viết bằng JavaScript thuần, nên dùng được với React hoặc Vue. Không cần thiết lập build, bạn có thể bắt đầu bằng thư viện CDN. Định dạng tài liệu riêng là NABI TREE có thể chuyển thành HTML ngay cả trong Node.js không có DOM trình duyệt. Tài liệu đầu vào được sửa theo quy tắc đã đăng ký; khi tạo HTML, văn bản và thuộc tính được escape, địa chỉ liên kết được kiểm tra. Tuy vậy, wing tùy chỉnh và dữ liệu từ máy chủ vẫn phải được xem xét riêng theo chính sách bảo mật của ứng dụng. Biến CSS và bố cục dựa trên rem cho phép chỉnh chủ đề và kích thước; đồng thời có chế độ tối và kiểu chữ cho nhiều ngôn ngữ. Sắp xếp bảng, lịch sử cục bộ và công việc tài liệu có AI hỗ trợ đều dùng cùng một mô hình tài liệu.
Kiểu chữ
Sans serif (mặc định), serif, monospace và chữ viết tay — mỗi họ xếp chồng phông theo hệ chữ, vì vậy ngôn ngữ nào cũng giữ dáng của họ đó; hệ chữ không có phông viết tay sẽ dùng phông riêng của trình duyệt. Trang chủ quyết định kiểu chữ mặc định.
Dưới đây là từng họ chữ hiển thị bằng nhiều ngôn ngữ.
Sans serif · 산세리프 · ゴシック体 · 无衬线 · Serifenlos · Sans empattement · Sin serifa · Sem serifa · Без засечек · بلا زخارف · सैन्स सेरिफ़ · স্যান্স সেরিফ · سانس سیرف · Tanpa serif · بدون سریف · सॅन्स सेरिफ · Không chân · శాన్స్ సెరిఫ్ · Mara seref · Bila serif · சான்ஸ் செரிஃப் · ไร้เชิง · Senza grazie
Serif · 세리프 · 明朝体 · 衬线 · Avec empattement · Con serifa · Com serifa · С засечками · بزخارف · सेरिफ़ · সেরিফ · سیرف · Berserif · سریف · सेरिफ · Có chân · సెరిఫ్ · Mai seref · Yenye serif · செரிஃப் · มีเชิง · Con grazie
Monospace · 고정폭 · 等幅 · 等宽 · Dicktengleich · Chasse fixe · Monoespaciada · Monoespaçada · Моноширинный · ثابت العرض · मोनोस्पेस · মনোস্পেস · یکساں چوڑائی · Lebar tetap · تکفاصله · Đơn cách · మోనోస్పేస్ · Tazara ɗaya · Eş aralıklı · Nafasi moja · ஒற்றையகலம் · ความกว้างคงที่ · Monospaziato
Chữ viết tay
Cỡ chữ
Rất nhỏ
Nhỏ
Lớn
Rất lớn
Tiêu đề
Trên dòng trống, gõ # rồi dấu cách để biến dòng đó thành tiêu đề.
H1
H2
H3
H4
H5
H6
Đậm · Nghiêng · Gạch chân · Gạch ngang
Đậm nghiêng gạch chân gạch ngang — một ví dụ.
Có thể chồng các định dạng này.
Chỉ số trên và dưới
Diện tích là 3.5m2 và chú thích được thêm như thế này.1
Nước là H2O.
Màu chữ · Đánh dấu
Bảng màu được chọn để vẫn dễ đọc ở cả chế độ sáng và tối.
Màu chữ Xanh lá · San hô · Tím · Hổ phách · Xanh dương
Đánh dấu Vàng · Xanh lá · Lục lam · Hồng · Tím · Cam
Liên kết
Nhập địa chỉ để tạo thành liên kết.
Bạn có thể dùng URL http://, https:// và đường dẫn cùng trang bắt đầu bằng . hoặc /. Các địa chỉ như javascript: không được cho phép.
Ví dụ, gõ https://nabi.saro.me rồi nhấn dấu cách hoặc Enter — nó tự chuyển đổi như bạn thấy ở đây.
Cách mở liên kết
Tài liệu chỉ lưu địa chỉ liên kết; trang hiển thị tài liệu quyết định cách mở liên kết.
Liên kết tệp đính kèm
Tải lên thứ không phải ảnh sẽ để lại liên kết dạng tệp như bên dưới.
Tệp đính kèm sẽ giữ nguyên như vậy.
Căn chỉnh
Căn trái
Căn giữa
Căn phải
Tiêu đề cũng có thể căn chỉnh.
Danh sách
Danh sách đầu dòng
Trên dòng trống, gõ - rồi nhấn dấu cách để tạo danh sách đầu dòng.
Một mục đầu dòng
Tab / Shift+Tab thụt vào và ra.
Danh sách đánh số
Trên dòng trống, gõ 1. rồi nhấn dấu cách để tạo danh sách đánh số.
Thứ nhất
Thứ hai
Thứ ba
Danh sách kiểm tra
Trên dòng trống, gõ [ ] hoặc [x] rồi nhấn dấu cách để tạo danh sách kiểm tra.
Mục này đã chọn.
Mục này chưa chọn.
Bảng
Nhấn nút bảng trên thanh công cụ để tạo bảng, sau đó thêm, xóa và gộp hàng cùng cột.
Sắp xếp cột
Xem trước sẽ mở trước; sau đó lần lượt nhấn ô tiêu đề Tồn kho và Giá.
Mẫu | Tồn kho | Giá | Khối lượng |
|---|---|---|---|
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 | Chưa xác định | 12,999 | 2.4 kg |
Giá toàn là số nên được sắp theo số.
Tồn kho được sắp như văn bản vì ô cuối có chữ.
Đường phân cách
Gõ --- rồi nhấn Enter để biến thành đường phân cách.
Hình ảnh
Chiều rộng từ 30% đến 100% và ảnh có thể nằm trái, giữa hoặc phải.
YouTube
Tải tệp lên
Hãy thử kéo ảnh hoặc tệp vào trình soạn thảo.
Lần tải ở đây là mô phỏng; một thiết lập sẽ kết nối nó với máy chủ của bạn.
Nếu tải lên thất bại, ảnh hoặc tệp sẽ bị xóa khỏi trình soạn thảo.
Trích dẫn
Trên dòng trống, gõ > rồi nhấn dấu cách để tạo khung trích dẫn.
Nó có thể kéo dài nhiều dòng.
Mã
Trên dòng trống, gõ ``` rồi nhấn dấu cách hoặc Enter để tạo khung mã.
Gõ cả ngôn ngữ, như ```java, rồi nhấn dấu cách hoặc Enter để tạo khung mã đã áp dụng ngôn ngữ đó.
import { createNabiWith, defaultWings } from 'nabi-note'
const { nabi } = createNabiWith(defaultWings)
const html = nabi.getHtml()Chi tiết
Chi tiết gồm phần tóm tắt và phần nội dung.
Bạn có thể chọn lưu ở trạng thái thu gọn hoặc mở.
Phím tắt
Nhấn Shift nhanh hai lần để hiện phím tắt của từng tính năng trên thanh công cụ.
Tự định dạng
Ví dụ | Phím thao tác | Kết quả |
|---|---|---|
# | Dấu cách | Tiêu đề |
- | Dấu cách | Danh sách đầu dòng |
1. | Dấu cách | Danh sách đánh số |
[ ] · [x] | Dấu cách | Danh sách kiểm tra |
> | Dấu cách | Trích dẫn |
``` · ```ts | Dấu cách · Enter | Khung mã |
--- | Enter | Đường phân cách |
https://… | Dấu cách · Enter | Liên kết |
Hàm xuất
Hàm | Kết quả |
|---|---|
getHtml() | HTML |
getJson() | JSON |
Hoạt động không cần DOM
Chuyển JSON thành HTML không cần DOM.
Máy chủ (Node.js) có thể đọc NABI TREE đã lưu và tạo HTML theo cùng quy tắc.
Thân thiện với thiết bị di động
Giao diện di động — bố cục đáp ứng hỗ trợ giao diện di động.
Khoảng chèn bàn phím di động — khi bàn phím mở, chiều cao của nó được tính đến.
Kích thước linh hoạt — mọi kích thước đều viết bằng rem.
Nhiều ngôn ngữ — hỗ trợ nhiều ngôn ngữ.
Tùy chỉnh
Wing của bạn — nếu cần tính năng, hãy tự tạo và đăng ký nó.
CSS của bạn — màu sắc, góc bo và khoảng cách đều định nghĩa bằng --nabi-*, nên bạn tự đặt chủ đề tối, sáng và bất cứ chủ đề nào khác.
Mã nguồn mở — mã nguồn được mở trên GitHub.
Đọc tài liệu → 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: 'vi', name: () => 'note' })
const history = mountLocalHistory({ nabi, storage: browserHistoryStorage(window) })
const diff = mountDiffWing({ nabi, registry, surface: content, locale: 'vi' })
// The locale also sets the direction — Arabic and Urdu run right to left
mountSurface({ nabi, registry, root: content, locale: 'vi', fileSink: upload.take })
const settle = watchSettle(document, { surface: content })
const shared = { nabi, registry, surface: content, settle, locale: 'vi' }
// 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: 'vi', 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()))