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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@nd-fir/react-terminal

v1.0.2

Published

Terminale React in stile Unix controllabile via event bus, con input multiline auto-size e supporto async.

Readme

@nd-fir/react-terminal

Terminale React stile Unix, controllabile da fuori via event bus.
Input multiline auto-size, wrapping adattivo, supporto a funzioni asincrone, loading interno/esterno.

Install

npm i @nd-fir/react-terminal
# oppure
pnpm add @nd-fir/react-terminal

Dipendenze

npm i react

Import base

import { Terminal, TerminalService } from "@nd-fir/react-terminal";
import "@nd-fir/react-terminal/styles.css"; // stile di default

Se preferisci import opt-in del CSS, mantieni la riga sopra. Se vuoi gestire tu lo stile, omettila e usa classi/options (vedi sotto).

Quick start

<Terminal
  prompt="$"
  run={async (input) => {
    const [cmd, ...args] = input.trim().split(/\s+/);
    switch (cmd) {
      case "echo": return args.join(" ");
      case "help": return "Comandi: help, echo";
      default:     return \`cmd non trovato: \${cmd}\`;
    }
  }}
/>

E da qualunque punto della tua app:

TerminalService.print("help", "Comandi: help, echo"); // (comando, risposta)
TerminalService.run("help"); // (comando)
TerminalService.cast("Recupero dati..."); // (messaggio_loading)
TerminalService.clear(); // pulisce la history

Comportamento tastiera

  • Enter → invia comando
  • Shift+Enter → nuova riga nell’input
  • ↑/↓ → naviga la history dei comandi

API

<Terminal /> Props

type TerminalProps = {
  prompt: string; // es. "$" o "nebula:~$"
  run: (input: string) => Promise<string | void | null | undefined>;
  isLoading?: boolean; // loading esterno (facoltativo)
  onContextMenu?: (e: React.MouseEvent) => void; // per delegare a ContextMenu
  options?: {
    container?: HTMLAttributes<HTMLDivElement>;
    prompt?: HTMLAttributes<HTMLSpanElement>;
    command?: HTMLAttributes<HTMLSpanElement>; // input cmd
    history?: HTMLAttributes<HTMLSpanElement>; // contenitore cmd/response in history
    response?: HTMLAttributes<HTMLDivElement>; // output
    loading?: HTMLAttributes<HTMLDivElement>; // area loading
  };
};
  • L’input è una textarea auto-size.
  • Il wrapping è già configurato: white-space: pre-wrap, overflow-wrap: anywhere, word-break: break-word.
  • Se isLoading è true, l’input è disabilitato e compare lo spinner anche senza run interno.

TerminalService (event bus)

TerminalService.run(cmd: string)                    // chiede al terminale di eseguire un comando
TerminalService.print(cmd: string, resp: string)    // append di una riga risposta (con cmd associato o vuoto)
TerminalService.cast(text: string)                  // messaggi di stato/progresso (mostrati con spinner)
TerminalService.clear()                             // pulisce la history

La consegna degli eventi è sincrona e in-order. Non fare lavoro pesante nel listener del componente; delega a funzioni async.

Loading interno vs esterno

  • Interno: quando il terminale chiama run(input) setta da solo il loading e mostra i messaggi passati da TerminalService.cast(...) durante l’esecuzione.
  • Esterno: passando la prop isLoading={true}, l’input viene disabilitato e compare uno spinner generico anche senza run interno. Utile quando vuoi bloccare UI durante operazioni di pagina o per eseguire operazioni semplici dal context menu.

Styling / theming

  • CSS di default: @nd-fir/react-terminal/styles.css.
  • Personalizza via options:
<Terminal
  prompt="nebula:~$"
  run={run}
  options={{
    container: { className: "my-term", style: { maxHeight: "28rem" } },
    prompt: { className: "text-emerald-400" },
    response: { className: "text-amber-300" },
  }}
/>

Ricette

1) ContextMenu con PrimeReact

import { ContextMenu } from "primereact/contextmenu";
import type { MenuItem } from "primereact/menuitem";

const cmRef = useRef<ContextMenu | null>(null);
const items: MenuItem[] = [
  { label: "Help", icon: "pi pi-question-circle", command: () => TerminalService.run("help") },
  { label: "Clear", icon: "pi pi-trash", command: () => TerminalService.clear() },
];

<>
  <ContextMenu ref={cmRef} model={items} />
  <Terminal
    prompt="$"
    run={run}
    onContextMenu={(e) => {
      e.preventDefault();
      cmRef.current?.show(e);
    }}
  />
</>;

2) Pipeline async a step con messaggi e spinner

const delay = (ms: number) => new Promise((r) => setTimeout(r, ms));

async function fakeStep(label: string, ms: number) {
  TerminalService.cast(label);
  await delay(ms);
}

const run = async (input: string) => {
  await fakeStep("Recupero dati…", 700);
  await fakeStep("Validazione…", 800);
  await fakeStep("Elaborazione…", 900);
  TerminalService.cast("Aggregazione…");
  await delay(600);
  return "Pipeline completata";
};

3) Comandi programmati all’avvio

useEffect(() => {
  TerminalService.print("", "Benvenuto");
  TerminalService.run("help");
}, []);

4) Blocco input con loading esterno

const [busy, setBusy] = useState(false);

<Terminal prompt="$" run={run} isLoading={busy} />;

Next.js / SSR

  • Il componente è client-only: in file e/o modulo c’è "use client".
  • Importa il CSS nel client (layout o pagina) o applica le tue classi via options.

Accessibilità

  • role="region", aria-label="Terminale".
  • Area loading con role="status" e aria-live="polite".
  • L’input riceve focus automatico; clic sul container rifocalizza l’input.

Troubleshooting

  • Invalid hook call / doppia React → assicurati che react siano installati nel progetto che usa la libreria e che non esistano copie duplicate nella workspace.
  • Niente stile → importa @nd-fir/react-terminal/styles.css o fornisci classi via options.
  • Context menu di sistema → ricorda e.preventDefault() in onContextMenu.

License

MIT