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ữ
Các họ không chân (mặc định), có chân, đơn cách và chữ viết tay đều có phông dự phòng theo ngôn ngữ. Nếu không có phông phù hợp với họ đã chọn, phông mặc định của trình duyệt sẽ được dùng. Trang nhúng trình soạn thảo quyết định kiểu chữ mặc định của nó.
Dưới đây là từng họ chữ hiển thị bằng nhiều ngôn ngữ.
산세리프 · ゴシック体 · 无衬线 · Serifenlos · Sans empattement · Sin serifa · Sem serifa · Без засечек · بلا زخارف · सैन्स सेरिफ़ · স্যান্স সেরিফ · سانس سیرف · Tanpa serif · بدون سریف · सॅन्स सेरिफ · Không chân · శాన్స్ సెరిఫ్ · Mara seref · Sans serif · Bila serif · சான்ஸ் செரிஃப் · ไร้เชิง · Senza grazie
세리프 · 明朝体 · 衬线 · Serif · Avec empattement · Con serifa · Com serifa · С засечками · بزخارف · सेरिफ़ · সেরিফ · سیرف · Berserif · سریف · सेरिफ · Có chân · సెరిఫ్ · Mai seref · Yenye serif · செரிஃப் · มีเชิง · Con grazie
고정폭 · 等幅 · 等宽 · Dicktengleich · Chasse fixe · Monoespaciada · Monoespaçada · Моноширинный · ثابت العرض · मोनोस्पेस · মনোস্পেস · یکساں چوڑائی · Lebar tetap · تکفاصله · Đơn cách · మోనోస్పేస్ · Tazara ɗaya · Eş aralıklı · Nafasi moja · ஒற்றையகலம் · ความกว้างคงที่ · Monospaziato · Monospace
필기체 · 筆記体 · 手写体 · Schreibschrift · Cursive · Cursiva · Рукописный · خط اليد · हस्तलिपि · হস্তলিপি · رواں خط · Tulisan tangan · دستنویس · हस्ताक्षर · Chữ viết tay · చేతిరాత · Rubutun hannu · El yazısı · Mwandiko · கையெழுத்து · ลายมือ · Corsivo
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 có thể cùng được áp dụng cho một ký tự.
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
Các tệp không phải ảnh được giữ lại dưới dạng liên kết đính kèm như bên dưới.
Tệp đính kèm được lưu trong tài liệu dưới dạng liên kết.
Căn chỉnh
Căn trái
Căn giữa
Căn phải
Không thể áp dụng căn chỉnh cho tiêu đề.
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
Ô có thể chứa nhiều đoạn và khối, với màu nền để phân biệt nội dung. Hãy thử sửa trực tiếp văn bản và hình ảnh trong bảng dưới đây.
Tài liệu | Ghi chú công việc |
|---|---|
Hình ảnh và mô tả nằm trong cùng một ô. | Đoạn văn linh hoạtNhấn Enter để tạo đoạn mới, hoặc Shift+Enter để xuống dòng trong cùng một đoạn. |
| Nhấn để xem mẹo chỉnh sửaÔ cũng có thể chứa khối thu gọn, trích dẫn, mã và video. Khi gộp ô, nội dung của từng ô vẫn được giữ theo thứ tự. |
Nhấn vào ô để hiện màu nền và công cụ hàng, cột. Chọn nhiều ô để đổi màu và căn chỉnh cùng lúc. Để đặt vị trí cho cả bảng, chọn “Chọn bảng”, rồi dùng công cụ căn chỉnh.
Bảng có màu hoặc nhiều khối vẫn được giữ dưới dạng HTML ngay cả khi lưu thành Markdown.
Màu nền ô
Tất cả màu nền ô được hỗ trợ đều có ở đây. Nhấn vào ô để đổi màu, hoặc chọn “Mặc định” trong công cụ thuộc tính để xóa màu nền.
Vàng yellow | Xanh lá green | Lục lam cyan |
Hồng pink | Tím purple | Cam orange |
Sắp xếp cột
Nhấn Xem trước, rồi lần lượt nhấn các ô 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á chỉ chứa các giá trị số nên cột được sắp xếp theo số.
Tồn kho chứa cả số và văn bản nên cột được sắp xếp theo chuỗi ký tự.
Đườ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.
Vùng nổi
Xem nhanh
Vùng phụ nhỏ chứa văn bản, hình ảnh và bảng.
Vị trí | Bên phải |
Chiều rộng tối đa | 40% |
Dùng vùng nổi để đặt giải thích bổ sung hoặc tài liệu nhỏ bên cạnh nội dung chính. Khi tài liệu đủ rộng, đoạn này chạy bên cạnh vùng đó. Nhấn vào bên trong để sửa văn bản và chèn ảnh hoặc bảng ngay trong cùng trình soạn thảo.
Đặt con trỏ trong vùng để mở công cụ thuộc tính. Đổi vị trí trái hoặc phải và chọn chiều rộng tối đa 20, 30, 40 hoặc 50% cho phù hợp với văn bản. Bảng và ảnh được dán cũng nằm trong vùng.
Bạn cũng có thể gom các đoạn đã chọn vào một vùng. Nút bỏ vùng cạnh chiều rộng giữ lại nội dung trong tài liệu chính. Bản xem trước ẩn đường viền chấm dành cho chỉnh sửa; tài liệu hẹp hoặc bản in hiển thị liên tiếp trong một cột.
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.
Bản demo này không gửi tệp tới máy chủ thật. Kết nối bộ tải lên trong ứng dụng để lưu tệp trên máy chủ.
Sau khi mở bộ chọn ảnh hoặc bắt đầu tải lên, nếu chỉ di chuyển con trỏ thì nội dung vẫn được chèn tại vị trí ban đầu. Sửa tài liệu trong lúc chờ sẽ hủy việc chèn và hiện thông báo. Tiến trình và kết quả tải lên bắt đầu trong ô cũng được giữ trong ô đó.
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ở.
Thanh công cụ
Các công cụ tiêu đề, định dạng chữ, căn chỉnh, danh sách, chèn và tài liệu được chia nhóm. Máy tính hiển thị đầy đủ công cụ; thiết bị di động có công cụ nhanh và bảng công cụ đầy đủ. Thuộc tính của bảng hoặc vùng nổi đang chọn xuất hiện phía dưới.
Phím tắt
Nhấn Shift nhanh hai lần để chọn công cụ bằng bàn phím. Trên máy tính, duyệt thanh công cụ đang mở; trên di động, duyệt bảng công cụ đầy đủ. Dùng Tab để đổi nhóm, phím mũi tên để chọn công cụ, rồi Enter để thực hiện.
Ở cuối khối trích dẫn, mã hoặc khối thu gọn, nhấn Enter nhanh hai lần để thoát ra ngoài. Khoảng thời gian mặc định giữa hai lần nhấn là 180ms.
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 NABI TREE thành HTML không cần DOM.
Máy chủ (Node.js) cũng 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
UI di động — Bố trí các công cụ chỉnh sửa theo chiều rộng màn hình.
Điều chỉnh theo bàn phím di động — Điều chỉnh chiều cao vùng chỉnh sửa khi bàn phím mở.
Kích thước linh hoạt — Dùng đơn vị rem cho bố cục chính để thích ứng khi phóng to và thu nhỏ.
Đa ngôn ngữ — Bạn có thể đặt locale và kiểu chữ phù hợp với ngôn ngữ và hướng văn bản.
Tùy chỉnh
Wing của bạn — nếu cần một tính năng, bạn có thể tự tạo và kết nối nó.
CSS của bạn — điều chỉnh màu chính, góc bo và khoảng cách bằng các token --nabi-* cho chế độ sáng và tối.
Mã nguồn mở — mã nguồn được công khai trên GitHub và cùng nhau phát triển.
Đọc tài liệu → nabi.saro.me
npm install nabi-noteimport {
createLocale, createNabiWith, mountSurface, mountToolbar,
mountContextToolbar, mountHints, watchSettle, makeTranslator,
defaultWings, mountUpload, mountUploadView, browserFileStore,
mountFile, mountViewTools, browserHistoryStorage, mountLocalHistory,
openHistoryPanel,
} from 'nabi-note'
import { mountDiffWing } from 'nabi-note/diff'
import { mountOutline } from 'nabi-note/viewer'
// 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 locale = createLocale('vi')
const { nabi, registry } = createNabiWith(selected, { ask, locale })
// 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, locale })
const upload = mountUpload({
nabi, root: content, locale,
// 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), locale, name: () => 'note' })
const history = mountLocalHistory({ nabi, storage: browserHistoryStorage(window) })
const diff = mountDiffWing({ nabi, registry, surface: content, locale })
// The locale also sets the direction — Arabic and Urdu run right to left
mountSurface({ nabi, registry, root: content, locale, fileSink: upload.take })
const trigger = document.createElement('button')
trigger.type = 'button'
trigger.textContent = makeTranslator(locale).t('outline')
trigger.style.cssText = 'position:fixed;right:1rem;bottom:1rem;z-index:1000'
root.append(trigger)
const outline = mountOutline(content, {
trigger, locale,
scrollContainer: () => root.classList.contains('is-fullscreen') ? root : window,
onChange: ({ items, activeIndex, available, open }) => {
trigger.hidden = !available
trigger.setAttribute("aria-expanded", String(open))
},
})
trigger.onclick = () => outline.toggle()
// Call outline.unmount() when removing the editor
const settle = watchSettle(document, { surface: content })
const shared = { nabi, registry, surface: content, settle, locale }
// 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, 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()))
// Call from your language selector. Content and editing history stay intact.
// locale.setLocale('ko')