diff --git a/css/tailwind.source.css b/css/tailwind.source.css index 3f38c8aa..81ced014 100644 --- a/css/tailwind.source.css +++ b/css/tailwind.source.css @@ -3295,6 +3295,23 @@ .watch-history-card__hidden .moderation-badge { @apply self-start; } + + /* Ensure the video modal avatar respects the blur state */ + #playerModal .channel-card__avatar img[data-visual-state="blurred"] { + @apply transition duration-base ease-in-out; + filter: blur(var(--blur-thumbnail-blur)); + transform: scale(1.05); + } + + /* Override moderation badge text color inside the modal bar for better contrast in all themes */ + #playerModal .video-modal__moderation-bar .moderation-badge { + --text-status-neutral-on: var(--color-text); + --text-status-warning-on: var(--color-warning-on); + } + + #playerModal .video-modal__moderation-bar .moderation-badge[data-variant="neutral"] { + color: var(--color-text); + } } @layer components { diff --git a/js/ui/components/VideoModal.js b/js/ui/components/VideoModal.js index 5b3093af..4a80168b 100644 --- a/js/ui/components/VideoModal.js +++ b/js/ui/components/VideoModal.js @@ -4140,6 +4140,14 @@ export class VideoModal { } } + if (this.creatorAvatar) { + if (shouldBlur) { + this.creatorAvatar.dataset.visualState = "blurred"; + } else if (this.creatorAvatar.dataset.visualState === "blurred") { + delete this.creatorAvatar.dataset.visualState; + } + } + if (overlay) { // Changed logic: Always show the bar if moderation is active, even if overridden if (showModeration) { diff --git a/verification/3_dark_mode_neutral.png b/verification/3_dark_mode_neutral.png new file mode 100644 index 00000000..036a24c3 Binary files /dev/null and b/verification/3_dark_mode_neutral.png differ diff --git a/verification/4_light_mode_neutral.png b/verification/4_light_mode_neutral.png new file mode 100644 index 00000000..4bf18ab1 Binary files /dev/null and b/verification/4_light_mode_neutral.png differ diff --git a/verification/verify_moderation_style.py b/verification/verify_moderation_style.py new file mode 100644 index 00000000..524189b9 --- /dev/null +++ b/verification/verify_moderation_style.py @@ -0,0 +1,123 @@ +from playwright.sync_api import sync_playwright +import time + +def verify_moderation_style(page): + print("Navigating to app...") + page.goto("http://localhost:8080") + + # Wait for the app to initialize + print("Waiting for #videoList...") + page.wait_for_selector("#videoList", timeout=30000) + + # Inject logic to manipulate DOM directly to verify structure and styles + print("Manipulating DOM to show modal...") + page.evaluate(""" + const modal = document.getElementById('playerModal'); + modal.classList.remove('hidden'); + modal.removeAttribute('hidden'); + document.body.classList.add('modal-open'); + + // Find the moderation bar and show it in "neutral" state (simulating "Show anyway") + const bar = modal.querySelector('[data-moderation-bar]'); + if (bar) { + bar.removeAttribute('hidden'); + const badge = bar.querySelector('.moderation-badge'); + if (badge) { + badge.setAttribute('data-variant', 'neutral'); + const text = badge.querySelector('[data-moderation-text]'); + if (text) text.textContent = "Showing despite blocked content"; + } + } + + // Unblur video but blur avatar to simulate "active moderation" state visually + // (Though logically if "neutral" usually implies override, maybe we want to test that too. + // Let's test the specific requirement: Avatar Blur + Text Color) + + // Force blur on avatar + const avatarImg = modal.querySelector('.channel-card__avatar img'); + if (avatarImg) { + avatarImg.src = 'assets/svg/default-profile.svg'; // Ensure src so it renders + avatarImg.dataset.visualState = 'blurred'; + } + """) + + time.sleep(1) + + # Test Dark Mode first (usually default or we can toggle) + # Check if we can toggle theme. + # Assuming default might be light or system. Let's force dark mode if possible. + print("Setting Dark Mode...") + page.evaluate("document.documentElement.setAttribute('data-theme', 'dark')") + time.sleep(0.5) + page.screenshot(path="verification/3_dark_mode_neutral.png") + print("Screenshot 3 taken: Dark Mode Neutral Badge + Blurred Avatar") + + # Verify Avatar Blur CSS + # We can check computed style via evaluate + blur_filter = page.evaluate(""" + () => { + const img = document.querySelector('#playerModal .channel-card__avatar img'); + return window.getComputedStyle(img).filter; + } + """) + print(f"Avatar Filter: {blur_filter}") + if "blur" in blur_filter: + print("SUCCESS: Avatar has blur filter applied.") + else: + print("FAILURE: Avatar does NOT have blur filter.") + + # Verify Text Color in Dark Mode + # The badge text should be readable (light color in dark mode) + text_color = page.evaluate(""" + () => { + const badge = document.querySelector('#playerModal .video-modal__moderation-bar .moderation-badge'); + return window.getComputedStyle(badge).color; + } + """) + print(f"Badge Text Color (Dark Mode): {text_color}") + # In dark mode, text should be rgb(226, 232, 240) or similar light color. + # rgb(24, 31, 44) is dark. + + # Simple check: sum of RGB should be high for light color + import re + rgb = re.findall(r'\d+', text_color) + if rgb and len(rgb) >= 3: + brightness = sum(map(int, rgb[:3])) + print(f"Text Brightness: {brightness}") + if brightness > 300: # Arbitrary threshold for "light enough" + print("SUCCESS: Text is light in Dark Mode.") + else: + print("FAILURE: Text is dark in Dark Mode (Hard to read).") + + # Test Light Mode + print("Setting Light Mode...") + page.evaluate("document.documentElement.setAttribute('data-theme', 'light')") + time.sleep(0.5) + page.screenshot(path="verification/4_light_mode_neutral.png") + print("Screenshot 4 taken: Light Mode Neutral Badge") + + text_color_light = page.evaluate(""" + () => { + const badge = document.querySelector('#playerModal .video-modal__moderation-bar .moderation-badge'); + return window.getComputedStyle(badge).color; + } + """) + print(f"Badge Text Color (Light Mode): {text_color_light}") + rgb_light = re.findall(r'\d+', text_color_light) + if rgb_light and len(rgb_light) >= 3: + brightness_light = sum(map(int, rgb_light[:3])) + print(f"Text Brightness: {brightness_light}") + if brightness_light < 400: # Arbitrary threshold for "dark enough" + print("SUCCESS: Text is dark in Light Mode.") + else: + print("FAILURE: Text is light in Light Mode (Hard to read).") + +with sync_playwright() as p: + browser = p.chromium.launch(headless=True) + page = browser.new_page() + try: + verify_moderation_style(page) + except Exception as e: + print(f"Verification failed: {e}") + finally: + browser.close()