Отзывы и предложения к софту от AleXStam
  • Страница 31 из 31
  • «
  • 1
  • 2
  • 29
  • 30
  • 31
Поговорим о...
Проблема в том, что при изменении render-image.html нарушился вывод изображений. Вернем рабочий вариант и добавим lightbox правильно.

Восстановите оригинальный render-image.html и добавьте lightbox отдельно:

1. Верните оригинальный 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 -}}

{{- 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 -}}
```

2. Добавьте lightbox через JavaScript в scripts.html:

```html
<!-- Ваши существующие скрипты -->

<!-- Lightbox CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.4/css/lightbox.min.css" />

<!-- Lightbox JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.4/js/lightbox.min.js"></script>

<script>
// Ждем полной загрузки DOM
document.addEventListener('DOMContentLoaded', function() {
// Находим ВСЕ изображения на странице (в основном контенте)
const images = document.querySelectorAll('main img, .content img, article img');

images.forEach((img, index) => {
// Пропускаем маленькие изображения (иконки)
if (img.width < 50 || img.height < 50) return;

// Пропускаем изображения, которые уже в ссылках
if (img.parentElement.tagName === 'A') return;

// Создаем ссылку для lightbox
const wrapper = document.createElement('a');
wrapper.href = img.src;
wrapper.setAttribute('data-lightbox', 'gallery');
wrapper.setAttribute('data-title', img.alt || '');

// Обертываем изображение
img.parentNode.insertBefore(wrapper, img);
wrapper.appendChild(img);

// Добавляем стили для курсора
img.style.cursor = 'zoom-in';
img.style.transition = 'opacity 0.2s';

// Эффект при наведении
img.addEventListener('mouseenter', () => {
img.style.opacity = '0.9';
});

img.addEventListener('mouseleave', () => {
img.style.opacity = '1';
});
});

// Настраиваем lightbox
if (typeof lightbox !== 'undefined') {
lightbox.option({
'resizeDuration': 200,
'wrapAround': true,
'showImageNumberLabel': true,
'albumLabel': 'Изображение %1 из %2',
'fadeDuration': 300,
'imageFadeDuration': 300
});
}
});
</script>

<!-- Простые стили для изображений -->
<style>
/* Стили для всех изображений в контенте */
main img, .content img, article img {
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
max-width: 100%;
height: auto;
}

main img:hover, .content img:hover, article img:hover {
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
}

/* Убираем стандартные стили lightbox для ссылок */
a[data-lightbox] {
display: inline-block;
border: none !important;
text-decoration: none !important;
}
</style>
```

Альтернативный вариант: Простой кастомный lightbox без библиотек

Если не хотите использовать lightbox2, вот чистый JavaScript вариант:

Добавьте в scripts.html:

```html
<script>
// Простой кастомный lightbox
class SimpleLightbox {
constructor() {
this.modal = null;
this.modalImg = null;
this.currentIndex = 0;
this.images = [];
this.init();
}

init() {
// Создаем модальное окно
this.createModal();

// Находим все изображения после загрузки DOM
document.addEventListener('DOMContentLoaded', () => {
this.attachToImages();
});
}

createModal() {
this.modal = document.createElement('div');
this.modal.className = 'simple-lightbox';
this.modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.95);
display: none;
align-items: center;
justify-content: center;
z-index: 9999;
cursor: pointer;
`;

const container = document.createElement('div');
container.style.cssText = `
position: relative;
max-width: 90vw;
max-height: 90vh;
`;

this.modalImg = document.createElement('img');
this.modalImg.style.cssText = `
max-width: 100%;
max-height: 85vh;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
cursor: default;
`;

const closeBtn = document.createElement('button');
closeBtn.innerHTML = '×';
closeBtn.style.cssText = `
position: absolute;
top: -40px;
right: 0;
background: none;
border: none;
color: white;
font-size: 40px;
cursor: pointer;
line-height: 1;
padding: 0;
width: 40px;
height: 40px;
`;
closeBtn.onclick = () => this.close();

container.appendChild(closeBtn);
container.appendChild(this.modalImg);
this.modal.appendChild(container);
document.body.appendChild(this.modal);

// Закрытие по клику на фон
this.modal.onclick = (e) => {
if (e.target === this.modal) this.close();
};

// Закрытие по ESC
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') this.close();
});
}

attachToImages() {
// Находим все изображения в основном контенте
const content = document.querySelector('main, .content, article');
if (!content) return;

this.images = Array.from(content.querySelectorAll('img'))
.filter(img => img.width > 50 && img.height > 50);

this.images.forEach((img, index) => {
// Добавляем стили
img.style.cursor = 'zoom-in';
img.style.transition = 'opacity 0.2s';

// Эффект при наведении
img.addEventListener('mouseenter', () => {
img.style.opacity = '0.9';
});

img.addEventListener('mouseleave', () => {
img.style.opacity = '1';
});

// Обработчик клика
img.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
this.currentIndex = index;
this.open(img.src, img.alt);
});
});

console.log(`Найдено ${this.images.length} изображений для lightbox`);
}

open(src, alt) {
this.modalImg.src = src;
this.modalImg.alt = alt;
this.modal.style.display = 'flex';
document.body.style.overflow = 'hidden';

// Анимация появления
this.modal.style.animation = 'fadeIn 0.3s ease';
this.modalImg.style.animation = 'zoomIn 0.3s ease';
}

close() {
this.modal.style.display = 'none';
document.body.style.overflow = '';
}
}

// Инициализируем lightbox когда DOM загружен
document.addEventListener('DOMContentLoaded', function() {
new SimpleLightbox();

// Добавляем CSS анимации
const style = document.createElement('style');
style.textContent = `
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes zoomIn {
from {
transform: scale(0.9);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}

/* Стили для изображений */
main img, .content img, article img {
cursor: zoom-in;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transition: all 0.2s ease;
}

main img:hover, .content img:hover, article img:hover {
opacity: 0.9;
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
}
`;
document.head.appendChild(style);
});
</script>
```

Если изображения все еще не показываются:

1. Проверьте консоль браузера (F12) на ошибки
2. Временно добавьте тестовое изображение в ваш пост:

```markdown
![Тестовое изображение](https://picsum.photos/600/400)
```

1. Проверьте структуру HTML - как выглядят изображения в инспекторе элементов

Быстрая диагностика:

Добавьте в конец single.html перед {{ end }}:

```html
<script>
console.log('Страница загружена');
console.log('Всего изображений на странице:', document.querySelectorAll('img').length);
console.log('Изображения в .content:', document.querySelectorAll('.content img').length);

// Простой тест - добавим красную рамку всем изображениям
setTimeout(() => {
document.querySelectorAll('.content img').forEach(img => {
img.style.border = '2px solid red';
console.log('Изображение найдено:', img.src);
});
}, 1000);
</script>
```

Шаги для восстановления:

1. Верните оригинальный render-image.html
2. Добавьте один из JavaScript вариантов в scripts.html
3. Перезапустите Hugo: hugo server --disableFastRender --forceSyncStatic
4. Проверьте в браузере с очищенным кэшем (Ctrl+F5)

Это должно восстановить отображение изображений и добавить функциональность увеличения.
Понял! Нужен кастомный 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 -}}

{{- with .Title -}}
<figure class="article-image">
<img src="{{ $dest | safeURL }}" title="{{ . }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} class="article-image__img" />
<figcaption class="article-image__caption">{{ . }}</figcaption>
</figure>
{{- else -}}
<img src="{{ $dest | safeURL }}" alt="{{ $alt }}" {{ if $lazyLoading }}loading="lazy"{{ end }} class="article-image article-image__img" />
{{- end -}}
```

Добавьте в scripts.html (перед закрывающим </body>):

```html
<!-- Ваши существующие скрипты -->

<!-- Кастомный Lightbox -->
<script>
class ArticleLightbox {
constructor() {
this.modal = null;
this.modalImage = null;
this.caption = null;
this.images = [];
this.currentIndex = 0;
this.init();
}

init() {
// Создаем элементы модального окна
this.createModal();

// Инициализируем после загрузки DOM
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.setup());
} else {
this.setup();
}
}

createModal() {
// Основной контейнер
this.modal = document.createElement('div');
this.modal.className = 'article-lightbox';
this.modal.setAttribute('aria-hidden', 'true');
this.modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.92);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
backdrop-filter: blur(5px);
`;

// Контейнер для изображения
const container = document.createElement('div');
container.className = 'article-lightbox__container';
container.style.cssText = `
position: relative;
max-width: 90vw;
max-height: 90vh;
margin: 20px;
`;

// Кнопка закрытия
const closeBtn = document.createElement('button');
closeBtn.className = 'article-lightbox__close';
closeBtn.innerHTML = '×';
closeBtn.setAttribute('aria-label', 'Закрыть');
closeBtn.style.cssText = `
position: absolute;
top: -50px;
right: 0;
background: none;
border: none;
color: white;
font-size: 40px;
cursor: pointer;
line-height: 1;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
transition: transform 0.2s ease;
`;
closeBtn.addEventListener('mouseenter', () => {
closeBtn.style.transform = 'scale(1.2)';
});
closeBtn.addEventListener('mouseleave', () => {
closeBtn.style.transform = 'scale(1)';
});
closeBtn.addEventListener('click', (e) => {
e.stopPropagation();
this.close();
});

// Изображение
this.modalImage = document.createElement('img');
this.modalImage.className = 'article-lightbox__image';
this.modalImage.style.cssText = `
display: block;
max-width: 100%;
max-height: 80vh;
margin: 0 auto;
border-radius: 8px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
cursor: default;
transform: scale(0.95);
opacity: 0;
transition: transform 0.3s ease, opacity 0.3s ease;
`;

// Подпись
this.caption = document.createElement('div');
this.caption.className = 'article-lightbox__caption';
this.caption.style.cssText = `
text-align: center;
color: white;
font-size: 16px;
margin-top: 20px;
padding: 10px 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
backdrop-filter: blur(10px);
opacity: 0;
transform: translateY(10px);
transition: opacity 0.3s ease, transform 0.3s ease;
`;

// Собираем структуру
container.appendChild(closeBtn);
container.appendChild(this.modalImage);
container.appendChild(this.caption);
this.modal.appendChild(container);
document.body.appendChild(this.modal);

// Закрытие по клику на фон
this.modal.addEventListener('click', (e) => {
if (e.target === this.modal) {
this.close();
}
});

// Закрытие по ESC
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.isOpen()) {
this.close();
}
});

// Навигация стрелками
document.addEventListener('keydown', (e) => {
if (!this.isOpen()) return;

if (e.key === 'ArrowLeft') {
e.preventDefault();
this.prev();
} else if (e.key === 'ArrowRight') {
e.preventDefault();
this.next();
}
});
}

setup() {
// Находим только изображения в основном контенте статьи
const mainContent = document.querySelector('main .content, article .content, .content');
if (!mainContent) return;

this.images = Array.from(mainContent.querySelectorAll('.article-image__img'))
.filter(img => {
// Фильтруем только достаточно большие изображения
return img.naturalWidth > 100 && img.naturalHeight > 100;
});

console.log(`Найдено ${this.images.length} изображений для lightbox`);

this.images.forEach((img, index) => {
// Добавляем курсор и эффекты
img.style.cursor = 'zoom-in';
img.style.transition = 'transform 0.2s ease, opacity 0.2s ease';

// Эффект при наведении
img.addEventListener('mouseenter', () => {
img.style.opacity = '0.95';
img.style.transform = 'translateY(-2px)';
});

img.addEventListener('mouseleave', () => {
img.style.opacity = '1';
img.style.transform = 'translateY(0)';
});

// Обработчик клика
img.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
this.currentIndex = index;
this.open(img.src, img.alt || img.title || '');
});
});
}

open(src, caption) {
this.modalImage.src = src;
this.modalImage.alt = caption;
this.caption.textContent = caption;

// Показываем модальное окно
this.modal.setAttribute('aria-hidden', 'false');
this.modal.style.visibility = 'visible';

// Запускаем анимации с небольшой задержкой
requestAnimationFrame(() => {
this.modal.style.opacity = '1';

setTimeout(() => {
this.modalImage.style.opacity = '1';
this.modalImage.style.transform = 'scale(1)';
this.caption.style.opacity = '1';
this.caption.style.transform = 'translateY(0)';
}, 50);
});

// Блокируем прокрутку страницы
document.body.style.overflow = 'hidden';
document.documentElement.style.overflow = 'hidden';
}

close() {
// Скрываем с анимацией
this.modalImage.style.opacity = '0';
this.modalImage.style.transform = 'scale(0.95)';
this.caption.style.opacity = '0';
this.caption.style.transform = 'translateY(10px)';

setTimeout(() => {
this.modal.style.opacity = '0';
this.modal.style.visibility = 'hidden';
this.modal.setAttribute('aria-hidden', 'true');

// Разблокируем прокрутку
document.body.style.overflow = '';
document.documentElement.style.overflow = '';
}, 300);
}

isOpen() {
return this.modal.getAttribute('aria-hidden') === 'false';
}

next() {
if (this.images.length > 1) {
this.currentIndex = (this.currentIndex + 1) % this.images.length;
const nextImg = this.images[this.currentIndex];
this.open(nextImg.src, nextImg.alt || nextImg.title || '');
}
}

prev() {
if (this.images.length > 1) {
this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
const prevImg = this.images[this.currentIndex];
this.open(prevImg.src, prevImg.alt || prevImg.title || '');
}
}
}

// Инициализация
document.addEventListener('DOMContentLoaded', () => {
// Ждем загрузки изображений
setTimeout(() => {
window.articleLightbox = new ArticleLightbox();
}, 100);

// Добавляем CSS стили
const style = document.createElement('style');
style.textContent = `
/* Стили для изображений в статьях */
.article-image__img {
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
max-width: 100%;
height: auto;
display: block;
margin: 1.5rem auto;
}

.article-image__img:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}

.article-image__caption {
text-align: center;
font-size: 0.9rem;
color: #666;
margin-top: 0.5rem;
font-style: italic;
}

/* Анимации для lightbox */
@keyframes lightboxFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes lightboxZoomIn {
from {
transform: scale(0.9);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}

@keyframes lightboxSlideUp {
from {
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
`;
document.head.appendChild(style);
});
</script>
```

Если нужны навигационные стрелки (опционально):

Добавьте этот код внутрь createModal() после создания closeBtn:

```javascript
// Кнопки навигации (только если больше одного изображения)
const navPrev = document.createElement('button');
navPrev.className = 'article-lightbox__nav article-lightbox__nav--prev';
navPrev.innerHTML = '‹';
navPrev.setAttribute('aria-label', 'Предыдущее изображение');
navPrev.style.cssText = `
position: absolute;
left: -60px;
top: 50%;
transform: translateY(-50%);
background: rgba(255, 255, 255, 0.1);
border: none;
color: white;
font-size: 30px;
width: 50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s ease, transform 0.2s ease;
backdrop-filter: blur(10px);
`;

const navNext = document.createElement('button');
navNext.className = 'article-lightbox__nav article-lightbox__nav--next';
navNext.innerHTML = '›';
navNext.setAttribute('aria-label', 'Следующее изображение');
navNext.style.cssText = `
position: absolute;
right: -60px;
top: 50%;
transform: translateY(-50%);
background: rgba(255, 255, 255, 0.1);
border: none;
color: white;
font-size: 30px;
width: 50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s ease, transform 0.2s ease;
backdrop-filter: blur(10px);
`;

navPrev.addEventListener('mouseenter', () => {
navPrev.style.background = 'rgba(255, 255, 255, 0.2)';
navPrev.style.transform = 'translateY(-50%) scale(1.1)';
});
navPrev.addEventListener('mouseleave', () => {
navPrev.style.background = 'rgba(255, 255, 255, 0.1)';
navPrev.style.transform = 'translateY(-50%) scale(1)';
});
navPrev.addEventListener('click', (e) => {
e.stopPropagation();
this.prev();
});

navNext.addEventListener('mouseenter', () => {
navNext.style.background = 'rgba(255, 255, 255, 0.2)';
navNext.style.transform = 'translateY(-50%) scale(1.1)';
});
navNext.addEventListener('mouseleave', () => {
navNext.style.background = 'rgba(255, 255, 255, 0.1)';
navNext.style.transform = 'translateY(-50%) scale(1)';
});
navNext.addEventListener('click', (e) => {
e.stopPropagation();
this.next();
});

// Добавляем кнопки навигации
container.appendChild(navPrev);
container.appendChild(navNext);
```

Особенности этого решения:

1. ✅ Только для изображений в статьях (фильтрует по .article-image__img)
2. ✅ Модальное окно с полупрозрачным фоном (backdrop-filter blur)
3. ✅ Без внешних библиотек - чистый JavaScript/CSS
4. ✅ Плавные анимации открытия/закрытия
5. ✅ Закрытие по клику на фон, ESC, кнопке закрытия
6. ✅ Навигация стрелками (опционально)
7. ✅ Адаптивный дизайн - работает на мобильных

Чтобы проверить:

1. Обновите render-image.html
2. Добавьте код в scripts.html
3. Перезапустите Hugo: hugo server --disableFastRender
4. В Markdown используйте обычный синтаксис: ![Описание](/images/photo.jpg)

Теперь при клике на изображение в статье будет открываться красивое модальное окно с полупрозрачным фоном, а не просто открываться в браузере!
Понял! Нужно:

1. Увеличить прозрачность фона
2. Исключить изображения на главной странице и в других местах
3. Не добавлять тень к маленьким изображениям, которые полностью помещаются
4. Исключить логотипы, иконки и другие декоративные изображения

Вот исправленная версия:

1. Обновите 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 -}}

{{/* Определяем, нужно ли добавлять lightbox */}}
{{- $addLightbox := true -}}
{{- $addShadow := true -}}

{{/* Исключаем определенные страницы */}}
{{- if eq .Page.Kind "home" -}}
{{- $addLightbox = false -}}
{{- $addShadow = false -}}
{{- else if eq .Page.Kind "section" -}}
{{- $addLightbox = false -}}
{{- $addShadow = false -}}
{{- end -}}

{{/* Определяем классы */}}
{{- $imageClass := "content-image" -}}
{{- if $addLightbox -}}
{{- $imageClass = printf "%s %s" $imageClass "content-image--zoomable" -}}
{{- end -}}
{{- if $addShadow -}}
{{- $imageClass = printf "%s %s" $imageClass "content-image--shadow" -}}
{{- end -}}

{{- with .Title -}}
<figure class="content-image__wrapper">
<img src="{{ $dest | safeURL }}"
title="{{ . }}"
alt="{{ $alt }}"
{{ if $lazyLoading }}loading="lazy"{{ end }}
class="{{ $imageClass }}"
data-lightbox="true"
data-original-src="{{ $dest | safeURL }}" />
<figcaption class="content-image__caption">{{ . }}</figcaption>
</figure>
{{- else -}}
<img src="{{ $dest | safeURL }}"
alt="{{ $alt }}"
{{ if $lazyLoading }}loading="lazy"{{ end }}
class="{{ $imageClass }}"
data-lightbox="{{ $addLightbox }}"
data-original-src="{{ $dest | safeURL }}" />
{{- end -}}
```

2. Обновите JavaScript в scripts.html:

```html
<script>
class ContentLightbox {
constructor() {
this.modal = null;
this.modalImage = null;
this.caption = null;
this.images = [];
this.currentIndex = 0;
this.init();
}

init() {
this.createModal();

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.setup());
} else {
this.setup();
}
}

createModal() {
// Основной контейнер с БОЛЬШЕЙ прозрачностью
this.modal = document.createElement('div');
this.modal.className = 'content-lightbox';
this.modal.setAttribute('aria-hidden', 'true');
this.modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.85); /* Увеличил прозрачность */
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
backdrop-filter: blur(8px);
`;

// Контейнер для изображения
const container = document.createElement('div');
container.className = 'content-lightbox__container';
container.style.cssText = `
position: relative;
max-width: 95vw;
max-height: 95vh;
margin: 20px;
`;

// Кнопка закрытия
const closeBtn = document.createElement('button');
closeBtn.className = 'content-lightbox__close';
closeBtn.innerHTML = '×';
closeBtn.setAttribute('aria-label', 'Закрыть');
closeBtn.style.cssText = `
position: absolute;
top: -45px;
right: 0;
background: rgba(255, 255, 255, 0.15);
border: none;
color: white;
font-size: 32px;
cursor: pointer;
line-height: 1;
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
transition: all 0.2s ease;
backdrop-filter: blur(5px);
`;
closeBtn.addEventListener('mouseenter', () => {
closeBtn.style.background = 'rgba(255, 255, 255, 0.25)';
closeBtn.style.transform = 'scale(1.1)';
});
closeBtn.addEventListener('mouseleave', () => {
closeBtn.style.background = 'rgba(255, 255, 255, 0.15)';
closeBtn.style.transform = 'scale(1)';
});
closeBtn.addEventListener('click', (e) => {
e.stopPropagation();
this.close();
});

// Изображение
this.modalImage = document.createElement('img');
this.modalImage.className = 'content-lightbox__image';
this.modalImage.style.cssText = `
display: block;
max-width: 100%;
max-height: 85vh;
margin: 0 auto;
border-radius: 6px;
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
cursor: default;
transform: scale(0.96);
opacity: 0;
transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275),
opacity 0.4s ease;
`;

// Подпись
this.caption = document.createElement('div');
this.caption.className = 'content-lightbox__caption';
this.caption.style.cssText = `
text-align: center;
color: rgba(255, 255, 255, 0.9);
font-size: 15px;
margin-top: 15px;
padding: 8px 16px;
background: rgba(255, 255, 255, 0.08);
border-radius: 4px;
backdrop-filter: blur(10px);
opacity: 0;
transform: translateY(10px);
transition: opacity 0.4s ease, transform 0.4s ease;
`;

// Собираем структуру
container.appendChild(closeBtn);
container.appendChild(this.modalImage);
container.appendChild(this.caption);
this.modal.appendChild(container);
document.body.appendChild(this.modal);

// Закрытие по клику на фон
this.modal.addEventListener('click', (e) => {
if (e.target === this.modal) {
this.close();
}
});

// Закрытие по ESC
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.isOpen()) {
this.close();
}
});

// Навигация стрелками
document.addEventListener('keydown', (e) => {
if (!this.isOpen()) return;

if (e.key === 'ArrowLeft') {
e.preventDefault();
this.prev();
} else if (e.key === 'ArrowRight') {
e.preventDefault();
this.next();
}
});
}

setup() {
// Находим только изображения с data-lightbox="true" (только из контента статей)
this.images = Array.from(document.querySelectorAll('img[data-lightbox="true"]'))
.filter(img => {
// Фильтруем по размеру: только достаточно большие изображения
const minSize = 200; // минимальный размер в пикселях
const isLargeEnough = img.naturalWidth > minSize && img.naturalHeight > minSize;

// Проверяем, что изображение видимо и не в шапке/футере
const isVisible = img.offsetWidth > 0 && img.offsetHeight > 0;
const isInContent = this.isInContentArea(img);

// Проверяем, не является ли изображение иконкой/логотипом
const isNotIcon = !this.isIcon(img);

return isLargeEnough && isVisible && isInContent && isNotIcon;
});

console.log(`Найдено ${this.images.length} изображений для lightbox`);

this.images.forEach((img, index) => {
// Добавляем курсор и эффекты только zoomable изображениям
if (img.classList.contains('content-image--zoomable')) {
img.style.cursor = 'zoom-in';
img.style.transition = 'transform 0.25s ease, opacity 0.25s ease';

// Эффект при наведении
img.addEventListener('mouseenter', () => {
img.style.opacity = '0.92';
if (!this.isSmallImage(img)) {
img.style.transform = 'translateY(-3px)';
}
});

img.addEventListener('mouseleave', () => {
img.style.opacity = '1';
img.style.transform = 'translateY(0)';
});

// Обработчик клика
img.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
this.currentIndex = index;
this.open(img.dataset.originalSrc || img.src, img.alt || img.title || '');
});
}
});
}

isInContentArea(element) {
// Проверяем, что элемент находится в основном контенте статьи
const contentSelectors = [
'main .content',
'article .content',
'.content',
'main article',
'article',
'[role="main"]'
];

for (const selector of contentSelectors) {
const contentArea = document.querySelector(selector);
if (contentArea && contentArea.contains(element)) {
return true;
}
}

return false;
}

isIcon(img) {
// Проверяем, является ли изображение иконкой/логотипом
const iconKeywords = ['icon', 'logo', 'avatar', 'favicon', 'sprite'];
const src = img.src.toLowerCase();
const alt = (img.alt || '').toLowerCase();
const classes = (img.className || '').toLowerCase();

// Проверяем по размеру (иконки обычно маленькие)
const isSmall = img.naturalWidth < 100 && img.naturalHeight < 100;

// Проверяем по имени файла, alt тексту и классам
const hasIconKeyword = iconKeywords.some(keyword =>
src.includes(keyword) || alt.includes(keyword) || classes.includes(keyword)
);

return isSmall || hasIconKeyword;
}

isSmallImage(img) {
// Проверяем, маленькое ли изображение (полностью помещается без тени)
const viewportWidth = window.innerWidth;
const imageWidth = img.naturalWidth || img.width;

// Если ширина изображения меньше 80% ширины контейнера
const container = img.closest('.content, main, article') || document.body;
const containerWidth = container.clientWidth;

return imageWidth < containerWidth * 0.8;
}

open(src, caption) {
this.modalImage.src = src;
this.modalImage.alt = caption;
this.caption.textContent = caption;

// Показываем модальное окно
this.modal.setAttribute('aria-hidden', 'false');
this.modal.style.visibility = 'visible';

// Анимация появления
requestAnimationFrame(() => {
this.modal.style.opacity = '1';

setTimeout(() => {
this.modalImage.style.opacity = '1';
this.modalImage.style.transform = 'scale(1)';
this.caption.style.opacity = '1';
this.caption.style.transform = 'translateY(0)';
}, 50);
});

// Блокируем прокрутку
document.body.style.overflow = 'hidden';
document.documentElement.style.overflow = 'hidden';
}

close() {
// Анимация закрытия
this.modalImage.style.opacity = '0';
this.modalImage.style.transform = 'scale(0.96)';
this.caption.style.opacity = '0';
this.caption.style.transform = 'translateY(10px)';

setTimeout(() => {
this.modal.style.opacity = '0';
this.modal.style.visibility = 'hidden';
this.modal.setAttribute('aria-hidden', 'true');

// Разблокируем прокрутку
document.body.style.overflow = '';
document.documentElement.style.overflow = '';
}, 400);
}

isOpen() {
return this.modal.getAttribute('aria-hidden') === 'false';
}

next() {
if (this.images.length > 1) {
this.currentIndex = (this.currentIndex + 1) % this.images.length;
const nextImg = this.images[this.currentIndex];
this.open(nextImg.dataset.originalSrc || nextImg.src,
nextImg.alt || nextImg.title || '');
}
}

prev() {
if (this.images.length > 1) {
this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
const prevImg = this.images[this.currentIndex];
this.open(prevImg.dataset.originalSrc || prevImg.src,
prevImg.alt || prevImg.title || '');
}
}
}

// Инициализация с задержкой для загрузки изображений
document.addEventListener('DOMContentLoaded', () => {
// Ждем немного для полной загрузки изображений
setTimeout(() => {
window.contentLightbox = new ContentLightbox();
}, 300);

// Добавляем CSS стили
const style = document.createElement('style');
style.textContent = `
/* Базовые стили для всех изображений в контенте */
.content-image {
max-width: 100%;
height: auto;
display: block;
margin: 1.5rem auto;
border-radius: 4px;
}

/* Стили только для zoomable изображений (в статьях) */
.content-image--zoomable {
cursor: zoom-in;
transition: all 0.25s ease;
}

/* Тень только для больших изображений */
.content-image--shadow {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

/* Маленькие изображения (без тени) */
.content-image:not(.content-image--shadow) {
box-shadow: none;
}

/* Эффект при наведении на большие изображения */
.content-image--zoomable.content-image--shadow:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
opacity: 0.95;
}

/* Подпись к изображениям */
.content-image__caption {
text-align: center;
font-size: 0.875rem;
color: #666;
margin-top: 0.5rem;
line-height: 1.4;
font-style: italic;
}

/* Обертка для изображений с подписью */
.content-image__wrapper {
margin: 1.5rem 0;
text-align: center;
}

/* Анимации для lightbox */
@keyframes lightboxFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes lightboxImagePop {
0% {
transform: scale(0.95);
opacity: 0;
}
70% {
transform: scale(1.02);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 1;
}
}

/* Стили для мобильных устройств */
@media (max-width: 768px) {
.content-image {
margin: 1rem auto;
border-radius: 3px;
}

.content-image--shadow {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.content-image--zoomable.content-image--shadow:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}

.content-lightbox__close {
top: 15px;
right: 15px;
width: 32px;
height: 32px;
font-size: 28px;
}
}

/* Отключение hover на тач-устройствах */
@media (hover: none) {
.content-image--zoomable:hover {
transform: none !important;
}
}
`;
document.head.appendChild(style);
});

// Определяем размеры изображений после загрузки
window.addEventListener('load', () => {
const images = document.querySelectorAll('.content-image');
images.forEach(img => {
if (img.complete) {
window.contentLightbox?.setup();
} else {
img.addEventListener('load', () => {
setTimeout(() => window.contentLightbox?.setup(), 100);
});
}
});
});
</script>
```

Что делает этот код:

1. Фильтрация изображений:

· ❌ Исключает главную страницу (home)
· ❌ Исключает разделы (section pages)
· ❌ Исключает маленькие изображения (< 200px)
· ❌ Исключает иконки/логотипы (по названию и размеру)
· ✅ Только для контента статей (проверяет область)

2. Управление тенями:

· ✅ Тень только для больших изображений (.content-image--shadow)
· ❌ Маленькие изображения без тени
· ✅ Определение "маленьких" - ширина < 80% контейнера

3. Прозрачность:

· ✅ Фон lightbox: rgba(0, 0, 0, 0.85) (более прозрачный)
· ✅ Плавные анимации с cubic-bezier

4. Умная логика:

· Автоматически определяет какие изображения нужно увеличивать
· Не трогает декоративные элементы
· Работает только в контенте статей
· Адаптивный дизайн для мобильных

Чтобы настроить:

1. Изменить прозрачность - настройте rgba(0, 0, 0, 0.85) в createModal()
2. Изменить минимальный размер - поменяйте minSize = 200 в setup()
3. Добавить исключения - в isIcon() добавьте ключевые слова

Теперь у вас будет красивый lightbox только для изображений в статьях, с увеличенной прозрачностью и умной фильтрацией!
Отлично! Давайте исправим эти две проблемы:

1. Вернем закругленные углы изображениям
2. Уберем курсор-лупу с изображений, которые не увеличиваются

Обновите 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 -}}

{{/* Определяем, нужно ли добавлять lightbox */}}
{{- $addLightbox := true -}}
{{- $addShadow := true -}}

{{/* Исключаем определенные страницы */}}
{{- if eq .Page.Kind "home" -}}
{{- $addLightbox = false -}}
{{- $addShadow = false -}}
{{- else if eq .Page.Kind "section" -}}
{{- $addLightbox = false -}}
{{- $addShadow = false -}}
{{- end -}}

{{/* Определяем классы */}}
{{- $imageClass := "content-image" -}}
{{- if $addLightbox -}}
{{- $imageClass = printf "%s %s" $imageClass "content-image--zoomable" -}}
{{- end -}}
{{- if $addShadow -}}
{{- $imageClass = printf "%s %s" $imageClass "content-image--shadow" -}}
{{- end -}}

{{- with .Title -}}
<figure class="content-image__wrapper">
<img src="{{ $dest | safeURL }}"
title="{{ . }}"
alt="{{ $alt }}"
{{ if $lazyLoading }}loading="lazy"{{ end }}
class="{{ $imageClass }}"
data-lightbox="{{ $addLightbox }}"
data-original-src="{{ $dest | safeURL }}" />
<figcaption class="content-image__caption">{{ . }}</figcaption>
</figure>
{{- else -}}
<img src="{{ $dest | safeURL }}"
alt="{{ $alt }}"
{{ if $lazyLoading }}loading="lazy"{{ end }}
class="{{ $imageClass }}"
data-lightbox="{{ $addLightbox }}"
data-original-src="{{ $dest | safeURL }}" />
{{- end -}}
```

Обновите JavaScript в scripts.html:

```javascript
setup() {
// Находим только изображения с data-lightbox="true" (только из контента статей)
this.images = Array.from(document.querySelectorAll('img[data-lightbox="true"]'))
.filter(img => {
// Фильтруем по размеру: только достаточно большие изображения
const minSize = 200; // минимальный размер в пикселях
const isLargeEnough = img.naturalWidth > minSize && img.naturalHeight > minSize;

// Проверяем, что изображение видимо и не в шапке/футере
const isVisible = img.offsetWidth > 0 && img.offsetHeight > 0;
const isInContent = this.isInContentArea(img);

// Проверяем, не является ли изображение иконкой/логотипом
const isNotIcon = !this.isIcon(img);

// Проверяем, полностью ли помещается изображение
const isNotFullyVisible = !this.isFullyVisible(img);

return isLargeEnough && isVisible && isInContent && isNotIcon && isNotFullyVisible;
});

console.log(`Найдено ${this.images.length} изображений для lightbox`);

// Проходим по ВСЕМ изображениям с data-lightbox="true"
document.querySelectorAll('img[data-lightbox="true"]').forEach((img, index) => {
// Проверяем, попадает ли это изображение в наш фильтрованный список
const isZoomable = this.images.includes(img);

if (isZoomable) {
// Добавляем курсор-лупу и эффекты только zoomable изображениям
img.style.cursor = 'zoom-in';
img.style.transition = 'transform 0.25s ease, opacity 0.25s ease';

// Добавляем класс для стилей
img.classList.add('is-zoomable');

// Эффект при наведении
img.addEventListener('mouseenter', () => {
img.style.opacity = '0.92';
img.style.transform = 'translateY(-3px)';
});

img.addEventListener('mouseleave', () => {
img.style.opacity = '1';
img.style.transform = 'translateY(0)';
});

// Обработчик клика
img.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
// Находим индекс в отфильтрованном массиве
const filteredIndex = this.images.indexOf(img);
if (filteredIndex !== -1) {
this.currentIndex = filteredIndex;
this.open(img.dataset.originalSrc || img.src, img.alt || img.title || '');
}
});
} else {
// Для не-zoomable изображений убираем курсор-лупу
img.style.cursor = 'default';
img.classList.remove('content-image--zoomable');
img.classList.add('not-zoomable');
}
});
}

// НОВЫЙ МЕТОД: Проверяет, полностью ли видно изображение
isFullyVisible(img) {
if (!img.parentElement) return false;

// Получаем размеры изображения и его контейнера
const imgWidth = img.naturalWidth || img.offsetWidth;
const container = img.closest('.content, main, article, .content-image__wrapper, p') || img.parentElement;
const containerWidth = container.clientWidth;

// Если контейнер - это параграф, учитываем padding
let containerEffectiveWidth = containerWidth;
if (container.tagName === 'P') {
const styles = window.getComputedStyle(container);
const paddingLeft = parseFloat(styles.paddingLeft) || 0;
const paddingRight = parseFloat(styles.paddingRight) || 0;
containerEffectiveWidth = containerWidth - paddingLeft - paddingRight;
}

// Изображение считается "полностью видимым", если его ширина меньше 95% ширины контейнера
// и при этом оно не больше 800px (чтобы большие изображения все равно можно было увеличить)
const isWidthFullyVisible = imgWidth < containerEffectiveWidth * 0.95;
const isNotTooLarge = imgWidth <= 800; // Максимальная ширина для "полной видимости"

// Также проверяем высоту
const imgHeight = img.naturalHeight || img.offsetHeight;
const viewportHeight = window.innerHeight;
const isHeightFullyVisible = imgHeight < viewportHeight * 0.8;

return isWidthFullyVisible && isNotTooLarge && isHeightFullyVisible;
}

// Обновите метод isSmallImage
isSmallImage(img) {
return this.isFullyVisible(img);
}
```

Обновите CSS стили в том же scripts.html:

```css
/* Базовые стили для всех изображений в контенте */
.content-image {
max-width: 100%;
height: auto;
display: block;
margin: 1.5rem auto;
border-radius: 8px; /* Закругленные углы */
}

/* Стили для zoomable изображений (которые можно увеличивать) */
.content-image.is-zoomable {
cursor: zoom-in;
transition: all 0.25s ease;
}

/* Для не-zoomable изображений (которые полностью видны) */
.content-image.not-zoomable {
cursor: default;
border-radius: 8px; /* Закругленные углы сохраняются */
}

/* Стили только для zoomable изображений (в статьях) */
.content-image--zoomable.is-zoomable {
cursor: zoom-in;
}

/* Тень только для больших изображений */
.content-image--shadow.is-zoomable {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

/* Маленькие/полностью видимые изображения (без тени) */
.content-image:not(.is-zoomable),
.content-image.not-zoomable {
box-shadow: none !important;
}

/* Эффект при наведении на zoomable изображения */
.content-image.is-zoomable:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
opacity: 0.95;
}

/* Отключаем hover эффекты для не-zoomable изображений */
.content-image.not-zoomable:hover {
transform: none !important;
opacity: 1 !important;
box-shadow: none !important;
}

/* Подпись к изображениям */
.content-image__caption {
text-align: center;
font-size: 0.875rem;
color: #666;
margin-top: 0.5rem;
line-height: 1.4;
font-style: italic;
}

/* Обертка для изображений с подписью */
.content-image__wrapper {
margin: 1.5rem 0;
text-align: center;
}

/* Анимации для lightbox */
@keyframes lightboxFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes lightboxImagePop {
0% {
transform: scale(0.95);
opacity: 0;
}
70% {
transform: scale(1.02);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 1;
}
}

/* Стили для мобильных устройств */
@media (max-width: 768px) {
.content-image {
margin: 1rem auto;
border-radius: 6px; /* Немного меньше на мобильных */
}

.content-image--shadow.is-zoomable {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.content-image.is-zoomable:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}

.content-lightbox__close {
top: 15px;
right: 15px;
width: 32px;
height: 32px;
font-size: 28px;
}

/* На мобильных делаем все изображения не-zoomable если они маленькие */
.content-image {
max-width: 100% !important;
}
}

/* Отключение hover на тач-устройствах */
@media (hover: none) {
.content-image:hover {
transform: none !important;
}
}

/* Плавная загрузка изображений */
.content-image {
opacity: 0;
animation: imageFadeIn 0.5s ease forwards;
}

@keyframes imageFadeIn {
to {
opacity: 1;
}
}
```

Дополнительно: Если нужно сохранить старые закругления

Если у вас уже были свои стили для закругления, добавьте в конец CSS:

```css
/* Сохраняем существующие стили закругления */
img.content-image {
border-radius: var(--image-border-radius, 8px) !important;
}

/* Переопределяем для разных случаев */
.content-image.is-zoomable {
border-radius: calc(var(--image-border-radius, 8px) + 2px) !important;
}
```

Что изменилось:

✅ Вернули закругленные углы:

· Все изображения теперь имеют border-radius: 8px
· На мобильных немного меньше: 6px

✅ Убрали лупу с полностью видимых изображений:

· Логика определения: изображение считается "полностью видимым", если:
1. Его ширина < 95% ширины контейнера
2. Высота < 80% высоты окна
3. Ширина ≤ 800px (чтобы большие можно было увеличить)
· Курсор: у таких изображений cursor: default вместо zoom-in
· Эффекты hover: отключены для не-zoomable изображений

✅ Разделили логику:

· isZoomable - изображения, которые можно увеличивать
· not-zoomable - полностью видимые изображения
· Разные CSS классы для разных типов

Как проверить:

1. Обновите оба файла
2. Перезапустите Hugo: hugo server --disableFastRender
3. Проверьте:
· У всех изображений закругленные углы
· У маленьких/полностью видимых изображений нет курсора-лупы
· Большие изображения все еще можно увеличивать

Теперь изображения будут выглядеть как раньше с закругленными углами, а лупа будет показываться только там, где действительно нужно увеличение!
Понял! Вот полный готовый код для вставки.

1. layouts/_default/_markup/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 -}}

{{- $addLightbox := true -}}
{{- $addShadow := true -}}

{{- if eq .Page.Kind "home" -}}
{{- $addLightbox = false -}}
{{- $addShadow = false -}}
{{- else if eq .Page.Kind "section" -}}
{{- $addLightbox = false -}}
{{- $addShadow = false -}}
{{- end -}}

{{- $imageClass := "content-image" -}}
{{- if $addLightbox -}}
{{- $imageClass = printf "%s %s" $imageClass "content-image--zoomable" -}}
{{- end -}}
{{- if $addShadow -}}
{{- $imageClass = printf "%s %s" $imageClass "content-image--shadow" -}}
{{- end -}}

{{- with .Title -}}
<figure class="content-image__wrapper">
<img src="{{ $dest | safeURL }}"
title="{{ . }}"
alt="{{ $alt }}"
{{ if $lazyLoading }}loading="lazy"{{ end }}
class="{{ $imageClass }}"
data-lightbox="{{ $addLightbox }}"
data-original-src="{{ $dest | safeURL }}" />
<figcaption class="content-image__caption">{{ . }}</figcaption>
</figure>
{{- else -}}
<img src="{{ $dest | safeURL }}"
alt="{{ $alt }}"
{{ if $lazyLoading }}loading="lazy"{{ end }}
class="{{ $imageClass }}"
data-lightbox="{{ $addLightbox }}"
data-original-src="{{ $dest | safeURL }}" />
{{- end -}}
```

2. В layouts/partials/scripts.html добавить ПЕРЕД закрывающим </body> (или в конец файла):

```html
<!-- Lightbox для изображений в статьях -->
<script>
class ContentLightbox {
constructor() {
this.modal = null;
this.modalImage = null;
this.caption = null;
this.images = [];
this.currentIndex = 0;
this.init();
}

init() {
this.createModal();
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.setup());
} else {
this.setup();
}
}

createModal() {
this.modal = document.createElement('div');
this.modal.className = 'content-lightbox';
this.modal.setAttribute('aria-hidden', 'true');
this.modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.85);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
backdrop-filter: blur(8px);
`;

const container = document.createElement('div');
container.className = 'content-lightbox__container';
container.style.cssText = `
position: relative;
max-width: 95vw;
max-height: 95vh;
margin: 20px;
`;

const closeBtn = document.createElement('button');
closeBtn.className = 'content-lightbox__close';
closeBtn.innerHTML = '×';
closeBtn.setAttribute('aria-label', 'Закрыть');
closeBtn.style.cssText = `
position: absolute;
top: -45px;
right: 0;
background: rgba(255, 255, 255, 0.15);
border: none;
color: white;
font-size: 32px;
cursor: pointer;
line-height: 1;
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
transition: all 0.2s ease;
backdrop-filter: blur(5px);
`;
closeBtn.addEventListener('mouseenter', () => {
closeBtn.style.background = 'rgba(255, 255, 255, 0.25)';
closeBtn.style.transform = 'scale(1.1)';
});
closeBtn.addEventListener('mouseleave', () => {
closeBtn.style.background = 'rgba(255, 255, 255, 0.15)';
closeBtn.style.transform = 'scale(1)';
});
closeBtn.addEventListener('click', (e) => {
e.stopPropagation();
this.close();
});

this.modalImage = document.createElement('img');
this.modalImage.className = 'content-lightbox__image';
this.modalImage.style.cssText = `
display: block;
max-width: 100%;
max-height: 85vh;
margin: 0 auto;
border-radius: 6px;
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
cursor: default;
transform: scale(0.96);
opacity: 0;
transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275),
opacity 0.4s ease;
`;

this.caption = document.createElement('div');
this.caption.className = 'content-lightbox__caption';
this.caption.style.cssText = `
text-align: center;
color: rgba(255, 255, 255, 0.9);
font-size: 15px;
margin-top: 15px;
padding: 8px 16px;
background: rgba(255, 255, 255, 0.08);
border-radius: 4px;
backdrop-filter: blur(10px);
opacity: 0;
transform: translateY(10px);
transition: opacity 0.4s ease, transform 0.4s ease;
`;

container.appendChild(closeBtn);
container.appendChild(this.modalImage);
container.appendChild(this.caption);
this.modal.appendChild(container);
document.body.appendChild(this.modal);

this.modal.addEventListener('click', (e) => {
if (e.target === this.modal) {
this.close();
}
});

document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.isOpen()) {
this.close();
}
});

document.addEventListener('keydown', (e) => {
if (!this.isOpen()) return;
if (e.key === 'ArrowLeft') {
e.preventDefault();
this.prev();
} else if (e.key === 'ArrowRight') {
e.preventDefault();
this.next();
}
});
}

setup() {
this.images = Array.from(document.querySelectorAll('img[data-lightbox="true"]'))
.filter(img => {
const minSize = 200;
const isLargeEnough = img.naturalWidth > minSize && img.naturalHeight > minSize;
const isVisible = img.offsetWidth > 0 && img.offsetHeight > 0;
const isInContent = this.isInContentArea(img);
const isNotIcon = !this.isIcon(img);
const isNotFullyVisible = !this.isFullyVisible(img);
return isLargeEnough && isVisible && isInContent && isNotIcon && isNotFullyVisible;
});

document.querySelectorAll('img[data-lightbox="true"]').forEach((img) => {
const isZoomable = this.images.includes(img);

if (isZoomable) {
img.style.cursor = 'zoom-in';
img.style.transition = 'transform 0.25s ease, opacity 0.25s ease';
img.classList.add('is-zoomable');

img.addEventListener('mouseenter', () => {
img.style.opacity = '0.92';
img.style.transform = 'translateY(-3px)';
});

img.addEventListener('mouseleave', () => {
img.style.opacity = '1';
img.style.transform = 'translateY(0)';
});

img.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const filteredIndex = this.images.indexOf(img);
if (filteredIndex !== -1) {
this.currentIndex = filteredIndex;
this.open(img.dataset.originalSrc || img.src, img.alt || img.title || '');
}
});
} else {
img.style.cursor = 'default';
img.classList.remove('content-image--zoomable');
img.classList.add('not-zoomable');
}
});
}

isInContentArea(element) {
const contentSelectors = [
'main .content',
'article .content',
'.content',
'main article',
'article',
'[role="main"]'
];

for (const selector of contentSelectors) {
const contentArea = document.querySelector(selector);
if (contentArea && contentArea.contains(element)) {
return true;
}
}
return false;
}

isIcon(img) {
const iconKeywords = ['icon', 'logo', 'avatar', 'favicon', 'sprite'];
const src = img.src.toLowerCase();
const alt = (img.alt || '').toLowerCase();
const classes = (img.className || '').toLowerCase();
const isSmall = img.naturalWidth < 100 && img.naturalHeight < 100;
const hasIconKeyword = iconKeywords.some(keyword =>
src.includes(keyword) || alt.includes(keyword) || classes.includes(keyword)
);
return isSmall || hasIconKeyword;
}

isFullyVisible(img) {
if (!img.parentElement) return false;
const imgWidth = img.naturalWidth || img.offsetWidth;
const container = img.closest('.content, main, article, .content-image__wrapper, p') || img.parentElement;
const containerWidth = container.clientWidth;
let containerEffectiveWidth = containerWidth;
if (container.tagName === 'P') {
const styles = window.getComputedStyle(container);
const paddingLeft = parseFloat(styles.paddingLeft) || 0;
const paddingRight = parseFloat(styles.paddingRight) || 0;
containerEffectiveWidth = containerWidth - paddingLeft - paddingRight;
}
const isWidthFullyVisible = imgWidth < containerEffectiveWidth * 0.95;
const isNotTooLarge = imgWidth <= 800;
const imgHeight = img.naturalHeight || img.offsetHeight;
const viewportHeight = window.innerHeight;
const isHeightFullyVisible = imgHeight < viewportHeight * 0.8;
return isWidthFullyVisible && isNotTooLarge && isHeightFullyVisible;
}

open(src, caption) {
this.modalImage.src = src;
this.modalImage.alt = caption;
this.caption.textContent = caption;
this.modal.setAttribute('aria-hidden', 'false');
this.modal.style.visibility = 'visible';

requestAnimationFrame(() => {
this.modal.style.opacity = '1';
setTimeout(() => {
this.modalImage.style.opacity = '1';
this.modalImage.style.transform = 'scale(1)';
this.caption.style.opacity = '1';
this.caption.style.transform = 'translateY(0)';
}, 50);
});

document.body.style.overflow = 'hidden';
document.documentElement.style.overflow = 'hidden';
}

close() {
this.modalImage.style.opacity = '0';
this.modalImage.style.transform = 'scale(0.96)';
this.caption.style.opacity = '0';
this.caption.style.transform = 'translateY(10px)';

setTimeout(() => {
this.modal.style.opacity = '0';
this.modal.style.visibility = 'hidden';
this.modal.setAttribute('aria-hidden', 'true');
document.body.style.overflow = '';
document.documentElement.style.overflow = '';
}, 400);
}

isOpen() {
return this.modal.getAttribute('aria-hidden') === 'false';
}

next() {
if (this.images.length > 1) {
this.currentIndex = (this.currentIndex + 1) % this.images.length;
const nextImg = this.images[this.currentIndex];
this.open(nextImg.dataset.originalSrc || nextImg.src,
nextImg.alt || nextImg.title || '');
}
}

prev() {
if (this.images.length > 1) {
this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
const prevImg = this.images[this.currentIndex];
this.open(prevImg.dataset.originalSrc || prevImg.src,
prevImg.alt || prevImg.title || '');
}
}
}

document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
window.contentLightbox = new ContentLightbox();
}, 300);

const style = document.createElement('style');
style.textContent = `
.content-image {
max-width: 100%;
height: auto;
display: block;
margin: 1.5rem auto;
border-radius: 8px;
}

.content-image.is-zoomable {
cursor: zoom-in;
transition: all 0.25s ease;
}

.content-image.not-zoomable {
cursor: default;
border-radius: 8px;
}

.content-image--zoomable.is-zoomable {
cursor: zoom-in;
}

.content-image--shadow.is-zoomable {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.content-image:not(.is-zoomable),
.content-image.not-zoomable {
box-shadow: none !important;
}

.content-image.is-zoomable:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
opacity: 0.95;
}

.content-image.not-zoomable:hover {
transform: none !important;
opacity: 1 !important;
box-shadow: none !important;
}

.content-image__caption {
text-align: center;
font-size: 0.875rem;
color: #666;
margin-top: 0.5rem;
line-height: 1.4;
font-style: italic;
}

.content-image__wrapper {
margin: 1.5rem 0;
text-align: center;
}

@keyframes lightboxFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes lightboxImagePop {
0% {
transform: scale(0.95);
opacity: 0;
}
70% {
transform: scale(1.02);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 1;
}
}

@media (max-width: 768px) {
.content-image {
margin: 1rem auto;
border-radius: 6px;
}

.content-image--shadow.is-zoomable {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.content-image.is-zoomable:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}

.content-lightbox__close {
top: 15px;
right: 15px;
width: 32px;
height: 32px;
font-size: 28px;
}
}

@media (hover: none) {
.content-image:hover {
transform: none !important;
}
}

.content-image {
opacity: 0;
animation: imageFadeIn 0.5s ease forwards;
}

@keyframes imageFadeIn {
to {
opacity: 1;
}
}
`;
document.head.appendChild(style);
});

window.addEventListener('load', () => {
const images = document.querySelectorAll('.content-image');
images.forEach(img => {
if (img.complete) {
window.contentLightbox?.setup();
} else {
img.addEventListener('load', () => {
setTimeout(() => window.contentLightbox?.setup(), 100);
});
}
});
});
</script>
```

Как установить:

1. Заменить полностью файл layouts/_default/_markup/render-image.html
2. Добавить JavaScript код в layouts/partials/scripts.html (перед </body> или в конец)
3. Перезапустить Hugo:

```bash
hugo server --disableFastRender
```

Что делает:

✅ Lightbox с полупрозрачным фоном (85% прозрачности)
✅ Только для изображений в статьях (не на главной)
✅ Закругленные углы у всех изображений (8px)
✅ Лупа только там, где нужно - убирает курсор с полностью видимых изображений
✅ Плавные анимации открытия/закрытия
✅ Навигация стрелками ← → и ESC для закрытия
✅ Адаптивный дизайн для мобильных

Теперь изображения будут с закругленными углами, а лупа появится только на тех, которые действительно нужно увеличивать!
  • Страница 31 из 31
  • «
  • 1
  • 2
  • 29
  • 30
  • 31
Поиск:
Новый ответ
Имя:
Текст сообщения: