ryOS ryOS / Docs
GitHub Launch

Component Architecture

ryOS uses a layered component architecture with theme-aware rendering, organized into layout, UI, dialog, shared, and error-isolation component categories.

Architecture Overview

graph TD
    subgraph Layer4["Layer 4: Application"]
        D1[Dialog Components]
        D2[Shared Components]
    end
    
    subgraph Layer3["Layer 3: Layout"]
        C1[WindowFrame]
        C2[MenuBar]
        C3[Desktop]
        C4[Dock]
        C5[Taskbar]
    end
    
    subgraph Layer2["Layer 2: Custom UI"]
        B1[audio-bars]
        B2[dial]
        B3[volume-bar]
        B4[playback-bars]
        B5[ThemedIcon]
        B6[TrafficLightButton]
    end
    
    subgraph Layer1["Layer 1: Base (shadcn)"]
        A1[button]
        A2[dialog]
        A3[dropdown-menu]
        A4[select]
        A5[slider]
        A6[tabs]
    end
    
    A1 --> B1 & B2 & B3
    B1 & B2 --> C1 & C2
    C1 & C2 --> D1 & D2

Component Directory Structure

src/components/
├── layout/           # Desktop environment structure
│   ├── Desktop.tsx
│   ├── Dock.tsx
│   ├── MenuBar.tsx
│   ├── WindowFrame.tsx
│   ├── AppleMenu.tsx
│   ├── StartMenu.tsx
│   ├── ExposeView.tsx
│   ├── SpotlightSearch.tsx
│   ├── AppSwitcher.tsx
│   └── AppMenu.tsx
├── ui/               # Base UI components
│   ├── button.tsx
│   ├── dialog.tsx
│   ├── dropdown-menu.tsx
│   ├── select.tsx
│   ├── slider.tsx
│   ├── switch.tsx
│   ├── tabs.tsx
│   ├── audio-bars.tsx
│   ├── dial.tsx
│   ├── volume-bar.tsx
│   └── ...
├── dialogs/          # Modal dialogs
│   ├── AboutDialog.tsx
│   ├── ConfirmDialog.tsx
│   ├── HelpDialog.tsx
│   └── ...
├── errors/           # Error boundary components
│   └── ErrorBoundaries.tsx
└── shared/           # Cross-app utilities
    ├── ThemedIcon.tsx
    ├── TrafficLightButton.tsx
    ├── ThemedTabs.tsx
    ├── EmojiAquarium.tsx
    ├── FullscreenPlayerControls.tsx
    ├── LinkPreview.tsx
    ├── ImageAttachment.tsx
    ├── LyricsSyncMode.tsx
    └── ...
AppManager is part of the application shell (src/apps/base/AppManager.tsx), where windows are rendered and wrapped with per-instance AppErrorBoundary.

Layout Components

Desktop (Desktop.tsx)

The root desktop surface handling wallpaper, icons, and drag-drop.

interface DesktopProps {
  apps: AnyApp[];
  toggleApp: (
    appId: AppId,
    initialData?: unknown,
    launchOrigin?: { x: number; y: number; width: number; height: number }
  ) => void;
  onClick?: () => void;
  desktopStyles?: DesktopStyles;
}
Features:
  • Theme-conditional icon layout (XP: left-aligned, macOS: right-aligned)
  • Video wallpaper support with visibility handling
  • Desktop shortcut management via useFilesStore
  • Drag-and-drop alias creation
  • Long-press context menu for mobile

WindowFrame (WindowFrame.tsx)

Window chrome with theme-specific rendering.

interface WindowFrameProps {
  children: React.ReactNode;
  title: string;
  appId: AppId;
  material?: "default" | "transparent" | "notitlebar" | "brushedmetal";
  windowConstraints?: WindowConstraints;
  instanceId?: string;
  menuBar?: React.ReactNode;
  keepMountedWhenMinimized?: boolean;
  onFullscreenToggle?: () => void;
  disableTitlebarAutoHide?: boolean;
  titleBarRightContent?: React.ReactNode;
}
Theme-Specific Rendering:
ThemeTitle BarControlsPosition
macOS XPinstripe gradientTraffic lightsLeft
System 7Dotted patternClose boxLeft
XPBlue gradientMin/Max/CloseRight
Win983D beveledMin/Max/CloseRight

MenuBar (MenuBar.tsx)

Top menu bar (Mac) or taskbar integration.

Mac Themes:
  • Apple menu with logo
  • Application menu
  • Status area (clock, volume, battery)
Windows Themes:
  • Renders as part of taskbar
  • Start menu integration

Dock (Dock.tsx)

macOS-style dock with magnification.

Features:
  • Icon magnification on hover
  • Running app indicators
  • Pinned items management
  • Drag-to-reorder
  • Context menus

Component Hierarchy

graph TD
    AM[AppManager] --> MB[MenuBar]
    AM --> DT[Desktop]
    AM --> WF[WindowFrame *]
    AM --> DK[Dock]
    AM --> EV[ExposeView]
    
    MB --> APM[AppleMenu]
    MB --> STM[StartMenu]
    MB --> VC[VolumeControl]
    MB --> CLK[Clock]
    
    DT --> FI[FileIcon *]
    DT --> RCM[RightClickMenu]
    DT --> VW[Video Wallpaper]
    
    WF --> TLB[TrafficLightButton]
    WF --> TI[ThemedIcon]
    WF --> APP[App Content]

UI Components

shadcn-based Components

Standard UI primitives from shadcn/ui:

ComponentBasePurpose
buttonCVA variantsButtons with theme variants
dialogRadix DialogModal dialogs
dropdown-menuRadix DropdownMenuContext/dropdown menus
selectRadix SelectSelection dropdowns
sliderRadix SliderValue sliders
switchRadix SwitchToggle switches
tabsRadix TabsTabbed interfaces
checkboxRadix CheckboxCheckboxes
tooltipRadix TooltipHover tooltips
scroll-areaRadix ScrollAreaCustom scrollbars
inputNativeText input fields
textareaNativeMultiline text input
labelRadix LabelForm labels
badgeCVA variantsStatus badges
cardNativeCard containers
tableNativeData tables
alertCVA variantsAlert messages

Custom Components

ryOS-specific UI components:

audio-bars

Real-time audio visualization bars.

interface AudioBarsProps {
  isPlaying: boolean;
  barCount?: number;
  color?: string;
  height?: number;
}

dial

Rotary knob control for audio parameters.

interface DialProps {
  value: number;
  min: number;
  max: number;
  step?: number;
  onChange: (value: number) => void;
  size?: "sm" | "md" | "lg";
  color?: string;
  label?: string;
  showValue?: boolean;
}
Features:
  • Horizontal drag-to-adjust
  • Touch and mouse support
  • Conic gradient fill
  • Size variants

volume-bar

Volume level indicator with animation.

interface VolumeBarProps {
  level: number;
  maxLevel?: number;
  barCount?: number;
  activeColor?: string;
  inactiveColor?: string;
}

playback-bars

Animated equalizer-style bars.

interface PlaybackBarsProps {
  isPlaying: boolean;
  variant?: "small" | "medium" | "large";
}

Theme-Aware Button Pattern

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, ...props }, ref) => {
    const currentTheme = useThemeStore((state) => state.current);
    const isXpTheme = currentTheme === "xp" || currentTheme === "win98";
    const isMacTheme = currentTheme === "macosx";

    // macOS Aqua styling
    if (isMacTheme && variant === "default") {
      return <Comp className={cn("aqua-button primary", className)} {...props} />;
    }
    
    // Windows styling
    if (isXpTheme && variant === "default") {
      return <Comp className={cn("button", className)} {...props} />;
    }
    
    // Fallback
    return <Comp className={cn(buttonVariants({ variant }), className)} {...props} />;
  }
);

Dialog Components

Dialog Inventory

DialogPurposeHas Sound
AboutDialogApp informationNo
AboutFinderDialogSystem infoYes
BootScreenBoot animationYes
ConfirmDialogConfirmationsYes (Alert)
EmojiDialogEmoji pickerNo
HelpDialogApp help contentNo
InputDialogText inputNo
LoginDialogAuthenticationNo
ShareItemDialogSharing optionsNo
SongSearchDialogSong searchNo
FutureSettingsDialogUpcoming features teaserNo
LogoutDialogLogout confirmationNo
LyricsSearchDialogLyrics searchNo
TelegramLinkDialogTelegram account linkingNo

Dialog Pattern

graph TD
    A[Dialog Component] --> B[Dialog Root]
    B --> C[DialogContent]
    C --> D[DialogHeader]
    C --> E[Content Area]
    C --> F[DialogFooter]
    
    D --> D1[Theme Title Bar]
    D1 --> D1a[macOS Traffic Lights]
    D1 --> D1b[Windows Controls]
    
    F --> F1[Cancel Button]
    F --> F2[Confirm Button]

ConfirmDialog Example

export function ConfirmDialog({
  isOpen,
  onOpenChange,
  onConfirm,
  title,
  description,
}: ConfirmDialogProps) {
  const { play: playAlertSound } = useSound(Sounds.ALERT_SOSUMI);
  const currentTheme = useThemeStore((state) => state.current);
  
  // Play alert sound on open
  useEffect(() => {
    if (isOpen) {
      playAlertSound();
    }
  }, [isOpen, playAlertSound]);

  return (
    <Dialog open={isOpen} onOpenChange={onOpenChange}>
      <DialogContent>
        <DialogHeader>{title}</DialogHeader>
        <div className="p-4">{description}</div>
        <DialogFooter>
          <Button variant="outline" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button onClick={onConfirm}>Confirm</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Shared Components

ThemedIcon

Theme-aware icon rendering with fallback support.

interface ThemedIconProps extends ImgHTMLAttributes<HTMLImageElement> {
  name: string;
  alt?: string;
  themeOverride?: OsThemeId;
}

// Usage
<ThemedIcon name="folder.png" alt="Folder" />
Features:
  • Automatic theme path resolution
  • Fallback to default icons
  • Safari image stabilizer for themed variants
  • Async theme path loading
Resolution Logic:
1. Check /icons/{theme}/{name}
2. If not in manifest, use /icons/default/{name}

TrafficLightButton

macOS window control buttons.

interface TrafficLightButtonProps {
  color: "red" | "yellow" | "green";
  onClick: () => void;
  isForeground?: boolean;
  ariaLabel?: string;
}
Features:
  • Gradient fills matching Aqua design
  • Inactive state styling
  • Enlarged clickable area
  • Proper accessibility labels

EmojiAquarium

Decorative animated emoji display.

interface EmojiAquariumProps {
  emojis: string[];
  containerRef: RefObject<HTMLDivElement>;
  speed?: number;
}

LinkPreview

URL preview card with OpenGraph data.

interface LinkPreviewProps {
  url: string;
  onLoad?: (data: OGData) => void;
  compact?: boolean;
}

Error Boundary Components

src/components/errors/ErrorBoundaries.tsx provides two shell-level resilience primitives:
  • DesktopErrorBoundary: wraps the desktop shell in App.tsx and offers a desktop reload recovery action.
  • AppErrorBoundary: wraps each rendered app instance in AppManager, presenting a themed crash dialog with Relaunch and Quit buttons so the user can recover or dismiss a crashed window without taking down other windows. Crash details are reported via reportRuntimeCrash.

Theme Integration Pattern

Standard Pattern

function MyComponent() {
  const currentTheme = useThemeStore((state) => state.current);
  const isXpTheme = currentTheme === "xp" || currentTheme === "win98";
  const isMacTheme = currentTheme === "macosx";

  return (
    <div className={cn(
      "base-styles",
      isMacTheme && "mac-specific-styles",
      isXpTheme && "windows-specific-styles",
    )}>
      {/* content */}
    </div>
  );
}

Theme Metadata Usage

import { getThemeMetadata } from "@/themes";

function ResponsiveLayout() {
  const theme = useThemeStore((s) => s.current);
  const metadata = getThemeMetadata(theme);
  
  return (
    <div style={{
      paddingTop: metadata.menuBarHeight,
      paddingBottom: metadata.hasDock ? metadata.baseDockHeight : metadata.taskbarHeight,
    }}>
      {/* content */}
    </div>
  );
}

Sound Integration Pattern

Most interactive components integrate with useSound:

import { useSound, Sounds } from "@/hooks/useSound";

function InteractiveButton({ onClick, children }) {
  const { play: playClick } = useSound(Sounds.BUTTON_CLICK);
  
  const handleClick = (e) => {
    playClick();
    onClick?.(e);
  };
  
  return <button onClick={handleClick}>{children}</button>;
}

Composition Patterns

WindowFrame + App Content

<WindowFrame
  appId="ipod"
  title="iPod"
  material="transparent"
  menuBar={<IpodMenuBar />}
  keepMountedWhenMinimized={true}
  windowConstraints={{
    minWidth: 280,
    minHeight: 400,
  }}
>
  <IpodAppContent />
</WindowFrame>

Dialog with Theme-Aware Content

<Dialog open={isOpen} onOpenChange={onOpenChange}>
  <DialogContent className="max-w-[400px]">
    <DialogHeader>
      {title}
    </DialogHeader>
    <div className={isXpTheme ? "window-body" : "p-6"}>
      {content}
    </div>
    <DialogFooter>
      <Button variant={isMacTheme ? "default" : "retro"}>
        Confirm
      </Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

Component Relationships

graph TB
    subgraph "Application Layer"
        AM[AppManager]
    end
    
    subgraph "Layout Layer"
        MB[MenuBar]
        DT[Desktop]
        WF[WindowFrame]
        DK[Dock]
    end
    
    subgraph "UI Layer"
        BTN[Button]
        DLG[Dialog]
        RCM[RightClickMenu]
    end
    
    subgraph "Shared Layer"
        TI[ThemedIcon]
        TLB[TrafficLightButton]
    end
    
    subgraph "State Layer"
        TS[useThemeStore]
        AS[useAppStore]
    end
    
    AM --> MB
    AM --> DT
    AM --> WF
    AM --> DK
    
    MB --> TI
    DK --> TI
    WF --> TLB
    DLG --> TLB
    
    MB --> TS
    DT --> TS
    WF --> TS
    DK --> AS

Best Practices

  1. Always use useThemeStore for theme-conditional rendering
  1. Use ThemedIcon for all icon rendering (handles theme paths)
  2. Integrate useSound for interactive feedback
  3. Use cn() utility for conditional classNames
  4. Use shadcn components from @/components/ui/ as base primitives
  5. Support touch and mouse for custom interactive components
  6. Provide accessibility labels for interactive elements

Related Documentation