Overlay Example

/**
 * Test Overlay - Background Module Example
 * This demonstrates a background overlay that runs outside of project context
*/

module.exports = ({ useState, useEffect, useRef, useGlobalState, api, usePlaceholders }) => {
  const [isVisible, setIsVisible] = useGlobalState('overlayVisible', true);
  const [position, setPosition] = useGlobalState('overlayPosition', { x: 100, y: 100 });
  const [isDragging, setIsDragging] = useState(false);
  const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
  const [stats, setStats] = useGlobalState('overlayStats', {
    clicks: 0,
    toggles: 0,
    lastInteraction: null
  });
  const overlayRef = useRef(null);
  const placeholders = usePlaceholders();

  // Listen for global keyboard shortcuts
  useEffect(() => {
    const handleKeyPress = (e) => {
      // Ctrl+Shift+O to toggle overlay
      if (e.ctrlKey && e.shiftKey && e.key === 'O') {
        e.preventDefault();
        setIsVisible(prev => {
          const newState = !prev;
          setStats(prev => ({
            ...prev,
            toggles: prev.toggles + 1,
            lastInteraction: new Date().toISOString()
          }));
          
          api.nexomaker.background.notify({
            type: newState ? 'success' : 'info',
            title: 'Test Overlay',
            message: `Overlay ${newState ? 'shown' : 'hidden'} via keyboard shortcut`,
            duration: 2000
          });
          
          return newState;
        });
      }
      
      // Ctrl+Shift+R to reset overlay position
      if (e.ctrlKey && e.shiftKey && e.key === 'R') {
        e.preventDefault();
        setPosition({ x: 100, y: 100 });
        api.nexomaker.background.notify({
          type: 'info',
          title: 'Test Overlay',
          message: 'Position reset to default',
          duration: 2000
        });
      }
    };

    window.addEventListener('keydown', handleKeyPress);
    return () => window.removeEventListener('keydown', handleKeyPress);
  }, []);

  // Handle dragging
  const handleMouseDown = (e) => {
    setIsDragging(true);
    const rect = overlayRef.current.getBoundingClientRect();
    setDragOffset({
      x: e.clientX - rect.left,
      y: e.clientY - rect.top
    });
  };

  useEffect(() => {
    const handleMouseMove = (e) => {
      if (isDragging) {
        setPosition({
          x: e.clientX - dragOffset.x,
          y: e.clientY - dragOffset.y
        });
      }
    };

    const handleMouseUp = () => {
      setIsDragging(false);
    };

    if (isDragging) {
      window.addEventListener('mousemove', handleMouseMove);
      window.addEventListener('mouseup', handleMouseUp);
    }

    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
      window.removeEventListener('mouseup', handleMouseUp);
    };
  }, [isDragging, dragOffset]);

  const handleClick = () => {
    setStats(prev => ({
      ...prev,
      clicks: prev.clicks + 1,
      lastInteraction: new Date().toISOString()
    }));
  };

  const showStatsOverlay = () => {
    api.nexomaker.background.showOverlay('stats-overlay', `
      <div style="
        background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
        color: white;
        padding: 24px;
        border-radius: 12px;
        box-shadow: 0 8px 32px rgba(0,0,0,0.3);
        max-width: 400px;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      ">
        <h3 style="margin: 0 0 20px 0; font-size: 18px; font-weight: 600;">
          📊 Test Overlay Statistics
        </h3>
        <div style="margin: 12px 0; padding: 16px; background: rgba(255,255,255,0.1); border-radius: 8px;">
          <p style="margin: 8px 0;"><strong>Total Clicks:</strong> ${stats.clicks}</p>
          <p style="margin: 8px 0;"><strong>Total Toggles:</strong> ${stats.toggles}</p>
          <p style="margin: 8px 0;"><strong>Current Position:</strong> (${Math.round(position.x)}, ${Math.round(position.y)})</p>
          <p style="margin: 8px 0;"><strong>Last Interaction:</strong> ${stats.lastInteraction ? new Date(stats.lastInteraction).toLocaleString() : 'Never'}</p>
          <p style="margin: 8px 0;"><strong>Overlay Visible:</strong> ${isVisible ? '✅ Yes' : '❌ No'}</p>
          <p style="margin: 8px 0;"><strong>Context:</strong> ${placeholders.context}</p>
          <p style="margin: 8px 0;"><strong>Project ID:</strong> ${placeholders.projectId}</p>
        </div>
        <div style="margin-top: 20px; font-size: 12px; opacity: 0.8;">
          <p style="margin: 4px 0;">🔥 <strong>Shortcuts:</strong></p>
          <p style="margin: 4px 0;">• Ctrl+Shift+O: Toggle overlay</p>
          <p style="margin: 4px 0;">• Ctrl+Shift+R: Reset position</p>
          <p style="margin: 4px 0;">• Drag to move overlay</p>
        </div>
        <button 
          onclick="window.dispatchEvent(new CustomEvent('modular-hide-overlay', {detail: {overlayId: 'stats-overlay'}}))" 
          style="
            background: rgba(255,255,255,0.2);
            color: white;
            border: 1px solid rgba(255,255,255,0.3);
            padding: 12px 24px;
            border-radius: 8px;
            cursor: pointer;
            font-size: 14px;
            width: 100%;
            margin-top: 16px;
          "
        >
          Close Statistics
        </button>
      </div>
    `);
  };

  const resetStats = () => {
    setStats({
      clicks: 0,
      toggles: 0,
      lastInteraction: null
    });
    api.nexomaker.background.notify({
      type: 'success',
      title: 'Test Overlay',
      message: 'Statistics reset successfully',
      duration: 2000
    });
  };

  const testBackgroundApis = () => {
    api.console.log('🧪 Testing background APIs...');
    api.console.log('Context:', placeholders.context);
    api.console.log('Project ID:', placeholders.projectId);
    
    // Test global data storage
    api.nexomaker.background.setGlobalData('testData', {
      timestamp: new Date().toISOString(),
      clicks: stats.clicks,
      context: placeholders.context
    });
    
    const retrievedData = api.nexomaker.background.getGlobalData('testData');
    api.console.log('Global data test:', retrievedData);
    
    api.nexomaker.background.notify({
      type: 'success',
      title: 'API Test Complete',
      message: 'All background APIs working correctly!',
      duration: 3000
    });
  };

  if (!isVisible) {
    return null;
  }

  return (
    <div 
      ref={overlayRef}
      className="interactive"
      style={{
        position: 'fixed',
        left: `${position.x}px`,
        top: `${position.y}px`,
        background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
        color: 'white',
        padding: '16px',
        borderRadius: '12px',
        boxShadow: '0 8px 32px rgba(0,0,0,0.3)',
        cursor: isDragging ? 'grabbing' : 'grab',
        userSelect: 'none',
        zIndex: 2000,
        minWidth: '280px',
        border: '1px solid rgba(255,255,255,0.2)',
        backdropFilter: 'blur(10px)'
      }}
      onMouseDown={handleMouseDown}
      onClick={handleClick}
    >
      <div style={{ marginBottom: '12px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <h4 style={{ margin: 0, fontSize: '16px', fontWeight: '600' }}>
          🧪 Test Overlay ({placeholders.context})
        </h4>
        <button
          onClick={(e) => {
            e.stopPropagation();
            setIsVisible(false);
          }}
          style={{
            background: 'rgba(255,255,255,0.2)',
            border: 'none',
            color: 'white',
            width: '24px',
            height: '24px',
            borderRadius: '50%',
            cursor: 'pointer',
            fontSize: '14px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center'
          }}
        >
          ×
        </button>
      </div>
      
      <div style={{ fontSize: '14px', marginBottom: '12px', opacity: 0.9 }}>
        Background overlay running in {placeholders.context} context
      </div>
      
      <div style={{ 
        background: 'rgba(255,255,255,0.1)', 
        padding: '12px', 
        borderRadius: '8px', 
        marginBottom: '12px',
        fontSize: '12px'
      }}>
        <div>📊 Clicks: {stats.clicks}</div>
        <div>🔄 Toggles: {stats.toggles}</div>
        <div>📍 Position: ({Math.round(position.x)}, {Math.round(position.y)})</div>
        <div>🎯 Project: {placeholders.projectId}</div>
      </div>
      
      <div style={{ display: 'flex', gap: '8px', flexDirection: 'column' }}>
        <button
          onClick={(e) => {
            e.stopPropagation();
            showStatsOverlay();
          }}
          style={{
            background: 'rgba(255,255,255,0.2)',
            border: '1px solid rgba(255,255,255,0.3)',
            color: 'white',
            padding: '8px 12px',
            borderRadius: '6px',
            cursor: 'pointer',
            fontSize: '12px'
          }}
        >
          📊 Show Statistics
        </button>
        
        <button
          onClick={(e) => {
            e.stopPropagation();
            testBackgroundApis();
          }}
          style={{
            background: 'rgba(16, 185, 129, 0.3)',
            border: '1px solid rgba(16, 185, 129, 0.5)',
            color: 'white',
            padding: '8px 12px',
            borderRadius: '6px',
            cursor: 'pointer',
            fontSize: '12px'
          }}
        >
          🧪 Test Background APIs
        </button>
        
        <button
          onClick={(e) => {
            e.stopPropagation();
            resetStats();
          }}
          style={{
            background: 'rgba(239, 68, 68, 0.3)',
            border: '1px solid rgba(239, 68, 68, 0.5)',
            color: 'white',
            padding: '8px 12px',
            borderRadius: '6px',
            cursor: 'pointer',
            fontSize: '12px'
          }}
        >
          🔄 Reset Stats
        </button>
        
        <button
          onClick={(e) => {
            e.stopPropagation();
            api.nexomaker.background.notify({
              type: 'info',
              title: 'Test Notification',
              message: `Overlay clicked ${stats.clicks} times! Running in ${placeholders.context} context`,
              duration: 4000
            });
          }}
          style={{
            background: 'rgba(59, 130, 246, 0.3)',
            border: '1px solid rgba(59, 130, 246, 0.5)',
            color: 'white',
            padding: '8px 12px',
            borderRadius: '6px',
            cursor: 'pointer',
            fontSize: '12px'
          }}
        >
          🔔 Test Notification
        </button>
      </div>
      
      <div style={{ 
        marginTop: '12px', 
        fontSize: '10px', 
        opacity: 0.7, 
        textAlign: 'center',
        borderTop: '1px solid rgba(255,255,255,0.2)',
        paddingTop: '8px'
      }}>
        Ctrl+Shift+O to toggle • Drag to move • Ctrl+Shift+R to reset
      </div>
    </div>
  );
};

Last updated