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.
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:
Proposed Technology Decisions
ipcMain+ Nodefswith JSON save filesredux-undo)Proposed Project Structure
Redux State Shape (mirrors the C++ singletons)
Implementation Phases
Phase 1 — Project scaffold
electron-vite(orvite-plugin-electron)Phase 2 — Data layer (types + slices)
Character,Stat,Effect,AppSettingsredux-undowrapping for the characters and initiative slicesPhase 3 — Core logic utilities
d20 + modifierformulas)Normal,Disabled,Dying,Dead,Stabilized)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 barHUDWindow— second Electron window (player-facing, filtered view)Phase 5 — Dialog components
EditCharacter— tabs: Base Info, Abilities, Saves, Skills, Effects, NotesDamageDialog— spin-box for damage/healing, full-heal buttonInitRollDialog— manual initiative entry per characterEffectsEditor— add/edit/remove timed effectsStatManager— configure custom stats, dice, order, toolbar/HUD visibilityPreferences— all AppSettings togglesAboutDialog— splash/aboutPhase 6 — File I/O
.turnwatcherXML files (optional conversion path)Phase 7 — Polish & packaging
electron-builderpackaging for macOS/Windows/LinuxKey C++ → JS/TS Mapping
sigc::signaluseSelectorsubscriptionsTransactionManagerundo/redo stackredux-undomolib::moPropBagserializationGtk::Dialoguislice stateGtk::TreeView(CharacterView)sigc::connectionuseEffectcleanupThis is a complete rewrite — the C++ source serves as the specification for behavior, not as code that can be ported mechanically. The existing
.turnwatchersave files can optionally be supported as an import path.