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/README.md +69 -0
- package/bin/wasibase.js +5 -0
- package/package.json +54 -0
- package/src/config.js +11 -0
- package/src/index.js +54 -0
- package/src/storage.js +262 -0
- package/src/ui/backup.js +248 -0
- package/src/ui/graph.js +21 -0
- package/src/ui/manage.js +320 -0
- package/src/ui/note.js +449 -0
- package/src/ui/search.js +21 -0
- package/src/ui/sync.js +348 -0
- package/src/utils.js +104 -0
- package/src/web/graphServer.js +897 -0
- package/src/web/server.js +2132 -0
package/src/ui/graph.js
ADDED
|
@@ -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
|
+
}
|
package/src/ui/manage.js
ADDED
|
@@ -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
|
+
}
|