a new start
This commit is contained in:
414
lib/custom-lists.js
Normal file
414
lib/custom-lists.js
Normal file
@@ -0,0 +1,414 @@
|
||||
const inquirer = require('inquirer');
|
||||
const chalk = require('chalk');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const { purpleGold, goldPink, sectionHeader } = require('./banner');
|
||||
const { getDb } = require('./database');
|
||||
|
||||
// Manage custom lists
|
||||
async function manage() {
|
||||
console.clear();
|
||||
sectionHeader('MY CUSTOM LISTS', '📝');
|
||||
|
||||
const db = getDb();
|
||||
const lists = db.prepare('SELECT * FROM custom_lists ORDER BY updated_at DESC').all();
|
||||
|
||||
if (lists.length === 0) {
|
||||
console.log(chalk.yellow(' No custom lists yet. Create your first awesome list!\n'));
|
||||
} else {
|
||||
console.log(chalk.hex('#FFD700')(` ${lists.length} custom lists\n`));
|
||||
|
||||
lists.forEach((list, idx) => {
|
||||
const items = db.prepare('SELECT COUNT(*) as count FROM custom_list_items WHERE custom_list_id = ?').get(list.id);
|
||||
console.log(` ${chalk.gray((idx + 1) + '.')} ${list.icon} ${chalk.hex('#FF69B4')(list.title)} ${chalk.gray(`(${items.count} items)`)}`);
|
||||
if (list.description) {
|
||||
console.log(` ${chalk.gray(list.description)}`);
|
||||
}
|
||||
});
|
||||
console.log();
|
||||
}
|
||||
|
||||
const { action } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'action',
|
||||
message: 'What would you like to do?',
|
||||
choices: [
|
||||
{ name: chalk.hex('#DA22FF')('➕ Create new list'), value: 'create' },
|
||||
...(lists.length > 0 ? [
|
||||
{ name: chalk.hex('#FF69B4')('📋 View/Edit list'), value: 'view' },
|
||||
{ name: chalk.hex('#FFD700')('💾 Export list'), value: 'export' }
|
||||
] : []),
|
||||
{ name: chalk.gray('← Back'), value: 'back' }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
switch (action) {
|
||||
case 'create':
|
||||
await createList();
|
||||
await manage();
|
||||
break;
|
||||
|
||||
case 'view':
|
||||
const { selectedList } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'selectedList',
|
||||
message: 'Select a list:',
|
||||
choices: lists.map(list => ({
|
||||
name: `${list.icon} ${list.title}`,
|
||||
value: list
|
||||
}))
|
||||
}
|
||||
]);
|
||||
await viewList(selectedList);
|
||||
await manage();
|
||||
break;
|
||||
|
||||
case 'export':
|
||||
const { listToExport } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'listToExport',
|
||||
message: 'Select a list to export:',
|
||||
choices: lists.map(list => ({
|
||||
name: `${list.icon} ${list.title}`,
|
||||
value: list
|
||||
}))
|
||||
}
|
||||
]);
|
||||
await exportList(listToExport);
|
||||
await manage();
|
||||
break;
|
||||
|
||||
case 'back':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new custom list
|
||||
async function createList() {
|
||||
const { title, description, author, icon, badgeStyle, badgeColor } = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'title',
|
||||
message: 'List title:',
|
||||
validate: input => input.trim() ? true : 'Title is required'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'description',
|
||||
message: 'Description:',
|
||||
default: ''
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'author',
|
||||
message: 'Author name:',
|
||||
default: os.userInfo().username
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'icon',
|
||||
message: 'Icon (emoji):',
|
||||
default: '📚'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'badgeStyle',
|
||||
message: 'Badge style:',
|
||||
choices: [
|
||||
{ name: 'Flat', value: 'flat' },
|
||||
{ name: 'Flat Square', value: 'flat-square' },
|
||||
{ name: 'Plastic', value: 'plastic' },
|
||||
{ name: 'For the Badge', value: 'for-the-badge' }
|
||||
],
|
||||
default: 'flat'
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'badgeColor',
|
||||
message: 'Badge color:',
|
||||
choices: [
|
||||
{ name: 'Purple', value: 'blueviolet' },
|
||||
{ name: 'Pink', value: 'ff69b4' },
|
||||
{ name: 'Gold', value: 'FFD700' },
|
||||
{ name: 'Blue', value: 'informational' },
|
||||
{ name: 'Green', value: 'success' }
|
||||
],
|
||||
default: 'blueviolet'
|
||||
}
|
||||
]);
|
||||
|
||||
const db = getDb();
|
||||
const stmt = db.prepare(`
|
||||
INSERT INTO custom_lists (title, description, author, icon, badge_style, badge_color)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
stmt.run(title, description, author, icon, badgeStyle, badgeColor);
|
||||
console.log(chalk.green('\n ✓ Custom list created!\n'));
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
// View/edit custom list
|
||||
async function viewList(list) {
|
||||
const db = getDb();
|
||||
const items = db.prepare(`
|
||||
SELECT cli.*, r.name, r.url, r.description, r.stars
|
||||
FROM custom_list_items cli
|
||||
JOIN repositories r ON r.id = cli.repository_id
|
||||
WHERE cli.custom_list_id = ?
|
||||
ORDER BY cli.order_index, cli.added_at
|
||||
`).all(list.id);
|
||||
|
||||
console.clear();
|
||||
console.log(purpleGold(`\n${list.icon} ${list.title}\n`));
|
||||
console.log(chalk.gray('━'.repeat(70)));
|
||||
if (list.description) console.log(chalk.gray(list.description));
|
||||
if (list.author) console.log(chalk.gray(`By ${list.author}`));
|
||||
console.log(chalk.gray('━'.repeat(70)));
|
||||
console.log(chalk.hex('#FFD700')(`\n ${items.length} items\n`));
|
||||
|
||||
if (items.length > 0) {
|
||||
items.forEach((item, idx) => {
|
||||
console.log(` ${chalk.gray((idx + 1) + '.')} ${chalk.hex('#FF69B4')(item.name)} ${chalk.gray(`(⭐ ${item.stars || 0})`)}`);
|
||||
if (item.notes) {
|
||||
console.log(` ${chalk.gray(item.notes)}`);
|
||||
}
|
||||
});
|
||||
console.log();
|
||||
}
|
||||
|
||||
const { action } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'action',
|
||||
message: 'What would you like to do?',
|
||||
choices: [
|
||||
{ name: chalk.hex('#DA22FF')('➕ Add item'), value: 'add' },
|
||||
...(items.length > 0 ? [{ name: chalk.hex('#FF69B4')('🗑️ Remove item'), value: 'remove' }] : []),
|
||||
{ name: chalk.hex('#FFD700')('💾 Export'), value: 'export' },
|
||||
{ name: chalk.hex('#9733EE')('🗑️ Delete list'), value: 'delete' },
|
||||
{ name: chalk.gray('← Back'), value: 'back' }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
switch (action) {
|
||||
case 'add':
|
||||
await addToList(list.id);
|
||||
await viewList(list);
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
const { itemToRemove } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'itemToRemove',
|
||||
message: 'Select item to remove:',
|
||||
choices: items.map(item => ({
|
||||
name: item.name,
|
||||
value: item
|
||||
}))
|
||||
}
|
||||
]);
|
||||
|
||||
const removeStmt = db.prepare('DELETE FROM custom_list_items WHERE id = ?');
|
||||
removeStmt.run(itemToRemove.id);
|
||||
console.log(chalk.green('\n ✓ Item removed\n'));
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
await viewList(list);
|
||||
break;
|
||||
|
||||
case 'export':
|
||||
await exportList(list);
|
||||
await viewList(list);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
const { confirmDelete } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirmDelete',
|
||||
message: 'Delete this list?',
|
||||
default: false
|
||||
}
|
||||
]);
|
||||
|
||||
if (confirmDelete) {
|
||||
const deleteStmt = db.prepare('DELETE FROM custom_lists WHERE id = ?');
|
||||
deleteStmt.run(list.id);
|
||||
console.log(chalk.green('\n ✓ List deleted\n'));
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'back':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add repository to custom list
|
||||
async function addToList(customListId) {
|
||||
const dbOps = require('./db-operations');
|
||||
const bookmarks = dbOps.getBookmarks();
|
||||
|
||||
if (bookmarks.length === 0) {
|
||||
console.log(chalk.yellow('\n No bookmarks to add. Bookmark some repositories first!\n'));
|
||||
await inquirer.prompt([{ type: 'input', name: 'continue', message: 'Press Enter...' }]);
|
||||
return;
|
||||
}
|
||||
|
||||
const { selectedRepo } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'selectedRepo',
|
||||
message: 'Select a repository:',
|
||||
choices: bookmarks.map(bookmark => ({
|
||||
name: `${bookmark.name} ${chalk.gray(`(⭐ ${bookmark.stars || 0})`)}`,
|
||||
value: bookmark
|
||||
})),
|
||||
pageSize: 15
|
||||
}
|
||||
]);
|
||||
|
||||
const { notes } = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'notes',
|
||||
message: 'Notes (optional):',
|
||||
default: ''
|
||||
}
|
||||
]);
|
||||
|
||||
const db = getDb();
|
||||
const stmt = db.prepare(`
|
||||
INSERT OR REPLACE INTO custom_list_items (custom_list_id, repository_id, notes)
|
||||
VALUES (?, ?, ?)
|
||||
`);
|
||||
|
||||
stmt.run(customListId, selectedRepo.repository_id, notes);
|
||||
console.log(chalk.green('\n ✓ Added to list!\n'));
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
// Export custom list
|
||||
async function exportList(list) {
|
||||
const { format } = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'format',
|
||||
message: 'Export format:',
|
||||
choices: [
|
||||
{ name: 'Markdown (.md)', value: 'markdown' },
|
||||
{ name: 'JSON (.json)', value: 'json' },
|
||||
{ name: chalk.gray('PDF (not yet implemented)'), value: 'pdf', disabled: true },
|
||||
{ name: chalk.gray('EPUB (not yet implemented)'), value: 'epub', disabled: true }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const db = getDb();
|
||||
const items = db.prepare(`
|
||||
SELECT cli.*, r.name, r.url, r.description, r.stars, r.language
|
||||
FROM custom_list_items cli
|
||||
JOIN repositories r ON r.id = cli.repository_id
|
||||
WHERE cli.custom_list_id = ?
|
||||
ORDER BY cli.order_index, cli.added_at
|
||||
`).all(list.id);
|
||||
|
||||
const defaultPath = path.join(os.homedir(), 'Downloads', `${list.title.replace(/[^a-z0-9]/gi, '-').toLowerCase()}.${format === 'markdown' ? 'md' : format}`);
|
||||
|
||||
const { outputPath } = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'outputPath',
|
||||
message: 'Output path:',
|
||||
default: defaultPath
|
||||
}
|
||||
]);
|
||||
|
||||
if (format === 'markdown') {
|
||||
await exportMarkdown(list, items, outputPath);
|
||||
} else if (format === 'json') {
|
||||
await exportJSON(list, items, outputPath);
|
||||
}
|
||||
|
||||
console.log(chalk.green(`\n ✓ Exported to: ${outputPath}\n`));
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
}
|
||||
|
||||
// Export as Markdown
|
||||
async function exportMarkdown(list, items, outputPath) {
|
||||
const badgeUrl = `https://img.shields.io/badge/awesome-list-${list.badge_color}?style=${list.badge_style}`;
|
||||
|
||||
let content = `# ${list.icon} ${list.title}\n\n`;
|
||||
content += `\n\n`;
|
||||
|
||||
if (list.description) {
|
||||
content += `> ${list.description}\n\n`;
|
||||
}
|
||||
|
||||
if (list.author) {
|
||||
content += `**Author:** ${list.author}\n\n`;
|
||||
}
|
||||
|
||||
content += `## Contents\n\n`;
|
||||
|
||||
items.forEach(item => {
|
||||
content += `### [${item.name}](${item.url})`;
|
||||
if (item.stars) {
|
||||
content += ` `;
|
||||
}
|
||||
if (item.language) {
|
||||
content += ` `;
|
||||
}
|
||||
content += `\n\n`;
|
||||
|
||||
if (item.description) {
|
||||
content += `${item.description}\n\n`;
|
||||
}
|
||||
|
||||
if (item.notes) {
|
||||
content += `*${item.notes}*\n\n`;
|
||||
}
|
||||
});
|
||||
|
||||
content += `\n---\n\n`;
|
||||
content += `*Generated with [Awesome CLI](https://github.com/yourusername/awesome) 💜*\n`;
|
||||
|
||||
fs.writeFileSync(outputPath, content, 'utf8');
|
||||
}
|
||||
|
||||
// Export as JSON
|
||||
async function exportJSON(list, items, outputPath) {
|
||||
const data = {
|
||||
title: list.title,
|
||||
description: list.description,
|
||||
author: list.author,
|
||||
icon: list.icon,
|
||||
createdAt: list.created_at,
|
||||
updatedAt: list.updated_at,
|
||||
items: items.map(item => ({
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
description: item.description,
|
||||
stars: item.stars,
|
||||
language: item.language,
|
||||
notes: item.notes
|
||||
}))
|
||||
};
|
||||
|
||||
fs.writeFileSync(outputPath, JSON.stringify(data, null, 2), 'utf8');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
manage,
|
||||
createList,
|
||||
viewList,
|
||||
addToList,
|
||||
exportList
|
||||
};
|
||||
Reference in New Issue
Block a user