@sheepbun/yips 0.1.1 → 0.1.46

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.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/bin/yips.js +15 -0
  4. package/package.json +21 -128
  5. package/postinstall.js +50 -0
  6. package/dist/agent/commands/command-catalog.js +0 -243
  7. package/dist/agent/commands/commands.js +0 -418
  8. package/dist/agent/conductor.js +0 -118
  9. package/dist/agent/context/code-context.js +0 -68
  10. package/dist/agent/context/memory-store.js +0 -159
  11. package/dist/agent/context/session-store.js +0 -211
  12. package/dist/agent/protocol/tool-protocol.js +0 -160
  13. package/dist/agent/skills/skills.js +0 -327
  14. package/dist/agent/tools/tool-executor.js +0 -415
  15. package/dist/agent/tools/tool-safety.js +0 -52
  16. package/dist/app/index.js +0 -35
  17. package/dist/app/repl.js +0 -105
  18. package/dist/app/update-check.js +0 -132
  19. package/dist/app/version.js +0 -51
  20. package/dist/code-context.js +0 -68
  21. package/dist/colors.js +0 -204
  22. package/dist/command-catalog.js +0 -242
  23. package/dist/commands.js +0 -350
  24. package/dist/conductor.js +0 -94
  25. package/dist/config/config.js +0 -335
  26. package/dist/config/hooks.js +0 -187
  27. package/dist/config.js +0 -335
  28. package/dist/downloader-state.js +0 -302
  29. package/dist/downloader-ui.js +0 -289
  30. package/dist/gateway/adapters/discord.js +0 -108
  31. package/dist/gateway/adapters/formatting.js +0 -96
  32. package/dist/gateway/adapters/telegram.js +0 -106
  33. package/dist/gateway/adapters/types.js +0 -2
  34. package/dist/gateway/adapters/whatsapp.js +0 -124
  35. package/dist/gateway/auth-policy.js +0 -66
  36. package/dist/gateway/core.js +0 -87
  37. package/dist/gateway/headless-conductor.js +0 -328
  38. package/dist/gateway/message-router.js +0 -23
  39. package/dist/gateway/rate-limiter.js +0 -48
  40. package/dist/gateway/runtime/backend-policy.js +0 -18
  41. package/dist/gateway/runtime/discord-bot.js +0 -104
  42. package/dist/gateway/runtime/discord-main.js +0 -69
  43. package/dist/gateway/session-manager.js +0 -77
  44. package/dist/gateway/types.js +0 -2
  45. package/dist/hardware.js +0 -92
  46. package/dist/hooks.js +0 -187
  47. package/dist/index.js +0 -34
  48. package/dist/input-engine.js +0 -250
  49. package/dist/llama-client.js +0 -227
  50. package/dist/llama-server.js +0 -620
  51. package/dist/llm/llama-client.js +0 -227
  52. package/dist/llm/llama-server.js +0 -620
  53. package/dist/llm/token-counter.js +0 -47
  54. package/dist/memory-store.js +0 -159
  55. package/dist/messages.js +0 -59
  56. package/dist/model-downloader.js +0 -382
  57. package/dist/model-manager-state.js +0 -118
  58. package/dist/model-manager-ui.js +0 -194
  59. package/dist/model-manager.js +0 -190
  60. package/dist/models/hardware.js +0 -92
  61. package/dist/models/model-downloader.js +0 -382
  62. package/dist/models/model-manager.js +0 -190
  63. package/dist/prompt-box.js +0 -78
  64. package/dist/prompt-composer.js +0 -498
  65. package/dist/repl.js +0 -105
  66. package/dist/session-store.js +0 -211
  67. package/dist/spinner.js +0 -76
  68. package/dist/title-box.js +0 -388
  69. package/dist/token-counter.js +0 -47
  70. package/dist/tool-executor.js +0 -415
  71. package/dist/tool-protocol.js +0 -121
  72. package/dist/tool-safety.js +0 -52
  73. package/dist/tui/app.js +0 -2553
  74. package/dist/tui/startup.js +0 -56
  75. package/dist/tui-input-routing.js +0 -53
  76. package/dist/tui.js +0 -51
  77. package/dist/types/app-types.js +0 -2
  78. package/dist/types.js +0 -2
  79. package/dist/ui/colors.js +0 -204
  80. package/dist/ui/downloader/downloader-state.js +0 -302
  81. package/dist/ui/downloader/downloader-ui.js +0 -289
  82. package/dist/ui/input/input-engine.js +0 -250
  83. package/dist/ui/input/tui-input-routing.js +0 -53
  84. package/dist/ui/input/vt-session.js +0 -168
  85. package/dist/ui/messages.js +0 -59
  86. package/dist/ui/model-manager/model-manager-state.js +0 -118
  87. package/dist/ui/model-manager/model-manager-ui.js +0 -194
  88. package/dist/ui/prompt/prompt-box.js +0 -78
  89. package/dist/ui/prompt/prompt-composer.js +0 -498
  90. package/dist/ui/spinner.js +0 -76
  91. package/dist/ui/title-box.js +0 -388
  92. package/dist/ui/tui/app.js +0 -6
  93. package/dist/ui/tui/autocomplete.js +0 -85
  94. package/dist/ui/tui/constants.js +0 -18
  95. package/dist/ui/tui/history.js +0 -29
  96. package/dist/ui/tui/layout.js +0 -341
  97. package/dist/ui/tui/runtime-core.js +0 -2584
  98. package/dist/ui/tui/runtime-utils.js +0 -53
  99. package/dist/ui/tui/start-tui.js +0 -54
  100. package/dist/ui/tui/startup.js +0 -56
  101. package/dist/version.js +0 -51
  102. package/dist/vt-session.js +0 -168
  103. package/install.sh +0 -457
@@ -1,211 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.slugifySessionNameFromMessage = slugifySessionNameFromMessage;
4
- exports.getFirstUserMessage = getFirstUserMessage;
5
- exports.createSessionFileFromHistory = createSessionFileFromHistory;
6
- exports.writeSessionFile = writeSessionFile;
7
- exports.listSessions = listSessions;
8
- exports.loadSession = loadSession;
9
- const promises_1 = require("node:fs/promises");
10
- const node_os_1 = require("node:os");
11
- const node_path_1 = require("node:path");
12
- const SESSION_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), ".yips", "memory");
13
- function pad2(value) {
14
- return value.toString().padStart(2, "0");
15
- }
16
- function toSessionTimestamp(now) {
17
- return [
18
- now.getFullYear(),
19
- "-",
20
- pad2(now.getMonth() + 1),
21
- "-",
22
- pad2(now.getDate()),
23
- "_",
24
- pad2(now.getHours()),
25
- "-",
26
- pad2(now.getMinutes()),
27
- "-",
28
- pad2(now.getSeconds())
29
- ].join("");
30
- }
31
- function toDisplayTitle(sessionName) {
32
- const words = sessionName
33
- .replace(/_/g, " ")
34
- .trim()
35
- .split(/\s+/u)
36
- .filter((word) => word.length > 0);
37
- if (words.length === 0) {
38
- return "Session";
39
- }
40
- return words
41
- .map((word) => `${word[0]?.toUpperCase() ?? ""}${word.slice(1).toLowerCase()}`)
42
- .join(" ");
43
- }
44
- function parseFileStem(path) {
45
- const stem = (0, node_path_1.basename)(path, ".md");
46
- const parts = stem.split("_", 3);
47
- if (parts.length >= 3) {
48
- const datePart = parts[0] ?? "";
49
- const timePart = parts[1] ?? "";
50
- const sessionName = stem.slice(`${datePart}_${timePart}_`.length);
51
- if (datePart.includes("-")) {
52
- const dt = new Date(`${datePart}T${timePart.replace(/-/g, ":")}`);
53
- if (!Number.isNaN(dt.valueOf())) {
54
- return { timestamp: dt, sessionName };
55
- }
56
- }
57
- else if (datePart.length === 8 && timePart.length >= 6) {
58
- const year = Number.parseInt(datePart.slice(0, 4), 10);
59
- const month = Number.parseInt(datePart.slice(4, 6), 10);
60
- const day = Number.parseInt(datePart.slice(6, 8), 10);
61
- const hour = Number.parseInt(timePart.slice(0, 2), 10);
62
- const minute = Number.parseInt(timePart.slice(2, 4), 10);
63
- const second = Number.parseInt(timePart.slice(4, 6), 10);
64
- const dt = new Date(year, month - 1, day, hour, minute, second);
65
- if (!Number.isNaN(dt.valueOf())) {
66
- return { timestamp: dt, sessionName };
67
- }
68
- }
69
- }
70
- return {
71
- timestamp: new Date(0),
72
- sessionName: stem
73
- };
74
- }
75
- function formatDisplayTimestamp(timestamp) {
76
- const year = timestamp.getFullYear();
77
- const month = timestamp.getMonth() + 1;
78
- const day = timestamp.getDate();
79
- const hours = timestamp.getHours();
80
- const minutes = pad2(timestamp.getMinutes());
81
- const amPm = hours < 12 ? "AM" : "PM";
82
- const displayHour = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
83
- return `${year}-${month}-${day} @ ${displayHour.toString().padStart(2, " ")}:${minutes} ${amPm}`;
84
- }
85
- function toDisplay(sessionName, timestamp) {
86
- return `${formatDisplayTimestamp(timestamp)}: ${toDisplayTitle(sessionName)}`;
87
- }
88
- function parseConversation(content) {
89
- const marker = "## Conversation";
90
- const markerIndex = content.indexOf(marker);
91
- const section = markerIndex >= 0
92
- ? content.slice(markerIndex + marker.length).split("\n---")[0] ?? ""
93
- : content;
94
- const history = [];
95
- const lines = section.split(/\r?\n/u);
96
- for (const rawLine of lines) {
97
- const line = rawLine.trimEnd();
98
- const roleMatch = line.match(/^\*\*([^*]+)\*\*:\s*(.*)$/u);
99
- if (roleMatch) {
100
- const roleName = (roleMatch[1] ?? "").trim();
101
- const text = roleMatch[2] ?? "";
102
- if (roleName.toLowerCase() === "yips") {
103
- history.push({ role: "assistant", content: text });
104
- }
105
- else {
106
- history.push({ role: "user", content: text });
107
- }
108
- continue;
109
- }
110
- const systemMatch = line.match(/^\*\[System:\s*(.*)\]\*$/u);
111
- if (systemMatch) {
112
- history.push({ role: "system", content: systemMatch[1] ?? "" });
113
- continue;
114
- }
115
- if (line.trim().length === 0) {
116
- continue;
117
- }
118
- const last = history[history.length - 1];
119
- if (last) {
120
- last.content = `${last.content}\n${line}`.trimEnd();
121
- }
122
- }
123
- return history;
124
- }
125
- function slugifySessionNameFromMessage(message) {
126
- const base = message.toLowerCase().trim().replace(/[^a-z0-9\s]/g, "");
127
- if (base.length === 0) {
128
- return "session";
129
- }
130
- const slug = base.replace(/\s+/g, "_").slice(0, 50).replace(/_+$/g, "");
131
- return slug.length > 0 ? slug : "session";
132
- }
133
- function getFirstUserMessage(history) {
134
- for (const message of history) {
135
- if (message.role === "user") {
136
- const trimmed = message.content.trim();
137
- if (trimmed.length > 0) {
138
- return trimmed;
139
- }
140
- }
141
- }
142
- return null;
143
- }
144
- async function createSessionFileFromHistory(history, now = new Date()) {
145
- await (0, promises_1.mkdir)(SESSION_DIR, { recursive: true });
146
- const firstUserMessage = getFirstUserMessage(history) ?? "";
147
- const sessionName = slugifySessionNameFromMessage(firstUserMessage);
148
- const path = (0, node_path_1.join)(SESSION_DIR, `${toSessionTimestamp(now)}_${sessionName}.md`);
149
- return { path, sessionName };
150
- }
151
- async function writeSessionFile(options) {
152
- const now = options.now ?? new Date();
153
- const lines = [];
154
- for (const message of options.history) {
155
- if (message.role === "user") {
156
- lines.push(`**${options.username}**: ${message.content}`);
157
- continue;
158
- }
159
- if (message.role === "assistant") {
160
- lines.push(`**Yips**: ${message.content}`);
161
- continue;
162
- }
163
- lines.push(`*[System: ${message.content}]*`);
164
- }
165
- const body = [
166
- "# Session Memory",
167
- "",
168
- `**Created**: ${now.toISOString()}`,
169
- "**Type**: Ongoing Session",
170
- "",
171
- "## Conversation",
172
- "",
173
- ...lines,
174
- "",
175
- "---",
176
- `*Last updated: ${now.toISOString()}*`,
177
- ""
178
- ].join("\n");
179
- await (0, promises_1.writeFile)(options.path, body, "utf8");
180
- }
181
- async function listSessions(limit) {
182
- try {
183
- const entries = await (0, promises_1.readdir)(SESSION_DIR, { withFileTypes: true });
184
- const sessionItems = entries
185
- .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".md"))
186
- .map((entry) => {
187
- const path = (0, node_path_1.join)(SESSION_DIR, entry.name);
188
- const parsed = parseFileStem(path);
189
- return {
190
- path,
191
- sessionName: parsed.sessionName,
192
- timestamp: parsed.timestamp,
193
- display: toDisplay(parsed.sessionName, parsed.timestamp)
194
- };
195
- })
196
- .sort((a, b) => b.timestamp.valueOf() - a.timestamp.valueOf());
197
- return typeof limit === "number" && limit > 0 ? sessionItems.slice(0, limit) : sessionItems;
198
- }
199
- catch {
200
- return [];
201
- }
202
- }
203
- async function loadSession(path) {
204
- const content = await (0, promises_1.readFile)(path, "utf8");
205
- const parsed = parseFileStem(path);
206
- return {
207
- history: parseConversation(content),
208
- sessionName: parsed.sessionName,
209
- path
210
- };
211
- }
package/dist/spinner.js DELETED
@@ -1,76 +0,0 @@
1
- "use strict";
2
- /** Pulsing braille spinner with color oscillation. */
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.PulsingSpinner = void 0;
5
- const colors_1 = require("./colors");
6
- const SPINNER_FRAMES = ["⠹", "⢸", "⣰", "⣤", "⣆", "⡇", "⠏", "⠛"];
7
- const FRAME_COUNT = SPINNER_FRAMES.length;
8
- const OSCILLATION_RATE = 2.0;
9
- const FRAME_INTERVAL_MS = 80;
10
- class PulsingSpinner {
11
- label;
12
- frameIndex;
13
- startTime;
14
- lastFrameTime;
15
- active;
16
- constructor(label = "Thinking...") {
17
- this.label = label;
18
- this.frameIndex = 0;
19
- this.startTime = Date.now();
20
- this.lastFrameTime = this.startTime;
21
- this.active = false;
22
- }
23
- start(label) {
24
- if (label !== undefined) {
25
- this.label = label;
26
- }
27
- this.frameIndex = 0;
28
- this.startTime = Date.now();
29
- this.lastFrameTime = this.startTime;
30
- this.active = true;
31
- }
32
- stop() {
33
- this.active = false;
34
- }
35
- update(label) {
36
- this.label = label;
37
- }
38
- isActive() {
39
- return this.active;
40
- }
41
- getElapsed() {
42
- return Math.floor((Date.now() - this.startTime) / 1000);
43
- }
44
- formatElapsed(seconds) {
45
- const minutes = Math.floor(seconds / 60);
46
- const remainder = seconds % 60;
47
- if (minutes === 0) {
48
- return `${remainder}s`;
49
- }
50
- return `${minutes}m ${remainder}s`;
51
- }
52
- render() {
53
- const now = Date.now();
54
- const elapsedSinceFrame = now - this.lastFrameTime;
55
- if (elapsedSinceFrame >= FRAME_INTERVAL_MS) {
56
- const frameSteps = Math.floor(elapsedSinceFrame / FRAME_INTERVAL_MS);
57
- this.frameIndex = (this.frameIndex + frameSteps) % FRAME_COUNT;
58
- this.lastFrameTime += frameSteps * FRAME_INTERVAL_MS;
59
- }
60
- const frame = SPINNER_FRAMES[this.frameIndex % FRAME_COUNT];
61
- const elapsedSeconds = Math.max(0, (now - this.startTime) / 1000);
62
- const elapsed = Math.floor(elapsedSeconds);
63
- const t = (Math.sin(elapsedSeconds * OSCILLATION_RATE) + 1) / 2;
64
- const color = (0, colors_1.interpolateColor)(colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, t);
65
- const timeText = this.formatElapsed(elapsed);
66
- const spinnerChar = (0, colors_1.colorText)(frame, color);
67
- const labelText = (0, colors_1.colorText)(this.label, color);
68
- const suffix = (0, colors_1.colorText)(`(${timeText})`, color);
69
- return `${spinnerChar} ${labelText} ${suffix}`;
70
- }
71
- static computeOscillationColor(elapsedSeconds) {
72
- const t = (Math.sin(elapsedSeconds * OSCILLATION_RATE) + 1) / 2;
73
- return (0, colors_1.interpolateColor)(colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, t);
74
- }
75
- }
76
- exports.PulsingSpinner = PulsingSpinner;
package/dist/title-box.js DELETED
@@ -1,388 +0,0 @@
1
- "use strict";
2
- /** Responsive title box using the yips-cli visual contract. */
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.stripMarkup = stripMarkup;
5
- exports.renderTitleBox = renderTitleBox;
6
- exports.getLayoutMode = getLayoutMode;
7
- const colors_1 = require("./colors");
8
- const YIPS_LOGO = [
9
- "██╗ ██╗██╗██████╗ ███████╗",
10
- "╚██╗ ██╔╝██║██╔══██╗██╔════╝",
11
- " ╚████╔╝ ██║██████╔╝███████╗",
12
- " ╚██╔╝ ██║██╔═══╝ ╚════██║",
13
- " ██║ ██║██║ ███████║",
14
- " ╚═╝ ╚═╝╚═╝ ╚══════╝"
15
- ];
16
- const LOGO_WIDTH = 28;
17
- const TOP_TITLE = "Yips";
18
- const TOP_TITLE_FALLBACK = "Yips";
19
- const TOP_BORDER_MIN_WIDTH = 25;
20
- function getLayoutMode(width) {
21
- if (width >= 80)
22
- return "full";
23
- if (width >= 60)
24
- return "single";
25
- if (width >= 45)
26
- return "compact";
27
- return "minimal";
28
- }
29
- function centerText(text, width) {
30
- if (width <= 0)
31
- return "";
32
- if (text.length >= width)
33
- return text.slice(0, width);
34
- const leftPadding = Math.floor((width - text.length) / 2);
35
- const rightPadding = width - text.length - leftPadding;
36
- return `${" ".repeat(leftPadding)}${text}${" ".repeat(rightPadding)}`;
37
- }
38
- function fitText(text, width) {
39
- if (width <= 0)
40
- return "";
41
- if (text.length >= width)
42
- return text.slice(0, width);
43
- return text.padEnd(width, " ");
44
- }
45
- function toSingleColor(char, progress) {
46
- return (0, colors_1.colorChar)(char, (0, colors_1.interpolateColor)(colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, progress));
47
- }
48
- function makeTopBorder(version, width) {
49
- if (width <= 0)
50
- return "";
51
- const fallback = `╭${"─".repeat(Math.max(0, width - 2))}╮`;
52
- if (width < TOP_BORDER_MIN_WIDTH) {
53
- return (0, colors_1.horizontalGradient)(fallback, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW);
54
- }
55
- let title = TOP_TITLE;
56
- let titleLength = title.length + 1 + version.length;
57
- let borderAvailable = width - titleLength - 7;
58
- if (borderAvailable < 0) {
59
- title = TOP_TITLE_FALLBACK;
60
- titleLength = title.length + 1 + version.length;
61
- borderAvailable = width - titleLength - 7;
62
- if (borderAvailable < 0) {
63
- return (0, colors_1.horizontalGradient)(fallback, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW);
64
- }
65
- }
66
- const pieces = [];
67
- let position = 0;
68
- const appendBorder = (segment) => {
69
- for (const char of segment) {
70
- const progress = position / Math.max(width - 1, 1);
71
- pieces.push(toSingleColor(char, progress));
72
- position += 1;
73
- }
74
- };
75
- appendBorder("╭─── ");
76
- pieces.push("\u001b[1m");
77
- for (let i = 0; i < title.length; i++) {
78
- const progress = i / Math.max(title.length - 1, 1);
79
- pieces.push(toSingleColor(title[i], progress));
80
- position += 1;
81
- }
82
- pieces.push(" ");
83
- position += 1;
84
- pieces.push((0, colors_1.colorText)(version, colors_1.GRADIENT_BLUE));
85
- position += version.length;
86
- pieces.push("\u001b[22m");
87
- const closing = ` ${"─".repeat(Math.max(0, borderAvailable))}╮`;
88
- appendBorder(closing);
89
- return pieces.join("");
90
- }
91
- /** Strip ANSI and legacy terminal-kit markup sequences for test/plain rendering. */
92
- function stripMarkup(text) {
93
- return (0, colors_1.stripAnsi)(text).replace(/\^\[#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})\]|\^#[0-9a-fA-F]{6}|\^:/g, "");
94
- }
95
- function makeBottomBorder(sessionName, width) {
96
- if (width <= 0)
97
- return "";
98
- const borderChars = Array.from({ length: Math.max(0, width - 2) }, () => "─");
99
- const trimmedSessionName = sessionName.trim();
100
- if (trimmedSessionName.length === 0) {
101
- return (0, colors_1.horizontalGradient)(`╰${borderChars.join("")}╯`, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW);
102
- }
103
- const displayName = ` ${trimmedSessionName.replace(/_/g, " ")} `;
104
- if (displayName.length <= borderChars.length) {
105
- const start = Math.floor((borderChars.length - displayName.length) / 2);
106
- for (let i = 0; i < displayName.length; i++) {
107
- borderChars[start + i] = displayName[i];
108
- }
109
- }
110
- return (0, colors_1.horizontalGradient)(`╰${borderChars.join("")}╯`, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW);
111
- }
112
- function buildModelInfo(backend, model, tokenUsage) {
113
- const trimmedModel = model.trim();
114
- if (trimmedModel.length === 0) {
115
- return backend;
116
- }
117
- const parts = [backend, trimmedModel];
118
- const usage = tokenUsage.trim();
119
- if (usage.length > 0) {
120
- parts.push(usage);
121
- }
122
- return parts.join(" · ");
123
- }
124
- function padLine(markup, plain, width) {
125
- const clippedPlain = fitText(plain, width);
126
- if (plain.length > width) {
127
- return clippedPlain;
128
- }
129
- const padding = " ".repeat(Math.max(0, width - plain.length));
130
- return `${markup}${padding}`;
131
- }
132
- function makeSingleRow(contentMarkup, contentPlain, width) {
133
- const innerWidth = Math.max(0, width - 2);
134
- const leftBorder = (0, colors_1.horizontalGradient)("│", colors_1.GRADIENT_PINK, colors_1.GRADIENT_PINK);
135
- const rightBorder = (0, colors_1.horizontalGradient)("│", colors_1.GRADIENT_YELLOW, colors_1.GRADIENT_YELLOW);
136
- return `${leftBorder}${padLine(contentMarkup, contentPlain, innerWidth)}${rightBorder}`;
137
- }
138
- function styleCenteredText(text, width, style) {
139
- const centered = centerText(text, width);
140
- switch (style) {
141
- case "gradient":
142
- return {
143
- markup: (0, colors_1.horizontalGradient)(centered, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW),
144
- plain: centered
145
- };
146
- case "blue":
147
- return { markup: (0, colors_1.colorText)(centered, colors_1.GRADIENT_BLUE), plain: centered };
148
- case "pink":
149
- return { markup: (0, colors_1.colorText)(centered, colors_1.INPUT_PINK), plain: centered };
150
- case "dim":
151
- return { markup: (0, colors_1.colorText)(centered, colors_1.DIM_GRAY), plain: centered };
152
- case "plain":
153
- return { markup: centered, plain: centered };
154
- }
155
- }
156
- function styleCenteredTextWithGradientSpan(text, width) {
157
- if (width <= 0)
158
- return { markup: "", plain: "" };
159
- const clipped = text.slice(0, width);
160
- if (clipped.length >= width) {
161
- return {
162
- markup: (0, colors_1.horizontalGradient)(clipped, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW),
163
- plain: clipped
164
- };
165
- }
166
- const leftPadding = Math.floor((width - clipped.length) / 2);
167
- const rightPadding = width - clipped.length - leftPadding;
168
- return {
169
- markup: `${" ".repeat(leftPadding)}${(0, colors_1.horizontalGradient)(clipped, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW)}${" ".repeat(rightPadding)}`,
170
- plain: `${" ".repeat(leftPadding)}${clipped}${" ".repeat(rightPadding)}`
171
- };
172
- }
173
- function withBold(row) {
174
- if (row.markup.length === 0)
175
- return row;
176
- return { markup: `\u001b[1m${row.markup}\u001b[22m`, plain: row.plain };
177
- }
178
- function styleLeftText(text, width, style) {
179
- const clipped = text.slice(0, Math.max(0, width));
180
- const padded = fitText(clipped, width);
181
- switch (style) {
182
- case "gradient":
183
- if (clipped.length === 0)
184
- return { markup: padded, plain: padded };
185
- return {
186
- markup: `${(0, colors_1.horizontalGradient)(clipped, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW)}${" ".repeat(Math.max(0, width - clipped.length))}`,
187
- plain: padded
188
- };
189
- case "blue":
190
- if (clipped.length === 0)
191
- return { markup: padded, plain: padded };
192
- return {
193
- markup: `${(0, colors_1.colorText)(clipped, colors_1.GRADIENT_BLUE)}${" ".repeat(Math.max(0, width - clipped.length))}`,
194
- plain: padded
195
- };
196
- case "white":
197
- if (clipped.length === 0)
198
- return { markup: padded, plain: padded };
199
- return {
200
- markup: `${(0, colors_1.colorText)(clipped, { r: 255, g: 255, b: 255 })}${" ".repeat(Math.max(0, width - clipped.length))}`,
201
- plain: padded
202
- };
203
- case "dim":
204
- if (clipped.length === 0)
205
- return { markup: padded, plain: padded };
206
- return {
207
- markup: `${(0, colors_1.colorText)(clipped, colors_1.DIM_GRAY)}${" ".repeat(Math.max(0, width - clipped.length))}`,
208
- plain: padded
209
- };
210
- case "plain":
211
- return { markup: padded, plain: padded };
212
- }
213
- }
214
- function styleHighlightedText(text, width) {
215
- const clipped = text.slice(0, Math.max(0, width));
216
- const padded = fitText(clipped, width);
217
- if (clipped.length === 0)
218
- return { markup: padded, plain: padded };
219
- return {
220
- markup: `\u001b[1m${(0, colors_1.colorText)(clipped, colors_1.INPUT_PINK)}\u001b[22m${" ".repeat(Math.max(0, width - clipped.length))}`,
221
- plain: padded
222
- };
223
- }
224
- function styleLeftTextGlobalGradient(text, width, totalWidth, startColumn) {
225
- const clipped = text.slice(0, Math.max(0, width));
226
- const padded = fitText(clipped, width);
227
- if (clipped.length === 0)
228
- return { markup: padded, plain: padded };
229
- const maxColumn = Math.max(totalWidth - 1, 1);
230
- const startColor = (0, colors_1.interpolateColor)(colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, Math.max(0, Math.min(1, startColumn / maxColumn)));
231
- const endColumn = startColumn + Math.max(clipped.length - 1, 0);
232
- const endColor = (0, colors_1.interpolateColor)(colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, Math.max(0, Math.min(1, endColumn / maxColumn)));
233
- return {
234
- markup: `${(0, colors_1.horizontalGradient)(clipped, startColor, endColor)}${" ".repeat(Math.max(0, width - clipped.length))}`,
235
- plain: padded
236
- };
237
- }
238
- function centerLogoLine(logoMarkup, logoPlain, width) {
239
- const paddingLeft = Math.max(0, Math.floor((width - logoPlain.length) / 2));
240
- const paddingRight = Math.max(0, width - logoPlain.length - paddingLeft);
241
- return {
242
- markup: `${" ".repeat(paddingLeft)}${logoMarkup}${" ".repeat(paddingRight)}`,
243
- plain: `${" ".repeat(paddingLeft)}${logoPlain}${" ".repeat(paddingRight)}`
244
- };
245
- }
246
- function renderSingleColumn(options, mode) {
247
- const { width, version, username, backend, model, tokenUsage, cwd, sessionName } = options;
248
- const innerWidth = Math.max(0, width - 2);
249
- const lines = [];
250
- const modelInfo = buildModelInfo(backend, model, tokenUsage);
251
- const gradientLogo = (0, colors_1.rowMajorGradient)(YIPS_LOGO, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW);
252
- const showLogo = innerWidth >= LOGO_WIDTH;
253
- lines.push(makeTopBorder(version, width));
254
- if (mode === "minimal") {
255
- const minimalRows = [];
256
- minimalRows.push(styleCenteredText("", innerWidth, "plain"));
257
- if (showLogo) {
258
- for (let i = 0; i < YIPS_LOGO.length; i++) {
259
- minimalRows.push(centerLogoLine(gradientLogo[i] ?? "", YIPS_LOGO[i] ?? "", innerWidth));
260
- }
261
- }
262
- else {
263
- minimalRows.push(styleCenteredText("YIPS", innerWidth, "gradient"));
264
- }
265
- minimalRows.push(styleCenteredText(modelInfo, innerWidth, "blue"));
266
- minimalRows.push(styleCenteredText("", innerWidth, "plain"));
267
- for (const row of minimalRows) {
268
- lines.push(makeSingleRow(row.markup, row.plain, width));
269
- }
270
- lines.push(makeBottomBorder(sessionName, width));
271
- return lines;
272
- }
273
- const rows = [];
274
- rows.push(styleCenteredText("", innerWidth, "plain"));
275
- rows.push(mode === "single"
276
- ? withBold(styleCenteredTextWithGradientSpan(`Welcome back ${username}!`, innerWidth))
277
- : styleCenteredTextWithGradientSpan(`Hi ${username}!`, innerWidth));
278
- rows.push(styleCenteredText("", innerWidth, "plain"));
279
- if (showLogo) {
280
- for (let i = 0; i < YIPS_LOGO.length; i++) {
281
- rows.push(centerLogoLine(gradientLogo[i] ?? "", YIPS_LOGO[i] ?? "", innerWidth));
282
- }
283
- }
284
- else {
285
- rows.push(styleCenteredText("YIPS", innerWidth, "gradient"));
286
- }
287
- rows.push(styleCenteredText(modelInfo, innerWidth, "blue"));
288
- if (mode === "single") {
289
- rows.push(styleCenteredTextWithGradientSpan(cwd, innerWidth));
290
- }
291
- rows.push(styleCenteredText("", innerWidth, "plain"));
292
- for (const row of rows) {
293
- lines.push(makeSingleRow(row.markup, row.plain, width));
294
- }
295
- lines.push(makeBottomBorder(sessionName, width));
296
- return lines;
297
- }
298
- function renderFull(options) {
299
- const { width, version, username, backend, model, tokenUsage, cwd, sessionName, recentActivity = [], sessionSelection } = options;
300
- const lines = [];
301
- const modelInfo = buildModelInfo(backend, model, tokenUsage);
302
- const availableWidth = Math.max(0, width - 3);
303
- const leftWidth = Math.max(Math.floor(availableWidth * 0.45), 30);
304
- const rightWidth = Math.max(0, availableWidth - leftWidth);
305
- const middleProgress = (leftWidth + 1) / Math.max(width - 1, 1);
306
- const middleBorderColor = (0, colors_1.interpolateColor)(colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, middleProgress);
307
- const leftBorder = (0, colors_1.horizontalGradient)("│", colors_1.GRADIENT_PINK, colors_1.GRADIENT_PINK);
308
- const middleBorder = (0, colors_1.colorText)("│", middleBorderColor);
309
- const rightBorder = (0, colors_1.horizontalGradient)("│", colors_1.GRADIENT_YELLOW, colors_1.GRADIENT_YELLOW);
310
- const gradientLogo = (0, colors_1.rowMajorGradient)(YIPS_LOGO, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW);
311
- const rightStartColumn = leftWidth + 2;
312
- const leftRows = [
313
- styleCenteredText("", leftWidth, "plain"),
314
- withBold(styleCenteredTextWithGradientSpan(`Welcome back ${username}!`, leftWidth)),
315
- styleCenteredText("", leftWidth, "plain")
316
- ];
317
- for (let i = 0; i < YIPS_LOGO.length; i++) {
318
- leftRows.push(centerLogoLine(gradientLogo[i] ?? "", YIPS_LOGO[i] ?? "", leftWidth));
319
- }
320
- leftRows.push(styleCenteredText(modelInfo, leftWidth, "blue"));
321
- leftRows.push(styleCenteredTextWithGradientSpan(cwd, leftWidth));
322
- leftRows.push(styleCenteredText("", leftWidth, "plain"));
323
- const rightRows = [
324
- withBold(styleLeftTextGlobalGradient("Tips for getting started:", rightWidth, width, rightStartColumn)),
325
- styleLeftTextGlobalGradient("- Ask questions, edit files, or run commands.", rightWidth, width, rightStartColumn),
326
- styleLeftTextGlobalGradient("- Be specific for the best results.", rightWidth, width, rightStartColumn),
327
- styleLeftTextGlobalGradient("- /help for more information.", rightWidth, width, rightStartColumn),
328
- styleLeftText("", rightWidth, "plain"),
329
- styleLeftTextGlobalGradient("─".repeat(Math.max(0, rightWidth)), rightWidth, width, rightStartColumn),
330
- withBold(styleLeftText("Recent activity", rightWidth, "white"))
331
- ];
332
- const activityItems = recentActivity.length > 0 ? recentActivity : ["No recent activity yet."];
333
- if (sessionSelection?.active) {
334
- const maxSlots = 5;
335
- const safeSelected = Math.max(0, Math.min(sessionSelection.selectedIndex, activityItems.length - 1));
336
- const start = Math.max(0, Math.min(safeSelected - Math.floor(maxSlots / 2), Math.max(0, activityItems.length - maxSlots)));
337
- const visible = activityItems.slice(start, start + maxSlots);
338
- for (let i = 0; i < visible.length; i++) {
339
- const actualIndex = start + i;
340
- const item = visible[i] ?? "";
341
- if (actualIndex === safeSelected) {
342
- rightRows.push(styleHighlightedText(`> ${item}`, rightWidth));
343
- }
344
- else {
345
- rightRows.push(styleLeftText(` ${item}`, rightWidth, "dim"));
346
- }
347
- }
348
- }
349
- else {
350
- for (const item of activityItems.slice(0, 5)) {
351
- rightRows.push(styleLeftText(item, rightWidth, "dim"));
352
- }
353
- }
354
- while (rightRows.length < leftRows.length) {
355
- rightRows.push(styleLeftText("", rightWidth, "plain"));
356
- }
357
- lines.push(makeTopBorder(version, width));
358
- const maxRows = Math.max(leftRows.length, rightRows.length);
359
- for (let row = 0; row < maxRows; row++) {
360
- const left = leftRows[row] ?? styleLeftText("", leftWidth, "plain");
361
- const right = rightRows[row] ?? styleLeftText("", rightWidth, "plain");
362
- lines.push(`${leftBorder}${padLine(left.markup, left.plain, leftWidth)}${middleBorder}${padLine(right.markup, right.plain, rightWidth)}${rightBorder}`);
363
- }
364
- lines.push(makeBottomBorder(sessionName, width));
365
- return lines;
366
- }
367
- function renderCompact(options) {
368
- return renderSingleColumn(options, "compact");
369
- }
370
- function renderMinimal(options) {
371
- return renderSingleColumn(options, "minimal");
372
- }
373
- function renderSingle(options) {
374
- return renderSingleColumn(options, "single");
375
- }
376
- function renderTitleBox(options) {
377
- const mode = getLayoutMode(options.width);
378
- switch (mode) {
379
- case "full":
380
- return renderFull(options);
381
- case "single":
382
- return renderSingle(options);
383
- case "compact":
384
- return renderCompact(options);
385
- case "minimal":
386
- return renderMinimal(options);
387
- }
388
- }