Wings
Kiểu chữ cơ sởĐộ lệch


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ố.

  1. Thứ nhất

  2. Thứ hai

  3. 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ạt

Nhấn Enter để tạo đoạn mới, hoặc Shift+Enter để xuống dòng trong cùng một đoạn.

  • Đổi màu nền ô

  • Thụt lề danh sách bằng Tab

  • Chọn nhiều ô và áp dụng cùng một màu

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

HTMLĐang tải trình soạn thảo…
JSONnabi-treeĐang tải trình soạn thảo…
Cài đặt
npm install nabi-note
Mã
import {
  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')