プログラミング初心者でも大丈夫!ArsagaNoteに自分だけの機能を追加しよう。 コピー&ペーストから始めて、あなたのアイデアを世界中の人と共有できます。
EditorCustomプラグインシステムとは?
EditorCustomプラグインシステムは、ArsagaNoteの機能を自由に拡張できるオープンソース開発プラットフォームです。
🚀 開発者フレンドリー設計
テンプレートから始めて、統一APIで素早く機能実装。 Hot Reloadで快適な開発体験を提供します。
🎨 利用可能なプラグイン
Notion風のブロックベースエディター
エディターMarkdown編集・プレビューツール
エディターUniver.js スプレッドシート
ツール上記のプラグインはすべてローカルで動作確認できます。 開発の参考やベースとして活用してください。
🔌 プラグインレイヤー
NotionEditor, MarkdownView, YourPlugin...
⚙️ プラグインシステム
Registry, Loader, Renderer, ErrorBoundary
🎨 Editor1統一基盤
UI, WebSocket, Authentication, Workspace
テンプレートを使って5分でプラグイン作成開始。 すぐにアイデアを形にできます。
Editor1の統一UIとWebSocket機能を 自動継承。一貫性のある体験を提供。
オープンソースコミュニティで プラグインを共有・改善できます。
プラグイン作成に必要なファイルをダウンロードして、すぐに開発を始められる環境を作ります
やること: プラグインを作るためのファイル一式をパソコンにダウンロードします
# ターミナル(コマンドプロンプト)で以下を実行:
# 1. プラグイン開発キットをダウンロード
git clone https://github.com/arsaga-note/plugin-marketplace.git
cd plugin-marketplace
# 2. 必要なプログラムをインストール
npm install✅ 完了すると:
• plugin-marketplace フォルダができます
• プラグイン作成に必要な全てのファイルが準備されます
• 他の人が作ったプラグインの例も見ることができます
💡 初心者の方へ: gitやnpmがインストールされていない場合は、先にNode.js公式サイトからインストールしてください
やること: 1つのコマンドで、プラグインをテストできる環境を自動で作ります
# 開発環境を自動でセットアップ(これ1つだけでOK)
./scripts/setup-dev-env.h🤔 これは何をしているの?
• ArsagaNoteのメインアプリを起動します
• あなたのプラグインファイルとメインアプリを連携させます
• データベースなど必要なものも一緒に準備します
• 全て自動なので、難しい設定は必要ありません
✅ 完了すると以下が使えるようになります:
• http://localhost:3000 - ArsagaNoteのメイン画面
• http://localhost:3001 - プラグイン開発用の画面
• 自動でファイルの変更を検知して、すぐに反映される環境
⚠️ 注意: 初回は時間がかかる場合があります(2-3分程度)。コーヒーでも飲んで待ちましょう☕
やること: プラグインのひな形をコピーして、あなた専用のプラグインファイルを作ります
# あなたのプラグイン名で新しいプラグインを作成
npm run create-plugin my-awesome-plugin
# ↑ "my-awesome-plugin" の部分を好きな名前に変えてください
# 例: npm run create-plugin todo-list
# 例: npm run create-plugin calculator🤔 何ができるの?
• すでに動く状態のプラグインファイルが作られます
• 必要な設定も全部入っているので、すぐに開発を始められます
• 他の人が作ったプラグインを参考にすることもできます
💡 プラグイン名のルール: 英数字とハイフン(-)のみ使用可能です(日本語は使えません)
やること: 作ったプラグインファイルの中で、名前や説明などを自分用に変更します
📁 編集するファイル: 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(便利機能)から選んでください
やること: 作ったプラグインをマーケットプレイスに登録して、みんなが使えるようにします
🎉 簡単登録! ファイル編集不要で、画面から簡単に登録できます
1️⃣ マーケットプレイスページにアクセス
ブラウザでマーケットプレイスページを開きます
http://localhost:3000/editor-custom/marketplace2️⃣ 「新しいプラグインを登録」ボタンをクリック
ページ上部にある紫色のボタンを押すと、登録フォームが開きます
← このボタンを探してクリック
3️⃣ フォームに情報を入力
3つのステップで簡単に入力できます:
プラグイン名・表示名・説明文・種類を入力
バージョン・作者名・開発者ページなど(任意)
入力内容を確認して「プラグインを登録」ボタン
4️⃣ 登録完了!
「🎉 プラグイン登録完了!」のメッセージが表示されたら成功です。
自動的にあなたのプラグインページが開きます。
💡 入力のヒント
• プラグイン名: Step 3で作ったフォルダ名と同じ
• コンポーネントパス: ../plugins/フォルダ名/index
• 種類: tool(ツール)が一般的
• わからない項目は空欄でもOK
❌ うまくいかない時
• プラグイン名が既に使われている
• ファイルパスが間違っている
• 必須項目が未入力
• ブラウザを更新してもう一度試す
✨ 登録後にできること
• マーケットプレイスに表示される
• http://localhost:3000/editor-custom/あなたのプラグイン名 でアクセス可能
• 他の人があなたのプラグインを発見・使用できる
• 設定は後からいつでも変更可能
やること: あなたが作ったプラグインが実際に動くか確認します
# 開発環境を起動(Step 2でやったコマンド)
./scripts/setup-dev-env.h
# ブラウザを開いて確認
open http://localhost:3000/editor-custom/my-awesome-plugin
# ↑ "my-awesome-plugin" の部分をあなたのプラグイン名に変更🎉 完成です!
ブラウザでプラグインが表示されれば成功です。 ファイルを編集すると、ブラウザも自動で更新されます。
✅ 成功の確認方法:
• ブラウザにプラグインが表示される
• ファイル編集すると画面が自動更新
• エラーメッセージが出ない
• プラグインの機能が動作する
🔧 次にできること:
• プラグインの機能を追加・変更
• 他のプラグインを参考に学習
• マーケットプレイスで公開
• コミュニティで共有
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;
}navigate(path: string)ページ遷移
showToast(message, type?) Toast通知表示
setSidebarOpen(open: boolean) サイドバー制御
openModal(content, title?) モーダル表示
collaboration['isConnected']接続状態
collaboration['ydoc']Y.jsドキュメント
collaboration['provider']Hocuspocusプロバイダー
collaboration.connectionStatus詳細接続状態
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; // プラグイン固有設定
}プラグイン専用の状態管理とローカルストレージ連携
const { pluginData, updatePluginData, resetPluginData } =
usePluginState('my-plugin');プラグインの権限チェック
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>
);
}外部サービスとの連携例(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>
);
}バグ報告や機能要望
詳細なAPI仕様書
リアルタイムサポート