ts-tui-game-skill
npx skills add https://github.com/kadajett/ts-tui-game-skill --skill ts-tui-game-skill
Agent 安装分布
Skill 文档
TS TUI Game Skill
Build beautiful, performant TUI applications and games with TypeScript and blessed.
Triggers
Use this skill when:
- Creating a new TUI application or game
- Building terminal-based interfaces with blessed
- Implementing game loops, render schedulers, or input handling in terminals
- Designing layouts for terminal applications
- Working with blessed widgets (Box, List, Form, etc.)
- Optimizing terminal rendering performance
- Adding keyboard and mouse input handling
- Creating modals, panels, or complex UI compositions
Keywords: tui, terminal, blessed, ncurses, console, cli game, terminal game, terminal ui, text interface
Core Instructions
When helping with blessed TUI development, follow these principles:
Project Setup
-
Always use TypeScript with strict mode
-
Recommended screen options:
const screen = blessed.screen({ smartCSR: true, // Efficient change-scroll-region rendering autoPadding: true, // Automatic border/padding handling fullUnicode: true, // Support for box drawing and unicode warnings: true, // Development warnings }); -
Directory structure – Separate game logic from UI:
src/ main.ts # Entrypoint, screen init, lifecycle ui/ app.ts # Root composition, layout creation theme.ts # Centralized theme tokens widgets/ # Custom widgets panels/ # Composed windows input/ keymap.ts # Key binding definitions input-router.ts # Focus-aware dispatch game/ state.ts # Authoritative state types reducer.ts # Pure state updates systems/ # Simulation systems engine/ scheduler.ts # Tick + render scheduling events.ts # Event bus
Rendering Rules
CRITICAL: Never call screen.render() directly from event handlers.
Use a render scheduler pattern:
export function createRenderScheduler(screen: blessed.Widgets.Screen, maxFps = 30) {
let pending = false;
const frameMs = Math.floor(1000 / maxFps);
const timer = setInterval(() => {
if (!pending) return;
pending = false;
screen.render();
}, frameMs);
return {
requestRender() { pending = true; },
stop() { clearInterval(timer); }
};
}
State Management
- Single authoritative state – One state object owned by game core
- Pure reducers – Actions in, state out, no side effects
- UI dispatches actions – Never mutate state directly from widgets
Layout Guidelines
Standard 3-pane game layout:
- Main viewport: map/scene (largest, fluid)
- Right sidebar: stats, inventory (24-32 cols fixed)
- Bottom log: messages, hints (7-12 rows fixed)
const sidebarWidth = 30;
const logHeight = 9;
const main = blessed.box({
parent: screen,
top: 0, left: 0,
width: `100%-${sidebarWidth}`,
height: `100%-${logHeight}`,
border: 'line',
});
Theme System
Centralize all colors and styles:
export const theme = {
fg: 'white',
bg: 'black',
panel: { fg: 'white', bg: 'black', border: { fg: '#888888' } },
accent: { fg: 'black', bg: '#f4d03f' },
danger: { fg: 'white', bg: 'red' },
muted: { fg: '#aaaaaa', bg: 'black' },
};
Rule: Pick 1 accent color and 1 danger color. Everything else muted.
Input Handling
Implement a router pattern for context-aware input:
function onKey(ch: string, key: blessed.Widgets.Events.IKeyEventArg) {
const name = key.full || ch;
if (globalKeys[name]) return globalKeys[name]();
if (state.ui.modal) return modalHandler(ch, key);
if (state.ui.activePanel === 'map') return mapHandler(ch, key);
}
screen.on('keypress', onKey);
Standard controls:
- Movement: arrows + hjkl
- Help:
? - Cycle focus:
tab - Confirm:
enter - Back/close:
esc - Quit:
qorC-c
Performance Rules
Hard rules:
- Never
screen.render()in tight loops - Never rebuild widget trees every frame
- Never create new elements every tick without destroying them
Soft rules:
- Update content/styles on existing elements
- One
setContent()per frame for large viewports - Keep tag parsing minimal in grid renders
- Use
alwaysScrollonly where necessary
Modal Pattern
- Create semi-transparent overlay covering screen
- Create centered modal box on top
- Capture input in modal
- On close:
destroy()both, restore focus
Cleanup (REQUIRED)
function safeExit(screen: blessed.Widgets.Screen, code = 0) {
try {
screen.destroy();
} finally {
process.exit(code);
}
}
screen.key(['escape', 'q', 'C-c'], () => safeExit(screen));
Resources
See the resources/ directory for:
- Style Guide – Comprehensive development guidelines
- Widget Reference – Blessed widget API quick reference
See the templates/ directory for starter code.
Best Practices Checklist
When reviewing or creating blessed TUI code, verify:
- One render scheduler controls
screen.render() - Game core is pure and testable (no terminal dependency)
- Layout uses stable proportions and resize behavior
- Theme tokens are centralized
- Controls are discoverable (
?help, footer hints) - Modals capture input and restore focus
- No per-tick widget creation
- Exit path calls
screen.destroy()and clears timers - Tags used sparingly in large grid renders
- Keyboard-first design (mouse is bonus)