
Markdown Editor:Gravity UIベースのWYSIWYGおよびmarkupエディタ
Markdown Editor:Gravity UIベースのWYSIWYGおよびmarkupエディタ

TL;DR
- WYSIWYGモードとmarkdownマークアップモード(プレビューとスプリット付き)を同時に使用できます。
- 多くのブロックを標準でサポートしています。
- 機能を拡張できます — WYSIWYGモードには拡張システムがあります。
- Reactアプリケーション用に作成されています。
- Gravity UIのテーマとコンポーネントを使用しています。
- 完全にオープンソース技術(ProseMirror、CodeMirror、markdown‑it、Diplodoc、Gravity UI)で構築されています。
- CommonMark標準に準拠し、標準のmarkdown言語とYandex Flavored Markdown (YFM)をサポートしています。
こんにちは!私はセルゲイ・マフナトキン、Yandex CloudのUser Experience部門の開発者です。昨年、私たちのデザインシステムとコンポーネントライブラリGravity UIについて書きました。それ以来、システムは何度も更新され、新しい機能が追加されました。今日は新しいツール — Markdown Editorについてお話しします。これはドキュメント作業プロセスを大幅に簡素化します。
ユーザーインターフェース作成の歴史、アーキテクチャの特徴、統合と独自の拡張機能開発の技術的な詳細、そしてなぜこれらすべてがオープンソースで利用可能なのかについてお話しします。
ちなみに、ここでツールを試すことができます:
独自のMarkdown Editorが必要な理由
企業情報を便利に保存・構造化するために、ナレッジベースを作成できるプラットフォームWikiを開発しました。ナレッジベースに加えて、ドキュメントとコードがファイルストレージ(.mdファイル)に並んで存在するDocs as Codeなどのドキュメンテーションアプローチも開発しました。こうしてDiplodoсプラットフォームが誕生しました。
WikiとDiplodocに共通しているのは、両方のプラットフォームがmarkdownの方言 — Yandex Flavored Markdown (YFM)を使用していることです。これはNebius、Bitrix、DoubleCloud、Mappable、Metemで使用されています。
時間の経過とともに、テキストの作成と編集のプロセスについて異なる見方を持つ2つのユーザーグループがあることに気づきました。一方はMS Word、Confluence、Notionのようにテキストを操作し、最終結果をすぐに見ることを好みます。もう一方はマークアップだけを信頼し、markdownを使用してページをフォーマットすることを好みます。WYSIWYG/markdownモードで同時に動作する既知のライブラリは見つかりませんでした。例えば、NotionはWYSIWYGのみで、コードエディタにはmarkdownとプレビューモードしかありません。
私たちは、ビジュアル(WYSIWYG)とマークアップ(markdown)の2つのモードで同時に動作できるmarkdownエディタを開発しました。最初のモードでは、パネルのアイコンがテキストのマークアップを助け、2番目のモードでは、ユーザーが手動でmarkdownコードを編集できます。さらに、私たちのソリューションは、作成時にどのモードを使用したかに関係なく、ドキュメントをmdファイルとして保存します。
これがビジュアルエディタの外観で、ボタンを使ってテキストをフォーマットできます:

そして、これがマークアップモードで、フォーマット要素は特殊文字を使用して示されます:

Gravity UIのMarkdown Editorの機能
エディタはCommonMark標準に準拠し、標準のmarkdown言語とYFM言語をサポートしています。また、Github Flavored Markdownなど、他のmarkdown方言で構文を拡張する機能も追加しました。同時に、エディタはmarkupモードからWYSIWYGモードに切り替えることができ、ドキュメント自体はmdまたは拡張md(例えばYFMの場合)マークアップとして保存されます。
拡張機能
エディタには多くの拡張機能と設定が最初から組み込まれています。例えば、Mermaidダイアグラムやhtmlブロック:


エディタのコアを簡単に拡張できるよう努めました。開発者は独自の拡張機能や追加機能を作成でき、これにより:
- 新しいエンティティ — ブロックやテキスト修飾子を追加できます
- markdownパーサーを追加設定できます
- エディタを外部から操作できるアクションを追加できます
- インターフェース機能を充実させることができます。例えば、スラッシュを入力したときに利用可能なコマンドのメニューを表示します
- 現在の動作を変更できます。例えば、画像やファイルを挿入してストレージにアップロードします
Wikiのために開発したそのような拡張機能の例をいくつか紹介します:
- 共同編集モード

- draw.ioダイアグラムブロック

- YandexGPTプラグイン


- インクルード

- セクション構造

- 便利なグリッドを作成するためのセクション

- プレビュー付きmarkdownモード

- その他多数

マークアップは自動的に変換できます。マウスを使わずに作業したい場合、ビジュアルエディタモードには、テキストに直接マークアップを適用できる特殊文字が用意されています。例えば、**はWYSIWYGモードでテキストを太字に変換します。これらの文字を使用して、テキストのフォーマット、インラインおよびブロックコードの作成ができます。
/記号を入力して拡張機能メニューを呼び出すこともできます。

プリセット
エディタでは、各プロジェクト用にツールパネルを個別に設定できますが、いくつかの既製設定 — プリセットが付属しています。
プリセットなしのエディタ:

CommonMarkプリセットは、太字、斜体、見出し、リスト、リンク、引用、コードブロックなどの標準的なmarkdown要素をサポートします。

デフォルトプリセットには、取り消し線テキストも追加され、セルにテキストのみを含むテーブルも追加されます。このプリセットは標準的なmarkdown‑itに対応しています。

上記のように、エディタはYFMもサポートしているため、Diplodocと優れた統合ができます。YFMプリセットには、強化されたテーブル、ファイルと画像の挿入、チェックボックス、カット、タブ、等幅フォントなどの追加要素が含まれています。

フルプリセットにはさらに多くの要素が含まれています。

アーキテクチャ
WYSIWYGモードのエディタの基盤は、有名なライブラリProseMirrorであり、マークアップにはCodeMirrorが使用されています。ProseMirrorはフォーマット付き編集をサポートし、CodeMirrorはマークアップなしのテキストを扱う必要がある状況に適しています。
これらのライブラリを選択した理由は、同じ作者によって開発され、アーキテクチャと実装アプローチが統一されており、大きなコミュニティによってサポートされ、多くのエディタで使用され、テキスト作業に最適化されているからです。例えば、ドキュメントに変更を加えるためのトランザクションシステム、ビュー用のデコレーション、DOM仮想化、多くのプログラミング言語の構文サポートなどがあります。
統合
私たちのエディタはReact hookとして簡単に接続できます:
import React from 'react';
import {useMarkdownEditor, MarkdownEditorView} from '@gravity-ui/markdown-editor';
import {toaster} from '@gravity-ui/uikit/toaster-singleton-react-18';
function Editor({onSubmit}) {
const editor = useMarkdownEditor({allowHTML: false});
React.useEffect(() => {
function submitHandler() {
// 現在のコンテンツをmarkdownマークアップにシリアライズ
const value = editor.getValue();
onSubmit(value);
}
editor.on('submit', submitHandler);
return () => {
editor.off('submit', submitHandler);
};
}, [onSubmit]);
return <MarkdownEditorView stickyToolbar autofocus toaster={toaster} editor={editor} />;
}
uikit GravityUIライブラリのコンポーネントを使用しています。これにより、インターフェース全体が一貫性を持ち、統一されたスタイルガイドラインに準拠することが保証されます。これらのコンポーネントを使用することで、ユーザーにとっての高い一貫性と認識性が確保され、エディタでの作業がさらに便利になります。
Reactアプリケーションにエディタを接続する詳細な手順と、さまざまな拡張機能を接続する方法(例:YandexGPT、Mermaid、LaTeX)があります。
カスタム拡張機能の統合
エディタにはすでにいくつかの追加拡張機能が統合されています。しかし、それが不十分な場合、開発者はエディタのWYSIWYGモードに独自の拡張機能を追加できます。拡張機能が提供できることについては上で説明しました。
新しいブロックやテキスト修飾子を追加したい場合、まずconfigureMdメソッドを使用してmarkdown-itの内部インスタンスを設定する必要があります。次に、addNodeまたはaddMarkメソッドを使用して、エンティティ名と3つの必須フィールドを持つオブジェクトを返すコールバック関数を渡して、新しいエンティティに関する知識を追加する必要があります:
import insPlugin from 'markdown-it-ins';
export const underlineMarkName = 'ins';
export const UnderlineSpecs: ExtensionAuto = (builder) => {
builder
.configureMd((md) => md.use(insPlugin))
.addMark(underlineMarkName, () => ({
spec: {
parseDOM: [{tag: 'ins'}, {tag: 'u'}],
toDOM() {
return ['ins'];
},
},
toMd: {open: '++', close: '++', mixable: true, expelEnclosingWhitespace: true},
fromMd: {tokenSpec: {name: underlineMarkName, type: 'mark'}},
}));
};
spec— ProseMirrorの仕様fromMd— markdownマークアップをProseMirror内の表現に解析するための設定toMd— エンティティをmarkdownマークアップにシリアライズするための設定
例えば、以下は下線テキスト用の拡張機能の設定です。addActionメソッドを使用してアクションを追加することで拡張できます:
import {toggleMark} from 'prosemirror-commands';
const undAction = 'underline';
builder
.addAction(undAction, ({schema}) => ({
isActive: (state) => Boolean(isMarkActive(state, markType)),
isEnable: toggleMark(underlineType(schema)),
run: toggleMark(underlineType(schema)),
})
)
そのようなアクションは次のようにコードで呼び出すことができます:
// editor – useMarkdownEditorの呼び出し結果として取得したエディタインスタンス
editor.actions.underline.run(),
新しい拡張機能を作成するための完全な手順はドキュメントで確認できます。
私たちは常にエディタの使用範囲を拡大しています:現在、VS Code用のプラグインに取り組んでおり、エディタから直接便利なWYSIWYGモードでmdファイルを操作できるようになります。また、フル機能のモバイルモードを追加する予定です。これにより、すべてのユーザーが携帯電話だけでエディタで作業できるようになります。
私たちのエディタは一瞬で登場したわけではありません:これは経験と知識の蓄積の結果です。エディタが完全にオープンソース製品(ProseMirror、CodeMirror、markdown-itなどの信頼性があり実績のあるツール、そして私たち独自の開発であるDiplodocとGravity UI)に基づいていることを誇りに思います。
いつでもエディタの開発に貢献できます:プルリクエストを作成するか、Issuesセクションにリストされている現在の問題の解決を手伝ってください。あなたのサポートと新鮮な視点が、エディタをより良くするのに役立ちます。プロジェクトが役立つと思われる場合は、GitHubリポジトリにスターを付けてください :)

セルゲイ・マフナトキン
インターフェース開発者