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 ADDED
@@ -0,0 +1,69 @@
1
+ # Wasibase
2
+
3
+ > Dein Second Brain. Terminal-basiert. Mit Backlinks.
4
+
5
+ Ein terminal-basiertes Notizen-System mit Markdown-Unterstuetzung, Backlinks und Graph-Visualisierung.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install -g wasibase
11
+ ```
12
+
13
+ ## Verwendung
14
+
15
+ ```bash
16
+ # Hauptmenu oeffnen
17
+ wasibase
18
+
19
+ # Neue Note erstellen oder bearbeiten
20
+ wasibase note
21
+
22
+ # Notes durchsuchen
23
+ wasibase search
24
+
25
+ # Wissens-Graph anzeigen
26
+ wasibase graph
27
+
28
+ # Backup erstellen/wiederherstellen
29
+ wasibase backup
30
+
31
+ # Mit Cloud synchronisieren (Proton Drive, Dropbox, iCloud)
32
+ wasibase sync
33
+ ```
34
+
35
+ ## Features
36
+
37
+ - **Markdown Notes** - Schreibe deine Notizen in Markdown mit Live-Vorschau
38
+ - **Backlinks** - Verknuepfe Wissen mit `[[Backlinks]]` wie in Obsidian
39
+ - **Graph View** - Visualisiere dein Wissen als interaktiven Graph
40
+ - **Schnelle Suche** - Finde jede Note sofort
41
+ - **Cloud Backup** - Automatisches Backup zu Proton Drive, Dropbox oder iCloud
42
+ - **Terminal First** - Schnell und effizient direkt aus dem Terminal
43
+
44
+ ## Struktur
45
+
46
+ Notes werden in `~/.wasibase/notes/` gespeichert:
47
+
48
+ ```
49
+ ~/.wasibase/
50
+ ├── notes/
51
+ │ ├── Oberkategorie/
52
+ │ │ └── Unterkategorie/
53
+ │ │ └── Note.md
54
+ ├── backups/
55
+ └── config.json
56
+ ```
57
+
58
+ ## Systemanforderungen
59
+
60
+ - Node.js 18+
61
+ - macOS, Linux oder Windows
62
+
63
+ ## Lizenz
64
+
65
+ MIT
66
+
67
+ ## Autor
68
+
69
+ [Wasili](https://github.com/wasuli)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { main } from '../src/index.js';
4
+
5
+ main();
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "wasibase",
3
+ "version": "1.0.0",
4
+ "description": "Second Brain - Terminal-basiertes Notizen-System mit Backlinks und Graph-Visualisierung",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "wasibase": "./bin/wasibase.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node bin/wasibase.js",
12
+ "note": "node bin/wasibase.js note",
13
+ "search": "node bin/wasibase.js search",
14
+ "graph": "node bin/wasibase.js graph",
15
+ "backup": "node bin/wasibase.js backup",
16
+ "sync": "node bin/wasibase.js sync"
17
+ },
18
+ "keywords": [
19
+ "notes",
20
+ "second-brain",
21
+ "backlinks",
22
+ "markdown",
23
+ "cli",
24
+ "terminal",
25
+ "knowledge-base",
26
+ "zettelkasten",
27
+ "note-taking",
28
+ "graph"
29
+ ],
30
+ "author": "Wasili",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/wasuli/wasibase.git"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/wasuli/wasibase/issues"
38
+ },
39
+ "homepage": "https://wasuli.github.io/wasibase",
40
+ "files": [
41
+ "bin",
42
+ "src"
43
+ ],
44
+ "dependencies": {
45
+ "chalk": "^5.6.2",
46
+ "express": "^5.2.1",
47
+ "inquirer": "^9.3.8",
48
+ "marked": "^17.0.1",
49
+ "open": "^11.0.0"
50
+ },
51
+ "engines": {
52
+ "node": ">=18.0.0"
53
+ }
54
+ }
package/src/config.js ADDED
@@ -0,0 +1,11 @@
1
+ import path from 'path';
2
+ import os from 'os';
3
+
4
+ export const CONFIG = {
5
+ baseDir: path.join(os.homedir(), '.wasibase'),
6
+ notesDir: path.join(os.homedir(), '.wasibase', 'notes'),
7
+ tempDir: path.join(os.homedir(), '.wasibase', 'temp'),
8
+ configFile: path.join(os.homedir(), '.wasibase', 'config.json'),
9
+ backupDir: path.join(os.homedir(), '.wasibase', 'backups'),
10
+ editor: process.env.EDITOR || 'vim'
11
+ };
package/src/index.js ADDED
@@ -0,0 +1,54 @@
1
+ import { mainMenu } from './ui/manage.js';
2
+ import { noteMenu } from './ui/note.js';
3
+ import { searchMenu } from './ui/search.js';
4
+ import { graphMenu } from './ui/graph.js';
5
+ import { backupMenu } from './ui/backup.js';
6
+ import { syncMenu } from './ui/sync.js';
7
+ import chalk from 'chalk';
8
+
9
+ export async function main() {
10
+ const args = process.argv.slice(2);
11
+ const command = args[0];
12
+
13
+ switch (command) {
14
+ case 'note':
15
+ case 'n':
16
+ await noteMenu();
17
+ break;
18
+ case 'search':
19
+ case 's':
20
+ await searchMenu();
21
+ break;
22
+ case 'graph':
23
+ case 'g':
24
+ await graphMenu();
25
+ break;
26
+ case 'backup':
27
+ case 'b':
28
+ await backupMenu();
29
+ break;
30
+ case 'sync':
31
+ await syncMenu();
32
+ break;
33
+ case 'help':
34
+ case 'h':
35
+ showHelp();
36
+ break;
37
+ default:
38
+ await mainMenu();
39
+ break;
40
+ }
41
+ }
42
+
43
+ function showHelp() {
44
+ console.clear();
45
+ console.log(chalk.cyan.bold('\n WASIBASE\n'));
46
+ console.log(chalk.bold(' Befehle:\n'));
47
+ console.log(chalk.gray(' wasibase ') + chalk.bold('Verwaltung (Kategorien)'));
48
+ console.log(chalk.gray(' wasibase note ') + chalk.bold('Note erstellen/bearbeiten'));
49
+ console.log(chalk.gray(' wasibase search ') + chalk.bold('Notes durchsuchen'));
50
+ console.log(chalk.gray(' wasibase graph ') + chalk.bold('Verknuepfungen visualisieren'));
51
+ console.log(chalk.gray(' wasibase backup ') + chalk.bold('Backup erstellen/wiederherstellen'));
52
+ console.log(chalk.gray(' wasibase sync ') + chalk.bold('Mit Cloud synchronisieren'));
53
+ console.log(chalk.gray(' wasibase help ') + chalk.bold('Diese Hilfe\n'));
54
+ }
package/src/storage.js ADDED
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Storage Module - File system operations for Wasibase
3
+ */
4
+
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { CONFIG } from './config.js';
8
+
9
+ // =============================================================================
10
+ // Directory Operations
11
+ // =============================================================================
12
+
13
+ export function ensureDir(dir) {
14
+ if (!fs.existsSync(dir)) {
15
+ fs.mkdirSync(dir, { recursive: true });
16
+ }
17
+ }
18
+
19
+ // =============================================================================
20
+ // Category Operations
21
+ // =============================================================================
22
+
23
+ export function getOberkategorien() {
24
+ ensureDir(CONFIG.notesDir);
25
+ return fs.readdirSync(CONFIG.notesDir, { withFileTypes: true })
26
+ .filter(d => d.isDirectory())
27
+ .map(d => d.name)
28
+ .sort();
29
+ }
30
+
31
+ export function getUnterkategorien(oberkategorie) {
32
+ const dir = path.join(CONFIG.notesDir, oberkategorie);
33
+ if (!fs.existsSync(dir)) return [];
34
+ return fs.readdirSync(dir, { withFileTypes: true })
35
+ .filter(d => d.isDirectory())
36
+ .map(d => d.name)
37
+ .sort();
38
+ }
39
+
40
+ export function createOberkategorie(name) {
41
+ const dir = path.join(CONFIG.notesDir, name);
42
+ ensureDir(dir);
43
+ return dir;
44
+ }
45
+
46
+ export function createUnterkategorie(oberkategorie, name) {
47
+ const dir = path.join(CONFIG.notesDir, oberkategorie, name);
48
+ ensureDir(dir);
49
+ return dir;
50
+ }
51
+
52
+ export function deleteOberkategorie(name) {
53
+ const dir = path.join(CONFIG.notesDir, name);
54
+ if (fs.existsSync(dir)) {
55
+ fs.rmSync(dir, { recursive: true });
56
+ }
57
+ }
58
+
59
+ export function deleteUnterkategorie(oberkategorie, name) {
60
+ const dir = path.join(CONFIG.notesDir, oberkategorie, name);
61
+ if (fs.existsSync(dir)) {
62
+ fs.rmSync(dir, { recursive: true });
63
+ }
64
+ }
65
+
66
+ // =============================================================================
67
+ // Note Operations
68
+ // =============================================================================
69
+
70
+ export function getNotes(oberkategorie, unterkategorie) {
71
+ const dir = path.join(CONFIG.notesDir, oberkategorie, unterkategorie);
72
+ if (!fs.existsSync(dir)) return [];
73
+ return fs.readdirSync(dir)
74
+ .filter(f => f.endsWith('.md'))
75
+ .map(f => f.replace('.md', ''))
76
+ .sort();
77
+ }
78
+
79
+ export function getNotePath(oberkategorie, unterkategorie, thema) {
80
+ return path.join(CONFIG.notesDir, oberkategorie, unterkategorie, `${thema}.md`);
81
+ }
82
+
83
+ export function noteExists(oberkategorie, unterkategorie, thema) {
84
+ return fs.existsSync(getNotePath(oberkategorie, unterkategorie, thema));
85
+ }
86
+
87
+ export function readNote(oberkategorie, unterkategorie, thema) {
88
+ const filePath = getNotePath(oberkategorie, unterkategorie, thema);
89
+ if (!fs.existsSync(filePath)) return null;
90
+ return fs.readFileSync(filePath, 'utf-8');
91
+ }
92
+
93
+ export function saveNote(oberkategorie, unterkategorie, thema, content) {
94
+ const dir = path.join(CONFIG.notesDir, oberkategorie, unterkategorie);
95
+ ensureDir(dir);
96
+ const filePath = path.join(dir, `${thema}.md`);
97
+ fs.writeFileSync(filePath, content);
98
+ return filePath;
99
+ }
100
+
101
+ export function deleteNote(oberkategorie, unterkategorie, thema) {
102
+ const filePath = getNotePath(oberkategorie, unterkategorie, thema);
103
+ if (fs.existsSync(filePath)) {
104
+ fs.unlinkSync(filePath);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Get all notes across all categories
110
+ */
111
+ export function getAllNotes() {
112
+ const allNotes = [];
113
+
114
+ for (const ober of getOberkategorien()) {
115
+ for (const unter of getUnterkategorien(ober)) {
116
+ for (const thema of getNotes(ober, unter)) {
117
+ allNotes.push({ oberkategorie: ober, unterkategorie: unter, thema });
118
+ }
119
+ }
120
+ }
121
+
122
+ return allNotes;
123
+ }
124
+
125
+ /**
126
+ * Get statistics about the notes collection
127
+ */
128
+ export function getStats() {
129
+ const oberkategorien = getOberkategorien();
130
+ let unterCount = 0;
131
+ let noteCount = 0;
132
+
133
+ for (const ober of oberkategorien) {
134
+ const unters = getUnterkategorien(ober);
135
+ unterCount += unters.length;
136
+ for (const unter of unters) {
137
+ noteCount += getNotes(ober, unter).length;
138
+ }
139
+ }
140
+
141
+ return {
142
+ oberkategorien: oberkategorien.length,
143
+ unterkategorien: unterCount,
144
+ notes: noteCount
145
+ };
146
+ }
147
+
148
+ // =============================================================================
149
+ // Search
150
+ // =============================================================================
151
+
152
+ export function searchNotes(query) {
153
+ const results = [];
154
+ const queryLower = query.toLowerCase();
155
+
156
+ for (const { oberkategorie, unterkategorie, thema } of getAllNotes()) {
157
+ const content = readNote(oberkategorie, unterkategorie, thema);
158
+ if (!content) continue;
159
+
160
+ const contentLower = content.toLowerCase();
161
+ const themaLower = thema.toLowerCase();
162
+
163
+ if (themaLower.includes(queryLower) || contentLower.includes(queryLower)) {
164
+ let preview = '';
165
+ const idx = contentLower.indexOf(queryLower);
166
+
167
+ if (idx !== -1) {
168
+ const start = Math.max(0, idx - 50);
169
+ const end = Math.min(content.length, idx + query.length + 50);
170
+ preview = (start > 0 ? '...' : '') +
171
+ content.substring(start, idx) +
172
+ '<mark>' + content.substring(idx, idx + query.length) + '</mark>' +
173
+ content.substring(idx + query.length, end) +
174
+ (end < content.length ? '...' : '');
175
+ } else {
176
+ preview = content.substring(0, 100) + (content.length > 100 ? '...' : '');
177
+ }
178
+
179
+ results.push({
180
+ oberkategorie,
181
+ unterkategorie,
182
+ thema,
183
+ preview: preview.replace(/\n/g, ' ')
184
+ });
185
+ }
186
+ }
187
+
188
+ return results;
189
+ }
190
+
191
+ // =============================================================================
192
+ // Backup & Restore
193
+ // =============================================================================
194
+
195
+ export function createBackup() {
196
+ return {
197
+ version: 1,
198
+ created: new Date().toISOString(),
199
+ notes: getAllNotes().map(note => ({
200
+ ...note,
201
+ content: readNote(note.oberkategorie, note.unterkategorie, note.thema) || ''
202
+ }))
203
+ };
204
+ }
205
+
206
+ export function saveBackupToFile(filePath) {
207
+ const backup = createBackup();
208
+ ensureDir(path.dirname(filePath));
209
+ fs.writeFileSync(filePath, JSON.stringify(backup, null, 2));
210
+ return backup.notes.length;
211
+ }
212
+
213
+ export function loadBackupFromFile(filePath) {
214
+ if (!fs.existsSync(filePath)) return null;
215
+ try {
216
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
217
+ } catch {
218
+ return null;
219
+ }
220
+ }
221
+
222
+ export function restoreFromBackup(backupData) {
223
+ let restored = 0;
224
+ for (const note of backupData.notes) {
225
+ saveNote(note.oberkategorie, note.unterkategorie, note.thema, note.content);
226
+ restored++;
227
+ }
228
+ return restored;
229
+ }
230
+
231
+ // =============================================================================
232
+ // Configuration
233
+ // =============================================================================
234
+
235
+ export function loadConfig() {
236
+ if (!fs.existsSync(CONFIG.configFile)) return {};
237
+ try {
238
+ return JSON.parse(fs.readFileSync(CONFIG.configFile, 'utf-8'));
239
+ } catch {
240
+ return {};
241
+ }
242
+ }
243
+
244
+ export function saveConfig(config) {
245
+ ensureDir(CONFIG.baseDir);
246
+ fs.writeFileSync(CONFIG.configFile, JSON.stringify(config, null, 2));
247
+ }
248
+
249
+ export function getSyncPath() {
250
+ return loadConfig().syncPath || null;
251
+ }
252
+
253
+ export function setSyncPath(syncPath) {
254
+ const config = loadConfig();
255
+ config.syncPath = syncPath;
256
+ saveConfig(config);
257
+ }
258
+
259
+ export function syncToPath(syncPath) {
260
+ const backupFile = path.join(syncPath, 'wasibase-backup.json');
261
+ return saveBackupToFile(backupFile);
262
+ }
@@ -0,0 +1,248 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
+ import * as storage from '../storage.js';
6
+ import { CONFIG } from '../config.js';
7
+
8
+ function clear() {
9
+ console.clear();
10
+ console.log('');
11
+ console.log(chalk.bgGreen.white.bold(' WASIBASE BACKUP '));
12
+ console.log('');
13
+ }
14
+
15
+ function formatSize(bytes) {
16
+ if (bytes < 1024) return bytes + ' B';
17
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
18
+ return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
19
+ }
20
+
21
+ export async function backupMenu() {
22
+ clear();
23
+
24
+ const oberkategorien = storage.getOberkategorien();
25
+ let totalNotes = 0;
26
+
27
+ for (const ober of oberkategorien) {
28
+ const unters = storage.getUnterkategorien(ober);
29
+ for (const unter of unters) {
30
+ totalNotes += storage.getNotes(ober, unter).length;
31
+ }
32
+ }
33
+
34
+ console.log(chalk.gray(` ${totalNotes} Notes in ${oberkategorien.length} Kategorien\n`));
35
+
36
+ const { action } = await inquirer.prompt([{
37
+ type: 'list',
38
+ name: 'action',
39
+ message: chalk.blue('Aktion'),
40
+ prefix: chalk.blue('◆'),
41
+ choices: [
42
+ { name: chalk.green(' ↓ ') + chalk.bold('Backup erstellen'), value: 'create' },
43
+ { name: chalk.yellow(' ↑ ') + chalk.bold('Backup wiederherstellen'), value: 'restore' },
44
+ { name: chalk.gray(' < Zurueck'), value: 'exit' }
45
+ ]
46
+ }]);
47
+
48
+ if (action === 'exit') return;
49
+
50
+ if (action === 'create') {
51
+ return createBackup();
52
+ }
53
+
54
+ if (action === 'restore') {
55
+ return restoreBackup();
56
+ }
57
+ }
58
+
59
+ async function createBackup() {
60
+ clear();
61
+
62
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
63
+ const defaultName = `wasibase-backup-${timestamp}.json`;
64
+ const defaultPath = path.join(CONFIG.backupDir, defaultName);
65
+
66
+ console.log(chalk.gray(' Standard Speicherort:'));
67
+ console.log(chalk.cyan(` ${CONFIG.backupDir}\n`));
68
+
69
+ const { location } = await inquirer.prompt([{
70
+ type: 'list',
71
+ name: 'location',
72
+ message: chalk.blue('Wo speichern?'),
73
+ prefix: chalk.blue('◆'),
74
+ choices: [
75
+ { name: chalk.cyan(' → ') + chalk.bold('Standard (~/.wasibase/backups/)'), value: 'default' },
76
+ { name: chalk.cyan(' → ') + chalk.bold('Eigener Pfad angeben'), value: 'custom' },
77
+ { name: chalk.gray(' < Zurueck'), value: 'back' }
78
+ ]
79
+ }]);
80
+
81
+ if (location === 'back') return backupMenu();
82
+
83
+ let filePath = defaultPath;
84
+
85
+ if (location === 'custom') {
86
+ const { customPath } = await inquirer.prompt([{
87
+ type: 'input',
88
+ name: 'customPath',
89
+ message: chalk.green('Pfad (mit Dateiname):'),
90
+ prefix: chalk.green('+'),
91
+ default: path.join(process.env.HOME, 'Downloads', defaultName)
92
+ }]);
93
+
94
+ if (!customPath.trim()) return backupMenu();
95
+ filePath = customPath.trim();
96
+
97
+ if (!filePath.endsWith('.json')) {
98
+ filePath = path.join(filePath, defaultName);
99
+ }
100
+ }
101
+
102
+ console.log('');
103
+ console.log(chalk.gray(' Erstelle Backup...'));
104
+
105
+ try {
106
+ const noteCount = storage.saveBackupToFile(filePath);
107
+ const stats = fs.statSync(filePath);
108
+
109
+ console.log('');
110
+ console.log(chalk.green.bold(' ✓ Backup erstellt!'));
111
+ console.log('');
112
+ console.log(chalk.gray(' Datei: ') + chalk.bold(filePath));
113
+ console.log(chalk.gray(' Notes: ') + chalk.bold(noteCount));
114
+ console.log(chalk.gray(' Groesse: ') + chalk.bold(formatSize(stats.size)));
115
+ console.log('');
116
+ } catch (err) {
117
+ console.log(chalk.red(`\n ✕ Fehler: ${err.message}\n`));
118
+ }
119
+
120
+ await inquirer.prompt([{
121
+ type: 'input',
122
+ name: 'continue',
123
+ message: chalk.gray('Enter zum Fortfahren...'),
124
+ prefix: ''
125
+ }]);
126
+
127
+ return backupMenu();
128
+ }
129
+
130
+ async function restoreBackup() {
131
+ clear();
132
+
133
+ storage.ensureDir(CONFIG.backupDir);
134
+ const backupFiles = fs.existsSync(CONFIG.backupDir)
135
+ ? fs.readdirSync(CONFIG.backupDir).filter(f => f.endsWith('.json')).sort().reverse()
136
+ : [];
137
+
138
+ if (backupFiles.length === 0) {
139
+ console.log(chalk.yellow(' Keine Backups gefunden.\n'));
140
+ console.log(chalk.gray(' Du kannst einen eigenen Pfad angeben.\n'));
141
+ } else {
142
+ console.log(chalk.gray(` ${backupFiles.length} Backup(s) gefunden\n`));
143
+ }
144
+
145
+ const choices = [
146
+ ...backupFiles.slice(0, 10).map(f => {
147
+ const filePath = path.join(CONFIG.backupDir, f);
148
+ const stats = fs.statSync(filePath);
149
+ return {
150
+ name: chalk.cyan(' → ') + chalk.bold(f) + chalk.gray(` (${formatSize(stats.size)})`),
151
+ value: filePath,
152
+ short: f
153
+ };
154
+ }),
155
+ { name: chalk.yellow(' ? ') + chalk.yellow('Eigenen Pfad angeben'), value: 'custom' },
156
+ { name: chalk.gray(' < Zurueck'), value: 'back' }
157
+ ];
158
+
159
+ if (backupFiles.length > 0) {
160
+ choices.splice(backupFiles.slice(0, 10).length, 0, new inquirer.Separator(chalk.gray('─'.repeat(40))));
161
+ }
162
+
163
+ const { selection } = await inquirer.prompt([{
164
+ type: 'list',
165
+ name: 'selection',
166
+ message: chalk.blue('Backup auswaehlen'),
167
+ prefix: chalk.blue('◆'),
168
+ choices,
169
+ pageSize: 15
170
+ }]);
171
+
172
+ if (selection === 'back') return backupMenu();
173
+
174
+ let filePath = selection;
175
+
176
+ if (selection === 'custom') {
177
+ const { customPath } = await inquirer.prompt([{
178
+ type: 'input',
179
+ name: 'customPath',
180
+ message: chalk.green('Pfad zur Backup-Datei:'),
181
+ prefix: chalk.green('?')
182
+ }]);
183
+
184
+ if (!customPath.trim()) return restoreBackup();
185
+ filePath = customPath.trim();
186
+ }
187
+
188
+ if (!fs.existsSync(filePath)) {
189
+ console.log(chalk.red(`\n ✕ Datei nicht gefunden: ${filePath}\n`));
190
+ await inquirer.prompt([{
191
+ type: 'input',
192
+ name: 'continue',
193
+ message: chalk.gray('Enter zum Fortfahren...'),
194
+ prefix: ''
195
+ }]);
196
+ return restoreBackup();
197
+ }
198
+
199
+ try {
200
+ const backup = storage.loadBackupFromFile(filePath);
201
+
202
+ if (!backup || !backup.notes) {
203
+ console.log(chalk.red('\n ✕ Ungueltige Backup-Datei\n'));
204
+ await inquirer.prompt([{
205
+ type: 'input',
206
+ name: 'continue',
207
+ message: chalk.gray('Enter zum Fortfahren...'),
208
+ prefix: ''
209
+ }]);
210
+ return restoreBackup();
211
+ }
212
+
213
+ console.log('');
214
+ console.log(chalk.yellow(' Backup-Info:'));
215
+ console.log(chalk.gray(' Erstellt: ') + chalk.bold(backup.created || 'Unbekannt'));
216
+ console.log(chalk.gray(' Notes: ') + chalk.bold(backup.notes.length));
217
+ console.log('');
218
+
219
+ const { confirm } = await inquirer.prompt([{
220
+ type: 'confirm',
221
+ name: 'confirm',
222
+ message: chalk.yellow('Wiederherstellen? (Ueberschreibt bestehende Notes)'),
223
+ default: false
224
+ }]);
225
+
226
+ if (!confirm) return backupMenu();
227
+
228
+ console.log('');
229
+ console.log(chalk.gray(' Stelle wieder her...'));
230
+
231
+ const restored = storage.restoreFromBackup(backup);
232
+
233
+ console.log('');
234
+ console.log(chalk.green.bold(' ✓ Wiederhergestellt!'));
235
+ console.log(chalk.gray(` ${restored} Notes wurden importiert.\n`));
236
+ } catch (err) {
237
+ console.log(chalk.red(`\n ✕ Fehler: ${err.message}\n`));
238
+ }
239
+
240
+ await inquirer.prompt([{
241
+ type: 'input',
242
+ name: 'continue',
243
+ message: chalk.gray('Enter zum Fortfahren...'),
244
+ prefix: ''
245
+ }]);
246
+
247
+ return backupMenu();
248
+ }