@sheepbun/yips 0.1.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/dist/agent/commands/command-catalog.js +243 -0
- package/dist/agent/commands/commands.js +418 -0
- package/dist/agent/conductor.js +118 -0
- package/dist/agent/context/code-context.js +68 -0
- package/dist/agent/context/memory-store.js +159 -0
- package/dist/agent/context/session-store.js +211 -0
- package/dist/agent/protocol/tool-protocol.js +160 -0
- package/dist/agent/skills/skills.js +327 -0
- package/dist/agent/tools/tool-executor.js +415 -0
- package/dist/agent/tools/tool-safety.js +52 -0
- package/dist/app/index.js +35 -0
- package/dist/app/repl.js +105 -0
- package/dist/app/update-check.js +132 -0
- package/dist/app/version.js +51 -0
- package/dist/code-context.js +68 -0
- package/dist/colors.js +204 -0
- package/dist/command-catalog.js +242 -0
- package/dist/commands.js +350 -0
- package/dist/conductor.js +94 -0
- package/dist/config/config.js +335 -0
- package/dist/config/hooks.js +187 -0
- package/dist/config.js +335 -0
- package/dist/downloader-state.js +302 -0
- package/dist/downloader-ui.js +289 -0
- package/dist/gateway/adapters/discord.js +108 -0
- package/dist/gateway/adapters/formatting.js +96 -0
- package/dist/gateway/adapters/telegram.js +106 -0
- package/dist/gateway/adapters/types.js +2 -0
- package/dist/gateway/adapters/whatsapp.js +124 -0
- package/dist/gateway/auth-policy.js +66 -0
- package/dist/gateway/core.js +87 -0
- package/dist/gateway/headless-conductor.js +328 -0
- package/dist/gateway/message-router.js +23 -0
- package/dist/gateway/rate-limiter.js +48 -0
- package/dist/gateway/runtime/backend-policy.js +18 -0
- package/dist/gateway/runtime/discord-bot.js +104 -0
- package/dist/gateway/runtime/discord-main.js +69 -0
- package/dist/gateway/session-manager.js +77 -0
- package/dist/gateway/types.js +2 -0
- package/dist/hardware.js +92 -0
- package/dist/hooks.js +187 -0
- package/dist/index.js +34 -0
- package/dist/input-engine.js +250 -0
- package/dist/llama-client.js +227 -0
- package/dist/llama-server.js +620 -0
- package/dist/llm/llama-client.js +227 -0
- package/dist/llm/llama-server.js +620 -0
- package/dist/llm/token-counter.js +47 -0
- package/dist/memory-store.js +159 -0
- package/dist/messages.js +59 -0
- package/dist/model-downloader.js +382 -0
- package/dist/model-manager-state.js +118 -0
- package/dist/model-manager-ui.js +194 -0
- package/dist/model-manager.js +190 -0
- package/dist/models/hardware.js +92 -0
- package/dist/models/model-downloader.js +382 -0
- package/dist/models/model-manager.js +190 -0
- package/dist/prompt-box.js +78 -0
- package/dist/prompt-composer.js +498 -0
- package/dist/repl.js +105 -0
- package/dist/session-store.js +211 -0
- package/dist/spinner.js +76 -0
- package/dist/title-box.js +388 -0
- package/dist/token-counter.js +47 -0
- package/dist/tool-executor.js +415 -0
- package/dist/tool-protocol.js +121 -0
- package/dist/tool-safety.js +52 -0
- package/dist/tui/app.js +2553 -0
- package/dist/tui/startup.js +56 -0
- package/dist/tui-input-routing.js +53 -0
- package/dist/tui.js +51 -0
- package/dist/types/app-types.js +2 -0
- package/dist/types.js +2 -0
- package/dist/ui/colors.js +204 -0
- package/dist/ui/downloader/downloader-state.js +302 -0
- package/dist/ui/downloader/downloader-ui.js +289 -0
- package/dist/ui/input/input-engine.js +250 -0
- package/dist/ui/input/tui-input-routing.js +53 -0
- package/dist/ui/input/vt-session.js +168 -0
- package/dist/ui/messages.js +59 -0
- package/dist/ui/model-manager/model-manager-state.js +118 -0
- package/dist/ui/model-manager/model-manager-ui.js +194 -0
- package/dist/ui/prompt/prompt-box.js +78 -0
- package/dist/ui/prompt/prompt-composer.js +498 -0
- package/dist/ui/spinner.js +76 -0
- package/dist/ui/title-box.js +388 -0
- package/dist/ui/tui/app.js +6 -0
- package/dist/ui/tui/autocomplete.js +85 -0
- package/dist/ui/tui/constants.js +18 -0
- package/dist/ui/tui/history.js +29 -0
- package/dist/ui/tui/layout.js +341 -0
- package/dist/ui/tui/runtime-core.js +2584 -0
- package/dist/ui/tui/runtime-utils.js +53 -0
- package/dist/ui/tui/start-tui.js +54 -0
- package/dist/ui/tui/startup.js +56 -0
- package/dist/version.js +51 -0
- package/dist/vt-session.js +168 -0
- package/install.sh +457 -0
- package/package.json +128 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderDownloaderLines = renderDownloaderLines;
|
|
4
|
+
const colors_1 = require("#ui/colors");
|
|
5
|
+
const TAB_INACTIVE = { r: 0x88, g: 0x88, b: 0x88 };
|
|
6
|
+
const BLACK = { r: 0x00, g: 0x00, b: 0x00 };
|
|
7
|
+
const FOCUS_ACCENT_BG = { r: 0xff, g: 0xcc, b: 0xff };
|
|
8
|
+
const DOWNLOADER_MODEL_BODY_ROWS = 10;
|
|
9
|
+
const DOWNLOADER_FILE_BODY_ROWS = 10;
|
|
10
|
+
const ANSI_BOLD_ON = "\u001b[1m";
|
|
11
|
+
const ANSI_BOLD_OFF = "\u001b[22m";
|
|
12
|
+
function charLength(text) {
|
|
13
|
+
return Array.from((0, colors_1.stripAnsi)(text)).length;
|
|
14
|
+
}
|
|
15
|
+
function fitRight(text, width) {
|
|
16
|
+
const chars = Array.from(text);
|
|
17
|
+
if (chars.length <= width) {
|
|
18
|
+
return text;
|
|
19
|
+
}
|
|
20
|
+
return chars.slice(chars.length - width).join("");
|
|
21
|
+
}
|
|
22
|
+
function fitLeft(text, width) {
|
|
23
|
+
const chars = Array.from(text);
|
|
24
|
+
if (chars.length <= width) {
|
|
25
|
+
return text;
|
|
26
|
+
}
|
|
27
|
+
if (width <= 3) {
|
|
28
|
+
return chars.slice(0, width).join("");
|
|
29
|
+
}
|
|
30
|
+
return `${chars.slice(0, width - 3).join("")}...`;
|
|
31
|
+
}
|
|
32
|
+
function formatCount(value) {
|
|
33
|
+
if (value >= 1_000_000) {
|
|
34
|
+
return `${(value / 1_000_000).toFixed(1)}M`;
|
|
35
|
+
}
|
|
36
|
+
if (value >= 1_000) {
|
|
37
|
+
return `${(value / 1_000).toFixed(1)}k`;
|
|
38
|
+
}
|
|
39
|
+
return String(value);
|
|
40
|
+
}
|
|
41
|
+
function formatSize(bytes) {
|
|
42
|
+
if (bytes === null || bytes <= 0) {
|
|
43
|
+
return "?";
|
|
44
|
+
}
|
|
45
|
+
const gib = bytes / (1024 * 1024 * 1024);
|
|
46
|
+
return `${gib.toFixed(1)}G`;
|
|
47
|
+
}
|
|
48
|
+
function tabText(tab, activeTab) {
|
|
49
|
+
const label = ` ${tab} `;
|
|
50
|
+
if (tab === activeTab) {
|
|
51
|
+
return `${ANSI_BOLD_ON}${(0, colors_1.horizontalGradientBackground)(label, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, BLACK)}${ANSI_BOLD_OFF}`;
|
|
52
|
+
}
|
|
53
|
+
return `${ANSI_BOLD_ON}${(0, colors_1.colorText)(label, TAB_INACTIVE)}${ANSI_BOLD_OFF}`;
|
|
54
|
+
}
|
|
55
|
+
function makeBorderTop(width) {
|
|
56
|
+
if (width <= 1)
|
|
57
|
+
return "╭";
|
|
58
|
+
const prefix = "╭─── ";
|
|
59
|
+
const titleBrand = "Yips";
|
|
60
|
+
const titleDetail = " Model Downloader";
|
|
61
|
+
const titleTail = " ";
|
|
62
|
+
const prefixLen = charLength(prefix);
|
|
63
|
+
const titleBrandLen = charLength(titleBrand);
|
|
64
|
+
const titleDetailLen = charLength(titleDetail);
|
|
65
|
+
const titleTailLen = charLength(titleTail);
|
|
66
|
+
const plainTitleLen = prefixLen + titleBrandLen + titleDetailLen + titleTailLen;
|
|
67
|
+
const fill = "─".repeat(Math.max(0, width - plainTitleLen - 1));
|
|
68
|
+
const fillOffset = prefixLen + titleBrandLen + titleDetailLen + titleTailLen;
|
|
69
|
+
const cornerOffset = width - 1;
|
|
70
|
+
return `${(0, colors_1.horizontalGradientAtOffset)(prefix, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, 0, width)}${ANSI_BOLD_ON}${(0, colors_1.horizontalGradient)(titleBrand, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW)}${(0, colors_1.colorText)(titleDetail, colors_1.GRADIENT_BLUE)}${ANSI_BOLD_OFF}${(0, colors_1.horizontalGradientAtOffset)(titleTail, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, prefixLen + titleBrandLen + titleDetailLen, width)}${(0, colors_1.horizontalGradientAtOffset)(fill, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, fillOffset, width)}${(0, colors_1.horizontalGradientAtOffset)("╮", colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, cornerOffset, width)}`;
|
|
71
|
+
}
|
|
72
|
+
function makeBorderBottom(width) {
|
|
73
|
+
if (width <= 1)
|
|
74
|
+
return "╰";
|
|
75
|
+
const mid = "─".repeat(Math.max(0, width - 2));
|
|
76
|
+
return `${(0, colors_1.colorText)("╰", colors_1.GRADIENT_PINK)}${(0, colors_1.horizontalGradient)(mid, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW)}${(0, colors_1.colorText)("╯", colors_1.GRADIENT_YELLOW)}`;
|
|
77
|
+
}
|
|
78
|
+
function lineWithBorders(inner, innerWidth) {
|
|
79
|
+
let fitted = inner;
|
|
80
|
+
if (charLength(fitted) > innerWidth) {
|
|
81
|
+
fitted = fitLeft((0, colors_1.stripAnsi)(inner), innerWidth);
|
|
82
|
+
}
|
|
83
|
+
const padding = " ".repeat(Math.max(0, innerWidth - charLength(fitted)));
|
|
84
|
+
return `${(0, colors_1.colorText)("│", colors_1.GRADIENT_PINK)}${fitted}${padding}${(0, colors_1.colorText)("│", colors_1.GRADIENT_YELLOW)}`;
|
|
85
|
+
}
|
|
86
|
+
function composeSplitFooter(left, right, innerWidth) {
|
|
87
|
+
const leftLen = charLength(left);
|
|
88
|
+
const rightLen = charLength(right);
|
|
89
|
+
if (leftLen + rightLen + 1 > innerWidth) {
|
|
90
|
+
return `${left} ${fitRight(right, Math.max(1, innerWidth - leftLen - 1))}`;
|
|
91
|
+
}
|
|
92
|
+
const gap = " ".repeat(Math.max(1, innerWidth - leftLen - rightLen));
|
|
93
|
+
return `${left}${gap}${right}`;
|
|
94
|
+
}
|
|
95
|
+
function highlightedRow(row) {
|
|
96
|
+
const chars = Array.from(row);
|
|
97
|
+
if (chars.length === 0)
|
|
98
|
+
return row;
|
|
99
|
+
const first = (0, colors_1.bgColorText)(chars[0] ?? "", FOCUS_ACCENT_BG, BLACK);
|
|
100
|
+
const rest = chars.slice(1).join("");
|
|
101
|
+
if (rest.length === 0) {
|
|
102
|
+
return first;
|
|
103
|
+
}
|
|
104
|
+
return `${first}${(0, colors_1.horizontalGradientBackground)(rest, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW, BLACK)}`;
|
|
105
|
+
}
|
|
106
|
+
function buildModelRows(state, contentWidth, rowCount) {
|
|
107
|
+
const rows = [];
|
|
108
|
+
if (state.models.length === 0) {
|
|
109
|
+
rows.push((0, colors_1.colorText)("No compatible models found for current RAM+VRAM.", colors_1.WARNING_YELLOW));
|
|
110
|
+
return rows;
|
|
111
|
+
}
|
|
112
|
+
const dlWidth = 9;
|
|
113
|
+
const likesWidth = 6;
|
|
114
|
+
const sizeWidth = 5;
|
|
115
|
+
const dateWidth = 10;
|
|
116
|
+
const staticWidth = 2 + 3 + dlWidth + 3 + likesWidth + 3 + sizeWidth + 3 + dateWidth;
|
|
117
|
+
const modelWidth = Math.max(8, contentWidth - staticWidth);
|
|
118
|
+
const header = ` ${"Model".padEnd(modelWidth, " ")} | ${"Downloads".padStart(dlWidth, " ")} | ${"Likes".padStart(likesWidth, " ")} | ${"Size".padStart(sizeWidth, " ")} | ${"Updated".padStart(dateWidth, " ")}`;
|
|
119
|
+
rows.push((0, colors_1.horizontalGradient)(header, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW));
|
|
120
|
+
const visibleRowCount = Math.max(0, rowCount - 1);
|
|
121
|
+
const start = state.modelScrollOffset;
|
|
122
|
+
const end = Math.min(state.models.length, start + visibleRowCount);
|
|
123
|
+
for (let index = start; index < end; index++) {
|
|
124
|
+
const model = state.models[index];
|
|
125
|
+
if (!model)
|
|
126
|
+
continue;
|
|
127
|
+
const prefix = index === state.selectedModelIndex ? ">" : " ";
|
|
128
|
+
const modelCell = fitLeft(model.id, modelWidth).padEnd(modelWidth, " ");
|
|
129
|
+
const downloadsCell = `${formatCount(model.downloads)}↓`.padStart(dlWidth, " ");
|
|
130
|
+
const likesCell = `${formatCount(model.likes)}♥`.padStart(likesWidth, " ");
|
|
131
|
+
const sizeCell = formatSize(model.sizeBytes).padStart(sizeWidth, " ");
|
|
132
|
+
const dateCell = model.lastModified.slice(0, 10).padStart(dateWidth, " ");
|
|
133
|
+
const row = `${prefix} ${modelCell} | ${downloadsCell} | ${likesCell} | ${sizeCell} | ${dateCell}`;
|
|
134
|
+
rows.push(index === state.selectedModelIndex ? highlightedRow(row) : row);
|
|
135
|
+
}
|
|
136
|
+
return rows;
|
|
137
|
+
}
|
|
138
|
+
function toFileFitLabel(state, file) {
|
|
139
|
+
if (!file.canRun) {
|
|
140
|
+
return file.reason;
|
|
141
|
+
}
|
|
142
|
+
if (file.sizeBytes === null || file.sizeBytes <= 0) {
|
|
143
|
+
return file.reason;
|
|
144
|
+
}
|
|
145
|
+
if (!Number.isFinite(state.vramGb) || state.vramGb <= 0) {
|
|
146
|
+
return file.reason;
|
|
147
|
+
}
|
|
148
|
+
const vramBytes = state.vramGb * 1024 ** 3;
|
|
149
|
+
return file.sizeBytes <= vramBytes ? "Fits on GPU" : "Fits on GPU+CPU";
|
|
150
|
+
}
|
|
151
|
+
function buildFileRows(state, contentWidth, rowCount) {
|
|
152
|
+
const rows = [];
|
|
153
|
+
if (state.files.length === 0) {
|
|
154
|
+
rows.push((0, colors_1.colorText)("No GGUF files found for this model.", colors_1.WARNING_YELLOW));
|
|
155
|
+
return rows;
|
|
156
|
+
}
|
|
157
|
+
const quantWidth = 21;
|
|
158
|
+
const sizeWidth = 5;
|
|
159
|
+
const fitWidth = 16;
|
|
160
|
+
const staticWidth = 2 + 3 + quantWidth + 3 + sizeWidth + 3 + fitWidth;
|
|
161
|
+
const fileWidth = Math.max(8, contentWidth - staticWidth);
|
|
162
|
+
const header = ` ${"File".padEnd(fileWidth, " ")} | ${"Quant".padEnd(quantWidth, " ")} | ${"Size".padStart(sizeWidth, " ")} | ${"Fit".padEnd(fitWidth, " ")}`;
|
|
163
|
+
rows.push((0, colors_1.horizontalGradient)(header, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW));
|
|
164
|
+
const visibleRowCount = Math.max(0, rowCount - 1);
|
|
165
|
+
const start = state.fileScrollOffset;
|
|
166
|
+
const end = Math.min(state.files.length, start + visibleRowCount);
|
|
167
|
+
for (let index = start; index < end; index++) {
|
|
168
|
+
const file = state.files[index];
|
|
169
|
+
if (!file)
|
|
170
|
+
continue;
|
|
171
|
+
const prefix = index === state.selectedFileIndex ? ">" : " ";
|
|
172
|
+
const basename = file.path.includes("/")
|
|
173
|
+
? (file.path.split("/").pop() ?? file.path)
|
|
174
|
+
: file.path;
|
|
175
|
+
const fileCell = fitLeft(basename, fileWidth).padEnd(fileWidth, " ");
|
|
176
|
+
const quantCell = fitLeft(file.quant, quantWidth).padEnd(quantWidth, " ");
|
|
177
|
+
const sizeCell = formatSize(file.sizeBytes).padStart(sizeWidth, " ");
|
|
178
|
+
const fitCell = fitLeft(toFileFitLabel(state, file), fitWidth).padEnd(fitWidth, " ");
|
|
179
|
+
const row = `${prefix} ${fileCell} | ${quantCell} | ${sizeCell} | ${fitCell}`;
|
|
180
|
+
if (index === state.selectedFileIndex) {
|
|
181
|
+
rows.push(highlightedRow(row));
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
rows.push((0, colors_1.colorText)(row, file.canRun ? colors_1.SUCCESS_GREEN : colors_1.ERROR_RED));
|
|
185
|
+
}
|
|
186
|
+
return rows;
|
|
187
|
+
}
|
|
188
|
+
function fillRows(rows, rowCount) {
|
|
189
|
+
if (rows.length >= rowCount) {
|
|
190
|
+
return rows.slice(0, rowCount);
|
|
191
|
+
}
|
|
192
|
+
return [...rows, ...new Array(rowCount - rows.length).fill("")];
|
|
193
|
+
}
|
|
194
|
+
function buildProgressBar(innerWidth, bytesDownloaded, totalBytes) {
|
|
195
|
+
if (totalBytes === null || totalBytes <= 0) {
|
|
196
|
+
return "Progress: downloading...";
|
|
197
|
+
}
|
|
198
|
+
const suffix = ` ${((bytesDownloaded / totalBytes) * 100).toFixed(1)}%`;
|
|
199
|
+
const barWidth = Math.max(1, innerWidth - suffix.length - 2);
|
|
200
|
+
const progress = Math.max(0, Math.min(1, bytesDownloaded / totalBytes));
|
|
201
|
+
const filled = Math.round(barWidth * progress);
|
|
202
|
+
const empty = Math.max(0, barWidth - filled);
|
|
203
|
+
return `[${"█".repeat(filled)}${"░".repeat(empty)}]${suffix}`;
|
|
204
|
+
}
|
|
205
|
+
function buildDownloadingRows(state, innerWidth) {
|
|
206
|
+
if (!state.download) {
|
|
207
|
+
return [`Loading: ${state.loadingMessage}`];
|
|
208
|
+
}
|
|
209
|
+
const filename = state.download.filename.includes("/")
|
|
210
|
+
? (state.download.filename.split("/").pop() ?? state.download.filename)
|
|
211
|
+
: state.download.filename;
|
|
212
|
+
const rows = [];
|
|
213
|
+
rows.push((0, colors_1.colorText)(`Downloading ${fitLeft(filename, Math.max(1, innerWidth - 12))}`, colors_1.GRADIENT_BLUE));
|
|
214
|
+
rows.push((0, colors_1.colorText)(buildProgressBar(innerWidth, state.download.bytesDownloaded, state.download.totalBytes), colors_1.SUCCESS_GREEN));
|
|
215
|
+
return rows;
|
|
216
|
+
}
|
|
217
|
+
function buildCancelConfirmRows(innerWidth) {
|
|
218
|
+
const message = fitLeft("Cancel download and delete partial file?", Math.max(1, innerWidth));
|
|
219
|
+
return [(0, colors_1.colorText)(message, colors_1.WARNING_YELLOW), ""];
|
|
220
|
+
}
|
|
221
|
+
function renderDownloaderLines(options) {
|
|
222
|
+
const width = Math.max(20, options.width);
|
|
223
|
+
const state = options.state;
|
|
224
|
+
const innerWidth = Math.max(1, width - 2);
|
|
225
|
+
const rows = [];
|
|
226
|
+
rows.push(makeBorderTop(width));
|
|
227
|
+
if (state.phase !== "downloading") {
|
|
228
|
+
const tabs = [
|
|
229
|
+
tabText("Most Downloaded", state.tab),
|
|
230
|
+
tabText("Top Rated", state.tab),
|
|
231
|
+
tabText("Newest", state.tab)
|
|
232
|
+
].join(" ");
|
|
233
|
+
const memText = `RAM: ${state.ramGb.toFixed(1)}GB | VRAM: ${state.vramGb.toFixed(1)}GB | Disk: ${state.diskFreeGb.toFixed(1)}GB`;
|
|
234
|
+
const memWidth = Math.min(charLength(memText), Math.max(24, innerWidth - 10));
|
|
235
|
+
const tabsWidth = Math.max(1, innerWidth - memWidth - 1);
|
|
236
|
+
let tabsCell = tabs;
|
|
237
|
+
if (charLength(tabsCell) > tabsWidth) {
|
|
238
|
+
tabsCell = fitLeft(" Most Downloaded Top Rated Newest ", tabsWidth);
|
|
239
|
+
}
|
|
240
|
+
const tabsPadding = " ".repeat(Math.max(0, tabsWidth - charLength(tabsCell)));
|
|
241
|
+
rows.push(lineWithBorders(`${tabsCell}${tabsPadding} ${fitRight(memText, memWidth)}`, innerWidth));
|
|
242
|
+
}
|
|
243
|
+
let bodyRows = [];
|
|
244
|
+
if (state.view === "models") {
|
|
245
|
+
if (state.phase === "loading-models") {
|
|
246
|
+
bodyRows = fillRows([`Loading: ${state.loadingMessage}`], DOWNLOADER_MODEL_BODY_ROWS);
|
|
247
|
+
}
|
|
248
|
+
else if (state.phase === "error" && state.errorMessage.length > 0) {
|
|
249
|
+
bodyRows = fillRows([`Error: ${state.errorMessage}`], DOWNLOADER_MODEL_BODY_ROWS);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
bodyRows = fillRows(buildModelRows(state, innerWidth, DOWNLOADER_MODEL_BODY_ROWS), DOWNLOADER_MODEL_BODY_ROWS);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else if (state.phase === "downloading") {
|
|
256
|
+
bodyRows = state.cancelConfirmOpen
|
|
257
|
+
? buildCancelConfirmRows(innerWidth)
|
|
258
|
+
: buildDownloadingRows(state, innerWidth);
|
|
259
|
+
}
|
|
260
|
+
else if (state.phase === "loading-files") {
|
|
261
|
+
bodyRows = fillRows([`Loading: ${state.loadingMessage}`], DOWNLOADER_FILE_BODY_ROWS);
|
|
262
|
+
}
|
|
263
|
+
else if (state.phase === "error" && state.errorMessage.length > 0) {
|
|
264
|
+
bodyRows = fillRows([`Error: ${state.errorMessage}`], DOWNLOADER_FILE_BODY_ROWS);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
const fileRows = [
|
|
268
|
+
(0, colors_1.colorText)(`Files for ${fitLeft(state.selectedRepoId, Math.max(1, innerWidth - 10))}`, colors_1.GRADIENT_BLUE),
|
|
269
|
+
...buildFileRows(state, innerWidth, DOWNLOADER_FILE_BODY_ROWS - 1)
|
|
270
|
+
];
|
|
271
|
+
bodyRows = fillRows(fileRows, DOWNLOADER_FILE_BODY_ROWS);
|
|
272
|
+
}
|
|
273
|
+
rows.push(...bodyRows.map((row) => lineWithBorders(row, innerWidth)));
|
|
274
|
+
if (state.phase === "downloading") {
|
|
275
|
+
const status = state.download?.statusText ?? "Downloading...";
|
|
276
|
+
const left = state.cancelConfirmOpen ? "[Enter] Yes [Esc] No" : "[Esc] Cancel";
|
|
277
|
+
const statusWidth = Math.max(1, innerWidth - Array.from(left).length - 1);
|
|
278
|
+
const footer = composeSplitFooter(left, fitRight(status, statusWidth), innerWidth);
|
|
279
|
+
rows.push(lineWithBorders((0, colors_1.horizontalGradient)(footer, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW), innerWidth));
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
const footer = state.view === "models"
|
|
283
|
+
? "[Enter] Select [↑/↓] Move [←/→] Sort [Esc] Close"
|
|
284
|
+
: "[Enter] Download [↑/↓] Move [Esc] Back";
|
|
285
|
+
rows.push(lineWithBorders((0, colors_1.horizontalGradient)(footer, colors_1.GRADIENT_PINK, colors_1.GRADIENT_YELLOW), innerWidth));
|
|
286
|
+
}
|
|
287
|
+
rows.push(makeBorderBottom(width));
|
|
288
|
+
return rows;
|
|
289
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Raw-stdin input parser for deterministic prompt editing actions. */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.InputEngine = void 0;
|
|
5
|
+
exports.parseCsiSequence = parseCsiSequence;
|
|
6
|
+
const node_string_decoder_1 = require("node:string_decoder");
|
|
7
|
+
function parseInteger(value) {
|
|
8
|
+
const parsed = Number(value);
|
|
9
|
+
return Number.isInteger(parsed) ? parsed : null;
|
|
10
|
+
}
|
|
11
|
+
function isEnterKeyCode(code) {
|
|
12
|
+
return code === 1 || code === 10 || code === 13 || code === 57414;
|
|
13
|
+
}
|
|
14
|
+
function toEnterAction(modifier) {
|
|
15
|
+
return modifier > 1 ? { type: "newline" } : { type: "submit" };
|
|
16
|
+
}
|
|
17
|
+
function parseEnterActionFromCsiBody(body, final) {
|
|
18
|
+
if (final === "u" || final === "~" || final === "M") {
|
|
19
|
+
const keyOnly = body.match(/^(\d+)$/);
|
|
20
|
+
if (keyOnly) {
|
|
21
|
+
const keyCode = parseInteger(keyOnly[1] ?? "");
|
|
22
|
+
if (keyCode !== null && isEnterKeyCode(keyCode)) {
|
|
23
|
+
return { type: "submit" };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const keyAndModifier = body.match(/^(\d+);(\d+)$/);
|
|
27
|
+
if (keyAndModifier) {
|
|
28
|
+
const keyCode = parseInteger(keyAndModifier[1] ?? "");
|
|
29
|
+
const modifier = parseInteger(keyAndModifier[2] ?? "");
|
|
30
|
+
if (keyCode !== null && modifier !== null && isEnterKeyCode(keyCode) && modifier >= 1) {
|
|
31
|
+
return toEnterAction(modifier);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (final === "~") {
|
|
36
|
+
const legacyModifyOtherKeys = body.match(/^27;(\d+);(\d+)$/);
|
|
37
|
+
if (legacyModifyOtherKeys) {
|
|
38
|
+
const first = parseInteger(legacyModifyOtherKeys[1] ?? "");
|
|
39
|
+
const second = parseInteger(legacyModifyOtherKeys[2] ?? "");
|
|
40
|
+
if (first === null || second === null) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (isEnterKeyCode(first) && second >= 1) {
|
|
44
|
+
return toEnterAction(second);
|
|
45
|
+
}
|
|
46
|
+
if (isEnterKeyCode(second) && first >= 1) {
|
|
47
|
+
return toEnterAction(first);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
function parseMouseActionFromCsiBody(body, final) {
|
|
54
|
+
if (final !== "M" && final !== "m") {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (!body.startsWith("<")) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const params = body.slice(1).split(";");
|
|
61
|
+
const buttonCode = parseInteger(params[0] ?? "");
|
|
62
|
+
if (buttonCode === null) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
// SGR mouse wheel encodes bit 6 for wheel + bit 0 for direction.
|
|
66
|
+
// Modifier bits may be present (shift/alt/ctrl), so match by bitmask.
|
|
67
|
+
if ((buttonCode & 0x40) !== 0) {
|
|
68
|
+
return (buttonCode & 0x01) === 0
|
|
69
|
+
? { type: "scroll-line-up" }
|
|
70
|
+
: { type: "scroll-line-down" };
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
function parseCsiSequence(sequence) {
|
|
75
|
+
if (!sequence.startsWith("\x1b[")) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (sequence.length < 3) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const final = sequence[sequence.length - 1] ?? "";
|
|
82
|
+
const body = sequence.slice(2, -1);
|
|
83
|
+
const enterAction = parseEnterActionFromCsiBody(body, final);
|
|
84
|
+
if (enterAction) {
|
|
85
|
+
return enterAction;
|
|
86
|
+
}
|
|
87
|
+
const mouseAction = parseMouseActionFromCsiBody(body, final);
|
|
88
|
+
if (mouseAction) {
|
|
89
|
+
return mouseAction;
|
|
90
|
+
}
|
|
91
|
+
if (final === "A")
|
|
92
|
+
return { type: "move-up" };
|
|
93
|
+
if (final === "B")
|
|
94
|
+
return { type: "move-down" };
|
|
95
|
+
if (final === "C")
|
|
96
|
+
return { type: "move-right" };
|
|
97
|
+
if (final === "D")
|
|
98
|
+
return { type: "move-left" };
|
|
99
|
+
if (final === "H")
|
|
100
|
+
return { type: "home" };
|
|
101
|
+
if (final === "F")
|
|
102
|
+
return { type: "end" };
|
|
103
|
+
if (final === "Z")
|
|
104
|
+
return { type: "tab" };
|
|
105
|
+
if (final === "~") {
|
|
106
|
+
const firstParam = body.split(";")[0] ?? "";
|
|
107
|
+
if (firstParam === "1" || firstParam === "7")
|
|
108
|
+
return { type: "home" };
|
|
109
|
+
if (firstParam === "4" || firstParam === "8")
|
|
110
|
+
return { type: "end" };
|
|
111
|
+
if (firstParam === "3")
|
|
112
|
+
return { type: "delete" };
|
|
113
|
+
if (firstParam === "5")
|
|
114
|
+
return { type: "scroll-page-up" };
|
|
115
|
+
if (firstParam === "6")
|
|
116
|
+
return { type: "scroll-page-down" };
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
function isCsiFinalByte(byte) {
|
|
121
|
+
return byte >= 0x40 && byte <= 0x7e;
|
|
122
|
+
}
|
|
123
|
+
function parseEscapeSequence(buffer, startIndex) {
|
|
124
|
+
if (startIndex + 1 >= buffer.length) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const second = buffer[startIndex + 1] ?? 0;
|
|
128
|
+
if (second === 0x5b) {
|
|
129
|
+
let index = startIndex + 2;
|
|
130
|
+
while (index < buffer.length) {
|
|
131
|
+
const byte = buffer[index] ?? 0;
|
|
132
|
+
if (isCsiFinalByte(byte)) {
|
|
133
|
+
const sequence = buffer.subarray(startIndex, index + 1).toString("latin1");
|
|
134
|
+
return { action: parseCsiSequence(sequence), nextIndex: index + 1 };
|
|
135
|
+
}
|
|
136
|
+
index += 1;
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
if (second === 0x4f) {
|
|
141
|
+
if (startIndex + 2 >= buffer.length) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const third = buffer[startIndex + 2] ?? 0;
|
|
145
|
+
if (third === 0x4d) {
|
|
146
|
+
return { action: { type: "submit" }, nextIndex: startIndex + 3 };
|
|
147
|
+
}
|
|
148
|
+
return { action: null, nextIndex: startIndex + 3 };
|
|
149
|
+
}
|
|
150
|
+
if (second === 0x0d || second === 0x0a) {
|
|
151
|
+
return { action: { type: "newline" }, nextIndex: startIndex + 2 };
|
|
152
|
+
}
|
|
153
|
+
return { action: null, nextIndex: startIndex + 2 };
|
|
154
|
+
}
|
|
155
|
+
class InputEngine {
|
|
156
|
+
pending = Buffer.alloc(0);
|
|
157
|
+
decoder = new node_string_decoder_1.StringDecoder("utf8");
|
|
158
|
+
reset() {
|
|
159
|
+
this.pending = Buffer.alloc(0);
|
|
160
|
+
this.decoder.end();
|
|
161
|
+
}
|
|
162
|
+
pushChunk(chunk) {
|
|
163
|
+
const incoming = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "latin1");
|
|
164
|
+
if (incoming.length === 0) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
this.pending =
|
|
168
|
+
this.pending.length > 0 ? Buffer.concat([this.pending, incoming]) : Buffer.from(incoming);
|
|
169
|
+
const actions = [];
|
|
170
|
+
let index = 0;
|
|
171
|
+
let textStart = null;
|
|
172
|
+
const flushText = (endIndex) => {
|
|
173
|
+
if (textStart === null || endIndex <= textStart) {
|
|
174
|
+
textStart = null;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const text = this.decoder.write(this.pending.subarray(textStart, endIndex));
|
|
178
|
+
if (text.length > 0) {
|
|
179
|
+
actions.push({ type: "insert", text });
|
|
180
|
+
}
|
|
181
|
+
textStart = null;
|
|
182
|
+
};
|
|
183
|
+
while (index < this.pending.length) {
|
|
184
|
+
const byte = this.pending[index] ?? 0;
|
|
185
|
+
if (byte === 0x1b) {
|
|
186
|
+
flushText(index);
|
|
187
|
+
const parsed = parseEscapeSequence(this.pending, index);
|
|
188
|
+
if (!parsed) {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
if (parsed.action) {
|
|
192
|
+
actions.push(parsed.action);
|
|
193
|
+
}
|
|
194
|
+
index = parsed.nextIndex;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (byte === 0x03) {
|
|
198
|
+
flushText(index);
|
|
199
|
+
actions.push({ type: "cancel" });
|
|
200
|
+
index += 1;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (byte === 0x7f || byte === 0x08) {
|
|
204
|
+
flushText(index);
|
|
205
|
+
actions.push({ type: "backspace" });
|
|
206
|
+
index += 1;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (byte === 0x0d) {
|
|
210
|
+
flushText(index);
|
|
211
|
+
actions.push({ type: "submit" });
|
|
212
|
+
index += 1;
|
|
213
|
+
if (this.pending[index] === 0x0a) {
|
|
214
|
+
index += 1;
|
|
215
|
+
}
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (byte === 0x0a) {
|
|
219
|
+
flushText(index);
|
|
220
|
+
actions.push({ type: "newline" });
|
|
221
|
+
index += 1;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (byte === 0x09) {
|
|
225
|
+
flushText(index);
|
|
226
|
+
actions.push({ type: "tab" });
|
|
227
|
+
index += 1;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (byte < 0x20) {
|
|
231
|
+
flushText(index);
|
|
232
|
+
index += 1;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (textStart === null) {
|
|
236
|
+
textStart = index;
|
|
237
|
+
}
|
|
238
|
+
index += 1;
|
|
239
|
+
}
|
|
240
|
+
// Treat lone ESC as cancel (close modal/back) when it is not part of a CSI sequence.
|
|
241
|
+
if (index < this.pending.length && this.pending[index] === 0x1b && this.pending.length - index === 1) {
|
|
242
|
+
actions.push({ type: "cancel" });
|
|
243
|
+
index += 1;
|
|
244
|
+
}
|
|
245
|
+
flushText(index);
|
|
246
|
+
this.pending = this.pending.subarray(index);
|
|
247
|
+
return actions;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
exports.InputEngine = InputEngine;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decideConfirmationAction = decideConfirmationAction;
|
|
4
|
+
exports.routeVtInput = routeVtInput;
|
|
5
|
+
function decideConfirmationAction(actions) {
|
|
6
|
+
for (const action of actions) {
|
|
7
|
+
if (action.type === "cancel") {
|
|
8
|
+
return "deny";
|
|
9
|
+
}
|
|
10
|
+
if (action.type === "submit") {
|
|
11
|
+
return "approve";
|
|
12
|
+
}
|
|
13
|
+
if (action.type === "insert") {
|
|
14
|
+
const normalized = action.text.trim().toLowerCase();
|
|
15
|
+
if (normalized === "y" || normalized === "yes") {
|
|
16
|
+
return "approve";
|
|
17
|
+
}
|
|
18
|
+
if (normalized === "n" || normalized === "no") {
|
|
19
|
+
return "deny";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function routeVtInput(sequence, escapePending) {
|
|
26
|
+
const bytes = Buffer.from(sequence, "latin1");
|
|
27
|
+
if (bytes.includes(0x11)) {
|
|
28
|
+
return {
|
|
29
|
+
exitToChat: true,
|
|
30
|
+
nextEscapePending: false,
|
|
31
|
+
passthrough: null
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (sequence === "\u001b") {
|
|
35
|
+
if (escapePending) {
|
|
36
|
+
return {
|
|
37
|
+
exitToChat: true,
|
|
38
|
+
nextEscapePending: false,
|
|
39
|
+
passthrough: null
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
exitToChat: false,
|
|
44
|
+
nextEscapePending: true,
|
|
45
|
+
passthrough: null
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
exitToChat: false,
|
|
50
|
+
nextEscapePending: false,
|
|
51
|
+
passthrough: sequence
|
|
52
|
+
};
|
|
53
|
+
}
|