wasibase 1.0.0

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/src/ui/note.js ADDED
@@ -0,0 +1,449 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import open from 'open';
4
+ import * as storage from '../storage.js';
5
+ import { startServer, startEditServer } from '../web/server.js';
6
+ import { findAvailablePort } from '../utils.js';
7
+
8
+ function clear() {
9
+ console.clear();
10
+ console.log('');
11
+ console.log(chalk.bgBlue.white.bold(' WASIBASE NOTE '));
12
+ console.log('');
13
+ }
14
+
15
+ function formatChoice(name, isNew = false) {
16
+ if (isNew) {
17
+ return chalk.green(' + ') + chalk.green(name);
18
+ }
19
+ return chalk.cyan(' → ') + chalk.bold(name);
20
+ }
21
+
22
+ export async function noteMenu() {
23
+ clear();
24
+
25
+ const oberkategorien = storage.getOberkategorien();
26
+
27
+ // Wenn keine Kategorien, direkt neue Note erstellen
28
+ if (oberkategorien.length === 0) {
29
+ console.log(chalk.gray(' Noch keine Kategorien vorhanden.\n'));
30
+ return createNewNote();
31
+ }
32
+
33
+ // Oberkategorie waehlen
34
+ const choices = [
35
+ { name: chalk.green(' + ') + chalk.green.bold('Neue Note erstellen'), value: 'new' },
36
+ new inquirer.Separator(chalk.gray('─'.repeat(40))),
37
+ ...oberkategorien.map(o => {
38
+ const unters = storage.getUnterkategorien(o);
39
+ const noteCount = unters.reduce((sum, u) => sum + storage.getNotes(o, u).length, 0);
40
+ return {
41
+ name: chalk.cyan(' → ') + chalk.bold(o) + chalk.gray(` (${noteCount} Notes)`),
42
+ value: { type: 'ober', name: o },
43
+ short: o
44
+ };
45
+ }),
46
+ new inquirer.Separator(chalk.gray('─'.repeat(40))),
47
+ { name: chalk.red(' ✕ ') + chalk.gray('Abbrechen'), value: 'exit' }
48
+ ];
49
+
50
+ const { selection } = await inquirer.prompt([{
51
+ type: 'list',
52
+ name: 'selection',
53
+ message: chalk.blue('Oberkategorie'),
54
+ prefix: chalk.blue('◆'),
55
+ choices,
56
+ pageSize: 20,
57
+ loop: false
58
+ }]);
59
+
60
+ if (selection === 'exit') return;
61
+ if (selection === 'new') return createNewNote();
62
+
63
+ return selectUnterkategorie(selection.name);
64
+ }
65
+
66
+ async function selectUnterkategorie(oberkategorie) {
67
+ clear();
68
+ console.log(chalk.gray(' Oberkategorie: ') + chalk.bold(oberkategorie) + '\n');
69
+
70
+ const unterkategorien = storage.getUnterkategorien(oberkategorie);
71
+
72
+ if (unterkategorien.length === 0) {
73
+ console.log(chalk.gray(' Noch keine Unterkategorien.\n'));
74
+ return createNewNote();
75
+ }
76
+
77
+ const choices = [
78
+ { name: chalk.green(' + ') + chalk.green.bold('Neue Note hier erstellen'), value: 'new' },
79
+ new inquirer.Separator(chalk.gray('─'.repeat(40))),
80
+ ...unterkategorien.map(u => {
81
+ const noteCount = storage.getNotes(oberkategorie, u).length;
82
+ return {
83
+ name: chalk.cyan(' → ') + chalk.bold(u) + chalk.gray(` (${noteCount} Notes)`),
84
+ value: { type: 'unter', name: u },
85
+ short: u
86
+ };
87
+ }),
88
+ new inquirer.Separator(chalk.gray('─'.repeat(40))),
89
+ { name: chalk.gray(' < Zurueck'), value: 'back' }
90
+ ];
91
+
92
+ const { selection } = await inquirer.prompt([{
93
+ type: 'list',
94
+ name: 'selection',
95
+ message: chalk.blue('Unterkategorie'),
96
+ prefix: chalk.blue('◆'),
97
+ choices,
98
+ pageSize: 20,
99
+ loop: false
100
+ }]);
101
+
102
+ if (selection === 'back') return noteMenu();
103
+ if (selection === 'new') return createNewNote();
104
+
105
+ return selectNote(oberkategorie, selection.name);
106
+ }
107
+
108
+ async function selectNote(oberkategorie, unterkategorie) {
109
+ clear();
110
+ console.log(chalk.gray(' Pfad: ') + chalk.bold(oberkategorie + ' / ' + unterkategorie) + '\n');
111
+
112
+ const notes = storage.getNotes(oberkategorie, unterkategorie);
113
+
114
+ const choices = [
115
+ { name: chalk.green(' + ') + chalk.green.bold('Neue Note erstellen'), value: 'new' },
116
+ new inquirer.Separator(chalk.gray('─ Notes ' + '─'.repeat(32))),
117
+ ...notes.map(thema => ({
118
+ name: chalk.cyan(' → ') + chalk.bold(thema),
119
+ value: { type: 'note', thema },
120
+ short: thema
121
+ })),
122
+ new inquirer.Separator(chalk.gray('─'.repeat(40))),
123
+ { name: chalk.gray(' < Zurueck'), value: 'back' }
124
+ ];
125
+
126
+ if (notes.length === 0) {
127
+ choices.splice(2, 1); // Entferne leeren Separator
128
+ }
129
+
130
+ const { selection } = await inquirer.prompt([{
131
+ type: 'list',
132
+ name: 'selection',
133
+ message: chalk.blue('Note auswaehlen'),
134
+ prefix: chalk.blue('◆'),
135
+ choices,
136
+ pageSize: 20,
137
+ loop: false
138
+ }]);
139
+
140
+ if (selection === 'back') return selectUnterkategorie(oberkategorie);
141
+ if (selection === 'new') {
142
+ // Direkt neue Note in dieser Kategorie erstellen
143
+ return openNewNoteInCategory(oberkategorie, unterkategorie);
144
+ }
145
+
146
+ // Note zum Bearbeiten oeffnen
147
+ return openNoteForEditing({
148
+ oberkategorie,
149
+ unterkategorie,
150
+ thema: selection.thema
151
+ });
152
+ }
153
+
154
+ async function openNewNoteInCategory(oberkategorie, unterkategorie) {
155
+ clear();
156
+ console.log(chalk.gray(' Pfad: ') + chalk.bold(oberkategorie + ' / ' + unterkategorie));
157
+ console.log('');
158
+ console.log(chalk.gray(' Browser wird geoeffnet...'));
159
+ console.log('');
160
+
161
+ const port = await findAvailablePort(3333);
162
+
163
+ return new Promise((resolve) => {
164
+ startServer({ oberkategorie, unterkategorie, port }, (result) => {
165
+ if (result.saved) {
166
+ console.log(chalk.green.bold('\n ✓ Gespeichert!'));
167
+ console.log(chalk.gray(` ${oberkategorie} / ${unterkategorie} / ${result.thema}\n`));
168
+ } else {
169
+ console.log(chalk.yellow('\n ○ Abgebrochen.\n'));
170
+ }
171
+ resolve();
172
+ });
173
+
174
+ open(`http://localhost:${port}`);
175
+ });
176
+ }
177
+
178
+ async function editNoteMenu(allNotes, searchQuery = '') {
179
+ clear();
180
+
181
+ let filteredNotes = allNotes;
182
+
183
+ if (searchQuery.trim()) {
184
+ const query = searchQuery.toLowerCase();
185
+ filteredNotes = allNotes.filter(n =>
186
+ n.thema.toLowerCase().includes(query) ||
187
+ n.oberkategorie.toLowerCase().includes(query) ||
188
+ n.unterkategorie.toLowerCase().includes(query)
189
+ );
190
+ }
191
+
192
+ if (searchQuery) {
193
+ console.log(chalk.gray(` Suche: "${searchQuery}" - ${filteredNotes.length} Treffer\n`));
194
+ } else {
195
+ console.log(chalk.gray(` ${filteredNotes.length} Note(s) verfuegbar\n`));
196
+ }
197
+
198
+ if (filteredNotes.length === 0) {
199
+ console.log(chalk.yellow(' Keine Notes gefunden.\n'));
200
+ const { action } = await inquirer.prompt([{
201
+ type: 'list',
202
+ name: 'action',
203
+ message: chalk.blue('Was tun?'),
204
+ prefix: chalk.blue('◆'),
205
+ choices: [
206
+ { name: chalk.yellow(' ↻ ') + chalk.yellow('Alle Notes anzeigen'), value: 'all' },
207
+ { name: chalk.cyan(' ? ') + chalk.cyan('Neue Suche'), value: 'search' },
208
+ { name: chalk.gray(' < Zurueck'), value: 'back' }
209
+ ]
210
+ }]);
211
+ if (action === 'all') return editNoteMenu(allNotes, '');
212
+ if (action === 'search') {
213
+ const { query } = await inquirer.prompt([{
214
+ type: 'input',
215
+ name: 'query',
216
+ message: chalk.blue('Suchbegriff:'),
217
+ prefix: chalk.blue('?')
218
+ }]);
219
+ return editNoteMenu(allNotes, query);
220
+ }
221
+ return noteMenu();
222
+ }
223
+
224
+ const choices = [
225
+ ...filteredNotes.map(n => ({
226
+ name: chalk.cyan(' → ') + chalk.bold(n.thema) + chalk.gray(` (${n.oberkategorie} / ${n.unterkategorie})`),
227
+ value: n,
228
+ short: n.thema
229
+ })),
230
+ new inquirer.Separator(chalk.gray('─'.repeat(40))),
231
+ { name: chalk.cyan(' ? ') + chalk.cyan('Suchen / Filtern'), value: 'search' },
232
+ { name: chalk.gray(' < Zurueck'), value: 'back' }
233
+ ];
234
+
235
+ const { selection } = await inquirer.prompt([{
236
+ type: 'list',
237
+ name: 'selection',
238
+ message: chalk.blue('Note auswaehlen'),
239
+ prefix: chalk.blue('◆'),
240
+ choices,
241
+ pageSize: 15,
242
+ loop: false
243
+ }]);
244
+
245
+ if (selection === 'back') return noteMenu();
246
+ if (selection === 'search') {
247
+ const { query } = await inquirer.prompt([{
248
+ type: 'input',
249
+ name: 'query',
250
+ message: chalk.blue('Suchbegriff:'),
251
+ prefix: chalk.blue('?')
252
+ }]);
253
+ return editNoteMenu(allNotes, query);
254
+ }
255
+
256
+ // Note zum Bearbeiten oeffnen
257
+ return openNoteForEditing(selection);
258
+ }
259
+
260
+ async function openNoteForEditing(note) {
261
+ clear();
262
+ console.log(chalk.gray(' Bearbeite: ') + chalk.bold(note.thema));
263
+ console.log(chalk.gray(' Pfad: ') + chalk.gray(`${note.oberkategorie} / ${note.unterkategorie}`));
264
+ console.log('');
265
+ console.log(chalk.gray(' Browser wird geoeffnet...'));
266
+ console.log('');
267
+
268
+ const port = await findAvailablePort(3333);
269
+ const existingContent = storage.readNote(note.oberkategorie, note.unterkategorie, note.thema);
270
+
271
+ return new Promise((resolve) => {
272
+ startEditServer({
273
+ oberkategorie: note.oberkategorie,
274
+ unterkategorie: note.unterkategorie,
275
+ thema: note.thema,
276
+ content: existingContent,
277
+ port
278
+ }, (result) => {
279
+ if (result.saved) {
280
+ console.log(chalk.green.bold('\n ✓ Gespeichert!'));
281
+ console.log(chalk.gray(` ${note.oberkategorie} / ${note.unterkategorie} / ${result.thema}\n`));
282
+ } else {
283
+ console.log(chalk.yellow('\n ○ Beendet.\n'));
284
+ }
285
+ resolve();
286
+ });
287
+
288
+ open(`http://localhost:${port}`);
289
+ });
290
+ }
291
+
292
+ async function createNewNote() {
293
+ clear();
294
+
295
+ // 1. Oberkategorie
296
+ const oberkategorien = storage.getOberkategorien();
297
+ let oberkategorie;
298
+
299
+ if (oberkategorien.length > 0) {
300
+ console.log(chalk.gray(' Waehle eine Oberkategorie:\n'));
301
+
302
+ const { selection } = await inquirer.prompt([{
303
+ type: 'list',
304
+ name: 'selection',
305
+ message: chalk.blue('Oberkategorie'),
306
+ prefix: chalk.blue('◆'),
307
+ choices: [
308
+ ...oberkategorien.map(o => ({
309
+ name: formatChoice(o),
310
+ value: o,
311
+ short: o
312
+ })),
313
+ new inquirer.Separator(chalk.gray('─'.repeat(30))),
314
+ {
315
+ name: formatChoice('Neue Oberkategorie erstellen', true),
316
+ value: '__new__',
317
+ short: 'Neu'
318
+ },
319
+ {
320
+ name: chalk.red(' ✕ ') + chalk.gray('Abbrechen'),
321
+ value: '__exit__',
322
+ short: 'Abbrechen'
323
+ }
324
+ ],
325
+ pageSize: 12,
326
+ loop: false
327
+ }]);
328
+
329
+ if (selection === '__exit__') return;
330
+
331
+ if (selection === '__new__') {
332
+ console.log('');
333
+ const { name } = await inquirer.prompt([{
334
+ type: 'input',
335
+ name: 'name',
336
+ message: chalk.green('Name:'),
337
+ prefix: chalk.green('+')
338
+ }]);
339
+ if (!name.trim()) return;
340
+ oberkategorie = name.trim();
341
+ storage.createOberkategorie(oberkategorie);
342
+ console.log(chalk.green(`\n ✓ "${oberkategorie}" erstellt\n`));
343
+ } else {
344
+ oberkategorie = selection;
345
+ }
346
+ } else {
347
+ console.log(chalk.gray(' Noch keine Kategorien vorhanden.\n'));
348
+ const { name } = await inquirer.prompt([{
349
+ type: 'input',
350
+ name: 'name',
351
+ message: chalk.green('Erste Oberkategorie:'),
352
+ prefix: chalk.green('+')
353
+ }]);
354
+ if (!name.trim()) return;
355
+ oberkategorie = name.trim();
356
+ storage.createOberkategorie(oberkategorie);
357
+ }
358
+
359
+ // 2. Unterkategorie
360
+ clear();
361
+ console.log(chalk.gray(' Oberkategorie: ') + chalk.bold(oberkategorie));
362
+ console.log('');
363
+
364
+ const unterkategorien = storage.getUnterkategorien(oberkategorie);
365
+ let unterkategorie;
366
+
367
+ if (unterkategorien.length > 0) {
368
+ console.log(chalk.gray(' Waehle eine Unterkategorie:\n'));
369
+
370
+ const { selection } = await inquirer.prompt([{
371
+ type: 'list',
372
+ name: 'selection',
373
+ message: chalk.blue('Unterkategorie'),
374
+ prefix: chalk.blue('◆'),
375
+ choices: [
376
+ ...unterkategorien.map(u => ({
377
+ name: formatChoice(u),
378
+ value: u,
379
+ short: u
380
+ })),
381
+ new inquirer.Separator(chalk.gray('─'.repeat(30))),
382
+ {
383
+ name: formatChoice('Neue Unterkategorie erstellen', true),
384
+ value: '__new__',
385
+ short: 'Neu'
386
+ },
387
+ {
388
+ name: chalk.red(' ✕ ') + chalk.gray('Abbrechen'),
389
+ value: '__exit__',
390
+ short: 'Abbrechen'
391
+ }
392
+ ],
393
+ pageSize: 12,
394
+ loop: false
395
+ }]);
396
+
397
+ if (selection === '__exit__') return;
398
+
399
+ if (selection === '__new__') {
400
+ console.log('');
401
+ const { name } = await inquirer.prompt([{
402
+ type: 'input',
403
+ name: 'name',
404
+ message: chalk.green('Name:'),
405
+ prefix: chalk.green('+')
406
+ }]);
407
+ if (!name.trim()) return;
408
+ unterkategorie = name.trim();
409
+ storage.createUnterkategorie(oberkategorie, unterkategorie);
410
+ console.log(chalk.green(`\n ✓ "${unterkategorie}" erstellt\n`));
411
+ } else {
412
+ unterkategorie = selection;
413
+ }
414
+ } else {
415
+ console.log(chalk.gray(' Noch keine Unterkategorien vorhanden.\n'));
416
+ const { name } = await inquirer.prompt([{
417
+ type: 'input',
418
+ name: 'name',
419
+ message: chalk.green('Erste Unterkategorie:'),
420
+ prefix: chalk.green('+')
421
+ }]);
422
+ if (!name.trim()) return;
423
+ unterkategorie = name.trim();
424
+ storage.createUnterkategorie(oberkategorie, unterkategorie);
425
+ }
426
+
427
+ // 3. Web-Editor oeffnen
428
+ clear();
429
+ console.log(chalk.gray(' Pfad: ') + chalk.bold(`${oberkategorie} / ${unterkategorie}`));
430
+ console.log('');
431
+ console.log(chalk.gray(' Browser wird geoeffnet...'));
432
+ console.log('');
433
+
434
+ const port = await findAvailablePort(3333);
435
+
436
+ return new Promise((resolve) => {
437
+ startServer({ oberkategorie, unterkategorie, port }, (result) => {
438
+ if (result.saved) {
439
+ console.log(chalk.green.bold('\n ✓ Gespeichert!'));
440
+ console.log(chalk.gray(` ${oberkategorie} / ${unterkategorie} / ${result.thema}\n`));
441
+ } else {
442
+ console.log(chalk.yellow('\n ○ Abgebrochen.\n'));
443
+ }
444
+ resolve();
445
+ });
446
+
447
+ open(`http://localhost:${port}`);
448
+ });
449
+ }
@@ -0,0 +1,21 @@
1
+ import chalk from 'chalk';
2
+ import open from 'open';
3
+ import { startSearchServer } from '../web/server.js';
4
+ import { findAvailablePort } from '../utils.js';
5
+
6
+ export async function searchMenu() {
7
+ console.clear();
8
+ console.log(chalk.cyan.bold('\n WASIBASE SEARCH\n'));
9
+ console.log(chalk.gray(' Browser oeffnet sich...\n'));
10
+
11
+ const port = await findAvailablePort(3334);
12
+
13
+ return new Promise((resolve) => {
14
+ startSearchServer({ port }, () => {
15
+ console.log(chalk.gray('\n Suche beendet.\n'));
16
+ resolve();
17
+ });
18
+
19
+ open(`http://localhost:${port}`);
20
+ });
21
+ }