diff --git a/components/form.php b/components/form.php
index 803682e..fe0fa71 100644
--- a/components/form.php
+++ b/components/form.php
@@ -198,6 +198,8 @@ $basePickerAccept = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,image/a
function redirectToTour(record) {
let targetUrl = record.meta.redirectBase + encodeURIComponent(record.key);
if (record.meta.redirectUtm) targetUrl += '&' + record.meta.redirectUtm;
+ const shownTheme = window.ZoomzillaTheme.current();
+ targetUrl += '&shown_theme=' + encodeURIComponent(shownTheme);
window.PublicNavigationGuard.allowNavigation(() => { window.location.href = targetUrl; });
}
diff --git a/components/theme-client.php b/components/theme-client.php
new file mode 100644
index 0000000..c2f76a1
--- /dev/null
+++ b/components/theme-client.php
@@ -0,0 +1,70 @@
+
+
diff --git a/components/theme-preference.php b/components/theme-preference.php
new file mode 100644
index 0000000..0dce82f
--- /dev/null
+++ b/components/theme-preference.php
@@ -0,0 +1,16 @@
+ 128
+ || !preg_match('/\Av1:(light|dark):(manual|preland):([A-Za-z0-9_-]{16,64})\z/', $value, $match)) {
+ return null;
+ }
+ return ['theme' => $match[1], 'source' => $match[2], 'revision' => $match[3]];
+}
+
+function prelandThemeResolve(string $fallback): string
+{
+ return prelandThemePreference()['theme'] ?? $fallback;
+}
diff --git a/components/theme-style.php b/components/theme-style.php
new file mode 100644
index 0000000..0aad049
--- /dev/null
+++ b/components/theme-style.php
@@ -0,0 +1,27 @@
+
diff --git a/components/theme-switch.php b/components/theme-switch.php
new file mode 100644
index 0000000..48019e1
--- /dev/null
+++ b/components/theme-switch.php
@@ -0,0 +1,15 @@
+ 'Тема оформления', 'light' => 'Светлая тема', 'dark' => 'Тёмная тема'];
+$themeInitial = isset($themeInitial) && $themeInitial === 'dark' ? 'dark' : 'light';
+$themeEscape = static fn(string $value): string => htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
+?>
+
+
diff --git a/index.php b/index.php
index a76b206..3c7504e 100644
--- a/index.php
+++ b/index.php
@@ -3,6 +3,7 @@
require_once __DIR__ . '/i18n.php';
require_once __DIR__ . '/components/asset-url.php';
require_once __DIR__ . '/components/site-domain.php';
+require_once __DIR__ . '/components/theme-preference.php';
$configFile = __DIR__ . '/config.json';
$config = file_exists($configFile) ? json_decode(file_get_contents($configFile), true) : [];
@@ -70,6 +71,8 @@ if (isset($config['utm']) && is_array($config['utm'])) {
}
$UTM = $get_params;
+$themeInitial = prelandThemeResolve(($UTM['theme'] ?? 'light') === 'dark' ? 'dark' : 'light');
+$themeSurface = 'preland';
$host = $config['host'] ?? ($_SERVER['HTTP_HOST'] ?? '');
$apiHost = $host ? (preg_match('#^https?://#', $host) ? rtrim($host, '/') : 'https://' . rtrim($host, '/')) : '';
diff --git a/messages/en/app.php b/messages/en/app.php
index a608f11..7332af8 100644
--- a/messages/en/app.php
+++ b/messages/en/app.php
@@ -370,4 +370,7 @@ return [
'х4' => '4×',
'—' => '—',
'₽' => '$',
+ 'Тема оформления' => 'Appearance',
+ 'Светлая тема' => 'Light theme',
+ 'Тёмная тема' => 'Dark theme',
];
diff --git a/release/assets.json b/release/assets.json
index 90e0f8e..f4a56da 100644
--- a/release/assets.json
+++ b/release/assets.json
@@ -724,7 +724,7 @@
"source": "https://zoomstage.online/images/sprite.svg"
},
"style.css": {
- "sha256": "cc88246e4efb4f2884d0f268b897950cd75620998e0d3ad9985076ce1021abfc",
+ "sha256": "839d51ff9e4541fc1a585c6c715a2d6b26685eefb615c90f372a4d82e24b1f63",
"source": "https://zoomstage.online/style.css"
},
"style.min.c.css": {
diff --git a/style.css b/style.css
index 70ea04d..573c0ec 100644
--- a/style.css
+++ b/style.css
@@ -108,39 +108,6 @@ body {
border: none;
}
-.theme-toggle {
- align-items: center;
- background-color: #F8F9FB;
- border: 1px solid #E5E5E7;
- padding: 4px;
- box-sizing: border-box;
- border-radius: 16px;
-}
-
-.theme-toggle .btn {
- padding: 0;
- border: none;
- border-radius: 0;
- background-color: transparent;
- display: flex;
- justify-content: center;
- align-items: center;
- width: 32px;
- height: 32px;
- outline: none !important;
- box-shadow: none;
- color: #728490;
-}
-
-.theme-toggle .btn:not(.dark) {
- color: #990694;
-}
-
-.theme-toggle .btn svg {
- width: 20px;
- height: 20px;
-}
-
.image-compare {
border-radius: 24px;
}
@@ -2137,15 +2104,6 @@ body:not(.theme-dark) .theme-light-hidden {
background-image: url(../images/dark-bg.jpg);
}
-.theme-dark .theme-toggle {
- background-color: #0B0B10;
- border-color: #323539;
-}
-
-.theme-dark .theme-toggle .btn:not(.dark) {
- color: #323539;
-}
-
.theme-dark .btn-header {
border-color: #fff;
color: #fff;
@@ -2368,11 +2326,7 @@ body:not(.theme-dark) .theme-light-hidden {
border-radius: 8px;
}
- .theme-toggle {
- border-radius: 8px;
- }
-
- .image-examples {
+.image-examples {
display: block;
text-align: center;
}
diff --git a/template.php b/template.php
index 81df815..8a7b095 100644
--- a/template.php
+++ b/template.php
@@ -3,6 +3,8 @@
+
+
= $page_title ?? $default_title ?>
@@ -51,7 +53,8 @@
-
+
+
diff --git a/tests/check-theme-preference.php b/tests/check-theme-preference.php
new file mode 100644
index 0000000..313186e
--- /dev/null
+++ b/tests/check-theme-preference.php
@@ -0,0 +1,20 @@
+$theme,'source'=>$source,'revision'=>$revision]
+ || prelandThemeResolve($theme === 'light' ? 'dark' : 'light') !== $theme) throw new RuntimeException('Canonical preference не побеждает fallback');
+ $checks++;
+}
+foreach ([null, [], false, 1, '', "v1:light:manual:$revision\n", 'v1:dark:other:'.$revision, 'v1:dark:manual:'.str_repeat('a',15), 'v1:dark:manual:'.str_repeat('a',65), str_repeat('a',129)] as $value) {
+ $_COOKIE['theme_choice'] = $value;
+ if (prelandThemePreference() !== null || prelandThemeResolve('light') !== 'light') throw new RuntimeException('Невалидная preference принята');
+ $checks++;
+}
+$source = file_get_contents(dirname(__DIR__) . '/components/form.php');
+if (!str_contains($source, 'const shownTheme = window.ZoomzillaTheme.current();')
+ || !str_contains($source, "targetUrl += '&shown_theme=' + encodeURIComponent(shownTheme)")) throw new RuntimeException('Handoff должен читать показанную тему в момент навигации');
+echo "OK: $checks standalone theme cases + dynamic shown_theme handoff\n";