canvaslms-cli 1.6.2 → 1.6.4-fix
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/CHANGELOG.md +64 -31
- package/README.md +131 -129
- package/dist/commands/announcements.d.ts +2 -2
- package/dist/commands/announcements.d.ts.map +1 -1
- package/dist/commands/announcements.js +29 -20
- package/dist/commands/announcements.js.map +1 -1
- package/dist/commands/api.d.ts +1 -1
- package/dist/commands/api.d.ts.map +1 -1
- package/dist/commands/api.js +1 -1
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/assignments.d.ts +2 -2
- package/dist/commands/assignments.d.ts.map +1 -1
- package/dist/commands/assignments.js +24 -19
- package/dist/commands/assignments.js.map +1 -1
- package/dist/commands/calendar.d.ts +7 -0
- package/dist/commands/calendar.d.ts.map +1 -0
- package/dist/commands/calendar.js +150 -0
- package/dist/commands/calendar.js.map +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +102 -85
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/grades.d.ts +2 -2
- package/dist/commands/grades.d.ts.map +1 -1
- package/dist/commands/grades.js +296 -176
- package/dist/commands/grades.js.map +1 -1
- package/dist/commands/list.d.ts +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +21 -19
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/modules.d.ts +5 -0
- package/dist/commands/modules.d.ts.map +1 -0
- package/dist/commands/modules.js +263 -0
- package/dist/commands/modules.js.map +1 -0
- package/dist/commands/profile.d.ts.map +1 -1
- package/dist/commands/profile.js +57 -32
- package/dist/commands/profile.js.map +1 -1
- package/dist/commands/submit.d.ts +1 -2
- package/dist/commands/submit.d.ts.map +1 -1
- package/dist/commands/submit.js +88 -79
- package/dist/commands/submit.js.map +1 -1
- package/dist/index.d.ts +16 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -14
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts +3 -0
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +31 -16
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/config-validator.d.ts.map +1 -1
- package/dist/lib/config-validator.js +12 -12
- package/dist/lib/config-validator.js.map +1 -1
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +16 -14
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/display.d.ts +10 -4
- package/dist/lib/display.d.ts.map +1 -1
- package/dist/lib/display.js +263 -167
- package/dist/lib/display.js.map +1 -1
- package/dist/lib/file-upload.d.ts.map +1 -1
- package/dist/lib/file-upload.js +17 -17
- package/dist/lib/file-upload.js.map +1 -1
- package/dist/lib/interactive.d.ts +1 -1
- package/dist/lib/interactive.d.ts.map +1 -1
- package/dist/lib/interactive.js +196 -144
- package/dist/lib/interactive.js.map +1 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +83 -63
- package/dist/src/index.js.map +1 -1
- package/dist/types/index.d.ts +34 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +10 -3
- package/dist/commands/coursenames.d.ts +0 -15
- package/dist/commands/coursenames.d.ts.map +0 -1
- package/dist/commands/coursenames.js +0 -150
- package/dist/commands/coursenames.js.map +0 -1
- package/dist/lib/course-utils.d.ts +0 -6
- package/dist/lib/course-utils.d.ts.map +0 -1
- package/dist/lib/course-utils.js +0 -17
- package/dist/lib/course-utils.js.map +0 -1
package/dist/lib/interactive.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import readline from
|
|
2
|
-
import fs from
|
|
3
|
-
import path from
|
|
4
|
-
import AdmZip from
|
|
5
|
-
import chalk from
|
|
1
|
+
import readline from "readline";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import AdmZip from "adm-zip";
|
|
5
|
+
import chalk from "chalk";
|
|
6
6
|
const KEYS = {
|
|
7
|
-
UP:
|
|
8
|
-
DOWN:
|
|
9
|
-
LEFT:
|
|
10
|
-
RIGHT:
|
|
11
|
-
SPACE:
|
|
12
|
-
ENTER:
|
|
13
|
-
ESCAPE:
|
|
14
|
-
BACKSPACE:
|
|
15
|
-
TAB:
|
|
16
|
-
CTRL_C:
|
|
7
|
+
UP: "\u001b[A",
|
|
8
|
+
DOWN: "\u001b[B",
|
|
9
|
+
LEFT: "\u001b[D",
|
|
10
|
+
RIGHT: "\u001b[C",
|
|
11
|
+
SPACE: " ",
|
|
12
|
+
ENTER: "\r",
|
|
13
|
+
ESCAPE: "\u001b",
|
|
14
|
+
BACKSPACE: "\u007f",
|
|
15
|
+
TAB: "\t",
|
|
16
|
+
CTRL_C: "\u0003",
|
|
17
17
|
};
|
|
18
18
|
export function createReadlineInterface() {
|
|
19
|
-
if (process.stdin.isTTY && typeof process.stdin.setRawMode ===
|
|
19
|
+
if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
|
|
20
20
|
try {
|
|
21
21
|
process.stdin.setRawMode(false);
|
|
22
22
|
}
|
|
@@ -26,7 +26,7 @@ export function createReadlineInterface() {
|
|
|
26
26
|
return readline.createInterface({
|
|
27
27
|
input: process.stdin,
|
|
28
28
|
output: process.stdout,
|
|
29
|
-
terminal: true
|
|
29
|
+
terminal: true,
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
export function askQuestion(rl, question) {
|
|
@@ -46,7 +46,7 @@ export function askQuestionWithValidation(rl, question, validator, errorMessage)
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
else {
|
|
49
|
-
console.log(errorMessage ||
|
|
49
|
+
console.log(errorMessage || "Invalid input. Please try again.");
|
|
50
50
|
}
|
|
51
51
|
} while (true);
|
|
52
52
|
});
|
|
@@ -78,22 +78,24 @@ export async function askConfirmation(rl, question, defaultYes = true, options =
|
|
|
78
78
|
}
|
|
79
79
|
export async function selectFromList(rl, items, displayProperty = null, allowCancel = true) {
|
|
80
80
|
if (!items || items.length === 0) {
|
|
81
|
-
console.log(
|
|
81
|
+
console.log("No items to select from.");
|
|
82
82
|
return null;
|
|
83
83
|
}
|
|
84
|
-
console.log(
|
|
84
|
+
console.log("\nSelect an option:");
|
|
85
85
|
items.forEach((item, index) => {
|
|
86
|
-
const displayText = displayProperty && typeof item ===
|
|
86
|
+
const displayText = displayProperty && typeof item === "object" && item !== null
|
|
87
|
+
? item[displayProperty]
|
|
88
|
+
: item;
|
|
87
89
|
console.log(`${index + 1}. ${displayText}`);
|
|
88
90
|
});
|
|
89
91
|
if (allowCancel) {
|
|
90
|
-
console.log(
|
|
92
|
+
console.log("0. Cancel");
|
|
91
93
|
}
|
|
92
94
|
const validator = (input) => {
|
|
93
95
|
const num = parseInt(input);
|
|
94
96
|
return !isNaN(num) && num >= (allowCancel ? 0 : 1) && num <= items.length;
|
|
95
97
|
};
|
|
96
|
-
const answer = await askQuestionWithValidation(rl,
|
|
98
|
+
const answer = await askQuestionWithValidation(rl, "\nEnter your choice: ", validator, `Please enter a number between ${allowCancel ? "0" : "1"} and ${items.length}.`);
|
|
97
99
|
const choice = parseInt(answer);
|
|
98
100
|
if (choice === 0 && allowCancel) {
|
|
99
101
|
return null;
|
|
@@ -111,13 +113,14 @@ export function getSubfoldersRecursive(startDir = process.cwd()) {
|
|
|
111
113
|
const stat = fs.statSync(fullPath);
|
|
112
114
|
if (stat.isDirectory()) {
|
|
113
115
|
const baseName = path.basename(fullPath);
|
|
114
|
-
if ([
|
|
116
|
+
if (["node_modules", ".git", "dist", "build"].includes(baseName))
|
|
115
117
|
continue;
|
|
116
118
|
result.push(fullPath);
|
|
117
119
|
walk(fullPath);
|
|
118
120
|
}
|
|
119
121
|
}
|
|
120
|
-
catch
|
|
122
|
+
catch {
|
|
123
|
+
console.warn(`Skipped unreadable folder: ${fullPath}`);
|
|
121
124
|
}
|
|
122
125
|
}
|
|
123
126
|
}
|
|
@@ -129,33 +132,34 @@ export function getFilesMatchingWildcard(pattern, currentDir = process.cwd()) {
|
|
|
129
132
|
const allFolders = [currentDir, ...getSubfoldersRecursive(currentDir)];
|
|
130
133
|
let allFiles = [];
|
|
131
134
|
for (const folder of allFolders) {
|
|
132
|
-
const files = fs.readdirSync(folder).map(f => path.join(folder, f));
|
|
135
|
+
const files = fs.readdirSync(folder).map((f) => path.join(folder, f));
|
|
133
136
|
for (const filePath of files) {
|
|
134
137
|
try {
|
|
135
138
|
if (fs.statSync(filePath).isFile()) {
|
|
136
139
|
allFiles.push(filePath);
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
|
-
catch
|
|
142
|
+
catch {
|
|
143
|
+
}
|
|
140
144
|
}
|
|
141
145
|
}
|
|
142
146
|
let regexPattern;
|
|
143
147
|
let matchFullPath = false;
|
|
144
|
-
if (pattern ===
|
|
145
|
-
regexPattern = new RegExp(
|
|
148
|
+
if (pattern === "*" || (!pattern.includes(".") && !pattern.includes("/"))) {
|
|
149
|
+
regexPattern = new RegExp(".*", "i");
|
|
146
150
|
matchFullPath = true;
|
|
147
151
|
}
|
|
148
|
-
else if (pattern.startsWith(
|
|
152
|
+
else if (pattern.startsWith("*.")) {
|
|
149
153
|
const extension = pattern.slice(2);
|
|
150
|
-
regexPattern = new RegExp(`\\.${extension.replace(/[.*+?^${}()|[\]\\]/g,
|
|
154
|
+
regexPattern = new RegExp(`\\.${extension.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "i");
|
|
151
155
|
}
|
|
152
|
-
else if (pattern.includes(
|
|
153
|
-
regexPattern = new RegExp(pattern.replace(/\*/g,
|
|
156
|
+
else if (pattern.includes("*")) {
|
|
157
|
+
regexPattern = new RegExp(pattern.replace(/\*/g, ".*").replace(/\?/g, "."), "i");
|
|
154
158
|
}
|
|
155
159
|
else {
|
|
156
|
-
regexPattern = new RegExp(`^${pattern.replace(/[.*+?^${}()|[\]\\]/g,
|
|
160
|
+
regexPattern = new RegExp(`^${pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "i");
|
|
157
161
|
}
|
|
158
|
-
const matchedFiles = allFiles.filter(filePath => {
|
|
162
|
+
const matchedFiles = allFiles.filter((filePath) => {
|
|
159
163
|
if (matchFullPath) {
|
|
160
164
|
const relPath = path.relative(currentDir, filePath);
|
|
161
165
|
return regexPattern.test(relPath);
|
|
@@ -173,40 +177,42 @@ export function getFilesMatchingWildcard(pattern, currentDir = process.cwd()) {
|
|
|
173
177
|
}
|
|
174
178
|
}
|
|
175
179
|
export function pad(str, len) {
|
|
176
|
-
return str +
|
|
180
|
+
return str + " ".repeat(Math.max(0, len - str.length));
|
|
177
181
|
}
|
|
178
182
|
export async function selectFilesImproved(rl, currentDir = process.cwd()) {
|
|
179
183
|
const selectedFiles = [];
|
|
180
|
-
console.log(chalk.cyan.bold(
|
|
181
|
-
console.log(chalk.cyan.bold(
|
|
182
|
-
console.log(chalk.cyan(
|
|
183
|
-
console.log(chalk.yellow(
|
|
184
|
-
console.log(
|
|
185
|
-
console.log(
|
|
184
|
+
console.log(chalk.cyan.bold("\n" + "-".repeat(50)));
|
|
185
|
+
console.log(chalk.cyan.bold("File Selection"));
|
|
186
|
+
console.log(chalk.cyan("-".repeat(50)));
|
|
187
|
+
console.log(chalk.yellow("Tips:"));
|
|
188
|
+
console.log(" • Type filename to add individual files");
|
|
189
|
+
console.log(" • Use wildcards: *.html, *.js, *.pdf, etc.");
|
|
186
190
|
console.log(' • Type "browse" to see available files');
|
|
187
191
|
console.log(' • Type "remove" to remove files from selection');
|
|
188
192
|
console.log(' • Type ".." or "back" to return to previous menu');
|
|
189
|
-
console.log(
|
|
193
|
+
console.log(" • Press Enter with no input to finish selection\n");
|
|
190
194
|
while (true) {
|
|
191
195
|
if (selectedFiles.length > 0) {
|
|
192
|
-
console.log(chalk.cyan(
|
|
196
|
+
console.log(chalk.cyan("\n" + "-".repeat(50)));
|
|
193
197
|
console.log(chalk.cyan.bold(`Currently selected (${selectedFiles.length} files):`));
|
|
194
198
|
selectedFiles.forEach((file, index) => {
|
|
195
199
|
const stats = fs.statSync(file);
|
|
196
|
-
const size = (stats.size / 1024).toFixed(1) +
|
|
197
|
-
console.log(pad(chalk.white(
|
|
200
|
+
const size = (stats.size / 1024).toFixed(1) + " KB";
|
|
201
|
+
console.log(pad(chalk.white(index + 1 + "."), 5) +
|
|
202
|
+
pad(path.basename(file), 35) +
|
|
203
|
+
chalk.gray(size));
|
|
198
204
|
});
|
|
199
|
-
console.log(chalk.cyan(
|
|
205
|
+
console.log(chalk.cyan("-".repeat(50)));
|
|
200
206
|
}
|
|
201
|
-
const input = await askQuestion(rl, chalk.bold.cyan(
|
|
207
|
+
const input = await askQuestion(rl, chalk.bold.cyan("\nAdd file (or press Enter to finish): "));
|
|
202
208
|
if (!input.trim())
|
|
203
209
|
break;
|
|
204
|
-
if (input ===
|
|
210
|
+
if (input === ".." || input.toLowerCase() === "back") {
|
|
205
211
|
return selectedFiles;
|
|
206
212
|
}
|
|
207
|
-
if (input.toLowerCase() ===
|
|
208
|
-
console.log(chalk.cyan(
|
|
209
|
-
console.log(chalk.cyan.bold(
|
|
213
|
+
if (input.toLowerCase() === "browse") {
|
|
214
|
+
console.log(chalk.cyan("\n" + "-".repeat(50)));
|
|
215
|
+
console.log(chalk.cyan.bold("Browsing available files:"));
|
|
210
216
|
try {
|
|
211
217
|
const listedFiles = [];
|
|
212
218
|
function walk(dir) {
|
|
@@ -214,7 +220,7 @@ export async function selectFilesImproved(rl, currentDir = process.cwd()) {
|
|
|
214
220
|
for (const entry of entries) {
|
|
215
221
|
const fullPath = path.join(dir, entry);
|
|
216
222
|
const relPath = path.relative(currentDir, fullPath);
|
|
217
|
-
if ([
|
|
223
|
+
if (["node_modules", ".git", "dist", "build"].includes(entry))
|
|
218
224
|
continue;
|
|
219
225
|
try {
|
|
220
226
|
const stat = fs.statSync(fullPath);
|
|
@@ -222,41 +228,47 @@ export async function selectFilesImproved(rl, currentDir = process.cwd()) {
|
|
|
222
228
|
walk(fullPath);
|
|
223
229
|
}
|
|
224
230
|
else if (stat.isFile()) {
|
|
225
|
-
listedFiles.push({
|
|
231
|
+
listedFiles.push({
|
|
232
|
+
path: fullPath,
|
|
233
|
+
rel: relPath,
|
|
234
|
+
size: stat.size,
|
|
235
|
+
});
|
|
226
236
|
}
|
|
227
237
|
}
|
|
228
|
-
catch
|
|
238
|
+
catch {
|
|
229
239
|
continue;
|
|
230
240
|
}
|
|
231
241
|
}
|
|
232
242
|
}
|
|
233
243
|
walk(currentDir);
|
|
234
244
|
if (listedFiles.length === 0) {
|
|
235
|
-
console.log(chalk.red(
|
|
245
|
+
console.log(chalk.red(" No suitable files found."));
|
|
236
246
|
}
|
|
237
247
|
else {
|
|
238
248
|
listedFiles.forEach((file, index) => {
|
|
239
249
|
const sizeKB = (file.size / 1024).toFixed(1);
|
|
240
|
-
console.log(pad(chalk.white(
|
|
250
|
+
console.log(pad(chalk.white(index + 1 + "."), 5) +
|
|
251
|
+
pad(file.rel, 35) +
|
|
252
|
+
chalk.gray(sizeKB + " KB"));
|
|
241
253
|
});
|
|
242
254
|
}
|
|
243
255
|
}
|
|
244
256
|
catch (error) {
|
|
245
257
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
246
|
-
console.log(chalk.red(
|
|
258
|
+
console.log(chalk.red(" Error reading directory: " + errorMessage));
|
|
247
259
|
}
|
|
248
260
|
continue;
|
|
249
261
|
}
|
|
250
|
-
if (input.toLowerCase() ===
|
|
262
|
+
if (input.toLowerCase() === "remove") {
|
|
251
263
|
if (selectedFiles.length === 0) {
|
|
252
|
-
console.log(chalk.red(
|
|
264
|
+
console.log(chalk.red("No files selected to remove."));
|
|
253
265
|
continue;
|
|
254
266
|
}
|
|
255
|
-
console.log(chalk.cyan(
|
|
267
|
+
console.log(chalk.cyan("\nSelect file to remove:"));
|
|
256
268
|
selectedFiles.forEach((file, index) => {
|
|
257
|
-
console.log(pad(chalk.white(
|
|
269
|
+
console.log(pad(chalk.white(index + 1 + "."), 5) + path.basename(file));
|
|
258
270
|
});
|
|
259
|
-
const removeChoice = await askQuestion(rl, chalk.bold.cyan(
|
|
271
|
+
const removeChoice = await askQuestion(rl, chalk.bold.cyan("\nEnter number to remove (or press Enter to cancel): "));
|
|
260
272
|
if (removeChoice.trim()) {
|
|
261
273
|
const removeIndex = parseInt(removeChoice) - 1;
|
|
262
274
|
if (removeIndex >= 0 && removeIndex < selectedFiles.length) {
|
|
@@ -266,14 +278,14 @@ export async function selectFilesImproved(rl, currentDir = process.cwd()) {
|
|
|
266
278
|
}
|
|
267
279
|
}
|
|
268
280
|
else {
|
|
269
|
-
console.log(chalk.red(
|
|
281
|
+
console.log(chalk.red("Invalid selection."));
|
|
270
282
|
}
|
|
271
283
|
}
|
|
272
284
|
continue;
|
|
273
285
|
}
|
|
274
286
|
let filePath = input;
|
|
275
287
|
let zipRequested = false;
|
|
276
|
-
if (filePath.endsWith(
|
|
288
|
+
if (filePath.endsWith(" -zip")) {
|
|
277
289
|
filePath = filePath.slice(0, -5).trim();
|
|
278
290
|
zipRequested = true;
|
|
279
291
|
}
|
|
@@ -282,16 +294,16 @@ export async function selectFilesImproved(rl, currentDir = process.cwd()) {
|
|
|
282
294
|
}
|
|
283
295
|
try {
|
|
284
296
|
if (!fs.existsSync(filePath)) {
|
|
285
|
-
console.log(chalk.red(
|
|
297
|
+
console.log(chalk.red("Error: File not found: " + input));
|
|
286
298
|
continue;
|
|
287
299
|
}
|
|
288
300
|
const stats = fs.statSync(filePath);
|
|
289
301
|
if (zipRequested) {
|
|
290
302
|
const baseName = path.basename(filePath);
|
|
291
|
-
const zipName = baseName.replace(/\.[^/.]+$/,
|
|
303
|
+
const zipName = baseName.replace(/\.[^/.]+$/, "") + ".zip";
|
|
292
304
|
const zipPath = path.join(currentDir, zipName);
|
|
293
305
|
const zip = new AdmZip();
|
|
294
|
-
process.stdout.write(chalk.yellow(
|
|
306
|
+
process.stdout.write(chalk.yellow("Zipping, please wait... "));
|
|
295
307
|
if (stats.isDirectory()) {
|
|
296
308
|
zip.addLocalFolder(filePath);
|
|
297
309
|
}
|
|
@@ -299,24 +311,24 @@ export async function selectFilesImproved(rl, currentDir = process.cwd()) {
|
|
|
299
311
|
zip.addLocalFile(filePath);
|
|
300
312
|
}
|
|
301
313
|
else {
|
|
302
|
-
console.log(chalk.red(
|
|
314
|
+
console.log(chalk.red("Not a file or folder."));
|
|
303
315
|
continue;
|
|
304
316
|
}
|
|
305
317
|
zip.writeZip(zipPath);
|
|
306
|
-
console.log(chalk.green(
|
|
318
|
+
console.log(chalk.green("Done."));
|
|
307
319
|
console.log(chalk.green(`Created ZIP: ${zipName}`));
|
|
308
320
|
if (selectedFiles.includes(zipPath)) {
|
|
309
321
|
console.log(chalk.yellow(`File already selected: ${zipName}`));
|
|
310
322
|
continue;
|
|
311
323
|
}
|
|
312
324
|
selectedFiles.push(zipPath);
|
|
313
|
-
const size = (fs.statSync(zipPath).size / 1024).toFixed(1) +
|
|
325
|
+
const size = (fs.statSync(zipPath).size / 1024).toFixed(1) + " KB";
|
|
314
326
|
console.log(chalk.green(`Added: ${zipName} (${size})`));
|
|
315
327
|
continue;
|
|
316
328
|
}
|
|
317
329
|
if (stats.isDirectory()) {
|
|
318
330
|
const baseName = path.basename(filePath);
|
|
319
|
-
if ([
|
|
331
|
+
if (["node_modules", ".git", "dist", "build"].includes(baseName))
|
|
320
332
|
continue;
|
|
321
333
|
const collectedFiles = [];
|
|
322
334
|
function walk(dir) {
|
|
@@ -326,7 +338,7 @@ export async function selectFilesImproved(rl, currentDir = process.cwd()) {
|
|
|
326
338
|
const stat = fs.statSync(fullPath);
|
|
327
339
|
if (stat.isDirectory()) {
|
|
328
340
|
const baseName = path.basename(fullPath);
|
|
329
|
-
if ([
|
|
341
|
+
if (["node_modules", ".git", "dist", "build"].includes(baseName))
|
|
330
342
|
continue;
|
|
331
343
|
walk(fullPath);
|
|
332
344
|
}
|
|
@@ -346,13 +358,15 @@ export async function selectFilesImproved(rl, currentDir = process.cwd()) {
|
|
|
346
358
|
const stat = fs.statSync(f);
|
|
347
359
|
totalSize += stat.size;
|
|
348
360
|
const relativePath = path.relative(currentDir, f);
|
|
349
|
-
console.log(pad(chalk.white(
|
|
361
|
+
console.log(pad(chalk.white(i + 1 + "."), 5) +
|
|
362
|
+
pad(relativePath, 35) +
|
|
363
|
+
chalk.gray((stat.size / 1024).toFixed(1) + " KB"));
|
|
350
364
|
});
|
|
351
|
-
console.log(chalk.cyan(
|
|
365
|
+
console.log(chalk.cyan("-".repeat(50)));
|
|
352
366
|
console.log(chalk.cyan(`Total size: ${(totalSize / 1024).toFixed(1)} KB`));
|
|
353
367
|
const confirmFolder = await askConfirmation(rl, chalk.bold.cyan(`Add all ${collectedFiles.length} files from this folder?`), true);
|
|
354
368
|
if (confirmFolder) {
|
|
355
|
-
const newFiles = collectedFiles.filter(f => !selectedFiles.includes(f));
|
|
369
|
+
const newFiles = collectedFiles.filter((f) => !selectedFiles.includes(f));
|
|
356
370
|
selectedFiles.push(...newFiles);
|
|
357
371
|
console.log(chalk.green(`Added ${newFiles.length} new files (${collectedFiles.length - newFiles.length} already selected)`));
|
|
358
372
|
}
|
|
@@ -363,12 +377,12 @@ export async function selectFilesImproved(rl, currentDir = process.cwd()) {
|
|
|
363
377
|
continue;
|
|
364
378
|
}
|
|
365
379
|
selectedFiles.push(filePath);
|
|
366
|
-
const size = (stats.size / 1024).toFixed(1) +
|
|
380
|
+
const size = (stats.size / 1024).toFixed(1) + " KB";
|
|
367
381
|
console.log(chalk.green(`Added: ${path.basename(filePath)} (${size})`));
|
|
368
382
|
}
|
|
369
383
|
catch (error) {
|
|
370
384
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
371
|
-
console.log(chalk.red(
|
|
385
|
+
console.log(chalk.red("Error accessing file: " + errorMessage));
|
|
372
386
|
}
|
|
373
387
|
}
|
|
374
388
|
return selectedFiles;
|
|
@@ -379,50 +393,81 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
379
393
|
let currentPath = currentDir;
|
|
380
394
|
let currentIndex = 0;
|
|
381
395
|
let isNavigating = true;
|
|
382
|
-
const prevDataListeners = process.stdin.listeners(
|
|
383
|
-
process.stdin.removeAllListeners(
|
|
396
|
+
const prevDataListeners = process.stdin.listeners("data").slice();
|
|
397
|
+
process.stdin.removeAllListeners("data");
|
|
384
398
|
if (process.stdin.isTTY) {
|
|
385
399
|
process.stdin.setRawMode(true);
|
|
386
400
|
}
|
|
387
401
|
process.stdin.resume();
|
|
388
|
-
process.stdin.setEncoding(
|
|
402
|
+
process.stdin.setEncoding("utf8");
|
|
389
403
|
function getFileIcon(filename) {
|
|
390
404
|
const ext = path.extname(filename).toLowerCase();
|
|
391
405
|
const icons = {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
406
|
+
".pdf": "📄",
|
|
407
|
+
".doc": "📄",
|
|
408
|
+
".docx": "📄",
|
|
409
|
+
".txt": "📄",
|
|
410
|
+
".js": "📜",
|
|
411
|
+
".ts": "📜",
|
|
412
|
+
".py": "📜",
|
|
413
|
+
".java": "📜",
|
|
414
|
+
".cpp": "📜",
|
|
415
|
+
".c": "📜",
|
|
416
|
+
".html": "🌐",
|
|
417
|
+
".css": "🎨",
|
|
418
|
+
".scss": "🎨",
|
|
419
|
+
".less": "🎨",
|
|
420
|
+
".json": "⚙️",
|
|
421
|
+
".xml": "⚙️",
|
|
422
|
+
".yml": "⚙️",
|
|
423
|
+
".yaml": "⚙️",
|
|
424
|
+
".zip": "📦",
|
|
425
|
+
".rar": "📦",
|
|
426
|
+
".7z": "📦",
|
|
427
|
+
".tar": "📦",
|
|
428
|
+
".jpg": "🖼️",
|
|
429
|
+
".jpeg": "🖼️",
|
|
430
|
+
".png": "🖼️",
|
|
431
|
+
".gif": "🖼️",
|
|
432
|
+
".svg": "🖼️",
|
|
433
|
+
".mp4": "🎬",
|
|
434
|
+
".avi": "🎬",
|
|
435
|
+
".mov": "🎬",
|
|
436
|
+
".mkv": "🎬",
|
|
437
|
+
".mp3": "🎵",
|
|
438
|
+
".wav": "🎵",
|
|
439
|
+
".flac": "🎵",
|
|
400
440
|
};
|
|
401
|
-
return icons[ext] ||
|
|
441
|
+
return icons[ext] || "📋";
|
|
402
442
|
}
|
|
403
443
|
function buildBreadcrumb() {
|
|
404
444
|
const relativePath = path.relative(currentDir, currentPath);
|
|
405
|
-
if (!relativePath || relativePath ===
|
|
406
|
-
return
|
|
445
|
+
if (!relativePath || relativePath === ".") {
|
|
446
|
+
return "";
|
|
407
447
|
}
|
|
408
448
|
const parts = relativePath.split(path.sep);
|
|
409
|
-
const breadcrumb = parts
|
|
449
|
+
const breadcrumb = parts
|
|
450
|
+
.map((part, index) => {
|
|
410
451
|
if (index === parts.length - 1) {
|
|
411
452
|
return chalk.white.bold(part);
|
|
412
453
|
}
|
|
413
454
|
return chalk.gray(part);
|
|
414
|
-
})
|
|
415
|
-
|
|
455
|
+
})
|
|
456
|
+
.join(chalk.gray(" › "));
|
|
457
|
+
return chalk.yellow("📂 ") + breadcrumb;
|
|
416
458
|
}
|
|
417
459
|
let lastDisplayLines = 0;
|
|
418
460
|
function stripAnsi(str) {
|
|
419
|
-
|
|
461
|
+
const ESC = String.fromCharCode(0x1b);
|
|
462
|
+
const CSI = String.fromCharCode(0x9b);
|
|
463
|
+
const pattern = `[${ESC}${CSI}][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]`;
|
|
464
|
+
return str.replace(new RegExp(pattern, "g"), "");
|
|
420
465
|
}
|
|
421
|
-
function printAndTrack(message =
|
|
466
|
+
function printAndTrack(message = "") {
|
|
422
467
|
console.log(message);
|
|
423
468
|
const width = process.stdout.columns || 80;
|
|
424
469
|
const clean = stripAnsi(message);
|
|
425
|
-
const lines = clean.split(
|
|
470
|
+
const lines = clean.split("\n");
|
|
426
471
|
let count = 0;
|
|
427
472
|
for (const line of lines) {
|
|
428
473
|
count += Math.max(1, Math.ceil((line.length || 0.5) / width));
|
|
@@ -445,11 +490,11 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
445
490
|
if (breadcrumb) {
|
|
446
491
|
printAndTrack(breadcrumb);
|
|
447
492
|
}
|
|
448
|
-
printAndTrack(chalk.gray(
|
|
493
|
+
printAndTrack(chalk.gray("💡 ↑↓←→:Navigate Space:Select Enter:Open/Finish Backspace:Up a:All c:Clear r:Reload Esc/Ctrl+C:Exit"));
|
|
449
494
|
if (Array.isArray(allowedExtensions) && allowedExtensions.length > 0) {
|
|
450
495
|
const exts = allowedExtensions
|
|
451
|
-
.map(e =>
|
|
452
|
-
.join(
|
|
496
|
+
.map((e) => e.startsWith(".") ? e.toLowerCase() : "." + e.toLowerCase())
|
|
497
|
+
.join(", ");
|
|
453
498
|
printAndTrack(chalk.yellow(`Allowed: ${exts}`));
|
|
454
499
|
}
|
|
455
500
|
if (selectedFiles.length > 0) {
|
|
@@ -465,7 +510,7 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
465
510
|
}
|
|
466
511
|
printAndTrack();
|
|
467
512
|
if (fileList.length === 0) {
|
|
468
|
-
printAndTrack(chalk.yellow(
|
|
513
|
+
printAndTrack(chalk.yellow("📭 No files found in this directory."));
|
|
469
514
|
return;
|
|
470
515
|
}
|
|
471
516
|
displayFileTree();
|
|
@@ -479,36 +524,38 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
479
524
|
if (startIdx > 0) {
|
|
480
525
|
printAndTrack(chalk.gray(` ⋮ (${startIdx} items above)`));
|
|
481
526
|
}
|
|
482
|
-
const maxItemWidth = Math.max(...visibleItems.map(item => {
|
|
527
|
+
const maxItemWidth = Math.max(...visibleItems.map((item) => {
|
|
483
528
|
const name = path.basename(item.path);
|
|
484
529
|
return name.length + 4;
|
|
485
530
|
}));
|
|
486
531
|
const itemWidth = Math.min(Math.max(maxItemWidth, 15), 25);
|
|
487
532
|
const columnsPerRow = Math.max(1, Math.floor((terminalWidth - 4) / itemWidth));
|
|
488
|
-
let currentRow =
|
|
533
|
+
let currentRow = "";
|
|
489
534
|
let itemsInCurrentRow = 0;
|
|
490
535
|
visibleItems.forEach((item, index) => {
|
|
491
536
|
const actualIndex = startIdx + index;
|
|
492
537
|
const isSelected = selectedFiles.includes(item.path);
|
|
493
538
|
const isCurrent = actualIndex === currentIndex;
|
|
494
|
-
let icon =
|
|
495
|
-
if (item.type ===
|
|
496
|
-
icon =
|
|
539
|
+
let icon = "";
|
|
540
|
+
if (item.type === "parent" || item.type === "directory") {
|
|
541
|
+
icon = "📁";
|
|
497
542
|
}
|
|
498
543
|
else {
|
|
499
544
|
icon = getFileIcon(path.basename(item.path));
|
|
500
545
|
}
|
|
501
546
|
const name = item.name || path.basename(item.path);
|
|
502
|
-
const truncatedName = name.length > itemWidth - 4
|
|
547
|
+
const truncatedName = name.length > itemWidth - 4
|
|
548
|
+
? name.slice(0, itemWidth - 7) + "..."
|
|
549
|
+
: name;
|
|
503
550
|
let itemDisplay = `${icon} ${truncatedName}`;
|
|
504
551
|
if (isCurrent) {
|
|
505
552
|
if (isSelected) {
|
|
506
553
|
itemDisplay = chalk.black.bgGreen(` ${itemDisplay}`.padEnd(itemWidth - 1));
|
|
507
554
|
}
|
|
508
|
-
else if (item.type ===
|
|
555
|
+
else if (item.type === "parent") {
|
|
509
556
|
itemDisplay = chalk.white.bgBlue(` ${itemDisplay}`.padEnd(itemWidth - 1));
|
|
510
557
|
}
|
|
511
|
-
else if (item.type ===
|
|
558
|
+
else if (item.type === "directory") {
|
|
512
559
|
itemDisplay = chalk.black.bgCyan(` ${itemDisplay}`.padEnd(itemWidth - 1));
|
|
513
560
|
}
|
|
514
561
|
else {
|
|
@@ -519,10 +566,10 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
519
566
|
if (isSelected) {
|
|
520
567
|
itemDisplay = chalk.green(`✓${itemDisplay}`.padEnd(itemWidth));
|
|
521
568
|
}
|
|
522
|
-
else if (item.type ===
|
|
569
|
+
else if (item.type === "parent") {
|
|
523
570
|
itemDisplay = chalk.blue(` ${itemDisplay}`.padEnd(itemWidth));
|
|
524
571
|
}
|
|
525
|
-
else if (item.type ===
|
|
572
|
+
else if (item.type === "directory") {
|
|
526
573
|
itemDisplay = chalk.cyan(` ${itemDisplay}`.padEnd(itemWidth));
|
|
527
574
|
}
|
|
528
575
|
else {
|
|
@@ -531,9 +578,10 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
531
578
|
}
|
|
532
579
|
currentRow += itemDisplay;
|
|
533
580
|
itemsInCurrentRow++;
|
|
534
|
-
if (itemsInCurrentRow >= columnsPerRow ||
|
|
581
|
+
if (itemsInCurrentRow >= columnsPerRow ||
|
|
582
|
+
index === visibleItems.length - 1) {
|
|
535
583
|
printAndTrack(currentRow);
|
|
536
|
-
currentRow =
|
|
584
|
+
currentRow = "";
|
|
537
585
|
itemsInCurrentRow = 0;
|
|
538
586
|
}
|
|
539
587
|
});
|
|
@@ -555,39 +603,40 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
555
603
|
try {
|
|
556
604
|
if (currentPath !== currentDir) {
|
|
557
605
|
fileList.push({
|
|
558
|
-
type:
|
|
606
|
+
type: "parent",
|
|
559
607
|
path: path.dirname(currentPath),
|
|
560
|
-
name:
|
|
608
|
+
name: "..",
|
|
561
609
|
});
|
|
562
610
|
}
|
|
563
611
|
const entries = fs.readdirSync(currentPath).sort();
|
|
564
|
-
entries.forEach(entry => {
|
|
612
|
+
entries.forEach((entry) => {
|
|
565
613
|
const fullPath = path.join(currentPath, entry);
|
|
566
614
|
const stat = fs.statSync(fullPath);
|
|
567
615
|
if (stat.isDirectory()) {
|
|
568
616
|
fileList.push({
|
|
569
|
-
type:
|
|
617
|
+
type: "directory",
|
|
570
618
|
path: fullPath,
|
|
571
|
-
name: entry
|
|
619
|
+
name: entry,
|
|
572
620
|
});
|
|
573
621
|
}
|
|
574
622
|
});
|
|
575
|
-
entries.forEach(entry => {
|
|
623
|
+
entries.forEach((entry) => {
|
|
576
624
|
const fullPath = path.join(currentPath, entry);
|
|
577
625
|
const stat = fs.statSync(fullPath);
|
|
578
626
|
if (stat.isFile()) {
|
|
579
|
-
if (Array.isArray(allowedExtensions) &&
|
|
580
|
-
|
|
627
|
+
if (Array.isArray(allowedExtensions) &&
|
|
628
|
+
allowedExtensions.length > 0) {
|
|
629
|
+
const lowerExts = allowedExtensions.map((e) => e.startsWith(".") ? e.toLowerCase() : "." + e.toLowerCase());
|
|
581
630
|
const ext = path.extname(entry).toLowerCase();
|
|
582
631
|
if (!lowerExts.includes(ext)) {
|
|
583
632
|
return;
|
|
584
633
|
}
|
|
585
634
|
}
|
|
586
635
|
fileList.push({
|
|
587
|
-
type:
|
|
636
|
+
type: "file",
|
|
588
637
|
path: fullPath,
|
|
589
638
|
name: entry,
|
|
590
|
-
size: stat.size
|
|
639
|
+
size: stat.size,
|
|
591
640
|
});
|
|
592
641
|
}
|
|
593
642
|
});
|
|
@@ -596,13 +645,13 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
596
645
|
}
|
|
597
646
|
}
|
|
598
647
|
catch (error) {
|
|
599
|
-
console.error(
|
|
648
|
+
console.error("Error reading directory:", error);
|
|
600
649
|
fileList = [];
|
|
601
650
|
}
|
|
602
651
|
}
|
|
603
652
|
function handleKeyInput(key) {
|
|
604
653
|
const terminalWidth = process.stdout.columns || 80;
|
|
605
|
-
const maxItemWidth = Math.max(...fileList.map(item => {
|
|
654
|
+
const maxItemWidth = Math.max(...fileList.map((item) => {
|
|
606
655
|
const name = path.basename(item.path);
|
|
607
656
|
return name.length + 4;
|
|
608
657
|
}));
|
|
@@ -638,7 +687,7 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
638
687
|
case KEYS.SPACE:
|
|
639
688
|
if (fileList.length > 0) {
|
|
640
689
|
const item = fileList[currentIndex];
|
|
641
|
-
if (item && item.type ===
|
|
690
|
+
if (item && item.type === "file") {
|
|
642
691
|
const index = selectedFiles.indexOf(item.path);
|
|
643
692
|
if (index === -1) {
|
|
644
693
|
selectedFiles.push(item.path);
|
|
@@ -653,7 +702,7 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
653
702
|
case KEYS.ENTER:
|
|
654
703
|
if (fileList.length > 0) {
|
|
655
704
|
const item = fileList[currentIndex];
|
|
656
|
-
if (item && (item.type ===
|
|
705
|
+
if (item && (item.type === "parent" || item.type === "directory")) {
|
|
657
706
|
currentPath = item.path;
|
|
658
707
|
currentIndex = 0;
|
|
659
708
|
refreshFileList();
|
|
@@ -679,10 +728,10 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
679
728
|
displayBrowser();
|
|
680
729
|
}
|
|
681
730
|
break;
|
|
682
|
-
case
|
|
731
|
+
case "a":
|
|
683
732
|
let addedCount = 0;
|
|
684
|
-
fileList.forEach(item => {
|
|
685
|
-
if (item.type ===
|
|
733
|
+
fileList.forEach((item) => {
|
|
734
|
+
if (item.type === "file" && !selectedFiles.includes(item.path)) {
|
|
686
735
|
selectedFiles.push(item.path);
|
|
687
736
|
addedCount++;
|
|
688
737
|
}
|
|
@@ -691,11 +740,11 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
691
740
|
displayBrowser();
|
|
692
741
|
}
|
|
693
742
|
break;
|
|
694
|
-
case
|
|
743
|
+
case "c":
|
|
695
744
|
selectedFiles.length = 0;
|
|
696
745
|
displayBrowser();
|
|
697
746
|
break;
|
|
698
|
-
case
|
|
747
|
+
case "r":
|
|
699
748
|
refreshFileList();
|
|
700
749
|
displayBrowser();
|
|
701
750
|
break;
|
|
@@ -704,7 +753,7 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
704
753
|
isNavigating = false;
|
|
705
754
|
break;
|
|
706
755
|
case KEYS.ESCAPE:
|
|
707
|
-
case
|
|
756
|
+
case "\u001b":
|
|
708
757
|
isNavigating = false;
|
|
709
758
|
break;
|
|
710
759
|
default:
|
|
@@ -721,10 +770,10 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
721
770
|
const keyStr = key.toString();
|
|
722
771
|
handleKeyInput(keyStr);
|
|
723
772
|
if (!isNavigating) {
|
|
724
|
-
process.stdin.removeListener(
|
|
773
|
+
process.stdin.removeListener("data", onData);
|
|
725
774
|
try {
|
|
726
775
|
for (const l of prevDataListeners) {
|
|
727
|
-
process.stdin.on(
|
|
776
|
+
process.stdin.on("data", l);
|
|
728
777
|
}
|
|
729
778
|
}
|
|
730
779
|
catch {
|
|
@@ -734,8 +783,8 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
734
783
|
}
|
|
735
784
|
process.stdin.pause();
|
|
736
785
|
if (selectedFiles.length > 0) {
|
|
737
|
-
console.log(chalk.green.bold(
|
|
738
|
-
console.log(chalk.cyan(
|
|
786
|
+
console.log(chalk.green.bold("✅ File Selection Complete!"));
|
|
787
|
+
console.log(chalk.cyan("-".repeat(50)));
|
|
739
788
|
const totalSize = selectedFiles.reduce((sum, file) => {
|
|
740
789
|
try {
|
|
741
790
|
return sum + fs.statSync(file).size;
|
|
@@ -748,19 +797,22 @@ export async function selectFilesKeyboard(_rl, currentDir = process.cwd(), allow
|
|
|
748
797
|
selectedFiles.forEach((file, index) => {
|
|
749
798
|
try {
|
|
750
799
|
const stats = fs.statSync(file);
|
|
751
|
-
const size = (stats.size / 1024).toFixed(1) +
|
|
752
|
-
console.log(pad(chalk.green(`${index + 1}.`), 5) +
|
|
800
|
+
const size = (stats.size / 1024).toFixed(1) + " KB";
|
|
801
|
+
console.log(pad(chalk.green(`${index + 1}.`), 5) +
|
|
802
|
+
pad(path.basename(file), 35) +
|
|
803
|
+
chalk.gray(size));
|
|
753
804
|
}
|
|
754
|
-
catch
|
|
755
|
-
console.log(pad(chalk.red(`${index + 1}.`), 5) +
|
|
805
|
+
catch {
|
|
806
|
+
console.log(pad(chalk.red(`${index + 1}.`), 5) +
|
|
807
|
+
chalk.red(path.basename(file) + " (Error reading file)"));
|
|
756
808
|
}
|
|
757
809
|
});
|
|
758
|
-
console.log(chalk.cyan(
|
|
810
|
+
console.log(chalk.cyan("-".repeat(50)));
|
|
759
811
|
}
|
|
760
812
|
resolve(selectedFiles);
|
|
761
813
|
}
|
|
762
814
|
};
|
|
763
|
-
process.stdin.on(
|
|
815
|
+
process.stdin.on("data", onData);
|
|
764
816
|
});
|
|
765
817
|
}
|
|
766
818
|
//# sourceMappingURL=interactive.js.map
|