@noobdemon/noob-cli 1.0.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/src/ui.js ADDED
@@ -0,0 +1,115 @@
1
+ import chalk from "chalk";
2
+ import gradient from "gradient-string";
3
+ import boxen from "boxen";
4
+ import { highlight, supportsLanguage } from "cli-highlight";
5
+ import { PROVIDERS, providerColor } from "./models.js";
6
+ import { t } from "./i18n.js";
7
+
8
+ const BRAND = ["#a78bfa", "#3b82f6", "#06b6d4"];
9
+ export const brand = gradient(BRAND);
10
+
11
+ export const c = {
12
+ dim: chalk.hex("#6b7280"),
13
+ user: chalk.hex("#a78bfa"),
14
+ ai: chalk.hex("#06b6d4"),
15
+ tool: chalk.hex("#f59e0b"),
16
+ ok: chalk.hex("#10b981"),
17
+ err: chalk.hex("#ef4444"),
18
+ accent: chalk.hex("#3b82f6"),
19
+ };
20
+
21
+ const term = () => process.stdout.columns || 80;
22
+
23
+ export function banner() {
24
+ const art = [
25
+ " ███╗ ██╗ ██████╗ ██████╗ ██████╗ ",
26
+ " ████╗ ██║██╔═══██╗██╔═══██╗██╔══██╗",
27
+ " ██╔██╗ ██║██║ ██║██║ ██║██████╔╝",
28
+ " ██║╚██╗██║██║ ██║██║ ██║██╔══██╗",
29
+ " ██║ ╚████║╚██████╔╝╚██████╔╝██████╔╝",
30
+ " ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ",
31
+ ].join("\n");
32
+ console.log("\n" + brand.multiline(art));
33
+ console.log(c.dim(" ") + brand("Noob Demon") + c.dim(" · " + t.tagline + "\n"));
34
+ }
35
+
36
+ export function rule(label = "") {
37
+ const w = Math.min(term(), 100);
38
+ if (!label) return c.dim("─".repeat(w));
39
+ const head = `── ${label} `;
40
+ return c.dim(head + "─".repeat(Math.max(0, w - head.length)));
41
+ }
42
+
43
+ export function modelBadge(model) {
44
+ const color = providerColor(model.provider);
45
+ const prov = PROVIDERS[model.provider]?.name || model.provider;
46
+ return chalk.hex(color)("●") + " " + chalk.bold(model.name) + c.dim(` (${prov} · ${model.tier})`);
47
+ }
48
+
49
+ // ── Markdown → ANSI ────────────────────────────────────────────────────────
50
+ function inline(s) {
51
+ return s
52
+ .replace(/\*\*(.+?)\*\*/g, (_, t) => chalk.bold(t))
53
+ .replace(/(^|[^*])\*(?!\*)(.+?)\*(?!\*)/g, (_, p, t) => p + chalk.italic(t))
54
+ .replace(/`([^`]+)`/g, (_, t) => chalk.hex("#f59e0b")(t))
55
+ .replace(/\[(.+?)\]\((.+?)\)/g, (_, t, u) => chalk.underline.cyan(t) + c.dim(` (${u})`));
56
+ }
57
+
58
+ function renderCode(code, lang) {
59
+ let body = code.replace(/\n$/, "");
60
+ try {
61
+ if (lang && supportsLanguage(lang)) body = highlight(body, { language: lang });
62
+ else body = highlight(body, { ignoreIllegals: true });
63
+ } catch {
64
+ /* fall through to raw */
65
+ }
66
+ const label = c.dim((lang || "code") + " ▸ ");
67
+ const lines = body.split("\n").map((l) => c.dim("│ ") + l);
68
+ return "\n" + label + "\n" + lines.join("\n") + "\n";
69
+ }
70
+
71
+ export function renderMarkdown(md) {
72
+ const out = [];
73
+ const lines = md.split("\n");
74
+ let i = 0;
75
+ while (i < lines.length) {
76
+ const line = lines[i];
77
+ const fence = line.match(/^```(\w*)\s*$/);
78
+ if (fence) {
79
+ const lang = fence[1];
80
+ const buf = [];
81
+ i++;
82
+ while (i < lines.length && !/^```\s*$/.test(lines[i])) buf.push(lines[i++]);
83
+ i++; // skip closing fence
84
+ out.push(renderCode(buf.join("\n"), lang));
85
+ continue;
86
+ }
87
+ let m;
88
+ if ((m = line.match(/^(#{1,6})\s+(.*)$/))) {
89
+ out.push(brand(chalk.bold(m[2])));
90
+ } else if (/^\s*[-*+]\s+/.test(line)) {
91
+ out.push(line.replace(/^(\s*)[-*+]\s+/, (_, sp) => sp + c.accent(" • ")) .replace(/^(\s*\S+\s)(.*)$/, (_, pre, rest) => pre + inline(rest)));
92
+ } else if ((m = line.match(/^(\s*)(\d+)\.\s+(.*)$/))) {
93
+ out.push(m[1] + c.accent(` ${m[2]}. `) + inline(m[3]));
94
+ } else if (/^\s*>\s?/.test(line)) {
95
+ out.push(c.dim("┃ ") + c.dim(inline(line.replace(/^\s*>\s?/, ""))));
96
+ } else if (/^\s*([-*_])\1{2,}\s*$/.test(line)) {
97
+ out.push(rule());
98
+ } else {
99
+ out.push(inline(line));
100
+ }
101
+ i++;
102
+ }
103
+ return out.join("\n");
104
+ }
105
+
106
+ export function box(content, title, color = "#a78bfa") {
107
+ return boxen(content, {
108
+ title,
109
+ titleAlignment: "left",
110
+ padding: { top: 0, bottom: 0, left: 1, right: 1 },
111
+ borderColor: color,
112
+ borderStyle: "round",
113
+ width: Math.min(term(), 100),
114
+ });
115
+ }