npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ravenstudiosio/laptop-sdk

v1.0.0

Published

SDK for building apps for our FiveM laptop resource.

Downloads

6

Readme

Laptop SDK

Official SDK for building apps for our FiveM laptop resource.

npm version License: MIT

Installation

npm install @ravenstudiosio/laptop-sdk

Quick Start

Method 1: Using Hooks with Context Provider (Recommended)

import {
  createApp,
  RavenStudiosSDKProvider,
  useFileSystemAPI,
  useNotificationAPI,
} from "@ravenstudiosio/laptop-sdk";
import type { AppComponentProps } from "@ravenstudiosio/laptop-sdk";
import { Calculator } from "lucide-react";

// Child component can use hooks
const CalculatorContent = () => {
  const fileSystemApi = useFileSystemAPI();
  const notificationApi = useNotificationAPI();

  const handleCalculate = () => {
    fileSystemApi.createFile("/Home", "result.txt", "2 + 2 = 4");
    notificationApi.notify({
      title: "Calculation Complete",
      message: "Result saved to file",
    });
  };

  return (
    <div className="p-4">
      <h1>Calculator</h1>
      <button onClick={handleCalculate}>Calculate & Save</button>
    </div>
  );
};

// Wrap with provider to enable hooks
const component = (props: AppComponentProps) => {
  return (
    <RavenStudiosSDKProvider value={props}>
      <CalculatorContent />
    </RavenStudiosSDKProvider>
  );
};

export default createApp(
  {
    id: "my-calculator",
    name: "Calculator",
    icon: Calculator,
    category: "Utilities",
    description: "A simple calculator app",
    version: "1.0.0",
    isSystem: false,
    isDownloadable: true,
    downloadSize: 2.5,
    publisher: "Your Name",
    defaultWidth: 400,
    defaultHeight: 500,
  },
  component
);

Method 2: Using Props (Traditional)

import { createApp } from "@ravenstudiosio/laptop-sdk";
import type { AppComponentProps } from "@ravenstudiosio/laptop-sdk";
import { Calculator } from "lucide-react";

const MyCalculatorApp = ({
  windowApi,
  fileSystemApi,
  notificationApi,
}: AppComponentProps) => {
  const handleCalculate = () => {
    notificationApi.notify({
      title: "Calculation Complete",
      message: "2 + 2 = 4",
    });
  };

  return (
    <div className="p-4">
      <h1>Calculator</h1>
      <button onClick={handleCalculate}>Calculate</button>
    </div>
  );
};

export default createApp(
  {
    id: "my-calculator",
    name: "Calculator",
    icon: Calculator,
    category: "Utilities",
    description: "A simple calculator app",
    version: "1.0.0",
    isSystem: false,
    isDownloadable: true,
    downloadSize: 2.5,
    publisher: "Your Name",
    defaultWidth: 400,
    defaultHeight: 500,
  },
  MyCalculatorApp
);

Method 3: Using Classic Export Pattern

import type { AppComponentProps } from "@ravenstudiosio/laptop-sdk";
import { FileText } from "lucide-react";

const MyTextEditor = (props: AppComponentProps) => {
  return <div>Text Editor Content</div>;
};

const metadata = {
  id: "text-editor",
  name: "Text Editor",
  icon: FileText,
  category: "Productivity" as const,
  description: "Simple text editor",
  version: "1.0.0",
  isSystem: false,
  isDownloadable: true,
};

const component = (props: AppComponentProps) => {
  return <MyTextEditor {...props} />;
};

export default {
  metadata,
  component,
};

React Hooks API

The SDK provides React hooks for a modern, ergonomic developer experience. Use these hooks to access SDK APIs anywhere in your component tree without prop drilling.

Available Hooks

  • useSDK() - Access all SDK APIs at once
  • useWindowAPI() - Window management
  • useFileSystemAPI() - File system operations
  • useStorageAPI() - App-scoped storage
  • useDialogAPI() - System dialogs
  • useClipboardAPI() - Clipboard operations
  • useMenuAPI() - Context menus
  • useShortcutAPI() - Keyboard shortcuts
  • useIPCAPI() - Inter-process communication
  • useNotificationAPI() - Toast notifications
  • useSystemAPI() - System information
  • useCommandAPI() - Command palette
  • useProcessAPI() - Process management
  • useAppInfo() - App metadata

Using Hooks

import {
  RavenStudiosSDKProvider,
  useFileSystemAPI,
  useNotificationAPI,
  useWindowAPI,
} from "@ravenstudiosio/laptop-sdk";

// Deeply nested component
const SaveButton = () => {
  const fileSystemApi = useFileSystemAPI();
  const notificationApi = useNotificationAPI();

  const handleSave = () => {
    fileSystemApi.createFile("/Home", "data.txt", "My data");
    notificationApi.notify({
      title: "Saved",
      message: "File saved successfully",
    });
  };

  return <button onClick={handleSave}>Save</button>;
};

const Toolbar = () => {
  const windowApi = useWindowAPI();

  return (
    <div>
      <SaveButton />
      <button onClick={() => windowApi.maximize()}>Maximize</button>
    </div>
  );
};

// Root component wraps with provider
const component = (props: AppComponentProps) => {
  return (
    <RavenStudiosSDKProvider value={props}>
      <div>
        <h1>My App</h1>
        <Toolbar />
      </div>
    </RavenStudiosSDKProvider>
  );
};

Hook Usage Examples

useSDK - Get all APIs:

const MyComponent = () => {
  const { windowApi, fileSystemApi, notificationApi } = useSDK();
  // Use any API you need
};

useSystemAPI - React to theme changes:

const ThemeDisplay = () => {
  const systemApi = useSystemAPI();
  const [theme, setTheme] = useState(systemApi.getTheme());

  useEffect(() => {
    return systemApi.onThemeChange(setTheme);
  }, [systemApi]);

  return <div>Current theme: {theme}</div>;
};

useShortcutAPI - Register keyboard shortcuts:

const ShortcutExample = () => {
  const shortcutApi = useShortcutAPI();
  const notificationApi = useNotificationAPI();

  useEffect(() => {
    const unregister = shortcutApi.register(
      "Ctrl+S",
      () =>
        notificationApi.notify({ title: "Save", message: "Ctrl+S pressed!" }),
      { description: "Save file", global: true }
    );
    return unregister;
  }, [shortcutApi, notificationApi]);

  return <div>Press Ctrl+S</div>;
};

useIPCAPI - Inter-app communication:

const IPCExample = () => {
  const ipcApi = useIPCAPI();
  const [messages, setMessages] = useState<string[]>([]);

  useEffect(() => {
    return ipcApi.on("chat", (data, senderAppId) => {
      setMessages((prev) => [...prev, `${senderAppId}: ${data}`]);
    });
  }, [ipcApi]);

  const sendMessage = () => {
    ipcApi.broadcast("chat", "Hello from my app!");
  };

  return (
    <div>
      <button onClick={sendMessage}>Send Message</button>
      <ul>
        {messages.map((m, i) => (
          <li key={i}>{m}</li>
        ))}
      </ul>
    </div>
  );
};

Available APIs

The SDK provides 13 powerful APIs to interact with the laptop system:

1. Window API

Manage windows, dialogs, and file pickers.

const MyApp = ({ windowApi }: AppComponentProps) => {
  const openNewWindow = () => {
    windowApi.openWindow("New Window", <div>Content</div>, 800, 600);
  };

  const closeApp = () => {
    windowApi.close();
  };

  const maximizeWindow = () => {
    windowApi.maximize();
  };

  return (
    <div>
      <button onClick={openNewWindow}>Open Window</button>
      <button onClick={maximizeWindow}>Maximize</button>
      <button onClick={closeApp}>Close</button>
    </div>
  );
};

2. File System API

Read, write, and manage files and folders.

const MyApp = ({ fileSystemApi, notificationApi }: AppComponentProps) => {
  const createFile = () => {
    fileSystemApi.createFile("/Home", "document.txt", "Hello World");
    notificationApi.notify({
      title: "File Created",
      message: "document.txt created successfully",
    });
  };

  const readFile = () => {
    const file = fileSystemApi.getItemByPath("/Home/document.txt");
    if (file && file.type === "file") {
      console.log("Content:", file.content);
    }
  };

  const searchFiles = () => {
    const results = fileSystemApi.searchItems("document");
    console.log("Found:", results);
  };

  return (
    <div>
      <button onClick={createFile}>Create File</button>
      <button onClick={readFile}>Read File</button>
      <button onClick={searchFiles}>Search</button>
    </div>
  );
};

3. Storage API

App-scoped persistent storage (in-memory).

const MyApp = ({ storageApi }: AppComponentProps) => {
  const saveData = () => {
    storageApi.set("user-preference", { theme: "dark", fontSize: 14 });
  };

  const loadData = () => {
    const prefs = storageApi.get("user-preference", { theme: "light" });
    console.log("Preferences:", prefs);
  };

  const clearData = () => {
    storageApi.clear();
  };

  return (
    <div>
      <button onClick={saveData}>Save</button>
      <button onClick={loadData}>Load</button>
      <button onClick={clearData}>Clear All</button>
    </div>
  );
};

4. Dialog API

System dialogs for user interaction.

const MyApp = ({ dialogApi, notificationApi }: AppComponentProps) => {
  const showAlert = async () => {
    await dialogApi.alert({
      title: "Information",
      message: "This is an informational message",
    });
  };

  const showConfirm = async () => {
    const confirmed = await dialogApi.confirm({
      title: "Delete File?",
      message: "This action cannot be undone.",
    });
    if (confirmed) {
      console.log("User confirmed");
    }
  };

  const showPrompt = async () => {
    const name = await dialogApi.prompt({
      title: "Enter your name:",
      defaultValue: "John Doe",
    });
    if (name) {
      notificationApi.notify({
        title: "Hello",
        message: `Welcome, ${name}!`,
      });
    }
  };

  return (
    <div>
      <button onClick={showAlert}>Alert</button>
      <button onClick={showConfirm}>Confirm</button>
      <button onClick={showPrompt}>Prompt</button>
    </div>
  );
};

5. Clipboard API

Copy and paste functionality.

const MyApp = ({ clipboardApi, notificationApi }: AppComponentProps) => {
  const copyText = () => {
    clipboardApi.writeText("Hello from Virtual OS!");
    notificationApi.notify({
      title: "Copied",
      message: "Text copied to clipboard",
    });
  };

  const pasteText = () => {
    const text = clipboardApi.readText();
    console.log("Pasted:", text);
  };

  return (
    <div>
      <button onClick={copyText}>Copy</button>
      <button onClick={pasteText}>Paste</button>
    </div>
  );
};

6. Notification API

Toast notifications.

const MyApp = ({ notificationApi }: AppComponentProps) => {
  const showNotification = () => {
    notificationApi.notify({
      title: "Success",
      message: "Operation completed successfully",
    });
  };

  return <button onClick={showNotification}>Notify</button>;
};

7. Menu API

Context menus.

const MyApp = ({ menuApi }: AppComponentProps) => {
  const showMenu = (e: React.MouseEvent) => {
    menuApi.showContextMenu(
      [
        { id: "cut", label: "Cut", onClick: () => console.log("Cut") },
        { id: "copy", label: "Copy", onClick: () => console.log("Copy") },
        { id: "sep", label: "", type: "separator" },
        { id: "paste", label: "Paste", onClick: () => console.log("Paste") },
      ],
      { x: e.clientX, y: e.clientY }
    );
  };

  return <div onContextMenu={showMenu}>Right-click me</div>;
};

8. Shortcut API

Global keyboard shortcuts.

const MyApp = ({ shortcutApi, notificationApi }: AppComponentProps) => {
  useEffect(() => {
    const unregister = shortcutApi.register(
      "Ctrl+S",
      () => {
        notificationApi.notify({
          title: "Save",
          message: "File saved!",
        });
      },
      { description: "Save file", global: true }
    );

    return unregister;
  }, [shortcutApi, notificationApi]);

  return <div>Press Ctrl+S to save</div>;
};

9. IPC API

Inter-process communication between apps.

const MyApp = ({ ipcApi, notificationApi }: AppComponentProps) => {
  useEffect(() => {
    // Listen for messages
    const unsubscribe = ipcApi.on("my-channel", (data, senderAppId) => {
      console.log("Received from", senderAppId, ":", data);
    });

    return unsubscribe;
  }, [ipcApi]);

  const sendMessage = () => {
    ipcApi.broadcast("my-channel", {
      message: "Hello from my app!",
      timestamp: Date.now(),
    });
  };

  return <button onClick={sendMessage}>Send Message</button>;
};

10. System API

System information and theme.

const MyApp = ({ systemApi }: AppComponentProps) => {
  const [theme, setTheme] = useState(systemApi.getTheme());

  useEffect(() => {
    const unsubscribe = systemApi.onThemeChange((newTheme) => {
      setTheme(newTheme);
    });
    return unsubscribe;
  }, [systemApi]);

  const launchApp = () => {
    systemApi.launchApp("file-explorer");
  };

  return (
    <div>
      <p>Current theme: {theme}</p>
      <button onClick={launchApp}>Open File Explorer</button>
    </div>
  );
};

11. Command API

Register commands in the command palette.

const MyApp = ({ commandApi }: AppComponentProps) => {
  useEffect(() => {
    commandApi.registerCommands([
      {
        id: "my-app-action",
        label: "Do Something Cool",
        description: "Performs a cool action",
        execute: () => {
          console.log("Command executed!");
        },
      },
    ]);

    return () => {
      commandApi.unregisterCommands(["my-app-action"]);
    };
  }, [commandApi]);

  return <div>Press Ctrl+K and search for "Do Something Cool"</div>;
};

12. Process API

Process and instance management.

const MyApp = ({ processApi }: AppComponentProps) => {
  const info = processApi.getInfo();

  return (
    <div>
      <p>Instance ID: {info.instanceId}</p>
      <p>App ID: {info.appId}</p>
      <p>Started: {new Date(info.startTime).toLocaleString()}</p>
    </div>
  );
};

TypeScript Support

The SDK is fully typed with TypeScript. All APIs have complete type definitions:

import type {
  AppComponentProps,
  FileSystemItem,
  WindowState,
  SystemTheme,
  // ... and many more
} from "@ravenstudiosio/laptop-sdk";

Building Your App

Project Setup

  1. Create a new React project with Vite:
npm create vite@latest my-virtual-os-app -- --template react-ts
cd my-virtual-os-app
npm install
  1. Install the SDK and dependencies:
npm install @ravenstudiosio/laptop-sdk
npm install lucide-react
  1. Configure Module Federation in vite.config.ts:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { federation } from "@module-federation/vite";

export default defineConfig({
  server: {
    port: 5001,
    strictPort: true,
    origin: "http://localhost:5001",
  },
  base: "http://localhost:5001",
  plugins: [
    react(),
    federation({
      name: "my-app",
      filename: "remoteEntry.js",
      manifest: true,
      exposes: {
        "./app": "./src/AppModule.tsx",
      },
      shared: {
        react: { singleton: true, requiredVersion: "^19.0.0" },
        "react-dom": { singleton: true, requiredVersion: "^19.0.0" },
        "lucide-react": { singleton: true },
      },
    }),
  ],
  build: {
    modulePreload: false,
    target: "chrome89",
    minify: false,
    cssCodeSplit: false,
  },
});
  1. Create your app in src/AppModule.tsx:
import { createApp } from "@ravenstudiosio/laptop-sdk";
import type { AppComponentProps } from "@ravenstudiosio/laptop-sdk";
import { AppWindow } from "lucide-react";

const MyApp = (props: AppComponentProps) => {
  return (
    <div className="p-4">
      <h1>My Laptop App</h1>
    </div>
  );
};

export default createApp(
  {
    id: "my-app",
    name: "My App",
    icon: AppWindow,
    category: "Utilities",
    description: "My awesome app",
    version: "1.0.0",
    isSystem: false,
    isDownloadable: true,
  },
  MyApp
);