import React, { useEffect, useRef, useState } from 'react'
import { Link, router, usePage } from '@inertiajs/react'
import SeoHead from '../../components/seo/SeoHead'
import { postAcademyAction, trackAcademyEvent, trackUpgradeClick, useAcademyPageAnalytics } from '../../lib/academyAnalytics'
function academyHref(section, slug) {
return `/academy/${section}/${encodeURIComponent(slug)}`
}
function AcademyBreadcrumbs({ items = [] }) {
if (!items.length) return null
return (
{items.map((item, index) => {
const isLast = index === items.length - 1
return (
{index > 0 ? / : null}
{isLast ? (
{item.label}
) : (
{item.label}
)}
)
})}
)
}
function slugifyHeading(value, fallback = 'section') {
const normalized = String(value || '')
.toLowerCase()
.trim()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '')
return normalized || fallback
}
function formatLessonDate(value) {
if (!value) return 'Recently updated'
const date = new Date(value)
if (Number.isNaN(date.getTime())) return 'Recently updated'
return new Intl.DateTimeFormat('en', { month: 'short', day: 'numeric', year: 'numeric' }).format(date)
}
function formatLessonMinutes(minutes) {
const value = Number(minutes || 0)
return value > 0 ? `${value} min read` : 'Quick read'
}
function normalizePromptAccessLevel(accessLevel) {
const value = String(accessLevel || 'free').trim().toLowerCase()
return value === 'creator' || value === 'pro' ? value : 'free'
}
function promptRequirementText(accessLevel) {
const level = normalizePromptAccessLevel(accessLevel)
if (level === 'pro') return 'Requires Pro access.'
if (level === 'creator') return 'Requires Creator or Pro access.'
return null
}
function promptUnlockHeading(accessLevel) {
const level = normalizePromptAccessLevel(accessLevel)
if (level === 'pro') return 'Unlock the full Pro prompt.'
if (level === 'creator') return 'Unlock the full Creator prompt.'
return 'Unlock the full prompt.'
}
function promptUnlockDescription(accessLevel) {
const level = normalizePromptAccessLevel(accessLevel)
if (level === 'pro') {
return 'Get the complete reusable prompt, negative prompt, workflow notes, model settings, and variation strategy.'
}
if (level === 'creator') {
return 'Get the complete reusable prompt, negative prompt, workflow notes, and creative workflow.'
}
return 'Get the complete reusable prompt and workflow notes.'
}
function promptInlineImage(url, thumbUrl) {
return thumbUrl || url || ''
}
function formatMetaDisplay(value) {
const normalized = String(value || '').trim()
if (!normalized) return ''
return normalized
.replace(/[_-]+/g, ' ')
.replace(/\b\w/g, (character) => character.toUpperCase())
}
function StatPill({ label, value, icon, accentClassName = 'border-white/10 bg-white/[0.04] text-slate-300', valueClassName = 'text-white' }) {
return (
)
}
function PromptHeaderStat({ label, value, icon, accentClassName = 'border-white/10 bg-white/[0.04] text-slate-300', valueClassName = 'text-white' }) {
return (
)
}
function LessonInfoRow({ label, value }) {
return (
{label}
{value}
)
}
function LessonNavCard({ direction, lesson }) {
if (!lesson) return null
const eyebrow = direction === 'previous' ? 'Previous lesson' : 'Next lesson'
const alignClass = direction === 'previous' ? 'items-start text-left' : 'items-end text-right'
const href = lesson.course_url || `/academy/lessons/${lesson.slug}`
return (
{eyebrow}
{lesson.lesson_label ?
{lesson.lesson_label}
: null}
{lesson.title}
{lesson.excerpt || lesson.content_preview || 'Open the next step in this Academy sequence.'}
)
}
function LockedPanel({ pricingUrl, label, accessLevel, onUpgrade }) {
const isPrompt = label === 'prompt'
const requirement = promptRequirementText(accessLevel)
return (
Premium content
{isPrompt ? promptUnlockHeading(accessLevel) : `Unlock the full ${label}.`}
{isPrompt ? promptUnlockDescription(accessLevel) : 'This preview is visible, but the full Academy content stays server-side until your account has the required access.'}
{requirement ?
{requirement}
: null}
See Academy plans
)
}
function copyTextToClipboard(text) {
const source = String(text || '')
if (!source) return Promise.reject(new Error('Nothing to copy'))
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
return navigator.clipboard.writeText(source)
}
const textarea = document.createElement('textarea')
textarea.value = source
textarea.setAttribute('readonly', 'true')
textarea.style.position = 'fixed'
textarea.style.top = '-1000px'
textarea.style.left = '-1000px'
document.body.appendChild(textarea)
textarea.select()
try {
if (document.execCommand('copy')) {
return Promise.resolve()
}
} finally {
document.body.removeChild(textarea)
}
return Promise.reject(new Error('Clipboard unavailable'))
}
function PromptCopyButton({ prompt, label = 'Copy prompt', analytics = null, contentId = null, eventType = 'academy_prompt_copy', metadata = {} }) {
const [status, setStatus] = useState('idle')
const resetTimerRef = useRef(0)
return (
{
copyTextToClipboard(prompt)
.then(() => {
setStatus('copied')
void trackAcademyEvent(eventType, analytics?.contentType || null, contentId || analytics?.contentId || null, metadata, {
url: analytics?.eventUrl,
pageName: analytics?.pageName,
useBeacon: false,
})
})
.catch(() => setStatus('failed'))
.finally(() => {
window.clearTimeout(resetTimerRef.current)
resetTimerRef.current = window.setTimeout(() => setStatus('idle'), 1800)
})
}}
className="inline-flex items-center gap-2 rounded-full border border-[#ffb9ab]/20 bg-[#ffb9ab]/10 px-4 py-2 text-sm font-semibold text-[#ffe2dc] transition hover:border-[#ffb9ab]/35 hover:bg-[#ffb9ab]/16"
aria-label="Copy prompt"
>
{status === 'copied' ? 'Copied' : status === 'failed' ? 'Copy failed' : label}
)
}
function ImageLightbox({ gallery, onClose, onNavigate }) {
useEffect(() => {
if (!gallery?.images?.length) return undefined
const handleEscape = (event) => {
if (event.key === 'Escape') {
onClose()
return
}
if (event.key === 'ArrowLeft') {
onNavigate(-1)
return
}
if (event.key === 'ArrowRight') {
onNavigate(1)
}
}
document.body.style.overflow = 'hidden'
window.addEventListener('keydown', handleEscape)
return () => {
document.body.style.overflow = ''
window.removeEventListener('keydown', handleEscape)
}
}, [gallery, onClose, onNavigate])
const images = Array.isArray(gallery?.images) ? gallery.images : []
const currentIndex = Math.max(0, Math.min(images.length - 1, Number(gallery?.index || 0)))
const currentImage = images[currentIndex]
if (!currentImage?.src) return null
return (
{images.length > 1 ? (
{ event.stopPropagation(); onNavigate(-1) }} className="absolute left-4 top-1/2 inline-flex h-12 w-12 -translate-y-1/2 items-center justify-center rounded-full border border-white/10 bg-black/35 text-white transition hover:border-white/25 hover:bg-white/10" aria-label="Previous image">
) : null}
{images.length > 1 ? (
{ event.stopPropagation(); onNavigate(1) }} className="absolute right-4 top-1/2 inline-flex h-12 w-12 -translate-y-1/2 items-center justify-center rounded-full border border-white/10 bg-black/35 text-white transition hover:border-white/25 hover:bg-white/10" aria-label="Next image">
) : null}
event.stopPropagation()}>
{images.length > 1 ? (
{currentImage.alt || `Image ${currentIndex + 1}`}
{`Image ${currentIndex + 1} of ${images.length}`}
{images.map((image, index) => (
onNavigate(index - currentIndex)}
className={`h-2.5 w-2.5 rounded-full transition ${index === currentIndex ? 'bg-white' : 'bg-white/25 hover:bg-white/45'}`}
aria-label={`Go to image ${index + 1}`}
/>
))}
) : null}
)
}
function PromptToolNoteCard({ note, index, galleryIndex, onOpenImage }) {
if (!note || typeof note !== 'object') return null
const displayType = String(note.display_type || '').trim()
const eyebrowLabel = displayType || 'AI comparison'
const title = note.model_name || note.provider || `${displayType || 'Comparison'} ${String(index + 1).padStart(2, '0')}`
const subtitle = [note.provider, note.model_name].filter(Boolean).join(' · ')
const previewUrl = promptInlineImage(note.image_url, note.thumb_url)
const hasContent = Boolean(displayType || note.notes || note.strengths || note.weaknesses || note.best_for || note.settings || previewUrl || note.score || subtitle)
if (!hasContent) return null
return (
{previewUrl ? (
onOpenImage?.(galleryIndex)}
className="group mb-5 block w-full overflow-hidden rounded-[24px] border border-white/10 bg-slate-950 text-left transition hover:border-sky-300/25 focus:outline-none focus:ring-2 focus:ring-sky-300/35"
aria-label={`Open comparison image for ${title}`}
>
Click to zoom
) : null}
{eyebrowLabel}
{title}
{subtitle ?
{subtitle}
: null}
{String(index + 1).padStart(2, '0')}
{note.score ? {`Score ${note.score}/10`} : null}
{note.settings ? (
Generated in
{note.settings}
) : null}
{note.notes ? (
Overall notes
{note.notes}
) : null}
{note.best_for ? (
) : null}
{note.strengths ? (
Strengths
{note.strengths}
) : null}
{note.weaknesses ? (
Weaknesses
{note.weaknesses}
) : null}
)
}
function normalizePromptDocumentation(documentation) {
const source = documentation && typeof documentation === 'object' && !Array.isArray(documentation) ? documentation : {}
const list = (key) => (Array.isArray(source[key]) ? source[key] : [])
.map((item) => String(item || '').trim())
.filter(Boolean)
return {
summary: String(source.summary || '').trim(),
best_for: list('best_for'),
how_to_use: list('how_to_use'),
required_inputs: list('required_inputs'),
workflow: list('workflow'),
tips: list('tips'),
common_mistakes: list('common_mistakes'),
data_accuracy_notes: list('data_accuracy_notes'),
display_notes: String(source.display_notes || '').trim(),
}
}
function PromptDocumentationPanel({ documentation }) {
const hasContent = Boolean(
documentation.summary
|| documentation.display_notes
|| documentation.best_for.length
|| documentation.how_to_use.length
|| documentation.required_inputs.length
|| documentation.workflow.length
|| documentation.tips.length
|| documentation.common_mistakes.length
|| documentation.data_accuracy_notes.length,
)
if (!hasContent) return null
return (
How to use
Prompt documentation
{documentation.summary ?
{documentation.summary}
: null}
{documentation.best_for.length ? (
Best for
{documentation.best_for.map((item) => (
{item}
))}
) : null}
{documentation.how_to_use.length ? (
How to use
{documentation.how_to_use.map((step, index) => (
{index + 1}
{step}
))}
) : null}
{documentation.workflow.length ? (
Workflow
{documentation.workflow.map((step, index) => (
{index + 1}
{step}
))}
) : null}
{documentation.required_inputs.length ? (
Required inputs
{documentation.required_inputs.map((item) => {item} )}
) : null}
{documentation.tips.length ? (
Tips
{documentation.tips.map((item) => {item} )}
) : null}
{documentation.common_mistakes.length ? (
Common mistakes
{documentation.common_mistakes.map((item) => {item} )}
) : null}
{documentation.data_accuracy_notes.length ? (
Data accuracy notes
{documentation.data_accuracy_notes.map((item) => {item} )}
) : null}
{documentation.display_notes ? (
Display note
{documentation.display_notes}
) : null}
)
}
function PromptPlaceholderCard({ placeholder }) {
if (!placeholder || typeof placeholder !== 'object') return null
const example = placeholder.example
const defaultValue = placeholder.default
const renderValue = (value) => {
if (value == null || value === '') return null
if (typeof value === 'object') {
return {JSON.stringify(value, null, 2)}
}
return {String(value)}
}
return (
Placeholder
[{placeholder.key || 'VALUE'}]
{placeholder.label ?
{placeholder.label}
: null}
{placeholder.type ? {placeholder.type} : null}
{placeholder.required ? Required : null}
{placeholder.description ? {placeholder.description}
: null}
{example != null && example !== '' ? (
Example
{renderValue(example)}
) : null}
{defaultValue != null && defaultValue !== '' ? (
Default
{renderValue(defaultValue)}
) : null}
)
}
function PromptFilledExampleCard({ example, analytics, contentId, index }) {
const placeholderEntries = Object.entries(example?.placeholder_values || {}).filter(([key, value]) => String(key || '').trim() && value != null && value !== '' && value !== false)
return (
Filled example {index + 1}
{example?.title || `Example ${index + 1}`}
{example?.description ?
{example.description}
: null}
{example?.prompt ? (
) : null}
{placeholderEntries.length ? (
{placeholderEntries.map(([key, value]) => (
{key}: {String(value)}
))}
) : null}
{example?.prompt ? {example.prompt} : null}
{example?.negative_prompt ? (
{example.negative_prompt}
) : null}
)
}
function PromptFilledExamplesSection({ examples, analytics, contentId }) {
const visibleExamples = Array.isArray(examples) ? examples.filter((example) => example && typeof example === 'object') : []
const [activeExampleIndex, setActiveExampleIndex] = useState(0)
const examplesScrollRef = useRef(null)
const [canScrollExamplesLeft, setCanScrollExamplesLeft] = useState(false)
const [canScrollExamplesRight, setCanScrollExamplesRight] = useState(false)
useEffect(() => {
if (typeof window === 'undefined') {
return undefined
}
const updateExampleScrollState = () => {
const element = examplesScrollRef.current
if (!element) {
setCanScrollExamplesLeft(false)
setCanScrollExamplesRight(false)
return
}
const maxScrollLeft = Math.max(0, element.scrollWidth - element.clientWidth)
setCanScrollExamplesLeft(element.scrollLeft > 6)
setCanScrollExamplesRight(element.scrollLeft < maxScrollLeft - 6)
}
updateExampleScrollState()
const element = examplesScrollRef.current
if (!element) {
return undefined
}
element.addEventListener('scroll', updateExampleScrollState, { passive: true })
window.addEventListener('resize', updateExampleScrollState, { passive: true })
return () => {
element.removeEventListener('scroll', updateExampleScrollState)
window.removeEventListener('resize', updateExampleScrollState)
}
}, [visibleExamples.length])
useEffect(() => {
if (!visibleExamples.length) {
setActiveExampleIndex(0)
return
}
setActiveExampleIndex((current) => Math.max(0, Math.min(current, visibleExamples.length - 1)))
}, [visibleExamples.length])
if (!visibleExamples.length) return null
const activeExample = visibleExamples[activeExampleIndex] || visibleExamples[0]
const activeExampleLabel = String(activeExample?.title || '').trim() || `Example ${activeExampleIndex + 1}`
const activeExampleDescription = String(activeExample?.description || '').trim()
const scrollExamples = (direction) => {
const element = examplesScrollRef.current
if (!element) return
const amount = Math.max(220, Math.floor(element.clientWidth * 0.65))
element.scrollBy({
left: direction === 'left' ? -amount : amount,
behavior: 'smooth',
})
}
return (
Selected example
{activeExampleLabel}
{activeExampleDescription ?
{activeExampleDescription}
: null}
scrollExamples('left')}
className={`absolute left-2 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full border border-white/12 bg-slate-950/80 text-white/80 shadow-[0_16px_36px_rgba(2,6,23,0.28)] backdrop-blur transition ${canScrollExamplesLeft ? 'opacity-100 hover:scale-105 hover:bg-slate-900/95' : 'pointer-events-none opacity-0'}`}
>
scrollExamples('right')}
className={`absolute right-2 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full border border-white/12 bg-slate-950/80 text-white/80 shadow-[0_16px_36px_rgba(2,6,23,0.28)] backdrop-blur transition ${canScrollExamplesRight ? 'opacity-100 hover:scale-105 hover:bg-slate-900/95' : 'pointer-events-none opacity-0'}`}
>
{visibleExamples.map((example, index) => {
const isActive = index === activeExampleIndex
const exampleLabel = String(example?.title || '').trim() || `Example ${index + 1}`
return (
setActiveExampleIndex(index)}
aria-pressed={isActive}
title={exampleLabel}
className={`max-w-full whitespace-nowrap rounded-full border px-4 py-2.5 text-sm font-semibold uppercase tracking-[0.18em] transition ${isActive ? 'border-violet-300/30 bg-violet-300/18 text-white shadow-[0_12px_30px_rgba(76,29,149,0.24)]' : 'border-white/10 bg-white/[0.04] text-violet-100/80 hover:border-violet-300/20 hover:bg-violet-300/10 hover:text-white'}`}
>
{exampleLabel}
)
})}
)
}
function PromptHelperPromptCard({ helperPrompt, analytics, contentId }) {
if (!helperPrompt || typeof helperPrompt !== 'object') return null
return (
Helper prompt
{helperPrompt.title || 'Helper prompt'}
{helperPrompt.description ?
{helperPrompt.description}
: null}
{helperPrompt.type ? {formatMetaDisplay(helperPrompt.type)} : null}
{helperPrompt.expected_output ? {helperPrompt.expected_output} : null}
)
}
function PromptVariantCard({ variant, analytics, contentId }) {
if (!variant || typeof variant !== 'object') return null
return (
Prompt variant
{variant.title || 'Variant'}
{variant.description ?
{variant.description}
: null}
{variant.recommended ? Recommended : null}
{variant.slug ? {variant.slug} : null}
{variant.recommended_for?.length ? (
{variant.recommended_for.map((item) => (
{item}
))}
) : null}
{variant.negative_prompt ? (
{variant.negative_prompt}
) : null}
{variant.risk_notes?.length ? (
Risk notes
{variant.risk_notes.map((item) => {item} )}
) : null}
)
}
function PromptVariantsSection({ variants, analytics, contentId }) {
const visibleVariants = Array.isArray(variants) ? variants.filter((variant) => variant && typeof variant === 'object') : []
const [activeVariantKey, setActiveVariantKey] = useState('')
const variantsScrollRef = useRef(null)
const [canScrollVariantsLeft, setCanScrollVariantsLeft] = useState(false)
const [canScrollVariantsRight, setCanScrollVariantsRight] = useState(false)
useEffect(() => {
if (typeof window === 'undefined') {
return undefined
}
const updateVariantScrollState = () => {
const element = variantsScrollRef.current
if (!element) {
setCanScrollVariantsLeft(false)
setCanScrollVariantsRight(false)
return
}
const maxScrollLeft = Math.max(0, element.scrollWidth - element.clientWidth)
setCanScrollVariantsLeft(element.scrollLeft > 6)
setCanScrollVariantsRight(element.scrollLeft < maxScrollLeft - 6)
}
updateVariantScrollState()
const element = variantsScrollRef.current
if (!element) {
return undefined
}
element.addEventListener('scroll', updateVariantScrollState, { passive: true })
window.addEventListener('resize', updateVariantScrollState, { passive: true })
return () => {
element.removeEventListener('scroll', updateVariantScrollState)
window.removeEventListener('resize', updateVariantScrollState)
}
}, [visibleVariants.length])
const scrollVariants = (direction) => {
const element = variantsScrollRef.current
if (!element) return
const amount = Math.max(260, Math.floor(element.clientWidth * 0.7))
element.scrollBy({
left: direction === 'left' ? -amount : amount,
behavior: 'smooth',
})
}
useEffect(() => {
if (!visibleVariants.length) {
setActiveVariantKey('')
return
}
const recommendedVariant = visibleVariants.find((variant) => variant?.recommended)
const nextDefaultKey = String(recommendedVariant?.slug || recommendedVariant?.title || visibleVariants[0]?.slug || visibleVariants[0]?.title || 'variant-0')
setActiveVariantKey((current) => {
if (visibleVariants.some((variant, index) => String(variant?.slug || variant?.title || `variant-${index}`) === current)) {
return current
}
return nextDefaultKey
})
}, [visibleVariants])
if (!visibleVariants.length) return null
const activeVariant = visibleVariants.find((variant, index) => String(variant?.slug || variant?.title || `variant-${index}`) === activeVariantKey) || visibleVariants[0]
return (
Variants
Alternative prompt versions
Switch between safer, shorter, or more specialized prompt variants without losing the core creative direction.
scrollVariants('left')}
className={`absolute left-2 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full border border-white/12 bg-slate-950/80 text-white/80 shadow-[0_16px_36px_rgba(2,6,23,0.28)] backdrop-blur transition ${canScrollVariantsLeft ? 'opacity-100 hover:scale-105 hover:bg-slate-900/95' : 'pointer-events-none opacity-0'}`}
>
scrollVariants('right')}
className={`absolute right-2 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full border border-white/12 bg-slate-950/80 text-white/80 shadow-[0_16px_36px_rgba(2,6,23,0.28)] backdrop-blur transition ${canScrollVariantsRight ? 'opacity-100 hover:scale-105 hover:bg-slate-900/95' : 'pointer-events-none opacity-0'}`}
>
{visibleVariants.map((variant, index) => {
const variantKey = String(variant?.slug || variant?.title || `variant-${index}`)
const isActive = activeVariant === variant
return (
setActiveVariantKey(variantKey)}
className={[
'w-[min(360px,calc(100vw-4.5rem))] shrink-0 snap-start rounded-[24px] border px-4 py-3 text-left transition sm:w-[320px]',
isActive
? 'border-sky-300/30 bg-sky-300/12 shadow-[0_16px_40px_rgba(2,6,23,0.18)]'
: 'border-white/10 bg-black/20 hover:border-white/20 hover:bg-white/[0.05]',
].join(' ')}
>
{variant.title || `Variant ${index + 1}`}
{variant.description ?
{variant.description}
: null}
{variant.recommended ?
Top pick : null}
{variant.slug ? {variant.slug} : null}
{variant.recommended_for?.length ? {variant.recommended_for.length} use case{variant.recommended_for.length === 1 ? '' : 's'} : null}
)
})}
)
}
function PromptPublicExampleCard({ example, index, galleryIndex, onOpenImage, className = '', frameClassName }) {
if (!example || typeof example !== 'object') return null
const previewUrl = promptInlineImage(example.image_url, example.thumb_url)
if (!previewUrl) return null
const title = example.title || `Prompt Example ${index + 1}`
const subtitle = [example.provider, example.model_name].filter(Boolean).join(' · ')
const resolvedFrameClassName = frameClassName || (index === 0 ? 'aspect-[6/5]' : 'aspect-[4/5]')
return (
onOpenImage?.(galleryIndex)}
className="group block w-full text-left"
aria-label={`Open example image for ${title}`}
>
{example.type_label || 'Variation'}
{example.score ? {`${example.score}/10`} : null}
{title}
{subtitle ?
{subtitle}
: null}
{example.caption ?
{example.caption}
: null}
)
}
function AiComparisonSection({ block }) {
const payload = block?.payload || {}
const criteria = Array.isArray(payload.criteria) ? payload.criteria.filter(Boolean) : []
const results = Array.isArray(block?.comparison_results) ? block.comparison_results.filter((result) => result?.active !== false) : []
const hasPrompt = Boolean(payload.prompt)
const hasNegativePrompt = Boolean(payload.negative_prompt)
const hasUsefulData = Boolean(block?.title || payload.title || payload.intro || hasPrompt || hasNegativePrompt || payload.aspect_ratio || criteria.length || results.length)
if (!hasUsefulData) return null
return (
AI Model Comparison
{payload.title || block.title || 'Same Prompt, Different AI Models'}
{payload.intro ?
{payload.intro}
: null}
{payload.aspect_ratio ?
Aspect ratio {payload.aspect_ratio}
: null}
{hasPrompt ? (
Prompt used
Shared source prompt across all compared models
{payload.prompt}
{hasNegativePrompt ? (
Negative prompt
{payload.negative_prompt}
) : null}
) : null}
{criteria.length ? (
What we compare
{criteria.map((criterion) => (
{criterion}
))}
) : null}
{results.length ? (
{results.map((result) => {
const imageUrl = result.thumb_url || result.image_url || result.thumb_path || result.image_path || ''
const score = Number(result.score || 0)
const hasScore = Number.isFinite(score) && score > 0
const altText = `${result.model_name || 'AI model'} by ${result.provider || 'unknown provider'} result for ${payload.prompt || 'comparison prompt'}`
return (
{imageUrl ? (
) : (
No comparison image provided.
)}
{result.model_name || result.provider || 'AI model'}
{result.provider ?
{result.provider}
: null}
{hasScore ?
{`Skinbase score ${score}/10`}
: null}
{result.settings ? (
Settings
{result.settings}
) : null}
{result.strengths ? (
Strengths
{result.strengths}
) : null}
{result.weaknesses ? (
Weaknesses
{result.weaknesses}
) : null}
{result.best_for ? (
Best for
{result.best_for}
) : null}
)
})}
) : null}
)
}
export default function AcademyShow({ pageType, item, relatedLessons = [], relatedCourses = [], previousLesson = null, nextLesson = null, seo, pricingUrl, completeUrl, completed: initialCompleted, saveUrl, unsaveUrl, saved: initialSaved, submitUrl, courseContext = null, interaction = null, interactionRoutes = null, loginUrl = null, analytics = null, progressRoutes = null }) {
const flash = usePage().props.flash || {}
useAcademyPageAnalytics(analytics)
const [completed, setCompleted] = useState(Boolean(initialCompleted))
const [saved, setSaved] = useState(Boolean(initialSaved))
const [liked, setLiked] = useState(Boolean(interaction?.liked))
const [likesCount, setLikesCount] = useState(Number(interaction?.likes_count || 0))
const [savesCount, setSavesCount] = useState(Number(interaction?.saves_count || 0))
const [tableOfContents, setTableOfContents] = useState([])
const [activeHeadingId, setActiveHeadingId] = useState('')
const [lightboxGallery, setLightboxGallery] = useState(null)
const articleContentRef = useRef(null)
const handledInitialHashRef = useRef(false)
const lessonCover = item?.cover_image_url || item?.cover_image || ''
const articleCover = item?.article_cover_image_url || item?.article_cover_image || ''
const lessonCategory = item?.category?.name || 'Academy'
const lessonSeries = String(item?.series_name || '').trim() || lessonCategory
const lessonDifficulty = item?.difficulty || 'Intermediate'
const lessonMinutes = formatLessonMinutes(item?.reading_minutes)
const lessonUpdated = formatLessonDate(item?.published_at)
const lessonBlocks = Array.isArray(item?.blocks) ? item.blocks : []
const relatedLessonList = Array.isArray(relatedLessons) ? relatedLessons : []
const relatedCourseList = Array.isArray(relatedCourses) ? relatedCourses : []
const courseOutline = Array.isArray(courseContext?.outline) ? courseContext.outline : []
const lessonSummary = item.excerpt || item.description || item.prompt_preview || item.content_preview || 'A focused Academy lesson with practical guidance and examples.'
const lessonTags = Array.isArray(item?.tags) ? item.tags.filter(Boolean) : []
const promptPreviewImage = item?.preview_image || ''
const promptPreviewThumbImage = item?.preview_image_thumb || promptPreviewImage
const promptPreviewSrcSet = item?.preview_image_srcset || ''
const promptBody = item?.prompt || item?.prompt_preview || ''
const promptDocumentation = normalizePromptDocumentation(item?.documentation)
const promptPlaceholders = Array.isArray(item?.placeholders)
? item.placeholders.filter((placeholder) => placeholder && typeof placeholder === 'object' && [
placeholder.key,
placeholder.label,
placeholder.description,
placeholder.example,
placeholder.default,
placeholder.type,
].some((value) => value != null && value !== '' && value !== false))
: []
const promptHelperPrompts = Array.isArray(item?.helper_prompts)
? item.helper_prompts.filter((helperPrompt) => helperPrompt && typeof helperPrompt === 'object' && [
helperPrompt.title,
helperPrompt.description,
helperPrompt.prompt,
helperPrompt.expected_output,
helperPrompt.type,
].some(Boolean))
: []
const promptVariants = Array.isArray(item?.prompt_variants)
? item.prompt_variants.filter((variant) => variant && typeof variant === 'object' && [
variant.title,
variant.description,
variant.prompt,
variant.negative_prompt,
variant.slug,
variant.recommended,
...(Array.isArray(variant.recommended_for) ? variant.recommended_for : []),
...(Array.isArray(variant.risk_notes) ? variant.risk_notes : []),
].some((value) => value != null && value !== '' && value !== false))
: []
const promptPublicExamples = Array.isArray(item?.public_examples)
? item.public_examples.filter((example) => example && typeof example === 'object' && [
example.title,
example.caption,
example.image_path,
example.image_url,
example.thumb_path,
example.thumb_url,
example.provider,
example.model_name,
example.score,
].some(Boolean))
: []
const promptComparisons = Array.isArray(item?.tool_notes)
? item.tool_notes.filter((note) => note && typeof note === 'object' && note.active !== false && [
note.display_type,
note.provider,
note.model_name,
note.notes,
note.strengths,
note.weaknesses,
note.best_for,
note.image_path,
note.image_url,
note.thumb_path,
note.thumb_url,
note.settings,
note.score,
].some(Boolean))
: []
const promptUsageNotes = String(item?.usage_notes || '').trim()
const promptWorkflowNotes = String(item?.workflow_notes || '').trim()
const promptHasFullAccess = Boolean(item?.prompt)
const hasPromptDocumentation = Boolean(
promptDocumentation.summary
|| promptDocumentation.display_notes
|| promptDocumentation.best_for.length
|| promptDocumentation.how_to_use.length
|| promptDocumentation.required_inputs.length
|| promptDocumentation.workflow.length
|| promptDocumentation.tips.length
|| promptDocumentation.common_mistakes.length
|| promptDocumentation.data_accuracy_notes.length,
)
const hasPromptPlaceholders = Boolean(item?.has_placeholder_inputs) && promptPlaceholders.length > 0
const promptFilledExamples = Array.isArray(item?.filled_examples)
? item.filled_examples.filter((example) => example && typeof example === 'object' && [
example.title,
example.description,
example.prompt,
example.negative_prompt,
...(example.placeholder_values && typeof example.placeholder_values === 'object' ? Object.values(example.placeholder_values) : []),
].some((value) => value != null && value !== '' && value !== false))
: []
const hasPromptFilledExamples = promptFilledExamples.length > 0
const promptFilledExamplesTotal = Number(item?.filled_examples_total || promptFilledExamples.length || 0)
const promptHasMoreFilledExamples = Boolean(item?.has_more_filled_examples) || promptFilledExamplesTotal > promptFilledExamples.length
const promptHasFullFilledExamplesAccess = Boolean(item?.has_full_filled_examples_access)
const promptHasLockedFilledExamples = Boolean(item?.has_filled_examples) && (!Boolean(item?.can_access_filled_examples) || promptHasMoreFilledExamples)
const promptHasLockedHelperPrompts = Boolean(item?.has_helper_prompts) && !promptHasFullAccess
const promptHasLockedVariants = Boolean(item?.has_prompt_variants) && !promptHasFullAccess
const hasPromptHelperPrompts = promptHelperPrompts.length > 0
const hasPromptVariants = promptVariants.length > 0
const showPromptHelperPrompts = true
const promptAccessRequirement = item?.access_requirement || promptRequirementText(item?.access_level)
const promptUnlockTitle = item?.unlock_heading || promptUnlockHeading(item?.access_level)
const promptUnlockDetails = item?.unlock_description || promptUnlockDescription(item?.access_level)
const promptFeaturedExamples = promptPreviewImage ? promptPublicExamples.slice(0, 2) : promptPublicExamples.slice(0, 4)
const promptOverflowExamples = promptPublicExamples.slice(promptFeaturedExamples.length)
const promptComparisonGalleryImages = promptComparisons
.map((note, index) => {
const src = note.image_url || note.thumb_url || ''
if (!src) return null
return {
src,
alt: note.model_name || note.provider || `Comparison ${index + 1}`,
}
})
.filter(Boolean)
const promptPublicExampleGalleryImages = [
...(promptPreviewImage ? [{ src: promptPreviewImage, alt: item?.title || 'Prompt preview' }] : []),
...promptPublicExamples
.map((example, index) => {
const src = example.image_url || example.thumb_url || ''
if (!src) return null
return {
src,
alt: example.alt || example.title || `Prompt example ${index + 1}`,
}
})
.filter(Boolean),
]
const promptBestUseCase = promptComparisons[0]?.best_for
|| promptDocumentation.best_for[0]
|| promptUsageNotes
|| lessonSummary
const academyBreadcrumbs = pageType === 'prompt'
? [
{ label: 'Academy', href: '/academy' },
{ label: 'Prompt Library', href: '/academy/prompts' },
{ label: item?.title || 'Prompt' },
]
: []
const fontScaleStorageKey = 'academy.lesson.font-scale'
const fontScaleMin = 0.95
const fontScaleMax = 1.12
const fontScaleStep = 0.04
const [lessonFontScale, setLessonFontScale] = useState(1.04)
const findArticleHeading = (headingId) => {
if (!headingId || typeof document === 'undefined') {
return null
}
const escapedHeadingId = typeof CSS !== 'undefined' && typeof CSS.escape === 'function'
? CSS.escape(headingId)
: String(headingId).replace(/[^a-zA-Z0-9_-]/g, '')
return articleContentRef.current?.querySelector(`#${escapedHeadingId}`) || document.getElementById(headingId)
}
const markComplete = () => {
if (!completeUrl || completed) return
router.post(completeUrl, courseContext?.completePayload || {}, {
preserveScroll: true,
onSuccess: () => setCompleted(true),
})
}
const requireLogin = () => {
if (loginUrl && typeof window !== 'undefined') {
window.location.href = loginUrl
}
}
const toggleLike = async () => {
if (!interactionRoutes?.like || !analytics?.contentType || !analytics?.contentId) {
return
}
if (analytics?.isGuest) {
requireLogin()
return
}
const payload = await postAcademyAction(interactionRoutes.like, {
content_type: analytics.contentType,
content_id: analytics.contentId,
})
if (payload?.liked !== undefined) {
setLiked(Boolean(payload.liked))
setLikesCount(Number(payload.likes_count || 0))
}
}
const toggleSave = async () => {
if (interactionRoutes?.save && analytics?.contentType && analytics?.contentId) {
if (analytics?.isGuest) {
requireLogin()
return
}
const payload = await postAcademyAction(interactionRoutes.save, {
content_type: analytics.contentType,
content_id: analytics.contentId,
})
if (payload?.saved !== undefined) {
setSaved(Boolean(payload.saved))
setSavesCount(Number(payload.saves_count || 0))
}
return
}
const url = saved ? unsaveUrl : saveUrl
if (!url) return
const method = saved ? router.delete : router.post
method(url, {}, {
preserveScroll: true,
onSuccess: () => setSaved(!saved),
})
}
useEffect(() => {
if (pageType !== 'lesson' || !progressRoutes?.startLesson || !item?.id || analytics?.isGuest || completed || typeof window === 'undefined') {
return
}
const onceKey = `academy-start-lesson:${item.id}:${courseContext?.id || 'solo'}`
if (window.sessionStorage.getItem(onceKey)) {
return
}
window.sessionStorage.setItem(onceKey, '1')
void postAcademyAction(progressRoutes.startLesson, {
lesson_id: item.id,
course_id: courseContext?.id || null,
})
}, [analytics?.isGuest, completed, courseContext?.id, item?.id, pageType, progressRoutes?.startLesson])
const decreaseFontSize = () => {
setLessonFontScale((current) => Math.max(fontScaleMin, Number((current - fontScaleStep).toFixed(2))))
}
const increaseFontSize = () => {
setLessonFontScale((current) => Math.min(fontScaleMax, Number((current + fontScaleStep).toFixed(2))))
}
const openPromptPreviewImage = () => {
if (!promptPreviewImage) return
setLightboxGallery({
images: [{ src: promptPreviewImage, alt: item?.title || 'Prompt preview' }],
index: 0,
})
}
const openPromptComparisonGallery = (index) => {
if (!promptComparisonGalleryImages.length) return
setLightboxGallery({
images: promptComparisonGalleryImages,
index: Math.max(0, Math.min(promptComparisonGalleryImages.length - 1, Number(index || 0))),
})
}
const openPromptExampleGallery = (index) => {
if (!promptPublicExampleGalleryImages.length) return
setLightboxGallery({
images: promptPublicExampleGalleryImages,
index: Math.max(0, Math.min(promptPublicExampleGalleryImages.length - 1, Number(index || 0))),
})
}
const navigateLightboxGallery = (direction) => {
setLightboxGallery((current) => {
if (!current?.images?.length) return current
const total = current.images.length
const nextIndex = typeof direction === 'number' && Math.abs(direction) > 1
? Math.max(0, Math.min(total - 1, current.index + direction))
: (current.index + direction + total) % total
return {
...current,
index: nextIndex,
}
})
}
const scrollToHeading = (headingId, behavior = 'smooth') => {
if (typeof window === 'undefined') {
return
}
const heading = findArticleHeading(headingId)
if (!heading) {
return
}
const top = Math.max(0, window.scrollY + heading.getBoundingClientRect().top - 112)
window.scrollTo({ top, behavior })
setActiveHeadingId(headingId)
if (window.history?.replaceState) {
window.history.replaceState(null, '', `${window.location.pathname}${window.location.search}#${headingId}`)
}
}
useEffect(() => {
handledInitialHashRef.current = false
}, [item?.slug])
useEffect(() => {
if (pageType !== 'lesson' || !item?.content || !articleContentRef.current) {
setTableOfContents([])
setActiveHeadingId('')
return
}
const headings = Array.from(articleContentRef.current.querySelectorAll('h2, h3'))
const seenIds = new Map()
const nextTableOfContents = headings.map((heading, index) => {
const baseId = slugifyHeading(heading.textContent, `section-${index + 1}`)
const seenCount = seenIds.get(baseId) ?? 0
const nextId = seenCount > 0 ? `${baseId}-${seenCount + 1}` : baseId
seenIds.set(baseId, seenCount + 1)
heading.id = nextId
heading.style.scrollMarginTop = '128px'
return {
id: nextId,
title: heading.textContent?.trim() || `Section ${index + 1}`,
level: heading.tagName.toLowerCase(),
}
})
setTableOfContents(nextTableOfContents)
}, [item?.content, pageType])
useEffect(() => {
if (pageType !== 'lesson' || tableOfContents.length === 0 || handledInitialHashRef.current || typeof window === 'undefined') {
return
}
const hash = window.location.hash.replace(/^#/, '').trim()
if (!hash) {
handledInitialHashRef.current = true
return
}
const matchingEntry = tableOfContents.find((entry) => entry.id === hash)
if (!matchingEntry) {
handledInitialHashRef.current = true
return
}
handledInitialHashRef.current = true
window.requestAnimationFrame(() => scrollToHeading(matchingEntry.id, 'auto'))
}, [pageType, tableOfContents])
useEffect(() => {
if (pageType !== 'lesson' || tableOfContents.length === 0 || typeof window === 'undefined') {
return undefined
}
const handleHashChange = () => {
const hash = window.location.hash.replace(/^#/, '').trim()
if (!hash) {
return
}
const matchingEntry = tableOfContents.find((entry) => entry.id === hash)
if (!matchingEntry) {
return
}
window.requestAnimationFrame(() => scrollToHeading(matchingEntry.id, 'auto'))
}
window.addEventListener('hashchange', handleHashChange)
return () => window.removeEventListener('hashchange', handleHashChange)
}, [pageType, tableOfContents])
useEffect(() => {
if (pageType !== 'lesson' || tableOfContents.length === 0 || !articleContentRef.current) {
setActiveHeadingId('')
return
}
const getActiveId = () => {
const headings = Array.from(articleContentRef.current.querySelectorAll('h2[id], h3[id]'))
if (!headings.length) return ''
// offset accounts for sticky header height + small buffer
const offset = 140
let activeId = headings[0].id
for (const heading of headings) {
if (heading.getBoundingClientRect().top <= offset) {
activeId = heading.id
}
}
return activeId
}
setActiveHeadingId(getActiveId())
const onScroll = () => setActiveHeadingId(getActiveId())
window.addEventListener('scroll', onScroll, { passive: true })
return () => window.removeEventListener('scroll', onScroll)
}, [pageType, tableOfContents, lessonFontScale])
useEffect(() => {
if (typeof window === 'undefined') {
return
}
const storedValue = Number(window.localStorage.getItem(fontScaleStorageKey))
if (!Number.isFinite(storedValue)) {
return
}
setLessonFontScale(Math.min(fontScaleMax, Math.max(fontScaleMin, storedValue)))
}, [fontScaleMax, fontScaleMin, fontScaleStorageKey])
useEffect(() => {
if (typeof window === 'undefined') {
return
}
window.localStorage.setItem(fontScaleStorageKey, String(lessonFontScale))
}, [lessonFontScale])
useEffect(() => {
if (pageType !== 'lesson' || !item?.content || !articleContentRef.current) {
return
}
const codeBlocks = Array.from(articleContentRef.current.querySelectorAll('pre code'))
if (!codeBlocks.length) {
return
}
const fallbackCopyText = (text) => {
const textarea = document.createElement('textarea')
textarea.value = text
textarea.setAttribute('readonly', 'true')
textarea.style.position = 'fixed'
textarea.style.top = '-1000px'
textarea.style.left = '-1000px'
document.body.appendChild(textarea)
textarea.select()
try {
return document.execCommand('copy')
} catch (_error) {
return false
} finally {
document.body.removeChild(textarea)
}
}
const copyText = (text) => {
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
return navigator.clipboard.writeText(text)
}
return fallbackCopyText(text)
? Promise.resolve()
: Promise.reject(new Error('Clipboard unavailable'))
}
codeBlocks.forEach((block) => {
const pre = block.parentElement
if (!pre || pre.dataset.academyCopyButtonMounted === 'true') {
return
}
const button = document.createElement('button')
const icon = document.createElement('span')
const label = document.createElement('span')
button.type = 'button'
button.className = 'story-code-copy-button academy-code-copy-button'
icon.className = 'story-code-copy-icon'
icon.setAttribute('aria-hidden', 'true')
icon.textContent = '⧉'
label.className = 'story-code-copy-label'
label.textContent = 'Copy'
button.appendChild(icon)
button.appendChild(label)
button.dataset.copied = 'idle'
button.setAttribute('aria-label', 'Copy code block')
let resetTimer = 0
button.addEventListener('click', () => {
const source = block.innerText || block.textContent || ''
copyText(source)
.then(() => {
icon.textContent = '✓'
label.textContent = 'Copied'
button.dataset.copied = 'true'
})
.catch(() => {
icon.textContent = '!'
label.textContent = 'Failed'
button.dataset.copied = 'false'
})
.finally(() => {
window.clearTimeout(resetTimer)
resetTimer = window.setTimeout(() => {
icon.textContent = '⧉'
label.textContent = 'Copy'
button.dataset.copied = 'idle'
}, 1800)
})
})
pre.appendChild(button)
pre.dataset.academyCopyButtonMounted = 'true'
})
}, [item?.content, lessonFontScale, pageType])
return (
{flash.success ?
{flash.success}
: null}
{flash.error ?
{flash.error}
: null}
{item.locked ?
trackUpgradeClick(analytics, { source: `${pageType}_locked_panel` })} /> : null}
{pageType === 'lesson' ? (
Skinbase AI Academy
Lesson
{lessonCategory}
{lessonDifficulty}
{item.lesson_label ?
{item.lesson_label}
: null}
{item.title}
{lessonSummary}
{lessonTags.length ? (
{lessonTags.map((tag) => (
{tag}
))}
) : null}
{courseContext?.title ? (
Part of course
{courseContext.title}
{courseContext.subtitle || 'This lesson is being viewed inside a structured Academy course path.'}
) : null}
{completeUrl ? {completed ? 'Completed' : 'Mark complete'} : null}
{liked ? `Liked · ${likesCount}` : `Like · ${likesCount}`}
{saved ? `Saved · ${savesCount}` : `Save · ${savesCount}`}
{submitUrl ? Submit artwork : null}
{lessonCover ?
:
Lesson cover
}
Lesson cover
{item.lesson_label || item.title}
{item.formatted_lesson_number ? : null}
Lesson status
{item.locked ? 'This lesson is partially locked for your account level.' : courseContext?.title ? 'This lesson is being tracked inside a course. Completion updates your course progress.' : 'Full lesson content is available below.'}
{lessonMinutes}
-
{Math.round(lessonFontScale * 100)}%
= fontScaleMax}
aria-label="Increase article text size"
className="inline-flex h-8 w-8 items-center justify-center rounded-full border border-white/10 bg-white/[0.04] text-sm font-semibold text-slate-200 transition hover:border-sky-300/30 hover:bg-sky-300/12 hover:text-sky-100 disabled:cursor-not-allowed disabled:opacity-40"
>
+
{articleCover ? (
) : null}
{item.content ? (
{lessonBlocks.map((block) =>
)}
) : (
{item.content_preview}
{lessonBlocks.map((block) =>
)}
)}
{(previousLesson || nextLesson) ? (
{courseContext?.title ? 'Course navigation' : 'Lesson navigation'}
{courseContext?.title ? 'Continue this course' : 'Continue in order'}
) : null}
{tableOfContents.length ? (
) : null}
{courseContext?.title ? 'Course progress' : 'Series info'}
{courseContext?.title ? courseContext.title : lessonSeries}
{item.formatted_lesson_number ? : null}
{courseOutline.length ? (
Course outline
{courseOutline.map((outlineLesson, index) => (
{String(index + 1).padStart(2, '0')}
{outlineLesson.title}
{outlineLesson.is_required ? 'Required' : 'Optional'}
))}
) : null}
{lessonTags.length ? (
Microtags
{lessonTags.map((tag) => (
{tag}
))}
) : null}
{relatedLessonList.length ? (
Continue learning
More in {lessonSeries}
{relatedLessonList.map((relatedLesson, index) => (
{String(index + 1).padStart(2, '0')}
{relatedLesson.formatted_lesson_number ?
{relatedLesson.formatted_lesson_number}
: null}
{relatedLesson.title}
{formatLessonMinutes(relatedLesson.reading_minutes)}
{relatedLesson.excerpt || relatedLesson.content_preview || 'Continue the series with the next lesson.'}
))}
) : null}
{relatedCourseList.length ? (
Related courses
{relatedCourseList.map((course) => (
{course.difficulty} · {course.access_level}
{course.title}
{course.excerpt || course.description || 'Open this course to continue with a guided path.'}
))}
) : null}
) : pageType === 'prompt' ? (
{academyBreadcrumbs.length ? (
) : null}
Skinbase AI Academy
Prompt Library
{lessonCategory}
{lessonDifficulty}
{item.aspect_ratio ? {item.aspect_ratio} : null}
{item.prompt_of_week ? Prompt of the week : null}
{item.featured ? Featured : null}
Prompt template
{item.title}
{lessonSummary}
{liked ? `Liked · ${likesCount}` : `Like · ${likesCount}`}
{saved ? `Saved · ${savesCount}` : `Save · ${savesCount}`}
{promptHasFullAccess ?
: null}
{promptHasFullAccess && item.negative_prompt ?
: null}
Preview artwork
{promptPreviewImage ?
Click to zoom : null}
{promptPreviewImage ? (
Prompt visual
Open full-size preview
) : (
Visual placeholder
Preview image coming soon
This prompt page will feel much better once the generated cover image is attached.
)}
{!promptHasFullAccess && (promptPreviewImage || promptPublicExamples.length) ? (
Public examples
Example results from this prompt
Preview the visual direction before unlocking the full prompt.
{item.locked && promptAccessRequirement ?
{promptAccessRequirement} : null}
{promptPreviewImage ? (
openPromptExampleGallery(0)}
className="group overflow-hidden rounded-[28px] border border-white/10 bg-slate-950 text-left shadow-[0_18px_50px_rgba(2,6,23,0.22)] transition hover:border-sky-300/25"
aria-label="Open main prompt preview"
>
Preview artwork
Prompt visual
{item?.excerpt || 'Studio-ready packaging, pose, and finish.'}
) : null}
{promptFeaturedExamples.length ? (
{promptFeaturedExamples.map((example, index) => (
))}
) : null}
{promptOverflowExamples.length ? (
{promptOverflowExamples.map((example, index) => (
))}
) : null}
) : null}
Prompt body
Prompt text and exclusions
{promptHasFullAccess ? 'Full prompt' : 'Preview prompt'}
{promptHasFullAccess ? 'Ready to paste into your generation workflow.' : 'Upgrade your Academy access to reveal the complete prompt text.'}
{promptBody ? (
) : null}
{promptBody || 'Prompt text is not available yet.'}
{!promptHasFullAccess ? (
{promptUnlockTitle || 'Unlock the full prompt'}
{promptUnlockDetails}
{promptAccessRequirement ?
{promptAccessRequirement}
: null}
) : null}
{item.negative_prompt ? (
) : null}
{(promptUsageNotes || promptWorkflowNotes) ? (
Prompt guidance
How to use this prompt
{!promptHasFullAccess ?
Full notes visible with access : null}
{promptUsageNotes ? (
Usage notes
{promptUsageNotes}
) : null}
{promptWorkflowNotes ? (
Workflow notes
{promptWorkflowNotes}
) : null}
) : null}
{hasPromptDocumentation ?
: null}
{hasPromptPlaceholders ? (
Data
Placeholders and required inputs
Prepare these variables before using the final prompt so the output stays consistent and reusable.
{promptPlaceholders.map((placeholder, index) => (
))}
) : null}
{hasPromptFilledExamples ? (
Filled examples
{promptFilledExamplesTotal > 0 ? `${promptFilledExamplesTotal} ready-made prompt runs for real user inputs` : 'Ready-made prompt runs for real user inputs'}
{promptHasMoreFilledExamples
? `You can view ${promptFilledExamples.length} example${promptFilledExamples.length === 1 ? '' : 's'} right now. Upgrade to Pro to unlock all ${promptFilledExamplesTotal} filled prompt runs and copy a closer starting point instead of filling everything from scratch.`
: 'These examples show how the prompt looks after swapping real placeholder values, so you can copy a closer starting point instead of filling everything from scratch.'}
) : null}
{promptHasLockedFilledExamples ? (
Filled examples
{promptHasMoreFilledExamples && hasPromptFilledExamples
? `${Math.max(promptFilledExamplesTotal - promptFilledExamples.length, 0)} more filled prompt example${promptFilledExamplesTotal - promptFilledExamples.length === 1 ? '' : 's'} are available`
: `${promptFilledExamplesTotal || 5} filled prompt examples are included`}
{promptHasMoreFilledExamples && hasPromptFilledExamples
? 'Creator access includes a smaller set here. Upgrade to Academy Pro to unlock the remaining filled prompt runs.'
: 'This prompt ships with ready-made filled examples for different user inputs, but they unlock only for Academy Pro members.'}
trackUpgradeClick(analytics, { source: 'prompt_filled_examples_locked_panel' })} />
) : null}
{showPromptHelperPrompts && hasPromptHelperPrompts ? (
Data helpers
Helper prompts for preparation and validation
Use these supporting prompts before or after the main prompt when you need better source data, cleaner structure, or a validation pass.
{promptHelperPrompts.map((helperPrompt, index) => (
))}
) : null}
{showPromptHelperPrompts && promptHasLockedHelperPrompts ? (
Data helpers
Helper prompts are included with this template
Data collection, validation, or refinement prompts are available once your Academy access matches this template.
trackUpgradeClick(analytics, { source: 'prompt_helper_locked_panel' })} />
) : null}
{hasPromptVariants ?
: null}
{promptHasLockedVariants ? (
Variants
Alternative prompt versions are included
This prompt includes recommended or model-specific variants, but they stay locked until your Academy access level matches the template.
trackUpgradeClick(analytics, { source: 'prompt_variant_locked_panel' })} />
) : null}
{promptComparisons.length ? (
AI model comparisons
How different models respond to the same prompt
Use these notes to decide which provider fits the result you want before you start tuning or post-processing.
{promptComparisons.map((note, index) =>
)}
) : null}
) : (
{pageType === 'pack' ? (
{item.description}
{(item.prompts || []).map((prompt) => (
{prompt.title}
{prompt.excerpt || prompt.prompt_preview}
))}
) : null}
{pageType === 'challenge' ? (
Brief
{item.brief || item.description}
Rules
{item.rules || 'No special rules posted yet.'}
{(item.submissions || []).length ? (
Approved submissions
{item.submissions.map((submission) => (
{submission.artwork?.title || 'Submission'}
{submission.user?.name || 'Unknown creator'}
))}
) : null}
) : null}
)}
setLightboxGallery(null)} onNavigate={navigateLightboxGallery} />
)
}