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.
@@ -0,0 +1,21 @@
1
+ import chalk from 'chalk';
2
+ import open from 'open';
3
+ import { startGraphServer } from '../web/graphServer.js';
4
+ import { findAvailablePort } from '../utils.js';
5
+
6
+ export async function graphMenu() {
7
+ console.clear();
8
+ console.log(chalk.cyan.bold('\n WASIBASE GRAPH\n'));
9
+ console.log(chalk.gray(' Visualisierung wird geladen...\n'));
10
+
11
+ const port = await findAvailablePort(3335);
12
+
13
+ return new Promise((resolve) => {
14
+ startGraphServer({ port }, () => {
15
+ console.log(chalk.gray('\n Graph geschlossen.\n'));
16
+ resolve();
17
+ });
18
+
19
+ open(`http://localhost:${port}`);
20
+ });
21
+ }
@@ -0,0 +1,320 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import * as storage from '../storage.js';
4
+
5
+ function clear() {
6
+ console.clear();
7
+ console.log('');
8
+ console.log(chalk.bgBlue.white.bold(' WASIBASE '));
9
+ console.log('');
10
+ }
11
+
12
+ function formatChoice(name, isNew = false, isAction = false) {
13
+ if (isNew) return chalk.green(' + ') + chalk.green(name);
14
+ if (isAction) return chalk.yellow(' ~ ') + chalk.yellow(name);
15
+ return chalk.cyan(' → ') + chalk.bold(name);
16
+ }
17
+
18
+ function getPreview(content, maxLength = 80) {
19
+ if (!content) return '';
20
+ // Entferne YAML Frontmatter
21
+ const withoutFrontmatter = content.replace(/^---[\s\S]*?---\n?/, '').trim();
22
+ // Entferne Markdown-Syntax
23
+ const cleaned = withoutFrontmatter
24
+ .replace(/^#+\s*/gm, '')
25
+ .replace(/\*\*/g, '')
26
+ .replace(/\*/g, '')
27
+ .replace(/\n/g, ' ')
28
+ .trim();
29
+ if (cleaned.length > maxLength) {
30
+ return cleaned.substring(0, maxLength) + '...';
31
+ }
32
+ return cleaned;
33
+ }
34
+
35
+ export async function mainMenu() {
36
+ clear();
37
+
38
+ const oberkategorien = storage.getOberkategorien();
39
+
40
+ if (oberkategorien.length === 0) {
41
+ console.log(chalk.gray(' Noch keine Kategorien.\n'));
42
+ console.log(chalk.gray(' Tipp: wasibase note\n'));
43
+
44
+ const { action } = await inquirer.prompt([{
45
+ type: 'list',
46
+ name: 'action',
47
+ message: chalk.blue('Aktion'),
48
+ prefix: chalk.blue('◆'),
49
+ choices: [
50
+ { name: formatChoice('Neue Oberkategorie erstellen', true), value: 'new' },
51
+ { name: chalk.red(' ✕ ') + chalk.gray('Beenden'), value: 'exit' }
52
+ ]
53
+ }]);
54
+
55
+ if (action === 'exit') return;
56
+
57
+ if (action === 'new') {
58
+ const { name } = await inquirer.prompt([{
59
+ type: 'input',
60
+ name: 'name',
61
+ message: chalk.green('Name:'),
62
+ prefix: chalk.green('+')
63
+ }]);
64
+ if (name.trim()) {
65
+ storage.createOberkategorie(name.trim());
66
+ console.log(chalk.green(`\n ✓ "${name.trim()}" erstellt\n`));
67
+ }
68
+ }
69
+ return mainMenu();
70
+ }
71
+
72
+ // Zaehle Notes pro Oberkategorie
73
+ function countNotesInOberkategorie(ober) {
74
+ const unters = storage.getUnterkategorien(ober);
75
+ return unters.reduce((sum, u) => sum + storage.getNotes(ober, u).length, 0);
76
+ }
77
+
78
+ const choices = [
79
+ ...oberkategorien.map(o => {
80
+ const unterCount = storage.getUnterkategorien(o).length;
81
+ const noteCount = countNotesInOberkategorie(o);
82
+ return {
83
+ name: chalk.cyan(' → ') + chalk.bold(o) + chalk.gray(` (${unterCount} Unter, ${noteCount} Notes)`),
84
+ value: { type: 'open', name: o },
85
+ short: o
86
+ };
87
+ }),
88
+ new inquirer.Separator(chalk.gray('─'.repeat(30))),
89
+ { name: formatChoice('Neue Oberkategorie', true), value: 'new', short: 'Neu' },
90
+ { name: chalk.red(' ✕ ') + chalk.gray('Beenden'), value: 'exit', short: 'Beenden' }
91
+ ];
92
+
93
+ const { action } = await inquirer.prompt([{
94
+ type: 'list',
95
+ name: 'action',
96
+ message: chalk.blue('Oberkategorie'),
97
+ prefix: chalk.blue('◆'),
98
+ choices,
99
+ pageSize: 15,
100
+ loop: false
101
+ }]);
102
+
103
+ if (action === 'exit') {
104
+ console.log(chalk.gray('\n Bis bald!\n'));
105
+ return;
106
+ }
107
+
108
+ if (action === 'new') {
109
+ const { name } = await inquirer.prompt([{
110
+ type: 'input',
111
+ name: 'name',
112
+ message: chalk.green('Name:'),
113
+ prefix: chalk.green('+')
114
+ }]);
115
+ if (name.trim()) {
116
+ storage.createOberkategorie(name.trim());
117
+ console.log(chalk.green(`\n ✓ "${name.trim()}" erstellt\n`));
118
+ }
119
+ return mainMenu();
120
+ }
121
+
122
+ if (action.type === 'open') {
123
+ return oberkategorieMenu(action.name);
124
+ }
125
+ }
126
+
127
+ async function oberkategorieMenu(oberkategorie) {
128
+ clear();
129
+ console.log(chalk.gray(' Oberkategorie: ') + chalk.bold(oberkategorie));
130
+ console.log('');
131
+
132
+ const unterkategorien = storage.getUnterkategorien(oberkategorie);
133
+
134
+ if (unterkategorien.length === 0) {
135
+ console.log(chalk.gray(' Noch keine Unterkategorien.\n'));
136
+ } else {
137
+ const totalNotes = unterkategorien.reduce((sum, u) => sum + storage.getNotes(oberkategorie, u).length, 0);
138
+ console.log(chalk.gray(` ${unterkategorien.length} Unterkategorie${unterkategorien.length === 1 ? '' : 'n'}, ${totalNotes} Note${totalNotes === 1 ? '' : 's'} gesamt\n`));
139
+ }
140
+
141
+ const choices = [
142
+ ...unterkategorien.map(u => {
143
+ const noteCount = storage.getNotes(oberkategorie, u).length;
144
+ return {
145
+ name: chalk.cyan(' → ') + chalk.bold(u) + chalk.gray(` (${noteCount} Note${noteCount === 1 ? '' : 's'})`),
146
+ value: { type: 'open', name: u },
147
+ short: u
148
+ };
149
+ }),
150
+ new inquirer.Separator(chalk.gray('─'.repeat(30))),
151
+ { name: formatChoice('Neue Unterkategorie', true), value: 'new' },
152
+ { name: chalk.red(' - ') + chalk.red('Oberkategorie loeschen'), value: 'delete' },
153
+ { name: chalk.gray(' < Zurueck'), value: 'back' }
154
+ ];
155
+
156
+ const { action } = await inquirer.prompt([{
157
+ type: 'list',
158
+ name: 'action',
159
+ message: chalk.blue('Unterkategorie'),
160
+ prefix: chalk.blue('◆'),
161
+ choices,
162
+ pageSize: 15,
163
+ loop: false
164
+ }]);
165
+
166
+ if (action === 'back') return mainMenu();
167
+
168
+ if (action === 'new') {
169
+ const { name } = await inquirer.prompt([{
170
+ type: 'input',
171
+ name: 'name',
172
+ message: chalk.green('Name:'),
173
+ prefix: chalk.green('+')
174
+ }]);
175
+ if (name.trim()) {
176
+ storage.createUnterkategorie(oberkategorie, name.trim());
177
+ console.log(chalk.green(`\n ✓ "${name.trim()}" erstellt\n`));
178
+ }
179
+ return oberkategorieMenu(oberkategorie);
180
+ }
181
+
182
+ if (action === 'delete') {
183
+ const { confirm } = await inquirer.prompt([{
184
+ type: 'confirm',
185
+ name: 'confirm',
186
+ message: chalk.red(`"${oberkategorie}" und alle Inhalte loeschen?`),
187
+ default: false
188
+ }]);
189
+ if (confirm) {
190
+ storage.deleteOberkategorie(oberkategorie);
191
+ console.log(chalk.red(`\n ✓ "${oberkategorie}" geloescht\n`));
192
+ return mainMenu();
193
+ }
194
+ return oberkategorieMenu(oberkategorie);
195
+ }
196
+
197
+ if (action.type === 'open') {
198
+ return unterkategorieMenu(oberkategorie, action.name);
199
+ }
200
+
201
+ return oberkategorieMenu(oberkategorie);
202
+ }
203
+
204
+ async function unterkategorieMenu(oberkategorie, unterkategorie) {
205
+ clear();
206
+ console.log(chalk.gray(' Pfad: ') + chalk.bold(`${oberkategorie} / ${unterkategorie}`));
207
+ console.log('');
208
+
209
+ const notes = storage.getNotes(oberkategorie, unterkategorie);
210
+
211
+ if (notes.length === 0) {
212
+ console.log(chalk.gray(' Noch keine Notes.\n'));
213
+ } else {
214
+ console.log(chalk.gray(` ${notes.length} Note${notes.length === 1 ? '' : 's'}:\n`));
215
+ }
216
+
217
+ const choices = [
218
+ ...notes.map(n => {
219
+ const content = storage.readNote(oberkategorie, unterkategorie, n);
220
+ const preview = getPreview(content, 40);
221
+ return {
222
+ name: chalk.cyan(' → ') + chalk.bold(n) + (preview ? chalk.gray(` "${preview}"`) : ''),
223
+ value: { type: 'note', name: n },
224
+ short: n
225
+ };
226
+ }),
227
+ new inquirer.Separator(chalk.gray('─'.repeat(30))),
228
+ { name: chalk.red(' - ') + chalk.red('Unterkategorie loeschen'), value: 'delete' },
229
+ { name: chalk.gray(' < Zurueck'), value: 'back' }
230
+ ];
231
+
232
+ const { action } = await inquirer.prompt([{
233
+ type: 'list',
234
+ name: 'action',
235
+ message: chalk.blue('Notes'),
236
+ prefix: chalk.blue('◆'),
237
+ choices,
238
+ pageSize: 15,
239
+ loop: false
240
+ }]);
241
+
242
+ if (action === 'back') return oberkategorieMenu(oberkategorie);
243
+
244
+ if (action === 'delete') {
245
+ const { confirm } = await inquirer.prompt([{
246
+ type: 'confirm',
247
+ name: 'confirm',
248
+ message: chalk.red(`"${unterkategorie}" und alle Notes loeschen?`),
249
+ default: false
250
+ }]);
251
+ if (confirm) {
252
+ storage.deleteUnterkategorie(oberkategorie, unterkategorie);
253
+ console.log(chalk.red(`\n ✓ "${unterkategorie}" geloescht\n`));
254
+ return oberkategorieMenu(oberkategorie);
255
+ }
256
+ return unterkategorieMenu(oberkategorie, unterkategorie);
257
+ }
258
+
259
+ if (action.type === 'note') {
260
+ return noteDetailMenu(oberkategorie, unterkategorie, action.name);
261
+ }
262
+
263
+ return unterkategorieMenu(oberkategorie, unterkategorie);
264
+ }
265
+
266
+ async function noteDetailMenu(oberkategorie, unterkategorie, thema) {
267
+ clear();
268
+ console.log(chalk.gray(' Note: ') + chalk.bold(thema));
269
+ console.log(chalk.gray(' Pfad: ') + chalk.gray(`${oberkategorie} / ${unterkategorie}`));
270
+ console.log('');
271
+
272
+ const content = storage.readNote(oberkategorie, unterkategorie, thema);
273
+
274
+ if (content) {
275
+ // Zeige Preview
276
+ const lines = content.split('\n');
277
+ const previewLines = lines.slice(0, 20);
278
+ console.log(chalk.gray(' ┌' + '─'.repeat(60)));
279
+ previewLines.forEach(line => {
280
+ const displayLine = line.length > 58 ? line.substring(0, 55) + '...' : line;
281
+ console.log(chalk.gray(' │ ') + chalk.bold(displayLine));
282
+ });
283
+ if (lines.length > 20) {
284
+ console.log(chalk.gray(' │ ') + chalk.gray(`... (${lines.length - 20} weitere Zeilen)`));
285
+ }
286
+ console.log(chalk.gray(' └' + '─'.repeat(60)));
287
+ console.log('');
288
+ }
289
+
290
+ const { action } = await inquirer.prompt([{
291
+ type: 'list',
292
+ name: 'action',
293
+ message: chalk.blue('Aktion'),
294
+ prefix: chalk.blue('◆'),
295
+ choices: [
296
+ { name: chalk.red(' - ') + chalk.red('Note loeschen'), value: 'delete' },
297
+ { name: chalk.gray(' < Zurueck'), value: 'back' }
298
+ ]
299
+ }]);
300
+
301
+ if (action === 'back') {
302
+ return unterkategorieMenu(oberkategorie, unterkategorie);
303
+ }
304
+
305
+ if (action === 'delete') {
306
+ const { confirm } = await inquirer.prompt([{
307
+ type: 'confirm',
308
+ name: 'confirm',
309
+ message: chalk.red(`Note "${thema}" wirklich loeschen?`),
310
+ default: false
311
+ }]);
312
+ if (confirm) {
313
+ storage.deleteNote(oberkategorie, unterkategorie, thema);
314
+ console.log(chalk.red(`\n ✓ "${thema}" geloescht\n`));
315
+ return unterkategorieMenu(oberkategorie, unterkategorie);
316
+ }
317
+ }
318
+
319
+ return noteDetailMenu(oberkategorie, unterkategorie, thema);
320
+ }