(.*?)<\/title>/is', $html, $matches) === 1) {
return trim(strip_tags($matches[1]));
}
return '';
}
/**
* Fetch one landscape-ish photo from Pexels for the given query.
*
* @return array{url:string,alt:string,photographer:string,photoUrl:string}|null
*/
function fetchPexelsPhoto(string $pexelsKey, string $query): ?array
{
if ($pexelsKey === '' || $query === '') {
return null;
}
$endpoint = 'https://api.pexels.com/v1/search?query=' . rawurlencode($query) . '&per_page=1&orientation=landscape&size=large';
$raw = false;
if (function_exists('curl_init')) {
$ch = curl_init($endpoint);
if ($ch !== false) {
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: ' . $pexelsKey,
'Accept: application/json',
],
CURLOPT_TIMEOUT => 12,
]);
$raw = curl_exec($ch);
curl_close($ch);
}
}
if ($raw === false) {
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => "Authorization: {$pexelsKey}\r\nAccept: application/json\r\n",
'timeout' => 12,
],
]);
$raw = @file_get_contents($endpoint, false, $context);
}
if ($raw === false) {
return null;
}
$json = json_decode($raw, true);
if (! is_array($json) || ! isset($json['photos'][0]) || ! is_array($json['photos'][0])) {
return null;
}
$photo = $json['photos'][0];
$src = $photo['src'] ?? [];
$url = (string) ($src['landscape'] ?? $src['large'] ?? $src['original'] ?? '');
if ($url === '') {
return null;
}
return [
'url' => $url,
'alt' => trim((string) ($photo['alt'] ?? 'Hero image')),
'photographer' => trim((string) ($photo['photographer'] ?? 'Pexels photographer')),
'photoUrl' => trim((string) ($photo['url'] ?? 'https://www.pexels.com/')),
];
}
/**
* Replace hero placeholders with Pexels image metadata.
*/
function injectHeroImagePlaceholders(string $html, ?array $photo): string
{
$fallbackSvg = rawurlencode('');
$fallbackUrl = 'data:image/svg+xml;utf8,' . $fallbackSvg;
$imageUrl = $photo['url'] ?? $fallbackUrl;
$imageAlt = $photo['alt'] ?? 'Hero image';
$photographer = $photo['photographer'] ?? 'Pexels';
$photoUrl = $photo['photoUrl'] ?? 'https://www.pexels.com/';
$replacements = [
'__PEXELS_HERO_IMAGE__' => htmlspecialchars((string) $imageUrl, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'),
'__PEXELS_HERO_ALT__' => htmlspecialchars((string) $imageAlt, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'),
'__PEXELS_PHOTOGRAPHER__' => htmlspecialchars((string) $photographer, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'),
'__PEXELS_PHOTO_URL__' => htmlspecialchars((string) $photoUrl, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'),
];
return strtr($html, $replacements);
}
/**
* Render a JPG preview for a generated page using a local headless browser.
*/
function renderPagePreview(string $seed, string $htmlFilePath): void
{
$previewDir = __DIR__ . '/previews';
if (! is_dir($previewDir)) {
mkdir($previewDir, 0775, true);
}
$realHtmlPath = realpath($htmlFilePath);
if ($realHtmlPath === false) {
return;
}
$pageUrl = 'file://' . $realHtmlPath;
$previewPngPath = $previewDir . '/' . $seed . '.png';
$previewJpgPath = $previewDir . '/' . $seed . '.jpg';
$browserCandidates = [
'/usr/bin/google-chrome',
'/usr/bin/google-chrome-stable',
'/usr/bin/chromium',
'/usr/bin/chromium-browser',
'google-chrome',
'google-chrome-stable',
'chromium',
'chromium-browser',
];
$browserBinary = null;
foreach ($browserCandidates as $candidate) {
$candidatePath = null;
if (str_starts_with($candidate, '/')) {
if (! is_executable($candidate)) {
continue;
}
$candidatePath = $candidate;
} else {
$pathOut = [];
$pathExitCode = 1;
exec('command -v ' . escapeshellarg($candidate) . ' 2>/dev/null', $pathOut, $pathExitCode);
if ($pathExitCode !== 0 || ! isset($pathOut[0]) || $pathOut[0] === '') {
continue;
}
$candidatePath = $pathOut[0];
}
$versionOut = [];
$versionExitCode = 1;
exec(escapeshellarg($candidatePath) . ' --version 2>/dev/null', $versionOut, $versionExitCode);
if ($versionExitCode === 0) {
$browserBinary = $candidatePath;
break;
}
}
if ($browserBinary === null) {
return;
}
$command = sprintf(
'%s --headless --disable-gpu --no-sandbox --disable-dev-shm-usage --hide-scrollbars --window-size=1440,900 --screenshot=%s --virtual-time-budget=8000 --run-all-compositor-stages-before-draw %s 2>/dev/null',
escapeshellarg($browserBinary),
escapeshellarg($previewPngPath),
escapeshellarg($pageUrl)
);
$output = [];
$exitCode = 1;
exec($command, $output, $exitCode);
if ($exitCode !== 0 || ! file_exists($previewPngPath)) {
@unlink($previewPngPath);
return;
}
if (function_exists('imagecreatefrompng') && function_exists('imagejpeg')) {
$image = @imagecreatefrompng($previewPngPath);
if ($image !== false) {
imagejpeg($image, $previewJpgPath, 85);
imagedestroy($image);
}
}
if (file_exists($previewJpgPath)) {
@unlink($previewPngPath);
} else {
// Keep PNG as fallback if JPG conversion is unavailable.
@rename($previewPngPath, $previewJpgPath);
}
}
header('Content-Type: text/html; charset=UTF-8');
$startedAt = microtime(true);
$pexelsKey = 'Clyqp9i9pewaxiiAdqVya0yZuuedjHtUtJdjRipNltp7wf0dY8K10h4h';
$apiKey = getenv('LLAMA_API_KEY') ?: '';
$model = getenv('LLAMA_MODEL') ?: 'local-model';
$maxTokens = (int) (getenv('LLAMA_MAX_TOKENS') ?: 4096);
$reasoningEffort = getenv('LLAMA_REASONING_EFFORT') ?: 'none';
$topicHint = isset($_GET['topic']) ? trim((string) $_GET['topic']) : '';
$topicHint = mb_substr($topicHint, 0, 120);
$seed = sprintf('seed-%08x%08x', random_int(0, 0xffffffff), random_int(0, 0xffffffff));
header('HX-Redirect: /' . $seed);
$topicCategories = [
'local restaurant landing page',
'fitness coaching brand site',
'travel destination mini-guide',
'event festival one-page promo',
'indie game launch page',
'architect portfolio page',
'music artist release page',
'bookstore seasonal campaign',
'pet adoption center homepage',
'artisan coffee roaster website',
'online course product page',
'nonprofit donation campaign page',
];
$selectedCategory = $topicCategories[array_rand($topicCategories)];
$bannedTerms = [
'chrono',
'temporal',
'chrono-',
'timewarp',
'timescape',
'timeshift',
'quantum',
'aether',
'epoch',
'singularity',
];
$bannedTermsList = implode(', ', $bannedTerms);
$factory = OpenAI::factory()->withBaseUri('http://localhost:8080/v1');
if ($apiKey !== '') {
$factory = $factory->withApiKey($apiKey);
}
$client = $factory->make();
$systemPrompt = << and include full , , and .
- Use Tailwind CSS via CDN in the same HTML document.
- Do not rely on local/external project files. Everything required must be inside this single HTML file.
- Inline any custom CSS and JavaScript in