ウィング
既定の書体高さ


NABI NOTE

オープンソースの WYSIWYG エディタ


NABI NOTE は、書式、配置、表、アップロードなどの機能をコアから分離し、独立モジュールである wing として構成したオープンソースの WYSIWYG エディターです。必要な wing だけを選んでエディターを組み立てられ、自分で作った wing も接続できます。純粋な JavaScript で書かれているため、React や Vue のようなフレームワークでも使えます。ビルド環境のないページでは CDN ライブラリー ですぐに始めることもできます。独自の文書形式である NABI TREE は、ブラウザー DOM のない Node.js 環境でも HTML に変換できます。入力された文書は登録された規則に従って整理され、HTML を作るときはテキストと属性をエスケープし、リンクアドレスを検査します。ただし、自作 wing とサーバーから受け取ったデータは、アプリケーションのセキュリティポリシーに合わせて別途確認する必要があります。CSS 変数と rem ベースのレイアウトでテーマとサイズを調整でき、ダークモードと複数言語に合う書体設定も提供します。表ソート、ローカル履歴、AI を活用した文書作業も、1 つの文書モデルの上で続きます。


書体

ゴシック体(既定)・明朝体・等幅・筆記体には、それぞれ言語別の代替フォントを用意しています。選んだ書体に対応するフォントがない場合は、ブラウザの既定フォントで表示されます。エディターの既定の書体はホストページで設定します。


以下は多言語で示した書体です。


산세리프 · ゴシック体 · 无衬线 · 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



文字サイズ

とても小さく

小さく

大きく

とても大きく



見出し

空行で # を打ってスペースを入れると、その場で見出しになります。

H1

H2

H3

H4

H5
H6



太字 · 斜体 · 下線 · 打ち消し線

太字 斜体 下線 打ち消し線を同じ文字にまとめて適用できます。

重ねて使うこともできます。

上付き・下付き

面積は 3.5m2、脚注はこのように1 付けます。

水は H2O です。



文字色 · 蛍光ペン

ライト / ダークどちらでも読みやすい色で構成しています。

文字色 緑 · 珊瑚 · 紫 · 琥珀 · 青

蛍光ペン 黄 · 黄緑 · 水色 · 桃 · 紫 · 橙



リンク

アドレスを入れるとリンクになります。

http:// と https:// のアドレスに加えて、. や / で始まる同じサイト内のパスもリンクに使えます。javascript: のようなアドレスは許可されません。

たとえば https://nabi.saro.me と入力してスペースか改行を打つと、ご覧のとおり自動で変換されます。

リンクの開き方

文書にはリンク先のアドレスだけが保存され、リンクをどう開くかは文書を表示するページが決めます。

添付リンク

画像以外のファイルは、下のような添付リンクとして残ります。

添付ファイルのリンクが文書に保存されます。



配置

左揃え

中央揃え

右揃え

見出しには配置の変更を適用できません。



リスト

箇条書き

空行で - を打ってスペースを押すと、その場で箇条書きになります。

  • 箇条書きの項目です

    • Tab / Shift+Tab で字下げと字上げができます。

番号付きリスト

空行で 1. を打ってスペースを押すと番号付きリストになります。

  1. 一つ目

  2. 二つ目

  3. 三つ目

チェックリスト

空行で [ ] または [x] を打ってスペースを押すとチェックリストになります。

  • チェック済みの項目です。

  • まだチェックしていない項目です。



表

セルには複数の段落やブロックを入れ、背景色で内容を区別できます。下の表の文章と画像を直接編集してみてください。

資料

作業メモ

画像と説明を同じセルに入れています。

段落も自由に

Enterで段落を分け、Shift+Enterで同じ段落内に改行を入れてみてください。

  • セルの背景色を変更する

  • リスト内でTabを押して字下げする

  • 複数のセルを選んで同じ色を適用する

クリックして編集のヒントを表示

セル内にも折りたたみ・引用・コード・動画を入れられます。

セルを結合しても、それぞれの内容は順番どおりに残ります。

セル内をクリックすると、背景色と行・列のツールが表示されます。複数のセルを選ぶと、色と配置をまとめて変更できます。表全体の位置は「表全体を選択」を押してから配置ツールで変えてみてください。

色や複数のブロックを含む表は、Markdownで保存するときもHTMLとして保持されます。

セルの背景色

対応しているセルの背景色をすべて並べました。各セルをクリックして色を変えるか、プロパティツールの「既定」を選んで背景色を消してみてください。

黄色

yellow

黄緑

green

水色

cyan

ピンク

pink

紫

purple

オレンジ

orange

表の並べ替え

プレビューを押してから、在庫と価格の見出しを順に押してみてください。

型番

在庫

価格

重さ

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% まで調節でき、左・中央・右に寄せられます。



フロート領域

ひと目で確認

文章・画像・表を入れられる小さな補助領域です。

配置

右

最大幅

40%

本文の横に補足説明や小さな資料を置きたいときは、フロート領域を使ってみてください。本文の幅が広ければ、この文章が領域の横に回り込みます。領域内をクリックすると、同じエディターで文章を編集し、画像や表も挿入できます。

領域内にカーソルを置くとプロパティツールが開きます。左右の配置を切り替え、最大幅を20・30・40・50%から選んで本文に合う比率を探してみてください。表や画像を貼り付けても領域内に入ります。

選択した段落を領域で囲むこともできます。幅の横の解除ボタンを押すと、中の内容は本文に残ります。プレビューでは編集用の点線が消え、本文の幅が狭い場合や印刷時は1列で表示されます。


YouTube



アップロード

画像やファイルをエディタにドラッグしてみてください。

このデモではファイルを実際のサーバーに送信しません。アプリケーションにアップローダーを接続すると、サーバーに保存できます。

画像選択画面やアップロードを開いた後、カーソルだけを動かしても最初の位置に挿入されます。待機中に文書を編集すると挿入を中止し、お知らせを表示します。セル内で開始したアップロードの進捗表示と結果も、そのセル内に残ります。



引用

空行で > を打ってスペースを押すと引用箱になります。

複数行にわたって使えます。



コード

空行で ``` を打ってスペースか改行を押すとコード箱になります。

```java のように言語を書いてスペースか改行を打つと、その言語が適用されたコード箱になります。

import { createNabiWith, defaultWings } from 'nabi-note'

const { nabi } = createNabiWith(defaultWings)
const html = nabi.getHtml()



折りたたみ

折りたたみは見出しと中身でできています。

閉じた状態と開いた状態のどちらで保存するか指定できます。



ツールバー

見出し・文字の書式・配置・リスト・挿入・文書のツールをグループにまとめています。PCではツールが展開され、モバイルではクイックツールと全ツールパレットを使えます。選択した表やフロートのプロパティはツールバーの下に表示されます。


ショートカット

Shiftをすばやく2回押すと、キーボードでツールを選べます。PCでは展開されたツールバー、モバイルでは全ツールパレットを操作します。Tabでグループを切り替え、方向キーでツールを選び、Enterで実行してみてください。

引用・コード・折りたたみなどのブロックの末尾でEnterをすばやく2回押すと、外へ出られます。連打間隔の既定値は180msです。



自動書式

例

操作キー

結果

#

スペース

見出し

-

スペース

箇条書き

1.

スペース

番号付きリスト

[ ] · [x]

スペース

チェックリスト

>

スペース

引用

``` · ```ts

スペース · 改行

コード箱

---

改行

区切り線

https://…

スペース · 改行

リンク



出力関数

関数

結果

getHtml()

HTML

getJson()

JSON



DOM のない環境に対応

NABI TREE から HTML に変換するとき DOM は要りません。

サーバー(Node.js)でも、保存した NABI TREE を読み込み、同じ規則で HTML を作成できます。


モバイルに優しい

  • モバイル UI — 画面の幅に合わせて編集ツールを配置します。

  • モバイルキーボード補正 — キーボードが開くと編集画面の高さを調整します。

  • 柔軟なサイズ — 主要なレイアウトに rem 単位を使い、拡大・縮小に対応します。

  • 多言語 — 言語や文章の方向に合ったロケールと書体を設定できます。


カスタマイズ

  • 自作のwing — 必要な機能があれば、自分で作って接続できます。

  • 自作の CSS — 主な色・角の丸み・余白を --nabi-* トークンで調整し、ライトモードとダークモードに合わせられます。

  • オープンソース — GitHub でソースコードを公開し、共同で開発しています。


ドキュメントを見る → nabi.saro.me

HTMLエディタを読み込んでいます…
JSONnabi-treeエディタを読み込んでいます…
インストール
npm install nabi-note
コード
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('ja')
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')