sapper-iq 1.1.34 → 1.1.36

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.
@@ -348,7 +348,7 @@ function getFilesForPicker(dir = '.', prefix = '', maxFiles = 50) {
348
348
  return files.slice(0, maxFiles);
349
349
  }
350
350
 
351
- // Interactive file picker with arrow keys
351
+ // Interactive file picker
352
352
  async function pickFiles() {
353
353
  const files = getFilesForPicker('.', '', 50).filter(f => !f.isDir);
354
354
 
@@ -357,117 +357,56 @@ async function pickFiles() {
357
357
  return [];
358
358
  }
359
359
 
360
- const selected = new Set();
361
- let cursor = 0;
362
- const pageSize = Math.min(15, process.stdout.rows - 10 || 15);
360
+ console.log();
361
+ console.log(box(
362
+ `Select files by number (e.g., ${chalk.cyan('1 3 5')} or ${chalk.cyan('1-5')} or ${chalk.cyan('all')})\n` +
363
+ `Press ${chalk.cyan('Enter')} with no input to cancel`,
364
+ '📎 File Picker', 'cyan'
365
+ ));
366
+ console.log();
363
367
 
364
- // Enable raw mode for key capture
365
- if (process.stdin.isTTY) {
366
- process.stdin.setRawMode(true);
368
+ // Display files in columns
369
+ files.forEach((file, i) => {
370
+ const num = chalk.cyan.bold(`[${(i + 1).toString().padStart(2)}]`);
371
+ const size = file.size ? chalk.gray(`(${Math.round(file.size/1024)}KB)`) : '';
372
+ console.log(` ${num} ${chalk.white(file.path)} ${size}`);
373
+ });
374
+
375
+ console.log();
376
+ const selection = await safeQuestion(chalk.cyan('Select files: '));
377
+
378
+ if (!selection.trim()) {
379
+ console.log(chalk.gray('Cancelled.'));
380
+ return [];
367
381
  }
368
- process.stdin.resume();
369
382
 
370
- const renderList = () => {
371
- // Clear screen and move cursor to top
372
- console.clear();
373
- console.log(box(
374
- `${chalk.cyan('↑↓')} Navigate ${chalk.cyan('Space')} Toggle ${chalk.cyan('a')} All ${chalk.cyan('Enter')} Confirm ${chalk.cyan('q/Esc')} Cancel`,
375
- '📎 Select Files', 'cyan'
376
- ));
377
- console.log();
378
-
379
- // Calculate visible range (pagination)
380
- const startIdx = Math.max(0, Math.min(cursor - Math.floor(pageSize / 2), files.length - pageSize));
381
- const endIdx = Math.min(startIdx + pageSize, files.length);
382
-
383
- // Show scroll indicator if needed
384
- if (startIdx > 0) {
385
- console.log(chalk.gray(' ↑ more files above...'));
386
- }
387
-
388
- for (let i = startIdx; i < endIdx; i++) {
389
- const file = files[i];
390
- const isSelected = selected.has(i);
391
- const isCursor = i === cursor;
392
-
393
- const checkbox = isSelected ? chalk.green('◉') : chalk.gray('○');
394
- const prefix = isCursor ? chalk.cyan('▸ ') : ' ';
395
- const name = isCursor ? chalk.cyan.bold(file.path) : chalk.white(file.path);
396
- const size = file.size ? chalk.gray(` (${Math.round(file.size/1024)}KB)`) : '';
397
-
398
- console.log(`${prefix}${checkbox} ${name}${size}`);
399
- }
400
-
401
- if (endIdx < files.length) {
402
- console.log(chalk.gray(' ↓ more files below...'));
403
- }
404
-
405
- console.log();
406
- console.log(chalk.gray(` Selected: ${selected.size} file${selected.size !== 1 ? 's' : ''}`));
407
- };
383
+ // Parse selection
384
+ const selectedFiles = [];
385
+ const parts = selection.toLowerCase().split(/[\s,]+/);
408
386
 
409
- return new Promise((resolve) => {
410
- renderList();
387
+ for (const part of parts) {
388
+ if (part === 'all') {
389
+ return files.map(f => f.path);
390
+ }
411
391
 
412
- const onKeypress = (chunk, key) => {
413
- if (!key) {
414
- // Handle raw chunk for arrow keys
415
- const str = chunk.toString();
416
- if (str === '\x1b[A') key = { name: 'up' };
417
- else if (str === '\x1b[B') key = { name: 'down' };
418
- else if (str === '\x1b[C') key = { name: 'right' };
419
- else if (str === '\x1b[D') key = { name: 'left' };
420
- else if (str === ' ') key = { name: 'space' };
421
- else if (str === '\r' || str === '\n') key = { name: 'return' };
422
- else if (str === '\x1b' || str === 'q') key = { name: 'escape' };
423
- else if (str === 'a' || str === 'A') key = { name: 'a' };
424
- else if (str === '\x03') key = { name: 'c', ctrl: true }; // Ctrl+C
392
+ // Handle ranges like "1-5"
393
+ const rangeMatch = part.match(/^(\d+)-(\d+)$/);
394
+ if (rangeMatch) {
395
+ const start = parseInt(rangeMatch[1]);
396
+ const end = parseInt(rangeMatch[2]);
397
+ for (let i = start; i <= end && i <= files.length; i++) {
398
+ if (i >= 1) selectedFiles.push(files[i - 1].path);
425
399
  }
426
-
427
- if (!key) return;
428
-
429
- if (key.name === 'up' || key.name === 'k') {
430
- cursor = cursor > 0 ? cursor - 1 : files.length - 1;
431
- renderList();
432
- } else if (key.name === 'down' || key.name === 'j') {
433
- cursor = cursor < files.length - 1 ? cursor + 1 : 0;
434
- renderList();
435
- } else if (key.name === 'space' || key.name === 'right') {
436
- if (selected.has(cursor)) {
437
- selected.delete(cursor);
438
- } else {
439
- selected.add(cursor);
440
- }
441
- renderList();
442
- } else if (key.name === 'a') {
443
- // Toggle all
444
- if (selected.size === files.length) {
445
- selected.clear();
446
- } else {
447
- for (let i = 0; i < files.length; i++) selected.add(i);
448
- }
449
- renderList();
450
- } else if (key.name === 'return') {
451
- cleanup();
452
- const selectedFiles = Array.from(selected).map(i => files[i].path);
453
- console.log(chalk.green(`\n✓ Selected ${selectedFiles.length} file${selectedFiles.length !== 1 ? 's' : ''}`));
454
- resolve(selectedFiles);
455
- } else if (key.name === 'escape' || key.name === 'q' || (key.ctrl && key.name === 'c')) {
456
- cleanup();
457
- console.log(chalk.gray('\nCancelled.'));
458
- resolve([]);
459
- }
460
- };
461
-
462
- const cleanup = () => {
463
- process.stdin.removeListener('data', onKeypress);
464
- if (process.stdin.isTTY) {
465
- process.stdin.setRawMode(false);
400
+ } else {
401
+ // Single number
402
+ const num = parseInt(part);
403
+ if (num >= 1 && num <= files.length) {
404
+ selectedFiles.push(files[num - 1].path);
466
405
  }
467
- };
468
-
469
- process.stdin.on('data', onKeypress);
470
- });
406
+ }
407
+ }
408
+
409
+ return [...new Set(selectedFiles)]; // Remove duplicates
471
410
  }
472
411
 
473
412
  // Format scan results for AI context