Skip to content

Repository files navigation

⚡ NodeBoard

A high-velocity, local-first narrative and tactical command console designed for tabletop roleplaying Game Masters.

CI License: MIT TypeScript React Tailwind CSS Vite


🎯 Why This Exists

Running modern narrative tabletop RPGs (such as Powered by the Apocalypse, Forged in the Dark, or rules-light investigative systems) creates severe cognitive friction during live sessions:

  1. Information Fragmentation: Game Masters must constantly juggle multiple browser tabs, PDF rulebooks, scratch notes, threat clocks, and character trackers.
  2. Combat Mathematics Friction: Traditional combat trackers force DMs into tracking high-number Hit Points and granular arithmetic, stalling dynamic narrative momentum.
  3. Session State Desynchronization: Passing secrets, tracking active threats, and recording live player dice rolls across physical tables or voice channels often leads to communication bottlenecks.

NodeBoard solves this by unifying preparation and live session management into an ultra-fast, local-first console. It marries segmented threat defeat clocks with an interactive relational web, a mobile-ready player companion portal, and a sub-millisecond deterministic rule engine that reacts to campaign state shifts instantly.


✨ Key Features

  • 🗂️ 3-Tier Command Navigation: Strict structural division separating live play (live), world prep (world), and system configuration (engine).
  • ⚔️ HP-Less Tactical Combat: Track threats using segmented defeat clocks (3, 4, 6, 8, or 12 segments) with interactive heat-map styling, direct +1 Impact / +2 Critical roll integration, and hero effort counters.
  • 🌐 Unified Relational Canvas: Interactive SVG graph with force-directed auto-layout and card-based directory mapping connections between scenes, clues, factions, NPCs, clocks, and heroes.
  • 📱 Mobile-First Player Portal: Dedicated player interface protected by individual PIN passwords, featuring real-time 2d6 PbtA rolling, tactical arena views, and shared secret logs.
  • ⚡ Sub-Millisecond Deterministic Rule Engine: Evaluates active campaign state against customizable trigger rules in under <0.05ms without external network dependencies or LLM latency.
  • 🔒 Local-First Architecture: Zero-config operation with instant tab synchronization via BroadcastChannel and reliable JSON campaign backup/import.

📋 Prerequisites

  • Node.js: >= 18.0.0 (LTS v20.x recommended)
  • npm: >= 9.0.0
  • A modern web browser supporting ES2022 and modern CSS (Chrome, Firefox, Safari, Edge).

🚀 Quickstart

Opción A: Doble Clic (Scripts Inteligentes)

  • Linux / macOS: Ejecuta ./start.sh en tu terminal o con doble clic.
  • Windows: Haz doble clic en start.bat.

El script comprobará Node.js, instalará dependencias si es la primera vez, compilará la versión optimizada, arrancará el servidor y abrirá tu navegador automáticamente en http://localhost:3000.

Opción B: Comandos de Terminal

# 1. Instalar dependencias
npm install

# 2. Servidor de Producción con Persistencia en Disco
npm run serve
# (o para desarrollo con recarga en caliente: npm run dev)

Opción C: Contenedor Docker (Sin instalar Node.js)

docker compose up -d

📱 Conexión de Jugadores en Red Local (Wi-Fi) y Código QR

Para partidas presenciales en la misma mesa:

  1. Conecta los teléfonos o portátiles de los jugadores a la misma red Wi-Fi.
  2. En la consola del DM, haz clic en el botón superior "Compartir con Jugadores".
  3. Se abrirá un modal con un Código QR de alta resolución y la URL de red local (http://192.168.x.x:3000/?view=player).
  4. Los jugadores simplemente apuntan la cámara de su móvil al QR, eligen su personaje e introducen su PIN (por defecto 1234).
  5. Todas las tiradas de dados y cambios se sincronizan en tiempo real y se persisten automáticamente en data/campaign.json.

💡 Usage Example

Evaluating Campaign Rules Programmatically

NodeBoard provides a headless, deterministic rule engine that can evaluate campaign states in node or browser environments:

import { RuleEngine } from './src/engine/RuleEngine';
import { CampaignData } from './src/types';
import sampleCampaign from './sample_campaign.json';

// Initialize the rule engine
const engine = new RuleEngine({ enableLogging: false });

// Evaluate active rules against the campaign state
const report = engine.evaluate(sampleCampaign as CampaignData, 'threat_clock');

console.log(`Evaluated ${report.ruleCount} rules in ${report.executionTimeMs}ms`);

// If conditions were met (e.g. clock filled to 100%), inspect generated actions
for (const action of report.actions) {
  console.log(`Triggered [${action.type}]: ${action.payload.title}`);
}

// Immutably apply actions to the campaign state
const updatedCampaign = engine.applyActions(sampleCampaign as CampaignData, report.actions);

🏗️ Architecture & Project Structure

NodeBoard is organized around a unidirectional data flow with local-first persistence:

nodeboard/
├── .github/                     # GitHub Actions CI workflows & issue templates
├── docs/                        # Specifications & API documentation
│   ├── API_REFERENCE.md         # Engine & algorithm API documentation
│   ├── CAMPAIGN_SPEC.md         # Domain schema definition & field descriptions
│   └── campaign_schema.json     # JSON Schema for CampaignData
├── public/                      # Static assets
├── scripts/                     # Automated test suites, benchmarks, and generator tools
│   ├── test-engine.mjs          # RuleEngine core unit tests
│   ├── test-combat-integration.mjs # Combat flow & defeat clock integration tests
│   ├── test-force-layout.mjs    # Graph positioning layout tests
│   └── test-campaign-migration.mjs # Milestone state transition tests
├── src/
│   ├── components/              # UI components
│   │   ├── tabs/                # Main tab views (Session, Combat, Nodes, Clocks, etc.)
│   │   ├── ui/                  # Reusable modals, drawers, and form controls
│   │   ├── Header.tsx           # Top navigation, status indicators, and export tools
│   │   ├── Sidebar.tsx          # 3-tier navigation menu
│   │   └── PlayerPortal.tsx     # Player-facing mobile interface
│   ├── engine/                  # Core domain logic and deterministic algorithms
│   │   ├── RuleEngine.ts        # Rule evaluation coordinator and benchmark harness
│   │   ├── ruleEvaluator.ts     # Pure condition matching and action generator
│   │   ├── relationshipGraph.ts # Graph topological metrics and tension calculations
│   │   ├── campaignMigration.ts # Session progression and schema validation
│   │   └── graph/               # Visual canvas synchronization
│   ├── types/                   # Master TypeScript domain definitions
│   │   ├── core.ts              # Campaign, Scene, Clock, and NPC definitions
│   │   ├── rules.ts             # Rule engine conditions and triggers
│   │   └── graph.ts             # Visual canvas node & edge interfaces
│   ├── utils/                   # Agnostic mathematical & UI helpers
│   │   ├── diceStats.ts         # 2d6 PbtA probability distributions
│   │   ├── forceLayout.ts       # N-body repulsive graph positioning algorithm
│   │   └── sampleData.ts        # World-agnostic starter campaign dataset
│   ├── App.tsx                  # Global state management and synchronization
│   └── main.tsx                 # React 19 application entry point
├── package.json
├── tsconfig.json
└── vite.config.ts

Data Flow Overview

[User Action / Player Portal Roll]
              │
              ▼
    [React Global State (App.tsx)]
              │
              ├──────► [BroadcastChannel (Multi-tab Sync)]
              ├──────► [localStorage (Local-First Persistence)]
              │
              ▼
   [RuleEngine (In-Memory <0.05ms)]
              │
              ▼
    [Active Triggers & Toasts]

⚙️ Configuration & Environment Variables

NodeBoard is completely local-first and zero-configuration. It does not require any external backend services or credentials.

Variable Type Default Description
PORT number 3000 Local port used by the Vite development server.
HOST string 0.0.0.0 Host binding interface (enables local Wi-Fi sharing).

🧪 Testing

The repository includes a suite of automated unit, integration, and benchmark tests executed directly with tsx:

# Run all test suites
npm test

# Type-check the codebase with strict TypeScript compiler options
npm run lint

# Verify code formatting against Prettier rules
npm run format:check

Test Suites Included:

  • test-engine.mjs: Benchmarks rule evaluation throughput and condition operator accuracy.
  • test-agnostic-engine.mjs: Confirms that rules and triggers operate without setting-specific assumptions.
  • test-combat-integration.mjs: Verifies combat participant additions, defeat clock mutations, and turn tracking.
  • test-campaign-migration.mjs: Ensures historical archiving preserves character progression across scenes.
  • test-force-layout.mjs: Validates repulsive and attractive force physics for graph layouts.
  • test-image-utils.mjs: Tests image fallback and sanitation routines.

🤝 Contributing

Contributions are welcome! Please read CONTRIBUTING.md for details on our code of conduct, branching strategy, commit conventions, and pull request checklist.


📄 License

Distributed under the MIT License. See LICENSE for more information.

About

⚡ High-velocity, local-first narrative & tactical command console for tabletop RPG Game Masters. Features HP-less combat, interactive relational web, dynamic rule engine, and mobile player portal with real-time sync.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages