Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/log-portfolio/llms.txt

Use this file to discover all available pages before exploring further.

Log Portfolio’s window management system is modelled after a minimal OS window manager, implemented entirely in React context and state. WindowContext holds an array of live window descriptors and exposes a focused set of functions for opening, closing, and manipulating those windows. Any component that calls useWindows() can read and drive the entire windowing system — no prop-drilling required.

WindowData Shape

Every entry in the windows array is a WindowData object assembled when a window is first opened. The shape is:
// Window object shape (plain JS — no TypeScript)
{
  id,           // string — unique identifier, e.g. "about" or "projects"
  title,        // string — displayed in the window's title bar
  icon,         // ReactNode — small icon shown in the title bar and taskbar
  content,      // ReactNode — the full app component rendered inside the window
  defaultSize: {
    width,      // number — initial width in pixels
    height,     // number — initial height in pixels
  },
  isOpen,       // boolean — always true while in the windows array
  isMinimized,  // boolean — true when the window is hidden to the taskbar
  isMaximized,  // boolean — true when the window fills the entire viewport
  zIndex,       // number — stacking order; higher = in front
}
WindowContext initialises these fields automatically when you call openWindow() — you only need to provide id, title, icon, content, and defaultSize.

WindowContext API

The context object returned by useWindows() exposes the following values and functions.

windows: WindowData[]

The live array of all currently open windows. Components like Taskbar map over this array to render one button per window.

openWindow(windowData)

Opens a new window or focuses an existing one. If a window with the same id is already in the array, openWindow calls focusWindow(id) instead of creating a duplicate — so double-clicking the same desktop icon twice will bring the window to the front rather than stacking two copies. When a new window is created the global z-index counter is incremented and the new window receives the fresh value.
openWindow({
  id: "contact",
  title: "Outlook Express",
  icon: <Mail size={16} />,
  content: <ContactApp />,
  defaultSize: { width: 550, height: 450 },
});

closeWindow(id)

Removes the window with the given id from the windows array entirely. If the closed window was the active window, activeWindowId is cleared to null. The Framer Motion AnimatePresence wrapper in Window.js plays the exit animation (scale down + slide to y: 100vh) before the component unmounts.

minimizeWindow(id)

Sets isMinimized: true on the target window. The Window component checks this flag and returns null when it is true, so the window disappears from the desktop while remaining in the windows array (which keeps its taskbar button alive). If the minimized window was active, activeWindowId is cleared.

restoreWindow(id)

Un-minimizes a window by delegating directly to focusWindow(id), which sets isMinimized: false, assigns a new z-index, and marks the window as active. This is the function the Taskbar calls when a user clicks a minimized window’s button.

maximizeWindow(id)

Toggles isMaximized on the target window, then calls focusWindow(id) to bring it forward. When isMaximized is true, the Window component animates to width: 100vw and height: calc(100vh - 40px) (leaving room for the Taskbar) and disables dragging.

focusWindow(id)

The core focus primitive. It increments the global z-index counter, writes the new value to the target window’s zIndex field, and sets activeWindowId to that window’s id. Called directly on onMouseDown in the Window component so that clicking anywhere inside a window brings it to the front.

activeWindowId: string | null

The id of the currently focused window, or null if no window is active (e.g. after all windows are minimized or closed). The Window component reads this to conditionally apply the titlebar-active CSS class, giving focused windows the characteristic blue Win98 title bar.

useWindows() Hook

useWindows() is the public interface for reading and driving the window system. It reads from WindowContext and throws a descriptive error if called outside a WindowProvider, making misconfigured usage immediately obvious in development.
import { useWindows } from "../contexts/WindowContext";

function MyComponent() {
  const { openWindow, closeWindow, windows, activeWindowId } = useWindows();
  // ...
}
useWindows() must be called inside a component that is a descendant of WindowProvider. If you call it outside this boundary, it throws: "useWindows must be used within a WindowProvider".

Window Component Internals

The Window component in components/Window.js is responsible for rendering a single window frame and wiring all interactive behaviour to WindowContext.

Dragging

Dragging is implemented with Framer Motion’s drag prop on the root motion.div. The drag handle is scoped to the title bar via dragHandle=".title-bar", so only dragging the title bar moves the window. Drag is automatically disabled when isMaximized is true — passing drag={!windowData.isMaximized} is all that’s needed. When a drag ends, the onDragEnd callback receives the final offset and adds it to the window’s current x/y position, stored in local component state:
onDragEnd={(event, info) => {
  setPosition({
    x: position.x + info.offset.x,
    y: position.y + info.offset.y,
  });
}}

Resizing

The resize handle is a small div anchored to the bottom-right corner of every non-maximized window. It uses raw mousedown, mousemove, and mouseup listeners attached to document to track the drag delta and update the window’s size state:
onMouseDown={(e) => {
  e.preventDefault();
  const startX = e.clientX;
  const startY = e.clientY;
  const startWidth = size.width;
  const startHeight = size.height;

  const onMouseMove = (moveEvent) => {
    setSize({
      width: Math.max(200, startWidth + moveEvent.clientX - startX),
      height: Math.max(150, startHeight + moveEvent.clientY - startY),
    });
  };

  const onMouseUp = () => {
    document.removeEventListener("mousemove", onMouseMove);
    document.removeEventListener("mouseup", onMouseUp);
  };

  document.addEventListener("mousemove", onMouseMove);
  document.addEventListener("mouseup", onMouseUp);
}}
Minimum dimensions are enforced: 200px wide and 150px tall.

Animations

AnimatePresence from Framer Motion wraps the window list so that enter and exit transitions play correctly. Each window animates in with:
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.8, opacity: 0, y: "100vh" }}
transition={{ type: "spring", damping: 25, stiffness: 300 }}
This produces a snappy spring-based open and a slide-down-and-shrink close that matches retro OS window behaviour.

React Portal

Each Window renders via ReactDOM.createPortal() targeting the document root. This means the window div is inserted as a direct child of <body> rather than being nested inside the Desktop component tree, which prevents any parent overflow: hidden or z-index stacking context from clipping windows.

Registering and Opening a New Window

The following example shows how to add a new “README” app to the desktop registry and open it programmatically from an arbitrary component. Step 1 — Add an entry to the app registry in Desktop.js:
// components/Desktop.js
import { FileText } from "lucide-react";
import ReadmeApp from "./apps/ReadmeApp";

const apps = [
  // ... existing entries
  {
    id: "readme",
    title: "README.md",
    icon: <FileText size={32} className="text-green-400 drop-shadow-md" />,
    component: <ReadmeApp />,
    defaultSize: { width: 520, height: 380 },
  },
];
Step 2 — Open the window programmatically from any component:
import { useWindows } from "../contexts/WindowContext";
import { FileText } from "lucide-react";
import ReadmeApp from "./apps/ReadmeApp";

function OpenReadmeButton() {
  const { openWindow } = useWindows();

  return (
    <button
      onClick={() =>
        openWindow({
          id: "readme",
          title: "README.md",
          icon: <FileText size={16} />,
          content: <ReadmeApp />,
          defaultSize: { width: 520, height: 380 },
        })
      }
    >
      Open README
    </button>
  );
}
Always use a stable, unique string for id. openWindow uses this value to detect duplicates — if two calls use the same id, the second call focuses the existing window rather than opening a new one.

Build docs developers (and LLMs) love