Source code for scitex_browser.collaboration.visual_feedback

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Timestamp: "2025-10-19 05:42:00 (ywatanabe)"
# File: ./src/scitex/browser/collaboration/visual_feedback.py
# ----------------------------------------
"""
Visual feedback for collaborative browser sessions.

Shows who's controlling the browser and what they're doing.
"""

from typing import Optional

from playwright.async_api import Page


[docs] class VisualFeedback: """ Simple visual feedback overlay on browser. Shows: - Who's in control - What action is being performed - Status messages """
[docs] def __init__(self, page: Page): self.page = page self.initialized = False
[docs] async def initialize(self): """Initialize visual feedback overlay.""" if self.initialized: return await self.page.add_init_script( """ () => { // Create feedback container const container = document.createElement('div'); container.id = 'scitex-feedback-container'; container.style.cssText = ` position: fixed; top: 10px; right: 10px; z-index: 2147483647; font-family: system-ui, -apple-system, sans-serif; pointer-events: none; `; document.body?.appendChild(container) || window.addEventListener('load', () => { document.body.appendChild(container); }); // Make globally accessible window.scitexFeedback = { show: function(message, type = 'info', duration = 3000) { const colors = { 'info': '#2196F3', 'success': '#4CAF50', 'warning': '#FF9800', 'error': '#F44336', 'agent': '#9C27B0', 'human': '#00BCD4', }; const badge = document.createElement('div'); badge.style.cssText = ` background: ${colors[type] || colors.info}; color: white; padding: 10px 16px; border-radius: 8px; margin-bottom: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.3); animation: slideIn 0.3s ease-out; font-size: 13px; max-width: 300px; `; badge.textContent = message; const container = document.getElementById('scitex-feedback-container'); if (container) { container.appendChild(badge); if (duration > 0) { setTimeout(() => { badge.style.animation = 'slideOut 0.3s ease-in'; setTimeout(() => badge.remove(), 300); }, duration); } } }, showParticipant: function(name, type = 'agent') { const indicator = document.createElement('div'); indicator.id = `scitex-participant-${name}`; indicator.style.cssText = ` background: ${type === 'agent' ? '#9C27B0' : '#00BCD4'}; color: white; padding: 6px 12px; border-radius: 16px; margin-bottom: 6px; font-size: 12px; font-weight: 500; `; indicator.textContent = `${type === 'agent' ? '🤖' : '👤'} ${name}`; const container = document.getElementById('scitex-feedback-container'); if (container) { container.appendChild(indicator); } }, clear: function() { const container = document.getElementById('scitex-feedback-container'); if (container) { container.innerHTML = ''; } } }; // Add animations const style = document.createElement('style'); style.textContent = ` @keyframes slideIn { from { transform: translateX(400px); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(400px); opacity: 0; } } `; document.head?.appendChild(style) || window.addEventListener('load', () => { document.head.appendChild(style); }); } """ ) self.initialized = True
[docs] async def show_message( self, message: str, type: str = "info", # info, success, warning, error, agent, human duration: int = 3000, ): """Show feedback message.""" await self.page.evaluate( f"window.scitexFeedback?.show('{message}', '{type}', {duration})" )
[docs] async def show_participant(self, name: str, type: str = "agent"): """Show participant indicator.""" await self.page.evaluate( f"window.scitexFeedback?.showParticipant('{name}', '{type}')" )
[docs] async def show_action(self, participant_name: str, action: str): """Show what participant is doing.""" await self.show_message( f"{participant_name} is {action}...", type="agent" if "agent" in participant_name.lower() else "human", duration=2000, )
[docs] async def clear(self): """Clear all feedback.""" await self.page.evaluate("window.scitexFeedback?.clear()")
# EOF