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

@industry-theme/ghostty-terminal-panel

v0.1.18

Published

High-performance terminal panel using Ghostty WebAssembly rendering engine

Downloads

248

Readme

Ghostty Terminal Panel Integration

This guide outlines how to integrate the experimental Ghostty WebAssembly (Wasm) engine into the industry-themed-panel-starter framework. This allows you to render a high-performance terminal interface within a React panel while offloading the shell processing to node-pty in the Electron main process.

1. Architecture Overview

Since Ghostty is a native Zig application, we cannot import it directly into a standard JavaScript bundle. We rely on the Wasm build to run the rendering engine in the browser context (Electron Renderer).

  • View Layer (Panel): GhosttyWeb (Wasm) handles rendering pixels and capturing input.
  • Transport Layer: The Panel uses actions and events props to communicate via Electron IPC.
  • Logic Layer (Host): node-pty runs the actual shell (bash/zsh/powershell).

2. Implementation Steps

Step 1: Install Dependencies

Since the official library is not yet public, use the experimental community wrapper or your own local Wasm build.

# Option A: Experimental package
npm install @coder/ghostty-web

# Option B: Local build (copy .wasm files to public/assets/)
# Ensure ghostty-vt.wasm is accessible in your public path

Step 2: Create the Panel Component

Create a new file: src/panels/GhosttyTerminal.tsx.

This component initializes the Wasm engine and bridges the gap between the Ghostty IO and the Panel Framework's event system.

import React, { useEffect, useRef } from 'react';
import { GhosttyWeb } from '@coder/ghostty-web'; // or local import
import type { PanelComponentProps } from '../types';

export const GhosttyTerminal: React.FC<PanelComponentProps> = ({
  context,
  actions,
  events,
}) => {
  const terminalRef = useRef<HTMLDivElement>(null);
  const engineRef = useRef<any>(null);

  useEffect(() => {
    if (!terminalRef.current) return;

    const init = async () => {
      // 1. Initialize Wasm Engine
      const ghostty = await GhosttyWeb.init({
        element: terminalRef.current,
        // Ensure this path points to where you deployed the .wasm file
        wasmUrl: '/assets/ghostty-vt.wasm',
        fontSize: 14,
        fontFamily: 'JetBrains Mono, monospace',
      });

      engineRef.current = ghostty;

      // 2. Outgoing: User types in Ghostty -> Send to Host
      ghostty.onData((input: string) => {
        // Use the generic action handler to send data to Electron
        if (actions.sendTerminalData) {
            actions.sendTerminalData(input);
        } else {
            // Fallback for custom implementations
            console.warn('Action "sendTerminalData" not available on host.');
        }
      });

      // 3. Handle Resize
      const resizeObserver = new ResizeObserver(() => {
        ghostty.fit();
      });
      resizeObserver.observe(terminalRef.current);
    };

    init();

    // Cleanup
    return () => {
      // engineRef.current?.dispose();
    };
  }, [actions]);

  // 4. Incoming: Host sends PTY data -> Write to Ghostty
  useEffect(() => {
    const handleOutput = (data: any) => {
        // Ensure data is string or Uint8Array
        if (engineRef.current && data.payload) {
            engineRef.current.write(data.payload);
        }
    };

    // Listen to specific event ID defined by your host
    events.on('terminal:output', handleOutput);

    return () => {
        events.off('terminal:output', handleOutput);
    };
  }, [events]);

  return (
    <div
      ref={terminalRef}
      className="w-full h-full bg-black overflow-hidden"
    />
  );
};

Step 3: Register the Panel

Update src/index.tsx to export your new panel configuration.

import { GhosttyTerminal } from './panels/GhosttyTerminal';

export const panels = [
  {
    id: 'com.your-org.ghostty-term',
    name: 'Ghostty Terminal',
    icon: 'Terminal',
    description: 'Experimental Wasm-based Ghostty renderer',
    component: GhosttyTerminal,
  },
  // ... other panels
];

3. Host Side Integration (Electron Main)

The panel is just a view. The Host application (Electron Main process) must handle the actual PTY creation and IPC routing.

In your Electron Main Process:

import * as pty from 'node-pty';

// 1. Setup PTY
const ptyProcess = pty.spawn('bash', [], {
  name: 'xterm-256color',
  cols: 80,
  rows: 30,
  cwd: process.env.HOME,
  env: process.env
});

// 2. Incoming from Panel (via IPC/Actions)
ipcMain.on('panel-action', (event, { action, payload }) => {
  if (action === 'sendTerminalData') {
    ptyProcess.write(payload);
  }
});

// 3. Outgoing to Panel (via IPC/Events)
ptyProcess.onData((data) => {
  mainWindow.webContents.send('panel-event', {
    event: 'terminal:output',
    payload: data
  });
});

4. Known Constraints

  • Asset Loading: The Wasm build requires access to font files. You may need to configure your build tool (Vite/Webpack) to copy the .wasm and font assets to the final build output directory.
  • Clipboard: Native clipboard integration might be limited in the Wasm environment; you may need to implement a bridge using navigator.clipboard.
  • WebGL: Ghostty Wasm uses WebGL. Ensure hardware acceleration is enabled in your Electron window configuration (webPreferences: { webgl: true }).