# Shadowdark Character Sheet Manager — Design Spec

## Overview

A web app for managing Shadowdark RPG character sheets in real-time. Players in a campaign can view and edit characters simultaneously, with changes syncing live across all connected clients. Hosted locally and exposed via ngrok for group access.

## Tech Stack

- **Frontend:** React + Vite (TypeScript)
- **Backend:** Node.js + Express + Socket.IO
- **Database:** SQLite (single file, zero config)
- **Project location:** `/Users/aaron.wood/workspace/shadowdark/`
- **Structure:** `client/` and `server/` directories

## Data Model

### campaigns

| Column     | Type    | Notes                      |
| ---------- | ------- | -------------------------- |
| id         | INTEGER | Primary key, autoincrement |
| name       | TEXT    | Campaign name              |
| created_by | TEXT    | For future auth            |
| created_at | TEXT    | ISO timestamp              |

### characters

| Column      | Type    | Notes                                              |
| ----------- | ------- | -------------------------------------------------- |
| id          | INTEGER | Primary key, autoincrement                         |
| campaign_id | INTEGER | FK to campaigns                                    |
| created_by  | TEXT    | For future auth                                    |
| name        | TEXT    | Character name                                     |
| class       | TEXT    | Fighter / Priest / Thief / Wizard                  |
| ancestry    | TEXT    | Human / Elf / Dwarf / Halfling / Goblin / Half-Orc |
| level       | INTEGER | Default 1                                          |
| xp          | INTEGER | Default 0                                          |
| hp_current  | INTEGER |                                                    |
| hp_max      | INTEGER |                                                    |
| ac          | INTEGER |                                                    |
| alignment   | TEXT    | Lawful / Neutral / Chaotic                         |
| title       | TEXT    | Optional, e.g. "the Brave"                         |
| notes       | TEXT    | Freeform notes                                     |

### character_stats

| Column       | Type    | Notes                             |
| ------------ | ------- | --------------------------------- |
| character_id | INTEGER | FK to characters                  |
| stat_name    | TEXT    | STR / DEX / CON / INT / WIS / CHA |
| value        | INTEGER | Raw score (3-18 typically)        |

One row per stat per character. Ability modifiers are derived, not stored:

| Score | Modifier |
| ----- | -------- |
| 1-3   | -4       |
| 4-5   | -3       |
| 6-7   | -2       |
| 8-9   | -1       |
| 10-11 | +0       |
| 12-13 | +1       |
| 14-15 | +2       |
| 16-17 | +3       |
| 18    | +4       |

### character_gear

| Column       | Type    | Notes                                                 |
| ------------ | ------- | ----------------------------------------------------- |
| id           | INTEGER | Primary key, autoincrement                            |
| character_id | INTEGER | FK to characters                                      |
| name         | TEXT    | Item name                                             |
| type         | TEXT    | weapon / armor / gear / spell                         |
| slot_count   | INTEGER | Gear slots used (Shadowdark inventory system)         |
| properties   | TEXT    | JSON — e.g. `{"damage":"1d6","melee":true,"bonus":1}` |

Spells are stored as gear entries with `type: "spell"` and a `tier` property in the JSON.

### character_talents

| Column       | Type    | Notes                                     |
| ------------ | ------- | ----------------------------------------- |
| id           | INTEGER | Primary key, autoincrement                |
| character_id | INTEGER | FK to characters                          |
| name         | TEXT    | Talent name                               |
| description  | TEXT    | Flavor/rules text                         |
| effect       | TEXT    | JSON — mechanical bonuses for dice roller |

## UI Layout

### Campaign List (home page)

- Grid/list of existing campaigns
- "+ New Campaign" button
- Click a campaign to enter it

### Campaign View (main screen)

- Campaign name header
- Responsive character card grid:
  - Desktop: 4 cards across
  - Tablet: 2 cards across
  - Mobile: 1 card, stacked
- Cards flow naturally — 6 characters = 4 + 2 rows on desktop, just scroll
- "+ Add Character" button
- Future: dice log panel (right side on desktop, bottom drawer on mobile)

### Character Card (in the grid)

- Name, class, ancestry, level header
- HP bar with +/- buttons (current / max)
- AC display
- Six stats in compact 3x2 layout: score, modifier, +/- buttons
- Gear summary (slots used / total)
- Click to expand into full detail view

### Character Detail (expanded / modal)

- Full stat editing with +/- buttons
- Gear/inventory management: add/remove items with type and properties
- Talents list: add/remove with name, description, effect
- Spell list (for Priests/Wizards — gear items with type "spell")
- Notes freeform text field
- Future: dice roll buttons next to weapons/spells

## Real-time Sync

- Client joins a Socket.IO room per campaign (room ID = campaign ID)
- Mutation flow: client -> server API -> save to SQLite -> broadcast to room
- Other clients receive update and patch local state (no full reload)
- Optimistic UI: editing client updates immediately, server confirms
- On reconnect, client fetches latest full state from server

## Auth (v1: none, future-ready)

- v1 is open free-for-all: anyone with the link can create/edit anything
- `created_by` fields exist in the schema for future use
- Migration path to light auth (name/pin per character) or GM controls requires adding middleware, not restructuring

## Future: Dice Rolling (not in v1)

Design accommodations for future dice rolling:

- **Shared dice log panel** — layout reserves space for it
- **Inline roll buttons** — next to weapons/spells and stats
- **Server-side rolls** — server calculates d20 + ability modifier + gear/talent bonuses, broadcasts result
- **Log entries** — show who rolled, what for, full breakdown (e.g. "Kira attacks with Longsword +1: d20(14) + STR(+2) + weapon(+1) = 17"), and timestamp
- **Data model supports it** — `character_gear.properties` and `character_talents.effect` JSON fields carry the mechanical data the roller needs

## Deployment

- Run `server/` on localhost with a configured port
- `client/` built and served by Express (or Vite dev server during development)
- Expose via ngrok for friends to access
- SQLite file lives in `server/data/shadowdark.db`

## Out of Scope for v1

- Authentication / permissions
- Dice rolling
- Character import/export
- Multiple themes / dark mode
- Mobile native app
