| Форум » Флуд » Общение » Поговорим о... |
| Поговорим о... |
Вторник, 2025-12-16, 10:18
# 436
Ррррп
Прикрепления:
3666322.noext
(61.4 Kb)
|
Вторник, 2025-12-16, 10:42
# 437
Ооор
Прикрепления:
3012760.noext
(62.4 Kb)
|
Вторник, 2025-12-16, 11:04
# 438
Ддд
Прикрепления:
6656892.noext
(65.3 Kb)
|
Вторник, 2025-12-16, 11:18
# 439
Роррпс
Прикрепления:
zzzzshsh.noext
(66.2 Kb)
|
Вторник, 2025-12-16, 11:31
# 440
Ооооо
Прикрепления:
8421743.noext
(66.2 Kb)
|
Вторник, 2025-12-16, 11:38
# 441
<!DOCTYPE html>
<html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ИСИХОГИ | Сравнение значков версий</title> <link rel="stylesheet" href="script/css/all.css"> <style> /* Сброс и базовые стили */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; background-color: #f8f9fa; color: #333; line-height: 1.5; min-height: 100vh; font-size: 14px; } /* Основной контейнер */ .icon-comparison-container { max-width: 1400px; margin: 0 auto; padding: 15px; } /* Заголовок */ .icon-comparison-title { text-align: center; color: #555; font-size: 22px; margin-bottom: 5px; font-weight: 700; padding-bottom: 10px; border-bottom: 2px solid #6c757d; } .icon-comparison-subtitle { text-align: center; color: #666; font-size: 14px; margin-bottom: 20px; } /* Панель поиска */ .icon-comparison-search-area { margin: 15px auto; text-align: center; max-width: 500px; } .icon-comparison-search { width: 100%; padding: 10px 15px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; background-color: #fff; color: #333; transition: all 0.3s ease; } .icon-comparison-search:focus { outline: none; border-color: #6c757d; box-shadow: 0 0 0 2px rgba(108, 117, 125, 0.15); } /* Панель управления - КОМПАКТНАЯ */ .icon-comparison-controls { display: flex; justify-content: space-between; align-items: center; background: white; padding: 12px 15px; margin-bottom: 20px; border-radius: 6px; border: 1px solid #eaeaea; flex-wrap: wrap; gap: 15px; position: relative; } .icon-comparison-stats { font-size: 13px; color: #555; display: flex; gap: 15px; flex-wrap: wrap; } .icon-comparison-stats span { font-weight: 700; color: #6c757d; } /* Выбор группы - ВСПЛЫВАЮЩИЙ СПИСОК С ЧЕКБОКСАМИ */ .groups-dropdown { position: relative; min-width: 180px; } .groups-dropdown-toggle { padding: 8px 12px; background: white; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; color: #333; cursor: pointer; display: flex; align-items: center; justify-content: space-between; min-width: 180px; transition: all 0.3s ease; } .groups-dropdown-toggle:hover { border-color: #6c757d; } .groups-dropdown-toggle.active { border-color: #6c757d; box-shadow: 0 0 0 2px rgba(108, 117, 125, 0.15); } .groups-dropdown-content { position: absolute; top: calc(100% + 5px); left: 0; background: white; border: 1px solid #ddd; border-radius: 4px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); width: 250px; max-height: 300px; overflow-y: auto; z-index: 1000; display: none; padding: 10px; } .groups-dropdown-content.show { display: block; animation: fadeIn 0.2s ease; } @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } .group-option { padding: 6px 0; display: flex; align-items: center; gap: 8px; cursor: pointer; } .group-option:hover { background-color: #f5f5f5; } .group-checkbox { width: 14px; height: 14px; cursor: pointer; } .group-name { font-size: 13px; color: #333; user-select: none; } .icon-comparison-toggle { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 500; } .icon-comparison-checkbox { width: 16px; height: 16px; cursor: pointer; } /* Стили для каменной кладки (masonry layout) - КОМПАКТНЫЕ */ .icon-comparison-columns { display: flex; flex-wrap: wrap; gap: 15px; width: 100%; justify-content: flex-start; transition: all 0.3s ease; } .icon-comparison-masonry-column { display: flex; flex-direction: column; gap: 15px; width: 380px; max-width: 100%; } /* Группа значков - КОМПАКТНАЯ */ .icon-comparison-group { break-inside: avoid; width: 380px; margin-bottom: 15px; page-break-inside: avoid; animation: fadeInUp 0.4s ease forwards; opacity: 0; transform: translateY(15px); } @keyframes fadeInUp { to { opacity: 1; transform: translateY(0); } } .icon-comparison-group-title { background: #6c757d; padding: 10px 12px; border-radius: 6px 6px 0 0; margin-bottom: 0; font-weight: 600; color: white; font-size: 14px; width: 380px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; justify-content: space-between; text-align: center; } .group-count { background: rgba(255, 255, 255, 0.2); padding: 2px 8px; border-radius: 12px; font-size: 12px; font-weight: 500; } .icon-comparison-panel { border: 1px solid #e0e0e0; border-radius: 0 0 6px 6px; padding: 12px; background: #fff; width: 380px; flex-shrink: 0; page-break-inside: avoid; } .icon-comparison-panel-header { display: grid; grid-template-columns: 40px 24px 40px 1fr; gap: 8px; align-items: center; padding: 8px 3px; margin-bottom: 6px; border-bottom: 1px solid #eee; font-weight: 600; text-align: center; } .icon-comparison-header-label { font-size: 12px; color: #555; white-space: nowrap; font-weight: 600; } .icon-comparison-row { display: grid; grid-template-columns: 40px 24px 40px 1fr; gap: 8px; align-items: center; padding: 6px 3px; border-bottom: 1px solid #f5f5f5; min-height: 40px; transition: background-color 0.2s ease; } .icon-comparison-row:hover { background-color: rgba(108, 117, 125, 0.05); } .icon-comparison-row:last-child { border-bottom: none; } .icon-comparison-image { width: 40px; height: 40px; object-fit: contain; background-color: #f8f9fa; border-radius: 4px; padding: 3px; box-sizing: border-box; border: 1px solid #eee; transition: transform 0.2s ease; } .icon-comparison-image:hover { transform: scale(1.05); } .icon-comparison-arrow { text-align: center; color: #888; font-size: 14px; font-weight: 300; white-space: nowrap; } .icon-comparison-info { padding-left: 8px; padding-right: 3px; min-width: 0; width: 100%; text-align: center; } .icon-comparison-name { color: #333; line-height: 1.4; word-wrap: break-word; overflow-wrap: break-word; text-align: center; font-size: 13px; font-weight: 500; max-width: 200px; margin: 0 auto; } /* Состояния загрузки и ошибок */ .icon-comparison-loading, .icon-comparison-no-results { text-align: center; padding: 40px 15px; color: #666; width: 100%; font-size: 14px; grid-column: 1 / -1; } .icon-comparison-loading { display: flex; flex-direction: column; align-items: center; gap: 15px; } .loading-spinner { width: 40px; height: 40px; border: 3px solid #f3f3f3; border-top: 3px solid #6c757d; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .no-results-icon { font-size: 36px; color: #ddd; margin-bottom: 10px; } /* Стили для печати - УЛУЧШЕННЫЕ */ @media print { * { margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } body { background-color: white !important; color: black !important; font-size: 10px !important; line-height: 1.3 !important; padding: 0 !important; margin: 0 !important; width: 100% !important; } .icon-comparison-container { max-width: 100% !important; margin: 0 auto !important; padding: 0 5mm !important; width: 100% !important; page-break-inside: avoid !important; } .icon-comparison-title { font-size: 14px !important; margin: 2mm 0 3mm 0 !important; padding: 0 0 2mm 0 !important; color: black !important; border-bottom: 1px solid #ccc !important; text-align: center !important; page-break-after: avoid !important; } .icon-comparison-subtitle, .icon-comparison-search-area, .icon-comparison-controls { display: none !important; } /* УПРОЩЕННЫЙ МАКЕТ ДЛЯ ПЕЧАТИ - ЕДИНЫЙ ДЛЯ РЕЖИМОВ С ГРУППАМИ И БЕЗ */ .icon-comparison-print-container { display: block !important; width: 100% !important; margin: 0 !important; padding: 0 !important; } /* Контейнер для режима БЕЗ групп */ .icon-comparison-no-groups-container { display: flex !important; flex-wrap: wrap !important; width: 100% !important; justify-content: space-between !important; margin: 0 !important; padding: 0 !important; } .icon-comparison-no-groups-column { width: 49% !important; float: left !important; page-break-inside: avoid !important; margin-bottom: 2mm !important; } .icon-comparison-no-groups-column:nth-child(2n) { margin-left: 2% !important; } /* Контейнер для режима С группами - ИСПРАВЛЕННЫЙ */ .icon-comparison-print-columns { display: block !important; width: 100% !important; margin: 0 !important; padding: 0 !important; column-count: 2 !important; column-gap: 5mm !important; column-fill: auto !important; /* Важно для контроля заполнения колонок */ } .icon-comparison-print-group { width: 100% !important; break-inside: avoid !important; page-break-inside: avoid !important; margin-bottom: 4mm !important; display: inline-block !important; position: relative !important; } /* УСИЛЕННАЯ ЗАЩИТА ОТ РАЗРЫВА ГРУПП */ .icon-comparison-print-group:not(:first-child) { margin-top: 2mm !important; } /* Группа значков - УВЕЛИЧЕННЫЙ РАЗМЕР ДЛЯ ПЕЧАТИ */ .icon-comparison-panel { width: 100% !important; padding: 3mm !important; break-inside: avoid !important; page-break-inside: avoid !important; border: 0.5px solid #ccc !important; background: white !important; margin: 0 0 2mm 0 !important; max-width: none !important; } .icon-comparison-group-title { width: 100% !important; font-size: 12px !important; padding: 2mm 3mm !important; margin: 0 0 2mm 0 !important; background: #f0f0f0 !important; color: black !important; border-radius: 2px !important; page-break-after: avoid !important; max-width: none !important; border: 0.5px solid #ddd !important; } /* Скрываем счетчики значков в группах при печати */ .group-count { display: none !important; } /* УВЕЛИЧЕННЫЙ РАЗМЕР ДЛЯ ПЕЧАТИ С ГРУППАМИ */ .icon-comparison-panel-header { padding: 2mm !important; margin-bottom: 2mm !important; gap: 3mm !important; border-bottom: 0.5px solid #ccc !important; font-size: 10px !important; page-break-after: avoid !important; grid-template-columns: 30px 20px 30px 1fr !important; } .icon-comparison-header-label { font-size: 10px !important; color: black !important; } .icon-comparison-row { padding: 2mm !important; min-height: 30px !important; gap: 3mm !important; border-bottom: 0.5px solid #eee !important; page-break-inside: avoid !important; grid-template-columns: 30px 20px 30px 1fr !important; } .icon-comparison-image { width: 30px !important; height: 30px !important; padding: 1px !important; background-color: #f0f0f0 !important; filter: none !important; -webkit-filter: none !important; border: 0.5px solid #ddd !important; } .icon-comparison-arrow { font-size: 10px !important; color: #666 !important; } .icon-comparison-name { font-size: 10px !important; max-width: none !important; color: black !important; line-height: 1.2 !important; margin: 0 auto !important; padding: 0 2mm !important; } /* Для режима БЕЗ групп также увеличиваем размеры */ .icon-comparison-no-groups-column .icon-comparison-panel { padding: 3mm !important; } .icon-comparison-no-groups-column .icon-comparison-panel-header { padding: 2mm !important; margin-bottom: 2mm !important; gap: 3mm !important; grid-template-columns: 30px 20px 30px 1fr !important; } .icon-comparison-no-groups-column .icon-comparison-row { padding: 2mm !important; min-height: 30px !important; gap: 3mm !important; grid-template-columns: 30px 20px 30px 1fr !important; } .icon-comparison-no-groups-column .icon-comparison-image { width: 30px !important; height: 30px !important; } .icon-comparison-no-groups-column .icon-comparison-name { font-size: 10px !important; max-width: none !important; padding: 0 2mm !important; } /* Отключаем все эффекты темной темы при печати */ .dark-mode .icon-comparison-body, [data-theme="dark"] .icon-comparison-body, .dark .icon-comparison-body, .dark-mode .icon-comparison-title, [data-theme="dark"] .icon-comparison-title, .dark .icon-comparison-title, .dark-mode .icon-comparison-name, [data-theme="dark"] .icon-comparison-name, .dark .icon-comparison-name, .dark-mode .icon-comparison-header-label, [data-theme="dark"] .icon-comparison-header-label, .dark .icon-comparison-header-label { background-color: white !important; color: black !important; } /* Скрываем ненужные элементы для печати */ .icon-comparison-row:hover { background-color: transparent !important; } /* Минимальные отступы на странице */ @page { margin: 12mm !important; } /* Предотвращаем разрыв страницы в неподходящих местах */ h1, h2, h3, h4, h5, h6 { page-break-after: avoid !important; } table, tr, td, img { page-break-inside: avoid !important; } /* Очистка float */ .icon-comparison-no-groups-container:after { content: ""; display: table; clear: both; } /* Для очень больших групп - принудительный разрыв */ .force-page-break { page-break-before: always !important; } } /* Адаптивность */ @media (max-width: 1300px) { .icon-comparison-columns { justify-content: center; } } @media (max-width: 850px) { .icon-comparison-masonry-column, .icon-comparison-group, .icon-comparison-group-title, .icon-comparison-panel { width: 100%; max-width: 500px; } .icon-comparison-columns { justify-content: center; } } @media (max-width: 600px) { .icon-comparison-container { padding: 10px; } .icon-comparison-title { font-size: 18px; } .icon-comparison-controls { flex-direction: column; align-items: flex-start; gap: 10px; padding: 10px; } .icon-comparison-panel-header { grid-template-columns: 36px 20px 36px 1fr; gap: 6px; } .icon-comparison-row { grid-template-columns: 36px 20px 36px 1fr; gap: 6px; } .icon-comparison-image { width: 36px; height: 36px; } .icon-comparison-name { font-size: 12px; max-width: 160px; } } @media (max-width: 480px) { .icon-comparison-panel-header { grid-template-columns: 32px 18px 32px 1fr; gap: 5px; font-size: 11px; } .icon-comparison-row { grid-template-columns: 32px 18px 32px 1fr; gap: 5px; padding: 5px 2px; } .icon-comparison-image { width: 32px; height: 32px; } .icon-comparison-name { font-size: 11px; max-width: 130px; } .icon-comparison-arrow { font-size: 12px; } } </style> </head> <body> <div class="icon-comparison-container"> <h1 class="icon-comparison-title">Сравнение значков ИСИХОГИ</h1> <p class="icon-comparison-subtitle">Сравнение значков старой и новой версий системы</p> <div class="icon-comparison-search-area"> <input type="text" id="iconComparisonSearch" class="icon-comparison-search" placeholder="Поиск по названию значка..." autocomplete="off"> </div> <div class="icon-comparison-controls"> <div class="icon-comparison-stats"> Всего значков: <span id="iconComparisonTotalCount">0</span> | Отображено: <span id="iconComparisonShownCount">0</span> </div> <div class="groups-dropdown"> <div class="groups-dropdown-toggle" id="groupsDropdownToggle"> <span>Выбрать группы</span> <i class="fas fa-chevron-down" style="font-size: 12px; margin-left: 5px;"></i> </div> <div class="groups-dropdown-content" id="groupsDropdownContent"> <!-- Чекбоксы будут добавлены динамически --> </div> </div> <div class="icon-comparison-toggle"> <input type="checkbox" id="iconComparisonShowGroups" class="icon-comparison-checkbox" checked> <label for="iconComparisonShowGroups">Показывать группы</label> </div> </div> <div class="icon-comparison-columns" id="iconComparisonContainer"> <div class="icon-comparison-loading"> <div class="loading-spinner"></div> <div>Загрузка значков...</div> </div> </div> </div> <script> // Основные переменные let iconComparisonAllIcons = []; let iconComparisonFilteredIcons = []; let iconComparisonAvailableGroups = []; let iconComparisonShowGroups = true; let isPrintMode = false; let selectedGroups = new Set(); // Храним выбранные группы // Функции для masonry layout (только для веб-отображения) function iconComparisonSplitLargeGroupOptimized(icons, maxItems = 15) { if (icons.length <= maxItems) { return [icons]; } const chunks = []; const total = icons.length; if (total <= maxItems * 2) { const half = Math.ceil(total / 2); chunks.push(icons.slice(0, half)); chunks.push(icons.slice(half)); } else { const numChunks = Math.ceil(total / maxItems); const baseSize = Math.floor(total / numChunks); let remainder = total % numChunks; let start = 0; for (let i = 0; i < numChunks; i++) { let chunkSize = baseSize; if (remainder > 0) { chunkSize++; remainder--; } chunks.push(icons.slice(start, start + chunkSize)); start += chunkSize; } } return chunks; } function iconComparisonRenderWithGroups() { const container = document.getElementById('iconComparisonContainer'); container.innerHTML = ''; // Если нет выбранных групп - показываем сообщение if (selectedGroups.size === 0) { container.innerHTML = ` <div class="icon-comparison-no-results"> <div class="no-results-icon"> <i class="fas fa-layer-group"></i> </div> <div>Не выбрано ни одной группы. Выберите группы для отображения значков.</div> </div> `; iconComparisonUpdateStats(); return; } if (iconComparisonFilteredIcons.length === 0) { container.innerHTML = ` <div class="icon-comparison-no-results"> <div class="no-results-icon"> <i class="fas fa-search"></i> </div> <div>Ничего не найдено. Попробуйте изменить поисковый запрос или выбрать другие группы.</div> </div> `; iconComparisonUpdateStats(); return; } // Для печати используем упрощенный макет if (isPrintMode) { renderGroupsForPrint(); return; } const { groups, noGroup } = iconComparisonGroupIcons(iconComparisonFilteredIcons); const allGroupChunks = []; // Сортировка групп по алфавиту const sortedGroupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b, 'ru')); // Добавляем обычные группы (отсортированные по алфавиту) sortedGroupNames.forEach(groupName => { const icons = groups[groupName]; const chunks = iconComparisonSplitLargeGroupOptimized(icons, 15); chunks.forEach((chunk, index) => { const chunkName = chunks.length > 1 ? `${groupName} (${index + 1}/${chunks.length})` : groupName; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: groupName, isNoGroupSection: false }); }); }); // Добавляем группу "Прочие" в конец, если она выбрана if (noGroup.length > 0 && selectedGroups.has('no_group')) { const noGroupChunks = iconComparisonSplitLargeGroupOptimized(noGroup, 15); noGroupChunks.forEach((chunk, index) => { const chunkName = noGroupChunks.length > 1 ? `Прочие значки (${index + 1}/${noGroupChunks.length})` : 'Прочие значки'; allGroupChunks.push({ name: chunkName, icons: chunk, originalGroup: 'no_group', isNoGroupSection: true }); }); } // Создаем обычный masonry layout для веб-отображения const columnsContainer = document.createElement('div'); columnsContainer.className = 'icon-comparison-columns'; // Определяем количество колонок в зависимости от количества групп // Минимум 2 колонки, если есть больше 1 группы let maxColumns; if (allGroupChunks.length <= 3) { // Для 1-3 групп - 2 колонки maxColumns = 2; } else if (allGroupChunks.length <= 6) { // Для 4-6 групп - 3 колонки maxColumns = 3; } else { // Для большего количества групп - максимум 3 колонки maxColumns = 3; } Добавлено (2025-12-16, 11:38) |
Вторник, 2025-12-16, 14:47
# 442
<!-- layouts/_default/single.html -->
<article> {{ .Content }} {{ if .Params.images }} <div class="gallery"> {{ range .Params.images }} <a href="{{ . }}" data-lightbox="post-{{ $.File.UniqueID }}" data-title="{{ $.Title }}"> <img src="{{ . }}" alt="{{ $.Title }}" loading="lazy"> </a> {{ end }} </div> {{ end }} </article> <!-- В head --> {{ if .IsPage }} <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.3/css/lightbox.min.css"> {{ end }} <!-- Перед </body> --> {{ if .IsPage }} <script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.3/js/lightbox.min.js"></script> <script> lightbox.option({ 'resizeDuration': 200, 'wrapAround': true, 'albumLabel': "Изображение %1 из %2" }) </script> {{ end }} |
Вторник, 2025-12-16, 14:48
# 443
{{ define "main" }}
<div class="hx-mx-auto hx-flex hx-max-w-[90rem]"> {{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }} {{ partial "toc.html" . }} <article class="hx-w-full hx-break-words hx-flex hx-min-h-[calc(100vh-var(--navbar-height))] hx-min-w-0 hx-justify-center hx-pb-8 hx-pr-[calc(env(safe-area-inset-right)-1.5rem)]"> <main class="hx-w-full hx-min-w-0 hx-max-w-6xl hx-px-6 hx-pt-4 md:hx-px-12"> <br class="hx-mt-1.5 hx-text-sm" /> {{ if .Title }}<h1 class="hx-text-center hx-mt-2 hx-text-4xl hx-font-bold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100">{{ .Title }}</h1>{{ end }} <div class="hx-mb-16"></div> <div class="content"> {{ .Content }} </div> <div class="hx-mt-16"></div> {{ partial "components/comments.html" . }} </main> </article> </div> {{ end }} |
Вторник, 2025-12-16, 14:53
# 444
<!-- В разделе head -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.4/css/lightbox.min.css" /> <!-- Перед закрывающим </body> --> <script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.4/js/lightbox.min.js"></script> <script> lightbox.option({ 'resizeDuration': 200, 'wrapAround': true, 'albumLabel': "Изображение %1 из %2", 'fadeDuration': 300 }) </script> {{ define "main" }} <div class="hx-mx-auto hx-flex hx-max-w-[90rem]"> {{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }} {{ partial "toc.html" . }} <article class="hx-w-full hx-break-words hx-flex hx-min-h-[calc(100vh-var(--navbar-height))] hx-min-w-0 hx-justify-center hx-pb-8 hx-pr-[calc(env(safe-area-inset-right)-1.5rem)]"> <main class="hx-w-full hx-min-w-0 hx-max-w-6xl hx-px-6 hx-pt-4 md:hx-px-12"> <br class="hx-mt-1.5 hx-text-sm" /> {{ if .Title }}<h1 class="hx-text-center hx-mt-2 hx-text-4xl hx-font-bold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100">{{ .Title }}</h1>{{ end }} <div class="hx-mb-16"></div> <div class="content"> <!-- Добавляем обработку изображений --> {{ $content := .Content }} {{ $content = replaceRE `<img([^>]+)src="([^"]+)"([^>]*)>` `<a href="$2" data-lightbox="post-{{ $.File.UniqueID }}" data-title="{{ $.Title }}"><img$1src="$2"$3 loading="lazy"></a>` $content }} {{ $content | safeHTML }} </div> <div class="hx-mt-16"></div> {{ partial "components/comments.html" . }} </main> </article> </div> <!-- Модальное окно для изображений (если не используете Lightbox) --> <!-- <div id="imageModal" class="hx-fixed hx-hidden hx-inset-0 hx-z-50 hx-bg-black hx-bg-opacity-90 hx-flex hx-items-center hx-justify-center"> <span class="hx-absolute hx-top-4 hx-right-4 hx-text-white hx-text-4xl hx-cursor-pointer" onclick="closeModal()">×</span> <img id="modalImage" class="hx-max-w-full hx-max-h-screen"> </div> --> {{ end }} |
Вторник, 2025-12-16, 15:01
# 445
{{ define "main" }}
<div class="hx-mx-auto hx-flex hx-max-w-[90rem]"> {{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }} {{ partial "toc.html" . }} <article class="hx-w-full hx-break-words hx-flex hx-min-h-[calc(100vh-var(--navbar-height))] hx-min-w-0 hx-justify-center hx-pb-8 hx-pr-[calc(env(safe-area-inset-right)-1.5rem)]"> <main class="hx-w-full hx-min-w-0 hx-max-w-6xl hx-px-6 hx-pt-4 md:hx-px-12"> <br class="hx-mt-1.5 hx-text-sm" /> {{ if .Title }}<h1 class="hx-text-center hx-mt-2 hx-text-4xl hx-font-bold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100">{{ .Title }}</h1>{{ end }} <div class="hx-mb-16"></div> <div class="content image-container"> <!-- Обработка изображений --> {{ $content := .Content }} {{ $content = replaceRE `<img([^>]+)src="([^"]+)"([^>]*)>` `<img$1src="$2"$3 class="clickable-image" onclick="openImageModal('$2')" loading="lazy">` $content }} {{ $content | safeHTML }} </div> <div class="hx-mt-16"></div> {{ partial "components/comments.html" . }} </main> </article> </div> <!-- Модальное окно --> <div id="imageModal" class="hx-fixed hx-hidden hx-inset-0 hx-z-50 hx-bg-black/90 hx-flex hx-items-center hx-justify-center"> <div class="hx-relative hx-max-w-4xl hx-w-full hx-p-4"> <button onclick="closeImageModal()" class="hx-absolute hx-top-0 hx-right-0 hx-text-white hx-text-4xl hx-bg-transparent hx-border-0 hx-cursor-pointer hx-z-10"> × </button> <img id="modalImage" class="hx-max-w-full hx-max-h-[90vh] hx-mx-auto hx-rounded-lg"> </div> </div> <script> function openImageModal(src) { const modal = document.getElementById('imageModal'); const modalImg = document.getElementById('modalImage'); modal.classList.remove('hidden'); modal.classList.add('flex'); modalImg.src = src; document.body.style.overflow = 'hidden'; } function closeImageModal() { const modal = document.getElementById('imageModal'); modal.classList.add('hidden'); modal.classList.remove('flex'); document.body.style.overflow = 'auto'; } // Закрытие по клику на фон или ESC document.getElementById('imageModal').addEventListener('click', function(e) { if (e.target === this) closeImageModal(); }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape') closeImageModal(); }); </script> <style> .clickable-image { cursor: zoom-in; transition: transform 0.2s ease; } .clickable-image:hover { transform: scale(1.01); } #imageModal { backdrop-filter: blur(4px); } #modalImage { animation: fadeIn 0.3s ease; } @keyframes fadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } } </style> {{ end }} |
Вторник, 2025-12-16, 15:05
# 446
{{ define "main" }}
<div class="hx-mx-auto hx-flex hx-max-w-[90rem]"> {{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }} {{ partial "toc.html" . }} <article class="hx-w-full hx-break-words hx-flex hx-min-h-[calc(100vh-var(--navbar-height))] hx-min-w-0 hx-justify-center hx-pb-8 hx-pr-[calc(env(safe-area-inset-right)-1.5rem)]"> <main class="hx-w-full hx-min-w-0 hx-max-w-6xl hx-px-6 hx-pt-4 md:hx-px-12"> <br class="hx-mt-1.5 hx-text-sm" /> {{ if .Title }}<h1 class="hx-text-center hx-mt-2 hx-text-4xl hx-font-bold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100">{{ .Title }}</h1>{{ end }} <div class="hx-mb-16"></div> <div class="content" id="content"> {{ .Content }} </div> <div class="hx-mt-16"></div> {{ partial "components/comments.html" . }} </main> </article> </div> <!-- Модальное окно --> <div id="imageModal" class="hx-fixed hx-hidden hx-inset-0 hx-z-[9999] hx-bg-black/95 hx-flex hx-items-center hx-justify-center"> <div class="hx-relative hx-max-w-5xl hx-w-full hx-p-4"> <button onclick="closeImageModal()" class="hx-absolute hx--top-12 hx-right-0 hx-text-white hx-text-4xl hx-bg-transparent hx-border-0 hx-cursor-pointer hover:hx-text-gray-300 hx-transition-colors"> ✕ </button> <img id="modalImage" class="hx-block hx-max-w-full hx-max-h-[90vh] hx-mx-auto hx-rounded-lg"> <div id="modalCaption" class="hx-text-center hx-text-white hx-mt-4 hx-text-lg"></div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function() { // Находим все изображения внутри контента const contentDiv = document.getElementById('content'); const images = contentDiv.querySelectorAll('img'); images.forEach(img => { // Пропускаем изображения, которые уже обработаны if (img.classList.contains('clickable-processed')) return; // Добавляем классы и обработчик img.classList.add('hx-clickable-image', 'clickable-processed'); img.style.cursor = 'zoom-in'; img.style.transition = 'opacity 0.2s'; // Добавляем alt как title для подсказки if (img.alt && !img.title) { img.title = "Нажмите для увеличения"; } img.addEventListener('click', function(e) { e.stopPropagation(); openImageModal(this.src, this.alt); }); // Эффект при наведении img.addEventListener('mouseenter', function() { this.style.opacity = '0.9'; }); img.addEventListener('mouseleave', function() { this.style.opacity = '1'; }); }); }); // Функции модального окна function openImageModal(src, alt = '') { const modal = document.getElementById('imageModal'); const modalImg = document.getElementById('modalImage'); const caption = document.getElementById('modalCaption'); modal.classList.remove('hx-hidden'); modal.classList.add('hx-flex'); modalImg.src = src; if (alt) { caption.textContent = alt; caption.classList.remove('hx-hidden'); } else { caption.classList.add('hx-hidden'); } // Блокировка прокрутки document.body.style.overflow = 'hidden'; document.documentElement.style.overflow = 'hidden'; } function closeImageModal() { const modal = document.getElementById('imageModal'); modal.classList.add('hx-hidden'); modal.classList.remove('hx-flex'); // Разблокировка прокрутки document.body.style.overflow = 'auto'; document.documentElement.style.overflow = 'auto'; } // Закрытие по клику на фон или ESC document.getElementById('imageModal').addEventListener('click', function(e) { if (e.target === this) closeImageModal(); }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape') closeImageModal(); }); </script> <style> .hx-clickable-image { border-radius: 6px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } .hx-clickable-image:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.2); } #imageModal { backdrop-filter: blur(8px); animation: modalFadeIn 0.25s ease-out; } @keyframes modalFadeIn { from { opacity: 0; backdrop-filter: blur(0); } to { opacity: 1; backdrop-filter: blur(8px); } } #modalImage { animation: imageZoomIn 0.3s ease-out; } @keyframes imageZoomIn { from { transform: scale(0.95); opacity: 0; } to { transform: scale(1); opacity: 1; } } </style> {{ end }} |
Вторник, 2025-12-16, 15:25
# 447
{{ define "main" }}
<div class="hx-mx-auto hx-flex hx-max-w-[90rem]"> {{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }} {{ partial "toc.html" . }} <article class="hx-w-full hx-break-words hx-flex hx-min-h-[calc(100vh-var(--navbar-height))] hx-min-w-0 hx-justify-center hx-pb-8 hx-pr-[calc(env(safe-area-inset-right)-1.5rem)]"> <main class="hx-w-full hx-min-w-0 hx-max-w-6xl hx-px-6 hx-pt-4 md:hx-px-12"> <br class="hx-mt-1.5 hx-text-sm" /> {{ if .Title }}<h1 class="hx-text-center hx-mt-2 hx-text-4xl hx-font-bold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100">{{ .Title }}</h1>{{ end }} <div class="hx-mb-16"></div> <div class="content"> {{ .Content }} </div> <div class="hx-mt-16"></div> {{ partial "components/comments.html" . }} </main> </article> </div> <!-- Модальное окно --> <div id="imageModal" class="hx-fixed hx-inset-0 hx-z-[9999] hx-hidden hx-bg-black/95 hx-flex hx-items-center hx-justify-center"> <div class="hx-relative hx-max-w-5xl hx-w-full hx-p-4"> <button onclick="closeModal()" class="hx-absolute hx--top-10 hx-right-0 hx-text-white hx-text-3xl hx-bg-transparent hx-border-0 hx-cursor-pointer hover:hx-opacity-80"> ✕ </button> <img id="modalImage" class="hx-block hx-max-w-full hx-max-h-[85vh] hx-mx-auto hx-rounded-lg"> </div> </div> <!-- Стили для изображений --> <style> /* Стили для всех изображений в контенте */ .content img { cursor: pointer; transition: all 0.3s ease; border-radius: 8px; max-width: 100%; height: auto; } .content img:hover { opacity: 0.9; transform: translateY(-2px); box-shadow: 0 10px 25px rgba(0,0,0,0.15); } /* Стили модального окна */ #imageModal { animation: modalFadeIn 0.25s ease; } #modalImage { animation: imageZoomIn 0.3s ease; } @keyframes modalFadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes imageZoomIn { from { transform: scale(0.9); opacity: 0; } to { transform: scale(1); opacity: 1; } } </style> <script> // Ждем полной загрузки страницы document.addEventListener('DOMContentLoaded', function() { console.log('Скрипт увеличения изображений загружен'); // Находим все изображения внутри .content const images = document.querySelectorAll('.content img'); console.log('Найдено изображений:', images.length); images.forEach(img => { // Пропускаем SVG и другие некликабельные элементы if (img.width < 30 || img.height < 30) return; // Добавляем стиль курсора img.style.cursor = 'zoom-in'; // Сохраняем оригинальный src если его еще нет if (!img.dataset.original) { img.dataset.original = img.src; } // Добавляем обработчик клика img.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); openModal(this.src); }); // Добавляем атрибут title для подсказки if (!img.hasAttribute('title')) { img.setAttribute('title', 'Нажмите для увеличения'); } }); // Также обрабатываем изображения внутри ссылок (которые могут быть в markdown) document.querySelectorAll('.content a img').forEach(img => { img.style.cursor = 'zoom-in'; img.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); openModal(this.src); }); }); }); // Функции для модального окна function openModal(src) { console.log('Открываем изображение:', src); const modal = document.getElementById('imageModal'); const modalImg = document.getElementById('modalImage'); modal.classList.remove('hx-hidden'); modal.classList.add('hx-flex'); modalImg.src = src; // Блокируем прокрутку фона document.body.style.overflow = 'hidden'; document.documentElement.style.overflow = 'hidden'; // Добавляем обработчик ESC document.addEventListener('keydown', handleEscape); } function closeModal() { const modal = document.getElementById('imageModal'); modal.classList.add('hx-hidden'); modal.classList.remove('hx-flex'); // Восстанавливаем прокрутку document.body.style.overflow = ''; document.documentElement.style.overflow = ''; // Удаляем обработчик ESC document.removeEventListener('keydown', handleEscape); } function handleEscape(e) { if (e.key === 'Escape') { closeModal(); } } // Закрытие по клику на фон document.getElementById('imageModal').addEventListener('click', function(e) { if (e.target === this || e.target.tagName === 'BUTTON') { closeModal(); } }); </script> {{ end }} |
Вторник, 2025-12-16, 15:41
# 448
{{- $alt := .PlainText | safeHTML -}}
{{- $lazyLoading := .Page.Site.Params.enableImageLazyLoading | default true -}} {{- $dest := .Destination -}} {{- $isLocal := not (urls.Parse $dest).Scheme -}} {{- $isPage := and (eq .Page.Kind "page") (not .Page.BundleType) -}} {{- $startsWithSlash := hasPrefix $dest "/" -}} {{- $startsWithRelative := hasPrefix $dest "../" -}} {{- if and $dest $isLocal -}} {{- if $startsWithSlash -}} {{/* Images under static directory */}} {{- $dest = (relURL (strings.TrimPrefix "/" $dest)) -}} {{- else if and $isPage (not $startsWithRelative) -}} {{/* Images that are sibling to the individual page file */}} {{ $dest = (printf "../%s" $dest) }} {{- end -}} {{- end -}} {{- with .Title -}} <figure> <img src="{{ $dest | safeURL }}" title="{{ . }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} /> <figcaption>{{ . }}</figcaption> </figure> {{- else -}} <img src="{{ $dest | safeURL }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} /> {{- end -}} Добавлено (2025-12-16, 15:41) |
Вторник, 2025-12-16, 15:51
# 449
Отлично! Теперь понятно. У вас уже есть кастомный рендеринг изображений. Нужно его модифицировать для добавления lightbox. Вот решение:
Обновите ваш render-image.html: ```html {{- $alt := .PlainText | safeHTML -}} {{- $lazyLoading := .Page.Site.Params.enableImageLazyLoading | default true -}} {{- $dest := .Destination -}} {{- $isLocal := not (urls.Parse $dest).Scheme -}} {{- $isPage := and (eq .Page.Kind "page") (not .Page.BundleType) -}} {{- $startsWithSlash := hasPrefix $dest "/" -}} {{- $startsWithRelative := hasPrefix $dest "../" -}} {{- if and $dest $isLocal -}} {{- if $startsWithSlash -}} {{/* Images under static directory */}} {{- $dest = (relURL (strings.TrimPrefix "/" $dest)) -}} {{- else if and $isPage (not $startsWithRelative) -}} {{/* Images that are sibling to the individual page file */}} {{ $dest = (printf "../%s" $dest) }} {{- end -}} {{- end -}} {{/* Получаем полный URL для lightbox */}} {{- $fullUrl := $dest | safeURL -}} {{/* Если изображение локальное, используем абсолютный путь */}} {{- if $isLocal -}} {{- $fullUrl = absURL $dest -}} {{- end -}} {{- with .Title -}} <figure class="hx-cursor-pointer"> <a href="{{ $fullUrl }}" class="lightbox-link" data-lightbox="gallery-{{ $.Page.File.UniqueID }}" data-title="{{ . }}"> <img src="{{ $dest | safeURL }}" title="{{ . }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} class="hx-rounded-lg hx-shadow hover:hx-shadow-lg hx-transition-shadow"> </a> <figcaption class="hx-text-sm hx-text-gray-600 dark:hx-text-gray-400 hx-mt-2">{{ . }}</figcaption> </figure> {{- else -}} <a href="{{ $fullUrl }}" class="lightbox-link" data-lightbox="gallery-{{ $.Page.File.UniqueID }}" data-title="{{ $alt }}"> <img src="{{ $dest | safeURL }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} class="hx-rounded-lg hx-shadow hover:hx-shadow-lg hx-transition-shadow hx-cursor-zoom-in"> </a> {{- end -}} ``` Теперь добавьте Lightbox в head.html и scripts.html: В layouts/partials/head.html добавьте: ```html <head> <!-- Ваши существующие метатеги --> {{/* Добавьте lightbox CSS */}} <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.4/css/lightbox.min.css" /> <!-- Остальные стили --> <!-- ... --> </head> ``` В layouts/partials/scripts.html добавьте: ```html <!-- Ваши существующие скрипты --> <!-- Добавьте lightbox JS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.4/js/lightbox.min.js"></script> <script> // Инициализация lightbox document.addEventListener('DOMContentLoaded', function() { // Настраиваем lightbox для всех изображений const lightboxLinks = document.querySelectorAll('.lightbox-link'); lightboxLinks.forEach(link => { // Добавляем обработчик для красивого открытия link.addEventListener('click', function(e) { e.preventDefault(); // Создаем lightbox вручную const img = this.querySelector('img'); const lightbox = new Lightbox(); lightbox.open({ src: this.href, alt: img ? img.alt : '', title: this.dataset.title || '' }); }); }); // Глобальные настройки lightbox if (typeof lightbox !== 'undefined') { lightbox.option({ 'resizeDuration': 200, 'wrapAround': true, 'albumLabel': "Изображение %1 из %2", 'fadeDuration': 300, 'imageFadeDuration': 300, 'disableScrolling': true }); } }); </script> ``` Альтернативный вариант: Собственный lightbox без зависимостей Если не хотите подключать внешнюю библиотеку, обновите render-image.html: ```html {{- $alt := .PlainText | safeHTML -}} {{- $lazyLoading := .Page.Site.Params.enableImageLazyLoading | default true -}} {{- $dest := .Destination -}} {{- $isLocal := not (urls.Parse $dest).Scheme -}} {{- $isPage := and (eq .Page.Kind "page") (not .Page.BundleType) -}} {{- $startsWithSlash := hasPrefix $dest "/" -}} {{- $startsWithRelative := hasPrefix $dest "../" -}} {{- if and $dest $isLocal -}} {{- if $startsWithSlash -}} {{- $dest = (relURL (strings.TrimPrefix "/" $dest)) -}} {{- else if and $isPage (not $startsWithRelative) -}} {{ $dest = (printf "../%s" $dest) }} {{- end -}} {{- end -}} {{- $fullUrl := $dest | safeURL -}} {{- if $isLocal -}} {{- $fullUrl = absURL $dest -}} {{- end -}} {{- with .Title -}} <figure class="clickable-image" onclick="openImageModal('{{ $fullUrl }}', '{{ . }}')"> <img src="{{ $dest | safeURL }}" title="{{ . }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} class="hx-cursor-zoom-in hx-rounded-lg hx-shadow hover:hx-shadow-lg hx-transition-all"> <figcaption class="hx-text-sm hx-text-gray-600 dark:hx-text-gray-400 hx-mt-2">{{ . }}</figcaption> </figure> {{- else -}} <div class="clickable-image" onclick="openImageModal('{{ $fullUrl }}', '{{ $alt }}')"> <img src="{{ $dest | safeURL }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} class="hx-cursor-zoom-in hx-rounded-lg hx-shadow hover:hx-shadow-lg hx-transition-all"> </div> {{- end -}} ``` Добавьте в scripts.html: ```html <!-- Ваши существующие скрипты --> <!-- Lightbox функциональность --> <script> // Глобальные функции для lightbox function openImageModal(src, title) { // Создаем модальное окно если его нет let modal = document.getElementById('imageLightbox'); if (!modal) { modal = document.createElement('div'); modal.id = 'imageLightbox'; modal.className = 'hx-fixed hx-inset-0 hx-z-[9999] hx-hidden hx-bg-black/95 hx-flex hx-items-center hx-justify-center'; modal.innerHTML = ` <button onclick="closeImageModal()" class="hx-absolute hx-top-4 hx-right-4 hx-text-white hx-text-3xl hx-z-10">×</button> <div class="hx-relative hx-max-w-4xl hx-w-full hx-p-4"> <img id="lightboxImage" class="hx-max-w-full hx-max-h-[85vh] hx-mx-auto hx-rounded"> <div id="lightboxCaption" class="hx-text-center hx-text-white hx-mt-4"></div> </div> `; document.body.appendChild(modal); // Закрытие по клику на фон modal.addEventListener('click', function(e) { if (e.target === this) closeImageModal(); }); // Закрытие по ESC document.addEventListener('keydown', function(e) { if (e.key === 'Escape') closeImageModal(); }); } // Показываем модальное окно document.getElementById('lightboxImage').src = src; if (title) { document.getElementById('lightboxCaption').textContent = title; document.getElementById('lightboxCaption').classList.remove('hx-hidden'); } else { document.getElementById('lightboxCaption').classList.add('hx-hidden'); } modal.classList.remove('hx-hidden'); document.body.style.overflow = 'hidden'; } function closeImageModal() { const modal = document.getElementById('imageLightbox'); if (modal) { modal.classList.add('hx-hidden'); document.body.style.overflow = ''; } } </script> <!-- Добавьте стили для lightbox --> <style> .clickable-image { cursor: pointer; display: inline-block; } .clickable-image img:hover { opacity: 0.9; transform: translateY(-1px); } #imageLightbox { animation: fadeIn 0.2s ease; } #lightboxImage { animation: zoomIn 0.3s ease; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes zoomIn { from { transform: scale(0.95); opacity: 0; } to { transform: scale(1); opacity: 1; } } </style> ``` Проверка: 1. Обновите файл render-image.html 2. Перезапустите Hugo: hugo server --disableFastRender 3. Используйте в Markdown обычный синтаксис:  4. Проверьте в браузере - теперь при клике на изображение должно открываться увеличенное Если все еще не работает: Проверьте DevTools Console (F12): · Есть ли ошибки JavaScript? · Правильно ли формируются ссылки в HTML? · Загружаются ли изображения? Это решение использует ваш существующий рендеринг изображений и добавляет к нему lightbox функциональность! |
Вторник, 2025-12-16, 16:02
# 450
Проблема с 404 означает, что пути к изображениям формируются неправильно. Нужно использовать правильные URL для lightbox. Давайте исправим ваш render-image.html:
Исправленная версия render-image.html: ```html {{- $alt := .PlainText | safeHTML -}} {{- $lazyLoading := .Page.Site.Params.enableImageLazyLoading | default true -}} {{- $dest := .Destination -}} {{/* Определяем абсолютный URL для изображения */}} {{- $imageUrl := "" -}} {{- $fullImageUrl := "" -}} {{/* Обрабатываем локальные изображения */}} {{- if (urls.Parse $dest).Scheme -}} {{/* Внешнее изображение */}} {{- $imageUrl = $dest -}} {{- $fullImageUrl = $dest -}} {{- else -}} {{/* Локальное изображение */}} {{- if hasPrefix $dest "/" -}} {{/* Путь от корня сайта */}} {{- $imageUrl = relURL (strings.TrimPrefix "/" $dest) -}} {{- $fullImageUrl = absURL (strings.TrimPrefix "/" $dest) -}} {{- else -}} {{/* Относительный путь */}} {{- $imageUrl = $dest -}} {{- /* Получаем абсолютный URL */}} {{- $fullImageUrl = (printf "%s/%s" (strings.TrimSuffix "/" .Page.Permalink) $dest) -}} {{- end -}} {{- end -}} {{/* Отладочная информация (удалите после проверки) */}} {{/* <!-- Debug: dest={{ $dest }}, imageUrl={{ $imageUrl }}, fullImageUrl={{ $fullImageUrl }} --> */}} {{- with .Title -}} <figure class="image-container"> <a href="{{ $fullImageUrl | safeURL }}" class="lightbox-trigger" data-image="{{ $fullImageUrl | safeURL }}" data-title="{{ . }}"> <img src="{{ $imageUrl | safeURL }}" title="{{ . }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} class="hx-cursor-zoom-in hx-rounded-lg hx-shadow hover:hx-shadow-lg hx-transition-all"> </a> <figcaption class="hx-text-sm hx-text-gray-600 dark:hx-text-gray-400 hx-mt-2">{{ . }}</figcaption> </figure> {{- else -}} <div class="image-container"> <a href="{{ $fullImageUrl | safeURL }}" class="lightbox-trigger" data-image="{{ $fullImageUrl | safeURL }}" data-title="{{ $alt }}"> <img src="{{ $imageUrl | safeURL }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} class="hx-cursor-zoom-in hx-rounded-lg hx-shadow hover:hx-shadow-lg hx-transition-all"> </a> </div> {{- end -}} ``` Обновите scripts.html - упрощенная версия: ```html <!-- Ваши существующие скрипты --> <!-- Lightbox CSS --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.4/css/lightbox.min.css" /> <!-- Lightbox JavaScript --> <script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.4/js/lightbox.min.js"></script> <script> // Ждем полной загрузки страницы document.addEventListener('DOMContentLoaded', function() { console.log('Lightbox скрипт загружен'); // Инициализируем lightbox с настройками if (typeof lightbox !== 'undefined') { lightbox.option({ 'resizeDuration': 200, 'wrapAround': true, 'showImageNumberLabel': true, 'albumLabel': "Изображение %1 из %2", 'disableScrolling': true, 'fadeDuration': 300, 'imageFadeDuration': 300, 'positionFromTop': 50 }); console.log('Lightbox инициализирован'); } // Добавляем обработчики кликов для отладки const imageLinks = document.querySelectorAll('.lightbox-trigger'); console.log('Найдено ссылок с изображениями:', imageLinks.length); imageLinks.forEach((link, index) => { const img = link.querySelector('img'); console.log(`Изображение ${index + 1}:`, { src: img?.src, alt: img?.alt, link: link.href }); // Предотвращаем переход по ссылке, если lightbox работает link.addEventListener('click', function(e) { console.log('Клик по изображению:', this.href); // Если lightbox не сработал, позволяем браузеру открыть изображение if (typeof lightbox === 'undefined') { console.log('Lightbox не найден, открываю в новой вкладке'); window.open(this.href, '_blank'); } }); }); }); </script> <!-- Стили для отладки --> <style> .image-container { display: inline-block; position: relative; } .image-container img { display: block; max-width: 100%; height: auto; } .lightbox-trigger { display: block; cursor: zoom-in; } .lightbox-trigger:hover img { opacity: 0.9; transform: scale(1.01); } </style> ``` Альтернатива: Простой кастомный lightbox (100% рабочий) Если Lightbox2 не работает, вот простой кастомный вариант: В render-image.html (упрощенная версия): ```html {{- $alt := .PlainText | safeHTML -}} {{- $lazyLoading := .Page.Site.Params.enableImageLazyLoading | default true -}} {{- $dest := .Destination -}} {{/* Простая обработка путей */}} {{- $imageUrl := $dest -}} {{- if not (urls.Parse $dest).Scheme -}} {{- if hasPrefix $dest "/" -}} {{- $imageUrl = relURL (strings.TrimPrefix "/" $dest) -}} {{- end -}} {{- end -}} <div class="custom-image-wrapper" onclick="openCustomLightbox('{{ $imageUrl | safeURL }}', '{{ $alt }}')"> <img src="{{ $imageUrl | safeURL }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} class="hx-cursor-zoom-in hx-rounded-lg hx-shadow hover:hx-shadow-lg hx-transition-all"> {{- with .Title -}} <div class="hx-text-sm hx-text-gray-600 dark:hx-text-gray-400 hx-mt-2">{{ . }}</div> {{- end -}} </div> ``` В scripts.html добавьте: ```html <script> // Простой кастомный lightbox function openCustomLightbox(src, alt) { console.log('Открываем изображение:', src); // Создаем модальное окно const modal = document.createElement('div'); modal.id = 'customLightbox'; modal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.95); display: flex; align-items: center; justify-content: center; z-index: 9999; cursor: pointer; animation: fadeIn 0.3s ease; `; // Создаем изображение const img = document.createElement('img'); img.src = src; img.alt = alt; img.style.cssText = ` max-width: 90vw; max-height: 90vh; border-radius: 8px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); cursor: default; animation: zoomIn 0.3s ease; `; // Создаем кнопку закрытия const closeBtn = document.createElement('button'); closeBtn.innerHTML = '×'; closeBtn.style.cssText = ` position: absolute; top: 20px; right: 20px; background: none; border: none; color: white; font-size: 40px; cursor: pointer; z-index: 10000; `; closeBtn.onclick = closeCustomLightbox; // Добавляем элементы modal.appendChild(closeBtn); modal.appendChild(img); document.body.appendChild(modal); // Блокируем прокрутку document.body.style.overflow = 'hidden'; // Закрытие по клику на фон modal.onclick = function(e) { if (e.target === modal) closeCustomLightbox(); }; // Закрытие по ESC document.addEventListener('keydown', function closeOnEsc(e) { if (e.key === 'Escape') { closeCustomLightbox(); document.removeEventListener('keydown', closeOnEsc); } }); } function closeCustomLightbox() { const modal = document.getElementById('customLightbox'); if (modal) { modal.remove(); document.body.style.overflow = ''; } } // Добавляем CSS анимации const style = document.createElement('style'); style.textContent = ` @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes zoomIn { from { transform: scale(0.8); opacity: 0; } to { transform: scale(1); opacity: 1; } } `; document.head.appendChild(style); // Инициализация при загрузке страницы document.addEventListener('DOMContentLoaded', function() { console.log('Кастомный lightbox готов к работе'); }); </script> ``` Отладка проблемы 404: 1. Проверьте пути к изображениям в DevTools: · Откройте Console (F12) · Нажмите на изображение · Посмотрите какой URL пытается открыть lightbox 2. Проверьте, где лежат изображения: · Если в static/images/ - используйте /images/filename.jpg · Если в папке с постом - используйте относительный путь 3. Временное решение для теста: Добавьте в начало контента Markdown: ```markdown  ``` Убедитесь, что файл static/images/test.jpg существует Важно: Hugo обрабатывает пути по-разному в зависимости от конфигурации. Если у вас все еще проблемы, покажите: 1. Как вы ссылаетесь на изображения в Markdown 2. Структуру ваших папок 3. Консольные ошибки из браузера |
| |||


