canvaslms-cli 1.6.0 → 1.6.3

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