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.tsx via boolean guard
  • OS queries execute every 2 seconds through window.electron.system.memory()
  • Data flows from Node.js os module → 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →