@oakoliver/gum 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/LICENSE +22 -0
- package/dist/cjs/index.cjs +2161 -0
- package/dist/esm/cli.js +2033 -0
- package/dist/esm/index.js +2104 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/cli.d.ts.map +1 -0
- package/dist/types/commands/choose.d.ts +7 -0
- package/dist/types/commands/choose.d.ts.map +1 -0
- package/dist/types/commands/confirm.d.ts +7 -0
- package/dist/types/commands/confirm.d.ts.map +1 -0
- package/dist/types/commands/file.d.ts +7 -0
- package/dist/types/commands/file.d.ts.map +1 -0
- package/dist/types/commands/format.d.ts +7 -0
- package/dist/types/commands/format.d.ts.map +1 -0
- package/dist/types/commands/input.d.ts +7 -0
- package/dist/types/commands/input.d.ts.map +1 -0
- package/dist/types/commands/join.d.ts +7 -0
- package/dist/types/commands/join.d.ts.map +1 -0
- package/dist/types/commands/log.d.ts +7 -0
- package/dist/types/commands/log.d.ts.map +1 -0
- package/dist/types/commands/pager.d.ts +7 -0
- package/dist/types/commands/pager.d.ts.map +1 -0
- package/dist/types/commands/spin.d.ts +7 -0
- package/dist/types/commands/spin.d.ts.map +1 -0
- package/dist/types/commands/style.d.ts +7 -0
- package/dist/types/commands/style.d.ts.map +1 -0
- package/dist/types/commands/table.d.ts +7 -0
- package/dist/types/commands/table.d.ts.map +1 -0
- package/dist/types/commands/write.d.ts +7 -0
- package/dist/types/commands/write.d.ts.map +1 -0
- package/dist/types/fuzzy.d.ts +36 -0
- package/dist/types/fuzzy.d.ts.map +1 -0
- package/dist/types/index.d.ts +22 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/internal/decode.d.ts +11 -0
- package/dist/types/internal/decode.d.ts.map +1 -0
- package/dist/types/internal/exit.d.ts +9 -0
- package/dist/types/internal/exit.d.ts.map +1 -0
- package/dist/types/internal/files.d.ts +9 -0
- package/dist/types/internal/files.d.ts.map +1 -0
- package/dist/types/internal/index.d.ts +7 -0
- package/dist/types/internal/index.d.ts.map +1 -0
- package/dist/types/internal/stdin.d.ts +21 -0
- package/dist/types/internal/stdin.d.ts.map +1 -0
- package/dist/types/internal/timeout.d.ts +18 -0
- package/dist/types/internal/timeout.d.ts.map +1 -0
- package/dist/types/internal/tty.d.ts +13 -0
- package/dist/types/internal/tty.d.ts.map +1 -0
- package/dist/types/parser.d.ts +36 -0
- package/dist/types/parser.d.ts.map +1 -0
- package/dist/types/style.d.ts +37 -0
- package/dist/types/style.d.ts.map +1 -0
- package/package.json +66 -0
package/dist/esm/cli.js
ADDED
|
@@ -0,0 +1,2033 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/parser.ts
|
|
4
|
+
var BOOL_FLAGS = /* @__PURE__ */ new Set([
|
|
5
|
+
"no-limit",
|
|
6
|
+
"no-show-help",
|
|
7
|
+
"no-strip-ansi",
|
|
8
|
+
"ordered",
|
|
9
|
+
"reverse",
|
|
10
|
+
"no-strict",
|
|
11
|
+
"strict",
|
|
12
|
+
"select-if-one",
|
|
13
|
+
"sort",
|
|
14
|
+
"no-sort",
|
|
15
|
+
"affirmative",
|
|
16
|
+
"negative",
|
|
17
|
+
"default",
|
|
18
|
+
"bold",
|
|
19
|
+
"faint",
|
|
20
|
+
"italic",
|
|
21
|
+
"strikethrough",
|
|
22
|
+
"underline",
|
|
23
|
+
"horizontal",
|
|
24
|
+
"vertical",
|
|
25
|
+
"show-line-numbers",
|
|
26
|
+
"soft-wrap",
|
|
27
|
+
"show-help",
|
|
28
|
+
"strip-ansi",
|
|
29
|
+
"cursor.bold",
|
|
30
|
+
"cursor.faint",
|
|
31
|
+
"cursor.italic",
|
|
32
|
+
"cursor.strikethrough",
|
|
33
|
+
"cursor.underline",
|
|
34
|
+
"header.bold",
|
|
35
|
+
"header.faint",
|
|
36
|
+
"header.italic",
|
|
37
|
+
"header.strikethrough",
|
|
38
|
+
"header.underline",
|
|
39
|
+
"selected.bold",
|
|
40
|
+
"selected.faint",
|
|
41
|
+
"selected.italic",
|
|
42
|
+
"selected.strikethrough",
|
|
43
|
+
"selected.underline",
|
|
44
|
+
"unselected.bold",
|
|
45
|
+
"unselected.faint",
|
|
46
|
+
"unselected.italic",
|
|
47
|
+
"unselected.strikethrough",
|
|
48
|
+
"unselected.underline",
|
|
49
|
+
"text.bold",
|
|
50
|
+
"text.faint",
|
|
51
|
+
"text.italic",
|
|
52
|
+
"text.strikethrough",
|
|
53
|
+
"text.underline",
|
|
54
|
+
"match.bold",
|
|
55
|
+
"match.faint",
|
|
56
|
+
"match.italic",
|
|
57
|
+
"match.strikethrough",
|
|
58
|
+
"match.underline",
|
|
59
|
+
"indicator.bold",
|
|
60
|
+
"indicator.faint",
|
|
61
|
+
"indicator.italic",
|
|
62
|
+
"indicator.strikethrough",
|
|
63
|
+
"indicator.underline",
|
|
64
|
+
"prompt.bold",
|
|
65
|
+
"prompt.faint",
|
|
66
|
+
"prompt.italic",
|
|
67
|
+
"prompt.strikethrough",
|
|
68
|
+
"prompt.underline",
|
|
69
|
+
"item.bold",
|
|
70
|
+
"item.faint",
|
|
71
|
+
"item.italic",
|
|
72
|
+
"item.strikethrough",
|
|
73
|
+
"item.underline",
|
|
74
|
+
"fuzzy",
|
|
75
|
+
"exact"
|
|
76
|
+
]);
|
|
77
|
+
function parseArgs(argv) {
|
|
78
|
+
if (argv.length === 0) {
|
|
79
|
+
return { command: "", flags: {}, args: [] };
|
|
80
|
+
}
|
|
81
|
+
const command = argv[0];
|
|
82
|
+
if (command === "--help" || command === "-h") {
|
|
83
|
+
return { command: "help", flags: {}, args: [] };
|
|
84
|
+
}
|
|
85
|
+
if (command === "--version" || command === "-v") {
|
|
86
|
+
return { command: "version", flags: {}, args: [] };
|
|
87
|
+
}
|
|
88
|
+
const flags = {};
|
|
89
|
+
const args = [];
|
|
90
|
+
let i = 1;
|
|
91
|
+
let pastFlags = false;
|
|
92
|
+
while (i < argv.length) {
|
|
93
|
+
const arg = argv[i];
|
|
94
|
+
if (pastFlags) {
|
|
95
|
+
args.push(arg);
|
|
96
|
+
i++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (arg === "--") {
|
|
100
|
+
pastFlags = true;
|
|
101
|
+
i++;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (arg.startsWith("--")) {
|
|
105
|
+
const eqIndex = arg.indexOf("=");
|
|
106
|
+
if (eqIndex !== -1) {
|
|
107
|
+
const key = arg.substring(2, eqIndex);
|
|
108
|
+
const value = arg.substring(eqIndex + 1);
|
|
109
|
+
flags[key] = value;
|
|
110
|
+
i++;
|
|
111
|
+
} else {
|
|
112
|
+
const key = arg.substring(2);
|
|
113
|
+
if (key.startsWith("no-") && !BOOL_FLAGS.has(key)) {
|
|
114
|
+
const positiveKey = key.substring(3);
|
|
115
|
+
flags[positiveKey] = false;
|
|
116
|
+
i++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (isBoolFlag(key)) {
|
|
120
|
+
flags[key] = true;
|
|
121
|
+
i++;
|
|
122
|
+
} else if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
|
|
123
|
+
flags[key] = argv[i + 1];
|
|
124
|
+
i += 2;
|
|
125
|
+
} else {
|
|
126
|
+
flags[key] = true;
|
|
127
|
+
i++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
args.push(arg);
|
|
132
|
+
i++;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { command, flags, args };
|
|
136
|
+
}
|
|
137
|
+
function isBoolFlag(key) {
|
|
138
|
+
if (BOOL_FLAGS.has(key)) return true;
|
|
139
|
+
const parts = key.split(".");
|
|
140
|
+
if (parts.length === 2) {
|
|
141
|
+
const suffix = parts[1];
|
|
142
|
+
if (["bold", "faint", "italic", "strikethrough", "underline"].includes(suffix)) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
function flagStr(flags, key, defaultValue) {
|
|
149
|
+
const v = flags[key];
|
|
150
|
+
if (v === void 0) return defaultValue;
|
|
151
|
+
if (typeof v === "boolean") return String(v);
|
|
152
|
+
return v;
|
|
153
|
+
}
|
|
154
|
+
function flagInt(flags, key, defaultValue) {
|
|
155
|
+
const v = flags[key];
|
|
156
|
+
if (v === void 0) return defaultValue;
|
|
157
|
+
const n = parseInt(String(v), 10);
|
|
158
|
+
return isNaN(n) ? defaultValue : n;
|
|
159
|
+
}
|
|
160
|
+
function flagBool(flags, key, defaultValue = false) {
|
|
161
|
+
const v = flags[key];
|
|
162
|
+
if (v === void 0) return defaultValue;
|
|
163
|
+
if (typeof v === "boolean") return v;
|
|
164
|
+
return v === "true" || v === "1" || v === "yes";
|
|
165
|
+
}
|
|
166
|
+
function flagWithEnv(flags, key, envKey, defaultValue) {
|
|
167
|
+
const v = flags[key];
|
|
168
|
+
if (v !== void 0) return typeof v === "boolean" ? String(v) : v;
|
|
169
|
+
const envVal = process.env[envKey];
|
|
170
|
+
if (envVal !== void 0) return envVal;
|
|
171
|
+
return defaultValue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/commands/choose.ts
|
|
175
|
+
import { Program, KeyPressMsg, KeyCode, KeyMod, Quit, WindowSizeMsg } from "@oakoliver/bubbletea";
|
|
176
|
+
import { newPaginator, PaginatorType } from "@oakoliver/bubbles";
|
|
177
|
+
|
|
178
|
+
// src/style.ts
|
|
179
|
+
import { newStyle } from "@oakoliver/lipgloss";
|
|
180
|
+
|
|
181
|
+
// src/internal/decode.ts
|
|
182
|
+
var alignMap = {
|
|
183
|
+
center: 0.5,
|
|
184
|
+
left: 0,
|
|
185
|
+
top: 0,
|
|
186
|
+
bottom: 1,
|
|
187
|
+
right: 1,
|
|
188
|
+
middle: 0.5
|
|
189
|
+
};
|
|
190
|
+
var borderMap = {
|
|
191
|
+
double: "double",
|
|
192
|
+
hidden: "hidden",
|
|
193
|
+
none: "none",
|
|
194
|
+
normal: "normal",
|
|
195
|
+
rounded: "rounded",
|
|
196
|
+
thick: "thick"
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/style.ts
|
|
200
|
+
function parsePadding(s) {
|
|
201
|
+
if (!s) return [0, 0, 0, 0];
|
|
202
|
+
const tokens = s.trim().split(/\s+/);
|
|
203
|
+
if (tokens.length > 4) return [0, 0, 0, 0];
|
|
204
|
+
const ints = [];
|
|
205
|
+
for (const token of tokens) {
|
|
206
|
+
const n = parseInt(token, 10);
|
|
207
|
+
if (isNaN(n)) return [0, 0, 0, 0];
|
|
208
|
+
ints.push(n);
|
|
209
|
+
}
|
|
210
|
+
if (ints.length === 1) return [ints[0], ints[0], ints[0], ints[0]];
|
|
211
|
+
if (ints.length === 2) return [ints[0], ints[1], ints[0], ints[1]];
|
|
212
|
+
if (ints.length === 4) return [ints[0], ints[1], ints[2], ints[3]];
|
|
213
|
+
return [0, 0, 0, 0];
|
|
214
|
+
}
|
|
215
|
+
function toLipgloss(opts) {
|
|
216
|
+
let s = newStyle();
|
|
217
|
+
if (opts.foreground) s = s.foreground(opts.foreground);
|
|
218
|
+
if (opts.background) s = s.background(opts.background);
|
|
219
|
+
if (opts.borderForeground) s = s.borderForeground(opts.borderForeground);
|
|
220
|
+
if (opts.borderBackground) s = s.borderBackground(opts.borderBackground);
|
|
221
|
+
if (opts.align && alignMap[opts.align] !== void 0) {
|
|
222
|
+
s = s.align(alignMap[opts.align]);
|
|
223
|
+
}
|
|
224
|
+
if (opts.border && borderMap[opts.border]) {
|
|
225
|
+
s = s.border(opts.border);
|
|
226
|
+
}
|
|
227
|
+
if (opts.height !== void 0 && opts.height > 0) s = s.height(opts.height);
|
|
228
|
+
if (opts.width !== void 0 && opts.width > 0) s = s.width(opts.width);
|
|
229
|
+
if (opts.margin) {
|
|
230
|
+
const [t, r, b, l] = parsePadding(opts.margin);
|
|
231
|
+
s = s.marginTop(t).marginRight(r).marginBottom(b).marginLeft(l);
|
|
232
|
+
}
|
|
233
|
+
if (opts.padding) {
|
|
234
|
+
const [t, r, b, l] = parsePadding(opts.padding);
|
|
235
|
+
s = s.paddingTop(t).paddingRight(r).paddingBottom(b).paddingLeft(l);
|
|
236
|
+
}
|
|
237
|
+
if (opts.bold) s = s.bold(true);
|
|
238
|
+
if (opts.faint) s = s.faint(true);
|
|
239
|
+
if (opts.italic) s = s.italic(true);
|
|
240
|
+
if (opts.strikethrough) s = s.strikethrough(true);
|
|
241
|
+
if (opts.underline) s = s.underline(true);
|
|
242
|
+
return s;
|
|
243
|
+
}
|
|
244
|
+
function extractStyleOptions(flags, prefix) {
|
|
245
|
+
const get = (key) => flags[`${prefix}.${key}`] ?? flags[`${prefix}-${key}`];
|
|
246
|
+
return {
|
|
247
|
+
foreground: get("foreground"),
|
|
248
|
+
background: get("background"),
|
|
249
|
+
border: get("border"),
|
|
250
|
+
borderBackground: get("border-background") ?? get("borderBackground"),
|
|
251
|
+
borderForeground: get("border-foreground") ?? get("borderForeground"),
|
|
252
|
+
align: get("align"),
|
|
253
|
+
height: get("height") !== void 0 ? parseInt(get("height"), 10) : void 0,
|
|
254
|
+
width: get("width") !== void 0 ? parseInt(get("width"), 10) : void 0,
|
|
255
|
+
margin: get("margin"),
|
|
256
|
+
padding: get("padding"),
|
|
257
|
+
bold: get("bold") === true || get("bold") === "true",
|
|
258
|
+
faint: get("faint") === true || get("faint") === "true",
|
|
259
|
+
italic: get("italic") === true || get("italic") === "true",
|
|
260
|
+
strikethrough: get("strikethrough") === true || get("strikethrough") === "true",
|
|
261
|
+
underline: get("underline") === true || get("underline") === "true"
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/internal/stdin.ts
|
|
266
|
+
import * as fs from "node:fs";
|
|
267
|
+
function stripAnsi(s) {
|
|
268
|
+
return s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
|
|
269
|
+
}
|
|
270
|
+
function isStdinEmpty() {
|
|
271
|
+
try {
|
|
272
|
+
const stat = fs.fstatSync(0);
|
|
273
|
+
if (stat.isFIFO()) return false;
|
|
274
|
+
if (stat.size > 0) return false;
|
|
275
|
+
return true;
|
|
276
|
+
} catch {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function readStdin(opts = {}) {
|
|
281
|
+
if (isStdinEmpty()) {
|
|
282
|
+
throw new Error("stdin is empty");
|
|
283
|
+
}
|
|
284
|
+
const chunks = [];
|
|
285
|
+
const buf = Buffer.alloc(1024);
|
|
286
|
+
let bytesRead;
|
|
287
|
+
try {
|
|
288
|
+
while (true) {
|
|
289
|
+
bytesRead = fs.readSync(0, buf, 0, buf.length, null);
|
|
290
|
+
if (bytesRead === 0) break;
|
|
291
|
+
chunks.push(Buffer.from(buf.subarray(0, bytesRead)));
|
|
292
|
+
if (opts.singleLine) {
|
|
293
|
+
const combined = Buffer.concat(chunks).toString("utf-8");
|
|
294
|
+
const nlIndex = combined.indexOf("\n");
|
|
295
|
+
if (nlIndex !== -1) {
|
|
296
|
+
const line = combined.substring(0, nlIndex).trim();
|
|
297
|
+
return opts.stripAnsi ? stripAnsi(line) : line;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
} catch {
|
|
302
|
+
}
|
|
303
|
+
let result = Buffer.concat(chunks).toString("utf-8").trim();
|
|
304
|
+
if (opts.singleLine) {
|
|
305
|
+
const nlIndex = result.indexOf("\n");
|
|
306
|
+
if (nlIndex !== -1) {
|
|
307
|
+
result = result.substring(0, nlIndex).trim();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (opts.stripAnsi) {
|
|
311
|
+
result = stripAnsi(result);
|
|
312
|
+
}
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/internal/tty.ts
|
|
317
|
+
import * as tty from "node:tty";
|
|
318
|
+
var _isTTY;
|
|
319
|
+
function isTTY() {
|
|
320
|
+
if (_isTTY === void 0) {
|
|
321
|
+
_isTTY = tty.isatty(1);
|
|
322
|
+
}
|
|
323
|
+
return _isTTY;
|
|
324
|
+
}
|
|
325
|
+
function println(s) {
|
|
326
|
+
if (isTTY()) {
|
|
327
|
+
console.log(s);
|
|
328
|
+
} else {
|
|
329
|
+
console.log(stripAnsi(s));
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/internal/exit.ts
|
|
334
|
+
var STATUS_ABORTED = 130;
|
|
335
|
+
|
|
336
|
+
// src/commands/choose.ts
|
|
337
|
+
function createModel(parsed) {
|
|
338
|
+
const flags = parsed.flags;
|
|
339
|
+
let items = [];
|
|
340
|
+
if (parsed.args.length > 0) {
|
|
341
|
+
items = parsed.args;
|
|
342
|
+
} else if (!isStdinEmpty()) {
|
|
343
|
+
const stdinContent = readStdin();
|
|
344
|
+
items = stdinContent.split("\n").filter((line) => line.length > 0);
|
|
345
|
+
}
|
|
346
|
+
if (items.length === 0) {
|
|
347
|
+
console.error("Error: no items provided");
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
const limit = flagInt(flags, "limit", 1);
|
|
351
|
+
const noLimit = flagBool(flags, "no-limit", false);
|
|
352
|
+
const height = flagInt(flags, "height", 10);
|
|
353
|
+
const header = flagStr(flags, "header", "");
|
|
354
|
+
const cursorPrefix = flagStr(flags, "cursor", "> ");
|
|
355
|
+
const selectedPrefix = flagStr(flags, "selected.prefix", "[\u2713] ");
|
|
356
|
+
const unselectedPrefix = flagStr(flags, "unselected.prefix", "[ ] ");
|
|
357
|
+
const selectIfOne = flagBool(flags, "select-if-one", false);
|
|
358
|
+
const ordered = flagBool(flags, "ordered", false);
|
|
359
|
+
const selectedItems = flagStr(flags, "selected", "").split(",").filter(Boolean);
|
|
360
|
+
const cursorStyle = toLipgloss(extractStyleOptions(flags, "cursor"));
|
|
361
|
+
const itemStyle = toLipgloss(extractStyleOptions(flags, "item"));
|
|
362
|
+
const selectedItemStyle = toLipgloss(extractStyleOptions(flags, "selected"));
|
|
363
|
+
const headerStyle = toLipgloss(extractStyleOptions(flags, "header"));
|
|
364
|
+
const choiceItems = items.map((text) => ({
|
|
365
|
+
text,
|
|
366
|
+
selected: selectedItems.includes(text),
|
|
367
|
+
order: 0
|
|
368
|
+
}));
|
|
369
|
+
const numSelected = choiceItems.filter((i) => i.selected).length;
|
|
370
|
+
const paginator = newPaginator();
|
|
371
|
+
paginator.type = PaginatorType.Dots;
|
|
372
|
+
paginator.perPage = height;
|
|
373
|
+
paginator.setTotalPages(choiceItems.length);
|
|
374
|
+
if (selectIfOne && choiceItems.length === 1) {
|
|
375
|
+
choiceItems[0].selected = true;
|
|
376
|
+
}
|
|
377
|
+
const isMulti = noLimit || limit > 1;
|
|
378
|
+
return {
|
|
379
|
+
items: choiceItems,
|
|
380
|
+
cursor: 0,
|
|
381
|
+
paginator,
|
|
382
|
+
limit: noLimit ? 0 : limit,
|
|
383
|
+
noLimit,
|
|
384
|
+
numSelected,
|
|
385
|
+
aborted: false,
|
|
386
|
+
quitting: false,
|
|
387
|
+
header,
|
|
388
|
+
cursorPrefix,
|
|
389
|
+
selectedPrefix: isMulti ? selectedPrefix : cursorPrefix,
|
|
390
|
+
unselectedPrefix: isMulti ? unselectedPrefix : " ",
|
|
391
|
+
cursorStyle,
|
|
392
|
+
itemStyle,
|
|
393
|
+
selectedItemStyle,
|
|
394
|
+
headerStyle,
|
|
395
|
+
height,
|
|
396
|
+
selectIfOne,
|
|
397
|
+
init() {
|
|
398
|
+
return null;
|
|
399
|
+
},
|
|
400
|
+
update(msg) {
|
|
401
|
+
if (msg instanceof WindowSizeMsg) {
|
|
402
|
+
this.paginator.perPage = msg.height - 1;
|
|
403
|
+
if (this.header) this.paginator.perPage--;
|
|
404
|
+
this.paginator.setTotalPages(this.items.length);
|
|
405
|
+
return [this, null];
|
|
406
|
+
}
|
|
407
|
+
if (msg instanceof KeyPressMsg) {
|
|
408
|
+
if (msg.mod & KeyMod.Ctrl && msg.text === "c") {
|
|
409
|
+
this.aborted = true;
|
|
410
|
+
this.quitting = true;
|
|
411
|
+
return [this, () => Quit()];
|
|
412
|
+
}
|
|
413
|
+
if (msg.code === KeyCode.Escape) {
|
|
414
|
+
this.aborted = true;
|
|
415
|
+
this.quitting = true;
|
|
416
|
+
return [this, () => Quit()];
|
|
417
|
+
}
|
|
418
|
+
if (msg.code === KeyCode.Enter) {
|
|
419
|
+
this.quitting = true;
|
|
420
|
+
if (!this.noLimit && this.limit === 1 && this.numSelected === 0) {
|
|
421
|
+
this.items[this.cursor].selected = true;
|
|
422
|
+
}
|
|
423
|
+
return [this, () => Quit()];
|
|
424
|
+
}
|
|
425
|
+
if (msg.code === KeyCode.Down || msg.text === "j" || msg.mod & KeyMod.Ctrl && msg.text === "n") {
|
|
426
|
+
this.cursor = Math.min(this.cursor + 1, this.items.length - 1);
|
|
427
|
+
const [start, end] = this.paginator.getSliceBounds(this.items.length);
|
|
428
|
+
if (this.cursor >= end) this.paginator.nextPage();
|
|
429
|
+
return [this, null];
|
|
430
|
+
}
|
|
431
|
+
if (msg.code === KeyCode.Up || msg.text === "k" || msg.mod & KeyMod.Ctrl && msg.text === "p") {
|
|
432
|
+
this.cursor = Math.max(this.cursor - 1, 0);
|
|
433
|
+
const [start] = this.paginator.getSliceBounds(this.items.length);
|
|
434
|
+
if (this.cursor < start) this.paginator.prevPage();
|
|
435
|
+
return [this, null];
|
|
436
|
+
}
|
|
437
|
+
if (msg.code === KeyCode.Home || msg.text === "g") {
|
|
438
|
+
this.cursor = 0;
|
|
439
|
+
this.paginator.page = 0;
|
|
440
|
+
return [this, null];
|
|
441
|
+
}
|
|
442
|
+
if (msg.code === KeyCode.End || msg.text === "G") {
|
|
443
|
+
this.cursor = this.items.length - 1;
|
|
444
|
+
this.paginator.page = this.paginator.totalPages - 1;
|
|
445
|
+
return [this, null];
|
|
446
|
+
}
|
|
447
|
+
if (msg.code === KeyCode.Right || msg.text === "l") {
|
|
448
|
+
this.paginator.nextPage();
|
|
449
|
+
const [start] = this.paginator.getSliceBounds(this.items.length);
|
|
450
|
+
this.cursor = start;
|
|
451
|
+
return [this, null];
|
|
452
|
+
}
|
|
453
|
+
if (msg.code === KeyCode.Left || msg.text === "h") {
|
|
454
|
+
this.paginator.prevPage();
|
|
455
|
+
const [start] = this.paginator.getSliceBounds(this.items.length);
|
|
456
|
+
this.cursor = start;
|
|
457
|
+
return [this, null];
|
|
458
|
+
}
|
|
459
|
+
if ((this.noLimit || this.limit > 1) && (msg.code === KeyCode.Space || msg.code === KeyCode.Tab || msg.text === "x")) {
|
|
460
|
+
const item = this.items[this.cursor];
|
|
461
|
+
if (item.selected) {
|
|
462
|
+
item.selected = false;
|
|
463
|
+
this.numSelected--;
|
|
464
|
+
} else if (this.noLimit || this.numSelected < this.limit) {
|
|
465
|
+
item.selected = true;
|
|
466
|
+
this.numSelected++;
|
|
467
|
+
item.order = this.numSelected;
|
|
468
|
+
}
|
|
469
|
+
return [this, null];
|
|
470
|
+
}
|
|
471
|
+
if ((this.noLimit || this.limit > 1) && msg.mod & KeyMod.Ctrl && msg.text === "a") {
|
|
472
|
+
const allSelected = this.items.every((i) => i.selected);
|
|
473
|
+
for (const item of this.items) {
|
|
474
|
+
item.selected = !allSelected;
|
|
475
|
+
}
|
|
476
|
+
this.numSelected = allSelected ? 0 : this.items.length;
|
|
477
|
+
return [this, null];
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return [this, null];
|
|
481
|
+
},
|
|
482
|
+
view() {
|
|
483
|
+
if (this.quitting) return "";
|
|
484
|
+
const [start, end] = this.paginator.getSliceBounds(this.items.length);
|
|
485
|
+
const visible = this.items.slice(start, end);
|
|
486
|
+
let out = "";
|
|
487
|
+
if (this.header) {
|
|
488
|
+
out += this.headerStyle.render(this.header) + "\n";
|
|
489
|
+
}
|
|
490
|
+
const isMulti2 = this.noLimit || this.limit > 1;
|
|
491
|
+
for (let i = 0; i < visible.length; i++) {
|
|
492
|
+
const idx = start + i;
|
|
493
|
+
const item = visible[i];
|
|
494
|
+
const isCursor = idx === this.cursor;
|
|
495
|
+
let prefix;
|
|
496
|
+
let style;
|
|
497
|
+
if (isCursor) {
|
|
498
|
+
prefix = isMulti2 ? item.selected ? this.selectedPrefix : this.unselectedPrefix : this.cursorPrefix;
|
|
499
|
+
style = this.cursorStyle;
|
|
500
|
+
} else if (item.selected) {
|
|
501
|
+
prefix = this.selectedPrefix;
|
|
502
|
+
style = this.selectedItemStyle;
|
|
503
|
+
} else {
|
|
504
|
+
prefix = isMulti2 ? this.unselectedPrefix : " ";
|
|
505
|
+
style = this.itemStyle;
|
|
506
|
+
}
|
|
507
|
+
out += style.render(prefix + item.text);
|
|
508
|
+
if (i < visible.length - 1) out += "\n";
|
|
509
|
+
}
|
|
510
|
+
if (this.paginator.totalPages > 1) {
|
|
511
|
+
out += "\n" + this.paginator.view();
|
|
512
|
+
}
|
|
513
|
+
return out;
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
async function run(parsed) {
|
|
518
|
+
const model = createModel(parsed);
|
|
519
|
+
if (model.selectIfOne && model.items.length === 1) {
|
|
520
|
+
println(model.items[0].text);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const p = new Program(model);
|
|
524
|
+
const final = await p.run();
|
|
525
|
+
if (final.aborted) {
|
|
526
|
+
process.exit(STATUS_ABORTED);
|
|
527
|
+
}
|
|
528
|
+
const selected = final.items.filter((i) => i.selected);
|
|
529
|
+
if (selected.length === 0) return;
|
|
530
|
+
for (const item of selected) {
|
|
531
|
+
println(item.text);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// src/commands/confirm.ts
|
|
536
|
+
import { Program as Program2, KeyPressMsg as KeyPressMsg2, KeyCode as KeyCode2, KeyMod as KeyMod2, Quit as Quit2 } from "@oakoliver/bubbletea";
|
|
537
|
+
|
|
538
|
+
// src/internal/timeout.ts
|
|
539
|
+
function parseDuration(s) {
|
|
540
|
+
if (!s || s === "0" || s === "0s" || s === "0ms") return 0;
|
|
541
|
+
const match = s.match(/^(\d+(?:\.\d+)?)(ms|s|m|h)?$/);
|
|
542
|
+
if (!match) return 0;
|
|
543
|
+
const value = parseFloat(match[1]);
|
|
544
|
+
const unit = match[2] || "ms";
|
|
545
|
+
switch (unit) {
|
|
546
|
+
case "ms":
|
|
547
|
+
return value;
|
|
548
|
+
case "s":
|
|
549
|
+
return value * 1e3;
|
|
550
|
+
case "m":
|
|
551
|
+
return value * 60 * 1e3;
|
|
552
|
+
case "h":
|
|
553
|
+
return value * 60 * 60 * 1e3;
|
|
554
|
+
default:
|
|
555
|
+
return 0;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// src/commands/confirm.ts
|
|
560
|
+
function createModel2(parsed) {
|
|
561
|
+
const flags = parsed.flags;
|
|
562
|
+
const prompt = parsed.args.length > 0 ? parsed.args.join(" ") : flagStr(flags, "prompt", "Are you sure?");
|
|
563
|
+
const affirmative = flagStr(flags, "affirmative", "Yes");
|
|
564
|
+
const negative = flagStr(flags, "negative", "No");
|
|
565
|
+
const defaultValue = flagBool(flags, "default", true);
|
|
566
|
+
const selectedStyle = toLipgloss(extractStyleOptions(flags, "selected"));
|
|
567
|
+
const unselectedStyle = toLipgloss(extractStyleOptions(flags, "unselected"));
|
|
568
|
+
const promptStyle = toLipgloss(extractStyleOptions(flags, "prompt"));
|
|
569
|
+
return {
|
|
570
|
+
affirmative,
|
|
571
|
+
negative,
|
|
572
|
+
confirmation: defaultValue,
|
|
573
|
+
defaultValue,
|
|
574
|
+
quitting: false,
|
|
575
|
+
aborted: false,
|
|
576
|
+
hasTimeout: false,
|
|
577
|
+
prompt,
|
|
578
|
+
selectedStyle: selectedStyle.padding(0, 3),
|
|
579
|
+
unselectedStyle: unselectedStyle.padding(0, 3),
|
|
580
|
+
promptStyle,
|
|
581
|
+
init() {
|
|
582
|
+
return null;
|
|
583
|
+
},
|
|
584
|
+
update(msg) {
|
|
585
|
+
if (msg instanceof KeyPressMsg2) {
|
|
586
|
+
if (msg.mod & KeyMod2.Ctrl && msg.text === "c") {
|
|
587
|
+
this.aborted = true;
|
|
588
|
+
this.quitting = true;
|
|
589
|
+
return [this, () => Quit2()];
|
|
590
|
+
}
|
|
591
|
+
if (msg.code === KeyCode2.Escape) {
|
|
592
|
+
this.aborted = true;
|
|
593
|
+
this.quitting = true;
|
|
594
|
+
return [this, () => Quit2()];
|
|
595
|
+
}
|
|
596
|
+
if (msg.code === KeyCode2.Enter) {
|
|
597
|
+
this.quitting = true;
|
|
598
|
+
return [this, () => Quit2()];
|
|
599
|
+
}
|
|
600
|
+
if (msg.code === KeyCode2.Left || msg.code === KeyCode2.Right || msg.text === "h" || msg.text === "l" || msg.code === KeyCode2.Tab) {
|
|
601
|
+
this.confirmation = !this.confirmation;
|
|
602
|
+
return [this, null];
|
|
603
|
+
}
|
|
604
|
+
if (msg.text === "y" || msg.text === "Y") {
|
|
605
|
+
this.confirmation = true;
|
|
606
|
+
this.quitting = true;
|
|
607
|
+
return [this, () => Quit2()];
|
|
608
|
+
}
|
|
609
|
+
if (msg.text === "n" || msg.text === "N") {
|
|
610
|
+
this.confirmation = false;
|
|
611
|
+
this.quitting = true;
|
|
612
|
+
return [this, () => Quit2()];
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return [this, null];
|
|
616
|
+
},
|
|
617
|
+
view() {
|
|
618
|
+
if (this.quitting) return "";
|
|
619
|
+
let out = "";
|
|
620
|
+
out += this.promptStyle.render(this.prompt) + "\n\n";
|
|
621
|
+
if (this.confirmation) {
|
|
622
|
+
out += this.selectedStyle.render(this.affirmative) + " ";
|
|
623
|
+
out += this.unselectedStyle.render(this.negative);
|
|
624
|
+
} else {
|
|
625
|
+
out += this.unselectedStyle.render(this.affirmative) + " ";
|
|
626
|
+
out += this.selectedStyle.render(this.negative);
|
|
627
|
+
}
|
|
628
|
+
return out;
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
async function run2(parsed) {
|
|
633
|
+
const model = createModel2(parsed);
|
|
634
|
+
const timeoutStr = flagStr(parsed.flags, "timeout", "0");
|
|
635
|
+
const timeoutMs = parseDuration(timeoutStr);
|
|
636
|
+
const p = new Program2(model);
|
|
637
|
+
const final = await p.run();
|
|
638
|
+
if (final.aborted) {
|
|
639
|
+
process.exit(STATUS_ABORTED);
|
|
640
|
+
}
|
|
641
|
+
process.exit(final.confirmation ? 0 : 1);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// src/commands/file.ts
|
|
645
|
+
import { Program as Program3, KeyPressMsg as KeyPressMsg3, KeyCode as KeyCode3, KeyMod as KeyMod3, Quit as Quit3, WindowSizeMsg as WindowSizeMsg3 } from "@oakoliver/bubbletea";
|
|
646
|
+
import { newFilePicker } from "@oakoliver/bubbles";
|
|
647
|
+
import { newStyle as newStyle2, joinVertical, Left } from "@oakoliver/lipgloss";
|
|
648
|
+
import { resolve } from "node:path";
|
|
649
|
+
function helpView() {
|
|
650
|
+
return " \u2193\u2191 navigate \u2022 esc close \u2022 enter select";
|
|
651
|
+
}
|
|
652
|
+
function createModel3(parsed) {
|
|
653
|
+
const flags = parsed.flags;
|
|
654
|
+
const startPath = resolve(parsed.args[0] ?? flagStr(flags, "path", "."));
|
|
655
|
+
const cursor = flagStr(flags, "cursor", ">");
|
|
656
|
+
const showHidden = flagBool(flags, "all", false);
|
|
657
|
+
const showPermissions = flagBool(flags, "permissions", true);
|
|
658
|
+
const showSize = flagBool(flags, "size", true);
|
|
659
|
+
const fileAllowed = flagBool(flags, "file", true);
|
|
660
|
+
const dirAllowed = flagBool(flags, "directory", false);
|
|
661
|
+
const showHelp = flagBool(flags, "show-help", true);
|
|
662
|
+
const header = flagStr(flags, "header", "");
|
|
663
|
+
const height = flagInt(flags, "height", 10);
|
|
664
|
+
const paddingStr = flagStr(flags, "padding", "0 0");
|
|
665
|
+
const padding = parsePadding(paddingStr);
|
|
666
|
+
if (!fileAllowed && !dirAllowed) {
|
|
667
|
+
console.error("Error: at least one of --file or --directory must be enabled");
|
|
668
|
+
process.exit(1);
|
|
669
|
+
}
|
|
670
|
+
const fp = newFilePicker();
|
|
671
|
+
fp.currentDirectory = startPath;
|
|
672
|
+
fp.cursor = cursor;
|
|
673
|
+
fp.showHidden = showHidden;
|
|
674
|
+
fp.showPermissions = showPermissions;
|
|
675
|
+
fp.showSize = showSize;
|
|
676
|
+
fp.fileAllowed = fileAllowed;
|
|
677
|
+
fp.dirAllowed = dirAllowed;
|
|
678
|
+
if (height > 0) {
|
|
679
|
+
fp.autoHeight = false;
|
|
680
|
+
fp.setHeight(height);
|
|
681
|
+
} else {
|
|
682
|
+
fp.autoHeight = true;
|
|
683
|
+
}
|
|
684
|
+
const cursorOpts = extractStyleOptions(flags, "cursor");
|
|
685
|
+
if (!cursorOpts.foreground) cursorOpts.foreground = "212";
|
|
686
|
+
fp.styles.cursor = toLipgloss(cursorOpts);
|
|
687
|
+
const symlinkOpts = extractStyleOptions(flags, "symlink");
|
|
688
|
+
if (!symlinkOpts.foreground) symlinkOpts.foreground = "36";
|
|
689
|
+
fp.styles.symlink = toLipgloss(symlinkOpts);
|
|
690
|
+
const directoryOpts = extractStyleOptions(flags, "directory");
|
|
691
|
+
if (!directoryOpts.foreground) directoryOpts.foreground = "99";
|
|
692
|
+
fp.styles.directory = toLipgloss(directoryOpts);
|
|
693
|
+
const fileOpts = extractStyleOptions(flags, "file");
|
|
694
|
+
fp.styles.file = toLipgloss(fileOpts);
|
|
695
|
+
const permOpts = extractStyleOptions(flags, "permissions");
|
|
696
|
+
if (!permOpts.foreground) permOpts.foreground = "244";
|
|
697
|
+
fp.styles.permission = toLipgloss(permOpts);
|
|
698
|
+
const selectedOpts = extractStyleOptions(flags, "selected");
|
|
699
|
+
if (!selectedOpts.foreground) selectedOpts.foreground = "212";
|
|
700
|
+
if (!selectedOpts.bold) selectedOpts.bold = true;
|
|
701
|
+
fp.styles.selected = toLipgloss(selectedOpts);
|
|
702
|
+
const fileSizeOpts = extractStyleOptions(flags, "file-size");
|
|
703
|
+
if (!fileSizeOpts.foreground) fileSizeOpts.foreground = "240";
|
|
704
|
+
fp.styles.fileSize = toLipgloss(fileSizeOpts);
|
|
705
|
+
const headerStyle = toLipgloss((() => {
|
|
706
|
+
const opts = extractStyleOptions(flags, "header");
|
|
707
|
+
if (!opts.foreground) opts.foreground = "99";
|
|
708
|
+
return opts;
|
|
709
|
+
})());
|
|
710
|
+
return {
|
|
711
|
+
filepicker: fp,
|
|
712
|
+
header,
|
|
713
|
+
headerStyle,
|
|
714
|
+
selectedPath: "",
|
|
715
|
+
quitting: false,
|
|
716
|
+
showHelp,
|
|
717
|
+
padding,
|
|
718
|
+
init() {
|
|
719
|
+
return this.filepicker.init();
|
|
720
|
+
},
|
|
721
|
+
update(msg) {
|
|
722
|
+
if (msg instanceof WindowSizeMsg3) {
|
|
723
|
+
const usable = msg.height - this.padding[0] - this.padding[2];
|
|
724
|
+
this.filepicker.setHeight(Math.max(usable, 1));
|
|
725
|
+
return [this, null];
|
|
726
|
+
}
|
|
727
|
+
if (msg instanceof KeyPressMsg3) {
|
|
728
|
+
if (msg.mod & KeyMod3.Ctrl && msg.text === "c") {
|
|
729
|
+
this.quitting = true;
|
|
730
|
+
return [this, () => Quit3()];
|
|
731
|
+
}
|
|
732
|
+
if (msg.code === KeyCode3.Escape || msg.text === "q") {
|
|
733
|
+
this.quitting = true;
|
|
734
|
+
return [this, () => Quit3()];
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
const [updatedFp, cmd] = this.filepicker.update(msg);
|
|
738
|
+
this.filepicker = updatedFp;
|
|
739
|
+
const result = this.filepicker.didSelectFile(msg);
|
|
740
|
+
const didSelect = Array.isArray(result) ? result[0] : result?.selected;
|
|
741
|
+
const selectedPath = Array.isArray(result) ? result[1] : result?.path;
|
|
742
|
+
if (didSelect && selectedPath) {
|
|
743
|
+
this.selectedPath = selectedPath;
|
|
744
|
+
this.quitting = true;
|
|
745
|
+
return [this, () => Quit3()];
|
|
746
|
+
}
|
|
747
|
+
return [this, cmd];
|
|
748
|
+
},
|
|
749
|
+
view() {
|
|
750
|
+
if (this.quitting) return "";
|
|
751
|
+
const parts = [];
|
|
752
|
+
if (this.header) {
|
|
753
|
+
parts.push(this.headerStyle.render(this.header));
|
|
754
|
+
}
|
|
755
|
+
parts.push(this.filepicker.view());
|
|
756
|
+
if (this.showHelp) {
|
|
757
|
+
parts.push(helpView());
|
|
758
|
+
}
|
|
759
|
+
let content = joinVertical(Left, ...parts);
|
|
760
|
+
const [pt, pr, pb, pl] = this.padding;
|
|
761
|
+
if (pt || pr || pb || pl) {
|
|
762
|
+
const padStyle = newStyle2().paddingTop(pt).paddingRight(pr).paddingBottom(pb).paddingLeft(pl);
|
|
763
|
+
content = padStyle.render(content);
|
|
764
|
+
}
|
|
765
|
+
return content;
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
async function run3(parsed) {
|
|
770
|
+
const model = createModel3(parsed);
|
|
771
|
+
const p = new Program3(model);
|
|
772
|
+
const final = await p.run();
|
|
773
|
+
if (final.quitting && !final.selectedPath) {
|
|
774
|
+
process.exit(STATUS_ABORTED);
|
|
775
|
+
}
|
|
776
|
+
if (final.selectedPath) {
|
|
777
|
+
console.log(final.selectedPath);
|
|
778
|
+
} else {
|
|
779
|
+
console.error("Error: no file selected");
|
|
780
|
+
process.exit(1);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/commands/format.ts
|
|
785
|
+
import { render as renderMarkdown } from "@oakoliver/glamour";
|
|
786
|
+
function detectFormatType(flags) {
|
|
787
|
+
if (flagBool(flags, "type", false)) {
|
|
788
|
+
const t = flagStr(flags, "type", "markdown");
|
|
789
|
+
if (["markdown", "code", "emoji", "template"].includes(t)) {
|
|
790
|
+
return t;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
if (flags["code"] === true) return "code";
|
|
794
|
+
if (flags["emoji"] === true) return "emoji";
|
|
795
|
+
if (flags["template"] === true) return "template";
|
|
796
|
+
if (flags["markdown"] === true) return "markdown";
|
|
797
|
+
return "markdown";
|
|
798
|
+
}
|
|
799
|
+
function formatCode(text, language) {
|
|
800
|
+
const wrapped = "```" + language + "\n" + text + "\n```";
|
|
801
|
+
return renderMarkdown(wrapped, "dark");
|
|
802
|
+
}
|
|
803
|
+
function formatEmoji(text) {
|
|
804
|
+
return text.replace(/:([a-z0-9_+-]+):/g, (match, name) => {
|
|
805
|
+
const emoji = EMOJI_MAP[name];
|
|
806
|
+
return emoji || match;
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
function formatTemplate(text, _flags) {
|
|
810
|
+
return text.replace(/\{\{\s*\.Env\.(\w+)\s*\}\}/g, (_match, key) => {
|
|
811
|
+
return process.env[key] || "";
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
var EMOJI_MAP = {
|
|
815
|
+
"smile": "\u{1F604}",
|
|
816
|
+
"laughing": "\u{1F606}",
|
|
817
|
+
"blush": "\u{1F60A}",
|
|
818
|
+
"smiley": "\u{1F603}",
|
|
819
|
+
"relaxed": "\u263A\uFE0F",
|
|
820
|
+
"heart_eyes": "\u{1F60D}",
|
|
821
|
+
"kissing_heart": "\u{1F618}",
|
|
822
|
+
"wink": "\u{1F609}",
|
|
823
|
+
"stuck_out_tongue_winking_eye": "\u{1F61C}",
|
|
824
|
+
"sunglasses": "\u{1F60E}",
|
|
825
|
+
"thumbsup": "\u{1F44D}",
|
|
826
|
+
"thumbsdown": "\u{1F44E}",
|
|
827
|
+
"clap": "\u{1F44F}",
|
|
828
|
+
"wave": "\u{1F44B}",
|
|
829
|
+
"fire": "\u{1F525}",
|
|
830
|
+
"heart": "\u2764\uFE0F",
|
|
831
|
+
"star": "\u2B50",
|
|
832
|
+
"sparkles": "\u2728",
|
|
833
|
+
"check": "\u2705",
|
|
834
|
+
"x": "\u274C",
|
|
835
|
+
"warning": "\u26A0\uFE0F",
|
|
836
|
+
"bulb": "\u{1F4A1}",
|
|
837
|
+
"rocket": "\u{1F680}",
|
|
838
|
+
"tada": "\u{1F389}",
|
|
839
|
+
"party_popper": "\u{1F389}",
|
|
840
|
+
"coffee": "\u2615",
|
|
841
|
+
"beer": "\u{1F37A}",
|
|
842
|
+
"pizza": "\u{1F355}",
|
|
843
|
+
"bug": "\u{1F41B}",
|
|
844
|
+
"wrench": "\u{1F527}",
|
|
845
|
+
"hammer": "\u{1F528}",
|
|
846
|
+
"gear": "\u2699\uFE0F",
|
|
847
|
+
"lock": "\u{1F512}",
|
|
848
|
+
"key": "\u{1F511}",
|
|
849
|
+
"link": "\u{1F517}",
|
|
850
|
+
"package": "\u{1F4E6}",
|
|
851
|
+
"memo": "\u{1F4DD}",
|
|
852
|
+
"book": "\u{1F4D6}",
|
|
853
|
+
"pencil": "\u270F\uFE0F",
|
|
854
|
+
"scissors": "\u2702\uFE0F",
|
|
855
|
+
"computer": "\u{1F4BB}",
|
|
856
|
+
"phone": "\u{1F4F1}",
|
|
857
|
+
"email": "\u{1F4E7}",
|
|
858
|
+
"globe": "\u{1F30D}",
|
|
859
|
+
"sun": "\u2600\uFE0F",
|
|
860
|
+
"moon": "\u{1F319}",
|
|
861
|
+
"cloud": "\u2601\uFE0F",
|
|
862
|
+
"rainbow": "\u{1F308}",
|
|
863
|
+
"zap": "\u26A1",
|
|
864
|
+
"boom": "\u{1F4A5}",
|
|
865
|
+
"collision": "\u{1F4A5}",
|
|
866
|
+
"eyes": "\u{1F440}",
|
|
867
|
+
"brain": "\u{1F9E0}",
|
|
868
|
+
"muscle": "\u{1F4AA}",
|
|
869
|
+
"green_circle": "\u{1F7E2}",
|
|
870
|
+
"red_circle": "\u{1F534}",
|
|
871
|
+
"blue_circle": "\u{1F535}",
|
|
872
|
+
"white_check_mark": "\u2705",
|
|
873
|
+
"heavy_check_mark": "\u2714\uFE0F",
|
|
874
|
+
"arrow_right": "\u27A1\uFE0F",
|
|
875
|
+
"arrow_left": "\u2B05\uFE0F",
|
|
876
|
+
"arrow_up": "\u2B06\uFE0F",
|
|
877
|
+
"arrow_down": "\u2B07\uFE0F",
|
|
878
|
+
"plus": "\u2795",
|
|
879
|
+
"minus": "\u2796"
|
|
880
|
+
};
|
|
881
|
+
async function run4(parsed) {
|
|
882
|
+
const flags = parsed.flags;
|
|
883
|
+
const formatType = detectFormatType(flags);
|
|
884
|
+
const language = flagStr(flags, "language", "") || flagStr(flags, "lang", "");
|
|
885
|
+
const theme = flagStr(flags, "theme", "dark");
|
|
886
|
+
let text;
|
|
887
|
+
if (parsed.args.length > 0) {
|
|
888
|
+
text = parsed.args.join("\n");
|
|
889
|
+
} else if (!isStdinEmpty()) {
|
|
890
|
+
text = readStdin();
|
|
891
|
+
} else {
|
|
892
|
+
text = "";
|
|
893
|
+
}
|
|
894
|
+
if (!text) return;
|
|
895
|
+
let output;
|
|
896
|
+
switch (formatType) {
|
|
897
|
+
case "code":
|
|
898
|
+
output = formatCode(text, language);
|
|
899
|
+
break;
|
|
900
|
+
case "emoji":
|
|
901
|
+
output = formatEmoji(text);
|
|
902
|
+
break;
|
|
903
|
+
case "template":
|
|
904
|
+
output = formatTemplate(text, flags);
|
|
905
|
+
break;
|
|
906
|
+
case "markdown":
|
|
907
|
+
default:
|
|
908
|
+
output = renderMarkdown(text, theme);
|
|
909
|
+
break;
|
|
910
|
+
}
|
|
911
|
+
process.stdout.write(output);
|
|
912
|
+
if (!output.endsWith("\n")) {
|
|
913
|
+
process.stdout.write("\n");
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// src/commands/input.ts
|
|
918
|
+
import { Program as Program4, KeyPressMsg as KeyPressMsg4, KeyCode as KeyCode4, KeyMod as KeyMod4, Quit as Quit4, WindowSizeMsg as WindowSizeMsg4 } from "@oakoliver/bubbletea";
|
|
919
|
+
import { newTextInput, EchoMode } from "@oakoliver/bubbles";
|
|
920
|
+
function createModel4(parsed) {
|
|
921
|
+
const flags = parsed.flags;
|
|
922
|
+
const ti = newTextInput();
|
|
923
|
+
ti.placeholder = flagWithEnv(flags, "placeholder", "GUM_INPUT_PLACEHOLDER", "Type something...");
|
|
924
|
+
ti.prompt = flagWithEnv(flags, "prompt", "GUM_INPUT_PROMPT", "> ");
|
|
925
|
+
const value = flagStr(flags, "value", "");
|
|
926
|
+
if (value) ti.setValue(value);
|
|
927
|
+
const charLimit = flagInt(flags, "char-limit", 400);
|
|
928
|
+
ti.charLimit = charLimit;
|
|
929
|
+
const echoModeStr = flagStr(flags, "echo-mode", "normal");
|
|
930
|
+
switch (echoModeStr) {
|
|
931
|
+
case "password":
|
|
932
|
+
ti.echoMode = EchoMode.EchoPassword;
|
|
933
|
+
break;
|
|
934
|
+
case "none":
|
|
935
|
+
ti.echoMode = EchoMode.EchoNone;
|
|
936
|
+
break;
|
|
937
|
+
default:
|
|
938
|
+
ti.echoMode = EchoMode.EchoNormal;
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
const password = flagBool(flags, "password", false);
|
|
942
|
+
if (password) ti.echoMode = EchoMode.EchoPassword;
|
|
943
|
+
const widthFlag = flagInt(flags, "width", 0);
|
|
944
|
+
const autoWidth = widthFlag === 0;
|
|
945
|
+
if (!autoWidth) {
|
|
946
|
+
ti.setWidth(widthFlag);
|
|
947
|
+
}
|
|
948
|
+
const header = flagStr(flags, "header", "");
|
|
949
|
+
const headerStyle = toLipgloss(extractStyleOptions(flags, "header"));
|
|
950
|
+
const promptStyleOpts = extractStyleOptions(flags, "prompt");
|
|
951
|
+
const promptStyle = toLipgloss(promptStyleOpts);
|
|
952
|
+
const styles = ti.styles();
|
|
953
|
+
styles.focused.prompt = promptStyle;
|
|
954
|
+
ti.setStyles(styles);
|
|
955
|
+
const cursorStyleOpts = extractStyleOptions(flags, "cursor");
|
|
956
|
+
const cursorStyle = toLipgloss(cursorStyleOpts);
|
|
957
|
+
const curStyles = ti.styles();
|
|
958
|
+
const cursorFg = flagStr(flags, "cursor.foreground", "");
|
|
959
|
+
if (cursorFg) {
|
|
960
|
+
curStyles.cursor.color = cursorFg;
|
|
961
|
+
}
|
|
962
|
+
ti.setStyles(curStyles);
|
|
963
|
+
ti.focus();
|
|
964
|
+
return {
|
|
965
|
+
textInput: ti,
|
|
966
|
+
aborted: false,
|
|
967
|
+
quitting: false,
|
|
968
|
+
header,
|
|
969
|
+
headerStyle,
|
|
970
|
+
autoWidth,
|
|
971
|
+
width: widthFlag || 80,
|
|
972
|
+
init() {
|
|
973
|
+
return null;
|
|
974
|
+
},
|
|
975
|
+
update(msg) {
|
|
976
|
+
if (msg instanceof WindowSizeMsg4) {
|
|
977
|
+
this.width = msg.width;
|
|
978
|
+
if (this.autoWidth) {
|
|
979
|
+
this.textInput.setWidth(msg.width - 1);
|
|
980
|
+
}
|
|
981
|
+
return [this, null];
|
|
982
|
+
}
|
|
983
|
+
if (msg instanceof KeyPressMsg4) {
|
|
984
|
+
if (msg.code === KeyCode4.Escape || msg.mod & KeyMod4.Ctrl && msg.text === "c") {
|
|
985
|
+
this.aborted = true;
|
|
986
|
+
this.quitting = true;
|
|
987
|
+
return [this, () => Quit4()];
|
|
988
|
+
}
|
|
989
|
+
if (msg.code === KeyCode4.Escape) {
|
|
990
|
+
this.quitting = true;
|
|
991
|
+
return [this, () => Quit4()];
|
|
992
|
+
}
|
|
993
|
+
if (msg.code === KeyCode4.Enter) {
|
|
994
|
+
this.quitting = true;
|
|
995
|
+
return [this, () => Quit4()];
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
const [updatedTi, cmd] = this.textInput.update(msg);
|
|
999
|
+
this.textInput = updatedTi;
|
|
1000
|
+
return [this, cmd];
|
|
1001
|
+
},
|
|
1002
|
+
view() {
|
|
1003
|
+
if (this.quitting) return "";
|
|
1004
|
+
let out = "";
|
|
1005
|
+
if (this.header) {
|
|
1006
|
+
out += this.headerStyle.render(this.header) + "\n";
|
|
1007
|
+
}
|
|
1008
|
+
out += this.textInput.view();
|
|
1009
|
+
return out;
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
async function run5(parsed) {
|
|
1014
|
+
const model = createModel4(parsed);
|
|
1015
|
+
const p = new Program4(model);
|
|
1016
|
+
const final = await p.run();
|
|
1017
|
+
if (final.aborted) {
|
|
1018
|
+
process.exit(STATUS_ABORTED);
|
|
1019
|
+
}
|
|
1020
|
+
process.stdout.write(final.textInput.value() + "\n");
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// src/commands/join.ts
|
|
1024
|
+
import { joinHorizontal, joinVertical as joinVertical2, Left as Left2, Center, Right } from "@oakoliver/lipgloss";
|
|
1025
|
+
function parseAlign(s) {
|
|
1026
|
+
switch (s.toLowerCase()) {
|
|
1027
|
+
case "center":
|
|
1028
|
+
return Center;
|
|
1029
|
+
case "right":
|
|
1030
|
+
case "bottom":
|
|
1031
|
+
return Right;
|
|
1032
|
+
case "left":
|
|
1033
|
+
case "top":
|
|
1034
|
+
default:
|
|
1035
|
+
return Left2;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
async function run6(parsed) {
|
|
1039
|
+
const flags = parsed.flags;
|
|
1040
|
+
const horizontal = flagBool(flags, "horizontal", false);
|
|
1041
|
+
const align = flagStr(flags, "align", "left");
|
|
1042
|
+
const texts = parsed.args;
|
|
1043
|
+
if (texts.length === 0) {
|
|
1044
|
+
process.stdout.write("");
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
const pos = parseAlign(align);
|
|
1048
|
+
let result;
|
|
1049
|
+
if (horizontal) {
|
|
1050
|
+
result = joinHorizontal(pos, ...texts);
|
|
1051
|
+
} else {
|
|
1052
|
+
result = joinVertical2(pos, ...texts);
|
|
1053
|
+
}
|
|
1054
|
+
process.stdout.write(result + "\n");
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// src/commands/log.ts
|
|
1058
|
+
import { newStyle as newStyle3 } from "@oakoliver/lipgloss";
|
|
1059
|
+
import * as fs2 from "node:fs";
|
|
1060
|
+
var LOG_LEVEL_COLORS = {
|
|
1061
|
+
debug: "#6c757d",
|
|
1062
|
+
info: "#0d6efd",
|
|
1063
|
+
warn: "#ffc107",
|
|
1064
|
+
error: "#dc3545",
|
|
1065
|
+
fatal: "#dc3545",
|
|
1066
|
+
none: ""
|
|
1067
|
+
};
|
|
1068
|
+
function formatTimestamp(format) {
|
|
1069
|
+
const now = /* @__PURE__ */ new Date();
|
|
1070
|
+
if (format === "TimeOnly" || format === "timeonly") {
|
|
1071
|
+
return now.toLocaleTimeString();
|
|
1072
|
+
}
|
|
1073
|
+
if (format === "DateTime" || format === "datetime") {
|
|
1074
|
+
return now.toLocaleString();
|
|
1075
|
+
}
|
|
1076
|
+
if (format === "DateOnly" || format === "dateonly") {
|
|
1077
|
+
return now.toLocaleDateString();
|
|
1078
|
+
}
|
|
1079
|
+
if (format === "Kitchen" || format === "kitchen") {
|
|
1080
|
+
return now.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", hour12: true });
|
|
1081
|
+
}
|
|
1082
|
+
if (format === "RFC3339" || format === "rfc3339") {
|
|
1083
|
+
return now.toISOString();
|
|
1084
|
+
}
|
|
1085
|
+
const h = now.getHours().toString().padStart(2, "0");
|
|
1086
|
+
const m = now.getMinutes().toString().padStart(2, "0");
|
|
1087
|
+
const s = now.getSeconds().toString().padStart(2, "0");
|
|
1088
|
+
return `${h}:${m}:${s}`;
|
|
1089
|
+
}
|
|
1090
|
+
function formatText(level, message, kvPairs, prefix, timeFormat, levelStyle, keyStyle, valueStyle, separatorStyle, prefixStyle, timeStyle, messageStyle) {
|
|
1091
|
+
const parts = [];
|
|
1092
|
+
if (timeFormat) {
|
|
1093
|
+
parts.push(timeStyle.render(formatTimestamp(timeFormat)));
|
|
1094
|
+
}
|
|
1095
|
+
const levelStr = level.toUpperCase().padEnd(5);
|
|
1096
|
+
parts.push(levelStyle.render(levelStr));
|
|
1097
|
+
if (prefix) {
|
|
1098
|
+
parts.push(prefixStyle.render(prefix));
|
|
1099
|
+
}
|
|
1100
|
+
parts.push(messageStyle.render(message));
|
|
1101
|
+
for (const [key, value] of Object.entries(kvPairs)) {
|
|
1102
|
+
parts.push(
|
|
1103
|
+
keyStyle.render(key) + separatorStyle.render("=") + valueStyle.render(value)
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
return parts.join(" ");
|
|
1107
|
+
}
|
|
1108
|
+
function formatJSON(level, message, kvPairs, prefix, timeFormat) {
|
|
1109
|
+
const obj = {};
|
|
1110
|
+
if (timeFormat) obj["time"] = formatTimestamp(timeFormat);
|
|
1111
|
+
obj["level"] = level.toUpperCase();
|
|
1112
|
+
if (prefix) obj["prefix"] = prefix;
|
|
1113
|
+
obj["msg"] = message;
|
|
1114
|
+
Object.assign(obj, kvPairs);
|
|
1115
|
+
return JSON.stringify(obj);
|
|
1116
|
+
}
|
|
1117
|
+
function formatLogfmt(level, message, kvPairs, prefix, timeFormat) {
|
|
1118
|
+
const parts = [];
|
|
1119
|
+
if (timeFormat) parts.push(`time=${formatTimestamp(timeFormat)}`);
|
|
1120
|
+
parts.push(`level=${level.toUpperCase()}`);
|
|
1121
|
+
if (prefix) parts.push(`prefix=${prefix}`);
|
|
1122
|
+
parts.push(`msg="${message}"`);
|
|
1123
|
+
for (const [key, value] of Object.entries(kvPairs)) {
|
|
1124
|
+
if (value.includes(" ")) {
|
|
1125
|
+
parts.push(`${key}="${value}"`);
|
|
1126
|
+
} else {
|
|
1127
|
+
parts.push(`${key}=${value}`);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
return parts.join(" ");
|
|
1131
|
+
}
|
|
1132
|
+
async function run7(parsed) {
|
|
1133
|
+
const flags = parsed.flags;
|
|
1134
|
+
const level = flagStr(flags, "level", "info");
|
|
1135
|
+
const formatter = flagStr(flags, "formatter", "text") || flagStr(flags, "format", "text");
|
|
1136
|
+
const prefix = flagStr(flags, "prefix", "");
|
|
1137
|
+
const timeFormat = flagStr(flags, "time", "");
|
|
1138
|
+
const structured = flagBool(flags, "structured", false);
|
|
1139
|
+
const message = parsed.args.length > 0 ? parsed.args[0] : "";
|
|
1140
|
+
const kvPairs = {};
|
|
1141
|
+
for (let i = 1; i < parsed.args.length; i++) {
|
|
1142
|
+
const arg = parsed.args[i];
|
|
1143
|
+
const eqIdx = arg.indexOf("=");
|
|
1144
|
+
if (eqIdx !== -1) {
|
|
1145
|
+
kvPairs[arg.substring(0, eqIdx)] = arg.substring(eqIdx + 1);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
const levelColor = LOG_LEVEL_COLORS[level] || "";
|
|
1149
|
+
const levelStyle = levelColor ? newStyle3().foreground(levelColor).bold(true) : newStyle3();
|
|
1150
|
+
const keyStyle = toLipgloss(extractStyleOptions(flags, "key"));
|
|
1151
|
+
const valueStyle = toLipgloss(extractStyleOptions(flags, "value"));
|
|
1152
|
+
const separatorStyle = toLipgloss(extractStyleOptions(flags, "separator"));
|
|
1153
|
+
const prefixStyle = toLipgloss(extractStyleOptions(flags, "prefix"));
|
|
1154
|
+
const timeStyle = toLipgloss(extractStyleOptions(flags, "time"));
|
|
1155
|
+
const messageStyle = toLipgloss(extractStyleOptions(flags, "message"));
|
|
1156
|
+
let output;
|
|
1157
|
+
switch (formatter) {
|
|
1158
|
+
case "json":
|
|
1159
|
+
output = formatJSON(level, message, kvPairs, prefix, timeFormat);
|
|
1160
|
+
break;
|
|
1161
|
+
case "logfmt":
|
|
1162
|
+
output = formatLogfmt(level, message, kvPairs, prefix, timeFormat);
|
|
1163
|
+
break;
|
|
1164
|
+
default:
|
|
1165
|
+
output = formatText(
|
|
1166
|
+
level,
|
|
1167
|
+
message,
|
|
1168
|
+
kvPairs,
|
|
1169
|
+
prefix,
|
|
1170
|
+
timeFormat,
|
|
1171
|
+
levelStyle,
|
|
1172
|
+
keyStyle,
|
|
1173
|
+
valueStyle,
|
|
1174
|
+
separatorStyle,
|
|
1175
|
+
prefixStyle,
|
|
1176
|
+
timeStyle,
|
|
1177
|
+
messageStyle
|
|
1178
|
+
);
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
const file = flagStr(flags, "file", "");
|
|
1182
|
+
if (file) {
|
|
1183
|
+
fs2.appendFileSync(file, output + "\n");
|
|
1184
|
+
}
|
|
1185
|
+
process.stderr.write(output + "\n");
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// src/commands/pager.ts
|
|
1189
|
+
import { Program as Program5, KeyPressMsg as KeyPressMsg5, KeyCode as KeyCode5, KeyMod as KeyMod5, Quit as Quit5, WithAltScreen, WindowSizeMsg as WindowSizeMsg5 } from "@oakoliver/bubbletea";
|
|
1190
|
+
import { newViewport, newTextInput as newTextInput2 } from "@oakoliver/bubbles";
|
|
1191
|
+
import { newStyle as newStyle4, stringWidth, roundedBorder } from "@oakoliver/lipgloss";
|
|
1192
|
+
var HELP_VIEW = " \u2191/\u2193 navigate \u2022 / search \u2022 n next \u2022 N prev \u2022 q quit";
|
|
1193
|
+
function lipglossPadding(style) {
|
|
1194
|
+
const rendered = style.render(" ");
|
|
1195
|
+
const idx = rendered.indexOf(" ");
|
|
1196
|
+
return [idx, rendered.length - 1 - idx];
|
|
1197
|
+
}
|
|
1198
|
+
function softWrapText(str, maxWidth, wrap) {
|
|
1199
|
+
if (!wrap || maxWidth <= 0) return [str];
|
|
1200
|
+
const lines = [];
|
|
1201
|
+
let remaining = str;
|
|
1202
|
+
while (stringWidth(remaining) > maxWidth) {
|
|
1203
|
+
let cut = 0;
|
|
1204
|
+
let w = 0;
|
|
1205
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
1206
|
+
const ch = remaining[i];
|
|
1207
|
+
if (ch === "\x1B") {
|
|
1208
|
+
const end = remaining.indexOf("m", i);
|
|
1209
|
+
if (end !== -1) {
|
|
1210
|
+
i = end;
|
|
1211
|
+
continue;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
w++;
|
|
1215
|
+
cut = i + 1;
|
|
1216
|
+
if (w >= maxWidth) break;
|
|
1217
|
+
}
|
|
1218
|
+
if (cut === 0) break;
|
|
1219
|
+
lines.push(remaining.substring(0, cut));
|
|
1220
|
+
remaining = remaining.substring(cut);
|
|
1221
|
+
}
|
|
1222
|
+
if (remaining.length > 0) lines.push(remaining);
|
|
1223
|
+
return lines;
|
|
1224
|
+
}
|
|
1225
|
+
function processText(model) {
|
|
1226
|
+
const lines = model.origContent.split("\n");
|
|
1227
|
+
const [padL, padR] = lipglossPadding(model.viewportStyle);
|
|
1228
|
+
const lineNumWidth = model.showLineNumbers ? ` ${lines.length} \u2502 `.length : 0;
|
|
1229
|
+
model.maxWidth = model.viewport.width() - padL - padR - lineNumWidth;
|
|
1230
|
+
const processed = [];
|
|
1231
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1232
|
+
let line = lines[i];
|
|
1233
|
+
const prefix = model.showLineNumbers ? model.lineNumberStyle.render(`${String(i + 1).padStart(4)} \u2502 `) : "";
|
|
1234
|
+
if (model.softWrap && model.maxWidth > 0) {
|
|
1235
|
+
const wrapped = softWrapText(line, model.maxWidth, true);
|
|
1236
|
+
for (let j = 0; j < wrapped.length; j++) {
|
|
1237
|
+
processed.push(j === 0 ? prefix + wrapped[j] : " ".repeat(lineNumWidth) + wrapped[j]);
|
|
1238
|
+
}
|
|
1239
|
+
} else {
|
|
1240
|
+
processed.push(prefix + line);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
if (model.showLineNumbers) {
|
|
1244
|
+
const visible = model.viewport.height();
|
|
1245
|
+
while (processed.length < visible) {
|
|
1246
|
+
processed.push(model.lineNumberStyle.render(" ~ \u2502 "));
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
model.content = processed.join("\n");
|
|
1250
|
+
model.viewport.setContent(model.content);
|
|
1251
|
+
}
|
|
1252
|
+
function searchBegin(search) {
|
|
1253
|
+
search.active = true;
|
|
1254
|
+
search.input = newTextInput2();
|
|
1255
|
+
search.input.placeholder = "search...";
|
|
1256
|
+
search.input.prompt = "/ ";
|
|
1257
|
+
search.input.focus();
|
|
1258
|
+
}
|
|
1259
|
+
function searchExecute(model) {
|
|
1260
|
+
const value = model.search.input.value();
|
|
1261
|
+
if (!value) return;
|
|
1262
|
+
let pattern;
|
|
1263
|
+
try {
|
|
1264
|
+
pattern = new RegExp(value, "gi");
|
|
1265
|
+
} catch {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
const highlighted = model.content.replace(pattern, (m) => model.matchStyle.render(m));
|
|
1269
|
+
model.content = highlighted;
|
|
1270
|
+
model.viewport.setContent(model.content);
|
|
1271
|
+
const styledSample = model.matchStyle.render(value);
|
|
1272
|
+
try {
|
|
1273
|
+
model.search.query = new RegExp(escapeRegex(styledSample), "gi");
|
|
1274
|
+
} catch {
|
|
1275
|
+
model.search.query = null;
|
|
1276
|
+
}
|
|
1277
|
+
model.search.matchIndex = -1;
|
|
1278
|
+
}
|
|
1279
|
+
function searchDone(search) {
|
|
1280
|
+
search.active = false;
|
|
1281
|
+
search.query = null;
|
|
1282
|
+
search.matchIndex = -1;
|
|
1283
|
+
search.matchString = "";
|
|
1284
|
+
search.matchHighlightStr = "";
|
|
1285
|
+
}
|
|
1286
|
+
function searchNextMatch(model) {
|
|
1287
|
+
if (!model.search.query) return;
|
|
1288
|
+
const matches = [...model.content.matchAll(model.search.query)];
|
|
1289
|
+
if (matches.length === 0) return;
|
|
1290
|
+
if (model.search.matchHighlightStr && model.search.matchString) {
|
|
1291
|
+
model.content = model.content.replace(model.search.matchHighlightStr, model.search.matchString);
|
|
1292
|
+
}
|
|
1293
|
+
model.search.matchIndex = (model.search.matchIndex + 1) % matches.length;
|
|
1294
|
+
const match = matches[model.search.matchIndex];
|
|
1295
|
+
model.search.matchString = match[0];
|
|
1296
|
+
model.search.matchHighlightStr = model.matchHighlightStyle.render(
|
|
1297
|
+
model.search.input.value()
|
|
1298
|
+
);
|
|
1299
|
+
model.content = model.content.substring(0, match.index) + model.search.matchHighlightStr + model.content.substring(match.index + match[0].length);
|
|
1300
|
+
model.viewport.setContent(model.content);
|
|
1301
|
+
const beforeMatch = model.content.substring(0, match.index);
|
|
1302
|
+
const line = beforeMatch.split("\n").length - 1;
|
|
1303
|
+
model.viewport.setYOffset(Math.max(0, line - Math.floor(model.viewport.height() / 2)));
|
|
1304
|
+
}
|
|
1305
|
+
function searchPrevMatch(model) {
|
|
1306
|
+
if (!model.search.query) return;
|
|
1307
|
+
const matches = [...model.content.matchAll(model.search.query)];
|
|
1308
|
+
if (matches.length === 0) return;
|
|
1309
|
+
if (model.search.matchHighlightStr && model.search.matchString) {
|
|
1310
|
+
model.content = model.content.replace(model.search.matchHighlightStr, model.search.matchString);
|
|
1311
|
+
}
|
|
1312
|
+
model.search.matchIndex = model.search.matchIndex <= 0 ? matches.length - 1 : model.search.matchIndex - 1;
|
|
1313
|
+
const match = matches[model.search.matchIndex];
|
|
1314
|
+
model.search.matchString = match[0];
|
|
1315
|
+
model.search.matchHighlightStr = model.matchHighlightStyle.render(
|
|
1316
|
+
model.search.input.value()
|
|
1317
|
+
);
|
|
1318
|
+
model.content = model.content.substring(0, match.index) + model.search.matchHighlightStr + model.content.substring(match.index + match[0].length);
|
|
1319
|
+
model.viewport.setContent(model.content);
|
|
1320
|
+
const beforeMatch = model.content.substring(0, match.index);
|
|
1321
|
+
const line = beforeMatch.split("\n").length - 1;
|
|
1322
|
+
model.viewport.setYOffset(Math.max(0, line - Math.floor(model.viewport.height() / 2)));
|
|
1323
|
+
}
|
|
1324
|
+
function escapeRegex(s) {
|
|
1325
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1326
|
+
}
|
|
1327
|
+
function createModel5(parsed) {
|
|
1328
|
+
const flags = parsed.flags;
|
|
1329
|
+
let content = "";
|
|
1330
|
+
if (parsed.args.length > 0) {
|
|
1331
|
+
content = parsed.args[0];
|
|
1332
|
+
} else if (!isStdinEmpty()) {
|
|
1333
|
+
content = readStdin();
|
|
1334
|
+
content = content.replace(/.\x08/g, "");
|
|
1335
|
+
}
|
|
1336
|
+
const showLineNumbers = flagBool(flags, "show-line-numbers", true);
|
|
1337
|
+
const softWrap = flagBool(flags, "soft-wrap", true);
|
|
1338
|
+
const lineNumberStyle = toLipgloss(extractStyleOptions(flags, "line-number"));
|
|
1339
|
+
const lineNumberFg = flagStr(flags, "line-number.foreground", "237");
|
|
1340
|
+
const lnStyle = lineNumberFg === "237" && !flags["line-number.foreground"] ? newStyle4().foreground("237") : lineNumberStyle;
|
|
1341
|
+
const matchOpts = extractStyleOptions(flags, "match");
|
|
1342
|
+
const matchStyle = matchOpts.foreground || matchOpts.bold ? toLipgloss(matchOpts) : newStyle4().foreground("212").bold(true);
|
|
1343
|
+
const mhOpts = extractStyleOptions(flags, "match-highlight");
|
|
1344
|
+
const matchHighlightStyle = mhOpts.foreground || mhOpts.background || mhOpts.bold ? toLipgloss(mhOpts) : newStyle4().foreground("235").background("225").bold(true);
|
|
1345
|
+
const borderFg = flagStr(flags, "border-foreground", "212");
|
|
1346
|
+
const vpStyle = newStyle4().border(roundedBorder()).borderForeground(borderFg).paddingRight(1).paddingLeft(1);
|
|
1347
|
+
const viewport = newViewport();
|
|
1348
|
+
viewport.softWrap = softWrap;
|
|
1349
|
+
viewport.style = vpStyle;
|
|
1350
|
+
const search = {
|
|
1351
|
+
active: false,
|
|
1352
|
+
input: newTextInput2(),
|
|
1353
|
+
query: null,
|
|
1354
|
+
matchIndex: -1,
|
|
1355
|
+
matchString: "",
|
|
1356
|
+
matchHighlightStr: ""
|
|
1357
|
+
};
|
|
1358
|
+
return {
|
|
1359
|
+
viewport,
|
|
1360
|
+
content,
|
|
1361
|
+
origContent: content,
|
|
1362
|
+
showLineNumbers,
|
|
1363
|
+
lineNumberStyle: lnStyle,
|
|
1364
|
+
softWrap,
|
|
1365
|
+
search,
|
|
1366
|
+
matchStyle,
|
|
1367
|
+
matchHighlightStyle,
|
|
1368
|
+
maxWidth: 0,
|
|
1369
|
+
aborted: false,
|
|
1370
|
+
quitting: false,
|
|
1371
|
+
viewportStyle: vpStyle,
|
|
1372
|
+
init() {
|
|
1373
|
+
return null;
|
|
1374
|
+
},
|
|
1375
|
+
update(msg) {
|
|
1376
|
+
if (msg instanceof WindowSizeMsg5) {
|
|
1377
|
+
this.viewport.setWidth(msg.width);
|
|
1378
|
+
this.viewport.setHeight(msg.height - 2);
|
|
1379
|
+
processText(this);
|
|
1380
|
+
return [this, null];
|
|
1381
|
+
}
|
|
1382
|
+
if (msg instanceof KeyPressMsg5) {
|
|
1383
|
+
if (this.search.active) {
|
|
1384
|
+
if (msg.code === KeyCode5.Enter) {
|
|
1385
|
+
if (this.search.input.value()) {
|
|
1386
|
+
searchExecute(this);
|
|
1387
|
+
this.search.active = false;
|
|
1388
|
+
} else {
|
|
1389
|
+
searchDone(this.search);
|
|
1390
|
+
this.content = this.origContent;
|
|
1391
|
+
processText(this);
|
|
1392
|
+
}
|
|
1393
|
+
return [this, null];
|
|
1394
|
+
}
|
|
1395
|
+
if (msg.code === KeyCode5.Escape || msg.mod & KeyMod5.Ctrl && msg.text === "c" || msg.mod & KeyMod5.Ctrl && msg.text === "d") {
|
|
1396
|
+
searchDone(this.search);
|
|
1397
|
+
this.content = this.origContent;
|
|
1398
|
+
processText(this);
|
|
1399
|
+
return [this, null];
|
|
1400
|
+
}
|
|
1401
|
+
const [updatedInput] = this.search.input.update(msg);
|
|
1402
|
+
this.search.input = updatedInput;
|
|
1403
|
+
return [this, null];
|
|
1404
|
+
}
|
|
1405
|
+
if (msg.text === "g" || msg.code === KeyCode5.Home) {
|
|
1406
|
+
this.viewport.gotoTop();
|
|
1407
|
+
return [this, null];
|
|
1408
|
+
}
|
|
1409
|
+
if (msg.text === "G" || msg.code === KeyCode5.End) {
|
|
1410
|
+
this.viewport.gotoBottom();
|
|
1411
|
+
return [this, null];
|
|
1412
|
+
}
|
|
1413
|
+
if (msg.text === "/") {
|
|
1414
|
+
searchBegin(this.search);
|
|
1415
|
+
return [this, null];
|
|
1416
|
+
}
|
|
1417
|
+
if (msg.text === "n") {
|
|
1418
|
+
searchNextMatch(this);
|
|
1419
|
+
return [this, null];
|
|
1420
|
+
}
|
|
1421
|
+
if (msg.text === "p" || msg.text === "N") {
|
|
1422
|
+
searchPrevMatch(this);
|
|
1423
|
+
return [this, null];
|
|
1424
|
+
}
|
|
1425
|
+
if (msg.text === "q" || msg.code === KeyCode5.Escape) {
|
|
1426
|
+
this.quitting = true;
|
|
1427
|
+
return [this, () => Quit5()];
|
|
1428
|
+
}
|
|
1429
|
+
if (msg.mod & KeyMod5.Ctrl && msg.text === "c") {
|
|
1430
|
+
this.aborted = true;
|
|
1431
|
+
this.quitting = true;
|
|
1432
|
+
return [this, () => Quit5()];
|
|
1433
|
+
}
|
|
1434
|
+
const [updatedVp, vpCmd] = this.viewport.update(msg);
|
|
1435
|
+
this.viewport = updatedVp;
|
|
1436
|
+
return [this, vpCmd];
|
|
1437
|
+
}
|
|
1438
|
+
return [this, null];
|
|
1439
|
+
},
|
|
1440
|
+
view() {
|
|
1441
|
+
if (this.quitting) return "";
|
|
1442
|
+
if (this.search.active) {
|
|
1443
|
+
return this.viewport.view() + "\n " + this.search.input.view();
|
|
1444
|
+
}
|
|
1445
|
+
return this.viewport.view() + "\n" + HELP_VIEW;
|
|
1446
|
+
}
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
async function run8(parsed) {
|
|
1450
|
+
const model = createModel5(parsed);
|
|
1451
|
+
const p = new Program5(model, WithAltScreen());
|
|
1452
|
+
const final = await p.run();
|
|
1453
|
+
if (final.aborted) {
|
|
1454
|
+
process.exit(STATUS_ABORTED);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
// src/commands/spin.ts
|
|
1459
|
+
import { Program as Program6, KeyPressMsg as KeyPressMsg6, KeyMod as KeyMod6, Quit as Quit6, Batch } from "@oakoliver/bubbletea";
|
|
1460
|
+
import { newSpinner, Line, Dot, MiniDot, Jump, Pulse, Points, Globe, Moon, Monkey, Meter, Hamburger } from "@oakoliver/bubbles";
|
|
1461
|
+
import { spawn } from "node:child_process";
|
|
1462
|
+
var spinnerMap = {
|
|
1463
|
+
line: Line,
|
|
1464
|
+
dot: Dot,
|
|
1465
|
+
minidot: MiniDot,
|
|
1466
|
+
jump: Jump,
|
|
1467
|
+
pulse: Pulse,
|
|
1468
|
+
points: Points,
|
|
1469
|
+
globe: Globe,
|
|
1470
|
+
moon: Moon,
|
|
1471
|
+
monkey: Monkey,
|
|
1472
|
+
meter: Meter,
|
|
1473
|
+
hamburger: Hamburger
|
|
1474
|
+
};
|
|
1475
|
+
var FinishCommandMsg = class {
|
|
1476
|
+
constructor(output, exitCode) {
|
|
1477
|
+
this.output = output;
|
|
1478
|
+
this.exitCode = exitCode;
|
|
1479
|
+
}
|
|
1480
|
+
_tag = "FinishCommandMsg";
|
|
1481
|
+
};
|
|
1482
|
+
function runCommand(command) {
|
|
1483
|
+
return () => new Promise((resolve2) => {
|
|
1484
|
+
const child = spawn(command[0], command.slice(1), {
|
|
1485
|
+
shell: true,
|
|
1486
|
+
stdio: ["inherit", "pipe", "pipe"]
|
|
1487
|
+
});
|
|
1488
|
+
let stdout = "";
|
|
1489
|
+
let stderr = "";
|
|
1490
|
+
child.stdout?.on("data", (data) => {
|
|
1491
|
+
stdout += data.toString();
|
|
1492
|
+
});
|
|
1493
|
+
child.stderr?.on("data", (data) => {
|
|
1494
|
+
stderr += data.toString();
|
|
1495
|
+
});
|
|
1496
|
+
child.on("close", (code) => {
|
|
1497
|
+
resolve2(new FinishCommandMsg(stdout || stderr, code ?? 1));
|
|
1498
|
+
});
|
|
1499
|
+
child.on("error", (err) => {
|
|
1500
|
+
resolve2(new FinishCommandMsg(err.message, 1));
|
|
1501
|
+
});
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
function createModel6(parsed) {
|
|
1505
|
+
const flags = parsed.flags;
|
|
1506
|
+
const spinnerName = flagStr(flags, "spinner", "dot");
|
|
1507
|
+
const spinnerType = spinnerMap[spinnerName] || Dot;
|
|
1508
|
+
const spinner = newSpinner();
|
|
1509
|
+
spinner.spinner = spinnerType;
|
|
1510
|
+
const title = flagStr(flags, "title", "Loading...");
|
|
1511
|
+
const showOutput = flagBool(flags, "show-output", false);
|
|
1512
|
+
const align = flagStr(flags, "align", "left");
|
|
1513
|
+
const titleStyle = toLipgloss(extractStyleOptions(flags, "title"));
|
|
1514
|
+
const spinnerStyle = toLipgloss(extractStyleOptions(flags, "spinner"));
|
|
1515
|
+
spinner.style = spinnerStyle;
|
|
1516
|
+
const command = parsed.args;
|
|
1517
|
+
return {
|
|
1518
|
+
spinner,
|
|
1519
|
+
title,
|
|
1520
|
+
titleStyle,
|
|
1521
|
+
spinnerStyle,
|
|
1522
|
+
command,
|
|
1523
|
+
aborted: false,
|
|
1524
|
+
quitting: false,
|
|
1525
|
+
output: "",
|
|
1526
|
+
exitCode: 0,
|
|
1527
|
+
showOutput,
|
|
1528
|
+
align,
|
|
1529
|
+
init() {
|
|
1530
|
+
const cmds = [];
|
|
1531
|
+
cmds.push(() => this.spinner.tickMsg());
|
|
1532
|
+
if (this.command.length > 0) {
|
|
1533
|
+
cmds.push(runCommand(this.command));
|
|
1534
|
+
}
|
|
1535
|
+
return Batch(...cmds);
|
|
1536
|
+
},
|
|
1537
|
+
update(msg) {
|
|
1538
|
+
if (msg instanceof FinishCommandMsg) {
|
|
1539
|
+
this.output = msg.output;
|
|
1540
|
+
this.exitCode = msg.exitCode;
|
|
1541
|
+
this.quitting = true;
|
|
1542
|
+
return [this, () => Quit6()];
|
|
1543
|
+
}
|
|
1544
|
+
if (msg instanceof KeyPressMsg6) {
|
|
1545
|
+
if (msg.mod & KeyMod6.Ctrl && msg.text === "c") {
|
|
1546
|
+
this.aborted = true;
|
|
1547
|
+
this.quitting = true;
|
|
1548
|
+
return [this, () => Quit6()];
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
const [updatedSpinner, cmd] = this.spinner.update(msg);
|
|
1552
|
+
this.spinner = updatedSpinner;
|
|
1553
|
+
return [this, cmd];
|
|
1554
|
+
},
|
|
1555
|
+
view() {
|
|
1556
|
+
if (this.quitting) return "";
|
|
1557
|
+
let line;
|
|
1558
|
+
const spinView = this.spinner.view();
|
|
1559
|
+
const titleView = this.titleStyle.render(this.title);
|
|
1560
|
+
if (this.align === "right") {
|
|
1561
|
+
line = titleView + " " + spinView;
|
|
1562
|
+
} else {
|
|
1563
|
+
line = spinView + " " + titleView;
|
|
1564
|
+
}
|
|
1565
|
+
let out = line;
|
|
1566
|
+
if (this.showOutput && this.output) {
|
|
1567
|
+
out += "\n" + this.output;
|
|
1568
|
+
}
|
|
1569
|
+
return out;
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
async function run9(parsed) {
|
|
1574
|
+
const model = createModel6(parsed);
|
|
1575
|
+
if (model.command.length === 0) {
|
|
1576
|
+
console.error("Error: no command provided. Usage: gum spin -- <command>");
|
|
1577
|
+
process.exit(1);
|
|
1578
|
+
}
|
|
1579
|
+
const p = new Program6(model);
|
|
1580
|
+
const final = await p.run();
|
|
1581
|
+
if (final.aborted) {
|
|
1582
|
+
process.exit(STATUS_ABORTED);
|
|
1583
|
+
}
|
|
1584
|
+
if (final.output) {
|
|
1585
|
+
process.stdout.write(final.output);
|
|
1586
|
+
if (!final.output.endsWith("\n")) {
|
|
1587
|
+
process.stdout.write("\n");
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
process.exit(final.exitCode);
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
// src/commands/style.ts
|
|
1594
|
+
async function run10(parsed) {
|
|
1595
|
+
const flags = parsed.flags;
|
|
1596
|
+
const opts = {
|
|
1597
|
+
foreground: flagStr(flags, "foreground", "") || void 0,
|
|
1598
|
+
background: flagStr(flags, "background", "") || void 0,
|
|
1599
|
+
border: flagStr(flags, "border", "") || void 0,
|
|
1600
|
+
borderForeground: flagStr(flags, "border-foreground", "") || flagStr(flags, "border.foreground", "") || void 0,
|
|
1601
|
+
borderBackground: flagStr(flags, "border-background", "") || flagStr(flags, "border.background", "") || void 0,
|
|
1602
|
+
align: flagStr(flags, "align", "") || void 0,
|
|
1603
|
+
height: flags["height"] !== void 0 ? parseInt(String(flags["height"]), 10) : void 0,
|
|
1604
|
+
width: flags["width"] !== void 0 ? parseInt(String(flags["width"]), 10) : void 0,
|
|
1605
|
+
margin: flagStr(flags, "margin", "") || void 0,
|
|
1606
|
+
padding: flagStr(flags, "padding", "") || void 0,
|
|
1607
|
+
bold: flags["bold"] === true || flags["bold"] === "true",
|
|
1608
|
+
faint: flags["faint"] === true || flags["faint"] === "true",
|
|
1609
|
+
italic: flags["italic"] === true || flags["italic"] === "true",
|
|
1610
|
+
strikethrough: flags["strikethrough"] === true || flags["strikethrough"] === "true",
|
|
1611
|
+
underline: flags["underline"] === true || flags["underline"] === "true"
|
|
1612
|
+
};
|
|
1613
|
+
const style = toLipgloss(opts);
|
|
1614
|
+
let text;
|
|
1615
|
+
if (parsed.args.length > 0) {
|
|
1616
|
+
text = parsed.args.join(" ");
|
|
1617
|
+
} else if (!isStdinEmpty()) {
|
|
1618
|
+
text = readStdin();
|
|
1619
|
+
} else {
|
|
1620
|
+
text = "";
|
|
1621
|
+
}
|
|
1622
|
+
const result = style.render(text);
|
|
1623
|
+
println(result);
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
// src/commands/table.ts
|
|
1627
|
+
import { Program as Program7, KeyPressMsg as KeyPressMsg7, KeyCode as KeyCode7, KeyMod as KeyMod7, Quit as Quit7, WindowSizeMsg as WindowSizeMsg6 } from "@oakoliver/bubbletea";
|
|
1628
|
+
import {
|
|
1629
|
+
newTable,
|
|
1630
|
+
withColumns,
|
|
1631
|
+
withRows,
|
|
1632
|
+
withTableFocused,
|
|
1633
|
+
withTableHeight,
|
|
1634
|
+
withTableStyles,
|
|
1635
|
+
tableDefaultStyles
|
|
1636
|
+
} from "@oakoliver/bubbles";
|
|
1637
|
+
import { newStyle as newStyle5, stringWidth as stringWidth2 } from "@oakoliver/lipgloss";
|
|
1638
|
+
import * as fs3 from "node:fs";
|
|
1639
|
+
function parseCSV(input, separator) {
|
|
1640
|
+
const rows = [];
|
|
1641
|
+
let i = 0;
|
|
1642
|
+
const len = input.length;
|
|
1643
|
+
while (i < len) {
|
|
1644
|
+
const row = [];
|
|
1645
|
+
while (i < len) {
|
|
1646
|
+
if (input[i] === '"') {
|
|
1647
|
+
i++;
|
|
1648
|
+
let field = "";
|
|
1649
|
+
while (i < len) {
|
|
1650
|
+
if (input[i] === '"') {
|
|
1651
|
+
if (i + 1 < len && input[i + 1] === '"') {
|
|
1652
|
+
field += '"';
|
|
1653
|
+
i += 2;
|
|
1654
|
+
} else {
|
|
1655
|
+
i++;
|
|
1656
|
+
break;
|
|
1657
|
+
}
|
|
1658
|
+
} else {
|
|
1659
|
+
field += input[i];
|
|
1660
|
+
i++;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
row.push(field);
|
|
1664
|
+
} else {
|
|
1665
|
+
let field = "";
|
|
1666
|
+
while (i < len && input[i] !== separator && input[i] !== "\n" && input[i] !== "\r") {
|
|
1667
|
+
field += input[i];
|
|
1668
|
+
i++;
|
|
1669
|
+
}
|
|
1670
|
+
row.push(field);
|
|
1671
|
+
}
|
|
1672
|
+
if (i < len && input[i] === separator) {
|
|
1673
|
+
i++;
|
|
1674
|
+
} else {
|
|
1675
|
+
break;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
if (i < len && input[i] === "\r") i++;
|
|
1679
|
+
if (i < len && input[i] === "\n") i++;
|
|
1680
|
+
if (row.length === 1 && row[0] === "" && i >= len) break;
|
|
1681
|
+
rows.push(row);
|
|
1682
|
+
}
|
|
1683
|
+
return rows;
|
|
1684
|
+
}
|
|
1685
|
+
function writeCSV(row, separator) {
|
|
1686
|
+
return row.map((field) => {
|
|
1687
|
+
if (field.includes(separator) || field.includes('"') || field.includes("\n")) {
|
|
1688
|
+
return '"' + field.replace(/"/g, '""') + '"';
|
|
1689
|
+
}
|
|
1690
|
+
return field;
|
|
1691
|
+
}).join(separator);
|
|
1692
|
+
}
|
|
1693
|
+
function createModel7(table, showHelp) {
|
|
1694
|
+
return {
|
|
1695
|
+
table,
|
|
1696
|
+
aborted: false,
|
|
1697
|
+
quitting: false,
|
|
1698
|
+
showHelp,
|
|
1699
|
+
init() {
|
|
1700
|
+
this.table.focus();
|
|
1701
|
+
return null;
|
|
1702
|
+
},
|
|
1703
|
+
update(msg) {
|
|
1704
|
+
if (msg instanceof WindowSizeMsg6) {
|
|
1705
|
+
this.table.setWidth(msg.width);
|
|
1706
|
+
this.table.setHeight(msg.height - 2);
|
|
1707
|
+
this.table.updateViewport();
|
|
1708
|
+
return [this, null];
|
|
1709
|
+
}
|
|
1710
|
+
if (msg instanceof KeyPressMsg7) {
|
|
1711
|
+
if (msg.mod & KeyMod7.Ctrl && msg.text === "c") {
|
|
1712
|
+
this.aborted = true;
|
|
1713
|
+
this.quitting = true;
|
|
1714
|
+
return [this, () => Quit7()];
|
|
1715
|
+
}
|
|
1716
|
+
if (msg.code === KeyCode7.Escape || msg.text === "q" || msg.mod & KeyMod7.Ctrl && msg.text === "q") {
|
|
1717
|
+
this.aborted = true;
|
|
1718
|
+
this.quitting = true;
|
|
1719
|
+
return [this, () => Quit7()];
|
|
1720
|
+
}
|
|
1721
|
+
if (msg.code === KeyCode7.Enter) {
|
|
1722
|
+
this.quitting = true;
|
|
1723
|
+
return [this, () => Quit7()];
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
const [updated, cmd] = this.table.update(msg);
|
|
1727
|
+
this.table = updated;
|
|
1728
|
+
return [this, cmd];
|
|
1729
|
+
},
|
|
1730
|
+
view() {
|
|
1731
|
+
if (this.quitting) return "";
|
|
1732
|
+
let out = this.table.view();
|
|
1733
|
+
const total = this.table.rows().length;
|
|
1734
|
+
const current = this.table.cursor() + 1;
|
|
1735
|
+
const counter = ` ${current}/${total}`;
|
|
1736
|
+
if (this.showHelp) {
|
|
1737
|
+
out += "\n" + newStyle5().faint(true).render(
|
|
1738
|
+
"\u2191/\u2193: navigate \u2022 enter: select \u2022 q/esc: quit"
|
|
1739
|
+
) + counter;
|
|
1740
|
+
} else {
|
|
1741
|
+
out += "\n" + counter;
|
|
1742
|
+
}
|
|
1743
|
+
return out;
|
|
1744
|
+
}
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
async function run11(parsed) {
|
|
1748
|
+
const flags = parsed.flags;
|
|
1749
|
+
const separator = flagStr(flags, "separator", ",");
|
|
1750
|
+
const colFlag = flagStr(flags, "columns", "");
|
|
1751
|
+
const widthsFlag = flagStr(flags, "widths", "");
|
|
1752
|
+
const height = flagInt(flags, "height", 0);
|
|
1753
|
+
const printMode = flagBool(flags, "print", false);
|
|
1754
|
+
const filePath = flagStr(flags, "file", "");
|
|
1755
|
+
const showHelp = flagBool(flags, "show-help", true);
|
|
1756
|
+
const returnColumn = flagInt(flags, "return-column", 0);
|
|
1757
|
+
let input = "";
|
|
1758
|
+
if (filePath) {
|
|
1759
|
+
input = fs3.readFileSync(filePath, "utf-8");
|
|
1760
|
+
} else if (!isStdinEmpty()) {
|
|
1761
|
+
input = readStdin();
|
|
1762
|
+
} else {
|
|
1763
|
+
console.error("Error: no input provided (pipe data or use --file)");
|
|
1764
|
+
process.exit(1);
|
|
1765
|
+
}
|
|
1766
|
+
const allRows = parseCSV(input, separator);
|
|
1767
|
+
if (allRows.length === 0) {
|
|
1768
|
+
console.error("Error: no data");
|
|
1769
|
+
process.exit(1);
|
|
1770
|
+
}
|
|
1771
|
+
let headers;
|
|
1772
|
+
let dataRows;
|
|
1773
|
+
if (colFlag) {
|
|
1774
|
+
headers = colFlag.split(",");
|
|
1775
|
+
dataRows = allRows;
|
|
1776
|
+
} else {
|
|
1777
|
+
headers = allRows[0];
|
|
1778
|
+
dataRows = allRows.slice(1);
|
|
1779
|
+
}
|
|
1780
|
+
const explicitWidths = widthsFlag ? widthsFlag.split(",").map((w) => parseInt(w, 10)) : [];
|
|
1781
|
+
const columns = headers.map((title, i) => {
|
|
1782
|
+
if (explicitWidths[i] && explicitWidths[i] > 0) {
|
|
1783
|
+
return { title, width: explicitWidths[i] };
|
|
1784
|
+
}
|
|
1785
|
+
let maxW = stringWidth2(title);
|
|
1786
|
+
for (const row of dataRows) {
|
|
1787
|
+
if (row[i] !== void 0) {
|
|
1788
|
+
maxW = Math.max(maxW, stringWidth2(row[i]));
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
return { title, width: maxW + 2 };
|
|
1792
|
+
});
|
|
1793
|
+
const cellOpts = extractStyleOptions(flags, "cell");
|
|
1794
|
+
const headerOpts = extractStyleOptions(flags, "header");
|
|
1795
|
+
const selectedOpts = extractStyleOptions(flags, "selected");
|
|
1796
|
+
if (!selectedOpts.foreground) selectedOpts.foreground = "212";
|
|
1797
|
+
const baseStyles = tableDefaultStyles();
|
|
1798
|
+
const styles = {
|
|
1799
|
+
cell: toLipgloss(cellOpts).inherit(baseStyles.cell),
|
|
1800
|
+
header: toLipgloss(headerOpts).inherit(baseStyles.header),
|
|
1801
|
+
selected: toLipgloss(selectedOpts).inherit(baseStyles.selected)
|
|
1802
|
+
};
|
|
1803
|
+
const tableHeight = height > 0 ? height : Math.min(dataRows.length, 20);
|
|
1804
|
+
const table = newTable(
|
|
1805
|
+
withColumns(columns),
|
|
1806
|
+
withRows(dataRows),
|
|
1807
|
+
withTableFocused(true),
|
|
1808
|
+
withTableStyles(styles),
|
|
1809
|
+
withTableHeight(tableHeight)
|
|
1810
|
+
);
|
|
1811
|
+
if (printMode) {
|
|
1812
|
+
println(table.view());
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
const model = createModel7(table, showHelp);
|
|
1816
|
+
const p = new Program7(model);
|
|
1817
|
+
const final = await p.run();
|
|
1818
|
+
if (final.aborted) {
|
|
1819
|
+
process.exit(STATUS_ABORTED);
|
|
1820
|
+
}
|
|
1821
|
+
const selected = final.table.selectedRow();
|
|
1822
|
+
if (!selected || selected.length === 0) return;
|
|
1823
|
+
if (returnColumn > 0 && returnColumn <= selected.length) {
|
|
1824
|
+
println(selected[returnColumn - 1]);
|
|
1825
|
+
} else {
|
|
1826
|
+
println(writeCSV(selected, separator));
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
// src/commands/write.ts
|
|
1831
|
+
import { Program as Program8, KeyPressMsg as KeyPressMsg8, KeyCode as KeyCode8, KeyMod as KeyMod8, Quit as Quit8, WindowSizeMsg as WindowSizeMsg7 } from "@oakoliver/bubbletea";
|
|
1832
|
+
import { newTextarea } from "@oakoliver/bubbles";
|
|
1833
|
+
function createModel8(parsed) {
|
|
1834
|
+
const flags = parsed.flags;
|
|
1835
|
+
const ta = newTextarea();
|
|
1836
|
+
ta.placeholder = flagWithEnv(flags, "placeholder", "GUM_WRITE_PLACEHOLDER", "Write something...");
|
|
1837
|
+
ta.prompt = flagStr(flags, "prompt", "\u2503 ");
|
|
1838
|
+
const value = flagStr(flags, "value", "");
|
|
1839
|
+
if (value) ta.setValue(value);
|
|
1840
|
+
const charLimit = flagInt(flags, "char-limit", 400);
|
|
1841
|
+
ta.charLimit = charLimit > 0 ? charLimit : 0;
|
|
1842
|
+
const showLineNumbers = flagBool(flags, "show-line-numbers", false);
|
|
1843
|
+
ta.showLineNumbers = showLineNumbers;
|
|
1844
|
+
const widthFlag = flagInt(flags, "width", 0);
|
|
1845
|
+
const heightFlag = flagInt(flags, "height", 10);
|
|
1846
|
+
const autoWidth = widthFlag === 0;
|
|
1847
|
+
const autoHeight = heightFlag === 0;
|
|
1848
|
+
if (!autoWidth) {
|
|
1849
|
+
ta.setWidth(widthFlag);
|
|
1850
|
+
}
|
|
1851
|
+
if (!autoHeight) {
|
|
1852
|
+
ta.setHeight(heightFlag);
|
|
1853
|
+
}
|
|
1854
|
+
const header = flagStr(flags, "header", "");
|
|
1855
|
+
const headerStyle = toLipgloss(extractStyleOptions(flags, "header"));
|
|
1856
|
+
const cursorFg = flagStr(flags, "cursor.foreground", "");
|
|
1857
|
+
if (cursorFg) {
|
|
1858
|
+
const styles = ta.getStyles();
|
|
1859
|
+
styles.cursor.color = cursorFg;
|
|
1860
|
+
ta.setStyles(styles);
|
|
1861
|
+
}
|
|
1862
|
+
const baseStyleOpts = extractStyleOptions(flags, "base");
|
|
1863
|
+
if (Object.values(baseStyleOpts).some((v) => v !== void 0 && v !== false && v !== "")) {
|
|
1864
|
+
const styles = ta.getStyles();
|
|
1865
|
+
const baseStyle = toLipgloss(baseStyleOpts);
|
|
1866
|
+
styles.focused.base = baseStyle;
|
|
1867
|
+
ta.setStyles(styles);
|
|
1868
|
+
}
|
|
1869
|
+
ta.focus();
|
|
1870
|
+
return {
|
|
1871
|
+
textarea: ta,
|
|
1872
|
+
aborted: false,
|
|
1873
|
+
quitting: false,
|
|
1874
|
+
header,
|
|
1875
|
+
headerStyle,
|
|
1876
|
+
autoWidth,
|
|
1877
|
+
autoHeight,
|
|
1878
|
+
width: widthFlag || 80,
|
|
1879
|
+
init() {
|
|
1880
|
+
return null;
|
|
1881
|
+
},
|
|
1882
|
+
update(msg) {
|
|
1883
|
+
if (msg instanceof WindowSizeMsg7) {
|
|
1884
|
+
this.width = msg.width;
|
|
1885
|
+
if (this.autoWidth) {
|
|
1886
|
+
this.textarea.setWidth(msg.width);
|
|
1887
|
+
}
|
|
1888
|
+
if (this.autoHeight) {
|
|
1889
|
+
this.textarea.setHeight(msg.height - 2);
|
|
1890
|
+
}
|
|
1891
|
+
return [this, null];
|
|
1892
|
+
}
|
|
1893
|
+
if (msg instanceof KeyPressMsg8) {
|
|
1894
|
+
if (msg.mod & KeyMod8.Ctrl && msg.text === "c") {
|
|
1895
|
+
this.aborted = true;
|
|
1896
|
+
this.quitting = true;
|
|
1897
|
+
return [this, () => Quit8()];
|
|
1898
|
+
}
|
|
1899
|
+
if (msg.code === KeyCode8.Escape) {
|
|
1900
|
+
this.quitting = true;
|
|
1901
|
+
return [this, () => Quit8()];
|
|
1902
|
+
}
|
|
1903
|
+
if (msg.mod & KeyMod8.Ctrl && msg.text === "d") {
|
|
1904
|
+
this.quitting = true;
|
|
1905
|
+
return [this, () => Quit8()];
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
const [updatedTa, cmd] = this.textarea.update(msg);
|
|
1909
|
+
this.textarea = updatedTa;
|
|
1910
|
+
return [this, cmd];
|
|
1911
|
+
},
|
|
1912
|
+
view() {
|
|
1913
|
+
if (this.quitting) return "";
|
|
1914
|
+
let out = "";
|
|
1915
|
+
if (this.header) {
|
|
1916
|
+
out += this.headerStyle.render(this.header) + "\n";
|
|
1917
|
+
}
|
|
1918
|
+
out += this.textarea.view();
|
|
1919
|
+
return out;
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
async function run12(parsed) {
|
|
1924
|
+
const model = createModel8(parsed);
|
|
1925
|
+
const p = new Program8(model);
|
|
1926
|
+
const final = await p.run();
|
|
1927
|
+
if (final.aborted) {
|
|
1928
|
+
process.exit(STATUS_ABORTED);
|
|
1929
|
+
}
|
|
1930
|
+
process.stdout.write(final.textarea.value() + "\n");
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
// src/cli.ts
|
|
1934
|
+
var VERSION = "1.0.1";
|
|
1935
|
+
function printHelp() {
|
|
1936
|
+
console.log(`gum ${VERSION}
|
|
1937
|
+
A tool for glamorous shell scripts.
|
|
1938
|
+
|
|
1939
|
+
Usage:
|
|
1940
|
+
gum <command> [flags]
|
|
1941
|
+
|
|
1942
|
+
Commands:
|
|
1943
|
+
choose Choose option(s) from a list
|
|
1944
|
+
confirm Ask a user to confirm an action
|
|
1945
|
+
file Pick a file from a folder
|
|
1946
|
+
filter Filter items from a list (coming soon)
|
|
1947
|
+
format Format text using a template
|
|
1948
|
+
input Prompt for input
|
|
1949
|
+
join Join multiple strings
|
|
1950
|
+
log Log a message with a level
|
|
1951
|
+
pager Scroll through content
|
|
1952
|
+
spin Display a spinner while running a command
|
|
1953
|
+
style Apply styling to text
|
|
1954
|
+
table Display data in a table
|
|
1955
|
+
write Prompt for multi-line input
|
|
1956
|
+
|
|
1957
|
+
Flags:
|
|
1958
|
+
-h, --help Show help
|
|
1959
|
+
-v, --version Show version
|
|
1960
|
+
|
|
1961
|
+
Examples:
|
|
1962
|
+
gum choose "Option 1" "Option 2" "Option 3"
|
|
1963
|
+
gum confirm "Are you sure?"
|
|
1964
|
+
gum input --placeholder "Enter your name"
|
|
1965
|
+
gum spin --title "Loading..." -- sleep 3
|
|
1966
|
+
gum style --foreground 212 "Hello, World!"
|
|
1967
|
+
`);
|
|
1968
|
+
}
|
|
1969
|
+
async function main() {
|
|
1970
|
+
const args = process.argv.slice(2);
|
|
1971
|
+
if (args.length === 0 || args[0] === "-h" || args[0] === "--help") {
|
|
1972
|
+
printHelp();
|
|
1973
|
+
process.exit(0);
|
|
1974
|
+
}
|
|
1975
|
+
if (args[0] === "-v" || args[0] === "--version") {
|
|
1976
|
+
console.log(`gum version ${VERSION}`);
|
|
1977
|
+
process.exit(0);
|
|
1978
|
+
}
|
|
1979
|
+
const command = args[0];
|
|
1980
|
+
const parsed = parseArgs(args.slice(1));
|
|
1981
|
+
try {
|
|
1982
|
+
switch (command) {
|
|
1983
|
+
case "choose":
|
|
1984
|
+
await run(parsed);
|
|
1985
|
+
break;
|
|
1986
|
+
case "confirm":
|
|
1987
|
+
await run2(parsed);
|
|
1988
|
+
break;
|
|
1989
|
+
case "file":
|
|
1990
|
+
await run3(parsed);
|
|
1991
|
+
break;
|
|
1992
|
+
case "format":
|
|
1993
|
+
await run4(parsed);
|
|
1994
|
+
break;
|
|
1995
|
+
case "input":
|
|
1996
|
+
await run5(parsed);
|
|
1997
|
+
break;
|
|
1998
|
+
case "join":
|
|
1999
|
+
await run6(parsed);
|
|
2000
|
+
break;
|
|
2001
|
+
case "log":
|
|
2002
|
+
await run7(parsed);
|
|
2003
|
+
break;
|
|
2004
|
+
case "pager":
|
|
2005
|
+
await run8(parsed);
|
|
2006
|
+
break;
|
|
2007
|
+
case "spin":
|
|
2008
|
+
await run9(parsed);
|
|
2009
|
+
break;
|
|
2010
|
+
case "style":
|
|
2011
|
+
await run10(parsed);
|
|
2012
|
+
break;
|
|
2013
|
+
case "table":
|
|
2014
|
+
await run11(parsed);
|
|
2015
|
+
break;
|
|
2016
|
+
case "write":
|
|
2017
|
+
await run12(parsed);
|
|
2018
|
+
break;
|
|
2019
|
+
case "filter":
|
|
2020
|
+
console.error("filter command is not yet implemented");
|
|
2021
|
+
process.exit(1);
|
|
2022
|
+
break;
|
|
2023
|
+
default:
|
|
2024
|
+
console.error(`Unknown command: ${command}`);
|
|
2025
|
+
printHelp();
|
|
2026
|
+
process.exit(1);
|
|
2027
|
+
}
|
|
2028
|
+
} catch (error) {
|
|
2029
|
+
console.error(`Error: ${error instanceof Error ? error.message : error}`);
|
|
2030
|
+
process.exit(1);
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
main();
|