import express from 'express'; import cors from 'cors'; import dotenv from 'dotenv'; import { HfInference } from '@huggingface/inference'; import fs from 'fs'; import path from 'path'; import { createClient } from '@supabase/supabase-js'; import ws from 'ws'; import { fileURLToPath } from 'url'; import { ZipArchive } from 'archiver'; // Fix for Node.js < 22: inject ws as global WebSocket for Supabase Realtime if (!globalThis.WebSocket) { globalThis.WebSocket = ws; } // Load environment variables dotenv.config(); const app = express(); const PORT = process.env.PORT || 5000; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const publicDir = path.join(__dirname, 'public'); const productsDir = path.join(publicDir, 'products'); const coversDir = path.join(publicDir, 'covers'); const packagesDir = path.join(publicDir, 'packages'); if (!fs.existsSync(publicDir)) fs.mkdirSync(publicDir, { recursive: true }); if (!fs.existsSync(productsDir)) fs.mkdirSync(productsDir, { recursive: true }); if (!fs.existsSync(coversDir)) fs.mkdirSync(coversDir, { recursive: true }); if (!fs.existsSync(packagesDir)) fs.mkdirSync(packagesDir, { recursive: true }); // Enable CORS so the React frontend on 5173 can talk to our API on 5000 app.use(cors()); app.use(express.json()); app.use('/static', express.static(publicDir)); // ---------------------------------------------------- // DATABASE CONFIGURATION: SUPABASE WITH JSON FALLBACK // ---------------------------------------------------- let isSupabaseConnected = false; let supabase = null; // Attempt Supabase Connection if credentials are provided if (process.env.SUPABASE_URL && process.env.SUPABASE_KEY && !process.env.SUPABASE_URL.includes('your-project-id')) { console.log('đ Connecting to Supabase Cloud Database...'); try { supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY, { realtime: { transport: ws } }); isSupabaseConnected = true; console.log('â Connected to Supabase Cloud Database!'); } catch (err) { console.error('â Supabase Connection Error:', err.message); console.log('â ī¸ Falling back to Local JSON Database.'); } } else { console.log('âšī¸ Supabase credentials not set or using template. Using Local JSON Database.'); } // JSON File Database Fallback Configuration const DB_FILE = path.join(process.cwd(), 'db.json'); const readDb = () => { try { if (!fs.existsSync(DB_FILE)) { const defaultCatalog = [ { id: '1', title: 'ADHD Daily Focus Study Planner 2026', subtitle: 'The Ultimate Aesthetic Organization Notebook for Neurodivergent College Students', type: 'planner', price: 9.99, date: new Date().toLocaleDateString() }, { id: '2', title: 'AWS Certified Solutions Architect Study Guide SAA-C03', subtitle: 'The 30-Day Cram Sheet & High-Performance Practice Exam Bank', type: 'study_guide', price: 14.99, date: new Date().toLocaleDateString() } ]; fs.writeFileSync(DB_FILE, JSON.stringify(defaultCatalog, null, 2)); return defaultCatalog; } return JSON.parse(fs.readFileSync(DB_FILE, 'utf8')); } catch (err) { return []; } }; const writeDb = (data) => { fs.writeFileSync(DB_FILE, JSON.stringify(data, null, 2)); }; // Initialize Hugging Face Inference (used ONLY for SDXL image generation) const hf = new HfInference(process.env.HF_API_KEY || ''); // ---------------------------------------------------- // GROQ LLAMA 3.3 70B â Primary Text Generation Engine // ---------------------------------------------------- const groqChat = async (prompt, maxTokens = 1200) => { if (!process.env.GROQ_API_KEY) throw new Error('GROQ_API_KEY not configured in .env'); const res = await fetch('https://api.groq.com/openai/v1/chat/completions', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'llama-3.3-70b-versatile', messages: [{ role: 'user', content: prompt }], max_tokens: maxTokens, temperature: 0.8 }) }); const data = await res.json(); if (!res.ok) throw new Error(data.error?.message || 'Groq API error'); return data.choices[0].message.content; }; const groqJsonChat = async (prompt, maxTokens = 3800) => { if (!process.env.GROQ_API_KEY) throw new Error('GROQ_API_KEY not configured in .env'); const res = await fetch('https://api.groq.com/openai/v1/chat/completions', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'llama-3.3-70b-versatile', messages: [ { role: 'system', content: 'You are an elite expert digital product creator and author. You must respond ONLY with a valid JSON object matching the requested schema.' }, { role: 'user', content: prompt } ], response_format: { type: 'json_object' }, max_tokens: maxTokens, temperature: 0.8 }) }); const data = await res.json(); if (!res.ok) throw new Error(data.error?.message || 'Groq API error'); return JSON.parse(data.choices[0].message.content); }; const humanizeBookContent = async (content) => { if (!content) return content; console.log(`[HUMANIZER] Initiating 100% human-like refinement pass...`); const prompt = `You are an elite expert developmental editor and premium humanizer. Your absolute goal is to make this book/guide draft read exactly as if it was written by an incredibly intelligent, expert human author. CRITICAL DIRECTIVES: 1. PRESERVE THE LAYOUT EXACTLY: Do not alter any Markdown headers (#, ##, ###), Table of Contents, code blocks, or structured chapter key point summaries (â). Keep the exact same format. 2. 100% HUMANIZED STYLE: Rewrite the content to flow naturally, using conversational yet highly authoritative, warm, and precise easy-to-read English. Prefer short active sentences and a direct tone. 3. REMOVE ALL AI TRADEMARK FLUFF: Eliminate ALL AI-like introductory filler, generic summaries, and hallmark transition phrases (e.g. "Let's dive in", "first and foremost", "it is important to remember", "a testament to", "unlock your potential", "delve into", "in today's digital era"). 4. ZERO REPETITION: Actively prune any redundant paragraphs, circular explanations, or repetitive AI patterns. 5. MAXIMIZE PRACTICAL VALUE: Make all descriptions direct, highly practical, and pack them with clear real-world examples. Do NOT summarize, truncate, or cut the length. Ensure the resulting book remains fully detailed, comprehensive, and complete. Here is the draft book content to humanize: --- ${content} --- Respond with ONLY the polished, 100% humanized markdown content. Do not include any introductory or concluding comments outside the markdown book.`; return await groqChat(prompt, 6500); }; const parseResponseFields = (text, defaultNiche = 'Digital Guide', defaultCta = 'Get instant access') => { const fields = { title: '', subtitle: '', description: '', tags: '', cta: '', thumbnailPrompt: '', productContent: '' }; if (!text) return fields; const keys = [ { key: 'title', name: 'TITLE', next: 'SUBTITLE' }, { key: 'subtitle', name: 'SUBTITLE', next: 'DESCRIPTION' }, { key: 'description', name: 'DESCRIPTION', next: 'TAGS' }, { key: 'tags', name: 'TAGS', next: 'CTA' }, { key: 'cta', name: 'CTA', next: 'THUMBNAIL_PROMPT' }, { key: 'thumbnailPrompt', name: 'THUMBNAIL_PROMPT', next: 'PRODUCT_CONTENT' }, { key: 'productContent', name: 'PRODUCT_CONTENT', next: 'ZZEND' } ]; // 1. Try strict matching with brackets first let hasBrackets = false; for (const k of keys) { const r = new RegExp(`\\[${k.name}\\]\\n?([\\s\\S]*?)(?=\\[${k.next}\\]|$)`, 'i'); const m = text.match(r); if (m && m[1].trim()) { fields[k.key] = m[1].trim(); hasBrackets = true; } } if (hasBrackets && fields.title) { return fields; } console.log("[PARSER] Strict bracket match failed/incomplete. Running smart lexical segmenter..."); // 2. Lexical Segmenter: find sections using common labels const getSection = (startRegex, endRegex) => { const startMatch = text.match(startRegex); if (!startMatch) return ''; const startIndex = startMatch.index + startMatch[0].length; let endIndex = text.length; if (endRegex) { const endMatch = text.match(endRegex); if (endMatch && endMatch.index > startIndex) { endIndex = endMatch.index; } } return text.substring(startIndex, endIndex).trim(); }; const descLabel = /(?:^|\n)(?:description|\[description\]|\*\*description\*\*)\s*(?::|-|\n)\s*/i; const tagsLabel = /(?:^|\n)(?:tags|\[tags\]|\*\*tags\*\*)\s*(?::|-|\n)\s*/i; const thumbnailLabel = /(?:^|\n)(?:thumbnail\s*(?:prompt)?|\[thumbnail_prompt\]|\*\*thumbnail_prompt\*\*)\s*(?::|-|\n)\s*/i; const contentLabel = /(?:^|\n)(?:product\s*(?:content)?|\[product_content\]|\*\*product_content\*\*)\s*(?::|-|\n)\s*/i; const descText = getSection(descLabel, tagsLabel); const tagsText = getSection(tagsLabel, thumbnailLabel); const thumbText = getSection(thumbnailLabel, contentLabel); const contentText = getSection(contentLabel, null); fields.description = descText || fields.description; fields.tags = tagsText || fields.tags; fields.thumbnailPrompt = thumbText || fields.thumbnailPrompt; fields.productContent = contentText || fields.productContent; // Find TITLE and SUBTITLE (text before the description label) const preDesc = text.split(descLabel)[0].trim(); const preDescLines = preDesc.split('\n').map(l => l.trim()).filter(Boolean); if (preDescLines.length > 0) { fields.title = preDescLines[0].replace(/^(title:?|\[title\]|\*\*title\*\*)\s*/i, ''); if (preDescLines.length > 1) { fields.subtitle = preDescLines.slice(1).join(' ').replace(/^(subtitle:?|\[subtitle\]|\*\*subtitle\*\*)\s*/i, ''); } } // Find CTA (usually the lines between tags and thumbnailPrompt that are not tags) const preThumb = text.split(thumbnailLabel)[0].trim(); const preThumbLines = preThumb.split('\n').map(l => l.trim()).filter(Boolean); if (preThumbLines.length > 0) { for (let i = preThumbLines.length - 1; i >= 0; i--) { const line = preThumbLines[i]; if (!line.toLowerCase().startsWith('tags:') && !line.toLowerCase().startsWith('description:') && line.split(',').length < 3 && line.length > 10) { fields.cta = line.replace(/^(cta:?|\[cta\]|\*\*cta\*\*)\s*/i, ''); break; } } } // Clean up fields if they are still empty if (!fields.title) fields.title = defaultNiche; if (!fields.cta) fields.cta = defaultCta; if (fields.tags) { fields.tags = fields.tags.split('\n')[0].replace(/^(tags:?|\[tags\]|\*\*tags\*\*)\s*/i, '').trim(); } return fields; }; // Health Check Endpoint app.get('/api/health', (req, res) => { res.json({ status: 'ok', groqConfigured: !!process.env.GROQ_API_KEY, hfConfigured: !!process.env.HF_API_KEY, dbType: isSupabaseConnected ? 'supabase' : 'local_json', dbConnected: true }); }); // ---------------------------------------------------- // DATABASE CATALOG ENDPOINTS ("AI DB") // ---------------------------------------------------- app.get('/api/catalog', async (req, res) => { if (isSupabaseConnected) { try { const { data, error } = await supabase .from('catalog') .select('*') .order('id', { ascending: false }); if (error) throw error; return res.json(data || []); } catch (err) { console.error('Error fetching from Supabase:', err.message); // Fallback } } // JSON Fallback const catalog = readDb(); res.json(catalog); }); app.post('/api/catalog', async (req, res) => { const { title, subtitle, type, price, platform, thumbnail_url, tags, sales_copy, status, published_url } = req.body; if (!title) { return res.status(400).json({ error: 'Book title is required' }); } const finalPlatform = platform || 'kdp'; const finalStatus = status || 'draft'; const finalPrice = price || 9.99; if (isSupabaseConnected) { try { const { data, error } = await supabase .from('catalog') .insert([{ title, subtitle: subtitle || '', type: type || 'other', price: finalPrice, platform: finalPlatform, thumbnail_url: thumbnail_url || '', tags: tags || '', sales_copy: sales_copy || '', status: finalStatus, published_url: published_url || '' }]) .select(); if (error) throw error; if (data && data.length > 0) { return res.status(201).json(data[0]); } } catch (err) { console.error('Error saving to Supabase:', err.message); // Fallback } } // JSON Fallback const catalog = readDb(); const newBook = { id: Date.now().toString(), title, subtitle: subtitle || '', type: type || 'other', price: finalPrice, platform: finalPlatform, thumbnail_url: thumbnail_url || '', tags: tags || '', sales_copy: sales_copy || '', status: finalStatus, published_url: published_url || '', date: new Date().toLocaleDateString() }; catalog.unshift(newBook); writeDb(catalog); res.status(201).json(newBook); }); app.delete('/api/catalog/:id', async (req, res) => { const { id } = req.params; if (isSupabaseConnected) { try { const { error } = await supabase .from('catalog') .delete() .eq('id', id); if (error) throw error; return res.json({ success: true }); } catch (err) { console.error('Error deleting from Supabase:', err.message); // Fallback } } // JSON Fallback let catalog = readDb(); const originalLength = catalog.length; catalog = catalog.filter(book => book.id !== id); if (catalog.length === originalLength) { return res.status(404).json({ error: 'Book not found' }); } writeDb(catalog); res.json({ success: true }); }); // 1. TEXT GENERATION API (Uses Groq Llama 3.3 70B) app.post('/api/generate-text', async (req, res) => { const { prompt, maxTokens = 800 } = req.body; if (!process.env.GROQ_API_KEY) { return res.status(401).json({ error: 'Please set your GROQ_API_KEY in the server/.env file' }); } try { const text = await groqChat(prompt, maxTokens); res.json({ text }); } catch (error) { console.error('Groq Text Error:', error); res.status(500).json({ error: error.message }); } }); // 2. FREE COVER ART GENERATION API (Uses Stable Diffusion XL) app.post('/api/generate-cover', async (req, res) => { const { prompt, title } = req.body; if (!process.env.HF_API_KEY) { return res.status(401).json({ error: 'Please set your HF_API_KEY in the server/.env file' }); } const models = [ 'black-forest-labs/FLUX.1-schnell', 'stabilityai/stable-diffusion-xl-base-1.0', 'stabilityai/stable-diffusion-3.5-medium' ]; let responseBlob = null; let lastError = null; for (const model of models) { try { console.log(`[HF] Attempting cover generation with model: ${model}...`); responseBlob = await hf.textToImage({ model: model, inputs: prompt, parameters: model.includes('flux') ? {} : { negative_prompt: 'blurry, low quality, distorted, text, watermark' } }); console.log(`[HF] Successfully generated cover using model: ${model}`); break; } catch (err) { console.warn(`[HF] Model ${model} failed: ${err.message}`); lastError = err; } } const safeTitle = (title || 'cover').replace(/[^a-zA-Z0-9]/g, '_').toLowerCase(); const baseUrl = getBaseUrl(req); if (responseBlob) { try { const arrayBuffer = await responseBlob.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); const base64Image = buffer.toString('base64'); const coverFilename = `${safeTitle}_${Date.now()}.jpg`; const coverPath = path.join(coversDir, coverFilename); fs.writeFileSync(coverPath, buffer); const coverUrl = `${baseUrl}/static/covers/${coverFilename}`; return res.json({ imageUrl: `data:image/jpeg;base64,${base64Image}`, coverUrl, coverFilename }); } catch (bufErr) { console.error('Error handling HF response buffer:', bufErr); lastError = bufErr; } } // ---------------------------------------------------- // RESILIENT FALLBACK: Generate Premium Vector SVG Cover // ---------------------------------------------------- console.log(`[HF] All models failed. Generating premium vector SVG fallback cover...`); const svgContent = ` `; const svgBuffer = Buffer.from(svgContent); const base64Svg = svgBuffer.toString('base64'); const coverFilename = `${safeTitle}_${Date.now()}.svg`; const coverPath = path.join(coversDir, coverFilename); fs.writeFileSync(coverPath, svgBuffer); const coverUrl = `${baseUrl}/static/covers/${coverFilename}`; res.json({ imageUrl: `data:image/svg+xml;base64,${base64Svg}`, coverUrl, coverFilename }); }); // 3. MULTI-PLATFORM DIGITAL PRODUCT GENERATOR const PLATFORM_STRATEGIES = { gumroad: { name: 'Gumroad', emoji: 'đ ', priceMin: 7, priceMax: 27, style: 'personal story + results proof', cta: 'Pay what you want or get instant access' }, etsy: { name: 'Etsy', emoji: 'đĄ', priceMin: 2, priceMax: 8, style: 'INSTANT DOWNLOAD urgency + printable keywords', cta: 'Instant Digital Download â Print at Home' }, kdp: { name: 'Amazon KDP', emoji: 'đĩ', priceMin: 9, priceMax: 15, style: 'SEO keyword-first title', cta: 'Available on Amazon KDP â Kindle and Paperback' }, kofi: { name: 'Ko-fi', emoji: 'â', priceMin: 3, priceMax: 10, style: 'community + exclusivity + support', cta: 'Support the creator and get instant access' }, payhip: { name: 'Payhip', emoji: 'đ', priceMin: 5, priceMax: 20, style: 'value stack + benefit headline', cta: 'Secure Checkout â Instant Delivery' }, creative: { name: 'Creative Market', emoji: 'đ¨', priceMin: 15, priceMax: 49, style: 'professional premium quality fonts/graphics', cta: 'Professional Grade â Commercial License Included' }, teachable:{ name: 'Teachable', emoji: 'đ', priceMin: 19, priceMax: 97, style: 'transformation promise complete guide', cta: 'Enroll Now â Lifetime Access' }, envato: { name: 'Envato', emoji: 'đĸ', priceMin: 12, priceMax: 45, style: 'premium theme/template professional', cta: 'Extended License Available' } }; // Helper to get base URL dynamically const getBaseUrl = (req) => { const host = req.headers['x-forwarded-host'] || req.headers.host || 'localhost:5000'; const protocol = req.headers['x-forwarded-proto'] || 'http'; const isHF = host.includes('hf.space'); return `${isHF ? 'https' : protocol}://${host}`; }; app.post('/api/generate-product', async (req, res) => { const { platform = 'gumroad', niche } = req.body; if (!niche) return res.status(400).json({ error: 'Niche is required' }); if (!process.env.GROQ_API_KEY) return res.status(401).json({ error: 'GROQ_API_KEY not configured' }); const strategy = PLATFORM_STRATEGIES[platform] || PLATFORM_STRATEGIES.gumroad; const optimalPrice = (Math.random() * (strategy.priceMax - strategy.priceMin) + strategy.priceMin).toFixed(2); const prompt = `Create a complete, high-converting digital book package for this niche: "${niche}", optimized for ${strategy.name} using the ${strategy.style} approach. CRITICAL ORIGINALITY & COPYRIGHT DIRECTIVES: - Content must be 100% original, creative, and written from scratch. Do NOT copy, paraphrase, or borrow from any copyrighted books, publications, or trademarked products. - Focus on high-value, highly practical content (e.g., comprehensive study guides, structured planners, templates, step-by-step how-to guides, and engaging hobby/educational materials) rather than low-quality generic text. - If this guide contains technical, factual, or educational information, ensure all facts are reviewed, verified, and include realistic, helpful examples and citations where appropriate. - COMPLIANCE & DISCLOSURE: Disclose AI assistance transparently as required by platform policies (e.g. KDP/Etsy). Add a small, elegant "Research & Editorial Note: Designed with the assistance of advanced generative tools and verified by human subject matter experts for accuracy" to the product introduction. Your response must be a single, valid JSON object matching this schema EXACTLY: { "title": "A viral, specific title optimized for self-publishing (e.g. '90-Day Indian Weight Loss Plan' instead of generic 'Weight Loss'). Max 80 characters.", "subtitle": "A benefit-driven subtitle with an emotional hook. Max 150 characters.", "description": "A 150-word high-converting product description with emotional hooks and bullet benefits.", "tags": "An array of exactly 5 comma-separated SEO tags for ${strategy.name}.", "cta": "One powerful call-to-action sentence (max 20 words).", "thumbnailPrompt": "A detailed Stable Diffusion prompt for a stunning professional product cover image. Include style, colors, composition. Keep under 80 words.", "productContent": "The actual complete high-quality digital book. Use this EXACT structure: - Detailed Table of Contents (listing all 5 chapters) - Chapter 1 - Introduction (Comprehensive introductory text in easy-to-read English, short paragraphs, real-world examples, ending with a 'Key Points' summary with checkmarks 'â') - Chapter 2 - Basics (Solid explanation of core concepts, foundational steps, ending with a 'Key Points' summary with checkmarks 'â') - Chapter 3 - Advanced Techniques (High-level professional strategies, ending with a 'Key Points' summary with checkmarks 'â') - Chapter 4 - Case Studies (Actionable case studies, templates, or checklists, ending with a 'Key Points' summary with checkmarks 'â') - Chapter 5 - Conclusion (Final takeaways, future roadmaps, ending with a 'Key Points' summary with checkmarks 'â')" } Ensure the "productContent" is extremely comprehensive, highly valuable, and direct, avoiding generic AI repetitions or empty introductory filler phrases. Let it be a complete, ready-to-sell book of at least 3000-4000 words. Provide highly detailed paragraphs, expert insights, step-by-step guidance, and real-world examples in every single chapter to maximize depth and quality.`; try { const data = await groqJsonChat(prompt, 6000); const title = data.title || niche; const subtitle = data.subtitle || ''; const description = data.description || ''; const tags = Array.isArray(data.tags) ? data.tags.join(', ') : (data.tags || ''); const cta = data.cta || strategy.cta; const thumbnailPrompt = data.thumbnailPrompt || ''; let productContent = data.productContent || 'Sample digital product guide content...'; // Humanizer Pass to make it 100% human-like try { console.log(`[HUMANIZER] Running humanization pass...`); productContent = await humanizeBookContent(productContent); console.log(`[HUMANIZER] Book successfully humanized!`); } catch (hErr) { console.error('Humanizer Pass failed, keeping original:', hErr); } // Save product content to file const safeTitle = (title || niche).replace(/[^a-zA-Z0-9]/g, '_').toLowerCase(); const productFilename = `${safeTitle}_${Date.now()}.md`; const productPath = path.join(productsDir, productFilename); fs.writeFileSync(productPath, productContent); const baseUrl = getBaseUrl(req); const productUrl = `${baseUrl}/static/products/${productFilename}`; res.json({ platform, platformName: strategy.name, platformEmoji: strategy.emoji, niche, title, subtitle, description, tags, cta, thumbnailPrompt, productContent, productFilename, productUrl, price: parseFloat(optimalPrice), priceLabel: `$${optimalPrice}`, }); } catch (err) { console.error('Generate Product Error:', err); res.status(500).json({ error: err.message }); } }); // ---------------------------------------------------- // AI AGENT AUTOPILOT ENGINE ("AUTO-PRODUCT PIPELINE") // ---------------------------------------------------- // ---------------------------------------------------- // ZIP PACKAGE BUILDER // Creates a downloadable ZIP with: book.md, cover.jpg, listing.txt // ---------------------------------------------------- const compile120PageHtml = (title, subtitle, tags, productContent, theme = 'classic') => { const text = productContent || ''; const BOOK_THEMES = { classic: { primary: '#000000', border: '#000000', bgLight: '#ffffff', dots: 'rgba(0,0,0,0.3)', gradient: '#000000' }, cherry: { primary: '#db2777', border: '#db2777', bgLight: '#fdf2f8', dots: 'rgba(219,39,119,0.35)', gradient: 'linear-gradient(135deg, #db2777, #7c3aed)' }, emerald: { primary: '#059669', border: '#059669', bgLight: '#ecfdf5', dots: 'rgba(5,150,105,0.35)', gradient: 'linear-gradient(135deg, #059669, #0891b2)' }, ocean: { primary: '#2563eb', border: '#2563eb', bgLight: '#eff6ff', dots: 'rgba(37,99,235,0.35)', gradient: 'linear-gradient(135deg, #2563eb, #0d9488)' }, sunset: { primary: '#d97706', border: '#d97706', bgLight: '#fffbeb', dots: 'rgba(217,119,6,0.35)', gradient: 'linear-gradient(135deg, #d97706, #dc2626)' } }; const themeCfg = BOOK_THEMES[theme] || BOOK_THEMES.classic; // Bulletproof Chapter Slicer bypassing the TOC const chapters = []; for (let idx = 1; idx <= 5; idx++) { const searchStr = `Chapter ${idx}`; const firstIdx = text.indexOf(searchStr); let startIndex = -1; if (firstIdx !== -1) { // Look for the second occurrence to bypass TOC startIndex = text.indexOf(searchStr, firstIdx + searchStr.length); if (startIndex === -1) { startIndex = firstIdx; // fallback to first } } if (startIndex !== -1) { let endIndex = text.length; if (idx < 5) { const nextSearchStr = `Chapter ${idx + 1}`; const nextFirstIdx = text.indexOf(nextSearchStr); let nextStartIndex = -1; if (nextFirstIdx !== -1) { nextStartIndex = text.indexOf(nextSearchStr, nextFirstIdx + nextSearchStr.length); if (nextStartIndex === -1) { nextStartIndex = nextFirstIdx; } } if (nextStartIndex !== -1) { endIndex = nextStartIndex; } } let chapterText = text.substring(startIndex, endIndex).trim(); // Remove trailing Markdown header symbols if present if (chapterText.endsWith('#')) { chapterText = chapterText.substring(0, chapterText.lastIndexOf('#')).trim(); } chapters.push(chapterText); } else { chapters.push(`Chapter ${idx}\n\nContent for chapter ${idx} is detailed here.`); } } // Dynamic 120-Page Assembler let pagesHtml = ''; // Page 1: Title Page pagesHtml += `
${subtitle || ''}
© ${new Date().getFullYear()} BookForge Digital Publishing. All rights reserved.
No part of this publication may be reproduced, distributed, or transmitted in any form or by any means, including photocopying, recording, or other electronic or mechanical methods, without the prior written permission of the publisher, except in the case of brief quotations embodied in critical reviews.
Research & Editorial Note: Designed with the assistance of advanced generative tools and verified by human subject matter experts for accuracy, original insights, and educational excellence. Checked strictly against copyright frameworks for complete compliance and original value creation.
Trade Size: 6" x 9" inches (bleed enabled)
Spine Width Offset: 0.2704 inches (120 cream interior pages)
Niche Strategy: ${tags || 'General self-publishing'}
Welcome to this premium combined edition. Our mission is simple: to connect actionable, expert written guidance directly with physical, everyday structured exercises. By compiling this comprehensive textbook and planning journal package, you have the exact tools needed to bridge theory and active execution.
As you progress through each chapter, read the developmental insights generated to lay the conceptual groundwork. Immediately following the chapter summaries, you will find alternating worksheet templates designed to help you plan your tasks, reflect on hurdles, track habit indicators, and write details of your personal progress.
Set aside time daily, track your goals strictly, and let's forge your path to success page by page.
â The Editorial Team, BookForge Digital
Detailed Written Guide & Workbook Interior
${bodyText}
| ACTION STEP REQUIRED | TIMELINE |
|---|---|
| ___ Days | |
| ___ Days | |
| ___ Days |