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:
| Theme | Title Bar | Controls | Position |
|---|---|---|---|
| macOS X | Pinstripe gradient | Traffic lights | Left |
| System 7 | Dotted pattern | Close box | Left |
| XP | Blue gradient | Min/Max/Close | Right |
| Win98 | 3D beveled | Min/Max/Close | Right |
MenuBar (MenuBar.tsx)
Top menu bar (Mac) or taskbar integration.
Mac Themes:- Apple menu with logo
- Application menu
- Status area (clock, volume, battery)
- 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:
| Component | Base | Purpose |
|---|---|---|
button | CVA variants | Buttons with theme variants |
dialog | Radix Dialog | Modal dialogs |
dropdown-menu | Radix DropdownMenu | Context/dropdown menus |
select | Radix Select | Selection dropdowns |
slider | Radix Slider | Value sliders |
switch | Radix Switch | Toggle switches |
tabs | Radix Tabs | Tabbed interfaces |
checkbox | Radix Checkbox | Checkboxes |
tooltip | Radix Tooltip | Hover tooltips |
scroll-area | Radix ScrollArea | Custom scrollbars |
input | Native | Text input fields |
textarea | Native | Multiline text input |
label | Radix Label | Form labels |
badge | CVA variants | Status badges |
card | Native | Card containers |
table | Native | Data tables |
alert | CVA variants | Alert 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
| Dialog | Purpose | Has Sound |
|---|---|---|
AboutDialog | App information | No |
AboutFinderDialog | System info | Yes |
BootScreen | Boot animation | Yes |
ConfirmDialog | Confirmations | Yes (Alert) |
EmojiDialog | Emoji picker | No |
HelpDialog | App help content | No |
InputDialog | Text input | No |
LoginDialog | Authentication | No |
ShareItemDialog | Sharing options | No |
SongSearchDialog | Song search | No |
FutureSettingsDialog | Upcoming features teaser | No |
LogoutDialog | Logout confirmation | No |
LyricsSearchDialog | Lyrics search | No |
TelegramLinkDialog | Telegram account linking | No |
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
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 inApp.tsxand offers a desktop reload recovery action.AppErrorBoundary: wraps each rendered app instance inAppManager, 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 viareportRuntimeCrash.
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
- Always use
useThemeStorefor theme-conditional rendering
- Use
ThemedIconfor all icon rendering (handles theme paths) - Integrate
useSoundfor interactive feedback - Use
cn()utility for conditional classNames - Use shadcn components from
@/components/ui/as base primitives - Support touch and mouse for custom interactive components
- Provide accessibility labels for interactive elements
Related Documentation
- Theme System - Theme definitions and CSS variables
- Window Management - WindowFrame details
- UI Components - Full component reference
- Component Library - Detailed component API