UNPKG

qraft

Version:

A powerful CLI tool to qraft structured project setups from GitHub template repositories

286 lines • 12 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InteractiveBoxSelector = void 0; const chalk_1 = __importDefault(require("chalk")); const inquirer_1 = __importDefault(require("inquirer")); const prompts_1 = require("./prompts"); /** * Interactive box selector with search, filtering, and preview capabilities */ class InteractiveBoxSelector { constructor(boxManager) { this.boxManager = boxManager; this.prompts = new prompts_1.InteractivePrompts(); } /** * Interactive box selection with full features * @param registryName Optional specific registry to search * @returns Promise<{box: BoxInfo, registry: string} | null> Selected box and registry or null if cancelled */ async selectBox(registryName) { try { // Step 1: Select registry if not specified let selectedRegistry = registryName; if (!selectedRegistry) { const registries = await this.boxManager.listRegistries(); if (registries.length === 0) { console.log(chalk_1.default.red('āŒ No registries configured.')); console.log(chalk_1.default.gray('Add a registry first:')); console.log(chalk_1.default.cyan(' qraft config add-registry <name> <repository>')); return null; } if (registries.length === 1) { selectedRegistry = registries[0].name; console.log(chalk_1.default.gray(`Using registry: ${chalk_1.default.cyan(selectedRegistry)}`)); } else { const registrySelection = await this.prompts.selectRegistry(registries); if (!registrySelection) { return null; // User cancelled } selectedRegistry = registrySelection; } } // Step 2: Load boxes from selected registry console.log(chalk_1.default.blue(`\nšŸ“¦ Loading boxes from ${chalk_1.default.cyan(selectedRegistry)}...`)); let boxes; try { const boxList = await this.boxManager.listBoxes(selectedRegistry); boxes = await Promise.all(boxList.map(async (boxSummary) => { const boxRef = await this.boxManager.parseBoxReference(boxSummary.name, selectedRegistry); const boxInfo = await this.boxManager.getBoxInfo(boxRef); return boxInfo; })); } catch (error) { console.error(chalk_1.default.red('āŒ Failed to load boxes:'), error instanceof Error ? error.message : 'Unknown error'); return null; } if (boxes.length === 0) { console.log(chalk_1.default.yellow('No boxes found in this registry.')); return null; } // Step 3: Interactive selection with search and preview return await this.interactiveSelection(boxes, selectedRegistry); } catch (error) { console.error(chalk_1.default.red('āŒ Error during box selection:'), error instanceof Error ? error.message : 'Unknown error'); return null; } } /** * Interactive selection with search, filter, and preview * @param boxes Available boxes * @param registryName Registry name * @returns Promise<{box: BoxInfo, registry: string} | null> Selected box or null */ async interactiveSelection(boxes, registryName) { let filteredBoxes = boxes; let searchQuery = ''; while (true) { // Show current search status if (searchQuery) { console.log(chalk_1.default.gray(`\nFiltered by: "${searchQuery}" (${filteredBoxes.length}/${boxes.length} boxes)`)); } else { console.log(chalk_1.default.gray(`\nShowing all boxes (${boxes.length} total)`)); } // Create menu choices const choices = []; // Search option choices.push({ name: searchQuery ? `${chalk_1.default.blue('šŸ” Search again')} ${chalk_1.default.gray(`(current: "${searchQuery}")`)}` : chalk_1.default.blue('šŸ” Search boxes'), value: 'search', short: 'Search' }); // Clear search if active if (searchQuery) { choices.push({ name: chalk_1.default.yellow('šŸ—‘ļø Clear search'), value: 'clear-search', short: 'Clear search' }); } // Separator if (filteredBoxes.length > 0) { choices.push(new inquirer_1.default.Separator(chalk_1.default.gray('─ Available Boxes ─'))); // Box choices filteredBoxes.forEach(box => { const tags = box.manifest.tags ? ` ${chalk_1.default.gray(`[${box.manifest.tags.join(', ')}]`)}` : ''; choices.push({ name: `${chalk_1.default.cyan(box.manifest.name)} ${chalk_1.default.gray(`(${box.manifest.version})`)}${tags}\n ${chalk_1.default.gray(box.manifest.description)}`, value: { type: 'box', box }, short: box.manifest.name }); }); } else { choices.push({ name: chalk_1.default.gray('No boxes match your search'), value: 'no-results', disabled: true }); } // Separator and actions choices.push(new inquirer_1.default.Separator()); choices.push({ name: chalk_1.default.gray('Cancel'), value: 'cancel', short: 'Cancel' }); // Show selection prompt const { selection } = await inquirer_1.default.prompt([ { type: 'list', name: 'selection', message: `Select a box from ${chalk_1.default.cyan(registryName)}:`, choices, pageSize: 15 } ]); // Handle selection if (selection === 'cancel') { return null; } if (selection === 'search') { searchQuery = await this.prompts.promptSearch('Enter search terms (name, description, tags):'); filteredBoxes = this.filterBoxes(boxes, searchQuery); continue; } if (selection === 'clear-search') { searchQuery = ''; filteredBoxes = boxes; continue; } if (selection === 'no-results') { continue; } if (selection.type === 'box') { // Show preview and confirm const confirmed = await this.previewAndConfirm(selection.box); if (confirmed) { return { box: selection.box, registry: registryName }; } // If not confirmed, continue the loop to show selection again continue; } } } /** * Filter boxes based on search query * @param boxes All available boxes * @param query Search query * @returns Filtered boxes */ filterBoxes(boxes, query) { if (!query.trim()) { return boxes; } const searchTerms = query.toLowerCase().split(' ').filter(term => term.length > 0); return boxes.filter(box => { const searchableText = [ box.manifest.name, box.manifest.description, box.manifest.author || '', ...(box.manifest.tags || []) ].join(' ').toLowerCase(); return searchTerms.every(term => searchableText.includes(term)); }); } /** * Show box preview and get confirmation * @param box Box to preview * @returns Promise<boolean> Whether user confirmed selection */ async previewAndConfirm(box) { // Show preview await this.prompts.previewBox(box); // Get confirmation const choices = [ { name: chalk_1.default.green('āœ… Select this box'), value: true, short: 'Select' }, { name: chalk_1.default.yellow('šŸ‘€ Show file list'), value: 'show-files', short: 'Show files' }, { name: chalk_1.default.gray('← Back to selection'), value: false, short: 'Back' } ]; const { action } = await inquirer_1.default.prompt([ { type: 'list', name: 'action', message: 'What would you like to do?', choices } ]); if (action === 'show-files') { // Show detailed file list console.log(chalk_1.default.blue.bold(`\nšŸ“ Files in ${box.manifest.name}:\n`)); box.files.forEach((file, index) => { console.log(` ${chalk_1.default.gray(`${(index + 1).toString().padStart(3)}.`)} ${file}`); }); console.log(chalk_1.default.gray(`\nTotal: ${box.files.length} files\n`)); // Ask again after showing files return this.prompts.confirm('Select this box?', true); } return action; } /** * Quick box selection without full interactive features * @param registryName Optional registry name * @returns Promise<{box: BoxInfo, registry: string} | null> Selected box or null */ async quickSelect(registryName) { try { // Get registry let selectedRegistry = registryName; if (!selectedRegistry) { const registries = await this.boxManager.listRegistries(); if (registries.length === 1) { selectedRegistry = registries[0].name; } else { const registrySelection = await this.prompts.selectRegistry(registries); if (!registrySelection) return null; selectedRegistry = registrySelection; } } // Get boxes const boxList = await this.boxManager.listBoxes(selectedRegistry); const boxes = await Promise.all(boxList.map(async (boxSummary) => { const boxRef = await this.boxManager.parseBoxReference(boxSummary.name, selectedRegistry); return await this.boxManager.getBoxInfo(boxRef); })); const validBoxes = boxes.filter(box => box !== null); if (validBoxes.length === 0) { console.log(chalk_1.default.yellow('No boxes available.')); return null; } // Simple selection const selectedBox = await this.prompts.selectBox(validBoxes, selectedRegistry); if (!selectedBox) return null; return { box: selectedBox, registry: selectedRegistry }; } catch (error) { console.error(chalk_1.default.red('āŒ Error during box selection:'), error instanceof Error ? error.message : 'Unknown error'); return null; } } } exports.InteractiveBoxSelector = InteractiveBoxSelector; //# sourceMappingURL=boxSelector.js.map