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

@gnome-ui/platform

v1.29.0

Published

TypeScript API layer for communicating with the GNOME host process from a React app

Readme

@gnome-ui/platform

TypeScript API layer for communicating with the GNOME host process from a React app running inside a WebKitGTK WebView.

npm npm downloads License: MIT GitHub last commit GitHub issues

Status: all 7 feature modules are implemented, each with a real browser/PWA fallback where a genuine web equivalent exists (and an honest rejection where one doesn't — see Modules). This package is the web side of the bridge; it assumes a GJS host that implements the matching window.webkit.messageHandlers channels and dispatches the gnome:* response/signal events documented per module below.

How it works

@gnome-ui/platform communicates with the GNOME host process through one of two mechanisms, resolved at runtime:

| Environment | Bridge | | --- | --- | | WebKitGTK WebView (GJS host) | window.webkit.messageHandlers.* | | Flatpak / sandboxed app | XDG Desktop Portals (via portals) | | Browser / PWA / test | real browser APIs where one exists, otherwise rejects |

Installation

npm install @gnome-ui/platform

Modules

| Module | Browser / PWA fallback | | --- | --- | | clipboard | Text and images: real navigator.clipboard. Files: none — no browser API exposes real filesystem paths. | | settings | None — no web equivalent of an app-schema-scoped GSettings store. | | notifications | Real Notification API (single click only — no action buttons). | | fileChooser | None — File System Access API only hands back opaque handles, never real paths. | | colorScheme | Read/subscribe: real matchMedia('(prefers-color-scheme: dark)'). Write: none. | | window | Partial — see the window section for exactly which state/actions have a real equivalent. | | portals | None — portals are meaningless outside a sandboxed GNOME host. |

Clipboard

import {
  readText, writeText,
  readImage, writeImage,
  readFiles, writeFiles,
} from "@gnome-ui/platform";

await writeText("hello");
const text = await readText();

// Images cross the bridge as data: URLs either way.
await writeImage(dataUrl);
const image = await readImage(); // string | null — null when nothing's there

// Paths only — WebKitGTK-only, rejects in a browser.
await writeFiles(["/home/user/report.pdf"]);
const paths = await readFiles(); // string[] — [] when nothing's there

Settings

Reads/writes a GSettings key. WebKitGTK-only.

import { getSetting, setSetting, onSettingChanged } from "@gnome-ui/platform";

const scheme = await getSetting<string>("color-scheme");
await setSetting("color-scheme", "prefer-dark");

// Fires for writes from this window AND external changes (another app,
// dconf-editor, `gsettings set` from a terminal).
const off = onSettingChanged<string>("color-scheme", (value) => {
  console.log("color-scheme is now", value);
});
off(); // unsubscribe

Notifications

import {
  sendNotification, withdrawNotification, onNotificationAction,
} from "@gnome-ui/platform";

const id = await sendNotification({
  title: "Download complete",
  body: "report.pdf",
  actions: [{ id: "open", label: "Open" }], // WebKitGTK only
});

onNotificationAction(id, (actionId) => {
  // actionId is "open" (WebKitGTK) or "default" (a click on the browser
  // fallback's plain Notification — it has no separate action buttons)
});

await withdrawNotification(id);

File chooser

WebKitGTK-only — every function rejects in a browser (see Modules for why). Each resolves { canceled: true, ... } rather than throwing when the user dismisses the dialog.

import { openFile, saveFile, selectFolder } from "@gnome-ui/platform";

const { canceled, paths } = await openFile({ multiple: true });
const { path } = await saveFile({ currentName: "export.csv" });
const { path: folder } = await selectFolder();

Color scheme

setColorScheme only ever overrides this app's own rendering (maps to Adw.StyleManager.set_color_scheme()), never the desktop-wide preference — a well-behaved app shouldn't be able to flip the user's whole desktop into dark mode. In a browser, use GnomeProvider's colorScheme prop instead — there is no JS API to force the browser's own rendering into a scheme.

import { getColorScheme, setColorScheme, onColorSchemeChanged } from "@gnome-ui/platform";

const scheme = await getColorScheme(); // "light" | "dark" — the resolved scheme
await setColorScheme("dark");          // "light" | "dark" | "system"

const off = onColorSchemeChanged((scheme) => {
  console.log("now rendering", scheme);
});

Window

import {
  getWindowState, setMaximized, setFullscreen,
  minimizeWindow, closeWindow, onWindowStateChanged,
} from "@gnome-ui/platform";

const { maximized, fullscreen, focused } = await getWindowState();

await setFullscreen(true);  // real Fullscreen API fallback in a browser
await setMaximized(true);   // WebKitGTK only — no browser fallback
await minimizeWindow();     // WebKitGTK only — no browser fallback
await closeWindow();        // real window.close() fallback (browsers only
                             // honor it for windows opened by script)

onWindowStateChanged(({ maximized, fullscreen, focused }) => { /* … */ });

maximized has no browser equivalent at all — there is no standard way for page script to ask whether the OS window chrome is maximized, so it is always reported as false outside WebKitGTK.

Portals

The generic escape hatch behind the other portal-backed modules — reach for it when the app needs a portal interface this package doesn't wrap in a dedicated module (OpenURI, Email, Print, Account, Background, Inhibit, Location, ScreenCast, …). WebKitGTK-only.

import { callPortal, onPortalSignal } from "@gnome-ui/platform";

await callPortal({
  interface: "org.freedesktop.portal.OpenURI",
  method: "OpenURI",
  args: { uri: "https://example.com" },
});

const off = onPortalSignal(
  "org.freedesktop.portal.Settings",
  "SettingChanged",
  (payload) => { /* … */ },
);

Bridge utilities

The typed modules above are all built on these — reach for them directly to talk to a bridge channel this package doesn't wrap yet.

Fire-and-forget

Send a message to a named GJS handler without waiting for a reply — resolves once dispatched, not once the host finishes handling it:

import { isWebKitBridge, postMessage } from "@gnome-ui/platform";

if (isWebKitBridge()) {
  console.log("Running in GNOME app context");
}

await postMessage("notifications", { action: "send", title: "Hello" });

Request/response

Several host operations are inherently asynchronous (a GDK clipboard read, a GSettings read, a portal call…) and need their reply matched back to the specific call that triggered it, especially with several calls in flight at once. postMessageAndWait tags the outgoing message with a requestId and resolves once the host dispatches a gnome:<responseEvent> carrying that same id back — every module above that needs a value back from the host is built on this:

import { postMessageAndWait } from "@gnome-ui/platform";

const { value } = await postMessageAndWait<{ requestId: string; value: string }>(
  "settings",
  { action: "get", key: "color-scheme" },
  "settings-get-result",
);

Rejects after 5s (configurable via a 4th argument) if no matching response arrives — e.g. because the WebView shell hasn't implemented that channel yet.

Native → Web events

Subscribe to events dispatched by the GJS host. The host fires them by evaluating a CustomEvent in the WebView:

// GJS side — dispatch an event to the web layer
webView.evaluate_javascript(
  `window.dispatchEvent(new CustomEvent("gnome:open-modal", { detail: { id: "settings" } }))`,
  -1, null, null, null, null
);
// Web side — subscribe (returns an unsubscribe function)
import { onNativeEvent } from "@gnome-ui/platform";

const off = onNativeEvent("open-modal", (payload) => {
  console.log("open modal:", payload.id);
});

// later, clean up:
off();

All native events use the gnome: prefix internally — pass only the unprefixed name to onNativeEvent.

Security note: events arrive as plain DOM CustomEvents, so any script running in the page can forge them — there is no way to verify the sender. Treat payloads as untrusted input, and never gate a privileged action solely on receiving one.

Runtime detection

import { getRuntime } from "@gnome-ui/platform";

const { shell, engine, browser, os } = getRuntime();

if (shell === "webkitgtk-webview") // running inside a GNOME native app
if (shell === "pwa")               // installed PWA
if (browser.epiphany)              // running inside GNOME Web (Epiphany)
if (os.linux)                      // Linux host

License

MIT