@notehub.md/cli 0.1.8 → 0.1.10
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/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +1 -3
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +13 -1
- package/dist/commands/create.js.map +1 -1
- package/package.json +2 -2
- package/templates/docs/en/01-getting-started.md +207 -0
- package/templates/docs/en/02-architecture.md +228 -0
- package/templates/docs/en/03-api-reference.md +747 -0
- package/templates/docs/en/04-widgets.md +322 -0
- package/templates/docs/en/05-settings.md +303 -0
- package/templates/docs/en/06-context-menu.md +283 -0
- package/templates/docs/en/07-examples.md +547 -0
- package/templates/docs/en/README.md +125 -0
- package/templates/docs/ru/01-getting-started.md +207 -0
- package/templates/docs/ru/02-architecture.md +228 -0
- package/templates/docs/ru/03-api-reference.md +747 -0
- package/templates/docs/ru/04-widgets.md +293 -0
- package/templates/docs/ru/05-settings.md +303 -0
- package/templates/docs/ru/06-context-menu.md +283 -0
- package/templates/docs/ru/07-examples.md +547 -0
- package/templates/docs/ru/README.md +125 -0
- package/templates/docs.html +6 -4
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
# Примеры плагинов
|
|
2
|
+
|
|
3
|
+
Полные, рабочие примеры плагинов, которые можно использовать как шаблоны.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Пример 1: Hello World (Минимальный)
|
|
8
|
+
|
|
9
|
+
Простейший возможный плагин.
|
|
10
|
+
|
|
11
|
+
### `manifest.json`
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"id": "hello-world",
|
|
16
|
+
"name": "Hello World",
|
|
17
|
+
"version": "1.0.0"
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### `src/index.ts`
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { NotehubPlugin, PluginContext } from '@notehub/api';
|
|
25
|
+
|
|
26
|
+
export default class HelloWorld extends NotehubPlugin {
|
|
27
|
+
async onload(ctx: PluginContext): Promise<void> {
|
|
28
|
+
await ctx.invokeApi('logger:info', 'HelloWorld', '👋 Привет из моего первого плагина!');
|
|
29
|
+
|
|
30
|
+
// Регистрация простого API
|
|
31
|
+
ctx.registerApi('hello:greet', (name: string) => {
|
|
32
|
+
return `Привет, ${name}!`;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async onunload(): Promise<void> {
|
|
37
|
+
console.log('До свидания!');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Пример 2: Виджет счетчика слов
|
|
45
|
+
|
|
46
|
+
Виджет, считающий слова в текущем параграфе.
|
|
47
|
+
|
|
48
|
+
### `manifest.json`
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"id": "word-counter",
|
|
53
|
+
"name": "Счетчик слов",
|
|
54
|
+
"version": "1.0.0"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### `src/index.ts`
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { NotehubPlugin, PluginContext } from '@notehub/api';
|
|
62
|
+
import React from 'react';
|
|
63
|
+
|
|
64
|
+
// Компонент виджета
|
|
65
|
+
const WordCounter: React.FC<{ match: RegExpExecArray }> = ({ match }) => {
|
|
66
|
+
const text = match[1];
|
|
67
|
+
const words = text.trim().split(/\s+/).filter(w => w.length > 0).length;
|
|
68
|
+
const chars = text.length;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<span style={{
|
|
72
|
+
display: 'inline-flex',
|
|
73
|
+
gap: '12px',
|
|
74
|
+
padding: '4px 12px',
|
|
75
|
+
background: 'var(--nh-bg-surface)',
|
|
76
|
+
borderRadius: '4px',
|
|
77
|
+
fontSize: '12px',
|
|
78
|
+
color: 'var(--nh-text-muted)',
|
|
79
|
+
border: '1px solid var(--nh-border-subtle)',
|
|
80
|
+
}}>
|
|
81
|
+
<span>📝 {words} слов</span>
|
|
82
|
+
<span>📏 {chars} символов</span>
|
|
83
|
+
</span>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default class WordCounterPlugin extends NotehubPlugin {
|
|
88
|
+
async onload(ctx: PluginContext): Promise<void> {
|
|
89
|
+
// Совпадение: {{count: любой текст здесь}}
|
|
90
|
+
await ctx.invokeApi(
|
|
91
|
+
'editor:register-widget',
|
|
92
|
+
'word-counter',
|
|
93
|
+
/\{\{count:\s*(.+?)\}\}/g,
|
|
94
|
+
WordCounter
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
await ctx.invokeApi('logger:info', 'WordCounter', 'Виджет зарегистрирован');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async onunload(): Promise<void> {}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Использование:**
|
|
105
|
+
```markdown
|
|
106
|
+
{{count: Это пример текста ровно с десятью словами всего}}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Пример 3: Плагин с настройками
|
|
112
|
+
|
|
113
|
+
Плагин с настраиваемыми параметрами.
|
|
114
|
+
|
|
115
|
+
### `manifest.json`
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"id": "settings-demo",
|
|
120
|
+
"name": "Демо настроек",
|
|
121
|
+
"version": "1.0.0"
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `src/index.ts`
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { NotehubPlugin, PluginContext } from '@notehub/api';
|
|
129
|
+
|
|
130
|
+
export default class SettingsDemo extends NotehubPlugin {
|
|
131
|
+
async onload(ctx: PluginContext): Promise<void> {
|
|
132
|
+
// Регистрация вкладки
|
|
133
|
+
await ctx.invokeApi('settings:register-tab', {
|
|
134
|
+
id: 'settings-demo',
|
|
135
|
+
label: 'Демо настроек',
|
|
136
|
+
icon: 'sliders',
|
|
137
|
+
order: 100
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Регистрация группы
|
|
141
|
+
await ctx.invokeApi('settings:register-group', {
|
|
142
|
+
id: 'demo-general',
|
|
143
|
+
tabId: 'settings-demo',
|
|
144
|
+
label: 'Основные опции',
|
|
145
|
+
order: 0
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Регистрация элементов
|
|
149
|
+
await ctx.invokeApi('settings:register-items', [
|
|
150
|
+
{
|
|
151
|
+
key: 'settings-demo.enabled',
|
|
152
|
+
type: 'toggle',
|
|
153
|
+
label: 'Включить функцию',
|
|
154
|
+
description: 'Включить или выключить демо-функцию',
|
|
155
|
+
groupId: 'demo-general',
|
|
156
|
+
order: 0,
|
|
157
|
+
defaultValue: true
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
key: 'settings-demo.name',
|
|
161
|
+
type: 'text',
|
|
162
|
+
label: 'Ваше имя',
|
|
163
|
+
placeholder: 'Введите ваше имя...',
|
|
164
|
+
groupId: 'demo-general',
|
|
165
|
+
order: 1,
|
|
166
|
+
defaultValue: ''
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
key: 'settings-demo.count',
|
|
170
|
+
type: 'number',
|
|
171
|
+
label: 'Количество элементов',
|
|
172
|
+
min: 1,
|
|
173
|
+
max: 100,
|
|
174
|
+
step: 1,
|
|
175
|
+
groupId: 'demo-general',
|
|
176
|
+
order: 2,
|
|
177
|
+
defaultValue: 10
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
key: 'settings-demo.mode',
|
|
181
|
+
type: 'select',
|
|
182
|
+
label: 'Режим отображения',
|
|
183
|
+
options: [
|
|
184
|
+
{ label: 'Компактный', value: 'compact' },
|
|
185
|
+
{ label: 'Обычный', value: 'normal' },
|
|
186
|
+
{ label: 'Расширенный', value: 'expanded' }
|
|
187
|
+
],
|
|
188
|
+
groupId: 'demo-general',
|
|
189
|
+
order: 3,
|
|
190
|
+
defaultValue: 'normal'
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
key: 'settings-demo.color',
|
|
194
|
+
type: 'color',
|
|
195
|
+
label: 'Цвет подсветки',
|
|
196
|
+
groupId: 'demo-general',
|
|
197
|
+
order: 4,
|
|
198
|
+
defaultValue: '#3b82f6'
|
|
199
|
+
}
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
// Чтение и использование настроек
|
|
203
|
+
const enabled = await ctx.invokeApi<boolean>('config:get', 'settings-demo.enabled', true);
|
|
204
|
+
const name = await ctx.invokeApi<string>('config:get', 'settings-demo.name', 'Пользователь');
|
|
205
|
+
|
|
206
|
+
await ctx.invokeApi('logger:info', 'SettingsDemo',
|
|
207
|
+
`Загружено! enabled=${enabled}, name="${name}"`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async onunload(): Promise<void> {}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Пример 4: Расширение контекстного меню
|
|
217
|
+
|
|
218
|
+
Добавление кастомных действий в контекстное меню проводника.
|
|
219
|
+
|
|
220
|
+
### `manifest.json`
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"id": "quick-actions",
|
|
225
|
+
"name": "Быстрые действия",
|
|
226
|
+
"version": "1.0.0"
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### `src/index.ts`
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { NotehubPlugin, PluginContext } from '@notehub/api';
|
|
234
|
+
|
|
235
|
+
interface FilePayload {
|
|
236
|
+
path: string;
|
|
237
|
+
isDirectory: boolean;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export default class QuickActionsPlugin extends NotehubPlugin {
|
|
241
|
+
private ctx!: PluginContext;
|
|
242
|
+
|
|
243
|
+
async onload(ctx: PluginContext): Promise<void> {
|
|
244
|
+
this.ctx = ctx;
|
|
245
|
+
|
|
246
|
+
await ctx.invokeApi(
|
|
247
|
+
'context-menu:register',
|
|
248
|
+
'explorer-item',
|
|
249
|
+
(payload: FilePayload) => this.buildMenu(payload)
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
await ctx.invokeApi('logger:info', 'QuickActions', 'Контекстное меню зарегистрировано');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private buildMenu(payload: FilePayload) {
|
|
256
|
+
const items = [];
|
|
257
|
+
|
|
258
|
+
// Только для markdown-файлов
|
|
259
|
+
if (payload.path.endsWith('.md')) {
|
|
260
|
+
items.push({
|
|
261
|
+
type: 'action' as const,
|
|
262
|
+
id: 'qa-word-count',
|
|
263
|
+
label: 'Подсчитать слова',
|
|
264
|
+
icon: 'hash',
|
|
265
|
+
onClick: () => this.countWords(payload.path)
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
items.push({
|
|
269
|
+
type: 'action' as const,
|
|
270
|
+
id: 'qa-add-date',
|
|
271
|
+
label: 'Добавить сегодняшнюю дату',
|
|
272
|
+
icon: 'calendar',
|
|
273
|
+
onClick: () => this.addDate(payload.path)
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Для всех файлов
|
|
278
|
+
items.push({ type: 'separator' as const });
|
|
279
|
+
|
|
280
|
+
items.push({
|
|
281
|
+
type: 'action' as const,
|
|
282
|
+
id: 'qa-copy-path',
|
|
283
|
+
label: 'Скопировать путь',
|
|
284
|
+
icon: 'copy',
|
|
285
|
+
onClick: () => navigator.clipboard.writeText(payload.path)
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Дублирование
|
|
289
|
+
if (!payload.isDirectory) {
|
|
290
|
+
items.push({
|
|
291
|
+
type: 'action' as const,
|
|
292
|
+
id: 'qa-duplicate',
|
|
293
|
+
label: 'Дублировать файл',
|
|
294
|
+
icon: 'files',
|
|
295
|
+
onClick: () => this.duplicateFile(payload.path)
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return items;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private async countWords(path: string) {
|
|
303
|
+
const content = await this.ctx.invokeApi<string>('fs:read-text-file', path);
|
|
304
|
+
const words = content.split(/\s+/).filter(w => w.length > 0).length;
|
|
305
|
+
await this.ctx.invokeApi('dialog:alert', 'Количество слов', `${words} слов в этом файле`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private async addDate(path: string) {
|
|
309
|
+
const content = await this.ctx.invokeApi<string>('fs:read-text-file', path);
|
|
310
|
+
const date = new Date().toISOString().split('T')[0];
|
|
311
|
+
const newContent = `---\ndate: ${date}\n---\n\n${content}`;
|
|
312
|
+
await this.ctx.invokeApi('fs:write-text-file', path, newContent);
|
|
313
|
+
await this.ctx.invokeApi('dialog:alert', 'Успех', 'Дата добавлена в файл');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private async duplicateFile(path: string) {
|
|
317
|
+
const content = await this.ctx.invokeApi<string>('fs:read-text-file', path);
|
|
318
|
+
const newPath = path.replace(/\.md$/, ' (копия).md');
|
|
319
|
+
await this.ctx.invokeApi('fs:write-text-file', newPath, content);
|
|
320
|
+
await this.ctx.invokeApi('dialog:alert', 'Успех', `Создано: ${newPath}`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async onunload(): Promise<void> {}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Пример 5: Полнофункциональный плагин
|
|
330
|
+
|
|
331
|
+
Комплексный плагин, объединяющий виджеты, настройки и контекстные меню.
|
|
332
|
+
|
|
333
|
+
### `manifest.json`
|
|
334
|
+
|
|
335
|
+
```json
|
|
336
|
+
{
|
|
337
|
+
"id": "task-tracker",
|
|
338
|
+
"name": "Трекер задач",
|
|
339
|
+
"version": "1.0.0"
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### `src/index.ts`
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { NotehubPlugin, PluginContext } from '@notehub/api';
|
|
347
|
+
import React, { useState } from 'react';
|
|
348
|
+
|
|
349
|
+
// === Виджет: Интерактивный чекбокс задачи ===
|
|
350
|
+
const TaskWidget: React.FC<{ match: RegExpExecArray }> = ({ match }) => {
|
|
351
|
+
const status = match[1]; // 'x' или ' '
|
|
352
|
+
const text = match[2];
|
|
353
|
+
const [checked, setChecked] = useState(status === 'x');
|
|
354
|
+
|
|
355
|
+
return (
|
|
356
|
+
<span
|
|
357
|
+
onClick={() => setChecked(!checked)}
|
|
358
|
+
style={{
|
|
359
|
+
display: 'inline-flex',
|
|
360
|
+
alignItems: 'center',
|
|
361
|
+
gap: '8px',
|
|
362
|
+
padding: '4px 8px',
|
|
363
|
+
background: checked ? 'var(--nh-accent-primary)20' : 'var(--nh-bg-surface)',
|
|
364
|
+
borderRadius: '4px',
|
|
365
|
+
cursor: 'pointer',
|
|
366
|
+
transition: 'all 0.2s',
|
|
367
|
+
}}
|
|
368
|
+
>
|
|
369
|
+
<span style={{
|
|
370
|
+
width: '18px',
|
|
371
|
+
height: '18px',
|
|
372
|
+
borderRadius: '4px',
|
|
373
|
+
border: `2px solid ${checked ? 'var(--nh-accent-primary)' : 'var(--nh-border-subtle)'}`,
|
|
374
|
+
background: checked ? 'var(--nh-accent-primary)' : 'transparent',
|
|
375
|
+
display: 'flex',
|
|
376
|
+
alignItems: 'center',
|
|
377
|
+
justifyContent: 'center',
|
|
378
|
+
color: '#fff',
|
|
379
|
+
fontSize: '12px',
|
|
380
|
+
}}>
|
|
381
|
+
{checked && '✓'}
|
|
382
|
+
</span>
|
|
383
|
+
<span style={{
|
|
384
|
+
textDecoration: checked ? 'line-through' : 'none',
|
|
385
|
+
color: checked ? 'var(--nh-text-muted)' : 'var(--nh-text-primary)',
|
|
386
|
+
}}>
|
|
387
|
+
{text}
|
|
388
|
+
</span>
|
|
389
|
+
</span>
|
|
390
|
+
);
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
// === Класс плагина ===
|
|
394
|
+
export default class TaskTracker extends NotehubPlugin {
|
|
395
|
+
private ctx!: PluginContext;
|
|
396
|
+
|
|
397
|
+
async onload(ctx: PluginContext): Promise<void> {
|
|
398
|
+
this.ctx = ctx;
|
|
399
|
+
|
|
400
|
+
// 1. Регистрация виджета
|
|
401
|
+
await ctx.invokeApi(
|
|
402
|
+
'editor:register-widget',
|
|
403
|
+
'task-tracker:checkbox',
|
|
404
|
+
/\[([x ])\]\s+(.+?)(?=\n|$)/g,
|
|
405
|
+
TaskWidget
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
// 2. Регистрация настроек
|
|
409
|
+
await ctx.invokeApi('settings:register-tab', {
|
|
410
|
+
id: 'task-tracker',
|
|
411
|
+
label: 'Трекер задач',
|
|
412
|
+
icon: 'check-square',
|
|
413
|
+
order: 50
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
await ctx.invokeApi('settings:register-group', {
|
|
417
|
+
id: 'task-tracker-options',
|
|
418
|
+
tabId: 'task-tracker',
|
|
419
|
+
label: 'Опции',
|
|
420
|
+
order: 0
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
await ctx.invokeApi('settings:register-items', [
|
|
424
|
+
{
|
|
425
|
+
key: 'task-tracker.style',
|
|
426
|
+
type: 'select',
|
|
427
|
+
label: 'Стиль чекбокса',
|
|
428
|
+
options: [
|
|
429
|
+
{ label: 'Круглый', value: 'round' },
|
|
430
|
+
{ label: 'Квадратный', value: 'square' }
|
|
431
|
+
],
|
|
432
|
+
groupId: 'task-tracker-options',
|
|
433
|
+
order: 0,
|
|
434
|
+
defaultValue: 'square'
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
key: 'task-tracker.strikethrough',
|
|
438
|
+
type: 'toggle',
|
|
439
|
+
label: 'Зачеркивать выполненные',
|
|
440
|
+
groupId: 'task-tracker-options',
|
|
441
|
+
order: 1,
|
|
442
|
+
defaultValue: true
|
|
443
|
+
}
|
|
444
|
+
]);
|
|
445
|
+
|
|
446
|
+
// 3. Регистрация контекстного меню
|
|
447
|
+
await ctx.invokeApi(
|
|
448
|
+
'context-menu:register',
|
|
449
|
+
'explorer-item',
|
|
450
|
+
(payload: { path: string }) => {
|
|
451
|
+
if (!payload.path.endsWith('.md')) return [];
|
|
452
|
+
|
|
453
|
+
return [
|
|
454
|
+
{
|
|
455
|
+
type: 'action' as const,
|
|
456
|
+
id: 'tt-count-tasks',
|
|
457
|
+
label: 'Подсчитать задачи',
|
|
458
|
+
icon: 'list-checks',
|
|
459
|
+
onClick: () => this.countTasks(payload.path)
|
|
460
|
+
}
|
|
461
|
+
];
|
|
462
|
+
}
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
await ctx.invokeApi('logger:info', 'TaskTracker', 'Плагин успешно загружен!');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private async countTasks(path: string) {
|
|
469
|
+
const content = await this.ctx.invokeApi<string>('fs:read-text-file', path);
|
|
470
|
+
const total = (content.match(/\[[x ]\]/g) || []).length;
|
|
471
|
+
const done = (content.match(/\[x\]/g) || []).length;
|
|
472
|
+
|
|
473
|
+
await this.ctx.invokeApi(
|
|
474
|
+
'dialog:alert',
|
|
475
|
+
'Сводка по задачам',
|
|
476
|
+
`${done}/${total} задач выполнено (${Math.round(done/total*100)}%)`
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async onunload(): Promise<void> {
|
|
481
|
+
await this.ctx.invokeApi('logger:info', 'TaskTracker', 'Плагин выгружен');
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Использование в документах:**
|
|
487
|
+
```markdown
|
|
488
|
+
## Задачи на сегодня
|
|
489
|
+
|
|
490
|
+
[x] Проверить pull request
|
|
491
|
+
[ ] Написать документацию
|
|
492
|
+
[ ] Задеплоить на продакшн
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## Конфигурация сборки
|
|
498
|
+
|
|
499
|
+
### `package.json`
|
|
500
|
+
|
|
501
|
+
```json
|
|
502
|
+
{
|
|
503
|
+
"name": "my-plugin",
|
|
504
|
+
"version": "1.0.0",
|
|
505
|
+
"type": "module",
|
|
506
|
+
"scripts": {
|
|
507
|
+
"build": "esbuild src/index.ts --bundle --format=esm --outfile=main.js --external:@notehub/api --external:react --external:react-dom",
|
|
508
|
+
"watch": "npm run build -- --watch"
|
|
509
|
+
},
|
|
510
|
+
"devDependencies": {
|
|
511
|
+
"@notehub/api": "file:../path/to/notehub/packages/api",
|
|
512
|
+
"@types/react": "^18.2.0",
|
|
513
|
+
"esbuild": "^0.20.0",
|
|
514
|
+
"typescript": "^5.3.0"
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### `tsconfig.json`
|
|
520
|
+
|
|
521
|
+
```json
|
|
522
|
+
{
|
|
523
|
+
"compilerOptions": {
|
|
524
|
+
"target": "ES2020",
|
|
525
|
+
"module": "ESNext",
|
|
526
|
+
"moduleResolution": "bundler",
|
|
527
|
+
"lib": ["ES2020", "DOM"],
|
|
528
|
+
"jsx": "react-jsx",
|
|
529
|
+
"strict": true,
|
|
530
|
+
"esModuleInterop": true,
|
|
531
|
+
"skipLibCheck": true,
|
|
532
|
+
"declaration": false,
|
|
533
|
+
"outDir": "./dist"
|
|
534
|
+
},
|
|
535
|
+
"include": ["src/**/*"]
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## Советы по разработке
|
|
542
|
+
|
|
543
|
+
1. **Используйте `npm run watch`** для автоматической пересборки
|
|
544
|
+
2. **Notehub автоматически перезагружает** плагины при изменении файлов
|
|
545
|
+
3. **Откройте DevTools** (Ctrl+Shift+I) для вывода в консоль
|
|
546
|
+
4. **Используйте `logger:info`** для структурированного логирования
|
|
547
|
+
5. **Тестируйте инкрементально** — добавляйте функции по одной
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<h1 align="center">🔌 Руководство разработчика плагинов Notehub</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<em>Создавайте мощные плагины для Notehub.md — расширяемого приложения для заметок</em>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="#-быстрый-старт">Быстрый старт</a> •
|
|
9
|
+
<a href="#-документация">Документация</a> •
|
|
10
|
+
<a href="#-примеры">Примеры</a> •
|
|
11
|
+
<a href="#-справочник-api">Справочник API</a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 🚀 Быстрый старт
|
|
17
|
+
|
|
18
|
+
### Вариант 1: Генератор плагинов (Рекомендуется)
|
|
19
|
+
|
|
20
|
+
Для **внутренних плагинов** (часть монорепо):
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm gen:plugin
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Интерактивный CLI:
|
|
27
|
+
1. Запросит имя плагина (kebab-case, например `my-feature`)
|
|
28
|
+
2. Предложит выбрать категорию (`system`, `ui`, `features`)
|
|
29
|
+
3. Сгенерирует полную структуру плагина
|
|
30
|
+
|
|
31
|
+
**Результат:**
|
|
32
|
+
```
|
|
33
|
+
🔌 Notehub.md Plugin Generator
|
|
34
|
+
|
|
35
|
+
✔ Plugin name (kebab-case): word-counter
|
|
36
|
+
✔ Select plugin category: features - User-facing features
|
|
37
|
+
|
|
38
|
+
📦 Creating plugin: nh.features.word-counter
|
|
39
|
+
Path: packages/plugins/features/word-counter
|
|
40
|
+
|
|
41
|
+
✅ Created: package.json
|
|
42
|
+
✅ Created: tsconfig.json
|
|
43
|
+
✅ Created: manifest.json
|
|
44
|
+
✅ Created: src/index.ts
|
|
45
|
+
|
|
46
|
+
✨ Plugin created successfully!
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### Вариант 2: Ручная настройка (Внешние плагины)
|
|
52
|
+
|
|
53
|
+
Для плагинов, загружаемых в runtime из хранилища:
|
|
54
|
+
|
|
55
|
+
#### 1. Создайте папку плагина
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
mkdir my-plugin && cd my-plugin
|
|
59
|
+
npm init -y
|
|
60
|
+
npm install @notehub/api typescript esbuild --save-dev
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 📚 Документация
|
|
66
|
+
|
|
67
|
+
| Глава | Описание |
|
|
68
|
+
|-------|----------|
|
|
69
|
+
| [Начало работы](01-getting-started.md) | Требования, настройка, первый плагин |
|
|
70
|
+
| [Архитектура](02-architecture.md) | Жизненный цикл, EventBus, ApiBus |
|
|
71
|
+
| [Справочник API](03-api-reference.md) | Все 50+ методов API с примерами |
|
|
72
|
+
| [Виджеты](04-widgets.md) | Кастомные React-компоненты в заметках |
|
|
73
|
+
| [Настройки](05-settings.md) | Добавление UI конфигурации |
|
|
74
|
+
| [Контекстное меню](06-context-menu.md) | Интеграция правой кнопки мыши |
|
|
75
|
+
| [Примеры](07-examples.md) | Полные рабочие плагины |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 💡 Что могут делать плагины?
|
|
80
|
+
|
|
81
|
+
| Возможность | API |
|
|
82
|
+
|-------------|-----|
|
|
83
|
+
| 📁 Чтение/запись файлов | `fs:read-text-file`, `fs:write-text-file` |
|
|
84
|
+
| ⚙️ Сохранение настроек | `config:get`, `config:set` |
|
|
85
|
+
| 🎨 Регистрация тем | `theme:register`, `theme:set` |
|
|
86
|
+
| 🧩 Создание виджетов | `editor:register-widget` |
|
|
87
|
+
| 📋 Контекстные меню | `context-menu:register` |
|
|
88
|
+
| 💬 Показ диалогов | `dialog:alert`, `dialog:confirm` |
|
|
89
|
+
| 📡 Подписка на события | `ctx.subscribe()` |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 🎯 Примеры
|
|
94
|
+
|
|
95
|
+
### Hello World
|
|
96
|
+
```typescript
|
|
97
|
+
ctx.registerApi('hello:greet', (name: string) => `Привет, ${name}!`);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Виджет прогресс-бара
|
|
101
|
+
```typescript
|
|
102
|
+
await ctx.invokeApi('editor:register-widget', 'progress', /\[progress:(\d+)\]/g,
|
|
103
|
+
({ match }) => <ProgressBar value={parseInt(match[1])} />);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Наблюдатель за файлами
|
|
107
|
+
```typescript
|
|
108
|
+
ctx.subscribe<{ path: string }>('explorer:file-selected', (payload) => {
|
|
109
|
+
console.log('Выбрано:', payload.path);
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🔗 Ссылки
|
|
116
|
+
|
|
117
|
+
- [Главный README](../../README.md)
|
|
118
|
+
- [Пакет API](../../packages/api)
|
|
119
|
+
- [Примеры плагинов](../../packages/plugins)
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
<p align="center">
|
|
124
|
+
<strong>Удачной разработки! 🎉</strong>
|
|
125
|
+
</p>
|