
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
nm-cli-screen-system
Advanced tools
Action-based terminal UI system built on Ink/React with auto-grouped footers, dynamic captions, and reusable components
A declarative, action-based terminal UI system built on Ink/React.
✨ Action-Based - Separate what keys do from which keys to listen for
🎯 Auto-Grouping - Footer automatically groups keys: ↑↓←→ to navigate
📦 Reusable Components - Self-contained list, grid, and preview components
🎨 Dynamic Captions - Footer updates based on state (e.g., "PLAYING")
⌨️ Smart Navigation - Left arrow on first item goes back
🔄 Position Memory - Remember scroll position when navigating back
📐 Responsive - Adapts to terminal width automatically
npm install nm-cli-screen-system
Peer Dependencies: ink ^4.0.0, react ^18.0.0
import { showListScreen } from 'nm-cli-screen-system';
const choice = await showListScreen({
title: 'Main Menu',
items: [
{ name: 'Start Game', value: 'start' },
{ name: 'Settings', value: 'settings' },
{ name: 'Exit', value: 'exit' }
],
onSelect: (value) => value
});
import { showMultiColumnListScreen } from 'nm-cli-screen-system';
const word = await showMultiColumnListScreen({
title: 'Select a Word',
items: ['apple', 'banana', 'cherry', 'date', ...],
onSelect: (word) => word
});
import { showMultiColumnListWithPreviewScreen } from 'nm-cli-screen-system';
const word = await showMultiColumnListWithPreviewScreen({
title: 'Words',
items: ['nefarious', 'ephemeral', 'ubiquitous'],
getPreviewContent: (word) => `Definition: ...`,
onSelect: (word) => word
});
┌─────────────────────────────────────────────┐
│ ← Menu ← Section │ Title (breadcrumb)
├─────────────────────────────────────────────┤
│ │
│ Body Content │
│ (Lists, grids, custom components) │
│ │
├─────────────────────────────────────────────┤
│ ↑↓ to navigate, enter to select, esc to go back │ Auto-generated
│ Total: 50 items │ Custom footer
└─────────────────────────────────────────────┘
Define what happens when keys are pressed:
ctx.setAction('playAudio', async () => {
await playSound();
});
Define which keys trigger which actions:
ctx.setKeyBinding({
key: 'p',
caption: 'play sound',
action: 'playAudio'
});
Keys with the same caption are grouped:
// These four bindings...
{ key: 'upArrow', caption: 'navigate', action: 'moveUp' }
{ key: 'downArrow', caption: 'navigate', action: 'moveDown' }
{ key: 'leftArrow', caption: 'navigate', action: 'moveLeft' }
{ key: 'rightArrow', caption: 'navigate', action: 'moveRight' }
// ...become one footer entry:
"↑↓←→ to navigate"
showScreen(config)Base screen with full control over content and bindings.
showListScreen(config)Single-column menu with up/down navigation.
showMultiColumnListScreen(config)Multi-column grid with 4-way arrow navigation.
showMultiColumnListWithPreviewScreen(config)Grid layout with live preview panel below that updates as you navigate.
ListComponentVertical list with built-in navigation.
MultiColumnListComponentMulti-column grid with 4-way navigation and smart left-arrow (goes back when at first item).
MultiColumnListWithPreviewComponentGrid plus preview panel that shows details of the selected item.
See examples/basic-spike.js for a complete demo featuring:
Run it:
node packages/screenSystem/examples/basic-spike.js
Methods available in onRender(ctx):
| Method | Description |
|---|---|
setAction(name, fn) | Define an action handler |
setKeyBinding(binding) | Add/update key binding(s) |
updateKeyBinding(key, updates) | Update existing binding |
addFooter(item) | Add custom footer text |
setFooter(items) | Replace all custom footer items |
clearFooter() | Clear custom footer |
update() | Trigger screen re-render |
goBack() | Trigger back action |
close(result) | Close screen with result |
Captions can be functions that return strings or styled components:
ctx.setKeyBinding({
key: 'p',
caption: () => {
if (isPlaying) {
return h(Text, { color: 'black', backgroundColor: 'green' }, ' PLAYING ');
}
return 'play sound';
},
action: 'playAudio'
});
Result:
p to play soundPLAYING (black on green background)Screens remember their scroll position:
let savedIndex = 0;
while (true) {
const item = await showMultiColumnListScreen({
items: myItems,
initialSelectedIndex: savedIndex,
onSelect: (item, index) => {
savedIndex = index; // Remember position
return item;
}
});
if (!item) break;
await showDetail(item);
// Returns to same position in list
}
In multi-column lists, pressing left arrow when on the first item goes back to the parent screen - natural and intuitive UX.
# Install dependencies
npm install
# Run demo
node examples/basic-spike.js
# Generate sample data
node examples/generate-sample-words.js
# Download audio pronunciations
node examples/download-sounds-gtts.js
MIT
FAQs
Action-based terminal UI system built on Ink/React with auto-grouped footers, dynamic captions, and reusable components
We found that nm-cli-screen-system demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.