@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.
@@ -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>