ArsagaNote
バージョンアップ中最新版を適用するため、更新ファイルの取得・キャッシュの入れ替えを行っています。

🚀 EditorCustom プラグイン開発ガイド

プログラミング初心者でも大丈夫!ArsagaNoteに自分だけの機能を追加しよう。 コピー&ペーストから始めて、あなたのアイデアを世界中の人と共有できます。

EditorCustomプラグインシステムとは?

EditorCustomプラグインシステムは、ArsagaNoteの機能を自由に拡張できるオープンソース開発プラットフォームです。

🚀 開発者フレンドリー設計

テンプレートから始めて、統一APIで素早く機能実装。 Hot Reloadで快適な開発体験を提供します。

✨ 主な特徴

  • 動的プラグインローディング
  • Editor1統一UI継承
  • WebSocket協調編集対応
  • 権限管理・セキュリティ
  • Hot Reload開発体験

🎯 開発用途

  • カスタムエディター開発
  • 外部サービス連携
  • 専用ツール・ユーティリティ
  • 実験的機能プロトタイピング
  • 企業専用カスタマイズ

🎨 利用可能なプラグイン

📝 NotionEditor

Notion風のブロックベースエディター

エディター

📄 MarkdownView

Markdown編集・プレビューツール

エディター

📊 SpreadsheetEditor

Univer.js スプレッドシート

ツール

上記のプラグインはすべてローカルで動作確認できます。 開発の参考やベースとして活用してください。

🏗️ システム構成

🔌 プラグインレイヤー

NotionEditor, MarkdownView, YourPlugin...

⚙️ プラグインシステム

Registry, Loader, Renderer, ErrorBoundary

🎨 Editor1統一基盤

UI, WebSocket, Authentication, Workspace

🚀 なぜプラグイン開発を始めるべきか?

迅速な開発

テンプレートを使って5分でプラグイン作成開始。 すぐにアイデアを形にできます。

統一体験

Editor1の統一UIとWebSocket機能を 自動継承。一貫性のある体験を提供。

コミュニティ

オープンソースコミュニティで プラグインを共有・改善できます。

🚀 5分で開発環境を準備しよう

プラグイン作成に必要なファイルをダウンロードして、すぐに開発を始められる環境を作ります

STEP 1

📦 プラグイン開発キットをダウンロード

やること: プラグインを作るためのファイル一式をパソコンにダウンロードします

# ターミナル(コマンドプロンプト)で以下を実行: # 1. プラグイン開発キットをダウンロード git clone https://github.com/arsaga-note/plugin-marketplace.git cd plugin-marketplace # 2. 必要なプログラムをインストール npm install

✅ 完了すると:

• plugin-marketplace フォルダができます
• プラグイン作成に必要な全てのファイルが準備されます
• 他の人が作ったプラグインの例も見ることができます

💡 初心者の方へ: gitやnpmがインストールされていない場合は、先にNode.js公式サイトからインストールしてください

STEP 2

⚡ 開発環境を自動で準備する

やること: 1つのコマンドで、プラグインをテストできる環境を自動で作ります

# 開発環境を自動でセットアップ(これ1つだけでOK) ./scripts/setup-dev-env.h

🤔 これは何をしているの?

• ArsagaNoteのメインアプリを起動します
• あなたのプラグインファイルとメインアプリを連携させます
• データベースなど必要なものも一緒に準備します
• 全て自動なので、難しい設定は必要ありません

✅ 完了すると以下が使えるようになります:

• http://localhost:3000 - ArsagaNoteのメイン画面
• http://localhost:3001 - プラグイン開発用の画面
• 自動でファイルの変更を検知して、すぐに反映される環境

⚠️ 注意: 初回は時間がかかる場合があります(2-3分程度)。コーヒーでも飲んで待ちましょう☕

STEP 3

📝 あなた専用のプラグインを作成

やること: プラグインのひな形をコピーして、あなた専用のプラグインファイルを作ります

# あなたのプラグイン名で新しいプラグインを作成 npm run create-plugin my-awesome-plugin # ↑ "my-awesome-plugin" の部分を好きな名前に変えてください # 例: npm run create-plugin todo-list # 例: npm run create-plugin calculator

🤔 何ができるの?

• すでに動く状態のプラグインファイルが作られます
• 必要な設定も全部入っているので、すぐに開発を始められます
• 他の人が作ったプラグインを参考にすることもできます

💡 プラグイン名のルール: 英数字とハイフン(-)のみ使用可能です(日本語は使えません)

STEP 4

✏️ プラグインの基本情報を編集

やること: 作ったプラグインファイルの中で、名前や説明などを自分用に変更します

📁 編集するファイル: src/plugins/あなたのプラグイン名/index.tsx

// プラグインの基本情報(これを変更してください) export const PLUGIN_CONFIG = { name: 'my-awesome-plugin', // URL識別子(変更不要) displayName: 'My Awesome Plugin', // 表示名(日本語OK) description: '素晴らしい機能を提供するプラグイン', // 説明文 version: '1.0.0', // バージョン author: 'Your Name', // あなたの名前 category: 'tool' as const, // 種類: tool, editor, utility など };

📝 編集する項目

• displayName: ユーザーが見る名前
• description: プラグインの説明
• author: あなたの名前
• category: プラグインの種類

⚠️ 変更しないでください

• name: システム内部で使用
• permissions: セキュリティ設定
• 他の技術的な項目

💡 プラグインの種類: tool(ツール)、editor(編集機能)、utility(便利機能)から選んでください

STEP 5

🚀 プラグインをマーケットプレイスに登録

やること: 作ったプラグインをマーケットプレイスに登録して、みんなが使えるようにします

🎉 簡単登録! ファイル編集不要で、画面から簡単に登録できます

1️⃣ マーケットプレイスページにアクセス

ブラウザでマーケットプレイスページを開きます

http://localhost:3000/editor-custom/marketplace

2️⃣ 「新しいプラグインを登録」ボタンをクリック

ページ上部にある紫色のボタンを押すと、登録フォームが開きます

🚀 新しいプラグインを登録

← このボタンを探してクリック

3️⃣ フォームに情報を入力

3つのステップで簡単に入力できます:

基本情報

プラグイン名・表示名・説明文・種類を入力

詳細設定

バージョン・作者名・開発者ページなど(任意)

確認・登録

入力内容を確認して「プラグインを登録」ボタン

4️⃣ 登録完了!

「🎉 プラグイン登録完了!」のメッセージが表示されたら成功です。
自動的にあなたのプラグインページが開きます。

💡 入力のヒント

• プラグイン名: Step 3で作ったフォルダ名と同じ
• コンポーネントパス: ../plugins/フォルダ名/index
• 種類: tool(ツール)が一般的
• わからない項目は空欄でもOK

❌ うまくいかない時

• プラグイン名が既に使われている
• ファイルパスが間違っている
• 必須項目が未入力
• ブラウザを更新してもう一度試す

✨ 登録後にできること

• マーケットプレイスに表示される
• http://localhost:3000/editor-custom/あなたのプラグイン名 でアクセス可能
• 他の人があなたのプラグインを発見・使用できる
• 設定は後からいつでも変更可能

STEP 6

🎉 プラグインを実際に動かしてみる

やること: あなたが作ったプラグインが実際に動くか確認します

# 開発環境を起動(Step 2でやったコマンド) ./scripts/setup-dev-env.h # ブラウザを開いて確認 open http://localhost:3000/editor-custom/my-awesome-plugin # ↑ "my-awesome-plugin" の部分をあなたのプラグイン名に変更

🎉 完成です!

ブラウザでプラグインが表示されれば成功です。 ファイルを編集すると、ブラウザも自動で更新されます。

✅ 成功の確認方法:

• ブラウザにプラグインが表示される
• ファイル編集すると画面が自動更新
• エラーメッセージが出ない
• プラグインの機能が動作する

🔧 次にできること:

• プラグインの機能を追加・変更
• 他のプラグインを参考に学習
• マーケットプレイスで公開
• コミュニティで共有

my-awesome-plugin/ ├── README.md # ドキュメント ├── index.tsx # メインプラグインコンポーネント ├── components/ # プラグイン専用コンポーネント │ ├── PluginHeader.tsx # ヘッダー │ ├── PluginContent.tsx # メインコンテンツ │ └── PluginSettings.tsx # 設定画面(オプション) ├── hooks/ # プラグイン専用フック │ └── usePluginState.ts # 状態管理フック ├── styles/ # プラグイン専用スタイル │ └── plugin.css # CSSスタイル └── types/ # プラグイン専用型定義 └── index.ts # 型定義

プラグインは必要な権限のみを要求できます。

interface PluginPermissions { fileSystem: boolean; // ファイル読み書き network: boolean; // 外部API呼び出し collaboration: boolean; // WebSocket/Y.js機能 workspace: 'read' | 'write' | 'admin'; // ワークスペース権限 localStorage: boolean; // ローカルストレージ clipboard: boolean; // クリップボード notifications: boolean; // 通知機能 }

推奨権限

  • • collaboration: WebSocket機能
  • • workspace: 'read' (基本)
  • • localStorage: データ保存

注意が必要

  • • fileSystem: セキュリティリスク
  • • network: 外部依存・プライバシー
  • • workspace: 'admin' 管理機能

プラグインは自動的にEditor1の統一機能を継承できます。

export default function MyPlugin({ context }) { const { editor1, collaboration, user, workspaceId } = context; // Toast表示 editor1.showToast('成功しました!', 'success'); // サイドバー制御 editor1.setSidebarOpen(false); // モーダル表示 editor1.openModal(<MyModal />, 'タイトル'); // ページ遷移 editor1.navigate('/editor-custom/other-plugin'); // WebSocket機能(権限必要) if (collaboration?.isConnected) { const { ydoc, provider } = collaboration; // Y.js協調編集機能を使用 } return <div>My Plugin Content</div>; }

WebSocket機能を使用するには、プラグインの権限設定でcollaboration: true を設定してください。

// Y.js協調編集の例 export default function CollaborativePlugin({ context }) { const { collaboration, user } = context; useEffect(() => { if (collaboration?.['ydoc']) { const sharedMap = collaboration['ydoc'].getMap('plugin-data'); // データ監視 sharedMap.observe((event) => { if (process.env.NODE_ENV === "development") { console.log('データが更新されました:', event); }); // データ更新 sharedMap.set('user-' + user['uid'], { name: user['displayName'], timestamp: Date.now(), }); } }, [collaboration, user]); return <div>リアルタイム協調機能</div>; }

🔌 PluginContextType API

interface PluginContextType { // 基本情報 pluginName: string; subPaths: string[]; searchParams: Record<string, any>; // 認証・ワークスペース user: AuthUser; workspaceId: string; workspace?: Workspace; // ページ管理 pages?: Page[]; pageList?: PageListData; // 権限 permissions: PluginPermissions; hasPermission: (permission: keyof PluginPermissions) => boolean; // Editor1統一機能 editor1: Editor1Functions; // WebSocket/Collaboration(権限がある場合のみ) collaboration?: CollaborationContext; }

Editor1機能

  • navigate(path: string)

    ページ遷移

  • showToast(message, type?)

    Toast通知表示

  • setSidebarOpen(open: boolean)

    サイドバー制御

  • openModal(content, title?)

    モーダル表示

WebSocket機能

  • collaboration['isConnected']

    接続状態

  • collaboration['ydoc']

    Y.jsドキュメント

  • collaboration['provider']

    Hocuspocusプロバイダー

  • collaboration.connectionStatus

    詳細接続状態

⚙️ プラグイン設定 API

interface PluginConfig { name: string; // プラグイン識別子 displayName: string; // 表示名 description: string; // 説明文 version: string; // バージョン author?: string | undefined; // 作者 // コンポーネント component: () => Promise<{ default: ComponentType<any> }>; icon?: ComponentType<any>; // ルーティング routes?: string[] | undefined; // サポートするサブルート defaultRoute?: string | undefined; // デフォルトルート // メタデータ category: PluginCategory; // カテゴリ tags?: string[] | undefined; // タグ permissions?: PluginPermissions; // 権限設定 config?: Record<string, any> | undefined; // プラグイン固有設定 }

🎣 利用可能なフック

usePluginState

プラグイン専用の状態管理とローカルストレージ連携

const { pluginData, updatePluginData, resetPluginData } = usePluginState('my-plugin');

usePluginPermissions

プラグインの権限チェック

const { permissions, hasPermission } = usePluginPermissions('my-plugin');

🎯 シンプルプラグイン

最小構成のプラグインサンプル

// HelloWorldPlugin.tsx export default function HelloWorldPlugin({ context }) { const { user, editor1 } = context; return ( <div> <h1>Hello, {user['displayName']}!</h1> <button onClick={() => editor1.showToast('Hello World!', 'success')} > 挨拶する </button> </div> ); }

💾 データ永続化プラグイン

ローカルストレージを使ったデータ保存例

// TodoPlugin.tsx import { usePluginState } from '../hooks/usePluginState'; export default function TodoPlugin({ context }) { const { pluginData, updatePluginData } = usePluginState('todo-plugin'); const [newTodo, setNewTodo] = useState(''); const todos = pluginData?.todos || []; const addTodo = () => { if (newTodo.trim()) { updatePluginData({ todos: [...todos, { id: Date.now(), text: newTodo, done: false }] }); setNewTodo(''); context.editor1.showToast('タスクを追加しました', 'success'); } }; return ( <div> <input value={newTodo} onChange={(e) => setNewTodo(e.target['value'])} placeholder="新しいタスク..." /> <button onClick={addTodo}>追加</button> {todos.map(todo => ( <div key={todo['id']}> <input type="checkbox" checked={todo.done} onChange={() => {/* 完了状態切り替え */}} /> {todo['text']} </div> ))} </div> ); }

🌐 リアルタイム協調プラグイン

Y.jsを使ったリアルタイム機能の例

// CollaborativeCounterPlugin.tsx export default function CollaborativeCounterPlugin({ context }) { const { collaboration, user } = context; const [counter, setCounter] = useState(0); useEffect(() => { if (collaboration?.['ydoc']) { const sharedMap = collaboration['ydoc'].getMap('counter'); // 初期値設定 if (!sharedMap.has('value')) { sharedMap.set('value', 0); } // リアルタイム同期 const updateCounter = () => { setCounter(sharedMap.get('value') || 0); }; sharedMap.observe(updateCounter); updateCounter(); return () => sharedMap.unobserve(updateCounter); } }, [collaboration]); const increment = () => { if (collaboration?.['ydoc']) { const sharedMap = collaboration['ydoc'].getMap('counter'); const currentValue = sharedMap.get('value') || 0; sharedMap.set('value', currentValue + 1); sharedMap.set('lastUpdatedBy', user['displayName']); } }; return ( <div> <h2>共有カウンター: {counter}</h2> <button onClick={increment}>+1</button> {collaboration?.isConnected ? ( <p>🟢 リアルタイム同期中</p> ) : ( <p>🔴 オフライン</p> )} </div> ); }

🌍 外部API連携プラグイン

外部サービスとの連携例(network権限が必要)

// WeatherPlugin.tsx export default function WeatherPlugin({ context }) { const [weather, setWeather] = useState(null); const [loading, setLoading] = useState(false); // network権限チェック if (!context.hasPermission('network')) { return <div>このプラグインにはネットワーク権限が必要です</div>; } const fetchWeather = async (city) => { setLoading(true); try { // RestApiClientを使用して天気情報を取得 const { restApiClient } = await import("@/lib/api"); const data = await restApiClient.getWeather(city); setWeather(data); context.editor1.showToast('天気情報を取得しました', 'success'); } catch (error) { context.editor1.showToast('天気情報の取得に失敗', 'error'); } finally { setLoading(false); } }; return ( <div> <input placeholder="都市名を入力..." onKeyPress={(e) => { if (e['key'] === 'Enter') { fetchWeather(e.target['value']); } }} /> {loading && <p>読み込み中...</p>} {weather && ( <div> <h3>{weather.city}</h3> <p>気温: {weather.temperature}°C</p> <p>天気: {weather['description']}</p> </div> )} </div> ); }

A. 以下を確認してください:

  • • プラグインレジストリーに正しく登録されているか
  • • コンポーネントのdefault exportが正しいか
  • • 必要な権限が設定されているか
  • • ブラウザのコンソールでエラーを確認
  • • PluginErrorBoundaryのエラー表示を確認

A. 必要最小限の権限を設定することを推奨します:

  • • collaboration: true - WebSocket機能を使う場合
  • • workspace: 'read' - 基本的な読み取り
  • • workspace: 'write' - データ変更が必要な場合
  • • localStorage: true - データ永続化が必要な場合

fileSystemやnetwork権限は セキュリティ上のリスクがあるため、本当に必要な場合のみ使用してください。

A. 複数の方法があります:

1. カスタムイベント(推奨)

// 送信側 window.dispatchEvent(new CustomEvent('plugin-message', { detail: { from: 'my-plugin', data: { hello: 'world' } } })); // 受信側 useEffect(() => { const handler = (e) => if (process.env.NODE_ENV === "development") { console.log(e.detail); window.addEventListener('plugin-message', handler); return () => window.removeEventListener('plugin-message', handler); }, []);

2. Y.js共有マップ(WebSocket権限必要)

const sharedMap = collaboration['ydoc'].getMap('plugin-communication'); sharedMap.set('message', { from: 'my-plugin', data: 'hello' });

A. 以下の最適化手法を活用してください:

  • • React.memoで不要な再レンダリングを防止
  • • useCallback/useMemoで計算をキャッシュ
  • • 重い処理はWeb Workersに移譲
  • • 不要なstate更新を避ける
  • • 大きなデータは仮想化を検討
  • • プラグインの動的インポートを活用

A. GitHubでプラグインを共有できます:

  • 1. プラグインをGitHubリポジトリに公開
  • 2. 詳細なREADME.mdを作成
  • 3. スクリーンショット・デモ動画を追加
  • 4. コミュニティDiscordで紹介
  • 5. プルリクエストでメインリポジトリに追加を提案

コミュニティプラグインマーケット(近日公開予定)
将来的にはWebUI上でプラグインの検索・インストールが ワンクリックで可能になります!

🆘 サポート・ヘルプ

GitHub Issues

バグ報告や機能要望

ドキュメント

詳細なAPI仕様書

Discord

リアルタイムサポート