How the RAM Indicator in the Top Bar Sources and Displays Memory Usage in Modly
The RAM indicator polls Node.js os APIs every 2 seconds via an Electron IPC bridge, formats bytes to gigabytes, and renders a color-coded progress bar that can be toggled in application settings.
The Modly desktop application includes a real-time memory monitor in its top bar. This guide examines the complete data flow—from user preference to OS-level queries—based on the lightningpixel/modly source code.
Architecture Overview
The RAM indicator spans four architectural layers:
| Layer | Key File | Purpose |
|---|---|---|
| Settings UI | src/areas/settings/components/ApplicationSection.tsx |
Toggle visibility |
| Global State | src/shared/stores/appStore.ts |
Persist preference across sessions |
| Top Bar | src/shared/components/layout/TopBar.tsx |
Conditional rendering |
| Indicator Logic | src/shared/components/layout/MemoryIndicator.tsx |
Sample, format, and visualize memory |
User Preference and Global State
The visibility of the RAM indicator is controlled through a zustand store with persistence.
Settings Toggle
In the Application settings panel, a boolean toggle wires directly to the store:
// src/areas/settings/components/ApplicationSection.tsx
const { showRamIndicator, setShowRamIndicator } = useAppStore()
<Toggle
value={showRamIndicator}
onChange={setShowRamIndicator}
label="RAM indicator"
/>
Persisted Store Configuration
appStore uses zustand-persist to survive application restarts:
// src/shared/stores/appStore.ts
showRamIndicator: true,
setShowRamIndicator: (v) => set({ showRamIndicator: v }),
The store rehydrates automatically on launch, restoring the user's last preference.
Top Bar Integration
The TopBar component consumes the flag and conditionally mounts the indicator:
// src/shared/components/layout/TopBar.tsx
const { patchUpdateReady, platform, showRamIndicator } = useAppStore()
{showRamIndicator && <MemoryIndicator />}
This pattern keeps the top bar lightweight when the indicator is disabled.
Memory Sampling and Display
MemoryIndicator.tsx handles all OS communication, formatting, and rendering.
Polling Mechanism
On mount, the component establishes a 2-second polling interval:
// src/shared/components/layout/MemoryIndicator.tsx
useEffect(() => {
let cancelled = false;
const tick = async () => {
const stats = await window.electron.system.memory(); // IPC call
if (!cancelled) setMem(stats);
};
tick(); // immediate first sample
const id = setInterval(tick, 2000);
return () => {
cancelled = true;
clearInterval(id);
};
}, []);
The cancelled flag prevents state updates after unmount, avoiding memory leaks.
Data Transformation
Raw bytes are converted for human-readable display:
const pct = mem.total > 0
? Math.min(100, Math.round((mem.used / mem.total) * 100))
: 0;
// fmtGB converts bytes → GB with fixed decimal precision
<span>{fmtGB(mem.used)} / {fmtGB(mem.total)} GB</span>
Visual Rendering
The indicator renders as a compact progress bar with dynamic coloring:
<div className="w-20 h-1.5 bg-zinc-900 rounded-full overflow-hidden">
<div
className={`h-full ${barColor} transition-all duration-500 ease-out`}
style={{ width: `${pct}%` }}
/>
</div>
<span className={`text-[11px] ${textColor}`}>
{fmtGB(mem.used)} / {fmtGB(mem.total)} GB
</span>
Color thresholds (implemented via Tailwind classes):
- Green — low usage
- Amber — moderate usage
- Red — high usage
A multi-line tooltip exposes exact byte values on hover.
Electron IPC Bridge
The window.electron.system.memory() call traverses Electron's context bridge:
| Step | Location | Action |
|---|---|---|
| Preload | electron/preload/electron-api.ts |
Exposes system.memory() to renderer |
| Main | electron/main/system.ts |
Executes Node.js os.totalmem() and os.freemem() |
| Calculation | Main process | used = total - free |
| Return | IPC | Plain object { total, used, available } |
This architecture isolates privileged OS calls to the main process while keeping the renderer sandboxed.
Complete Implementation Example
To replicate the indicator's core logic in your own component:
import { useEffect, useState } from 'react';
type MemStats = { total: number; used: number; available: number };
export function MemoryIndicator() {
const [mem, setMem] = useState<MemStats | null>(null);
useEffect(() => {
let cancelled = false;
const tick = async () => {
const stats = await window.electron.system.memory();
if (!cancelled) setMem(stats);
};
tick();
const id = setInterval(tick, 2000);
return () => { cancelled = true; clearInterval(id); };
}, []);
if (!mem) return null;
const pct = Math.round((mem.used / mem.total) * 100);
const fmtGB = (b: number) => (b / 2 ** 30).toFixed(1);
return (
<div className="flex items-center gap-2">
<div className="w-20 h-1.5 bg-zinc-900 rounded-full overflow-hidden">
<div
className="h-full bg-green-500 transition-all"
style={{ width: `${pct}%` }}
/>
</div>
<span className="text-[11px]">
{fmtGB(mem.used)} / {fmtGB(mem.total)} GB
</span>
</div>
);
}
Summary
- Preferences live in
appStore.ts, persisted via zustand-persist - Conditional rendering happens in
TopBar.tsxvia boolean guard - OS queries execute every 2 seconds through
window.electron.system.memory() - Data flows from Node.js
osmodule → main process → preload → renderer - Visual output includes percentage bar, GB readout, color coding, and tooltip
Frequently Asked Questions
How do I disable the RAM indicator in Modly?
Open Settings → Application, then toggle off "RAM indicator". The preference persists across restarts through zustand-persist in appStore.ts.
Why does the indicator update every 2 seconds?
The setInterval(tick, 2000) in MemoryIndicator.tsx balances real-time responsiveness with minimal CPU overhead. Shorter intervals increase IPC traffic without meaningful usability gains.
What Node.js APIs provide the memory data?
The main process uses os.totalmem() and os.freemem(), then calculates used = total - free. These are cross-platform APIs that work on Windows, macOS, and Linux.
Can I change the color thresholds for memory usage?
The bar colors in MemoryIndicator.tsx are determined by Tailwind classes applied based on percentage ranges. Modify the barColor logic to adjust thresholds—though the source uses standard green/amber/red conventions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →