Отзывы и предложения к софту от AleXStam
Поговорим о...
Ррррп
Прикрепления:
3666322.noext (61.4 Kb)
Ооор
Прикрепления:
3012760.noext (62.4 Kb)
Ддд
Прикрепления:
6656892.noext (65.3 Kb)
Роррпс
Прикрепления:
zzzzshsh.noext (66.2 Kb)
Ооооо
Прикрепления:
8421743.noext (66.2 Kb)
<!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)
---------------------------------------------

// Ограничиваем максимальное количество колонок шириной контейнера
const containerWidth = container.clientWidth;
const minColumnWidth = 400; // Минимальная ширина колонки
const maxPossibleColumns = Math.max(1, Math.floor(containerWidth / minColumnWidth));
maxColumns = Math.min(maxColumns, maxPossibleColumns);

// Создаем колонки
const columns = Array.from({ length: maxColumns }, () => {
const col = document.createElement('div');
col.className = 'icon-comparison-masonry-column';
return col;
});

// Распределяем группы по колонкам с учетом высоты
if (maxColumns === 1) {
// Если только одна колонка, просто добавляем все группы
allGroupChunks.forEach(chunk => {
const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection);
columns[0].appendChild(section);
});
} else {
// Для нескольких колонок используем алгоритм распределения
// Оцениваем примерную высоту каждой группы
const groupHeights = allGroupChunks.map(chunk => {
// Примерная высота: 60px заголовок + 55px на каждую строку
return 60 + (chunk.icons.length * 55);
});

// Массив для хранения текущей высоты каждой колонки
const columnHeights = new Array(maxColumns).fill(0);

// Распределяем группы по колонкам, чтобы выровнять высоту
allGroupChunks.forEach((chunk, index) => {
// Находим колонку с минимальной текущей высотой
let minHeightIndex = 0;
for (let i = 1; i < maxColumns; i++) {
if (columnHeights[i] < columnHeights[minHeightIndex]) {
minHeightIndex = i;
}
}

// Добавляем группу в колонку с минимальной высотой
const section = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection);
columns[minHeightIndex].appendChild(section);

// Обновляем высоту колонки
columnHeights[minHeightIndex] += groupHeights[index];
});
}

// Добавляем только непустые колонки
columns.forEach(col => {
if (col.children.length > 0) {
columnsContainer.appendChild(col);
}
});

container.appendChild(columnsContainer);
iconComparisonUpdateStats();

// Добавляем анимацию появления
setTimeout(() => {
document.querySelectorAll('.icon-comparison-group').forEach((group, index) => {
group.style.animationDelay = `${index * 0.03}s`;
});
}, 10);
}

function renderGroupsForPrint() {
const container = document.getElementById('iconComparisonContainer');
container.innerHTML = '';

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
});
});
}

// Создаем контейнер для печати с двумя колонками
const printContainer = document.createElement('div');
printContainer.className = 'icon-comparison-print-columns';

// Добавляем все группы (уже разбитые на части)
allGroupChunks.forEach((chunk, index) => {
const groupSection = iconComparisonCreateGroupSection(chunk.name, chunk.icons, chunk.isNoGroupSection, true);
groupSection.className = 'icon-comparison-print-group';

// Для очень больших групп (более 25 значков) добавляем принудительный разрыв страницы
if (index > 0 && chunk.icons.length > 25) {
groupSection.classList.add('force-page-break');
}

printContainer.appendChild(groupSection);
});

container.appendChild(printContainer);
iconComparisonUpdateStats();
}

function iconComparisonCreateGroupSection(groupName, icons, isNoGroupSection = false, forPrint = false) {
const section = document.createElement('div');
section.className = forPrint ? 'icon-comparison-print-group' : 'icon-comparison-group';

const header = document.createElement('div');
header.className = 'icon-comparison-group-title';

const titleSpan = document.createElement('span');
titleSpan.textContent = groupName;

// Для печати не показываем счетчик
if (!forPrint) {
const countSpan = document.createElement('span');
countSpan.className = 'group-count';
countSpan.textContent = icons.length;
header.appendChild(countSpan);
}

header.appendChild(titleSpan);
section.appendChild(header);

const column = iconComparisonCreateIconColumn(icons, forPrint);
section.appendChild(column);

return section;
}

function iconComparisonCreateIconColumn(icons, forPrint = false) {
const column = document.createElement('div');
column.className = 'icon-comparison-panel';

const header = document.createElement('div');
header.className = 'icon-comparison-panel-header';

const beforeLabel = document.createElement('div');
beforeLabel.className = 'icon-comparison-header-label';
beforeLabel.textContent = 'ДО';

const arrowSpace = document.createElement('div');
arrowSpace.className = 'icon-comparison-arrow';
arrowSpace.innerHTML = '→';

const afterLabel = document.createElement('div');
afterLabel.className = 'icon-comparison-header-label';
afterLabel.textContent = 'ПОСЛЕ';

const nameLabel = document.createElement('div');
nameLabel.className = 'icon-comparison-header-label';
nameLabel.textContent = 'Название';
nameLabel.style.textAlign = 'center';

header.appendChild(beforeLabel);
header.appendChild(arrowSpace);
header.appendChild(afterLabel);
header.appendChild(nameLabel);

column.appendChild(header);

icons.forEach(icon => {
column.appendChild(iconComparisonCreateIconRow(icon, forPrint));
});

return column;
}

function iconComparisonCreateIconRow(icon, forPrint = false) {
const row = document.createElement('div');
row.className = 'icon-comparison-row';

const beforeContainer = document.createElement('div');
const afterContainer = document.createElement('div');

// Используем оригинальную функцию загрузки изображений
Promise.all([
iconComparisonLoadImage(icon.before, `${icon.name} (до)`),
iconComparisonLoadImage(icon.after, `${icon.name} (после)`)
]).then(([beforeResult, afterResult]) => {
const beforeImg = beforeResult.element;
const afterImg = afterResult.element;

beforeImg.className = 'icon-comparison-image' + (beforeResult.processed ? ' icon-comparison-bmp-processed' : '');
afterImg.className = 'icon-comparison-image' + (afterResult.processed ? ' icon-comparison-bmp-processed' : '');

beforeContainer.appendChild(beforeImg);
afterContainer.appendChild(afterImg);
});

const arrow = document.createElement('div');
arrow.className = 'icon-comparison-arrow';
arrow.innerHTML = '→';

const infoDiv = document.createElement('div');
infoDiv.className = 'icon-comparison-info';

const name = document.createElement('div');
name.className = 'icon-comparison-name';
name.textContent = icon.name;
name.title = icon.name;

infoDiv.appendChild(name);

row.appendChild(beforeContainer);
row.appendChild(arrow);
row.appendChild(afterContainer);
row.appendChild(infoDiv);

return row;
}

// Оригинальные функции обработки изображений
function iconComparisonProcessBMP(img) {
return new Promise((resolve) => {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;

ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';

ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;

for (let i = 0; i < data.length; i += 4) {
if (data[i] === 255 && data[i + 1] === 0 && data[i + 2] === 255) {
data[i + 3] = 0;
}
}

ctx.putImageData(imageData, 0, 0);

const processedImg = new Image();
processedImg.onload = () => resolve({
element: processedImg,
processed: true
});
processedImg.src = canvas.toDataURL('image/png');
} catch (error) {
console.warn('Ошибка обработки BMP:', error);
resolve({
element: img,
processed: false
});
}
});
}

function iconComparisonLoadImage(src, alt) {
return new Promise((resolve) => {
const img = new Image();

img.onload = async function() {
try {
if (src.toLowerCase().endsWith('.bmp')) {
const result = await iconComparisonProcessBMP(img);
result.element.alt = alt;
resolve(result);
} else {
img.alt = alt;
resolve({ element: img, processed: false });
}
} catch (error) {
console.warn('Ошибка обработки изображения:', error, src);
img.alt = alt;
resolve({ element: img, processed: false });
}
};

img.onerror = function() {
console.warn('Ошибка загрузки изображения:', src);
const placeholder = new Image();
placeholder.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjQwIiBoZWlnaHQ9IjQwIiBmaWxsPSIjRjVGNUY1IiByeD0iNCIvPgo8cGF0aCBkPSJNMjAgMjVDMjEuMTA0NiAyNSAyMiAyNC4xMDQ2IDIyIDIzVjE3QzIyIDE1Ljg5NTQgMjEuMTA0NiAxNSAyMCAxNUMxOC44OTU0IDE1IDE4IDE1Ljg5NTQgMTggMTdWMjNDMTggMjQuMTA0NiAxOC44OTU0IDI1IDIwIDI1WiIgZmlsbD0iIzk5OTk5OSIvPgo8cGF0aCBkPSJNMjAgMjhDMjEuMTA0NiAyOCAyMiAyNy4xMDQ2IDIyIDI2QzIyIDI0Ljg5NTQgMjEuMTA0NiAyNCAyMCAyNEMxOC44OTU0IDI0IDE4IDI0Ljg5NTQgMTggMjZDMTggMjcuMTA0NiAxOC44OTA1IDI4IDIwIDI4WiIgZmlsbD0iIzk5OTk5OSIvPgo8L3N2Zz4K';
placeholder.alt = alt;
resolve({ element: placeholder, processed: false });
};

img.src = src;
});
}

function iconComparisonGroupIcons(icons) {
const groups = {};
const noGroup = [];

icons.forEach(icon => {
if (icon.group && icon.group.trim() !== '') {
if (!groups[icon.group]) {
groups[icon.group] = [];
}
groups[icon.group].push(icon);
} else {
noGroup.push(icon);
}
});

return { groups, noGroup };
}

function iconComparisonRenderWithoutGroups() {
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;
}

const sortedIcons = [...iconComparisonFilteredIcons].sort((a, b) => {
const groupA = a.group || 'zzz';
const groupB = b.group || 'zzz';

if (groupA !== groupB) {
return groupA.localeCompare(groupB);
}

return a.name.localeCompare(b.name);
});

// Для печати создаем специальный контейнер с двумя колонками
if (isPrintMode) {
const itemsPerColumn = Math.ceil(sortedIcons.length / 2);
const columnsContainer = document.createElement('div');
columnsContainer.className = 'icon-comparison-no-groups-container';

// Создаем две колонки
for (let i = 0; i < 2; i++) {
const startIndex = i * itemsPerColumn;
const endIndex = startIndex + itemsPerColumn;
const columnIcons = sortedIcons.slice(startIndex, endIndex);

if (columnIcons.length > 0) {
const column = document.createElement('div');
column.className = 'icon-comparison-no-groups-column';
const panel = iconComparisonCreateIconColumn(columnIcons, true);
column.appendChild(panel);
columnsContainer.appendChild(column);
}
}

container.appendChild(columnsContainer);
} else {
// Для обычного отображения используем старую логику
const itemsPerColumn = 20;
const columnCount = Math.ceil(sortedIcons.length / itemsPerColumn);

for (let i = 0; i < columnCount; i++) {
const startIndex = i * itemsPerColumn;
const endIndex = startIndex + itemsPerColumn;
const columnIcons = sortedIcons.slice(startIndex, endIndex);
const column = iconComparisonCreateIconColumn(columnIcons);
container.appendChild(column);
}
}

iconComparisonUpdateStats();
}

function iconComparisonFilterIcons(searchTerm = '') {
let filtered = [...iconComparisonAllIcons];

// Фильтрация по поисковому запросу
if (searchTerm) {
const term = searchTerm.toLowerCase();
filtered = filtered.filter(icon =>
icon.name.toLowerCase().includes(term) ||
(icon.group && icon.group.toLowerCase().includes(term))
);
}

// Фильтрация по выбранным группам
if (selectedGroups.size > 0) {
filtered = filtered.filter(icon => {
// Если у значка нет группы, проверяем наличие 'no_group'
if (!icon.group || icon.group.trim() === '') {
return selectedGroups.has('no_group');
}
return selectedGroups.has(icon.group);
});
}

iconComparisonFilteredIcons = filtered;
iconComparisonRenderFilteredIcons();

// Обновляем текст кнопки выбора групп
updateGroupsDropdownText();
}

function iconComparisonRenderFilteredIcons() {
if (iconComparisonShowGroups) {
iconComparisonRenderWithGroups();
} else {
iconComparisonRenderWithoutGroups();
}
}

function iconComparisonUpdateStats() {
document.getElementById('iconComparisonTotalCount').textContent = iconComparisonAllIcons.length;
const shownCount = selectedGroups.size === 0 ? 0 : iconComparisonFilteredIcons.length;
document.getElementById('iconComparisonShownCount').textContent = shownCount;
}

function updateGroupsDropdownText() {
const toggle = document.getElementById('groupsDropdownToggle');
const groupsSpan = toggle.querySelector('span:first-child');

if (selectedGroups.size === 0) {
groupsSpan.textContent = 'Выбрать группы';
} else if (selectedGroups.size === 1) {
const groupName = Array.from(selectedGroups)[0];
groupsSpan.textContent = groupName === 'no_group' ? 'Прочие' : groupName;
} else {
groupsSpan.textContent = `Выбрано групп: ${selectedGroups.size}`;
}
}

function iconComparisonInitGroupSelect(groups) {
const dropdownContent = document.getElementById('groupsDropdownContent');

// Добавляем чекбокс "Выбрать все"
const selectAllOption = document.createElement('div');
selectAllOption.className = 'group-option';
selectAllOption.innerHTML = `
<input type="checkbox" id="selectAllGroups" class="group-checkbox" checked>
<label for="selectAllGroups" class="group-name">Все группы</label>
`;
dropdownContent.appendChild(selectAllOption);

const selectAllCheckbox = selectAllOption.querySelector('.group-checkbox');

// Добавляем чекбоксы для каждой группы
groups.sort((a, b) => a.localeCompare(b, 'ru')).forEach(group => {
const groupOption = document.createElement('div');
groupOption.className = 'group-option';

const checkboxId = `group_${group.replace(/\s+/g, '_')}`;

groupOption.innerHTML = `
<input type="checkbox" id="${checkboxId}" class="group-checkbox" checked>
<label for="${checkboxId}" class="group-name">${group}</label>
`;

dropdownContent.appendChild(groupOption);

// Добавляем группу в выбранные
selectedGroups.add(group);

// Обработчик изменения чекбокса
const checkbox = groupOption.querySelector('.group-checkbox');
checkbox.addEventListener('change', function() {
if (this.checked) {
selectedGroups.add(group);
} else {
selectedGroups.delete(group);
}

// Обновляем состояние "Выбрать все"
updateSelectAllCheckbox();

// Фильтруем значки
const searchTerm = document.getElementById('iconComparisonSearch').value;
iconComparisonFilterIcons(searchTerm);
});
});

// Проверяем, есть ли значки без группы
const hasNoGroup = iconComparisonAllIcons.some(icon => !icon.group || icon.group.trim() === '');

if (hasNoGroup) {
const separator = document.createElement('div');
separator.style.height = '1px';
separator.style.background = '#ddd';
separator.style.margin = '5px 0';
dropdownContent.appendChild(separator);

const noGroupOption = document.createElement('div');
noGroupOption.className = 'group-option';
noGroupOption.innerHTML = `
<input type="checkbox" id="group_no_group" class="group-checkbox" checked>
<label for="group_no_group" class="group-name">Прочие (без группы)</label>
`;

dropdownContent.appendChild(noGroupOption);

// Добавляем 'no_group' в выбранные
selectedGroups.add('no_group');

// Обработчик изменения чекбокса
const noGroupCheckbox = noGroupOption.querySelector('.group-checkbox');
noGroupCheckbox.addEventListener('change', function() {
if (this.checked) {
selectedGroups.add('no_group');
} else {
selectedGroups.delete('no_group');
}

// Обновляем состояние "Выбрать все"
updateSelectAllCheckbox();

// Фильтруем значки
const searchTerm = document.getElementById('iconComparisonSearch').value;
iconComparisonFilterIcons(searchTerm);
});
}

// Обработчик для "Выбрать все"
selectAllCheckbox.addEventListener('change', function() {
const allCheckboxes = dropdownContent.querySelectorAll('.group-checkbox');
allCheckboxes.forEach(checkbox => {
if (checkbox.id !== 'selectAllGroups') {
checkbox.checked = this.checked;

// Обновляем selectedGroups
const groupName = checkbox.id === 'group_no_group' ? 'no_group' :
checkbox.id.replace('group_', '').replace(/_/g, ' ');

if (this.checked) {
selectedGroups.add(groupName);
} else {
selectedGroups.delete(groupName);
}
}
});

// Фильтруем значки
const searchTerm = document.getElementById('iconComparisonSearch').value;
iconComparisonFilterIcons(searchTerm);
});

// Инициализируем текст кнопки
updateGroupsDropdownText();
}

function updateSelectAllCheckbox() {
const allCheckboxes = document.querySelectorAll('.group-checkbox:not(#selectAllGroups)');
const checkedCheckboxes = document.querySelectorAll('.group-checkbox:not(#selectAllGroups):checked');

const selectAllCheckbox = document.getElementById('selectAllGroups');
if (checkedCheckboxes.length === allCheckboxes.length) {
selectAllCheckbox.checked = true;
selectAllCheckbox.indeterminate = false;
} else if (checkedCheckboxes.length === 0) {
selectAllCheckbox.checked = false;
selectAllCheckbox.indeterminate = false;
} else {
selectAllCheckbox.checked = false;
selectAllCheckbox.indeterminate = true;
}
}

async function iconComparisonLoadIcons() {
try {
// Загружаем данные из image_list.js
const script = document.createElement('script');
script.src = 'image_list.js';
document.head.appendChild(script);

script.onload = function() {
try {
const data = loadBeforeAfterImages();

if (data && data.images) {
const processedImages = data.images.map(icon => {
const displayName = icon.name.replace(/^32x32_/, '');
return {
...icon,
name: displayName
};
});

iconComparisonAllIcons = processedImages;
iconComparisonFilteredIcons = [...iconComparisonAllIcons];
iconComparisonAvailableGroups = data.availableGroups || [];

iconComparisonInitGroupSelect(iconComparisonAvailableGroups);
iconComparisonRenderFilteredIcons();
iconComparisonUpdateStats();

console.log(`Загружено ${iconComparisonAllIcons.length} значков, ${iconComparisonAvailableGroups.length} групп`);
} else {
showError('Ошибка загрузки данных');
}
} catch (error) {
console.error('Ошибка обработки данных:', error);
showError('Ошибка обработки данных');
}
};

script.onerror = function() {
showError('Ошибка загрузки файла image_list.js');
};

} catch (error) {
console.error('Ошибка загрузки значков:', error);
showError('Ошибка загрузки значков');
}
}

function showError(message) {
const container = document.getElementById('iconComparisonContainer');
container.innerHTML = `
<div class="icon-comparison-no-results">
<div class="no-results-icon">
<i class="fas fa-exclamation-triangle"></i>
</div>
<div>${message}</div>
</div>
`;
}

function iconComparisonSetupPrintHandler() {
window.addEventListener('beforeprint', function() {
console.log('Before print - activating print mode');
isPrintMode = true;
if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) {
setTimeout(() => {
iconComparisonRenderWithGroups();
}, 50);
} else if (iconComparisonFilteredIcons.length > 0) {
setTimeout(() => {
iconComparisonRenderWithoutGroups();
}, 50);
}
});

window.addEventListener('afterprint', function() {
console.log('After print - deactivating print mode');
isPrintMode = false;
if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) {
setTimeout(() => {
iconComparisonRenderWithGroups();
}, 50);
} else if (iconComparisonFilteredIcons.length > 0) {
setTimeout(() => {
iconComparisonRenderWithoutGroups();
}, 50);
}
});
}

function iconComparisonSetupResizeHandler() {
let resizeTimeout;
let lastWidth = 0;

function handleResize() {
if (isPrintMode) return;

const currentWidth = document.getElementById('iconComparisonContainer').clientWidth;

if (Math.abs(currentWidth - lastWidth) > 20) {
lastWidth = currentWidth;

if (iconComparisonShowGroups && iconComparisonFilteredIcons.length > 0) {
iconComparisonRenderWithGroups();
} else if (iconComparisonFilteredIcons.length > 0) {
iconComparisonRenderWithoutGroups();
}
}
}

window.addEventListener('resize', function() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(handleResize, 150);
});
}

function iconComparisonSetupSearch() {
const searchInput = document.getElementById('iconComparisonSearch');
let searchTimeout;

searchInput.addEventListener('input', function(e) {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
iconComparisonFilterIcons(e.target.value);
}, 300);
});

searchInput.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
this.value = '';
iconComparisonFilterIcons('');
}
});
}

function iconComparisonSetupToggle() {
const toggle = document.getElementById('iconComparisonShowGroups');

toggle.addEventListener('change', function() {
iconComparisonShowGroups = this.checked;
iconComparisonRenderFilteredIcons();
});
}

function setupGroupsDropdown() {
const toggle = document.getElementById('groupsDropdownToggle');
const content = document.getElementById('groupsDropdownContent');

toggle.addEventListener('click', function(e) {
e.stopPropagation();
this.classList.toggle('active');
content.classList.toggle('show');
});

// Закрытие при клике вне dropdown
document.addEventListener('click', function(e) {
if (!e.target.closest('.groups-dropdown')) {
toggle.classList.remove('active');
content.classList.remove('show');
}
});

// Предотвращаем закрытие при клике внутри dropdown
content.addEventListener('click', function(e) {
e.stopPropagation();
});
}

// Инициализация при загрузке страницы
document.addEventListener('DOMContentLoaded', function() {
console.log('Инициализация системы сравнения значков ИСИХОГИ');
iconComparisonLoadIcons();
iconComparisonSetupSearch();
iconComparisonSetupToggle();
iconComparisonSetupResizeHandler();
iconComparisonSetupPrintHandler();
setupGroupsDropdown();
});
</script>
</body>
</html>

<!-- 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 }}
{{ 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 }}
<!-- В разделе 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 }}
{{ 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 }}
{{ 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 }}
{{ 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 }}
{{- $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)
---------------------------------------------
<!DOCTYPE html>
<html lang="{{ .Site.Language.Lang }}">
{{- partial "head.html" . -}}
<body dir="{{ .Site.Language.LanguageDirection | default `ltr` }}">
{{- partial "navbar.html" . -}}
{{- block "main" . }}{{ end -}}
{{- if or (eq .Site.Params.footer.enable nil) (.Site.Params.footer.enable) }}
{{ partial "footer.html" . }}
{{ end }}
{{ partial "scripts.html" . }}
</body>
</html>

Отлично! Теперь понятно. У вас уже есть кастомный рендеринг изображений. Нужно его модифицировать для добавления 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 обычный синтаксис: ![Описание](/images/photo.jpg)
4. Проверьте в браузере - теперь при клике на изображение должно открываться увеличенное

Если все еще не работает:

Проверьте DevTools Console (F12):

· Есть ли ошибки JavaScript?
· Правильно ли формируются ссылки в HTML?
· Загружаются ли изображения?

Это решение использует ваш существующий рендеринг изображений и добавляет к нему lightbox функциональность!
Проблема с 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
![Тест](/images/test.jpg)
```
Убедитесь, что файл static/images/test.jpg существует

Важно: Hugo обрабатывает пути по-разному в зависимости от конфигурации. Если у вас все еще проблемы, покажите:

1. Как вы ссылаетесь на изображения в Markdown
2. Структуру ваших папок
3. Консольные ошибки из браузера
Поиск:
Новый ответ
Имя:
Текст сообщения: