Skip to main content

Overview

Adding a new application to Web XP involves several steps:
  1. Create the app component
  2. Add app assets (icons, images)
  3. Register the app in appSettings
  4. Add the app to desktop icons (optional)
  5. Add the app to the start menu (optional)
This guide walks through each step with examples.

Step 1: Create the App Component

Create a new component for your app in src/WinXP/apps/.

Example: Simple Calculator App

Create src/WinXP/apps/Calculator/index.jsx:
import React, { useState } from 'react';
import styled from 'styled-components';

function Calculator() {
  const [display, setDisplay] = useState('0');
  
  const handleNumberClick = (num) => {
    setDisplay(display === '0' ? String(num) : display + num);
  };
  
  const handleClear = () => {
    setDisplay('0');
  };
  
  return (
    <Container>
      <Display>{display}</Display>
      <ButtonGrid>
        {[1, 2, 3, 4, 5, 6, 7, 8, 9, 0].map(num => (
          <Button key={num} onClick={() => handleNumberClick(num)}>
            {num}
          </Button>
        ))}
        <Button onClick={handleClear}>C</Button>
      </ButtonGrid>
    </Container>
  );
}

const Container = styled.div`
  display: flex;
  flex-direction: column;
  padding: 10px;
  background: #ece9d8;
  height: 100%;
`;

const Display = styled.div`
  background: white;
  padding: 10px;
  margin-bottom: 10px;
  text-align: right;
  font-family: 'Courier New', monospace;
  font-size: 24px;
  border: 2px inset #fff;
`;

const ButtonGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 5px;
`;

const Button = styled.button`
  padding: 20px;
  font-size: 18px;
  background: #d4d0c8;
  border: 2px outset #fff;
  cursor: pointer;
  
  &:active {
    border-style: inset;
  }
`;

export default Calculator;

App Component Guidelines

Styling:
  • Use styled-components for all styling
  • Match Windows XP visual style (colors: #ece9d8, #d4d0c8, #fff)
  • Use borders: 2px outset #fff for buttons
Dimensions:
  • The app fills its window container automatically
  • Use height: 100% to fill the window
  • Design for the default window size you’ll specify
Props:
  • Apps receive injectProps from the window (if specified)
  • Example: function MyApp({ message, title }) { ... }

Step 2: Add App Assets

Add icons and images to src/assets/windowsIcons/.

Icon Requirements

Two icon sizes:
  • 16x16 - For window header and taskbar
  • 32x32 - For desktop icons (optional, can reuse 16x16)
Format:
  • PNG with transparency preferred
  • Name convention: appname(16x16).png and appname(32x32).png

Example: Calculator Icons

Place these files:
  • src/assets/windowsIcons/calc(16x16).png
  • src/assets/windowsIcons/calc(32x32).png

Step 3: Register in appSettings

Add your app to the appSettings object in src/WinXP/apps/index.jsx.

Import Your Component and Icons

At the top of src/WinXP/apps/index.jsx:
import Calculator from './Calculator';
import calcIcon from 'assets/windowsIcons/calc(16x16).png';
import calcIconLarge from 'assets/windowsIcons/calc(32x32).png';

Add to appSettings Object

Add an entry to the appSettings export (src/WinXP/apps/index.jsx:234):
export const appSettings = {
  // ... existing apps ...
  
  Calculator: {
    name: 'Calculator',
    header: {
      icon: calcIcon,
      title: 'Calculator',
    },
    component: Calculator,
    defaultSize: {
      width: 300,
      height: 400,
    },
    defaultOffset: getCenter(300, 400),
    resizable: false,
    minimized: false,
    maximized: shouldMaximize(300, 400, false),
    multiInstance: true,
  },
};

appSettings Configuration Options

PropertyTypeRequiredDescription
namestringYesApp name identifier
header.iconstringYesPath to 16x16 icon
header.titlestringYesWindow title text
header.buttonsstring[]NoButton visibility (e.g., ['close'] for close only)
header.noFooterWindowbooleanNoIf true, hides from taskbar
header.invisiblebooleanNoIf true, hides window chrome
componentReact.ComponentYesThe app component
defaultSize.widthnumberYesInitial window width in pixels
defaultSize.heightnumberYesInitial window height in pixels
defaultOffset.xnumberYesInitial X position
defaultOffset.ynumberYesInitial Y position
resizablebooleanYesCan window be resized?
minimizedbooleanYesStart minimized? (usually false)
maximizedbooleanYesStart maximized?
multiInstancebooleanYesAllow multiple windows?
minWidthnumberNoMinimum window width
minHeightnumberNoMinimum window height
injectPropsobjectNoProps to pass to component

Helper Functions

getCenter(width, height) Returns { x, y } to center the window:
defaultOffset: getCenter(300, 400)
Implementation: src/WinXP/apps/index.jsx:65 shouldMaximize(width, height, isResizable) Returns true if window should start maximized (e.g., on mobile or if window is too large for screen):
maximized: shouldMaximize(300, 400, false)
Implementation: src/WinXP/apps/index.jsx:75

multiInstance vs Single Instance

multiInstance: true (multiple windows allowed):
  • Each open creates a new window
  • Examples: Notepad, Internet Explorer, Error dialogs
multiInstance: false (single window only):
  • Opening again focuses existing window
  • Examples: My Computer, Winamp, Paint

injectProps Example

Pass props to your app component:
Calculator: {
  name: 'Calculator',
  component: Calculator,
  injectProps: {
    mode: 'scientific',
    precision: 10,
  },
  // ... other config ...
}
Your component receives these as props:
function Calculator({ mode, precision }) {
  // Use mode and precision
}

Step 4: Add to Desktop Icons

To add your app to the desktop, add an entry to defaultIconState in src/WinXP/apps/index.jsx:135:
export const defaultIconState = [
  // ... existing icons ...
  
  {
    id: genId(),
    icon: calcIconLarge,
    title: 'Calculator',
    component: Calculator,
    isFocus: false,
    appName: 'Calculator',
  },
];

Desktop Icon Properties

PropertyTypeDescription
idnumberUnique ID (use genId())
iconstringPath to 32x32 icon
titlestringLabel shown under icon
componentReact.ComponentThe app component
isFocusbooleanInitially focused? (always false)
appNamestringApp name (matches appSettings key)

Step 5: Add to Start Menu

To add your app to the start menu, modify the onClickMenuItem function in src/WinXP/index.jsx:138:
function onClickMenuItem(itemName) {
  switch (itemName) {
    // ... existing cases ...
    
    case 'Calculator':
      dispatch({ type: ADD_APP, payload: appSettings.Calculator });
      break;
    
    // ... rest of cases ...
  }
}
Then add the menu item to the Footer component’s start menu configuration (in src/WinXP/Footer/). Note: The exact implementation of start menu items depends on the Footer component structure. Refer to existing menu items for the pattern.

Complete Example: Adding Calculator

Here’s a complete example of all files to modify:

1. Create component

src/WinXP/apps/Calculator/index.jsx (see Step 1 above)

2. Add icons

  • src/assets/windowsIcons/calc(16x16).png
  • src/assets/windowsIcons/calc(32x32).png

3. Update src/WinXP/apps/index.jsx

// Add imports at top
import Calculator from './Calculator';
import calcIcon from 'assets/windowsIcons/calc(16x16).png';
import calcIconLarge from 'assets/windowsIcons/calc(32x32).png';

// Add to defaultIconState
export const defaultIconState = [
  // ... existing icons ...
  {
    id: genId(),
    icon: calcIconLarge,
    title: 'Calculator',
    component: Calculator,
    isFocus: false,
    appName: 'Calculator',
  },
];

// Add to appSettings
export const appSettings = {
  // ... existing apps ...
  Calculator: {
    name: 'Calculator',
    header: { icon: calcIcon, title: 'Calculator' },
    component: Calculator,
    defaultSize: { width: 300, height: 400 },
    defaultOffset: getCenter(300, 400),
    resizable: false,
    minimized: false,
    maximized: shouldMaximize(300, 400, false),
    multiInstance: true,
  },
};

// Add to exports at bottom
export {
  // ... existing exports ...
  Calculator,
};

4. Update src/WinXP/index.jsx

function onClickMenuItem(itemName) {
  switch (itemName) {
    // ... existing cases ...
    
    case 'Calculator':
      dispatch({ type: ADD_APP, payload: appSettings.Calculator });
      break;
    
    // ... rest of switch ...
  }
}

5. Add to start menu

Modify the Footer component to include “Calculator” in the appropriate submenu.

Advanced: Conditional App Loading

Some apps should show error messages on certain devices or screen sizes:

Wrapper Component Pattern

See src/WinXP/apps/index.jsx:85 for examples:
const WrappedMinesweeper = props => {
  if (checkMinesweeperBlock()) {
    return (
      <ErrorBox
        {...props}
        message="Mobile Device Detected: Minesweeper is designed for desktop mouse interaction."
        title="Compatibility Warning"
      />
    );
  }
  return <MinesweeperComponent {...props} />;
};
Then use the wrapper in appSettings:
Minesweeper: {
  component: WrappedMinesweeper,
  // ... rest of config ...
}

Detection Functions

Mobile detection:
const isMobileUA = () => {
  if (typeof window === 'undefined') return false;
  const userAgent = navigator.userAgent || navigator.vendor || window.opera;
  return (
    /android/i.test(userAgent) ||
    (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream)
  );
};
Implementation: src/WinXP/apps/index.jsx:41 Screen size detection:
const isScreenTooSmall = (minW, minH) => {
  if (typeof window === 'undefined') return false;
  return window.innerWidth < minW || window.innerHeight < minH;
};
Implementation: src/WinXP/apps/index.jsx:50

Window Configuration Best Practices

Resizable Apps

For apps with flexible content (browsers, file explorers, text editors):
resizable: true,
maximized: shouldMaximize(660, 500, true),

Fixed-Size Apps

For apps with fixed layouts (games, calculators):
resizable: false,
maximized: shouldMaximize(300, 400, false),

Constrained Resizing

For apps that can resize but have minimum sizes:
resizable: true,
minWidth: 400,
minHeight: 350,
maximized: false,

Chromeless Windows

For apps that manage their own window chrome (Winamp):
header: {
  icon: winampIcon,
  title: 'Winamp',
  invisible: true,  // Hide window chrome
},
resizable: false,

Testing Your App

After adding your app:
  1. Test opening from desktop icon:
    • Double-click icon
    • Window appears with correct size and position
  2. Test opening from start menu:
    • Click Start
    • Navigate to your app
    • Window appears
  3. Test window operations:
    • Drag window
    • Resize (if resizable)
    • Minimize
    • Maximize
    • Close
    • Focus (click on window)
  4. Test multi-instance:
    • If multiInstance: true: multiple windows should open
    • If multiInstance: false: should focus existing window
  5. Test taskbar:
    • App appears in taskbar
    • Clicking taskbar button minimizes/restores
    • Icon and title are correct
  6. Test mobile (if applicable):
    • Open DevTools
    • Toggle device toolbar
    • Verify mobile warnings appear if expected

Troubleshooting

App doesn’t open

  • Check console for errors
  • Verify component is exported from src/WinXP/apps/index.jsx
  • Verify appSettings entry is correct
  • Check onClickMenuItem has your case

Icon doesn’t appear on desktop

  • Verify icon path is correct
  • Check defaultIconState has your entry
  • Verify genId() is called for id

Window size is wrong

  • Check defaultSize in appSettings
  • Use getCenter() for defaultOffset
  • Consider using shouldMaximize() for maximized

Can’t resize window

  • Set resizable: true in appSettings
  • Optionally add minWidth and minHeight

Multiple windows open when they shouldn’t

  • Set multiInstance: false in appSettings

App doesn’t appear in taskbar

  • Remove or set header.noFooterWindow: false
  • Verify app is in state.apps (check with React DevTools)

Examples from Existing Apps

Refer to these existing apps for examples:
  • Simple app: src/WinXP/apps/ErrorBox/ - Basic dialog
  • Complex app: src/WinXP/apps/MyComputer/ - Navigation and state
  • Game: src/WinXP/apps/Minesweeper/ - Game logic and UI
  • Embedded iframe: src/WinXP/apps/InternetExplorer/ - Iframe wrapper
  • Custom chrome: src/WinXP/apps/Winamp/ - No window header

Next Steps

After adding your app:
  1. Test thoroughly (see Testing section)
  2. Run linter: npm run lint
  3. Build project: npm run build
  4. Submit a pull request (see Contributing)

Build docs developers (and LLMs) love