@notehub.md/cli 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/commands/build.d.ts +30 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +202 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/create.d.ts +33 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +236 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
- package/templates/PLUGIN_GUIDE.md +599 -0
- package/templates/PLUGIN_GUIDE_RU.md +599 -0
- package/templates/docs.html +534 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
# 🔌 Руководство разработчика плагинов Notehub
|
|
2
|
+
|
|
3
|
+
> Полная документация по созданию плагинов для Notehub.md
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Содержание
|
|
8
|
+
|
|
9
|
+
1. [Начало работы](#начало-работы)
|
|
10
|
+
2. [Архитектура плагинов](#архитектура-плагинов)
|
|
11
|
+
3. [Справочник API](#справочник-api)
|
|
12
|
+
4. [Виджеты (Порталы)](#виджеты-порталы)
|
|
13
|
+
5. [Интеграция настроек](#интеграция-настроек)
|
|
14
|
+
6. [Контекстное меню](#контекстное-меню)
|
|
15
|
+
7. [Примеры плагинов](#примеры-плагинов)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Начало работы
|
|
20
|
+
|
|
21
|
+
Это руководство проведет вас через создание первого плагина Notehub.md.
|
|
22
|
+
|
|
23
|
+
## Предварительные требования
|
|
24
|
+
|
|
25
|
+
- **Node.js** v18+ с npm/pnpm
|
|
26
|
+
- **TypeScript** (рекомендуется, JavaScript тоже работает)
|
|
27
|
+
- **Сборщик**: esbuild, Vite или Rollup
|
|
28
|
+
- Хранилище Notehub.md для тестирования
|
|
29
|
+
|
|
30
|
+
## Структура плагина
|
|
31
|
+
|
|
32
|
+
Каждый плагин требует как минимум:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
my-plugin/
|
|
36
|
+
├── manifest.json # Метаданные плагина (обязательно)
|
|
37
|
+
├── main.js # Точка входа (скомпилированная)
|
|
38
|
+
└── src/ # Исходные файлы (опционально)
|
|
39
|
+
└── index.ts
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## manifest.json
|
|
43
|
+
|
|
44
|
+
Манифест описывает ваш плагин для Notehub:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"id": "my-awesome-plugin",
|
|
49
|
+
"name": "Мой Крутой Плагин",
|
|
50
|
+
"version": "1.0.0",
|
|
51
|
+
"main": "main.js",
|
|
52
|
+
"dependencies": []
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Поле | Обязательно | Описание |
|
|
57
|
+
|------|-------------|----------|
|
|
58
|
+
| `id` | ✅ | Уникальный идентификатор (строчные буквы, дефисы разрешены) |
|
|
59
|
+
| `name` | ✅ | Человекочитаемое имя |
|
|
60
|
+
| `version` | ✅ | Семантическая версия (например, `1.0.0`) |
|
|
61
|
+
| `main` | ❌ | Файл точки входа, по умолчанию `main.js` |
|
|
62
|
+
| `dependencies` | ❌ | Массив ID плагинов, от которых зависит этот плагин |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Быстрый старт с CLI
|
|
67
|
+
|
|
68
|
+
Самый быстрый способ создать новый плагин:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx @notehub.md/cli create ext.my-plugin --name "My Plugin"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Это сгенерирует полную структуру плагина со всеми необходимыми файлами.
|
|
75
|
+
|
|
76
|
+
## Ручная настройка
|
|
77
|
+
|
|
78
|
+
### Шаг 1: Создайте структуру папок
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
mkdir my-plugin
|
|
82
|
+
cd my-plugin
|
|
83
|
+
npm init -y
|
|
84
|
+
npm install @notehub/api typescript esbuild --save-dev
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Шаг 2: Создайте src/index.ts
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { NotehubPlugin, PluginContext } from '@notehub/api';
|
|
91
|
+
|
|
92
|
+
export default class HelloWorldPlugin extends NotehubPlugin {
|
|
93
|
+
async onload(ctx: PluginContext): Promise<void> {
|
|
94
|
+
await ctx.invokeApi('logger:info', 'HelloWorld', 'Привет из моего плагина!');
|
|
95
|
+
|
|
96
|
+
ctx.registerApi('hello:say', (message: string) => {
|
|
97
|
+
console.log(`[HelloWorld] ${message}`);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
ctx.subscribe<{ path: string }>('explorer:file-selected', (payload) => {
|
|
101
|
+
console.log('Выбран файл:', payload.path);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async onunload(): Promise<void> {
|
|
106
|
+
console.log('Плагин HelloWorld выгружен');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Шаг 3: Соберите и установите
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm run build
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Скопируйте в ваше хранилище:
|
|
118
|
+
```
|
|
119
|
+
MyVault/.notehub/plugins/hello-world/
|
|
120
|
+
├── manifest.json
|
|
121
|
+
└── main.js
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Горячая перезагрузка
|
|
125
|
+
|
|
126
|
+
Notehub следит за директорией `.notehub/plugins/`. При обновлении плагина:
|
|
127
|
+
|
|
128
|
+
1. Старая версия автоматически выгружается
|
|
129
|
+
2. Новая версия загружается
|
|
130
|
+
3. Все регистрации API очищаются автоматически!
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
# Архитектура плагинов
|
|
135
|
+
|
|
136
|
+
## Микроядерная архитектура
|
|
137
|
+
|
|
138
|
+
Notehub.md следует **микроядерному** дизайну, где ядро минимально, а вся функциональность приходит от плагинов:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
142
|
+
│ NotehubCore │
|
|
143
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
|
144
|
+
│ │ EventBus │ │ ApiBus │ │ Plugin Registry │ │
|
|
145
|
+
│ │ (pub/sub) │ │(RPC вызовы) │ │ (управление ЖЦ) │ │
|
|
146
|
+
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
|
147
|
+
└─────────────────────────────────────────────────────────────┘
|
|
148
|
+
▲ ▲ ▲
|
|
149
|
+
│ │ │
|
|
150
|
+
┌────┴───┐ ┌─────┴────┐ ┌─────┴────┐
|
|
151
|
+
│ Logger │ │ Editor │ │ Explorer │
|
|
152
|
+
│ Plugin │ │ Plugin │ │ Plugin │
|
|
153
|
+
└────────┘ └──────────┘ └──────────┘
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## PluginContext
|
|
157
|
+
|
|
158
|
+
Ваш шлюз в экосистему Notehub:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
interface PluginContext {
|
|
162
|
+
registerApi(name: string, handler: (...args: unknown[]) => unknown): void;
|
|
163
|
+
invokeApi<T>(name: string, ...args: unknown[]): Promise<T>;
|
|
164
|
+
subscribe<T>(event: string, handler: (payload: T) => void): void;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Магия автоматической очистки
|
|
169
|
+
|
|
170
|
+
Когда ваш плагин выгружается, `PluginContext` автоматически:
|
|
171
|
+
|
|
172
|
+
- ✅ Отменяет регистрацию всех ваших API
|
|
173
|
+
- ✅ Отписывается от всех событий
|
|
174
|
+
- ✅ Удаляет виджеты редактора
|
|
175
|
+
- ✅ Удаляет вкладки/группы/элементы настроек
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
# Справочник API
|
|
180
|
+
|
|
181
|
+
## Logger API
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
await ctx.invokeApi('logger:info', 'MyPlugin', 'Операция завершена');
|
|
185
|
+
await ctx.invokeApi('logger:warn', 'MyPlugin', 'Конфиг не найден');
|
|
186
|
+
await ctx.invokeApi('logger:error', 'MyPlugin', 'Ошибка загрузки');
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Config API (Персистентный)
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// Получить значение
|
|
193
|
+
const fontSize = await ctx.invokeApi<number>('config:get', 'editor.font-size', 14);
|
|
194
|
+
|
|
195
|
+
// Установить значение (автосохранение)
|
|
196
|
+
await ctx.invokeApi('config:set', 'my-plugin.option', true);
|
|
197
|
+
|
|
198
|
+
// Удалить
|
|
199
|
+
await ctx.invokeApi('config:delete', 'my-plugin.option');
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## State API (Runtime)
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
await ctx.invokeApi('state:set', 'my-plugin.cache', { data: [...] });
|
|
206
|
+
const cache = await ctx.invokeApi<MyData>('state:get', 'my-plugin.cache');
|
|
207
|
+
await ctx.invokeApi('state:delete', 'my-plugin.cache');
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Filesystem API
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// Чтение файла
|
|
214
|
+
const content = await ctx.invokeApi<string>('fs:read-text-file', '/path/to/file.md');
|
|
215
|
+
|
|
216
|
+
// Запись файла
|
|
217
|
+
await ctx.invokeApi('fs:write-text-file', '/path/to/file.md', '# Привет мир');
|
|
218
|
+
|
|
219
|
+
// Проверка существования
|
|
220
|
+
const exists = await ctx.invokeApi<boolean>('fs:exists', '/path/to/file.md');
|
|
221
|
+
|
|
222
|
+
// Чтение директории
|
|
223
|
+
const entries = await ctx.invokeApi<DirEntry[]>('fs:read-dir', '/path/to/folder');
|
|
224
|
+
|
|
225
|
+
// Создание директории
|
|
226
|
+
await ctx.invokeApi('fs:create-dir', '/path/to/new-folder', { recursive: true });
|
|
227
|
+
|
|
228
|
+
// Удаление файла
|
|
229
|
+
await ctx.invokeApi('fs:remove-file', '/path/to/file.md');
|
|
230
|
+
|
|
231
|
+
// Переименование/перемещение
|
|
232
|
+
await ctx.invokeApi('fs:rename', '/old/path.md', '/new/path.md');
|
|
233
|
+
|
|
234
|
+
// Наблюдение за изменениями
|
|
235
|
+
const unwatch = await ctx.invokeApi<() => void>(
|
|
236
|
+
'fs:watch',
|
|
237
|
+
'/path/to/folder',
|
|
238
|
+
(event) => console.log('Изменение:', event)
|
|
239
|
+
);
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Dialog API
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
// Сообщение
|
|
246
|
+
await ctx.invokeApi('dialog:alert', 'Внимание', 'Файл был удален');
|
|
247
|
+
|
|
248
|
+
// Подтверждение
|
|
249
|
+
const confirmed = await ctx.invokeApi<boolean>(
|
|
250
|
+
'dialog:confirm', 'Удаление файла', 'Вы уверены?'
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// Ввод
|
|
254
|
+
const name = await ctx.invokeApi<string | null>(
|
|
255
|
+
'dialog:prompt', 'Переименование', 'Введите новое имя:', 'default.md'
|
|
256
|
+
);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Theme API
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// Регистрация темы
|
|
263
|
+
await ctx.invokeApi('theme:register', 'my-theme', {
|
|
264
|
+
'bg-main': '#1a1a2e',
|
|
265
|
+
'accent-primary': '#e94560',
|
|
266
|
+
// ...
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Применение темы
|
|
270
|
+
await ctx.invokeApi('theme:set', 'my-theme');
|
|
271
|
+
|
|
272
|
+
// Получить текущую
|
|
273
|
+
const theme = await ctx.invokeApi<string>('theme:get-current');
|
|
274
|
+
|
|
275
|
+
// Список всех
|
|
276
|
+
const themes = await ctx.invokeApi<string[]>('theme:list');
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Editor Widget API
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
// Регистрация виджета
|
|
283
|
+
await ctx.invokeApi(
|
|
284
|
+
'editor:register-widget',
|
|
285
|
+
'my-plugin:progress-bar',
|
|
286
|
+
/\[progress:(\d+)\]/g,
|
|
287
|
+
ProgressBarComponent
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// Отмена регистрации (опционально - очищается автоматически)
|
|
291
|
+
await ctx.invokeApi('editor:unregister-widget', 'my-plugin:progress-bar');
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Settings API
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
// Регистрация вкладки
|
|
298
|
+
await ctx.invokeApi('settings:register-tab', {
|
|
299
|
+
id: 'my-plugin', label: 'Мой плагин', icon: 'puzzle', order: 100
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Регистрация группы
|
|
303
|
+
await ctx.invokeApi('settings:register-group', {
|
|
304
|
+
id: 'my-group', tabId: 'my-plugin', label: 'Основные', order: 0
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Регистрация элемента
|
|
308
|
+
await ctx.invokeApi('settings:register-item', {
|
|
309
|
+
key: 'my-plugin.enabled',
|
|
310
|
+
type: 'toggle', // 'toggle' | 'text' | 'number' | 'select' | 'color'
|
|
311
|
+
label: 'Включить плагин',
|
|
312
|
+
groupId: 'my-group',
|
|
313
|
+
order: 0,
|
|
314
|
+
defaultValue: true
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Открыть/закрыть настройки
|
|
318
|
+
await ctx.invokeApi('settings:open');
|
|
319
|
+
await ctx.invokeApi('settings:close');
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Context Menu API
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
await ctx.invokeApi(
|
|
326
|
+
'context-menu:register',
|
|
327
|
+
'explorer-item',
|
|
328
|
+
(payload: { path: string }) => [
|
|
329
|
+
{
|
|
330
|
+
type: 'action',
|
|
331
|
+
id: 'my-action',
|
|
332
|
+
label: 'Моё действие',
|
|
333
|
+
icon: 'star',
|
|
334
|
+
onClick: () => console.log('Клик:', payload.path)
|
|
335
|
+
},
|
|
336
|
+
{ type: 'separator' },
|
|
337
|
+
{
|
|
338
|
+
type: 'submenu',
|
|
339
|
+
label: 'Ещё опции',
|
|
340
|
+
items: [/* ... */]
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
);
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Shell API
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
await ctx.invokeApi('shell:open', 'https://notehub.md');
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Vault API
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
await ctx.invokeApi('vault:close');
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
# Виджеты (Порталы)
|
|
361
|
+
|
|
362
|
+
Порталы — это кастомные React-компоненты, которые рендерятся inline в редакторе.
|
|
363
|
+
|
|
364
|
+
## Как работают порталы
|
|
365
|
+
|
|
366
|
+
1. Вы определяете **regex-паттерн**, который ищет текст в документе
|
|
367
|
+
2. Вы предоставляете **React-компонент** для рендеринга совпадений
|
|
368
|
+
3. Notehub заменяет найденный текст на ваш компонент в **режиме просмотра**
|
|
369
|
+
4. Когда курсор входит в совпадение, переключается **режим редактирования**
|
|
370
|
+
|
|
371
|
+
## Полный пример: Прогресс-бар
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import { NotehubPlugin, PluginContext } from '@notehub/api';
|
|
375
|
+
import React from 'react';
|
|
376
|
+
|
|
377
|
+
const ProgressBar: React.FC<{ match: RegExpExecArray }> = ({ match }) => {
|
|
378
|
+
const percentage = parseInt(match[1], 10);
|
|
379
|
+
|
|
380
|
+
return (
|
|
381
|
+
<span style={{
|
|
382
|
+
display: 'inline-flex',
|
|
383
|
+
alignItems: 'center',
|
|
384
|
+
gap: '8px',
|
|
385
|
+
padding: '2px 8px',
|
|
386
|
+
background: 'var(--nh-bg-surface)',
|
|
387
|
+
borderRadius: '4px',
|
|
388
|
+
}}>
|
|
389
|
+
<span style={{
|
|
390
|
+
width: '100px',
|
|
391
|
+
height: '8px',
|
|
392
|
+
background: 'var(--nh-bg-secondary)',
|
|
393
|
+
borderRadius: '4px',
|
|
394
|
+
overflow: 'hidden',
|
|
395
|
+
}}>
|
|
396
|
+
<span style={{
|
|
397
|
+
width: `${percentage}%`,
|
|
398
|
+
height: '100%',
|
|
399
|
+
background: 'var(--nh-accent-primary)',
|
|
400
|
+
display: 'block',
|
|
401
|
+
}} />
|
|
402
|
+
</span>
|
|
403
|
+
<span style={{ fontSize: '12px', color: 'var(--nh-text-muted)' }}>
|
|
404
|
+
{percentage}%
|
|
405
|
+
</span>
|
|
406
|
+
</span>
|
|
407
|
+
);
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
export default class ProgressBarPlugin extends NotehubPlugin {
|
|
411
|
+
async onload(ctx: PluginContext): Promise<void> {
|
|
412
|
+
await ctx.invokeApi(
|
|
413
|
+
'editor:register-widget',
|
|
414
|
+
'progress-bar',
|
|
415
|
+
/\[progress:(\d+)\]/g,
|
|
416
|
+
ProgressBar
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async onunload(): Promise<void> {}
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Использование в документах:**
|
|
425
|
+
```markdown
|
|
426
|
+
Прогресс проекта: [progress:75]
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
# Интеграция настроек
|
|
432
|
+
|
|
433
|
+
## Структура
|
|
434
|
+
|
|
435
|
+
```
|
|
436
|
+
Модальное окно настроек
|
|
437
|
+
└── Вкладка (например, "Мой плагин")
|
|
438
|
+
└── Группа (например, "Внешний вид")
|
|
439
|
+
└── Элемент (например, "Включить тёмный режим")
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## Типы элементов
|
|
443
|
+
|
|
444
|
+
| Тип | Описание |
|
|
445
|
+
|-----|----------|
|
|
446
|
+
| `toggle` | Булевый переключатель |
|
|
447
|
+
| `text` | Текстовый ввод |
|
|
448
|
+
| `number` | Числовой ввод с min/max/step |
|
|
449
|
+
| `select` | Выпадающий список с опциями |
|
|
450
|
+
| `color` | Выбор цвета |
|
|
451
|
+
|
|
452
|
+
## Чтение настроек
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
const isEnabled = await ctx.invokeApi<boolean>('config:get', 'my-plugin.enabled', true);
|
|
456
|
+
const maxItems = await ctx.invokeApi<number>('config:get', 'my-plugin.max-items', 10);
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
# Контекстное меню
|
|
462
|
+
|
|
463
|
+
## Типы элементов меню
|
|
464
|
+
|
|
465
|
+
### Action (Действие)
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
{
|
|
469
|
+
type: 'action',
|
|
470
|
+
id: 'my-action',
|
|
471
|
+
label: 'Моё действие',
|
|
472
|
+
icon: 'star',
|
|
473
|
+
onClick: (payload) => { /* обработка */ }
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Separator (Разделитель)
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
{ type: 'separator' }
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Submenu (Подменю)
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
{
|
|
487
|
+
type: 'submenu',
|
|
488
|
+
label: 'Ещё опции',
|
|
489
|
+
items: [/* вложенные элементы */]
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
# Примеры плагинов
|
|
496
|
+
|
|
497
|
+
## Hello World
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
import { NotehubPlugin, PluginContext } from '@notehub/api';
|
|
501
|
+
|
|
502
|
+
export default class HelloWorld extends NotehubPlugin {
|
|
503
|
+
async onload(ctx: PluginContext): Promise<void> {
|
|
504
|
+
await ctx.invokeApi('logger:info', 'HelloWorld', '👋 Привет!');
|
|
505
|
+
|
|
506
|
+
ctx.registerApi('hello:greet', (name: string) => {
|
|
507
|
+
return `Привет, ${name}!`;
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async onunload(): Promise<void> {}
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
## Виджет подсчёта слов
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
const WordCounter: React.FC<{ match: RegExpExecArray }> = ({ match }) => {
|
|
519
|
+
const text = match[1];
|
|
520
|
+
const words = text.trim().split(/\s+/).filter(w => w.length > 0).length;
|
|
521
|
+
|
|
522
|
+
return <span>📝 {words} слов</span>;
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// Регистрация с паттерном: {{count: ваш текст здесь}}
|
|
526
|
+
await ctx.invokeApi(
|
|
527
|
+
'editor:register-widget',
|
|
528
|
+
'word-counter',
|
|
529
|
+
/\{\{count:\s*(.+?)\}\}/g,
|
|
530
|
+
WordCounter
|
|
531
|
+
);
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
## Полноценный плагин
|
|
535
|
+
|
|
536
|
+
Смотрите [документацию с примерами](https://github.com/khton-tech/notehub.md/tree/main/docs/forPluginMakers/ru/07-examples.md) для полного кода плагина, комбинирующего виджеты, настройки и контекстные меню.
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## Конфигурация сборки
|
|
541
|
+
|
|
542
|
+
### package.json
|
|
543
|
+
|
|
544
|
+
```json
|
|
545
|
+
{
|
|
546
|
+
"scripts": {
|
|
547
|
+
"build": "nhp build"
|
|
548
|
+
},
|
|
549
|
+
"devDependencies": {
|
|
550
|
+
"@notehub/api": "workspace:*",
|
|
551
|
+
"@types/react": "^18.3.0",
|
|
552
|
+
"typescript": "^5.6.0"
|
|
553
|
+
},
|
|
554
|
+
"peerDependencies": {
|
|
555
|
+
"react": "^18.3.0"
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Внешние зависимости
|
|
561
|
+
|
|
562
|
+
Эти пакеты предоставляются Notehub — отметьте их как **external**:
|
|
563
|
+
|
|
564
|
+
- `@notehub/api`
|
|
565
|
+
- `react`
|
|
566
|
+
- `react-dom`
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## Советы по отладке
|
|
571
|
+
|
|
572
|
+
1. **Откройте DevTools** (Ctrl+Shift+I) чтобы видеть логи консоли
|
|
573
|
+
2. **Используйте `logger:info`** API для структурированного логирования
|
|
574
|
+
3. **Проверьте логи плагина Synapse** для событий загрузки/выгрузки
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## CSS-переменные
|
|
579
|
+
|
|
580
|
+
Используйте для консистентной стилизации:
|
|
581
|
+
|
|
582
|
+
- `--nh-bg-main`, `--nh-bg-sidebar`, `--nh-bg-surface`
|
|
583
|
+
- `--nh-text-primary`, `--nh-text-secondary`, `--nh-text-muted`
|
|
584
|
+
- `--nh-accent-primary`, `--nh-accent-secondary`
|
|
585
|
+
- `--nh-border-accent`, `--nh-border-subtle`
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## Ресурсы
|
|
590
|
+
|
|
591
|
+
- [Репозиторий GitHub](https://github.com/khton-tech/notehub.md)
|
|
592
|
+
- [Пакет API](https://github.com/khton-tech/notehub.md/tree/main/packages/api)
|
|
593
|
+
- [Примеры плагинов](https://github.com/khton-tech/notehub.md/tree/main/packages/plugins)
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
<p align="center">
|
|
598
|
+
<strong>Удачной разработки! 🎉</strong>
|
|
599
|
+
</p>
|