Hooks Architecture
ryOS provides 44 custom hooks organized by functionality, enabling audio playback, window management, media handling, undo/redo, realtime presence, background synchronization, and more.
Hook Categories Overview
graph TB
subgraph Audio["Audio Hooks"]
US[useSound]
UTT[useTtsQueue]
UCS[useChatSynth]
UAR[useAudioRecorder]
USB[useSoundboard]
UTS[useTerminalSounds]
UAT[useAudioTranscription]
UV[useVibration]
end
subgraph Window["App/Window Hooks"]
UWM[useWindowManager]
ULA[useLaunchApp]
UAS[useActivityState]
UAL[useActivityLabel]
UWI[useWindowInsets]
end
subgraph Media["Media Hooks"]
UL[useLyrics]
USC[useSongCover]
UCP[useCoverPalette]
UF[useFurigana]
ULE[useLyricsErrorToast]
end
subgraph UndoRedo["Undo/Redo Hooks"]
UUR[useUndoRedo]
UGUR[useGlobalUndoRedo]
end
subgraph Utility["Utility Hooks"]
UIM[useIsMobile]
UIP[useIsPhone]
UMQ[useMediaQuery]
UT[useToast]
UO[useOffline]
ULP[useLongPress]
USN[useSwipeNavigation]
UW[useWallpaper]
UCB[useCacheBustTrigger]
USS[useSpotlightSearch]
UBN[useBackgroundChatNotifications]
UAC[useAutoCloudSync]
ULS[useListenSync]
USF[useStreamingFetch]
UTH[useTranslatedHelpItems]
URCS[useRealtimeConnectionStatus]
UTL[useTelegramLink]
end
subgraph Effect["Effect Hooks"]
UEL[useEventListener]
ULR[useLatestRef]
UIT[useInterval]
UTO[useTimeout]
URO[useResizeObserver]
end
subgraph Auth["Auth Hooks"]
UA[useAuth]
end
Audio Hooks
useSound
Web Audio API-based playback for UI sounds with caching and volume control.
interface UseSoundReturn {
play: (options?: { volume?: number; fadeIn?: boolean }) => Promise<void>;
stop: () => void;
isPlaying: boolean;
}
// Usage
const { play: playClick } = useSound(Sounds.BUTTON_CLICK);
playClick({ volume: 0.8 });
Features:
- AudioBuffer caching with LRU eviction
- Load deduplication (prevents duplicate fetches)
- Concurrent source limiting (16 mobile, 32 desktop)
- Volume ramping to prevent clicks
- Context change detection (re-caches when AudioContext recreated)
| Category | Sounds |
|---|---|
| Window | WINDOW_OPEN, WINDOW_CLOSE, WINDOW_EXPAND, WINDOW_COLLAPSE |
| Interaction | BUTTON_CLICK, MENU_OPEN, MENU_CLOSE |
| Alerts | ALERT_SOSUMI, ALERT_BONK, ALERT_INDIGO |
| App-specific | PHOTO_SHUTTER, VIDEO_TAPE, IPOD_CLICK_WHEEL |
useTtsQueue
Gap-free text-to-speech with intelligent queuing and volume ducking.
interface UseTtsQueueReturn {
speak: (text: string, options?: TtsOptions) => Promise<void>;
stop: () => void;
isSpeaking: boolean;
queue: TtsQueueItem[];
}
// Usage
const { speak, stop, isSpeaking } = useTtsQueue();
await speak("Hello, world!", { provider: "openai", voice: "nova" });
Features:
- AudioContext timeline scheduling for gap-free playback
- Parallel fetching (up to 3 concurrent TTS requests)
- Volume ducking: iPod reduced to 35%, chat synth to 60% during speech
- Micro-fades (10ms) to prevent audio clicks
useChatSynth
Musical feedback for typing using Tone.js synthesis.
interface UseChatSynthReturn {
playNote: () => void;
setPreset: (preset: SynthPreset) => void;
currentPreset: SynthPreset;
}
// Usage
const { playNote, setPreset } = useChatSynth();
setPreset("ethereal");
Presets:
| Preset | Oscillator | Character |
|---|---|---|
| Classic | Triangle | Warm, balanced |
| Ethereal | Sine | Soft, dreamy |
| Digital | Square | Sharp, electronic |
| Retro | Sawtooth | Vintage, buzzy |
| Off | - | Disabled |
- Pentatonic scale (C4, D4, F4, G4, A4, C5, D5)
- Effects chain: Filter → Tremolo → Reverb → PolySynth
- 16 voice polyphony limit
- Global instance persists across HMR
useAudioRecorder
Microphone recording with format detection.
interface UseAudioRecorderReturn {
startRecording: () => Promise<void>;
stopRecording: () => Promise<string>; // Returns base64
isRecording: boolean;
error: string | null;
}
// Usage
const { startRecording, stopRecording, isRecording } = useAudioRecorder();
await startRecording();
const base64Audio = await stopRecording();
Features:
- MediaRecorder API with 200ms chunk intervals
- Format detection: WebM (Chrome/Firefox), MP4 (Safari)
- Base64 encoding for storage
- Proper stream cleanup
useAudioTranscription
Voice-to-text with silence detection.
interface UseAudioTranscriptionReturn {
startListening: () => Promise<void>;
stopListening: () => Promise<void>;
transcript: string;
isListening: boolean;
silenceLevel: number;
}
// Usage
const { startListening, transcript, isListening } = useAudioTranscription({
onTranscript: (text) => console.log(text),
silenceThreshold: 0.01,
silenceDuration: 2000,
});
Features:
- Adaptive silence detection
- Auto-stop on silence
- Volume level monitoring
- Whisper API integration
App/Window Hooks
useWindowManager
Window positioning, dragging, resizing, and snap-to-edge.
interface UseWindowManagerReturn {
windowPosition: { x: number; y: number };
windowSize: { width: number; height: number };
isDragging: boolean;
resizeType: ResizeType;
handleMouseDown: (e: MouseEvent) => void;
handleResizeStart: (e: MouseEvent, type: ResizeType) => void;
setWindowSize: (size: WindowSize) => void;
setWindowPosition: (pos: WindowPosition) => void;
maximizeWindowHeight: () => void;
getSafeAreaBottomInset: () => number;
snapZone: "left" | "right" | null;
computeInsets: () => WindowInsets;
}
// Usage
const { windowPosition, handleMouseDown, snapZone } = useWindowManager({
appId: "finder",
instanceId: "finder-1",
});
Features:
- Cascade positioning for new windows
- Snap-to-edge (50% screen width)
- Mobile-aware constraints
- Sound integration for drag/resize
useLaunchApp
App launching with instance management.
interface UseLaunchAppReturn {
launchApp: (appId: AppId, initialData?: unknown) => string;
launchOrFocusApp: (appId: AppId) => void;
closeApp: (instanceId: string) => void;
}
// Usage
const { launchApp, launchOrFocusApp } = useLaunchApp();
const instanceId = launchApp("textedit", { filePath: "/Documents/note.md" });
Features:
- Multi-instance support for eligible apps
- Restore minimized instances
- Initial data passing
- Instance ID generation
useWindowInsets
Theme-aware safe area calculation.
interface WindowInsets {
top: number; // Menu bar height
bottom: number; // Dock/taskbar height
left: number;
right: number;
}
// Usage
const insets = useWindowInsets();
const availableHeight = window.innerHeight - insets.top - insets.bottom;
Media Hooks
useLyrics
Lyrics fetching with translation support.
interface UseLyricsReturn {
lyrics: LyricLine[];
isLoading: boolean;
error: string | null;
hasLyrics: boolean;
translation: LyricTranslation | null;
refetch: () => void;
}
// Usage
const { lyrics, isLoading, translation } = useLyrics({
songId: "abc123",
title: "Song Title",
artist: "Artist Name",
enableTranslation: true,
});
Features:
- Multiple lyrics sources (Kugou, etc.)
- AI translation with streaming
- Furigana/soramimi prefetch
- Cache bust trigger support
useFurigana
Japanese text annotation (furigana and soramimi).
interface UseFuriganaReturn {
furiganaMap: Map<string, FuriganaSegment[]>;
soramimiMap: Map<string, SoramimiSegment[]>;
furiganaProgress: number;
soramimiProgress: number;
isLoadingFurigana: boolean;
isLoadingSoramimi: boolean;
}
// Usage
const { furiganaMap, soramimiMap, furiganaProgress } = useFurigana({
songId: "abc123",
lines: lyricsLines,
enabled: true,
});
Features:
- Progressive SSE streaming
- Ordered loading (furigana before soramimi for Japanese)
- Real-time progress updates
useSongCover
Album art fetching with caching.
interface UseSongCoverReturn {
coverUrl: string | null;
isLoading: boolean;
error: string | null;
}
// Usage
const { coverUrl, isLoading } = useSongCover({
title: "Song Title",
artist: "Artist Name",
fallbackUrl: "/default-cover.png",
});
Features:
- In-memory cache
- YouTube thumbnail fallback
- Error handling with fallback
Utility Hooks
useIsMobile / useIsPhone
Device detection hooks.
// useIsMobile: Touch OR small screen
const isMobile = useIsMobile(); // Default breakpoint: 768px
// useIsPhone: Touch AND small screen
const isPhone = useIsPhone(); // Default breakpoint: 640px
useOffline
Network status detection with polling.
interface UseOfflineReturn {
isOffline: boolean;
lastOnline: Date | null;
}
// Usage
const { isOffline } = useOffline();
if (isOffline) {
showOfflineIndicator();
}
Features:
navigator.onLinemonitoring- Periodic polling (5s intervals)
- Last online timestamp
useLongPress
Touch long-press detection.
interface UseLongPressReturn {
onTouchStart: (e: TouchEvent) => void;
onTouchEnd: () => void;
onTouchMove: () => void;
}
// Usage
const longPressHandlers = useLongPress({
onLongPress: () => showContextMenu(),
delay: 500, // ms
});
<div {...longPressHandlers} />
useSwipeNavigation
Horizontal swipe gesture detection.
interface UseSwipeNavigationReturn {
handlers: {
onTouchStart: (e: TouchEvent) => void;
onTouchMove: (e: TouchEvent) => void;
onTouchEnd: () => void;
};
isSwiping: boolean;
swipeDirection: "left" | "right" | null;
}
// Usage
const { handlers, swipeDirection } = useSwipeNavigation({
onSwipeLeft: () => nextApp(),
onSwipeRight: () => prevApp(),
threshold: 50,
});
useStreamingFetch
Generic SSE streaming with lifecycle management.
interface UseStreamingFetchReturn<T> {
data: T | null;
isLoading: boolean;
error: string | null;
progress: number;
}
// Usage
const { data, progress, isLoading } = useStreamingFetch<TranslationResult>({
url: "/api/translate",
body: { text, targetLang },
onProgress: (p) => setProgress(p),
onComplete: (result) => handleResult(result),
});
Features:
- Abort controller integration
- Stale request prevention
- Offline detection
- Progress tracking
useSpotlightSearch
Spotlight query orchestration with worker-backed indexing.
interface SpotlightSearchState {
results: SpotlightResult[];
isSearching: boolean;
}
// Usage
const { results, isSearching } = useSpotlightSearch(query);
Features:
- Uses
spotlightSearch.worker.tsandspotlightSearch.shared.tsfor worker-backed indexing - Posts index/query messages to the worker
- Defers initial indexing with
requestIdleCallbackfor responsiveness - Subscribes to files, music, sites, video, calendar, and contacts stores and refreshes index incrementally
- Ignores stale worker responses with request IDs
useAutoCloudSync
Realtime cloud sync orchestration via Pusher or local WebSocket. Subscribes to sync channels for 11 domains (settings, files-metadata, files-images, files-trash, files-applets, songs, videos, stickies, calendar, contacts, custom-wallpapers) and applies remote updates in real time. Syncs dock icons, iPod/Karaoke settings, Stickies, wallpapers, images, contacts, videos library, and calendar data. Used by App.tsx for authenticated users with auto-sync enabled.
useBackgroundChatNotifications / useListenSync
Background orchestration hooks used by the desktop shell:
useBackgroundChatNotificationsmonitors chat activity and system notifications while apps run in background.useListenSynckeeps Listen Session playback/session state synchronized across app components.
useCacheBustTrigger
Force refresh detection for shared state.
interface UseCacheBustTriggerReturn {
currentTrigger: number;
isForceRequest: boolean;
markHandled: () => void;
}
// Usage
const { isForceRequest, markHandled } = useCacheBustTrigger();
useEffect(() => {
if (isForceRequest) {
refetchData();
markHandled();
}
}, [isForceRequest]);
Auth Hook
useAuth
Complete authentication flow management.
interface UseAuthReturn {
username: string | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (username: string, password: string) => Promise<boolean>;
logout: () => Promise<void>;
verifyToken: () => Promise<boolean>;
showPasswordDialog: boolean;
setShowPasswordDialog: (show: boolean) => void;
}
// Usage
const { username, isAuthenticated, login, logout } = useAuth();
if (!isAuthenticated) {
await login("alice", "password123");
}
Undo/Redo Hooks
useUndoRedo
Provides useRegisterUndoRedo and useInstanceUndoRedo for wiring app-level undo/redo to the central useUndoRedoStore.
// In app component — register handlers for this instance
useRegisterUndoRedo(instanceId, {
undo: () => history.undo(),
redo: () => history.redo(),
canUndo: history.canUndo,
canRedo: history.canRedo,
});
// In menu bar — read undo/redo state for Edit menu items
const { canUndo, canRedo, undo, redo } = useInstanceUndoRedo(instanceId);
Features:
- Per-instance registration, automatically cleaned up on unmount
- State updates propagated reactively to menu bars
- Used by Finder, Paint, and TextEdit
useGlobalUndoRedo
Global keyboard listener dispatching Cmd/Ctrl+Z (undo) and Cmd/Ctrl+Shift+Z / Ctrl+Y (redo) to the foreground window's registered handlers. Mounted once at the top level in AppManager.
// In AppManager — mount once at the top level
useGlobalUndoRedo();
Realtime Hooks
useRealtimeConnectionStatus
Subscribes to Pusher connection state changes and returns "connected", "connecting", or "disconnected".
const connectionState = useRealtimeConnectionStatus();
// "connected" | "connecting" | "disconnected"
useTelegramLink
Manages Telegram account linking flow including link creation, deep-link opening, code copying, and disconnection.
const {
telegramLinkedAccount,
telegramLinkSession,
handleCreateTelegramLink,
handleDisconnectTelegramLink,
} = useTelegramLink({ username, authToken });
Effect Hooks
useEventListener
Type-safe event listener management with automatic cleanup.
// Window event
useEventListener("resize", handleResize);
// Document event
useEventListener("visibilitychange", handleVisibility, document);
// Element event via ref
const buttonRef = useRef<HTMLButtonElement>(null);
useEventListener("click", handleClick, buttonRef);
Features:
- Automatic cleanup on unmount
- Ref-based handler to avoid stale closures
- Supports window, document, and element targets
- TypeScript-safe event types
Also exports useCustomEventListener for custom events with typed payloads.
useLatestRef
Keeps a ref updated with the latest value without triggering re-renders.
const [count, setCount] = useState(0);
const countRef = useLatestRef(count);
// In callbacks - always gets current value
const handleClick = useCallback(() => {
console.log('Current count:', countRef.current);
}, []); // No need to include count in deps
Use cases:
- Avoiding stale closures in callbacks
- Event handlers that need current state
- Animation frame callbacks
Also exports usePrevious for tracking previous render values.
useInterval
Declarative interval management with automatic cleanup.
// Basic usage - runs every second
useInterval(() => setCount(c => c + 1), 1000);
// Conditional interval - pauses when delay is null
useInterval(() => tick(), isRunning ? 1000 : null);
// Immediate execution on mount
useInterval(() => fetchData(), 5000, { immediate: true });
useTimeout
Declarative timeout with manual control.
// Basic usage
useTimeout(() => setVisible(false), 3000);
// Conditional timeout
useTimeout(() => hideMessage(), showMessage ? 5000 : null);
// Manual control
const { clear, reset } = useTimeout(() => autoSave(), 10000);
// Later: clear() to cancel, reset() to restart
useResizeObserver
Element size observation with automatic cleanup.
// Basic usage - returns ref to attach
const ref = useResizeObserver<HTMLDivElement>((entry) => {
setWidth(entry.contentRect.width);
});
return <div ref={ref}>...</div>;
// With debouncing
const ref = useResizeObserver<HTMLDivElement>(
(entry) => setDimensions(entry.contentRect),
{ debounce: 100 }
);
Also exports:
useResizeObserverWithRef- for existing refsuseElementSize- simple hook returning[ref, { width, height }]
Common Patterns
Global AudioContext Management
Audio hooks share a global context:
import { getAudioContext, resumeAudioContext } from "@/lib/audioContext";
// Always resume before playback
await resumeAudioContext();
const ctx = getAudioContext();
Store Integration
Hooks use fine-grained Zustand selectors:
const speechVolume = useAudioSettingsStore((s) => s.speechVolume);
const masterVolume = useAudioSettingsStore((s) => s.masterVolume);
Stale Request Prevention
const currentIdRef = useRef(id);
useEffect(() => {
currentIdRef.current = id;
const controller = new AbortController();
fetchData(id, controller.signal).then((data) => {
// Check if still current
if (id !== currentIdRef.current) return;
setData(data);
});
return () => controller.abort();
}, [id]);
Volume Ramping
const targetVolume = volume * uiVolume * masterVolume;
const now = audioContext.currentTime;
gainNode.gain.setValueAtTime(gainNode.gain.value, now);
gainNode.gain.linearRampToValueAtTime(targetVolume, now + 0.01);
Hook Dependencies
graph TD
subgraph "Stores"
AS[useAudioSettingsStore]
IS[useIpodStore]
CS[useChatsStore]
APS[useAppStore]
URS[useUndoRedoStore]
end
subgraph "Audio Hooks"
US[useSound] --> AS
UTT[useTtsQueue] --> AS
UTT --> IS
UCS[useChatSynth] --> AS
UCS --> UV[useVibration]
end
subgraph "Media Hooks"
UL[useLyrics] --> IS
UL --> UCB[useCacheBustTrigger]
UF[useFurigana] --> UCB
end
subgraph "Window Hooks"
UWM[useWindowManager] --> US
UWM --> APS
end
subgraph "Undo/Redo Hooks"
UGUR[useGlobalUndoRedo] --> APS
UGUR --> URS
UUR[useRegisterUndoRedo] --> URS
end
Usage Recommendations
| Hook | Use When | Notes |
|---|---|---|
useSound | UI feedback | Use Sounds enum for paths |
useTtsQueue | Long-form speech | Auto-queues for seamless playback |
useChatSynth | Typing feedback | Configurable presets |
useWindowManager | Any draggable window | Handles snap-to-edge |
useLaunchApp | Opening apps | Handles multi-instance |
useLyrics | Synced lyrics | Includes translation support |
useFurigana | Japanese text | Wait for furigana before soramimi |
useIsMobile | Touch/small screen | Use for layout decisions |
useIsPhone | Phones specifically | More restrictive than isMobile |
useOffline | Network-dependent features | Includes periodic polling |
useEventListener | DOM event handlers | Auto-cleanup, avoids stale closures |
useLatestRef | Callbacks needing current state | Eliminates manual ref sync |
useInterval | Polling, animations | Pass null to pause |
useTimeout | Delayed actions | Returns clear/reset controls |
useResizeObserver | Responsive components | Optional debouncing |
useRegisterUndoRedo | Apps with undo/redo | Register in app component, pairs with useGlobalUndoRedo |
useInstanceUndoRedo | Menu bars with Edit > Undo/Redo | Reads undo/redo state for a given instance |
useRealtimeConnectionStatus | Showing connection state | Returns Pusher connection status |
useTelegramLink | Telegram integration UI | Full link/unlink lifecycle |
Related Documentation
- Audio System - Audio architecture details
- Window Management - Window hook integration
- State Management - Store patterns