import React, { useEffect, useRef, useState } from 'react'
import { Link, router, usePage } from '@inertiajs/react'
import SeoHead from '../../components/seo/SeoHead'
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 StatPill({ label, value }) {
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 }) {
return (
Premium content
Unlock the full {label}.
This preview is visible, but the full Academy content stays server-side until your account has the required Creator or Pro access.
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' }) {
const [status, setStatus] = useState('idle')
const resetTimerRef = useRef(0)
return (
{
copyTextToClipboard(prompt)
.then(() => setStatus('copied'))
.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 title = note.model_name || note.provider || `Comparison ${String(index + 1).padStart(2, '0')}`
const subtitle = [note.provider, note.model_name].filter(Boolean).join(' · ')
const previewUrl = note.image_url || note.thumb_url || ''
const hasContent = Boolean(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}
AI comparison
{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 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 }) {
const flash = usePage().props.flash || {}
const [completed, setCompleted] = useState(Boolean(initialCompleted))
const [saved, setSaved] = useState(Boolean(initialSaved))
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 promptBody = item?.prompt || item?.prompt_preview || ''
const promptComparisons = Array.isArray(item?.tool_notes)
? item.tool_notes.filter((note) => note && typeof note === 'object' && note.active !== false && [
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 promptModelsCovered = promptComparisons.map((note, index) => note.model_name || note.provider || `Model ${index + 1}`)
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 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 toggleSave = () => {
const url = saved ? unsaveUrl : saveUrl
const method = saved ? router.delete : router.post
method(url, {}, {
preserveScroll: true,
onSuccess: () => setSaved(!saved),
})
}
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 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 ?
: null}
{pageType === 'lesson' ? (
{lessonCover ?
: null}
Skinbase AI Academy
{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}
{saveUrl ? {saved ? 'Saved' : 'Save prompt'} : null}
{submitUrl ? Submit artwork : null}
{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' ? (
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.
)}
{academyBreadcrumbs.length ? (
) : null}
Skinbase AI Academy
{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}
{saveUrl ?
{saved ? 'Saved' : 'Save prompt'} : null}
{promptBody ?
: null}
{item.negative_prompt ?
: null}
{lessonTags.length ? (
Microtags
{lessonTags.map((tag) => (
{tag}
))}
) : null}
Prompt status
{item.locked
? 'This page shows the prompt summary, but the full prompt text and editor notes stay locked until your Academy access level matches the template.'
: 'This template includes the main prompt, reuse guidance, and model-specific comparison notes in one place.'}
{promptModelsCovered.length ? (
Compared with
{promptModelsCovered.length} model{promptModelsCovered.length > 1 ? 's' : ''} documented for this prompt.
{promptModelsCovered.length}
{promptModelsCovered.map((model) => (
{model}
))}
) : 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 || 'Prompt text is not available yet.'}
{item.negative_prompt ? (
Negative prompt
{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}
{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} />
)
}