From 029d7a232a7fd9e9bf51aa0c845d5a0dd68d232b Mon Sep 17 00:00:00 2001 From: igor Date: Mon, 15 Jun 2026 04:42:54 +0200 Subject: [PATCH] implemented step 17 by Gemini - Rendering CSS assets --- src/Services/Renderer.php | 83 +++++++++++++++++++++++++-- src/Templates/base.php | 10 +++- src/Templates/css/site.css | 111 +++++++++++++++++++++++++++---------- 3 files changed, 170 insertions(+), 34 deletions(-) diff --git a/src/Services/Renderer.php b/src/Services/Renderer.php index 5d03445..dd96528 100644 --- a/src/Services/Renderer.php +++ b/src/Services/Renderer.php @@ -14,6 +14,45 @@ class Renderer private string $exportsPath; private string $templatesPath; + private array $palettes = [ + 'blue-gray' => [ + 'primary' => '#2563eb', + 'secondary' => '#64748b', + 'bg' => '#f8fafc', + 'text' => '#1e293b' + ], + 'nature-green' => [ + 'primary' => '#059669', + 'secondary' => '#fbbf24', + 'bg' => '#fdfdfd', + 'text' => '#064e3b' + ], + 'elegant-gold' => [ + 'primary' => '#d4af37', + 'secondary' => '#111827', + 'bg' => '#ffffff', + 'text' => '#111827' + ] + ]; + + private array $styles = [ + 'minimal' => [ + 'radius' => '0px', + 'shadow' => 'none', + 'padding' => '4rem 1rem' + ], + 'modern' => [ + 'radius' => '0.5rem', + 'shadow' => '0 4px 6px -1px rgb(0 0 0 / 0.1)', + 'padding' => '6rem 1.5rem' + ], + 'premium' => [ + 'radius' => '1.5rem', + 'shadow' => '0 10px 15px -3px rgb(0 0 0 / 0.1)', + 'padding' => '8rem 2rem' + ] + ]; + public function __construct() { $this->storage = new FileStorage(); @@ -43,7 +82,17 @@ class Renderer $siteCssSource = $this->templatesPath . DIRECTORY_SEPARATOR . 'css' . DIRECTORY_SEPARATOR . 'site.css'; copy($siteCssSource, $assetsDir . DIRECTORY_SEPARATOR . 'site.css'); - // 3. Render Content Sections + // 3. Prepare relative paths for assets + if (!empty($projectData['wizard_data']['assets']['logo'])) { + $projectData['wizard_data']['assets']['logo'] = $this->makePathRelative($projectData['wizard_data']['assets']['logo'], $projectId); + } + if (!empty($projectData['wizard_data']['assets']['gallery'])) { + foreach ($projectData['wizard_data']['assets']['gallery'] as $key => $path) { + $projectData['wizard_data']['assets']['gallery'][$key] = $this->makePathRelative($path, $projectId); + } + } + + // 4. Render Content Sections $sectionsHtml = ''; $selectedSections = $projectData['wizard_data']['modules']['sections'] ?? []; @@ -57,16 +106,42 @@ class Renderer } } - // 4. Render HTML base template + // 4. Prepare Visual Variables + $visuals = $projectData['wizard_data']['visuals'] ?? []; + $palette = $this->palettes[$visuals['palette'] ?? 'blue-gray'] ?? $this->palettes['blue-gray']; + $style = $this->styles[$visuals['style'] ?? 'modern'] ?? $this->styles['modern']; + + // 5. Render HTML base template $html = $this->renderTemplate('base', [ 'project' => $projectData, - 'content' => $sectionsHtml + 'content' => $sectionsHtml, + 'css_vars' => [ + 'primary' => $palette['primary'], + 'secondary' => $palette['secondary'], + 'bg' => $palette['bg'], + 'text' => $palette['text'], + 'radius' => $style['radius'], + 'shadow' => $style['shadow'], + 'padding' => $style['padding'] + ] ]); - // 5. Save to exports + // 6. Save to exports return file_put_contents($projectExportDir . DIRECTORY_SEPARATOR . 'index.html', $html) !== false; } + /** + * Makes an absolute or project-root relative path relative to the project's export directory. + */ + private function makePathRelative(string $path, string $projectId): string + { + $prefix = "exports/{$projectId}/"; + if (strpos($path, $prefix) === 0) { + return substr($path, strlen($prefix)); + } + return $path; + } + /** * Internal helper to render a PHP template with variables. */ diff --git a/src/Templates/base.php b/src/Templates/base.php index 0783f84..674a7a3 100644 --- a/src/Templates/base.php +++ b/src/Templates/base.php @@ -28,8 +28,14 @@ $assets = $project['wizard_data']['assets'] ?? []; diff --git a/src/Templates/css/site.css b/src/Templates/css/site.css index 05bc765..a397bef 100644 --- a/src/Templates/css/site.css +++ b/src/Templates/css/site.css @@ -1,8 +1,11 @@ /* Reset & Base Styles */ :root { --primary-color: #2563eb; + --secondary-color: #64748b; --text-color: #1f2937; --bg-color: #ffffff; + --radius: 0.5rem; + --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); --section-padding: 5rem 1.5rem; } @@ -17,11 +20,13 @@ body { color: var(--text-color); background-color: var(--bg-color); line-height: 1.6; + transition: background-color 0.3s ease, color 0.3s ease; } h1, h2, h3 { line-height: 1.2; margin-bottom: 1.5rem; + color: var(--text-color); } p { @@ -42,26 +47,36 @@ section { .btn { display: inline-block; padding: 0.75rem 1.5rem; - border-radius: 0.375rem; + border-radius: var(--radius); font-weight: 600; text-decoration: none; transition: all 0.2s; cursor: pointer; + border: 2px solid transparent; } .btn-primary { background-color: var(--primary-color); color: white; + box-shadow: var(--shadow); } -.btn-primary:hover { +.btn-secondary { + background-color: transparent; + border-color: var(--secondary-color); + color: var(--secondary-color); +} + +.btn:hover { opacity: 0.9; + transform: translateY(-1px); } /* Header & Nav */ header { padding: 1.5rem 0; - border-bottom: 1px solid #e5e7eb; + background-color: var(--bg-color); + border-bottom: 1px solid rgba(0,0,0,0.05); } nav .container { @@ -76,12 +91,16 @@ nav .container { /* Footer */ footer { - background-color: #111827; + background-color: var(--secondary-color); color: white; padding: 4rem 0; margin-top: 2rem; } +footer h4, footer p { + color: white; +} + /* Section specific */ .section-header { text-align: center; @@ -90,20 +109,31 @@ footer { /* Hero */ .hero { - background-color: #f3f4f6; + background-color: var(--bg-color); text-align: center; - padding: 8rem 1.5rem; + padding: calc(var(--section-padding) * 1.5); + border-bottom: 1px solid rgba(0,0,0,0.05); } .hero h1 { - font-size: 3rem; - font-weight: 800; + font-size: 3.5rem; + font-weight: 900; + letter-spacing: -0.025em; } .hero .subtitle { - font-size: 1.25rem; - color: #4b5563; - margin-bottom: 2rem; + font-size: 1.5rem; + color: var(--secondary-color); + margin-bottom: 2.5rem; + max-width: 800px; + margin-left: auto; + margin-right: auto; +} + +.hero-actions { + display: flex; + gap: 1rem; + justify-content: center; } /* Services */ @@ -114,34 +144,38 @@ footer { } .service-card { - padding: 2rem; - border: 1px solid #e5e7eb; - border-radius: 0.5rem; - transition: transform 0.2s; + padding: 2.5rem; + border: 1px solid rgba(0,0,0,0.05); + background-color: white; + border-radius: var(--radius); + box-shadow: var(--shadow); + transition: all 0.3s ease; } .service-card:hover { - transform: translateY(-5px); + transform: translateY(-8px); } .price { - font-weight: 700; + font-weight: 800; + font-size: 1.25rem; color: var(--primary-color); - margin-top: 1rem; + margin-top: 1.5rem; } /* Gallery */ .gallery-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 1rem; + gap: 1.5rem; } .gallery-item img { width: 100%; - height: 250px; + height: 300px; object-fit: cover; - border-radius: 0.5rem; + border-radius: var(--radius); + box-shadow: var(--shadow); } /* FAQ */ @@ -152,26 +186,44 @@ footer { .faq-item { margin-bottom: 2rem; - border-bottom: 1px solid #e5e7eb; - padding-bottom: 1.5rem; + border-bottom: 1px solid rgba(0,0,0,0.05); + padding-bottom: 2rem; +} + +.faq-item h3 { + font-size: 1.25rem; + margin-bottom: 1rem; } /* Contact */ .contact-grid { display: grid; grid-template-columns: 1fr 1fr; - gap: 4rem; + gap: 5rem; +} + +.contact-form { + background-color: white; + padding: 2.5rem; + border-radius: var(--radius); + box-shadow: var(--shadow); } .contact-form .form-group { - margin-bottom: 1rem; + margin-bottom: 1.25rem; } .contact-form input, .contact-form textarea { width: 100%; - padding: 0.75rem; + padding: 1rem; border: 1px solid #e5e7eb; - border-radius: 0.25rem; + border-radius: var(--radius); + font-family: inherit; +} + +.contact-form input:focus, .contact-form textarea:focus { + outline: none; + border-color: var(--primary-color); } @media (max-width: 768px) { @@ -179,6 +231,9 @@ footer { grid-template-columns: 1fr; } .hero h1 { - font-size: 2rem; + font-size: 2.5rem; + } + .hero-actions { + flex-direction: column; } }