import re import sys from pathlib import Path HTML_FILES = [ "data/wiki/vulture_shuttle_zh.html", "data/wiki/vulture_shuttle_en.html", "data/wiki/echo_shuttle_zh.html", "data/wiki/echo_shuttle_en.html", "data/wiki/enterprise_shuttle_zh.html", "data/wiki/enterprise_shuttle_en.html", ] STYLE_RE = re.compile(r"", re.IGNORECASE) SCRIPT_RE = re.compile(r"", re.IGNORECASE | re.DOTALL) LIGHTBOX_CSS = """ .wiki-article img[data-wiki-asset], .vulture-article img[data-wiki-asset] { cursor: zoom-in; } .wiki-image-lightbox { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; padding: 24px; background: rgba(32,33,34,0.84); z-index: 10000; box-sizing: border-box; } .wiki-image-lightbox[hidden] { display: none; } .wiki-image-lightbox-panel { position: relative; width: min(1120px, 96vw); max-height: 92vh; overflow: auto; margin: 0; padding: 10px; border: 1px solid #a2a9b1; background: #fff; box-shadow: 0 12px 38px rgba(0,0,0,0.35); box-sizing: border-box; } .wiki-image-lightbox-panel img { display: block; width: 100%; height: auto; max-height: 82vh; object-fit: contain; background: #000; } .wiki-image-lightbox-close { position: sticky; top: 0; float: right; width: 30px; height: 30px; margin: -2px -2px 8px 10px; border: 1px solid #a2a9b1; background: #eaecf0; color: #202122; font-weight: bold; line-height: 26px; cursor: pointer; } .wiki-image-lightbox-close:hover, .wiki-image-lightbox-close:focus { background: #fff; } body.wiki-image-lightbox-open { overflow: hidden; } @media (max-width: 820px) { .wiki-image-lightbox { padding: 12px; } .wiki-image-lightbox-panel { width: 98vw; max-height: 94vh; } } """.strip() LIGHTBOX_JS = """ (function () { function ready(fn) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', fn, { once: true }); } else { fn(); } } function captionFor(image) { var container = image.closest('figure, td, tr, .infobox-image'); if (!container) { return image.alt || ''; } var caption = container.querySelector('.thumbcaption'); return caption ? caption.textContent.trim() : (image.alt || ''); } function install(article) { if (!article || article.dataset.lightboxReady === '1') { return; } article.dataset.lightboxReady = '1'; var overlay = document.createElement('div'); overlay.className = 'wiki-image-lightbox'; overlay.hidden = true; overlay.innerHTML = ''; document.body.appendChild(overlay); var previewImage = overlay.querySelector('img'); var previewCaption = overlay.querySelector('figcaption'); var closeButton = overlay.querySelector('.wiki-image-lightbox-close'); function closePreview() { overlay.hidden = true; previewImage.removeAttribute('src'); previewCaption.textContent = ''; document.body.classList.remove('wiki-image-lightbox-open'); } function openPreview(image, event) { event.preventDefault(); event.stopPropagation(); if (event.stopImmediatePropagation) { event.stopImmediatePropagation(); } previewImage.src = image.currentSrc || image.src; previewImage.alt = image.alt || ''; previewCaption.textContent = captionFor(image); overlay.hidden = false; document.body.classList.add('wiki-image-lightbox-open'); closeButton.focus(); } Array.prototype.forEach.call(article.querySelectorAll('img[data-wiki-asset]'), function (image) { if (image.dataset.lightboxBound === '1') { return; } image.dataset.lightboxBound = '1'; image.addEventListener('click', function (event) { openPreview(image, event); }, true); }); article.addEventListener('click', function (event) { var image = event.target.closest && event.target.closest('img[data-wiki-asset]'); if (!image || !article.contains(image) || image.closest('.wiki-image-lightbox')) { return; } openPreview(image, event); }); closeButton.addEventListener('click', closePreview); overlay.addEventListener('click', function (event) { if (event.target === overlay) { closePreview(); } }); document.addEventListener('keydown', function (event) { if (event.key === 'Escape' && !overlay.hidden) { closePreview(); } }); } function installAll() { Array.prototype.forEach.call(document.querySelectorAll('.wiki-article, .vulture-article'), install); } ready(function () { installAll(); if (window.MutationObserver) { var observer = new MutationObserver(installAll); observer.observe(document.body, { childList: true, subtree: true }); } window.setTimeout(installAll, 500); window.setTimeout(installAll, 1500); window.setTimeout(installAll, 3000); }); }()); """.strip() def ensure_css(content): if ".wiki-image-lightbox" in content: return content if not STYLE_RE.search(content): raise RuntimeError("No style block found") return STYLE_RE.sub("\n" + LIGHTBOX_CSS + "\n", content, count=1) def ensure_script(content): script = "" if SCRIPT_RE.search(content): return SCRIPT_RE.sub(script, content, count=1) return content.rstrip() + "\n" + script + "\n" def main(): repo_root = Path.cwd() changed = 0 for relative in HTML_FILES: path = repo_root / relative original = path.read_text(encoding="utf-8") updated = ensure_script(ensure_css(original)) if updated != original: path.write_text(updated, encoding="utf-8", newline="\n") changed += 1 print(f"lightbox_updated file={relative}") else: print(f"lightbox_unchanged file={relative}") print(f"lightbox_summary html_changed={changed}") return 0 if __name__ == "__main__": sys.exit(main())