{"_id":"@averagejoeslab/tui","_rev":"2-5acaf28734ee78fb21d3123252e2fe71","name":"@averagejoeslab/tui","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@averagejoeslab/tui","version":"1.0.0","keywords":["tui","terminal","cli","elm","architecture","ui","framework"],"author":{"name":"Average Joe's Lab"},"license":"MIT","_id":"@averagejoeslab/tui@1.0.0","maintainers":[{"name":"chasedovey","email":"chase@odovey.com"}],"homepage":"https://github.com/averagejoeslab/tui#readme","bugs":{"url":"https://github.com/averagejoeslab/tui/issues"},"dist":{"shasum":"c950f9d67a65f19871e95b26f23b1b5f2a249481","tarball":"https://registry.npmjs.org/@averagejoeslab/tui/-/tui-1.0.0.tgz","fileCount":7,"integrity":"sha512-3/052ybpL7LcmgDiIvhBqBm4jaWCEwlT51BOVkullfIG4qQiGNPpuQd/d/eKRbrfd7yqKmB+F8u1VKV8hH2qAA==","signatures":[{"sig":"MEYCIQC1/lNhBVUbJOcTFvTBBuoN84I70kbMe8nbgGbQHVj5sgIhAID4LgVg8xOrQlm3/R/qdRowi78I+MJFfxsQw+ox0CyB","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":74017},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"7b7bfaf6e869fc2dbb8ce1eab903ee97408ba6e4","scripts":{"test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"chasedovey","email":"chase@odovey.com"},"repository":{"url":"git+https://github.com/averagejoeslab/tui.git","type":"git"},"_npmVersion":"11.5.2","description":"Terminal user interface framework based on the Elm Architecture","directories":{},"_nodeVersion":"22.16.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.1","vitest":"^1.0.0","typescript":"^5.3.0","@types/node":"^20.10.0"},"_npmOperationalInternal":{"tmp":"tmp/tui_1.0.0_1770014884886_0.710686249087368","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@averagejoeslab/tui","version":"1.0.1","description":"Terminal user interface framework based on the Elm Architecture","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"build":"tsup src/index.ts --format cjs,esm --dts","test":"vitest run","test:watch":"vitest","prepublishOnly":"npm run build"},"keywords":["tui","terminal","cli","elm","architecture","ui","framework"],"author":{"name":"Average Joe's Lab"},"license":"MIT","devDependencies":{"@types/node":"^20.10.0","tsup":"^8.0.1","typescript":"^5.3.0","vitest":"^1.0.0"},"repository":{"type":"git","url":"git+https://github.com/averagejoeslab/tui.git"},"engines":{"node":">=18"},"_id":"@averagejoeslab/tui@1.0.1","gitHead":"7b7bfaf6e869fc2dbb8ce1eab903ee97408ba6e4","bugs":{"url":"https://github.com/averagejoeslab/tui/issues"},"homepage":"https://github.com/averagejoeslab/tui#readme","_nodeVersion":"22.16.0","_npmVersion":"11.5.2","dist":{"integrity":"sha512-MXYwjf7Wvb6u4aHP9O0DZnE4wfje3b4Fj0SFReByV25tjKDXI3KBElXrmFhFTUJlvAEA6gTK/9CimFgwvgF5sQ==","shasum":"c7fc987d8cfb9dbaf3e5f7fb97286383239296a5","tarball":"https://registry.npmjs.org/@averagejoeslab/tui/-/tui-1.0.1.tgz","fileCount":7,"unpackedSize":74017,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDf7sn0OCMkdIhEcrK7NqOIy5PzuxBui4yuKObWr8V+VgIhAN4ojDXESjw91hbQNvOuvthjpaQE7VhvkLH9G5NGYEIt"}]},"_npmUser":{"name":"chasedovey","email":"chase@odovey.com"},"directories":{},"maintainers":[{"name":"chasedovey","email":"chase@odovey.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/tui_1.0.1_1770018699811_0.16513549841560082"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-02T06:48:04.824Z","modified":"2026-02-02T07:51:40.096Z","1.0.0":"2026-02-02T06:48:05.039Z","1.0.1":"2026-02-02T07:51:39.960Z"},"bugs":{"url":"https://github.com/averagejoeslab/tui/issues"},"author":{"name":"Average Joe's Lab"},"license":"MIT","homepage":"https://github.com/averagejoeslab/tui#readme","keywords":["tui","terminal","cli","elm","architecture","ui","framework"],"repository":{"type":"git","url":"git+https://github.com/averagejoeslab/tui.git"},"description":"Terminal user interface framework based on the Elm Architecture","maintainers":[{"name":"chasedovey","email":"chase@odovey.com"}],"readme":"# @puppuccino/tui\n\nTerminal UI framework using the Elm Architecture.\n\n## Installation\n\n```bash\nnpm install @puppuccino/tui\n```\n\nOr install from GitHub:\n\n```bash\nnpm install github:averagejoeslab/tui\n```\n\n## Features\n\n- **Elm Architecture** - Model-Update-View pattern for predictable state management\n- **Message-Based Updates** - Type-safe messages drive all state changes\n- **Command System** - Handle async operations cleanly\n- **Built-in Messages** - Key, mouse, window resize, focus, paste events\n- **Input Handling** - Full keyboard support with modifier keys\n- **Terminal Features** - Alternate screen, mouse mode, bracketed paste\n\n## Quick Start\n\n```typescript\nimport { run, Model, Cmd, Key, KeyMsg, isKeyMsg, quit } from '@puppuccino/tui';\n\n// Define your message types\ntype Msg = KeyMsg | { type: 'tick' };\n\n// Define your model\nclass Counter implements Model<Msg> {\n  constructor(private count: number = 0) {}\n\n  init(): Cmd<Msg> {\n    return null;  // No initial command\n  }\n\n  update(msg: Msg): [Model<Msg>, Cmd<Msg>] {\n    if (isKeyMsg(msg)) {\n      if (msg.key === 'q' || msg.isCtrl('c')) {\n        return [this, quit()];\n      }\n      if (msg.key === Key.Up) {\n        return [new Counter(this.count + 1), null];\n      }\n      if (msg.key === Key.Down) {\n        return [new Counter(this.count - 1), null];\n      }\n    }\n    return [this, null];\n  }\n\n  view(): string {\n    return `Count: ${this.count}\\n\\n↑/↓ to change, q to quit`;\n  }\n}\n\n// Run the program\nrun(new Counter());\n```\n\n## Core Concepts\n\n### Model\n\nThe Model interface defines your application's state and behavior:\n\n```typescript\ninterface Model<M extends Msg> {\n  init(): Cmd<M>;                         // Initial command\n  update(msg: M): [Model<M>, Cmd<M>];     // State transition\n  view(): string;                          // Render to string\n}\n```\n\n### Messages\n\nMessages are events that trigger state updates:\n\n```typescript\nimport {\n  KeyMsg,          // Keyboard input\n  MouseMsg,        // Mouse events\n  WindowSizeMsg,   // Terminal resize\n  FocusMsg,        // Focus gained/lost\n  PasteMsg,        // Pasted text\n  TickMsg,         // Timer tick\n  CustomMsg,       // User-defined messages\n} from '@puppuccino/tui';\n\n// Check message types\nif (isKeyMsg(msg)) {\n  console.log(`Key pressed: ${msg.key}`);\n  console.log(`Modifiers: ctrl=${msg.ctrl} alt=${msg.alt} shift=${msg.shift}`);\n}\n```\n\n### Commands\n\nCommands handle side effects and async operations:\n\n```typescript\nimport {\n  quit,           // Exit the program\n  none,           // Do nothing\n  tick,           // Timer that sends a message\n  tickMsg,        // Timer with TickMsg\n  timeout,        // One-shot timer\n  exec,           // Execute async function\n  batch,          // Run multiple commands\n} from '@puppuccino/tui';\n\n// Timer example\nupdate(msg: Msg): [Model<Msg>, Cmd<Msg>] {\n  if (msg.type === 'start') {\n    return [this, tickMsg(1000, 'timer-id')];\n  }\n  return [this, null];\n}\n\n// Async operation\nconst loadData: Cmd<DataMsg> = exec(\n  () => fetch('/api/data').then(r => r.json()),\n  (data) => ({ type: 'dataLoaded', data }),\n  (error) => ({ type: 'error', error })\n);\n```\n\n## Built-in Messages\n\n### KeyMsg\n\n```typescript\nimport { KeyMsg, Key, isKeyMsg } from '@puppuccino/tui';\n\n// In your update function\nif (isKeyMsg(msg)) {\n  // Check specific keys\n  if (msg.key === Key.Enter) { /* ... */ }\n  if (msg.key === Key.Escape) { /* ... */ }\n  if (msg.key === Key.Up) { /* ... */ }\n\n  // Check with modifiers\n  if (msg.isCtrl('c')) { /* Ctrl+C */ }\n  if (msg.isAlt('x')) { /* Alt+X */ }\n\n  // Check modifiers directly\n  if (msg.ctrl && msg.key === 's') { /* Ctrl+S */ }\n}\n```\n\nAvailable keys:\n- Control: `Enter`, `Tab`, `Backspace`, `Escape`, `Space`, `Delete`\n- Arrows: `Up`, `Down`, `Left`, `Right`\n- Navigation: `Home`, `End`, `PageUp`, `PageDown`, `Insert`\n- Function: `F1` - `F12`\n\n### WindowSizeMsg\n\n```typescript\nimport { WindowSizeMsg, isWindowSizeMsg } from '@puppuccino/tui';\n\nif (isWindowSizeMsg(msg)) {\n  console.log(`Terminal size: ${msg.width}x${msg.height}`);\n}\n```\n\n### PasteMsg\n\n```typescript\nimport { PasteMsg, isPasteMsg } from '@puppuccino/tui';\n\nif (isPasteMsg(msg)) {\n  console.log(`Pasted text: ${msg.text}`);\n}\n```\n\n## Program Options\n\n```typescript\nimport { run, ProgramOptions } from '@puppuccino/tui';\n\nconst options: ProgramOptions = {\n  altScreen: true,       // Use alternate screen buffer\n  mouse: true,           // Enable mouse support\n  bracketedPaste: true,  // Enable bracketed paste mode\n  reportFocus: true,     // Report focus changes\n  fps: 60,               // Render frame rate\n  title: 'My App',       // Window title\n};\n\nrun(model, options);\n```\n\n## Advanced Usage\n\n### Custom Messages\n\n```typescript\nimport { CustomMsg, isCustomMsg } from '@puppuccino/tui';\n\n// Send custom messages\nconst cmd = send('userLoaded', { id: 1, name: 'Alice' });\n\n// Check for custom messages\nif (isCustomMsg<User>(msg, 'userLoaded')) {\n  console.log(msg.payload.name);  // Type-safe access\n}\n```\n\n### Batch Commands\n\n```typescript\nimport { batch, tick, exec } from '@puppuccino/tui';\n\n// Run multiple commands together\nconst cmd = batch(\n  tick(100, () => ({ type: 'tick' })),\n  exec(loadData, onSuccess, onError)\n);\n```\n\n### Mapping Messages\n\n```typescript\nimport { map } from '@puppuccino/tui';\n\n// Transform a command's message type\nconst mappedCmd = map(\n  childCmd,\n  (childMsg) => ({ type: 'childMessage', msg: childMsg })\n);\n```\n\n### Interval Timer\n\n```typescript\nimport { every } from '@puppuccino/tui';\n\n// Create a repeating timer\nconst [cmd, cancel] = every(1000, (time) => ({ type: 'tick', time }));\n\n// Later, cancel the timer\ncancel();\n```\n\n## Complete Example\n\n```typescript\nimport {\n  run,\n  Model,\n  Cmd,\n  Key,\n  KeyMsg,\n  TickMsg,\n  isKeyMsg,\n  isTickMsg,\n  tickMsg,\n  batch,\n  quit,\n} from '@puppuccino/tui';\n\ntype Msg = KeyMsg | TickMsg;\n\nclass App implements Model<Msg> {\n  constructor(\n    private time: Date = new Date(),\n    private running: boolean = true\n  ) {}\n\n  init(): Cmd<Msg> {\n    return tickMsg(1000, 'clock');\n  }\n\n  update(msg: Msg): [Model<Msg>, Cmd<Msg>] {\n    if (isKeyMsg(msg)) {\n      if (msg.key === 'q') return [this, quit()];\n      if (msg.key === ' ') {\n        return [new App(this.time, !this.running), null];\n      }\n    }\n\n    if (isTickMsg(msg) && msg.id === 'clock') {\n      const nextCmd = this.running ? tickMsg(1000, 'clock') : null;\n      return [new App(new Date(), this.running), nextCmd];\n    }\n\n    return [this, null];\n  }\n\n  view(): string {\n    const status = this.running ? '▶ Running' : '⏸ Paused';\n    return `\n  ╭─────────────────────────╮\n  │  ${this.time.toLocaleTimeString().padEnd(19)} │\n  │  ${status.padEnd(21)} │\n  ╰─────────────────────────╯\n\n  Space: pause/resume  Q: quit\n`;\n  }\n}\n\nrun(new App(), { altScreen: true });\n```\n\n## API Reference\n\n### Core\n\n- `run(model, options?)` - Run a TUI program\n- `create(model, options?)` - Create program without running\n- `Program` - Program class with `run()`, `send()`, `quit()` methods\n\n### Types\n\n- `Model<M>` - Model interface\n- `Cmd<M>` - Command type\n- `Msg` - Base message type\n- `ProgramOptions` - Configuration options\n\n### Messages\n\n- `QuitMsg` - Quit the program\n- `KeyMsg` - Keyboard input\n- `WindowSizeMsg` - Terminal resize\n- `FocusMsg` - Focus change\n- `MouseMsg` - Mouse event\n- `PasteMsg` - Pasted text\n- `TickMsg` - Timer tick\n- `ErrorMsg` - Error occurred\n- `CustomMsg<T>` - Custom message\n\n### Commands\n\n- `quit()` - Exit program\n- `none()` - No operation\n- `tick(ms, fn)` - Timer\n- `tickMsg(ms, id?)` - Timer with TickMsg\n- `timeout(ms, msg)` - One-shot timer\n- `every(ms, fn)` - Repeating timer\n- `exec(fn, success, error?)` - Async operation\n- `send(tag, payload)` - Send custom message\n- `batch(...cmds)` - Combine commands\n- `map(cmd, fn)` - Transform message\n\n### Keys\n\n- `Key` - Key name constants\n- `parseKey(data)` - Parse key from input\n- `matches(key, name, modifiers?)` - Match key\n\n## License\n\nMIT\n","readmeFilename":"README.md"}