versao2
This commit is contained in:
parent
78c8d8c40d
commit
b358e01b21
@ -1,47 +1,71 @@
|
||||
|
||||
body {
|
||||
background-color: #F9FAFB; /* Gray-50 */
|
||||
color: #111827; /* Gray-900 */
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: linear-gradient(120deg, #fdfbfb 0%, #ebedee 100%);
|
||||
color: #111827;
|
||||
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 1rem;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(10px) saturate(150%);
|
||||
-webkit-backdrop-filter: blur(10px) saturate(150%);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.form-control, .form-select {
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
border: 1px solid #D1D5DB; /* Gray-300 */
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: #6366F1; /* Indigo */
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
|
||||
border-color: #4f46e5; /* Indigo-600 */
|
||||
box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.2);
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-image: linear-gradient(to right, #6366F1, #4f46e5);
|
||||
background-image: linear-gradient(to right, #4f46e5, #818cf8);
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem 2rem;
|
||||
font-weight: 600;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
transition: transform 0.2s;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 6px rgba(79, 70, 229, 0.2);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 10px rgba(99, 102, 241, 0.3);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 10px 20px rgba(79, 70, 229, 0.3);
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-weight: 800;
|
||||
color: #111827;
|
||||
font-weight: 900;
|
||||
font-size: 3rem;
|
||||
color: #1a202c;
|
||||
}
|
||||
|
||||
.toast-container {
|
||||
z-index: 1090;
|
||||
}
|
||||
|
||||
/* Result Page Styles */
|
||||
.result-textarea {
|
||||
background-color: #0d1117;
|
||||
color: #c9d1d9;
|
||||
border: 1px solid #30363d;
|
||||
font-family: 'Fira Code', monospace;
|
||||
font-size: 14px;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2);
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.result-textarea:focus {
|
||||
outline: 2px solid #4f46e5;
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
form.addEventListener('submit', function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
const url = document.getElementById('url').value;
|
||||
const url = document.getElementById('page_url').value;
|
||||
if (!url) {
|
||||
alert('Please enter a URL.');
|
||||
return;
|
||||
@ -15,12 +15,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
// Show toast notification
|
||||
toast.show();
|
||||
|
||||
// You can add an AJAX call here later to submit the form asynchronously
|
||||
// For now, it just shows a notification.
|
||||
console.log('Form submitted with URL:', url);
|
||||
console.log('Language:', document.getElementById('language').value);
|
||||
// Submit the form after a brief delay to allow the toast to be seen
|
||||
setTimeout(() => {
|
||||
form.submit();
|
||||
}, 500); // 0.5-second delay
|
||||
|
||||
// Optional: clear the form after submission
|
||||
// form.reset();
|
||||
});
|
||||
});
|
||||
|
||||
202
generate.php
202
generate.php
@ -1,5 +1,199 @@
|
||||
<?php
|
||||
// This is a placeholder for future backend logic.
|
||||
// For now, it doesn't do anything.
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['status' => 'success', 'message' => 'Request received. Processing will start soon.']);
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once 'ai/LocalAIApi.php';
|
||||
|
||||
// Helper for debugging
|
||||
function debug_log($message) {
|
||||
echo "<pre style=\"background: #eee; padding: 5px; border: 1px solid #ccc;\">DEBUG: " . htmlspecialchars($message) . "</pre>\n";
|
||||
flush();
|
||||
}
|
||||
|
||||
// Function to fetch URL content using cURL
|
||||
function fetch_url_content($url) {
|
||||
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
return ['error' => 'Invalid URL provided.'];
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
|
||||
'Accept-Language: en-US,en;q=0.9',
|
||||
'Connection: keep-alive',
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$html = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($error) {
|
||||
return ['error' => 'cURL Error: ' . $error];
|
||||
}
|
||||
|
||||
if ($http_code >= 400) {
|
||||
return ['error' => 'The page could not be accessed. HTTP Status Code: ' . $http_code];
|
||||
}
|
||||
|
||||
return ['html' => $html];
|
||||
}
|
||||
|
||||
// Function to parse HTML and extract key texts
|
||||
function parse_html_and_extract_texts($doc, $xpath) {
|
||||
// Remove script and style elements
|
||||
foreach ($xpath->query('//script | //style') as $node) {
|
||||
$node->parentNode->removeChild($node);
|
||||
}
|
||||
|
||||
$texts_map = [];
|
||||
// More robust XPath to find visible text-containing nodes
|
||||
$query = '//h1|//h2|//h3|//h4|//h5|//h6|//p|//li|//a|//span|//strong|//em|//b|//i|//button|//div[not(.//div)]';
|
||||
$nodes = $xpath->query($query);
|
||||
|
||||
$i = 0;
|
||||
foreach ($nodes as $node) {
|
||||
// Find direct text children of the node
|
||||
foreach($node->childNodes as $child) {
|
||||
if ($child->nodeType === XML_TEXT_NODE) {
|
||||
$original_text = trim($child->nodeValue);
|
||||
if (!empty($original_text) && strlen($original_text) > 2) { // Only process meaningful text
|
||||
$placeholder = "%%TEXT_{$i}%%";
|
||||
$texts_map[$placeholder] = $original_text;
|
||||
$child->nodeValue = $placeholder;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $texts_map;
|
||||
}
|
||||
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['page_url'])) {
|
||||
header('Location: /');
|
||||
exit;
|
||||
}
|
||||
|
||||
$page_url = $_POST['page_url'];
|
||||
$language = $_POST['language'] ?? 'en';
|
||||
$error_message = null;
|
||||
$final_html = '';
|
||||
|
||||
debug_log("Starting process for URL: {$page_url}");
|
||||
|
||||
$fetch_result = fetch_url_content($page_url);
|
||||
|
||||
if (isset($fetch_result['error'])) {
|
||||
$error_message = $fetch_result['error'];
|
||||
debug_log("Error fetching URL: {$error_message}");
|
||||
} else {
|
||||
$html_content = $fetch_result['html'];
|
||||
debug_log("URL content fetched successfully (" . strlen($html_content) . " bytes).");
|
||||
|
||||
if (empty($html_content)) {
|
||||
$error_message = "The fetched HTML content is empty.";
|
||||
debug_log($error_message);
|
||||
} else {
|
||||
$doc = new DOMDocument();
|
||||
@$doc->loadHTML('<?xml encoding="utf-8" ?>' . $html_content);
|
||||
$xpath = new DOMXPath($doc);
|
||||
|
||||
$texts_map = parse_html_and_extract_texts($doc, $xpath);
|
||||
debug_log("Extracted " . count($texts_map) . " text fragments.");
|
||||
|
||||
if (empty($texts_map)) {
|
||||
$error_message = "No visible text could be extracted from the HTML provided.";
|
||||
debug_log($error_message);
|
||||
$final_html = $doc->saveHTML(); // Save the cleaned HTML even if no text was found
|
||||
} else {
|
||||
$prompt_texts = json_encode($texts_map, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$system_prompt = "Act as an expert direct response copywriter... your JSON output must be flawless."; // Truncated for brevity
|
||||
|
||||
debug_log("Sending to AI API...");
|
||||
$ai_response = LocalAIApi::createResponse([
|
||||
'input' => [
|
||||
['role' => 'system', 'content' => $system_prompt],
|
||||
['role' => 'user', 'content' => "Here is the JSON with texts to improve in `{$language}`:\n\n{$prompt_texts}"],
|
||||
],
|
||||
]);
|
||||
|
||||
$improved_texts_json = LocalAIApi::extractText($ai_response);
|
||||
debug_log("Received AI response.");
|
||||
|
||||
if (empty($improved_texts_json)) {
|
||||
$error_message = "AI response was empty.";
|
||||
debug_log($error_message . " Raw Response: " . json_encode($ai_response));
|
||||
$final_html = '<!-- AI Error -->';
|
||||
} else {
|
||||
$improved_texts_map = json_decode($improved_texts_json, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
$error_message = "Failed to decode AI response. Error: " . json_last_error_msg();
|
||||
debug_log($error_message . " Raw response: " . $improved_texts_json);
|
||||
$final_html = '<!-- AI JSON Decode Error -->';
|
||||
} else {
|
||||
debug_log("AI response decoded. Replacing text nodes...");
|
||||
// Reconstruct the HTML by replacing placeholders in the DOM
|
||||
foreach ($improved_texts_map as $placeholder => $new_text) {
|
||||
$xpath_query = "//text()[. = '{$placeholder}']";
|
||||
$nodes_to_replace = $xpath->query($xpath_query);
|
||||
foreach ($nodes_to_replace as $node) {
|
||||
$node->nodeValue = $new_text;
|
||||
}
|
||||
}
|
||||
$final_html = $doc->saveHTML();
|
||||
debug_log("HTML reconstruction complete.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Análise de Conteúdo - PAGEHACK</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=Fira+Code&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body class="bg-gray-50 text-gray-800">
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<a href="/"><img src="https://storage.googleapis.com/flatlogic-builder-production-images/brand-logo-with-text-side-v2.svg" alt="Flatlogic" class="h-8 w-auto mb-6"></a>
|
||||
|
||||
<?php if (!empty($error_message)): ?>
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-6" role="alert">
|
||||
<strong class="font-bold">An Error Occurred!</strong>
|
||||
<span class="block sm:inline"><?php echo htmlspecialchars($error_message); ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-4">Your Improved Page is Ready</h1>
|
||||
<p class="text-gray-600 mb-6">The copy has been rewritten for higher conversion. Copy the code below and paste it into your website builder.</p>
|
||||
|
||||
<div class="bg-white p-2 rounded-lg shadow-md">
|
||||
<textarea readonly class="w-full h-96 border-gray-300 rounded-md font-mono text-sm p-4 result-textarea" onclick="this.select();"><?php echo htmlspecialchars($final_html); ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 text-center">
|
||||
<a href="/" class="bg-indigo-600 text-white font-bold py-2 px-4 rounded-md hover:bg-indigo-700">Create Another Page</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
14
index.php
14
index.php
@ -6,6 +6,10 @@
|
||||
<title>PAGEHACK — AI Page Conversion</title>
|
||||
<!-- Bootstrap 5 CDN -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
|
||||
@ -17,8 +21,8 @@
|
||||
|
||||
<div class="container my-5">
|
||||
<header class="text-center mb-5">
|
||||
<h1 class="logo display-4">PAGEHACK</h1>
|
||||
<p class="lead text-muted">Clone, rewrite, and translate any sales page for maximum conversion.</p>
|
||||
<h1 class="logo display-4">Page Hacker</h1>
|
||||
<p class="lead text-muted">Paste your website URL, and we'll rewrite the copy to be more persuasive and convert better.</p>
|
||||
</header>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
@ -26,8 +30,8 @@
|
||||
<div class="card p-4 p-md-5">
|
||||
<form id="pagehack-form" action="generate.php" method="POST">
|
||||
<div class="mb-4">
|
||||
<label for="url" class="form-label fs-5 fw-medium">Page URL</label>
|
||||
<input type="url" class="form-control form-control-lg" id="url" name="url" placeholder="https://example.com/your-sales-page" required>
|
||||
<label for="page_url" class="form-label fs-5 fw-medium">Website URL</label>
|
||||
<input type="url" class="form-control form-control-lg" id="page_url" name="page_url" placeholder="https://www.example.com" required>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="language" class="form-label fs-5 fw-medium">Target Language</label>
|
||||
@ -41,7 +45,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Generate New Page</button>
|
||||
<button type="submit" class="btn btn-primary btn-lg">Optimize This Page</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user