feat(*.liquid): apply prettier to liquid templates

- offer to create tag when none is found while adding a link from cli
- fix tag display in search
This commit is contained in:
Cory Dransfeldt 2025-06-16 14:40:54 -07:00
parent 49e21d574e
commit efe701f939
No known key found for this signature in database
112 changed files with 1319 additions and 1134 deletions

View file

@ -1,38 +1,38 @@
import inquirer from 'inquirer';
import { loadConfig } from '../config.js';
import { initDirectusClient, searchItems, createItem } from '../directus/client.js';
import inquirer from "inquirer";
import { loadConfig } from "../config.js";
import { initDirectusClient, searchItems, createItem } from "../directus/client.js";
export const addBlockedRobot = async () => {
const config = await loadConfig();
initDirectusClient(config);
const robots = await searchItems('robots', '/');
let rootRobot = robots.find((r) => r.path === '/');
const robots = await searchItems("robots", "/");
let rootRobot = robots.find((r) => r.path === "/");
if (!rootRobot) {
console.log(' No robots entry for `/` found. Creating one...');
console.log(" No robots entry for `/` found. Creating one...");
const newRobot = await createItem('robots', { path: '/' });
const newRobot = await createItem("robots", { path: "/" });
rootRobot = newRobot.data || newRobot;
console.log('✅ Created robots rule for `/`');
console.log("✅ Created robots rule for `/`");
}
const { userAgent } = await inquirer.prompt({
name: 'userAgent',
message: '🤖 Enter the user-agent string to block:',
validate: (input) => !!input || 'User-agent cannot be empty'
name: "userAgent",
message: "🤖 Enter the user-agent string to block:",
validate: (input) => !!input || "User-agent cannot be empty"
});
const createdAgent = await createItem('user_agents', {
const createdAgent = await createItem("user_agents", {
user_agent: userAgent
});
const agentId = createdAgent.data?.id || createdAgent.id;
await createItem('robots_user_agents', {
await createItem("robots_user_agents", {
robots_id: rootRobot.id,
user_agents_id: agentId
});

View file

@ -1,12 +1,12 @@
import inquirer from 'inquirer';
import { loadConfig } from '../config.js';
import inquirer from "inquirer";
import { loadConfig } from "../config.js";
import {
initDirectusClient,
getDirectusClient,
searchItems,
createItem,
updateItem
} from '../directus/client.js';
} from "../directus/client.js";
export const addEpisodeToShow = async () => {
const config = await loadConfig();
@ -15,21 +15,21 @@ export const addEpisodeToShow = async () => {
const directus = getDirectusClient();
const showResults = await inquirer.prompt({
name: 'query',
message: 'Search for a show:'
name: "query",
message: "Search for a show:"
});
const matches = await searchItems('shows', showResults.query);
const matches = await searchItems("shows", showResults.query);
if (!matches.length) {
console.warn('⚠️ No matching shows found.');
console.warn("⚠️ No matching shows found.");
return;
}
const { showId } = await inquirer.prompt({
type: 'list',
name: 'showId',
message: 'Select a show:',
type: "list",
name: "showId",
message: "Select a show:",
choices: matches.map((s) => ({
name: s.title || s.name || s.id,
value: s.id
@ -37,23 +37,23 @@ export const addEpisodeToShow = async () => {
});
const { season_number, episode_number, plays } = await inquirer.prompt([
{
name: 'season_number',
message: 'Season number:',
name: "season_number",
message: "Season number:",
validate: (val) => !isNaN(val)
},
{
name: 'episode_number',
message: 'Episode number:',
name: "episode_number",
message: "Episode number:",
validate: (val) => !isNaN(val)
},
{
name: 'plays',
message: 'Play count:',
name: "plays",
message: "Play count:",
default: 0,
validate: (val) => !isNaN(val)
}
]);
const existing = await searchItems('episodes', `${season_number} ${episode_number}`);
const existing = await searchItems("episodes", `${season_number} ${episode_number}`);
const match = existing.find(
(e) =>
Number(e.season_number) === Number(season_number) &&
@ -63,21 +63,21 @@ export const addEpisodeToShow = async () => {
if (match) {
const { update } = await inquirer.prompt({
type: 'confirm',
name: 'update',
type: "confirm",
name: "update",
message: `Episode exists. Update play count from ${match.plays ?? 0} to ${plays}?`,
default: true
});
if (update) {
await updateItem('episodes', match.id, { plays });
await updateItem("episodes", match.id, { plays });
console.log(`✅ Updated episode: S${season_number}E${episode_number}`);
} else {
console.warn('⚠️ Skipped update.');
console.warn("⚠️ Skipped update.");
}
} else {
await createItem('episodes', {
await createItem("episodes", {
season_number: Number(season_number),
episode_number: Number(episode_number),
plays: Number(plays),

View file

@ -1,7 +1,7 @@
import inquirer from 'inquirer';
import { loadConfig } from '../config.js';
import { initDirectusClient, searchItems, createItem } from '../directus/client.js';
import { removeUrlProtocol } from '../sanitize.js';
import inquirer from "inquirer";
import { loadConfig } from "../config.js";
import { initDirectusClient, searchItems, createItem } from "../directus/client.js";
import { removeUrlProtocol } from "../sanitize.js";
export const addLinkToShare = async () => {
const config = await loadConfig();
@ -10,34 +10,34 @@ export const addLinkToShare = async () => {
const { title, link, description, authorQuery } = await inquirer.prompt([
{
name: 'title',
message: '📝 Title for the link:',
validate: (input) => !!input || 'Title is required'
name: "title",
message: "📝 Title for the link:",
validate: (input) => !!input || "Title is required"
},
{
name: 'link',
message: '🔗 URL to share:',
validate: (input) => input.startsWith('http') || 'Must be a valid URL'
name: "link",
message: "🔗 URL to share:",
validate: (input) => input.startsWith("http") || "Must be a valid URL"
},
{
name: 'description',
message: '🗒 Description (optional):',
default: ''
name: "description",
message: "🗒 Description (optional):",
default: ""
},
{
name: 'authorQuery',
message: '👤 Search for an author:'
name: "authorQuery",
message: "👤 Search for an author:"
}
]);
const authorMatches = await searchItems('authors', authorQuery);
const authorMatches = await searchItems("authors", authorQuery);
let author;
if (!authorMatches.length) {
const { shouldCreate } = await inquirer.prompt({
type: 'confirm',
name: 'shouldCreate',
message: '❌ No authors found. Do you want to create a new one?',
type: "confirm",
name: "shouldCreate",
message: "❌ No authors found. Do you want to create a new one?",
default: true
});
@ -45,19 +45,19 @@ export const addLinkToShare = async () => {
const { name, url, mastodon, rss, json, newsletter, blogroll } = await inquirer.prompt([
{
name: 'name',
message: '👤 Author name:',
validate: (input) => !!input || 'Name is required'
name: "name",
message: "👤 Author name:",
validate: (input) => !!input || "Name is required"
},
{ name: 'url', message: '🔗 URL (optional):', default: '' },
{ name: 'mastodon', message: '🐘 Mastodon handle (optional):', default: '' },
{ name: 'rss', message: '📡 RSS feed (optional):', default: '' },
{ name: 'json', message: '🧾 JSON feed (optional):', default: '' },
{ name: 'newsletter', message: '📰 Newsletter URL (optional):', default: '' },
{ type: 'confirm', name: 'blogroll', message: '📌 Add to blogroll?', default: false }
{ name: "url", message: "🔗 URL (optional):", default: "" },
{ name: "mastodon", message: "🐘 Mastodon handle (optional):", default: "" },
{ name: "rss", message: "📡 RSS feed (optional):", default: "" },
{ name: "json", message: "🧾 JSON feed (optional):", default: "" },
{ name: "newsletter", message: "📰 Newsletter URL (optional):", default: "" },
{ type: "confirm", name: "blogroll", message: "📌 Add to blogroll?", default: false }
]);
const created = await createItem('authors', {
const created = await createItem("authors", {
name,
url,
mastodon,
@ -70,9 +70,9 @@ export const addLinkToShare = async () => {
author = created.data?.id || created.id;
} else {
const response = await inquirer.prompt({
type: 'list',
name: 'author',
message: 'Select an author:',
type: "list",
name: "author",
message: "Select an author:",
choices: authorMatches.map((a) => {
const cleanUrl = removeUrlProtocol(a.url);
const display = cleanUrl ? `${a.name} (${cleanUrl})` : a.name;
@ -91,41 +91,64 @@ export const addLinkToShare = async () => {
while (true) {
const { query } = await inquirer.prompt({
name: 'query',
message: '🏷 Search for tags (or leave blank to finish):'
name: "query",
message: "🏷 Search for tags (or leave blank to finish):"
});
const trimmedQuery = query.trim();
if (!trimmedQuery) break;
const tags = await searchItems('tags', trimmedQuery);
const tags = await searchItems("tags", trimmedQuery);
if (!tags.length) {
console.warn(`⚠️ No tags found matching "${trimmedQuery}"`);
const { shouldCreateTag } = await inquirer.prompt({
type: "confirm",
name: "shouldCreateTag",
message: `Do you want to create a new tag named "${trimmedQuery}"?`,
default: true
});
if (shouldCreateTag) {
const createdTag = await createItem("tags", { name: trimmedQuery });
const newTagId = createdTag.data?.id || createdTag.id;
tagIds.push(newTagId);
}
const { again } = await inquirer.prompt({
type: "confirm",
name: "again",
message: "Search and select more tags?",
default: false
});
if (!again) break;
continue;
}
const { selected } = await inquirer.prompt({
type: 'checkbox',
name: 'selected',
message: '✔ Select tags to add:',
type: "checkbox",
name: "selected",
message: "✔ Select tags to add:",
choices: tags.map((tag) => ({ name: tag.name, value: tag.id }))
});
tagIds.push(...selected);
const { again } = await inquirer.prompt({
type: 'confirm',
name: 'again',
message: 'Search and select more tags?',
type: "confirm",
name: "again",
message: "Search and select more tags?",
default: false
});
if (!again) break;
}
await createItem('links', {
await createItem("links", {
title,
link,
description,
@ -134,5 +157,5 @@ export const addLinkToShare = async () => {
date: new Date().toISOString()
});
console.log('✅ Link created successfully.');
console.log("✅ Link created successfully.");
};

View file

@ -1,16 +1,16 @@
import inquirer from 'inquirer';
import { loadConfig } from '../config.js';
import { initDirectusClient, createItem, searchItems } from '../directus/client.js';
import { promptForMultipleRelations } from '../directus/relationHelpers.js';
import inquirer from "inquirer";
import { loadConfig } from "../config.js";
import { initDirectusClient, createItem, searchItems } from "../directus/client.js";
import { promptForMultipleRelations } from "../directus/relationHelpers.js";
const ASSOCIATED_MEDIA_TYPES = ['artists', 'books', 'movies', 'shows', 'genres'];
const ASSOCIATED_MEDIA_TYPES = ["artists", "books", "movies", "shows", "genres"];
const BLOCK_COLLECTIONS = [
'youtube_player',
'github_banner',
'npm_banner',
'rss_banner',
'calendar_banner',
'forgejo_banner'
"youtube_player",
"github_banner",
"npm_banner",
"rss_banner",
"calendar_banner",
"forgejo_banner"
];
export const addPost = async () => {
@ -20,24 +20,24 @@ export const addPost = async () => {
const { title, description, content, featured } = await inquirer.prompt([
{
name: 'title',
message: '📝 Title:',
validate: (input) => !!input || 'Title is required'
name: "title",
message: "📝 Title:",
validate: (input) => !!input || "Title is required"
},
{
name: 'description',
message: '🗒 Description:',
default: ''
name: "description",
message: "🗒 Description:",
default: ""
},
{
name: 'content',
message: '📄 Content:',
default: ''
name: "content",
message: "📄 Content:",
default: ""
},
{
type: 'confirm',
name: 'featured',
message: '⭐ Featured?',
type: "confirm",
name: "featured",
message: "⭐ Featured?",
default: false
}
]);
@ -46,14 +46,14 @@ export const addPost = async () => {
while (true) {
const { query } = await inquirer.prompt({
name: 'query',
message: '🏷 Search for tags (or leave blank to finish):'
name: "query",
message: "🏷 Search for tags (or leave blank to finish):"
});
const trimmedQuery = query.trim();
if (!trimmedQuery) break;
const tags = await searchItems('tags', trimmedQuery);
const tags = await searchItems("tags", trimmedQuery);
if (!tags.length) {
console.warn(`⚠️ No tags found matching "${trimmedQuery}"`);
@ -62,18 +62,18 @@ export const addPost = async () => {
}
const { selected } = await inquirer.prompt({
type: 'checkbox',
name: 'selected',
message: '✔ Select tags to add:',
type: "checkbox",
name: "selected",
message: "✔ Select tags to add:",
choices: tags.map((tag) => ({ name: tag.name, value: tag.id }))
});
tagIds.push(...selected);
const { again } = await inquirer.prompt({
type: 'confirm',
name: 'again',
message: 'Search and select more tags?',
type: "confirm",
name: "again",
message: "Search and select more tags?",
default: false
});
@ -82,25 +82,25 @@ export const addPost = async () => {
const selectedBlocks = [];
const { includeBlocks } = await inquirer.prompt({
type: 'confirm',
name: 'includeBlocks',
message: ' Add blocks?',
type: "confirm",
name: "includeBlocks",
message: " Add blocks?",
default: false
});
if (includeBlocks) {
while (true) {
const { collection } = await inquirer.prompt({
type: 'list',
name: 'collection',
message: '🧱 Choose a block collection (or Cancel to finish):',
choices: [...BLOCK_COLLECTIONS, new inquirer.Separator(), 'Cancel']
type: "list",
name: "collection",
message: "🧱 Choose a block collection (or Cancel to finish):",
choices: [...BLOCK_COLLECTIONS, new inquirer.Separator(), "Cancel"]
});
if (collection === 'Cancel') break;
if (collection === "Cancel") break;
const { query } = await inquirer.prompt({
name: 'query',
name: "query",
message: `🔍 Search ${collection}:`
});
const results = await searchItems(collection, query);
@ -112,8 +112,8 @@ export const addPost = async () => {
}
const { itemId } = await inquirer.prompt({
type: 'list',
name: 'itemId',
type: "list",
name: "itemId",
message: `Select an item from ${collection}:`,
choices: results.map((item) => ({
name: item.title || item.name || item.id,
@ -124,9 +124,9 @@ export const addPost = async () => {
selectedBlocks.push({ collection, item: itemId });
const { again } = await inquirer.prompt({
type: 'confirm',
name: 'again',
message: ' Add another block?',
type: "confirm",
name: "again",
message: " Add another block?",
default: false
});
@ -136,16 +136,16 @@ export const addPost = async () => {
const associatedMediaPayload = {};
const { includeMedia } = await inquirer.prompt({
type: 'confirm',
name: 'includeMedia',
message: ' Add associated media?',
type: "confirm",
name: "includeMedia",
message: " Add associated media?",
default: false
});
if (includeMedia) {
for (const mediaType of ASSOCIATED_MEDIA_TYPES) {
const { query } = await inquirer.prompt({
name: 'query',
name: "query",
message: `🔎 Search for ${mediaType} to associate (or leave blank to skip):`
});
@ -160,8 +160,8 @@ export const addPost = async () => {
}
const { selected } = await inquirer.prompt({
type: 'checkbox',
name: 'selected',
type: "checkbox",
name: "selected",
message: `✔ Select ${mediaType} to associate:`,
choices: matches.map((m) => ({
name: m.name_string || m.title || m.name || m.label || m.id,
@ -176,7 +176,7 @@ export const addPost = async () => {
}
}
const media = await promptForMultipleRelations('media', 'Associated media');
const media = await promptForMultipleRelations("media", "Associated media");
const payload = {
title,
description,
@ -188,7 +188,7 @@ export const addPost = async () => {
...associatedMediaPayload
};
await createItem('posts', payload);
await createItem("posts", payload);
console.log('✅ Post created successfully.');
console.log("✅ Post created successfully.");
};

View file

@ -1,24 +1,24 @@
import inquirer from 'inquirer';
import { addPost } from './addPost.js';
import { addLinkToShare } from './addLinkToShare.js';
import { addEpisodeToShow } from './addEpisodeToShow.js';
import { updateReadingProgress } from './updateReadingProgress.js';
import { addBlockedRobot } from './addBlockedRobot.js';
import inquirer from "inquirer";
import { addPost } from "./addPost.js";
import { addLinkToShare } from "./addLinkToShare.js";
import { addEpisodeToShow } from "./addEpisodeToShow.js";
import { updateReadingProgress } from "./updateReadingProgress.js";
import { addBlockedRobot } from "./addBlockedRobot.js";
const TASKS = [
{ name: '📄 Add post', handler: addPost },
{ name: '🔗 Add link to share', handler: addLinkToShare },
{ name: ' Add episode to show', handler: addEpisodeToShow },
{ name: '📚 Update reading progress', handler: updateReadingProgress },
{ name: '🤖 Block robot', handler: addBlockedRobot }
{ name: "📄 Add post", handler: addPost },
{ name: "🔗 Add link to share", handler: addLinkToShare },
{ name: " Add episode to show", handler: addEpisodeToShow },
{ name: "📚 Update reading progress", handler: updateReadingProgress },
{ name: "🤖 Block robot", handler: addBlockedRobot }
];
export const runTasksMenu = async () => {
const { task } = await inquirer.prompt([
{
type: 'list',
name: 'task',
message: 'Select a task to perform:',
type: "list",
name: "task",
message: "Select a task to perform:",
choices: TASKS.map((t) => ({ name: t.name, value: t.handler }))
}
]);

View file

@ -1,13 +1,13 @@
import inquirer from 'inquirer';
import { loadConfig } from '../config.js';
import { initDirectusClient, searchItems, updateItem } from '../directus/client.js';
import inquirer from "inquirer";
import { loadConfig } from "../config.js";
import { initDirectusClient, searchItems, updateItem } from "../directus/client.js";
export const updateReadingProgress = async () => {
const config = await loadConfig();
initDirectusClient(config);
const readingBooks = await searchItems('books', '', { read_status: 'started' });
const readingBooks = await searchItems("books", "", { read_status: "started" });
if (!readingBooks.length) {
console.log('📖 No books currently marked as "started".');
@ -16,9 +16,9 @@ export const updateReadingProgress = async () => {
}
const { bookId } = await inquirer.prompt({
type: 'list',
name: 'bookId',
message: '📚 Select a book to update progress:',
type: "list",
name: "bookId",
message: "📚 Select a book to update progress:",
choices: readingBooks.map((book) => {
const title = book.title || book.name || `Book #${book.id}`;
const progress = book.progress ?? 0;
@ -30,16 +30,16 @@ export const updateReadingProgress = async () => {
})
});
const { progress } = await inquirer.prompt({
name: 'progress',
message: '📕 New progress percentage (0100):',
name: "progress",
message: "📕 New progress percentage (0100):",
validate: (input) => {
const num = Number(input);
return (!isNaN(num) && num >= 0 && num <= 100) || 'Enter a number from 0 to 100';
return (!isNaN(num) && num >= 0 && num <= 100) || "Enter a number from 0 to 100";
}
});
await updateItem('books', bookId, { progress: Number(progress) });
await updateItem("books", bookId, { progress: Number(progress) });
console.log(`✅ Updated book progress to ${progress}%`);
};