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

nexfep

v0.6.1

Published

A desktop application framework based on @webviewjs/webview

Readme

Nexfep

Language: English(now) | 简体中文

A desktop application framework based on @webviewjs/webview

Project Status

🚧 Early Stage

This project is currently in early development, with many core capabilities for desktop application development still missing. The framework is being continuously iterated.

Introduction

Nexfep is a desktop application framework built on @webviewjs/webview, written in TypeScript. It aims to provide developers with a concise and efficient toolchain for building cross-platform desktop applications.

The framework uses a window pool management mechanism, supporting multi-window application scenarios such as code editors, chat tools, dashboards, etc.

Features

  • Window Pool Management — Built-in window pool mechanism for automatic reuse and recycling of window resources, avoiding the overhead of frequent creation and destruction
  • IPC Communication — Supports bidirectional message communication between the main process and WebView, via injected functions
  • Window Control — Provides complete window operation APIs including maximize, minimize, close, title setting, and developer tools
  • Drag Regions — Built-in HTML attribute support for defining window drag regions (nexfep-area-drag, etc.)
  • System Tray — Create and manage system tray icons with context menus
  • Desktop Notifications — Send desktop notifications via app.utils.notify()
  • Logging System — Built-in logger with file output and colored console support, with automatic interception of page console messages
  • CLI Build Tool — Package your application into a standalone executable via nexfep build
  • TypeScript Support — Complete type definitions for excellent development experience

Installation

pnpm add nexfep

Quick Start

import { Application } from "nexfep";

const app = new Application();

const window = await app.windows.createWindow({
  visible: true,
  decoration: false,
});

await window.loadHTML("<h1 nexfep-area-drag>Hello Nexfep!</h1>");

Usage Guide

Application

Application is the main entry point of the framework, responsible for managing the application lifecycle and providing access to windows, system tray, and logger.

import { Application } from "nexfep";

const app = new Application();
// or with custom WebView2 user data directory (Windows only)
const app = new Application({ WindowsWebview2UserDataFolder: "C:\\custom\\webview2-data" });
// or with log file path
const app = new Application({ LogFilePath: "./app.log" });

Constructor Options

| Option | Type | Default | Description | | ------------------------------- | ------------------- | ------------------------------------------------ | ------------------------------------------- | | WindowsWebview2UserDataFolder | string (optional) | %LOCALAPPDATA%\NexfepDevelopment.webview2-data | WebView2 user data directory (Windows only) | | LogFilePath | string (optional) | none | File path for log output |

Properties

  • windows — The WindowPool instance for managing browser windows
  • utils — Utility methods (e.g., desktop notifications)
  • logger — The Logger instance for logging

Methods

  • createTray(options) — Create a system tray icon with context menu
  • createLocker(appName) — Create an application instance lock to prevent multiple instances
  • exit() — Exit the application

Icon

Icon represents an image resource that can be used as a window icon, tray icon, etc.

import { Icon } from "nexfep";

// Create from file path
const icon = Icon.from("./icon.png");

// Create from Buffer or Uint8Array
const icon = Icon.from(buffer);
const icon = Icon.from(uint8Array);

// Create from an object containing data/width/height
const icon = Icon.from({ data: buffer, width: 64, height: 64 });
const icon = Icon.from({ data: uint8Array, width: 64, height: 64 });

Static Methods

| Method | Parameters | Return Value | Description | | ------------------ | ----------------------------------------------------------------------------------------- | ------------ | ------------------------ | | Icon.from(input) | string | Buffer | Uint8Array | { data: Uint8Array \| Buffer, width?, height? } | Icon | Creates an icon instance |

Logger

The logger supports both file output and colored console output. It can be accessed via app.logger.

app.logger.clear();
app.logger.log("Hello World");
app.logger.error("An error occurred");
app.logger.warn("Warning message");
app.logger.info("Info message");
app.logger.debug("Debug message");

Methods

| Method | Description | | ---------------- | ------------------------------------------------------------------------------ | | clear() | Clear the log file (only takes effect when the LogFilePath parameter is set) | | log(message) | Log a message | | error(message) | Log an error message (red) | | warn(message) | Log a warning message (yellow) | | info(message) | Log an info message (blue) | | debug(message) | Log a debug message (gray) |

Each method accepts either a string or an array of strings.

Page Console Interception

Console calls (console.log, console.error, console.info, console.warn, console.debug) in the page are automatically intercepted and forwarded to the main process logger, with the source window ID included in the output.

Locker

Locker is a class for managing application instance locks, preventing multiple instances from running simultaneously.

const locker = app.createLocker("my-app");
try {
  // Try to acquire the lock
  await locker.lock();
  // You can also pass data to the other instance when acquiring the lock
  // await locker.lock(process.argv[2]);
} catch {
  // Instance already exists, exit the application
  app.exit();
}
// Focus the current instance when other instances acquire the lock
locker.whenLost((data) => {
  if (!win.isFocused()) {
    win.focus();
  }
  // If the other instance passed data, you can use it to perform specific actions
  // If no data was passed, data will be null
  if (data) {
    console.log("Other instance passed data:", data);
  }
});
// Release the lock
locker.unlock();

Methods

  • lock(data?) — Try to acquire the lock
  • whenLost(callback) — Register a callback to be called when other instances acquire the lock
  • unlock() — Release the lock

Tray

Create and manage system tray icons with context menus via app.createTray().

import { Icon } from "nexfep";

const icon = Icon.from("./icon.png");

const tray = app.createTray({
  id: "my-tray",
  tooltip: "My App",
  icon: icon, // Icon instance
  menuItems: [
    { id: "show", label: "Show Window" },
    { id: "quit", label: "Quit" },
  ],
});

Methods

| Method | Description | | --------------------- | ------------------------------------------------ | | addMenuItem(item) | Add a menu item | | removeMenuItem(id) | Remove a menu item by ID | | setMenuItems(items) | Replace all menu items | | setIcon(icon) | Change the tray icon, accepts an Icon instance | | setTooltip(tooltip) | Change the tooltip text | | on(event, callback) | Listen for tray events (e.g. 'click') | | show() | Show the tray icon | | hide() | Hide the tray icon | | destroy() | Destroy the tray icon |

tray.on("click", () => {
  console.log("Tray clicked");
});
tray.addMenuItem({ id: "about", label: "About" });
tray.setTooltip("Nexfep App");

Notifications

Send desktop notifications via app.utils.notify().

const notification = app.utils.notify("Title", { body: "Notification body" });

Parameters

  • title — Notification title
  • options (optional) — Configuration object
    • body — Notification body text

Window Pool

WindowPool is the core management class of the framework, responsible for window creation and recycling.

const pool = app.windows;

Window Creation

// Use all defaults by omitting parameters
const win = await pool.createWindow();

// Pass partial parameters
const win = await pool.createWindow({
  visible: true,
  title: "My App",
});

// All parameters
const win = await pool.createWindow({
  visible: true, // Whether to show immediately, default true
  decoration: true, // Whether to use system decorations, default true
  title: "My App", // Window title, default "Nexfep Window"
  icon: iconInstance, // Window icon, Icon instance, optional
  resizable: true, // Whether the window is resizable, default true
  width: 800, // Window width, default 800
  height: 600, // Window height, default 600
});

Parameters

| Option | Type | Default | Description | | ------------ | --------- | ----------------- | ---------------------------------------------------------------------------------------------------------------- | | visible | boolean | true | Whether to immediately show the window | | decoration | boolean | true | Whether to use system window decorations. When false, the window has no border and requires a custom title bar | | title | string | "Nexfep Window" | Window title | | icon | Icon | none | Window icon | | resizable | boolean | true | Whether the window is resizable | | width | number | 800 | Window width in pixels | | height | number | 600 | Window height in pixels |

Window Operations

window.show();
window.hide();
window.maximize();
window.minimize();
window.close();
window.focus();
window.setTitle("New Title");
window.setSize(800, 600);
window.openDevTools();

TypeScript Definitions for Frontend API

Add the following content to your frontend's tsconfig.json to use the frontend API definitions:

{
  "compilerOptions": {
    "types": ["nexfep/frontend"]
  }
}

Custom Events

Invoke Events

Invoke events via window.invoke in the page:

window.invoke("hello");
window.invoke("hello", "world");

Parameters

  • event — Event name
  • data (optional) — Any serializable data, will be serialized to JSON string before sending

Listen for Events

Listen for events via pool.handle in the main process:

pool.handle("hello", (data) => {
  console.log("Received event hello:", data);
});

Parameters

  • event — Event name, must match the event name when invoking
  • callback — Event handler function, receives event data as data parameter. Can return any serializable data, which will be serialized to JSON string and sent back to the frontend as the return value of window.invoke (can also return nothing)

Remove Event Listener

Remove event listener via pool.unhandle in the main process:

pool.unhandle("hello", (data) => {
  console.log("Received event hello:", data);
});

Parameters

  • event — Event name, must match the event name when listening
  • callback — Event handler function, must match the callback function when listening

Global Variables

Set Variables

Set a global variable via window.setGlobal in the page:

window.setGlobal("hello", "world");

Parameters

  • name — Global variable name
  • value — Global variable value, any serializable data, will be serialized to JSON string before sending

Get Variables

Get a global variable via window.getGlobal in the page:

const value = await window.getGlobal("hello");

Parameters

  • name — Global variable name

Global Variable Map

Get a Map<string, any> containing all global variables via pool.global in the main process, which supports operations like set and get:

const globals = pool.global;
globals.set("hello", "world");
const value = globals.get("hello");

Inter-Window Communication

The framework supports direct communication between windows via window.broadcast and window.tell, without going through the main process.

Broadcast

Send an event to all other open windows via window.broadcast:

window.broadcast("user-login", { userId: 123 });

Parameters

  • name — Event name
  • data (optional) — Any serializable data, will be serialized to JSON string before sending

Other windows listen for the broadcast event via window.addEventListener:

window.addEventListener("user-login", (event) => {
  console.log("User logged in:", event.detail);
});

The main process can send events to all open windows via pool.broadcast, with parameters identical to those of window.broadcast in the page:

pool.broadcast("user-login", { userId: 123 });

Tell

Send a message to a specific window by its ID via window.tell:

window.tell(2, "custom-message", { text: "Hello Window 2" });

Parameters

  • to — Target window ID (number)
  • message — Event name
  • data (optional) — Any serializable data, will be serialized to JSON string before sending

The target window receives the message via window.addEventListener:

window.addEventListener("custom-message", (event) => {
  console.log("Received message:", event.detail);
});

Each window's ID can be accessed via window.id:

console.log("This window ID:", window.id);

The main process can also send messages to this window through win.tell:

win.tell("custom-message", { text: `Hello Window ${win.id}` });

Custom Messages

Send Messages

In the page, use window.tell with to 0 to send a message to the main process.

window.tell(0, "custom-message", { hello: "world" });

Listen for Messages

Receive messages via onCustomMessage callback in the main process:

pool.onCustomMessage = (window, message, data) => {
  console.log(`[${message}] from window ${window.id}:`, data);
};

Callback Parameters

  • window — The window object that sent the message
  • message — Event name
  • data — Message content, an object type (automatically converted from JSON string via JSON.parse)

We recommend using window.invoke to communicate between pages and the main process, instead of window.tell.

Window Control Functions

The following injected functions can be directly called in the page for window control:

window.close(); // Close window
window.minimize(); // Minimize window
window.unminimize(); // Restore minimized window
window.toggleMinimize(); // Toggle minimized state
window.maximize(); // Maximize window
window.unmaximize(); // Restore maximized window
window.toggleMaximize(); // Toggle maximized state
window.setTitle("Title"); // Set window title
window.openDevTools(); // Open developer tools
window.closeDevTools(); // Close developer tools

The following properties are also available in the page:

console.log(window.id); // Window unique identifier
console.log(window.isNexfepLoadDone); // Whether the window has finished loading

Drag Regions

Define window drag regions via HTML attributes without writing additional JavaScript code. These attributes automatically apply -webkit-app-region and app-region CSS properties.

nexfep-area-drag

Makes the entire region and all its child elements draggable. Suitable for scenarios like custom title bars where the entire region needs to be draggable.

<div nexfep-area-drag>
  <h1>Title Bar</h1>
  <span>Subtitle</span>
</div>

nexfep-element-drag

Makes only the specified element itself draggable; child elements are not affected. Suitable for scenarios requiring precise control over the drag region.

<div>
  <div nexfep-element-drag>Drag Handle</div>
  <p>This part is not draggable</p>
</div>

nexfep-no-drag

Makes the specified region and all its child elements non-draggable. Highest priority, can override parent element's drag attributes. Suitable for interactive elements like buttons and input fields.

<div nexfep-area-drag>
  <h1>Title Bar</h1>
  <button nexfep-no-drag>Click Button</button>
</div>

nexfep-auto-drag

Automatically determines drag regions: the entire region is draggable, but common interactive elements (button, input, select, textarea, a) are automatically set to non-draggable. Suitable for complex regions containing multiple interactive elements.

<div nexfep-auto-drag>
  <h1>Title Bar</h1>
  <button>Automatically non-draggable</button>
  <input placeholder="Automatically non-draggable" />
  <a href="#">Automatically non-draggable</a>
</div>

Load Complete Event

The nexfep-load-done event is triggered after the WebView window finishes loading:

window.addEventListener("nexfep-load-done", () => {
  console.log("Nexfep window loaded");
});

It can also be checked via the window.isNexfepLoadDone property:

if (window.isNexfepLoadDone) {
  // Window is ready
}

CLI

help

Use the help command to get a list of available commands:

npx nexfep help

build

Nexfep provides a command-line tool for building applications into standalone executables.

Usage

npx nexfep build [options]

Options

| Option | Description | | ------------------------------- | ----------------------------------------------------------- | | -n, --name <name> | Application name (default: from package.json) | | -e, --entry <file> | Entry file path (default: from package.json main) | | -o, --output <dir> | Output directory (default: dist) | | -i, --ignore <pattern> | Files or directories to ignore (can be used multiple times) | | -c, --console | Show console window on Windows (default: false) | | -r, --reinstall | Reinstall production dependencies only before building | | -s, --skip-clean | Skip cleaning old build files before building | | -u, --upx <level> | Use UPX to compress the executable, level 0-9 (default: 0) | | -m, --meta, --metadata <file> | Metadata file path (default: none) |

Examples

# Build using defaults from package.json
nexfep build

# Set custom application name and entry file
nexfep build -n my-app -e ./src/index.js

# Set custom output directory
nexfep build -o ./build

# Ignore multiple patterns
nexfep build -i node_modules -i test -i temp

# Build with UPX compression
nexfep build -u 7

This command uses nexfpack to package your application into a standalone executable.

For metadata parameter, you can refer to Nexfpack documentation for details.

Please do not include the outer metadata field, just the internal fields. Like this:

{ "1033": { "FileVersion": "..." } }

API

Application

| Method/Property | Parameters | Return Value | Description | | ----------------------- | -------------------------------------------------- | ------------ | -------------------------------- | | constructor(options?) | { WindowsWebview2UserDataFolder?, LogFilePath? } | Application | Creates the application instance | | windows | / | WindowPool | The window pool instance | | utils | / | __Utils | Utility methods (notifications) | | logger | / | Logger | The logger instance | | createTray(options) | see Tray section | Tray | Creates a system tray icon | | exit() | None | void | Exits the application |

WindowPool

| Method/Property | Parameters | Return Value | Description | | --------------------------- | ------------------------------------------------------ | ----------------- | ------------------------------------------------------ | | createWindow(options?) | Optional parameters, see Window Creation section above | Promise<Window> | Creates and returns a window | | handle(event, callback) | event: string, callback: (data: any) => any | None | Listens for the specified event | | unhandle(event, callback) | event: string, callback: (data: any) => any | None | Removes the specified event listener | | global | / | Map<string, any> | A global variable map | | closeWindow(window) | window: Window | Promise<void> | Closes the specified window and returns it to the pool | | onCustomMessage | (window: Window, data: string) => void | None | Custom message callback |

Window

| Method/Property | Parameters | Return Value | Description | | --------------------------------------- | ----------------------------------------------------------------- | --------------------------------- | -------------------------------------------------------------------------------------------------- | | loadURL(url) | url: string — URL to load | Promise<void> | Loads the specified URL | | loadHTML(html) | html: string — HTML string | Promise<void> | Loads the specified HTML content | | show() | None | void | Shows the window | | hide() | None | void | Hides the window | | maximize() | None | void | Maximizes the window | | unMaximize() | None | void | Restores the window (cancels maximize) | | minimize() | None | void | Minimizes the window | | unMinimize() | None | void | Restores the window (cancels minimize) | | close() | None | void | Closes the window and returns to pool | | setTitle(title) | title: string | void | Sets the window title | | setDecorated(isDecorated) | isDecorated: boolean | void | Sets whether the window has borders and title bar | | setResizable(resizable) | resizable: boolean | void | Sets whether the window is resizable | | setLevel(level) | level: -1 | 0 | 1 | void | Sets window level: -1=bottom, 0=normal, 1=top | | setFullScreen(isFullScreen, options?) | isFullScreen: boolean, options?: { borderless?: boolean } | void | Sets fullscreen mode. borderless: true for borderless fullscreen, otherwise exclusive fullscreen | | setIcon(icon) | icon: Icon | void | Sets the window icon | | setSize(width, height) | width: number, height: number | void | Sets the window size in pixels | | getSize() | None | { width: number, height: number } | Gets the window size in pixels | | setPosition(x, y) | x: number, y: number | void | Sets the window position in pixels | | getPosition() | None | { x: number, y: number } | Gets the window position in pixels | | focus() | None | void | Focuses the window | | isFocused() | None | boolean | Whether the window has focus | | isMaximized() | None | boolean | Whether the window is maximized | | isMinimized() | None | boolean | Whether the window is minimized | | toggleMaximize() | None | void | Toggles the window maximized state | | toggleMinimize() | None | void | Toggles the window minimized state | | openDevTools() | None | void | Opens developer tools | | closeDevTools() | None | void | Closes developer tools | | id | None | number | Unique window identifier, auto-incrementing |

Development

pnpm install
pnpm run compile

License

MIT License