Skip to content

Port to Node.js / React.js / Vite / Redux / Electron #2

@dooglio

Description

@dooglio

Turn Watcher — Conversion Plan: C++/Gtkmm → Node.js/React/Redux/Vite/Electron

What the app does (summary from code analysis)

Turn Watcher is a tabletop RPG combat initiative tracker. It manages:

  • Characters — name, public name, notes, monster/PC flag, hit points (base, temp, damage), stats (abilities, saves, skills), effects
  • Stats/Attributes — a configurable set of stats (STR, DEX, CON, INT, WIS, CHA, Spot, Listen, Will, Level, Initiative, HP) with dice formulas
  • Initiative / Combat Rounds — rolling initiative, sorting, tracking round number and current turn, handling delayed/readied actions, duplicate resolution, "ultra-init" mode
  • Effects — spells/abilities/other timed effects attached to characters, with round countdowns and HP modifications
  • Transactions — full undo/redo stack (add, edit, delete characters; damage; stabilize; bleed-out; move in initiative; roll; sort; etc.)
  • Settings — window positions, rule options (skip dead, alternate death, bleed-out dying, death threshold), font preferences, HUD visibility
  • Persistence — load/save to XML-like property bag files
  • UI Windows — Main window, Edit character, Damage dialog, Initiative-roll dialog, Duplicate-roll resolver, Effects editor, Stat manager, HUD (player-facing view), Preferences, About, Splash

Proposed Technology Decisions

Concern Choice Rationale
App shell Electron Desktop native feel, file-system access for save files
Frontend bundler Vite Fast HMR, first-class React support
UI framework React 18 Component tree maps naturally to the many dialogs/panels
State management Redux Toolkit Replaces the C++ singleton managers + sigc++ signals; slices map directly to CharacterManager, InitiativeManager, StatManager, AppSettings, TransactionManager
Persistence Electron ipcMain + Node fs with JSON save files Replaces molib property bags; simpler than XML, still file-based
Undo/Redo Redux undo middleware (redux-undo) Replaces the C++ TransactionManager stack
Styling CSS Modules + Tailwind CSS Quick, maintainable styles
Language TypeScript throughout Type safety replaces C++ strong typing

Proposed Project Structure

turnwatcher/
├── electron/               # Electron main process
│   ├── main.ts             # App entry, window creation
│   ├── preload.ts          # Context bridge (IPC)
│   └── fileManager.ts      # Load/save JSON files via Node fs
├── src/                    # React renderer process
│   ├── main.tsx            # Vite entry point
│   ├── App.tsx             # Root component, modal routing
│   ├── store/              # Redux store
│   │   ├── index.ts
│   │   ├── slices/
│   │   │   ├── charactersSlice.ts     # CharacterManager
│   │   │   ├── initiativeSlice.ts     # InitiativeManager
│   │   │   ├── statsSlice.ts          # StatManager
│   │   │   ├── settingsSlice.ts       # AppSettings
│   │   │   └── uiSlice.ts             # Modal open/close, selections
│   │   └── middleware/
│   │       └── undoMiddleware.ts      # TransactionManager replacement
│   ├── types/              # TypeScript interfaces (Character, Effect, Stat, …)
│   ├── components/
│   │   ├── MainWindow/     # Main initiative list + toolbar + menus
│   │   ├── CharacterView/  # The sortable initiative table
│   │   ├── HUDWindow/      # Player-facing overlay window
│   │   ├── EditCharacter/  # Character editor dialog
│   │   ├── DamageDialog/   # Apply damage / healing
│   │   ├── InitRollDialog/ # Roll initiative (manual entry)
│   │   ├── EffectsEditor/  # Add/edit/remove effects on a character
│   │   ├── EffectsPanel/   # Effects book panel in main window
│   │   ├── StatManager/    # Configure custom stats
│   │   ├── Preferences/    # App settings dialog
│   │   ├── AboutDialog/    # About box
│   │   └── shared/         # Reusable widgets (SpinBox, LabelField, etc.)
│   └── utils/
│       ├── dice.ts         # Dice rolling logic
│       ├── initiative.ts   # Sorting/ordering algorithms
│       └── health.ts       # HP/health status helpers
├── public/                 # Static assets (icons, splash image)
├── package.json
├── vite.config.ts
├── tsconfig.json
└── electron-builder.json   # Packaging config

Redux State Shape (mirrors the C++ singletons)

{
  characters: {          // CharacterManager
    list: Character[],
    past: Character[][],   // undo history (redux-undo)
    future: Character[][]
  },
  initiative: {          // InitiativeManager
    inRounds: boolean,
    roundNumber: number,
    currentInit: number,
    order: string[],       // character IDs in sorted order
  },
  stats: {               // StatManager
    list: Stat[]
  },
  settings: { ... },     // AppSettings
  ui: {                  // dialog visibility, selection
    selectedCharacterIds: string[],
    openDialog: string | null,
    editingCharacterId: string | null,
  }
}

Implementation Phases

Phase 1 — Project scaffold

  • Initialize Electron + Vite + React + TypeScript project
  • Configure electron-vite (or vite-plugin-electron)
  • Set up Redux Toolkit store with empty slices
  • Set up Electron IPC bridge for file load/save

Phase 2 — Data layer (types + slices)

  • Define TypeScript types for Character, Stat, Effect, AppSettings
  • Implement all Redux slices with full CRUD actions
  • Implement redux-undo wrapping for the characters and initiative slices
  • Implement JSON serialization/deserialization (replacing molib property bags)

Phase 3 — Core logic utilities

  • Dice rolling (d20 + modifier formulas)
  • Initiative sorting (position, sub-position, manual moves, duplicates)
  • Health status computation (Normal, Disabled, Dying, Dead, Stabilized)
  • Effect apply/unapply logic (tempHP, hpBoost, round countdown)

Phase 4 — Main UI components

  • CharacterView — sortable table (drag-and-drop rows for manual initiative moves)
  • MainWindow — toolbar, menus (File/Edit/View/Rounds/Help), status bar
  • HUDWindow — second Electron window (player-facing, filtered view)

Phase 5 — Dialog components

  • EditCharacter — tabs: Base Info, Abilities, Saves, Skills, Effects, Notes
  • DamageDialog — spin-box for damage/healing, full-heal button
  • InitRollDialog — manual initiative entry per character
  • EffectsEditor — add/edit/remove timed effects
  • StatManager — configure custom stats, dice, order, toolbar/HUD visibility
  • Preferences — all AppSettings toggles
  • AboutDialog — splash/about

Phase 6 — File I/O

  • Save/load campaign files as JSON via Electron IPC
  • Import legacy .turnwatcher XML files (optional conversion path)
  • Auto-save on quit

Phase 7 — Polish & packaging

  • Keyboard shortcuts (mirrors existing accelerators)
  • App icon, splash screen
  • electron-builder packaging for macOS/Windows/Linux

Key C++ → JS/TS Mapping

C++ TypeScript/Redux equivalent
sigc::signal Redux actions dispatched; React useSelector subscriptions
Singleton managers Redux slices (single store)
TransactionManager undo/redo stack redux-undo
molib::moPropBag serialization Plain JSON
Gtk::Dialog React modal component + ui slice state
Gtk::TreeView (CharacterView) React table (e.g. TanStack Table) with drag handles
sigc::connection React useEffect cleanup

This is a complete rewrite — the C++ source serves as the specification for behavior, not as code that can be ported mechanically. The existing .turnwatcher save files can optionally be supported as an import path.

Metadata

Metadata

Assignees

No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions