// app/Taskbar.tsx

'use client';

import React from 'react';
import { useWindowManager } from '../contexts/WindowManagerContext';
import { FaWindows } from 'react-icons/fa';

const Taskbar: React.FC = () => {
  const { windows, windowStates, openWindow, restoreWindow, minimizeWindow } = useWindowManager();

  return (
    <div className="fixed bottom-0 left-0 right-0 bg-gray-800 h-[50px] flex items-center px-2 taskbar">
      {/* Meta Icon Button */}
      <button className="text-white p-2 hover:bg-gray-700 rounded">
        <FaWindows size={24} />
      </button>
      {/* Divider */}
      <div className="h-6 border-l border-gray-600 mx-2"></div>
      {/* Window Buttons */}
      {Object.keys(windows).map((name) => {
        const state = windowStates[name];
        if (!state) return null;

        let buttonStyle = 'bg-gray-600 text-white px-4 py-1 mx-1 rounded hover:bg-gray-500';

        if (state.visible && !state.minimized) {
          // Window is open
          buttonStyle = 'bg-blue-600 text-white px-4 py-1 mx-1 rounded hover:bg-blue-500';
        } else if (state.minimized) {
          // Window is minimized
          buttonStyle = 'bg-yellow-500 text-white px-4 py-1 mx-1 rounded hover:bg-yellow-400';
        } else {
          // Window is closed
          buttonStyle = 'bg-gray-600 text-white px-4 py-1 mx-1 rounded hover:bg-gray-500';
        }

        const handleClick = () => {
          if (state.visible && !state.minimized) {
            // Window is open and not minimized, minimize it
            minimizeWindow(name);
          } else if (state.minimized) {
            // Restore minimized window
            restoreWindow(name);
          } else {
            // Open window
            openWindow(name);
          }
        };

        return (
          <button key={name} onClick={handleClick} className={buttonStyle}>
            {name}
          </button>
        );
      })}
    </div>
  );
};

export default Taskbar;

AND:

// contexts/WindowManagerContext.tsx

'use client';

import React, { createContext, useContext, useState, useEffect } from 'react';

type WindowProps = {
  width?: string;
  height?: string;
  initialState?: 'open' | 'closed' | 'minimized';
  gravity?: number; // Existing gravity property
  initialX?: string; // New initialX property
  initialY?: string; // New initialY property
  windowTitle?: string; // New windowTitle property
};

type WindowData = {
  name: string;
  Component: React.ComponentType;
  windowProps: WindowProps;
};

type WindowState = {
  visible: boolean;
  minimized: boolean;
  maximized: boolean;
  width: number;
  height: number;
  x: number;
  y: number;
  zIndex: number;
  windowTitle: string; // Added windowTitle to WindowState for easy access
  prevWidth?: number;
  prevHeight?: number;
  prevX?: number;
  prevY?: number;
};

type WindowManagerContextType = {
  windows: { [key: string]: WindowData };
  windowStates: { [key: string]: WindowState };
  openWindow: (name: string) => void;
  closeWindow: (name: string) => void;
  minimizeWindow: (name: string) => void;
  restoreWindow: (name: string) => void;
  maximizeWindow: (name: string) => void;
  updateWindowState: (name: string, newState: Partial<WindowState>, skipGrid?: boolean) => void;
  bringToFront: (name: string) => void;
};

const WindowManagerContext = createContext<WindowManagerContextType | undefined>(undefined);

export const useWindowManager = () => {
  const context = useContext(WindowManagerContext);
  if (!context) {
    throw new Error('useWindowManager must be used within a WindowManagerProvider');
  }
  return context;
};

export const WindowManagerProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [windows, setWindows] = useState<{ [key: string]: WindowData }>({});
  const [windowStates, setWindowStates] = useState<{ [key: string]: WindowState }>({});
  const [nextZIndex, setNextZIndex] = useState(1);

  // Define grid size constant
  const GRID_SIZE = 25;

  type WindowDataAndState = {
    windowData: WindowData;
    windowState: WindowState;
  };

  useEffect(() => {
    // Dynamically import window components
    const importWindows = async () => {
      // Replace with your actual dynamic import logic if needed
      const windowsContext = require.context('../windows', false, /\.tsx$/);
      const windowFiles = windowsContext.keys();

      let zIndexCounter = 1;

      const windowsDataPromises: Promise<WindowDataAndState>[] = windowFiles.map(
        async (file: string, index: number): Promise<WindowDataAndState> => {
          const mod = await windowsContext(file);
          const name = file.replace('./', '').replace('.tsx', '');
          const Component = mod.default;
          const windowProps: WindowProps = mod.windowProps || {};

          // Parse initial size
          let width = windowProps.width
            ? parseSize(windowProps.width, 'width')
            : 0.35 * window.innerWidth;
          let height = windowProps.height
            ? parseSize(windowProps.height, 'height')
            : 0.2 * window.innerHeight;

          // Adjust width and height to align with grid
          width = Math.round(width / GRID_SIZE) * GRID_SIZE;
          height = Math.round(height / GRID_SIZE) * GRID_SIZE;

          // Parse initial position
          let x = windowProps.initialX
            ? parseSize(windowProps.initialX, 'width') // Use 'width' for initialX
            : 100 + index * 50; // Default value if initialX not provided
          let y = windowProps.initialY
            ? parseSize(windowProps.initialY, 'height') // Use 'height' for initialY
            : 100 + index * 50; // Default value if initialY not provided

          // Adjust x and y to align with grid
          x = Math.round(x / GRID_SIZE) * GRID_SIZE;
          y = Math.round(y / GRID_SIZE) * GRID_SIZE;

          // Determine initial state
          const initialState = windowProps.initialState || 'open';
          let visible = true;
          let minimized = false;

          switch (initialState) {
            case 'open':
              visible = true;
              minimized = false;
              break;
            case 'closed':
              visible = false;
              minimized = false;
              break;
            case 'minimized':
              visible = false;
              minimized = true;
              break;
            default:
              visible = true;
              minimized = false;
              break;
          }

          const windowData: WindowData = { name, Component, windowProps };
          const windowTitle = windowProps.windowTitle || name; // Use windowTitle prop or default to name
          const windowState: WindowState = {
            visible,
            minimized,
            maximized: false,
            width,
            height,
            x,
            y,
            zIndex: zIndexCounter++,
            windowTitle, // Store windowTitle in state
          };

          return { windowData, windowState };
        }
      );

      const windowsDataArray: WindowDataAndState[] = await Promise.all(windowsDataPromises);

      const newWindows: { [key: string]: WindowData } = {};
      const newWindowStates: { [key: string]: WindowState } = {};

      windowsDataArray.forEach(({ windowData, windowState }) => {
        newWindows[windowData.name] = windowData;
        newWindowStates[windowData.name] = windowState;
      });

      setWindows(newWindows);
      setWindowStates(newWindowStates);
      setNextZIndex(zIndexCounter);
    };

    importWindows();
  }, []);

  const bringToFront = (name: string) => {
    setWindowStates((prev) => {
      const maxZ = nextZIndex;
      setNextZIndex(maxZ + 1);
      return {
        ...prev,
        [name]: {
          ...prev[name],
          zIndex: maxZ,
        },
      };
    });
  };

  const openWindow = (name: string) => {
    setWindowStates((prev) => ({
      ...prev,
      [name]: {
        ...prev[name],
        visible: true,
        minimized: false,
      },
    }));
    bringToFront(name);
  };

  const closeWindow = (name: string) => {
    setWindowStates((prev) => ({
      ...prev,
      [name]: {
        ...prev[name],
        visible: false,
        minimized: false,
      },
    }));
  };

  const minimizeWindow = (name: string) => {
    setWindowStates((prev) => ({
      ...prev,
      [name]: {
        ...prev[name],
        minimized: true,
        visible: false,
      },
    }));
  };

  const restoreWindow = (name: string) => {
    setWindowStates((prev) => ({
      ...prev,
      [name]: {
        ...prev[name],
        minimized: false,
        visible: true,
      },
    }));
    bringToFront(name);
  };

  const maximizeWindow = (name: string) => {
    setWindowStates((prev) => {
      const state = prev[name];
      const isMaximized = !state.maximized;
  
      if (isMaximized) {
        // Store previous size and position
        // Client-side check for taskbar height
      let TASKBAR_HEIGHT_PX = 80; // Default value
      if (typeof window !== 'undefined') {
        const taskbar = document.querySelector('.taskbar');
        if (taskbar) {
          TASKBAR_HEIGHT_PX = taskbar.clientHeight;
        }
      }
        const prevSizePosition = {
          prevWidth: state.width,
          prevHeight: state.height,
          prevX: state.x,
          prevY: state.y,
        };
  
        const maxWidth = window.innerWidth - 20; // Adjusted width for spacing
        const maxHeight = window.innerHeight - TASKBAR_HEIGHT_PX - 20; // Subtract taskbar height
  
        // Adjust to align with grid
        const adjustedWidth = Math.floor(maxWidth / GRID_SIZE) * GRID_SIZE;
        const adjustedHeight = Math.floor(maxHeight / GRID_SIZE) * GRID_SIZE;
  
        return {
          ...prev,
          [name]: {
            ...state,
            maximized: true,
            ...prevSizePosition,
            x: 10, // Adjusted for spacing when maximized
            y: 10, // Adjusted for spacing when maximized
            width: adjustedWidth,
            height: adjustedHeight,
          },
        };
      } else {
        // Restore previous size and position
        return {
          ...prev,
          [name]: {
            ...state,
            maximized: false,
            width: state.prevWidth || state.width,
            height: state.prevHeight || state.height,
            x: state.prevX || state.x,
            y: state.prevY || state.y,
          },
        };
      }
    });
    bringToFront(name);
  };

  const updateWindowState = (
    name: string,
    newState: Partial<WindowState>,
    skipGrid = false
  ) => {
    setWindowStates((prev) => {
      let updatedState = {
        ...prev[name],
        ...newState,
      };

      if (!skipGrid) {
        // Ensure positions and sizes align with the grid
        updatedState.x = Math.round(updatedState.x / GRID_SIZE) * GRID_SIZE;
        updatedState.y = Math.round(updatedState.y / GRID_SIZE) * GRID_SIZE;
        updatedState.width = Math.round(updatedState.width / GRID_SIZE) * GRID_SIZE;
        updatedState.height = Math.round(updatedState.height / GRID_SIZE) * GRID_SIZE;
      }

      return {
        ...prev,
        [name]: updatedState,
      };
    });
  };

  function parseSize(value: string, dimension: 'width' | 'height'): number {
    if (typeof window === 'undefined') return 400;

    value = value.trim();

    if (value.endsWith('%')) {
      const percentage = parseFloat(value) / 100;
      if (dimension === 'width') {
        return percentage * window.innerWidth;
      } else {
        return percentage * window.innerHeight;
      }
    } else if (value.endsWith('px')) {
      return parseFloat(value);
    } else {
      // Assume pixels if unit is missing
      return parseFloat(value);
    }
  }

  return (
    <WindowManagerContext.Provider
      value={{
        windows,
        windowStates,
        openWindow,
        closeWindow,
        minimizeWindow,
        restoreWindow,
        maximizeWindow,
        updateWindowState,
        bringToFront,
      }}
    >
      {children}
    </WindowManagerContext.Provider>
  );
};

AND:

// app/DraggableWindows.tsx

'use client';

import React from 'react';
import { Rnd } from 'react-rnd';
import { FiMinimize, FiMaximize, FiX } from 'react-icons/fi';
import { useWindowManager } from '../contexts/WindowManagerContext';
import { motion, AnimatePresence } from 'framer-motion';

const DraggableWindows: React.FC = () => {
  const {
    windows,
    windowStates,
    closeWindow,
    minimizeWindow,
    maximizeWindow,
    updateWindowState,
    bringToFront,
  } = useWindowManager();

  return (
    <div className="w-full h-full fixed top-0 left-0">
      {Object.keys(windows).map((name) => {
        const { Component } = windows[name];
        const state = windowStates[name];

        if (!Component) return null;

        const isVisible = state.visible && !state.minimized;

        return (
          <AnimatePresence key={name}>
            {isVisible && (
              <WindowWrapper
                key={name}
                name={name}
                state={state}
                bringToFront={bringToFront}
                updateWindowState={updateWindowState}
                minimizeWindow={minimizeWindow}
                maximizeWindow={maximizeWindow}
                closeWindow={closeWindow}
              >
                <Component />
              </WindowWrapper>
            )}
          </AnimatePresence>
        );
      })}
    </div>
  );
};

interface WindowState {
  visible: boolean;
  minimized: boolean;
  maximized: boolean;
  width: number;
  height: number;
  x: number;
  y: number;
  zIndex: number;
  windowTitle: string; // Include windowTitle in state
}

interface WindowWrapperProps {
  name: string;
  state: WindowState;
  bringToFront: (name: string) => void;
  updateWindowState: (name: string, newState: Partial<WindowState>) => void;
  minimizeWindow: (name: string) => void;
  maximizeWindow: (name: string) => void;
  closeWindow: (name: string) => void;
  children: React.ReactNode;
}

const WindowWrapper: React.FC<WindowWrapperProps> = ({
  name,
  state,
  bringToFront,
  updateWindowState,
  minimizeWindow,
  maximizeWindow,
  closeWindow,
  children,
}) => {
  const { windowStates } = useWindowManager();

  // Determine if the window is the topmost window
  const maxZIndex = Math.max(...Object.values(windowStates).map((w) => w.zIndex));
  const isTopWindow = state.zIndex === maxZIndex;

  // Determine box shadow based on zIndex
  const boxShadow = isTopWindow
    ? '0px 0px 10px rgba(0,0,0,0.5)'
    : '0px 0px 5px rgba(0,0,0,0.2)';

  // Use layout animations for smooth transitions
  const windowMotionProps = {
    layout: true,
    initial: { opacity: 0 },
    animate: { opacity: 1 },
    exit: { opacity: 0 },
    transition: { duration: 0.2 },
  };

  return (
    <Rnd
      size={{ width: state.width, height: state.height }}
      position={{ x: state.x, y: state.y }}
      onMouseDown={() => bringToFront(name)}
      onDragStart={() => bringToFront(name)}
      onDrag={(e, d) => {
        updateWindowState(name, { x: d.x, y: d.y }, true); // skipGrid = true
      }}
      onDragStop={(e, d) => {
        updateWindowState(name, { x: d.x, y: d.y }); // Snap to grid after drag ends
      }}
      onResize={(e, direction, ref, delta, position) => {
        const newWidth = parseInt(ref.style.width, 10);
        const newHeight = parseInt(ref.style.height, 10);
        updateWindowState(
          name,
          {
            width: newWidth,
            height: newHeight,
            x: position.x,
            y: position.y,
          },
          true // skipGrid = true
        );
      }}
      onResizeStop={(e, direction, ref, delta, position) => {
        const newWidth = parseInt(ref.style.width, 10);
        const newHeight = parseInt(ref.style.height, 10);
        updateWindowState(name, {
          width: newWidth,
          height: newHeight,
          x: position.x,
          y: position.y,
        }); // Snap to grid after resize ends
      }}
      minWidth={200}
      minHeight={100}
      grid={[25, 25]} // Snapping to 25px grid
      dragHandleClassName="window-bar"
      disableDragging={state.maximized}
      enableResizing={!state.maximized}
      bounds="parent"
      style={{ zIndex: state.zIndex }}
    >
      <motion.div
        {...windowMotionProps}
        className="bg-white border border-gray-500 flex flex-col h-full"
        style={{ boxShadow }}
        onMouseDown={() => bringToFront(name)}
      >
        {/* Window Bar */}
        <div
          className="flex items-center justify-between bg-gray-200 p-1 window-bar"
          style={{ cursor: state.maximized ? 'default' : 'move' }}
        >
          <span className="pl-2">{state.windowTitle}</span> {/* Use windowTitle from state */}
          <div className="flex">
            <button
              onClick={(e) => {
                e.stopPropagation();
                minimizeWindow(name);
              }}
              className="p-1 hover:bg-gray-300"
            >
              <FiMinimize />
            </button>
            <button
              onClick={(e) => {
                e.stopPropagation();
                maximizeWindow(name);
              }}
              className="p-1 hover:bg-gray-300"
            >
              <FiMaximize />
            </button>
            <button
              onClick={(e) => {
                e.stopPropagation();
                closeWindow(name);
              }}
              className="p-1 hover:bg-gray-300"
            >
              <FiX />
            </button>
          </div>
        </div>
        {/* Window Content */}
        <div className="flex-grow overflow-auto window-content p-[5px]">
          {children}
        </div>
      </motion.div>
    </Rnd>
  );
};

export default DraggableWindows;

TASK: I want it so that there is a new prop called pinnedToTaskbar either true or false and that will mean if it is pinned to taskbar e.g. always displayed even is window is closed. if that is false window will still display in taskbar but only is opened or minimsied.