Skip to main content

Architecture Overview

Time Capsule is a Progressive Web App that recreates the 1990s Unix CDE (Common Desktop Environment) desktop experience. The architecture follows a modular, event-driven design pattern built on modern web technologies.

System Design

The application follows a layered architecture with clear separation of concerns:

Core Principles

1. Modular Architecture

Each core system is implemented as an independent module with a well-defined interface. This promotes code reusability and maintainability.
// src/scripts/core/config.ts
export interface Config {
  WINDOW: WindowConfig;
  AUDIO: AudioConfig;
  FS: FSConfig;
  TERMINAL: TerminalConfig;
  // ... more configuration sections
}

export const CONFIG: Config = {
  WINDOW: {
    MIN_VISIBLE: 20,
    BASE_Z_INDEX: 10000,
    TOP_BAR_HEIGHT: 30,
  },
  AUDIO: {
    BEEP_FREQUENCY: 880,
    BEEP_GAIN: 0.9,
    BEEP_DURATION: 0.1,
  },
  // ... configuration values
};

2. Event-Driven Communication

Components communicate through custom events dispatched on the window object, promoting loose coupling:
// Notify system of filesystem changes
function dispatchChange(path: string): void {
  window.dispatchEvent(
    new CustomEvent('cde-fs-change', {
      detail: { path },
    })
  );
}

3. Singleton Pattern for Core Systems

Core systems use the singleton pattern to ensure single instances throughout the application lifecycle:
class SettingsManager {
  private static instance: SettingsManager;
  private settings: SystemSettings;

  private constructor() {
    this.settings = this.getDefaultSettings();
    this.load();
  }

  public static getInstance(): SettingsManager {
    if (!SettingsManager.instance) {
      SettingsManager.instance = new SettingsManager();
    }
    return SettingsManager.instance;
  }
}

export const settingsManager = SettingsManager.getInstance();

4. Progressive Enhancement

The application provides a functional experience even with limited JavaScript, with enhanced features loading progressively.

Core Components

WindowManager

Handles all window lifecycle operations including creation, positioning, focus management, and z-index ordering. Key Responsibilities:
  • Window registration and lifecycle management
  • Drag and drop functionality
  • Z-index management (base: 10000 for windows, 90000 for modals)
  • Focus management with click and point-to-focus modes
  • Window state persistence (position, maximized state)
  • Workspace management (4 virtual desktops)
  • Mobile-specific centering and constraints
See Window Manager for detailed documentation.

Virtual Filesystem (VFS)

Provides a POSIX-like filesystem abstraction in browser memory with O(1) path lookups. Key Features:
  • Hierarchical folder structure
  • File metadata (size, mtime, owner, permissions)
  • CRUD operations (touch, mkdir, rm, rename, move, copy)
  • Trash functionality
  • Search with recursive option
  • Event notifications on changes
See Virtual Filesystem for detailed documentation.

SettingsManager

Centralized configuration management with persistent storage. Features:
  • Unified settings storage
  • Version-aware cache management
  • Migration from legacy localStorage keys
  • Section-based organization (theme, mouse, keyboard, beep, session, desktop)
  • Window session persistence
See Storage Management for detailed documentation.

AudioManager

Retro audio system using Web Audio API for classic CDE system sounds. Capabilities:
  • Lazy AudioContext initialization (on first user gesture)
  • System beeps with configurable frequency/duration
  • UI feedback sounds (click, error, success)
  • Window operation sounds (open, close, minimize, maximize, shade)
  • Melody playback for startup chimes
  • Volume control
export interface IAudioManager {
  beep(frequency?: number, duration?: number): void;
  click(): void;
  error(): void;
  success(): void;
  windowOpen(): void;
  windowClose(): void;
  windowMinimize(): void;
  windowMaximize(): void;
  windowShade(): void;
  menuOpen(): void;
  menuClose(): void;
  notification(): void;
  setVolume(volume: number): void;
  playMelody(notes: Array<{
    freq: number;
    duration: number;
    type?: OscillatorType;
    delay?: number
  }>): Promise<void>;
  playStartupChime(): void;
}

StyleManager

Orchestrates multiple style modules for system customization. Modules:
  • ThemeModule: Color palette management (CDE palettes)
  • FontModule: Font family and sizing controls
  • MouseModule: Cursor settings and acceleration
  • KeyboardModule: Keyboard repeat and shortcuts
  • BeepModule: Audio feedback settings
  • BackdropModule: Wallpaper/backdrop management with XPM parsing
  • WindowModule: Window behavior (focus mode, opaque dragging)
  • ScreenModule: Screen settings
  • StartupModule: Boot sequence customization
export class StyleManager {
  public theme: ThemeModule;
  public font: FontModule;
  public mouse: MouseModule;
  public keyboard: KeyboardModule;
  public beep: BeepModule;
  public backdrop: BackdropModule;
  public windowBehavior: WindowModule;
  public screen: ScreenModule;
  public startup: StartupModule;

  public init(): void {
    const themeSettings = settingsManager.getSection('theme');
    
    // Apply default palette if none saved
    if (!themeSettings.colors || 
        Object.keys(themeSettings.colors).length === 0) {
      this.theme.applyCdePalette('latesummer');
    } else {
      this.theme.loadSavedColors(themeSettings.colors);
    }
    
    // Initialize all modules
    this.mouse.load();
    this.keyboard.load();
    this.beep.load();
    this.backdrop.load();
    // ...
  }
}

Configuration System

Centralized configuration in config.ts provides type-safe access to all system constants:
export interface WindowConfig {
  MIN_VISIBLE: number;
  BASE_Z_INDEX: number;
  TOP_BAR_HEIGHT: number;
}

export interface FSConfig {
  HOME: string;
  DESKTOP: string;
  TRASH: string;
  NETWORK: string;
}

export interface TerminalConfig {
  HOME_PATH: string;
  MIN_TYPING_DELAY: number;
  MAX_TYPING_DELAY: number;
  POST_COMMAND_DELAY: number;
  POST_SEQUENCE_DELAY: number;
  MAX_LINES: number;
  CLEANUP_INTERVAL: number;
}

Data Flow Patterns

Window Creation Flow

Settings Persistence Flow

VFS Operation Flow

Performance Considerations

Z-Index Management

Z-index allocation uses separate counters for different layers:
let highestWindowZIndex = CONFIG.WINDOW.BASE_Z_INDEX; // 10000
let highestModalZIndex = 90000;

function getNextZIndex(isModal: boolean = false): number {
  if (isModal) {
    return ++highestModalZIndex;
  }
  return ++highestWindowZIndex;
}
This ensures modals always appear above regular windows, preventing z-index conflicts.

Memory-Optimized VFS

The VFS uses a flattened Map for O(1) path resolution instead of traversing a tree structure:
const fsMap: Record<string, VFSNode> = {};

function flatten(basePath: string, node: VFSNode): void {
  fsMap[basePath] = node;
  if (node.type === 'folder') {
    for (const [name, child] of Object.entries(node.children)) {
      const fullPath = basePath + name + 
        (child.type === 'folder' ? '/' : '');
      flatten(fullPath, child);
    }
  }
}

// O(1) lookup instead of tree traversal
getNode(path: string): VFSNode | null {
  return fsMap[path] || null;
}

Window Position Normalization

Window positions are normalized on viewport resize to ensure they remain accessible:
function normalizeWindowPosition(win: HTMLElement): void {
  const rect = win.getBoundingClientRect();
  const TOP_BAR_HEIGHT = CONFIG.WINDOW.TOP_BAR_HEIGHT;
  const viewportWidth = window.innerWidth;
  const viewportHeight = window.innerHeight;

  // Clamp within viewport bounds
  const minY = TOP_BAR_HEIGHT;
  const minX = 0;
  const maxX = Math.max(0, viewportWidth - rect.width);
  const maxY = Math.max(minY, viewportHeight - rect.height);

  let newTop = Math.max(rect.top, minY);
  newTop = Math.min(newTop, maxY);

  let newLeft = Math.max(rect.left, minX);
  newLeft = Math.min(newLeft, maxX);

  win.style.top = newTop + 'px';
  win.style.left = newLeft + 'px';
}

Error Handling

Graceful degradation is implemented throughout:
try {
  await storageAdapter.setItemSync(key, value);
} catch (e) {
  console.error('[SettingsManager] Failed to save:', e);
  // Continue operation, data will be lost on refresh
}

Type Safety

Strong TypeScript typing throughout the codebase:
export interface VFSMetadata {
  size: number;
  mtime: string; // ISO string
  owner: string;
  permissions: string;
}

export interface VFSFile {
  type: 'file';
  content: string;
  metadata?: VFSMetadata;
}

export interface VFSFolder {
  type: 'folder';
  children: Record<string, VFSNode>;
  metadata?: VFSMetadata;
}

export type VFSNode = VFSFile | VFSFolder;

Global Exposure

Core systems are exposed globally for debugging and feature access:
declare global {
  interface Window {
    WindowManager: typeof WindowManager;
    VirtualFS: IVFS;
    AudioManager: IAudioManager;
    styleManager: StyleManager;
    CONFIG: Config;
  }
}

if (typeof window !== 'undefined') {
  window.WindowManager = WindowManager;
  window.VirtualFS = VFS;
  window.AudioManager = AudioManager;
  window.CONFIG = CONFIG;
}

Next Steps