natureco-cli 2.23.32 → 4.4.1
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/AUDIT.md +178 -0
- package/CHANGELOG.md +422 -0
- package/DEPLOY_v2.0.0.md +400 -0
- package/README.md +159 -1
- package/bin/natureco.js +170 -8
- package/package.json +43 -11
- package/skills/code-review/SKILL.md +0 -0
- package/skills/summarize/SKILL.md +0 -0
- package/skills/translate/SKILL.md +0 -0
- package/src/commands/acp.js +0 -0
- package/src/commands/admin-rpc.js +0 -0
- package/src/commands/agent.js +0 -0
- package/src/commands/agents.js +0 -0
- package/src/commands/approvals.js +0 -0
- package/src/commands/ask.js +0 -0
- package/src/commands/audit.js +209 -0
- package/src/commands/backup.js +0 -0
- package/src/commands/bonjour.js +0 -0
- package/src/commands/bots.js +0 -0
- package/src/commands/browser.js +0 -0
- package/src/commands/capability.js +0 -0
- package/src/commands/channels.js +0 -0
- package/src/commands/chat.js +0 -0
- package/src/commands/clawbot.js +0 -0
- package/src/commands/clickclack.js +0 -0
- package/src/commands/code.js +0 -0
- package/src/commands/commands.js +0 -0
- package/src/commands/commitments.js +0 -0
- package/src/commands/completion.js +0 -0
- package/src/commands/config.js +0 -0
- package/src/commands/configure.js +0 -0
- package/src/commands/cost.js +210 -0
- package/src/commands/crestodian.js +0 -0
- package/src/commands/cron.js +0 -0
- package/src/commands/daemon.js +0 -0
- package/src/commands/dashboard.js +126 -45
- package/src/commands/device-pair.js +0 -0
- package/src/commands/devices.js +0 -0
- package/src/commands/directory.js +0 -0
- package/src/commands/discord.js +0 -0
- package/src/commands/dns.js +0 -0
- package/src/commands/docs.js +0 -0
- package/src/commands/doctor.js +134 -15
- package/src/commands/exec-policy.js +0 -0
- package/src/commands/gateway-server.js +0 -0
- package/src/commands/gateway.js +0 -0
- package/src/commands/git.js +0 -0
- package/src/commands/health.js +0 -0
- package/src/commands/help.js +0 -0
- package/src/commands/hooks.js +0 -0
- package/src/commands/imessage.js +0 -0
- package/src/commands/infer.js +0 -0
- package/src/commands/init.js +0 -0
- package/src/commands/irc.js +0 -0
- package/src/commands/login.js +0 -0
- package/src/commands/logout.js +0 -0
- package/src/commands/logs.js +0 -0
- package/src/commands/mattermost.js +0 -0
- package/src/commands/mcp.js +0 -0
- package/src/commands/medium.js +206 -0
- package/src/commands/memory-cmd.js +0 -0
- package/src/commands/memory.js +0 -0
- package/src/commands/message.js +0 -0
- package/src/commands/migrate.js +0 -0
- package/src/commands/models.js +0 -0
- package/src/commands/naturehub.js +156 -0
- package/src/commands/node.js +0 -0
- package/src/commands/nodes.js +0 -0
- package/src/commands/oc-path.js +0 -0
- package/src/commands/onboard.js +0 -0
- package/src/commands/open-prose.js +0 -0
- package/src/commands/pairing.js +0 -0
- package/src/commands/path.js +0 -0
- package/src/commands/plugins.js +0 -0
- package/src/commands/policy.js +0 -0
- package/src/commands/proxy.js +0 -0
- package/src/commands/qr.js +0 -0
- package/src/commands/reset.js +0 -0
- package/src/commands/run.js +0 -0
- package/src/commands/sandbox.js +0 -0
- package/src/commands/secrets.js +0 -0
- package/src/commands/security.js +0 -0
- package/src/commands/seo.js +268 -0
- package/src/commands/sessions.js +0 -0
- package/src/commands/setup.js +13 -6
- package/src/commands/signal.js +0 -0
- package/src/commands/skills.js +82 -1
- package/src/commands/slack.js +0 -0
- package/src/commands/sms.js +0 -0
- package/src/commands/status.js +0 -0
- package/src/commands/system.js +0 -0
- package/src/commands/tasks.js +0 -0
- package/src/commands/team.js +171 -0
- package/src/commands/telegram.js +0 -0
- package/src/commands/terminal.js +0 -0
- package/src/commands/thread-ownership.js +0 -0
- package/src/commands/transcripts.js +0 -0
- package/src/commands/tui.js +0 -0
- package/src/commands/ultrareview.js +0 -0
- package/src/commands/uninstall.js +0 -0
- package/src/commands/update.js +0 -0
- package/src/commands/voice.js +0 -0
- package/src/commands/vydra.js +0 -0
- package/src/commands/web-fetch.js +0 -0
- package/src/commands/webhooks.js +0 -0
- package/src/commands/whatsapp.js +0 -0
- package/src/commands/wiki.js +0 -0
- package/src/commands/workboard.js +0 -0
- package/src/commands/xp.js +194 -0
- package/src/tools/audio_understanding.js +0 -0
- package/src/tools/bash.js +0 -0
- package/src/tools/browser.js +0 -0
- package/src/tools/canvas.js +0 -0
- package/src/tools/document_extract.js +0 -0
- package/src/tools/duckduckgo.js +0 -0
- package/src/tools/exa_search.js +0 -0
- package/src/tools/filesystem.js +0 -0
- package/src/tools/firecrawl.js +0 -0
- package/src/tools/git.js +0 -0
- package/src/tools/http.js +0 -0
- package/src/tools/image_generation.js +0 -0
- package/src/tools/list_dir.js +0 -0
- package/src/tools/llm_task.js +43 -11
- package/src/tools/media_understanding.js +0 -0
- package/src/tools/music_generation.js +0 -0
- package/src/tools/parallel_search.js +0 -0
- package/src/tools/phone_control.js +0 -0
- package/src/tools/phone_control_enhanced.js +0 -0
- package/src/tools/read_file.js +0 -0
- package/src/tools/searxng.js +0 -0
- package/src/tools/speech_to_text.js +0 -0
- package/src/tools/text_to_speech.js +0 -0
- package/src/tools/thread_ownership.js +0 -0
- package/src/tools/video_generation.js +0 -0
- package/src/tools/web_readability.js +0 -0
- package/src/tools/web_search.js +0 -0
- package/src/tools/write_file.js +0 -0
- package/src/utils/agents-md.js +0 -0
- package/src/utils/agents.js +0 -0
- package/src/utils/api.js +5 -1
- package/src/utils/approvals.js +0 -0
- package/src/utils/audit.js +199 -0
- package/src/utils/background.js +0 -0
- package/src/utils/baileys.js +0 -0
- package/src/utils/branding.js +136 -0
- package/src/utils/commands.js +0 -0
- package/src/utils/config.js +0 -0
- package/src/utils/cost-tracker.js +360 -0
- package/src/utils/cron.js +0 -0
- package/src/utils/dashboard-server.js +284 -0
- package/src/utils/errors.js +0 -0
- package/src/utils/format.js +7 -10
- package/src/utils/gateway-ws.js +0 -0
- package/src/utils/headless.js +0 -0
- package/src/utils/history.js +0 -0
- package/src/utils/hooks.js +0 -0
- package/src/utils/inquirer-wrapper.js +0 -0
- package/src/utils/mcp-client.js +0 -0
- package/src/utils/mcp.js +0 -0
- package/src/utils/memory.js +0 -0
- package/src/utils/parallel-tools.js +0 -0
- package/src/utils/path-utils.js +0 -0
- package/src/utils/pattern-detector.js +314 -0
- package/src/utils/plugin-registry.js +0 -0
- package/src/utils/secret-scanner.js +204 -0
- package/src/utils/secrets.js +0 -0
- package/src/utils/sessions.js +0 -0
- package/src/utils/skills.js +0 -0
- package/src/utils/sub-agent.js +6 -0
- package/src/utils/token-budget.js +0 -0
- package/src/utils/tool-adapter.js +0 -0
- package/src/utils/tool-runner.js +0 -0
- package/src/utils/tui.js +750 -0
- package/src/utils/web-fetch.js +0 -0
package/src/utils/tui.js
ADDED
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NatureCo CLI — Terminal UI Engine (Phase 9 — "DESKTOP-LIKE")
|
|
3
|
+
*
|
|
4
|
+
* Hedef: Kullanıcı terminal açık olduğunu unutsun.
|
|
5
|
+
* PC uygulaması hissi için: animasyonlar, progress, interaktif box'lar,
|
|
6
|
+
* keyboard shortcuts, tab navigation, breadcrumb, status bar, theme.
|
|
7
|
+
*
|
|
8
|
+
* Sıfır dependency — sadece ANSI escape kodları + readline.
|
|
9
|
+
* Ink/blessed kurmuyoruz (bundle boyutu, startup hızı için).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// ════════════════════════════════════════════════════════════
|
|
13
|
+
// ANSI Escape Sequences
|
|
14
|
+
// ════════════════════════════════════════════════════════════
|
|
15
|
+
|
|
16
|
+
const ESC = '\x1b';
|
|
17
|
+
const CSI = ESC + '[';
|
|
18
|
+
|
|
19
|
+
// Cursor
|
|
20
|
+
const CURSOR = {
|
|
21
|
+
hide: CSI + '?25l',
|
|
22
|
+
show: CSI + '?25h',
|
|
23
|
+
save: ESC + '7',
|
|
24
|
+
restore: ESC + '8',
|
|
25
|
+
home: CSI + 'H',
|
|
26
|
+
pos: (row, col) => CSI + `${row};${col}H`,
|
|
27
|
+
up: (n = 1) => CSI + n + 'A',
|
|
28
|
+
down: (n = 1) => CSI + n + 'B',
|
|
29
|
+
right: (n = 1) => CSI + n + 'C',
|
|
30
|
+
left: (n = 1) => CSI + n + 'D',
|
|
31
|
+
nextLine: CSI + '1E',
|
|
32
|
+
prevLine: CSI + '1F',
|
|
33
|
+
clearLine: CSI + '2K',
|
|
34
|
+
clearLineRight: CSI + '0K',
|
|
35
|
+
clearScreen: CSI + '2J',
|
|
36
|
+
clearScreenDown: CSI + '0J',
|
|
37
|
+
clearScreenUp: CSI + '1J',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Screen modes
|
|
41
|
+
const SCREEN = {
|
|
42
|
+
altEnter: CSI + '?1049h', // Alternate screen buffer
|
|
43
|
+
altExit: CSI + '?1049l',
|
|
44
|
+
rawEnable: () => process.stdin.setRawMode && process.stdin.setRawMode(true),
|
|
45
|
+
rawDisable: () => process.stdin.setRawMode && process.stdin.setRawMode(false),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Mouse (gerekirse)
|
|
49
|
+
const MOUSE = {
|
|
50
|
+
enable: CSI + '?1000h' + CSI + '?1006h',
|
|
51
|
+
disable: CSI + '?1006l' + CSI + '?1000l',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ════════════════════════════════════════════════════════════
|
|
55
|
+
// Capability Detection
|
|
56
|
+
// ════════════════════════════════════════════════════════════
|
|
57
|
+
|
|
58
|
+
const CAPS = {
|
|
59
|
+
color: false,
|
|
60
|
+
trueColor: false,
|
|
61
|
+
unicode: false,
|
|
62
|
+
width: 80,
|
|
63
|
+
height: 24,
|
|
64
|
+
isTTY: false,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function detectCapabilities() {
|
|
68
|
+
CAPS.isTTY = !!process.stdout.isTTY;
|
|
69
|
+
|
|
70
|
+
// Renk desteği
|
|
71
|
+
if (process.env.NO_COLOR || process.env.CI) {
|
|
72
|
+
CAPS.color = false;
|
|
73
|
+
CAPS.trueColor = false;
|
|
74
|
+
} else if (process.env.FORCE_COLOR === '0') {
|
|
75
|
+
CAPS.color = false;
|
|
76
|
+
} else {
|
|
77
|
+
const term = (process.env.TERM || '').toLowerCase();
|
|
78
|
+
CAPS.color = CAPS.isTTY && !term.includes('dumb');
|
|
79
|
+
CAPS.trueColor = CAPS.isTTY && (
|
|
80
|
+
process.env.COLORTERM === 'truecolor' ||
|
|
81
|
+
process.env.COLORTERM === '24bit' ||
|
|
82
|
+
term.includes('256color') ||
|
|
83
|
+
term.includes('truecolor')
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Unicode desteği
|
|
88
|
+
const lang = process.env.LANG || process.env.LC_ALL || '';
|
|
89
|
+
CAPS.unicode = !/^(C|POSIX)/i.test(lang);
|
|
90
|
+
|
|
91
|
+
// Terminal boyutu
|
|
92
|
+
if (CAPS.isTTY && process.stdout.columns) {
|
|
93
|
+
CAPS.width = process.stdout.columns;
|
|
94
|
+
CAPS.height = process.stdout.rows;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return CAPS;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ════════════════════════════════════════════════════════════
|
|
101
|
+
// Color Palette — NatureCo Brand
|
|
102
|
+
// ════════════════════════════════════════════════════════════
|
|
103
|
+
|
|
104
|
+
const PALETTE = {
|
|
105
|
+
// Brand renkleri
|
|
106
|
+
primary: '#22c55e', // NatureCo yeşil
|
|
107
|
+
secondary: '#0ea5e9', // sky blue
|
|
108
|
+
accent: '#f59e0b', // amber
|
|
109
|
+
success: '#10b981',
|
|
110
|
+
warning: '#eab308',
|
|
111
|
+
danger: '#ef4444',
|
|
112
|
+
info: '#3b82f6',
|
|
113
|
+
muted: '#64748b',
|
|
114
|
+
text: '#e2e8f0',
|
|
115
|
+
bg: '#0f172a',
|
|
116
|
+
bgAlt: '#1e293b',
|
|
117
|
+
border: '#334155',
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const STYLE = {
|
|
121
|
+
reset: '\x1b[0m',
|
|
122
|
+
bold: '\x1b[1m',
|
|
123
|
+
dim: '\x1b[2m',
|
|
124
|
+
italic: '\x1b[3m',
|
|
125
|
+
underline: '\x1b[4m',
|
|
126
|
+
blink: '\x1b[5m',
|
|
127
|
+
reverse: '\x1b[7m',
|
|
128
|
+
hidden: '\x1b[8m',
|
|
129
|
+
strikethrough: '\x1b[9m',
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// True color helper
|
|
133
|
+
function fg(hex) {
|
|
134
|
+
if (!CAPS.trueColor) return '';
|
|
135
|
+
const [r, g, b] = hexToRgb(hex);
|
|
136
|
+
return `\x1b[38;2;${r};${g};${b}m`;
|
|
137
|
+
}
|
|
138
|
+
function bg(hex) {
|
|
139
|
+
if (!CAPS.trueColor) return '';
|
|
140
|
+
const [r, g, b] = hexToRgb(hex);
|
|
141
|
+
return `\x1b[48;2;${r};${g};${b}m`;
|
|
142
|
+
}
|
|
143
|
+
function hexToRgb(hex) {
|
|
144
|
+
const h = hex.replace('#', '');
|
|
145
|
+
return [
|
|
146
|
+
parseInt(h.slice(0, 2), 16),
|
|
147
|
+
parseInt(h.slice(2, 4), 16),
|
|
148
|
+
parseInt(h.slice(4, 6), 16),
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 256-color fallback
|
|
153
|
+
function fg256(n) { return CAPS.color ? `\x1b[38;5;${n}m` : ''; }
|
|
154
|
+
function bg256(n) { return CAPS.color ? `\x1b[48;5;${n}m` : ''; }
|
|
155
|
+
|
|
156
|
+
// Styled text helper
|
|
157
|
+
function styled(text, opts = {}) {
|
|
158
|
+
if (!CAPS.color && !CAPS.trueColor) return text;
|
|
159
|
+
let code = '';
|
|
160
|
+
if (opts.color) code += typeof opts.color === 'string' && opts.color.startsWith('#') ? fg(opts.color) : fg256(opts.color);
|
|
161
|
+
if (opts.bg) code += typeof opts.bg === 'string' && opts.bg.startsWith('#') ? bg(opts.bg) : bg256(opts.bg);
|
|
162
|
+
if (opts.bold) code += STYLE.bold;
|
|
163
|
+
if (opts.dim) code += STYLE.dim;
|
|
164
|
+
if (opts.italic) code += STYLE.italic;
|
|
165
|
+
if (opts.underline) code += STYLE.underline;
|
|
166
|
+
return `${code}${text}${STYLE.reset}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Renkli kısayollar
|
|
170
|
+
const C = {
|
|
171
|
+
brand: (t) => styled(t, { color: PALETTE.primary, bold: true }),
|
|
172
|
+
sky: (t) => styled(t, { color: PALETTE.secondary }),
|
|
173
|
+
amber: (t) => styled(t, { color: PALETTE.accent }),
|
|
174
|
+
green: (t) => styled(t, { color: PALETTE.success }),
|
|
175
|
+
red: (t) => styled(t, { color: PALETTE.danger }),
|
|
176
|
+
yellow: (t) => styled(t, { color: PALETTE.warning }),
|
|
177
|
+
blue: (t) => styled(t, { color: PALETTE.info }),
|
|
178
|
+
muted: (t) => styled(t, { color: PALETTE.muted }),
|
|
179
|
+
text: (t) => styled(t, { color: PALETTE.text }),
|
|
180
|
+
dim: (t) => styled(t, { dim: true }),
|
|
181
|
+
bold: (t) => styled(t, { bold: true }),
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// ════════════════════════════════════════════════════════════
|
|
185
|
+
// Box Drawing — Unicode borders
|
|
186
|
+
// ════════════════════════════════════════════════════════════
|
|
187
|
+
|
|
188
|
+
const BORDER = {
|
|
189
|
+
single: { tl: '┌', tr: '┐', bl: '└', br: '┘', h: '─', v: '│', tDown: '┬', tUp: '┴', tRight: '├', tLeft: '┤', cross: '┼' },
|
|
190
|
+
double: { tl: '╔', tr: '╗', bl: '╚', br: '╝', h: '═', v: '║', tDown: '╦', tUp: '╩', tRight: '╠', tLeft: '╣', cross: '╬' },
|
|
191
|
+
round: { tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│', tDown: '┬', tUp: '┴', tRight: '├', tLeft: '┤', cross: '┼' },
|
|
192
|
+
heavy: { tl: '┏', tr: '┓', bl: '┗', br: '┛', h: '━', v: '┃', tDown: '┳', tUp: '┻', tRight: '┣', tLeft: '┫', cross: '╋' },
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
function box(width, height, options = {}) {
|
|
196
|
+
const {
|
|
197
|
+
style = 'round',
|
|
198
|
+
title = '',
|
|
199
|
+
titleColor = PALETTE.primary,
|
|
200
|
+
borderColor = PALETTE.border,
|
|
201
|
+
bg: bgColor = null,
|
|
202
|
+
padding = 1,
|
|
203
|
+
} = options;
|
|
204
|
+
|
|
205
|
+
const b = BORDER[style] || BORDER.round;
|
|
206
|
+
const w = Math.max(10, width);
|
|
207
|
+
const h = Math.max(3, height);
|
|
208
|
+
|
|
209
|
+
const bc = CAPS.trueColor ? fg(borderColor) : '';
|
|
210
|
+
const reset = STYLE.reset;
|
|
211
|
+
const tColor = CAPS.trueColor ? fg(titleColor) : '';
|
|
212
|
+
|
|
213
|
+
const lines = [];
|
|
214
|
+
// Üst kenar
|
|
215
|
+
const topTitle = title ? ` ${title} ` : '';
|
|
216
|
+
const topFill = w - 2 - topTitle.length;
|
|
217
|
+
const leftFill = Math.floor(topFill / 2);
|
|
218
|
+
const rightFill = topFill - leftFill;
|
|
219
|
+
lines.push(bc + b.tl + b.h.repeat(leftFill) + tColor + topTitle + bc + b.h.repeat(rightFill) + b.tr + reset);
|
|
220
|
+
|
|
221
|
+
// İçeride boş satırlar (doldurulacak)
|
|
222
|
+
for (let i = 1; i < h - 1; i++) {
|
|
223
|
+
lines.push(bc + b.v + reset + ' '.repeat(w - 2) + bc + b.v + reset);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Alt kenar
|
|
227
|
+
lines.push(bc + b.bl + b.h.repeat(w - 2) + b.br + reset);
|
|
228
|
+
|
|
229
|
+
return lines.join('\n');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ════════════════════════════════════════════════════════════
|
|
233
|
+
// Progress Bar & Spinner
|
|
234
|
+
// ════════════════════════════════════════════════════════════
|
|
235
|
+
|
|
236
|
+
const SPINNER_FRAMES = {
|
|
237
|
+
dots: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
|
|
238
|
+
line: ['|', '/', '-', '\\'],
|
|
239
|
+
arc: ['◜', '◠', '◝', '◞', '◡', '◟'],
|
|
240
|
+
pulse: ['█', '▓', '▒', '░', '▒', '▓'],
|
|
241
|
+
bounce: ['⠁', '⠂', '⠄', '⠂'],
|
|
242
|
+
grow: ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃', '▂'],
|
|
243
|
+
circle: ['◐', '◓', '◑', '◒'],
|
|
244
|
+
arrow: ['←', '↖', '↑', '↗', '→', '↘', '↓', '↙'],
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
function progressBar(current, total, options = {}) {
|
|
248
|
+
const { width = 30, showPercent = true, showETA = false, fillChar = '█', emptyChar = '░' } = options;
|
|
249
|
+
const pct = Math.min(1, Math.max(0, current / total));
|
|
250
|
+
const filled = Math.floor(pct * width);
|
|
251
|
+
const empty = width - filled;
|
|
252
|
+
const bar = C.brand(fillChar.repeat(filled)) + C.muted(emptyChar.repeat(empty));
|
|
253
|
+
let suffix = '';
|
|
254
|
+
if (showPercent) suffix += ` ${(pct * 100).toFixed(0).padStart(3)}%`;
|
|
255
|
+
if (showETA && options.startTime) {
|
|
256
|
+
const elapsed = (Date.now() - options.startTime) / 1000;
|
|
257
|
+
const remaining = pct > 0 ? (elapsed / pct - elapsed) : 0;
|
|
258
|
+
suffix += ` ETA: ${formatDuration(remaining)}`;
|
|
259
|
+
}
|
|
260
|
+
return `${bar}${suffix}`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function formatDuration(seconds) {
|
|
264
|
+
if (!isFinite(seconds) || seconds < 0) return '--:--';
|
|
265
|
+
if (seconds < 60) return `${Math.round(seconds)}s`;
|
|
266
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
|
|
267
|
+
return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ════════════════════════════════════════════════════════════
|
|
271
|
+
// Animated Spinner — async/await ile
|
|
272
|
+
// ════════════════════════════════════════════════════════════
|
|
273
|
+
|
|
274
|
+
class Spinner {
|
|
275
|
+
constructor(text = '', options = {}) {
|
|
276
|
+
this.text = text;
|
|
277
|
+
this.style = options.style || 'dots';
|
|
278
|
+
this.frames = SPINNER_FRAMES[this.style] || SPINNER_FRAMES.dots;
|
|
279
|
+
this.interval = options.interval || 80;
|
|
280
|
+
this.timer = null;
|
|
281
|
+
this.index = 0;
|
|
282
|
+
}
|
|
283
|
+
start() {
|
|
284
|
+
if (!CAPS.isTTY) {
|
|
285
|
+
process.stdout.write(this.text + '... ');
|
|
286
|
+
return this;
|
|
287
|
+
}
|
|
288
|
+
process.stdout.write(CURSOR.hide);
|
|
289
|
+
this.timer = setInterval(() => {
|
|
290
|
+
const frame = C.brand(this.frames[this.index]) + ' ' + C.text(this.text);
|
|
291
|
+
process.stdout.write('\r' + CURSOR.clearLineRight + frame);
|
|
292
|
+
this.index = (this.index + 1) % this.frames.length;
|
|
293
|
+
}, this.interval);
|
|
294
|
+
return this;
|
|
295
|
+
}
|
|
296
|
+
stop(finalText = null) {
|
|
297
|
+
if (this.timer) clearInterval(this.timer);
|
|
298
|
+
if (CAPS.isTTY) {
|
|
299
|
+
process.stdout.write(CURSOR.show + '\r' + CURSOR.clearLineRight);
|
|
300
|
+
}
|
|
301
|
+
if (finalText) console.log(finalText);
|
|
302
|
+
return this;
|
|
303
|
+
}
|
|
304
|
+
update(text) {
|
|
305
|
+
this.text = text;
|
|
306
|
+
return this;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function withSpinner(text, fn, options = {}) {
|
|
311
|
+
const spinner = new Spinner(text, options).start();
|
|
312
|
+
try {
|
|
313
|
+
const result = await fn(spinner);
|
|
314
|
+
spinner.stop(options.successText || C.green('✓ ' + text));
|
|
315
|
+
return result;
|
|
316
|
+
} catch (err) {
|
|
317
|
+
spinner.stop(C.red('✗ ' + text) + C.red('\n ' + err.message));
|
|
318
|
+
throw err;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ════════════════════════════════════════════════════════════
|
|
323
|
+
// Banner / Splash Screen
|
|
324
|
+
// ════════════════════════════════════════════════════════════
|
|
325
|
+
|
|
326
|
+
function bigBanner(text, color = PALETTE.primary) {
|
|
327
|
+
// Banner font: minimal ASCII
|
|
328
|
+
const lines = text.split('\n');
|
|
329
|
+
const out = [];
|
|
330
|
+
for (const line of lines) {
|
|
331
|
+
out.push(C.text(line, { color }));
|
|
332
|
+
}
|
|
333
|
+
return out.join('\n');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Smooth fade-in effect (line by line)
|
|
337
|
+
async function fadeIn(lines, delay = 30) {
|
|
338
|
+
for (const line of lines) {
|
|
339
|
+
console.log(line);
|
|
340
|
+
if (CAPS.isTTY) await sleep(delay);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
345
|
+
|
|
346
|
+
// ════════════════════════════════════════════════════════════
|
|
347
|
+
// Status Bar (footer / header)
|
|
348
|
+
// ════════════════════════════════════════════════════════════
|
|
349
|
+
|
|
350
|
+
function statusBar(items, options = {}) {
|
|
351
|
+
// items: [{ label, value, color }]
|
|
352
|
+
const separator = C.muted(' │ ');
|
|
353
|
+
const parts = items.map(({ label, value, color }) => {
|
|
354
|
+
const l = C.muted(label);
|
|
355
|
+
const v = typeof color === 'string' ? styled(value, { color }) : value;
|
|
356
|
+
return `${l} ${v}`;
|
|
357
|
+
});
|
|
358
|
+
return parts.join(separator);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function breadcrumb(path) {
|
|
362
|
+
// path: ['home', 'naturehub', 'post']
|
|
363
|
+
return path.map((seg, i) => {
|
|
364
|
+
const isLast = i === path.length - 1;
|
|
365
|
+
return isLast ? C.brand(seg) : C.muted(seg);
|
|
366
|
+
}).join(C.muted(' › '));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ════════════════════════════════════════════════════════════
|
|
370
|
+
// Table — gelişmiş, border + color + alignment
|
|
371
|
+
// ════════════════════════════════════════════════════════════
|
|
372
|
+
|
|
373
|
+
function table(data, columns, options = {}) {
|
|
374
|
+
const reset = STYLE.reset;
|
|
375
|
+
const { borderStyle = 'single', headerColor = PALETTE.primary, zebra = true } = options;
|
|
376
|
+
const b = BORDER[borderStyle] || BORDER.single;
|
|
377
|
+
|
|
378
|
+
// Sütun genişliklerini hesapla
|
|
379
|
+
const widths = columns.map(col => {
|
|
380
|
+
const headerLen = stripAnsi(col.label || col.key).length;
|
|
381
|
+
const maxDataLen = Math.max(0, ...data.map(row => {
|
|
382
|
+
const val = col.render ? col.render(row) : (row[col.key] || '');
|
|
383
|
+
return stripAnsi(String(val)).length;
|
|
384
|
+
}));
|
|
385
|
+
return Math.max(headerLen, maxDataLen, col.minWidth || 3);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
const lines = [];
|
|
389
|
+
const bc = CAPS.trueColor ? fg(PALETTE.border) : '';
|
|
390
|
+
|
|
391
|
+
// Üst
|
|
392
|
+
const topBorder = b.tl + b.h.repeat(widths.reduce((s, w) => s + w + 3, 1) - 1) + b.tr;
|
|
393
|
+
lines.push(bc + topBorder + STYLE.reset);
|
|
394
|
+
|
|
395
|
+
// Header
|
|
396
|
+
const headerCells = columns.map((col, i) => {
|
|
397
|
+
const text = (col.label || col.key).padEnd(widths[i]);
|
|
398
|
+
return styled(text, { color: headerColor, bold: true });
|
|
399
|
+
});
|
|
400
|
+
lines.push(bc + b.v + reset + ' ' + headerCells.join(' ' + bc + b.v + reset + ' ') + ' ' + bc + b.v + reset);
|
|
401
|
+
|
|
402
|
+
// Header alt border
|
|
403
|
+
const midBorder = b.tRight + widths.map(w => b.h.repeat(w + 2)).join(b.tDown) + b.tLeft;
|
|
404
|
+
lines.push(bc + midBorder + STYLE.reset);
|
|
405
|
+
|
|
406
|
+
// Veri satırları
|
|
407
|
+
data.forEach((row, ri) => {
|
|
408
|
+
const bgColor = zebra && ri % 2 === 1 ? bg(PALETTE.bgAlt) : '';
|
|
409
|
+
const cells = columns.map((col, i) => {
|
|
410
|
+
const val = col.render ? col.render(row) : (row[col.key] || '');
|
|
411
|
+
const text = String(val).padEnd(widths[i]);
|
|
412
|
+
return bgColor + text + STYLE.reset;
|
|
413
|
+
});
|
|
414
|
+
lines.push(bc + b.v + reset + ' ' + cells.join(' ' + bc + b.v + reset + ' ') + ' ' + bc + b.v + reset);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Alt
|
|
418
|
+
const botBorder = b.bl + b.h.repeat(widths.reduce((s, w) => s + w + 3, 1) - 1) + b.br;
|
|
419
|
+
lines.push(bc + botBorder + STYLE.reset);
|
|
420
|
+
|
|
421
|
+
return lines.join('\n');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function stripAnsi(str) {
|
|
425
|
+
return String(str).replace(/\x1b\[[0-9;]*m/g, '');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// ════════════════════════════════════════════════════════════
|
|
429
|
+
// Keyboard Input
|
|
430
|
+
// ════════════════════════════════════════════════════════════
|
|
431
|
+
|
|
432
|
+
function readKey() {
|
|
433
|
+
return new Promise((resolve) => {
|
|
434
|
+
if (!CAPS.isTTY) {
|
|
435
|
+
process.stdin.once('data', (chunk) => {
|
|
436
|
+
const key = chunk.toString();
|
|
437
|
+
resolve(parseKey(key));
|
|
438
|
+
});
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
SCREEN.rawEnable();
|
|
442
|
+
process.stdin.once('data', (chunk) => {
|
|
443
|
+
SCREEN.rawDisable();
|
|
444
|
+
resolve(parseKey(chunk.toString()));
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function parseKey(str) {
|
|
450
|
+
// Özel tuşlar
|
|
451
|
+
if (str === '\x1b[A' || str === '\x1bOA') return { type: 'arrow', direction: 'up' };
|
|
452
|
+
if (str === '\x1b[B' || str === '\x1bOB') return { type: 'arrow', direction: 'down' };
|
|
453
|
+
if (str === '\x1b[C' || str === '\x1bOC') return { type: 'arrow', direction: 'right' };
|
|
454
|
+
if (str === '\x1b[D' || str === '\x1bOD') return { type: 'arrow', direction: 'left' };
|
|
455
|
+
if (str === '\x1b[H') return { type: 'home' };
|
|
456
|
+
if (str === '\x1b[F') return { type: 'end' };
|
|
457
|
+
if (str === '\t') return { type: 'tab' };
|
|
458
|
+
if (str === '\r' || str === '\n') return { type: 'enter' };
|
|
459
|
+
if (str === '\x1b' || str === '\x1b\x1b') return { type: 'escape' };
|
|
460
|
+
if (str === '\x03') return { type: 'ctrl-c' };
|
|
461
|
+
if (str === '\x1b[5~') return { type: 'page-up' };
|
|
462
|
+
if (str === '\x1b[6~') return { type: 'page-down' };
|
|
463
|
+
// Normal karakter
|
|
464
|
+
if (str.length === 1) return { type: 'char', value: str };
|
|
465
|
+
return { type: 'unknown', raw: str };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// ════════════════════════════════════════════════════════════
|
|
469
|
+
// Help Overlay (kısayollar)
|
|
470
|
+
// ════════════════════════════════════════════════════════════
|
|
471
|
+
|
|
472
|
+
function helpOverlay(shortcuts) {
|
|
473
|
+
const lines = [C.bold(C.brand(' ⌨️ Klavye Kısayolları\n'))];
|
|
474
|
+
for (const [key, desc] of Object.entries(shortcuts)) {
|
|
475
|
+
lines.push(` ${styled(key.padEnd(12), { bg: PALETTE.bgAlt, color: PALETTE.text })} ${C.muted(desc)}`);
|
|
476
|
+
}
|
|
477
|
+
return lines.join('\n');
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ════════════════════════════════════════════════════════════
|
|
481
|
+
// Splash Screen — Animasyonlu açılış
|
|
482
|
+
// ════════════════════════════════════════════════════════════
|
|
483
|
+
|
|
484
|
+
async function splash(options = {}) {
|
|
485
|
+
const {
|
|
486
|
+
title = 'NatureCo CLI',
|
|
487
|
+
version = '4.2.0',
|
|
488
|
+
subtitle = 'OpenClaw\'dan daha güvenli, daha hızlı, daha ucuz',
|
|
489
|
+
duration = 1500,
|
|
490
|
+
} = options;
|
|
491
|
+
|
|
492
|
+
if (!CAPS.isTTY) {
|
|
493
|
+
console.log(`${title} v${version}`);
|
|
494
|
+
console.log(subtitle);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Logo satırları (ASCII art)
|
|
499
|
+
const logo = [
|
|
500
|
+
'███╗ ██╗ █████╗ ████████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ',
|
|
501
|
+
'████╗ ██║██╔══██╗╚══██╔══╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔═══██╗',
|
|
502
|
+
'██╔██╗ ██║███████║ ██║ ██║ ██║██████╔╝█████╗ ██║ ██║██║ ██║',
|
|
503
|
+
'██║╚██╗██║██╔══██║ ██║ ██║ ██║██╔══██╗██╔══╝ ██║ ██║██║ ██║',
|
|
504
|
+
'██║ ╚████║██║ ██║ ██║ ╚██████╔╝██║ ██║███████╗╚██████╔╝╚██████╔╝',
|
|
505
|
+
'╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ',
|
|
506
|
+
];
|
|
507
|
+
|
|
508
|
+
// Animasyon: satır satır fade-in
|
|
509
|
+
process.stdout.write('\n');
|
|
510
|
+
for (const line of logo) {
|
|
511
|
+
process.stdout.write(' ' + styled(line, { color: PALETTE.primary, bold: true }) + '\n');
|
|
512
|
+
await sleep(50);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Alt yazı fade-in
|
|
516
|
+
await sleep(200);
|
|
517
|
+
process.stdout.write('\n');
|
|
518
|
+
process.stdout.write(' ' + styled(title + ' v' + version, { color: PALETTE.text, bold: true }) + '\n');
|
|
519
|
+
process.stdout.write(' ' + styled(subtitle, { color: PALETTE.muted, italic: true }) + '\n');
|
|
520
|
+
process.stdout.write('\n');
|
|
521
|
+
|
|
522
|
+
// Loading dots animasyonu
|
|
523
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
524
|
+
const startTime = Date.now();
|
|
525
|
+
let i = 0;
|
|
526
|
+
while (Date.now() - startTime < duration) {
|
|
527
|
+
const elapsed = Math.floor((Date.now() - startTime) / 100);
|
|
528
|
+
process.stdout.write('\r ' + styled(frames[i % frames.length], { color: PALETTE.primary, bold: true }) + ' ' +
|
|
529
|
+
styled('Hazır', { color: PALETTE.muted }) +
|
|
530
|
+
' '.repeat(10));
|
|
531
|
+
i++;
|
|
532
|
+
await sleep(80);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Final clear
|
|
536
|
+
process.stdout.write('\r' + ' '.repeat(60) + '\r');
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// ════════════════════════════════════════════════════════════
|
|
540
|
+
// Welcome Card — PC uygulaması açılış hissi
|
|
541
|
+
// ════════════════════════════════════════════════════════════
|
|
542
|
+
|
|
543
|
+
function welcomeCard({ version, user, status, tips }) {
|
|
544
|
+
const w = Math.min(70, CAPS.width - 4);
|
|
545
|
+
const lines = [];
|
|
546
|
+
|
|
547
|
+
// Üst çerçeve
|
|
548
|
+
lines.push(C.brand('╭' + '─'.repeat(w - 2) + '╮'));
|
|
549
|
+
|
|
550
|
+
// Logo (küçük)
|
|
551
|
+
const logo = '🌿 NatureCo CLI';
|
|
552
|
+
const ver = `v${version}`;
|
|
553
|
+
const titlePadding = w - 2 - logo.length - ver.length - 2;
|
|
554
|
+
lines.push(C.brand('│ ') + C.bold(logo) + ' '.repeat(titlePadding) + C.muted(ver) + C.brand(' │'));
|
|
555
|
+
|
|
556
|
+
// Ayraç
|
|
557
|
+
lines.push(C.brand('├' + '─'.repeat(w - 2) + '┤'));
|
|
558
|
+
|
|
559
|
+
// Status satırları
|
|
560
|
+
const statusLines = [
|
|
561
|
+
['Kullanıcı', user?.name || C.amber('(setup gerekli)')],
|
|
562
|
+
['Sürüm', C.text(version)],
|
|
563
|
+
['Durum', status],
|
|
564
|
+
['Çalışma dizini', C.dim(process.cwd())],
|
|
565
|
+
];
|
|
566
|
+
for (const [label, value] of statusLines) {
|
|
567
|
+
const line = ` ${C.muted(label.padEnd(16))} ${value}`;
|
|
568
|
+
const padLen = w - 2 - stripAnsi(line).length;
|
|
569
|
+
lines.push(C.brand('│') + line + ' '.repeat(Math.max(0, padLen)) + C.brand('│'));
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Ayraç
|
|
573
|
+
lines.push(C.brand('├' + '─'.repeat(w - 2) + '┤'));
|
|
574
|
+
|
|
575
|
+
// Tip
|
|
576
|
+
const tipText = tips || '🌱 natureco code → Edit & refactor any file';
|
|
577
|
+
const tipLabel = '💡 İpucu';
|
|
578
|
+
const tipLine = ` ${C.amber(tipLabel)} ${C.dim(tipText)}`;
|
|
579
|
+
const tipPadLen = w - 2 - stripAnsi(tipLine).length;
|
|
580
|
+
lines.push(C.brand('│') + tipLine + ' '.repeat(Math.max(0, tipPadLen)) + C.brand('│'));
|
|
581
|
+
|
|
582
|
+
// Ayraç
|
|
583
|
+
lines.push(C.brand('├' + '─'.repeat(w - 2) + '┤'));
|
|
584
|
+
|
|
585
|
+
// Kısayollar
|
|
586
|
+
const shortcuts = [
|
|
587
|
+
['Tab', 'otomatik tamamlama'],
|
|
588
|
+
['↑↓', 'komut geçmişi'],
|
|
589
|
+
['Ctrl+C', 'güvenli çıkış'],
|
|
590
|
+
['? help', 'tüm komutlar'],
|
|
591
|
+
];
|
|
592
|
+
for (const [key, desc] of shortcuts) {
|
|
593
|
+
const k = styled(key.padEnd(8), { bg: PALETTE.bgAlt, color: PALETTE.text });
|
|
594
|
+
const line = ` ${k} ${C.muted(desc)}`;
|
|
595
|
+
const padLen = w - 2 - stripAnsi(line).length;
|
|
596
|
+
lines.push(C.brand('│') + line + ' '.repeat(Math.max(0, padLen)) + C.brand('│'));
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Alt çerçeve
|
|
600
|
+
lines.push(C.brand('╰' + '─'.repeat(w - 2) + '╯'));
|
|
601
|
+
|
|
602
|
+
return lines.join('\n');
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// ════════════════════════════════════════════════════════════
|
|
606
|
+
// Pretty Errors — Stack trace yerine güzel hata mesajı
|
|
607
|
+
// ════════════════════════════════════════════════════════════
|
|
608
|
+
|
|
609
|
+
function prettyError(err, options = {}) {
|
|
610
|
+
const { title = 'Bir hata oluştu', showStack = false, suggestion = null } = options;
|
|
611
|
+
const lines = [];
|
|
612
|
+
const w = Math.min(70, CAPS.width - 4);
|
|
613
|
+
|
|
614
|
+
lines.push(C.red('╭' + '─'.repeat(w - 2) + '╮'));
|
|
615
|
+
const titleIcon = '✗ ' + title;
|
|
616
|
+
const titlePad = w - 2 - titleIcon.length;
|
|
617
|
+
lines.push(C.red('│ ') + C.bold(C.red(titleIcon)) + ' '.repeat(Math.max(0, titlePad)) + C.red(' │'));
|
|
618
|
+
|
|
619
|
+
lines.push(C.red('├' + '─'.repeat(w - 2) + '┤'));
|
|
620
|
+
|
|
621
|
+
// Hata mesajı
|
|
622
|
+
const msg = err.message || String(err);
|
|
623
|
+
const wrapped = wrapText(msg, w - 6);
|
|
624
|
+
for (const line of wrapped) {
|
|
625
|
+
const padded = ' ' + line;
|
|
626
|
+
const padLen = w - 2 - padded.length;
|
|
627
|
+
lines.push(C.red('│') + padded + ' '.repeat(Math.max(0, padLen)) + C.red(' │'));
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (suggestion) {
|
|
631
|
+
lines.push(C.red('├' + '─'.repeat(w - 2) + '┤'));
|
|
632
|
+
const sugText = '💡 ' + suggestion;
|
|
633
|
+
const sugWrap = wrapText(sugText, w - 6);
|
|
634
|
+
for (const line of sugWrap) {
|
|
635
|
+
const padded = ' ' + styled(line, { color: PALETTE.warning });
|
|
636
|
+
const padLen = w - 2 - padded.length;
|
|
637
|
+
lines.push(C.red('│') + padded + ' '.repeat(Math.max(0, padLen)) + C.red(' │'));
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (showStack && err.stack) {
|
|
642
|
+
lines.push(C.red('├' + '─'.repeat(w - 2) + '┤'));
|
|
643
|
+
const stackLines = err.stack.split('\n').slice(0, 5);
|
|
644
|
+
for (const s of stackLines) {
|
|
645
|
+
const padded = ' ' + styled(s, { color: PALETTE.muted, dim: true });
|
|
646
|
+
const padLen = w - 2 - Math.min(stripAnsi(padded).length, w - 4);
|
|
647
|
+
lines.push(C.red('│') + padded.slice(0, w - 2) + ' '.repeat(Math.max(0, padLen)) + C.red(' │'));
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
lines.push(C.red('╰' + '─'.repeat(w - 2) + '╯'));
|
|
652
|
+
return lines.join('\n');
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function wrapText(text, width) {
|
|
656
|
+
const words = text.split(' ');
|
|
657
|
+
const lines = [];
|
|
658
|
+
let current = '';
|
|
659
|
+
for (const word of words) {
|
|
660
|
+
if ((current + ' ' + word).trim().length > width) {
|
|
661
|
+
if (current) lines.push(current);
|
|
662
|
+
current = word;
|
|
663
|
+
} else {
|
|
664
|
+
current = current ? current + ' ' + word : word;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (current) lines.push(current);
|
|
668
|
+
return lines;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// ════════════════════════════════════════════════════════════
|
|
672
|
+
// Notification — toast gibi sağ üstten (basit)
|
|
673
|
+
// ════════════════════════════════════════════════════════════
|
|
674
|
+
|
|
675
|
+
function notify(message, options = {}) {
|
|
676
|
+
const { type = 'info', duration = 3000 } = options;
|
|
677
|
+
const icons = { info: 'ℹ️', success: '✓', warning: '⚠️', error: '✗' };
|
|
678
|
+
const colors = { info: PALETTE.info, success: PALETTE.success, warning: PALETTE.warning, error: PALETTE.danger };
|
|
679
|
+
const icon = icons[type] || icons.info;
|
|
680
|
+
const color = colors[type] || colors.info;
|
|
681
|
+
const text = ` ${icon} ${message} `;
|
|
682
|
+
console.log('\n' + styled(text, { bg: color, color: '#000000', bold: true }));
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// ════════════════════════════════════════════════════════════
|
|
686
|
+
// Tree view — file/dir ağacı
|
|
687
|
+
// ════════════════════════════════════════════════════════════
|
|
688
|
+
|
|
689
|
+
function tree(items, options = {}) {
|
|
690
|
+
const { indent = ' ', expanded = {} } = options;
|
|
691
|
+
const lines = [];
|
|
692
|
+
|
|
693
|
+
function render(items, depth = 0) {
|
|
694
|
+
for (const item of items) {
|
|
695
|
+
const isLast = item === items[items.length - 1];
|
|
696
|
+
const prefix = depth === 0 ? '' : (indent.repeat(depth - 1) + (isLast ? '└─ ' : '├─ '));
|
|
697
|
+
const icon = item.icon || (item.children ? '📁' : '📄');
|
|
698
|
+
const name = item.name || item.label || '';
|
|
699
|
+
const color = item.children ? PALETTE.primary : PALETTE.text;
|
|
700
|
+
lines.push(prefix + icon + ' ' + styled(name, { color }));
|
|
701
|
+
if (item.children && (depth === 0 || expanded[item.name])) {
|
|
702
|
+
render(item.children, depth + 1);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
render(items);
|
|
708
|
+
return lines.join('\n');
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// ════════════════════════════════════════════════════════════
|
|
712
|
+
// Init
|
|
713
|
+
// ════════════════════════════════════════════════════════════
|
|
714
|
+
|
|
715
|
+
function init() {
|
|
716
|
+
detectCapabilities();
|
|
717
|
+
// Resize olunca güncelle
|
|
718
|
+
if (CAPS.isTTY) {
|
|
719
|
+
process.stdout.on('resize', () => {
|
|
720
|
+
CAPS.width = process.stdout.columns || 80;
|
|
721
|
+
CAPS.height = process.stdout.rows || 24;
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
module.exports = {
|
|
727
|
+
// Capabilities
|
|
728
|
+
CAPS, PALETTE, STYLE,
|
|
729
|
+
// ANSI primitives
|
|
730
|
+
CURSOR, SCREEN, MOUSE,
|
|
731
|
+
// Color helpers
|
|
732
|
+
fg, bg, fg256, bg256, styled, hexToRgb, C, stripAnsi,
|
|
733
|
+
// Borders & boxes
|
|
734
|
+
BORDER, box,
|
|
735
|
+
// Progress
|
|
736
|
+
SPINNER_FRAMES, Spinner, withSpinner, progressBar, formatDuration,
|
|
737
|
+
// Visual
|
|
738
|
+
bigBanner, fadeIn, splash, welcomeCard, table, statusBar, breadcrumb,
|
|
739
|
+
// Errors
|
|
740
|
+
prettyError,
|
|
741
|
+
// Notifications
|
|
742
|
+
notify,
|
|
743
|
+
// Tree
|
|
744
|
+
tree,
|
|
745
|
+
// Input
|
|
746
|
+
readKey, parseKey, helpOverlay,
|
|
747
|
+
// Init
|
|
748
|
+
init, detectCapabilities,
|
|
749
|
+
sleep,
|
|
750
|
+
};
|