perchance-ai-prompt-library
Version:
🎨 Complete AI prompt library and generator with advanced CLI, batch processing, analytics, and multi-format export capabilities
257 lines (225 loc) • 8.71 kB
JavaScript
const stylesData = require('../data/styles.json');
const { randomChoice, replacePlaceholders, validateConfig } = require('../utils/helpers');
const PhotographyGenerator = require('./PhotographyGenerator');
class PromptGenerator {
constructor(options = {}) {
this.styles = stylesData;
this.photographyGenerator = new PhotographyGenerator();
this.options = {
includeQuality: true,
includeNegativePrompt: true,
randomizeVariables: false,
enablePhotographyMode: false,
...options
};
}
/**
* Get a random style from the available styles
* @param {Array} [filter] - Optional array of style keys to filter by
* @returns {Object} A random style object
*/
getRandomStyle(filter = []) {
let availableStyles = this.styles;
// Filter styles if filter array is provided
if (filter.length > 0) {
availableStyles = this.styles.filter(style => filter.includes(style.key));
if (availableStyles.length === 0) {
availableStyles = this.styles; // Fallback to all styles if filter returns none
}
}
return randomChoice(availableStyles);
}
/**
* Generate a prompt based on the provided configuration
* @param {Object} config - Configuration object
* @param {string} [config.style] - Style key (optional if customPrompt is provided)
* @param {string} [config.customPrompt] - Custom prompt text (optional)
* @param {Object} [config.variables] - Variables to replace in the prompt
* @param {boolean} [config.includeQuality] - Whether to include quality modifiers
* @param {boolean} [config.includeNegativePrompt] - Whether to include negative prompts
* @param {boolean} [config.randomizeVariables] - Whether to randomize variables
* @param {string} [config.randomFromStyles] - Comma-separated list of style keys to randomize from
* @returns {Object} Generated prompt and metadata
*/
generate(config = {}) {
// If customPrompt is provided, use it directly
if (config.customPrompt) {
let promptText = config.customPrompt;
// Apply photography enhancements if in photography mode
if (this.options.enablePhotographyMode && config.photographyOptions) {
promptText = this.enhanceWithPhotographyStandards(promptText, config.photographyOptions);
}
return {
prompt: promptText,
style: 'custom',
variables: {},
negativePrompt: ''
};
}
// If randomFromStyles is provided, select a random style from the specified list
if (config.randomFromStyles) {
const styleKeys = config.randomFromStyles.split(',').map(s => s.trim());
const randomStyle = this.getRandomStyle(styleKeys);
config.style = randomStyle.key;
}
const validation = validateConfig(config);
if (!validation.valid) {
throw new Error(`Invalid configuration: ${validation.errors.join(', ')}`);
}
// Find the style by key (supports both string keys and array indices)
let style;
if (Array.isArray(this.styles)) {
style = this.styles.find(s => s.key === config.style);
} else {
style = this.styles[config.style];
}
// If style not found, use a random style
if (!style) {
style = this.getRandomStyle();
}
if (!style) {
const availableStyles = Array.isArray(this.styles)
? this.styles.map(s => s.key).join(', ')
: Object.keys(this.styles).join(', ');
throw new Error(`Style "${config.style}" not found. Available: ${availableStyles}`);
}
// Ensure style has required properties
if (!style.formula || !style.variables) {
throw new Error(`Invalid style configuration for "${config.style}". Missing required properties.`);
}
// Initialize prompt with the style's formula
let prompt = style.formula;
// Prepare variables for placeholder replacement
const variables = { ...config };
// Handle variables defined in the style
if (Array.isArray(style.variables)) {
// If variables is an array of variable names
style.variables.forEach(varName => {
if (!variables[varName] && style.examples && style.examples[0]) {
// Use the first example as a default value if available
variables[varName] = style.examples[0];
}
});
} else if (typeof style.variables === 'object' && style.variables !== null) {
// If variables is an object with variable names as keys and possible values as arrays
Object.keys(style.variables).forEach(key => {
if (!variables[key] && Array.isArray(style.variables[key]) && style.variables[key].length > 0) {
variables[key] = this.options.randomizeVariables ?
randomChoice(style.variables[key]) :
style.variables[key][0];
}
});
}
// Ensure subject is set
if (config.subject) {
variables.subject = config.subject;
} else if (!variables.subject) {
// If no subject is provided, use a default based on the style
variables.subject = style.examples && style.examples.length > 0
? style.examples[0]
: 'a character';
}
// Replace placeholders in the prompt
prompt = replacePlaceholders(prompt, variables);
// Add quality modifiers if enabled
if (this.options.includeQuality && style.quality_modifiers && Array.isArray(style.quality_modifiers)) {
const qualityMods = style.quality_modifiers.slice(0, 3).join(', ');
prompt += `, ${qualityMods}`;
}
const result = {
text: prompt,
style: style.key || config.style, // Use the style's key or the provided style name
variables: variables,
timestamp: new Date().toISOString(),
metadata: {
wordCount: prompt.split(' ').length,
characterCount: prompt.length
}
};
if (this.options.includeNegativePrompt && style.negative_prompt) {
result.negativePrompt = style.negative_prompt;
}
return result;
}
generateVariations(style, config, count = 3) {
const variations = [];
for (let i = 0; i < count; i++) {
const originalRandomize = this.options.randomizeVariables;
this.options.randomizeVariables = true;
try {
const variation = this.generate({ ...config, style: style });
variation.variationNumber = i + 1;
variations.push(variation);
} catch (error) {
console.warn(`Failed to generate variation ${i + 1}:`, error.message);
}
this.options.randomizeVariables = originalRandomize;
}
return variations;
}
getStats() {
const styleCount = Object.keys(this.styles).length;
let totalVariables = 0;
Object.values(this.styles).forEach(style => {
totalVariables += Object.keys(style.variables).length;
});
return {
totalStyles: styleCount,
totalVariables: totalVariables,
availableStyles: Object.keys(this.styles)
};
}
/**
* Enhance a prompt with professional photography standards
* @param {string} basePrompt - The base prompt text
* @param {Object} options - Photography enhancement options
* @returns {string} Enhanced prompt with photography standards
*/
enhanceWithPhotographyStandards(basePrompt, options = {}) {
const {
style = 'standard',
format = 'landscape',
includeSettings = true,
includeEquipment = false,
termCount = 3,
customTerms = []
} = options;
// Generate photography-specific parts
const photoPrompt = this.photographyGenerator.generatePrompt({
subject: basePrompt,
style,
format,
includeSettings,
includeEquipment,
termCount,
customTerms
});
return photoPrompt;
}
/**
* Enable or disable photography mode
* @param {boolean} enabled - Whether to enable photography mode
*/
setPhotographyMode(enabled = true) {
this.options.enablePhotographyMode = enabled;
return this;
}
/**
* Get a random camera setting
* @param {string} type - Type of setting to get (aperture, shutter_speed, iso, focal_length)
* @returns {string} Random setting value
*/
getRandomCameraSetting(type) {
return this.photographyGenerator.getCameraSetting(type);
}
/**
* Get a random resolution
* @param {string} type - Resolution type (standard, social_media, print)
* @param {string} format - Format (portrait, landscape, square, etc.)
* @returns {string} Random resolution in WxH format
*/
getRandomResolution(type = 'standard', format = 'landscape') {
return this.photographyGenerator.getResolution(type, format);
}
}
module.exports = PromptGenerator;