Compare commits

...

No commits in common. "ffde8b1863477685e8ab1f87afd0b16a2ca8ca77" and "73ef86ceac67d3dc0b89b586ca216b9c1958c2fa" have entirely different histories.

22 changed files with 6475 additions and 18 deletions

37
.gitignore vendored
View file

@ -1,15 +1,11 @@
# ---> Node
# Logs # Logs
logs logs
*.log *.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data # Runtime data
pids pids
@ -55,9 +51,6 @@ web_modules/
# Optional eslint cache # Optional eslint cache
.eslintcache .eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache # Microbundle cache
.rpt2_cache/ .rpt2_cache/
.rts2_cache_cjs/ .rts2_cache_cjs/
@ -73,7 +66,7 @@ web_modules/
# Yarn Integrity file # Yarn Integrity file
.yarn-integrity .yarn-integrity
# dotenv environment variable files # dotenv environment variables file
.env .env
.env.development.local .env.development.local
.env.test.local .env.test.local
@ -86,7 +79,6 @@ web_modules/
# Next.js build output # Next.js build output
.next .next
out
# Nuxt.js build / generate output # Nuxt.js build / generate output
.nuxt .nuxt
@ -101,13 +93,6 @@ dist
# vuepress build output # vuepress build output
.vuepress/dist .vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories # Serverless directories
.serverless/ .serverless/
@ -130,3 +115,21 @@ dist
.yarn/install-state.gz .yarn/install-state.gz
.pnp.* .pnp.*
# Vite
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# System files
Thumbs.db

64
.opencode-config.json Normal file
View file

@ -0,0 +1,64 @@
{
"project": {
"name": "bms-forge",
"description": "BattleMap Notation Schema (BMS) renderer for DnD/TTRPG maps",
"version": "0.1.0",
"repository": "https://git.bouncypixel.com/helm/bms-forge.git",
"deploymentUrl": "maps.bouncypixel.com"
},
"userPreferences": {
"codingStyle": {
"typescript": "strict-types-minimal-anys",
"react": "pragmatic-balance",
"stateManagement": "context-first-upgrade-if-needed",
"errorHandling": "graceful-degradation-with-optional-libs",
"testing": "critical-paths-first-then-expand"
},
"architecture": {
"componentStructure": "type-based-with-domain-subfolders",
"styling": "tailwind-hybrid-with-css-modules",
"buildTool": "vite",
"canvasLibrary": "konva",
"validation": "zod",
"yamlParsing": "js-yaml",
"editor": "monaco"
}
},
"projectContext": {
"phase": "implementation",
"currentFocus": "project-initialized",
"decisionsMade": [
"Frontend-only MVP with React + TypeScript",
"Use BMS 1.0 schema as defined in docs/",
"Paperly style as default free tier",
"Texture packs as future monetization",
"AI integration as subscription feature",
"Initial deployment to maps.bouncypixel.com"
],
"nextSteps": [
"Create BMS TypeScript interfaces",
"Implement YAML parser and validator",
"Build basic rendering engine",
"Create editor and preview components",
"Implement export functionality"
],
"keyFiles": [
"docs/PROJECT_PLAN.md",
"docs/BMS_SCHEMA_SPEC.md",
"docs/IMPLEMENTATION_ROADMAP.md",
"docs/TECHNICAL_ARCHITECTURE.md",
".opencode-config.json"
]
},
"sessionNotes": {
"lastUpdated": "2026-03-19",
"developerProfile": "Senior software engineer and manager of small dev team. Prefers pragmatic TypeScript with strict types but willing to use 'any' when needed. State management depends on complexity. Open to functional error handling libraries but aware of learning curve. Tests critical paths first, expands coverage after features stabilize.",
"projectGoals": [
"V1: Frontend-only BMS renderer with Paperly style",
"Free forever for basic functionality",
"Monetize via texture packs and AI subscriptions",
"Build community around BMS schema format",
"Potential future VTT integration"
]
}
}

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Lukas Parsons
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

108
QUICK_START.md Normal file
View file

@ -0,0 +1,108 @@
# BMS Forge - Quick Start Guide
## Project Context
### What is this?
A web-based BattleMap Notation Schema (BMS) renderer for DnD/TTRPG maps.
### Current Phase: Planning → Implementation
- **V1 Goal**: Frontend-only BMS renderer with "Paperly" style
- **Deployment**: maps.bouncypixel.com (alpha)
- **Tech Stack**: React + TypeScript + Konva.js + Zod + js-yaml
### Developer Preferences (Lukas)
- **TypeScript**: Strict types but pragmatic about `any`
- **State Management**: Context first, upgrade if needed
- **Error Handling**: Graceful degradation, open to functional libs
- **Testing**: Critical paths first, expand later
- **Components**: Type-based with domain subfolders
- **Styling**: Tailwind hybrid with CSS modules
- **Build**: Vite
## Getting Started (After Break)
1. **Check context**: Read `.opencode-config.json` and `docs/`
2. **Review decisions**: BMS schema format, tech stack, architecture
3. **Continue from**: Last completed task in roadmap
## Key Decisions Made
### Architecture
- **Frontend-only MVP**: No backend needed initially
- **BMS 1.0 Schema**: Defined in `docs/BMS_SCHEMA_SPEC.md`
- **Rendering Engine**: Konva.js with layered rendering
- **Validation**: Zod for runtime schema validation
- **Editor**: Monaco Editor for YAML editing
### Business Model
- **Free Tier**: Paperly style rendering
- **Monetization**: Texture packs + AI subscription
- **Community**: BMS schema as sharing format
### Deployment
- **Alpha**: maps.bouncypixel.com
- **Version Control**: Private Forgejo at git.bouncypixel.com
- **Future**: Potential custom domain (bms-forge.com)
## Next Immediate Actions
Based on current roadmap (`docs/IMPLEMENTATION_ROADMAP.md`):
### Week 1, Days 1-2: Project Setup
```bash
npm create vite@latest bms-forge -- --template react-ts
cd bms-forge
npm install konva react-konva js-yaml zod @monaco-editor/react
npm install tailwindcss postcss autoprefixer
npm install -D @types/js-yaml @types/node
```
### Project Structure to Create
```
src/
├── types/ # BMS TypeScript interfaces
├── schema/ # Zod validation schemas
├── parser/ # YAML parsing utilities
├── renderer/ # Konva.js rendering engine
├── components/ # Type-based with domains
│ ├── layout/ # Header, Sidebar, etc.
│ ├── canvas/ # Map rendering components
│ ├── editor/ # YAML editor components
│ └── shared/ # Common UI components
├── hooks/ # Custom React hooks
├── utils/ # Helper functions
└── App.tsx
```
## When You Return
1. **Check status**: What was last completed?
2. **Read decisions**: Review config and docs
3. **Pick up**: Continue from next logical step
4. **Ask**: "What should we work on next?"
## Common Commands
```bash
# Development
npm run dev
# Build
npm run build
# Type checking
npm run type-check
# Linting
npm run lint
```
## Questions to Consider When Continuing
1. **Parsing complete?** Can we parse BMS YAML yet?
2. **Rendering working?** Are rooms showing on canvas?
3. **UI usable?** Editor + preview layout functional?
4. **Testing needed?** Critical paths covered?
---
*Last Updated: 2026-03-19*
*Project: BMS Forge - BattleMap Generator*

179
README.md
View file

@ -1,2 +1,179 @@
# bms-forge # BMS Forge
> BattleMap Notation Schema (BMS) Renderer for DnD/TTRPG Maps
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)](https://www.typescriptlang.org/)
[![React](https://img.shields.io/badge/React-19-blue)](https://reactjs.org/)
[![Vite](https://img.shields.io/badge/Vite-6.0-purple)](https://vitejs.dev/)
A web-based tool for rendering and editing DnD/TTRPG battlemaps using the structured [Battlemap Notation Schema (BMS)](docs/BMS_SCHEMA_SPEC.md) format. Create, share, and customize battlemaps with a clean, semantic YAML-based format.
## ✨ Features
- **BMS Schema Support**: Parse and validate BMS 1.0 YAML files
- **Real-time Rendering**: Instant visual feedback as you edit
- **"Paperly" Style**: Clean, vector-style default theme (always free)
- **Export Options**: PNG, JSON, and BMS YAML formats
- **Future Plans**: Texture packs, AI generation, VTT integration
## 🚀 Quick Start
### Prerequisites
- Node.js 18+ and npm/yarn/pnpm
### Installation
```bash
# Clone the repository
git clone ssh://git@git.bouncypixel.com:222/helm/bms-forge.git
cd bms-forge
# Install dependencies
npm install
# Start development server
npm run dev
```
Visit `http://localhost:3000` to see the app in action.
### Build for Production
```bash
npm run build
npm run preview
```
## 📁 Project Structure
```
bms-forge/
├── src/
│ ├── types/ # BMS TypeScript interfaces
│ ├── schema/ # Zod validation schemas
│ ├── parser/ # YAML parsing utilities
│ ├── renderer/ # Konva.js rendering engine
│ │ ├── layers/ # Individual layer renderers
│ │ └── styles/ # Paperly + texture pack styles
│ ├── components/ # React components (type-based)
│ │ ├── layout/ # Layout components
│ │ ├── canvas/ # Map rendering components
│ │ ├── editor/ # YAML editor components
│ │ └── shared/ # Common UI components
│ ├── hooks/ # Custom React hooks
│ ├── utils/ # Helper functions
│ ├── App.tsx
│ └── main.tsx
├── docs/ # Project documentation
├── public/ # Static assets
└── package.json
```
## 🛠️ Technology Stack
- **Frontend**: React 19 + TypeScript
- **Build Tool**: Vite
- **Canvas Rendering**: Konva.js
- **Validation**: Zod
- **YAML Parsing**: js-yaml
- **Code Editor**: Monaco Editor
- **Styling**: Tailwind CSS + CSS Modules
- **Linting**: ESLint + TypeScript ESLint
## 📋 Development
### Available Scripts
```bash
npm run dev # Start development server
npm run build # Build for production
npm run preview # Preview production build
npm run lint # Run ESLint
npm run type-check # TypeScript type checking
```
### Code Style
- TypeScript with strict mode (pragmatic about `any`)
- Functional components with hooks
- Type-based component organization
- Tailwind utilities for layout, CSS modules for complex styles
## 📚 BMS Schema
The Battlemap Notation Schema (BMS) is a YAML-based format for describing DnD/TTRPG battlemaps. See the [complete specification](docs/BMS_SCHEMA_SPEC.md).
### Example BMS File
```yaml
BMS Version: 1.0
Map Name: Guard Room
Grid Size: 20x15
Grid Unit: 5ft
Style: Stone Fortress
Lighting: Torchlight
ROOMS:
- R1: Guard Room (0,0)-(20,15)
Walls: Stone-1ft-Smooth
Floor: Flagstones
Ceiling: 15ft, wooden beams
DOORS:
- D1: Wooden Door (20,6)-(20,9)
Type: Wood-Single-Reinforced
Connects: R1 -> Corridor
State: Closed-Unlocked
```
## 🎯 Roadmap
### Phase 1: MVP (Current)
- [x] Project setup and structure
- [ ] BMS TypeScript interfaces and validation
- [ ] Basic YAML parsing
- [ ] Grid and room rendering
- [ ] Door and object rendering
- [ ] Export functionality
- [ ] Alpha deployment to maps.bouncypixel.com
### Phase 2: Texture Packs
- [ ] Texture pack system architecture
- [ ] Pack management UI
- [ ] Texture rendering engine
- [ ] Marketplace integration
### Phase 3: AI Integration
- [ ] OpenAI API integration
- [ ] Chat-based schema generation
- [ ] Iterative editing workflow
- [ ] Subscription system
### Phase 4: Community & VTT
- [ ] Template sharing
- [ ] VTT export (FoundryVTT, Roll20)
- [ ] User accounts
- [ ] Collaboration features
## 📄 License
MIT License - see [LICENSE](LICENSE) file for details.
## 🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
## 📞 Contact
Project maintained by [Lukas Parsons](https://github.com/lukasparsons).
- **Project**: [BMS Forge](https://maps.bouncypixel.com) (coming soon)
- **Repository**: `ssh://git@git.bouncypixel.com:222/helm/bms-forge.git`
---
<p align="center">
<i>Built with ❤️ for the TTRPG community</i>
</p>

463
docs/BMS_SCHEMA_SPEC.md Normal file
View file

@ -0,0 +1,463 @@
# Battlemap Notation Schema (BMS) - Technical Specification
## Schema Version: 1.0
## Complete Field Reference
### 1. Header Section (Required)
| Field | Type | Description | Example |
|-------|------|-------------|---------|
| `BMS Version` | String | Schema version | `"1.0"` |
| `Map Name` | String | Map title | `"Goblin Warrens"` |
| `Grid Size` | String | Width×Height in squares | `"40x30"` |
| `Grid Unit` | String | Feet per square | `"5ft"` |
| `Style` | String | Visual theme | `"Stone Cavern"` |
| `Lighting` | String | Ambient light | `"Dim (torchlight)"` |
| `Ambient Sound` | String | Background audio | `"Dripping water"` |
| `Temperature` | String | Environmental temp | `"55°F, damp"` |
### 2. Coordinate System
```
(0,0)───────→ x (width)
y (height)
Example: (5,10) = column 6, row 11 (5ft squares)
Range: (x1,y1)-(x2,y2) defines rectangle
```
### 3. Room Definition
```yaml
ROOMS:
- R1: [Room Name] (x1,y1)-(x2,y2)
Walls: [Material-Thickness-Style]
Floor: [Material-Texture]
Ceiling: [Height-Features]
Lighting: [Light sources within]
Features: [Special characteristics]
```
**Wall Types:**
- `Stone-1ft-Rough`
- `Wood-6in-Carved`
- `Iron-2ft-Reinforced`
- `Masonry-1ft-Smooth`
- `Natural-Rockface`
**Floor Types:**
- `Rough stone, debris`
- `Packed earth`
- `Wooden planks`
- `Flagstones`
- `Mosaic tile`
### 4. Door Definition
```yaml
DOORS:
- D1: [Door Name]
Type: [Material-Single/Double-Style]
Position: (x1,y1)-(x2,y2)
Connects: [Room1] <-> [Room2]
State: [Open/Closed]-[Locked/Unlocked]
Hinges: [Side]
DC: [Difficulty if locked/trapped]
```
**Door Types:**
- `Wood-Single-Banded`
- `Iron-Double-Portcullis`
- `Stone-Sliding-Secret`
- `Wood-Double-Ornate`
**States:**
- `Open-Unlocked`
- `Closed-Locked` (DC15)
- `Closed-Barred`
- `Destroyed`
### 5. Object Definition
```yaml
OBJECTS:
- O1: [Object Name]
Type: [Material-Type-Style]
Position: (x1,y1)-(x2,y2)
Elevation: [+/- ft from floor]
State: [Condition]
Contents: [What's inside]
Lock: [Lock details if applicable]
Trapped: [Trap details if applicable]
```
**Object Types:**
- `Wood-Table-Rough`
- `Stone-Throne-Ornate`
- `Iron-Chest-Large`
- `Wood-Barrel-Sturdy`
- `Stone-Altar-Carved`
### 6. Hazard Definition
```yaml
HAZARDS:
- H1: [Hazard Name]
Type: [Mechanical/Magical/Environmental]-[Effect]
Position: (x1,y1)-(x2,y2)
Trigger: [Activation mechanism]
DC: [Perception/Disable DCs]
Save: [Saving throw type & DC]
Damage: [Damage dice]
Effect: [Additional effects]
Reset: [Manual/Automatic]
```
**Hazard Types:**
- `Mechanical-Pit-20ft`
- `Magical-Glyph-Fire`
- `Environmental-Gas-Poison`
- `Mechanical-Dart-Pressure`
- `Magical-Fear-Aura`
### 7. Lighting Definition
```yaml
LIGHTING:
- L1: [Light Name]
Type: [Source-Type]
Position: (x,y)
Radius: [Bright ft, Dim ft]
Color: [Light color]
Fuel: [Duration/Fuel source]
Special: [Flickering/Steady etc]
```
**Light Types:**
- `Torch-Wall`
- `Lantern-Hanging`
- `Candle-Table`
- `Magical-Everburning`
- `Fungi-Bioluminescent`
### 8. Connection Graph
```yaml
CONNECTIONS:
[Room1] -- [Door/Passage] --> [Room2]
[Room2] -- [Tunnel] --> [Room3]
[Room3] -- [Secret] --> [Room4]
```
**Connection Types:**
- `-- [Door] -->` (Standard door)
- `-- [Secret] -->` (Hidden passage)
- `-- [Tunnel] -->` (Long corridor)
- `-- [Stairs] -->` (Vertical connection)
### 9. Annotation Definition
```yaml
ANNOTATIONS:
- "[Text]" at (x,y)
- "[Label]" pointing to (x1,y1)-(x2,y2)
- "DM: [Note]" at (x,y)
```
## Advanced Features
### Multi-level Maps
```yaml
ELEVATION:
- Base Level: 0ft
- Upper Balcony: +15ft
- Lower Pit: -20ft
STAIRS:
- S1: Spiral Stairs
Position: (25,15)-(27,17)
Connects: Level1 -> Level2 (+10ft)
Steps: 20
```
### Secret/Concealed Features
```yaml
SECRETS:
- S1: Hidden Door
Position: (14,12)-(14,14)
Type: Stone-Sliding
DC: 20 Perception
Mechanism: Lever at (12,18)
Revealed: When mechanism activated
```
### Environmental Zones
```yaml
ZONES:
- Z1: Fog Bank
Position: (10,10)-(20,20)
Effect: Heavily obscured
Save: None
Movement: Normal
- Z2: Slippery Ice
Position: (30,30)-(40,40)
Effect: Difficult terrain
Save: DC 10 Acrobatics or fall prone
```
## Visualization Standards
### Color Coding (Suggested)
- **Walls:** Dark gray (#333333)
- **Doors:** Brown (#8B4513)
- **Hazards:** Red (#FF0000) with pattern
- **Objects:** Medium gray (#666666)
- **Light sources:** Yellow glow (#FFFF00)
- **Water:** Blue (#0000FF)
- **Difficult terrain:** Green diagonal lines
- **Elevation changes:** Contour lines
### Icon Standards
- **Door:** Rectangle with arc
- **Chest:** Rectangle with curved lid
- **Table:** Rectangle
- **Chair/T hrone:** Square with back
- **Torch:** Small circle with flame above
- **Pit:** Diagonal hatch pattern
- **Secret:** Dotted outline
## Example: Simple Guard Room
```yaml
BMS Version: 1.0
Map Name: Guard Room
Grid Size: 20x15
Grid Unit: 5ft
Style: Stone Fortress
Lighting: Torchlight
ROOMS:
- R1: Guard Room (0,0)-(20,15)
Walls: Stone-1ft-Smooth
Floor: Flagstones
Ceiling: 15ft, wooden beams
Lighting: Wall torches at (3,0), (17,0)
DOORS:
- D1: Wooden Door (20,6)-(20,9)
Type: Wood-Single-Reinforced
Connects: R1 -> Corridor
State: Closed-Unlocked
Hinges: East side
OBJECTS:
- O1: Guard Table (5,5)-(9,9)
Type: Wood-Table-Sturdy
Contents: Maps, mugs, dice
- O2: Weapon Rack (15,5)-(17,10)
Type: Wood-Rack-Wall
Contents: 4 spears, 2 swords
LIGHTING:
- L1: Wall Torch (3,0)
Radius: 20ft bright, 40ft dim
Color: Orange flickering
- L2: Wall Torch (17,0)
Radius: 20ft bright, 40ft dim
Color: Orange flickering
ANNOTATIONS:
- "Guard post" at (10,7)
- "Weapons" pointing to O2
```
## TypeScript Interface Definition
```typescript
// Core BMS Types
type Coordinate = [number, number]; // (x, y)
type Rectangle = { from: Coordinate; to: Coordinate };
interface BMSHeader {
bmsVersion: string;
mapName: string;
gridSize: { width: number; height: number };
gridUnit: string;
style: string;
lighting: string;
ambientSound?: string;
temperature?: string;
}
interface Room {
id: string; // "R1"
name: string;
position: Rectangle;
walls: string; // "Stone-1ft-Rough"
floor: string; // "Rough stone, debris"
ceiling: string; // "15ft, wooden beams"
lighting?: string;
features?: string;
}
interface Door {
id: string; // "D1"
name: string;
type: string; // "Wood-Single-Banded"
position: Rectangle;
connects: { room1: string; room2: string };
state: { open: boolean; locked: boolean; dc?: number };
hinges?: string; // "East side"
}
interface MapObject {
id: string; // "O1"
name: string;
type: string; // "Wood-Table-Rough"
position: Rectangle;
elevation?: string; // "+2ft"
state?: string;
contents?: string;
lock?: string;
trapped?: string;
}
interface Hazard {
id: string; // "H1"
name: string;
type: string; // "Mechanical-Pit-20ft"
position: Rectangle;
trigger?: string;
dc?: { perception?: number; disable?: number };
save?: { type: string; dc: number };
damage?: string;
effect?: string;
reset?: string;
}
interface LightSource {
id: string; // "L1"
name: string;
type: string; // "Torch-Wall"
position: Coordinate;
radius: { bright: number; dim: number };
color?: string;
fuel?: string;
special?: string;
}
interface Connection {
from: string; // Room ID
to: string; // Room ID
type: string; // "Door", "Secret", "Tunnel", "Stairs"
via?: string; // Door ID if applicable
}
interface Annotation {
text: string;
position: Coordinate;
type?: "label" | "note" | "pointer";
target?: Rectangle; // For pointer annotations
}
interface BMSSchema extends BMSHeader {
rooms?: Room[];
doors?: Door[];
objects?: MapObject[];
hazards?: Hazard[];
lightingSources?: LightSource[];
connections?: Connection[];
annotations?: Annotation[];
// Advanced features (future)
elevation?: Elevation[];
stairs?: Stairs[];
secrets?: Secret[];
zones?: Zone[];
}
```
## Parser Functions
### Grid Size Parser
```typescript
function parseGridSize(str: string): { width: number; height: number } {
// "40x30" → { width: 40, height: 30 }
const match = str.match(/^(\d+)x(\d+)$/);
if (!match) throw new Error(`Invalid grid size: ${str}`);
return { width: parseInt(match[1]), height: parseInt(match[2]) };
}
```
### Coordinate Parser
```typescript
function parseCoordinate(str: string): Coordinate {
// "(5,10)" → [5, 10]
const match = str.match(/^\((\d+),(\d+)\)$/);
if (!match) throw new Error(`Invalid coordinate: ${str}`);
return [parseInt(match[1]), parseInt(match[2])];
}
```
### Rectangle Parser
```typescript
function parseRectangle(str: string): Rectangle {
// "(0,0)-(20,15)" → { from: [0,0], to: [20,15] }
const match = str.match(/^\((\d+),(\d+)\)-\((\d+),(\d+)\)$/);
if (!match) throw new Error(`Invalid rectangle: ${str}`);
return {
from: [parseInt(match[1]), parseInt(match[2])],
to: [parseInt(match[3]), parseInt(match[4])]
};
}
```
### Door State Parser
```typescript
function parseDoorState(str: string): { open: boolean; locked: boolean; dc?: number } {
// "Open-Unlocked" → { open: true, locked: false }
// "Closed-Locked (DC15)" → { open: false, locked: true, dc: 15 }
const match = str.match(/^(Open|Closed)-(Unlocked|Locked)(?: \(DC(\d+)\))?$/);
if (!match) throw new Error(`Invalid door state: ${str}`);
return {
open: match[1] === "Open",
locked: match[2] === "Locked",
dc: match[3] ? parseInt(match[3]) : undefined
};
}
```
## Validation Rules
### Required Fields
1. `BMS Version` must be "1.0"
2. `Map Name` must be non-empty string
3. `Grid Size` must match pattern "WxH" where W,H > 0
4. `Grid Unit` must contain "ft" (e.g., "5ft")
### Coordinate Validation
1. Coordinates must be within grid bounds
2. Rectangle `from` coordinates must be ≤ `to` coordinates
3. Room rectangles must not overlap (optional warning)
### Door Validation
1. Door must connect two existing rooms
2. Door position must be on room boundary
3. Hinge side must be consistent with door position
### Lighting Validation
1. Light position must be within a room
2. Bright radius ≤ dim radius
3. Color must be valid CSS color
## Best Practices
1. **Start Simple**: Define rooms and connections first
2. **Be Specific**: Use exact coordinates, not "center of room"
3. **Consistent Naming**: R1, D1, O1, H1, L1, etc.
4. **Include DCs**: For all interactive elements
5. **Visual Notes**: Describe what players SEE
6. **Mechanical Notes**: Describe what players INTERACT WITH
7. **Layer Complexity**: Add hazards/objects after base layout
---
*Last Updated: March 19, 2026*

View file

@ -0,0 +1,313 @@
# BMS BattleMap Generator - Implementation Roadmap
## Phase 1: Frontend Renderer MVP (2-3 weeks)
### Week 1: Core Foundation
#### **Days 1-2: Project Setup**
**Tasks:**
1. Initialize Git repository
2. Create Vite + React + TypeScript project
3. Install core dependencies:
- `konva react-konva` (Canvas rendering)
- `js-yaml` (YAML parsing)
- `zod` (Validation)
- `@monaco-editor/react` (Code editor)
- `tailwindcss postcss autoprefixer` (Styling)
4. Configure development environment
5. Set up TypeScript, ESLint, Prettier
**Deliverables:**
- ✅ Project repository initialized
- ✅ Development environment working
- ✅ Basic React app running
#### **Days 3-4: BMS Schema Implementation**
**Tasks:**
1. Create TypeScript interfaces for BMS 1.0
2. Implement Zod validation schemas
3. Create YAML parsing utilities
4. Add error handling and validation
5. Create test BMS files for development
**Deliverables:**
- ✅ Complete BMS TypeScript types
- ✅ Runtime validation with Zod
- ✅ YAML parser with error messages
- ✅ Sample BMS files for testing
#### **Days 5-7: Basic Rendering Engine**
**Tasks:**
1. Set up Konva.js canvas
2. Implement grid renderer
3. Define Paperly style system
4. Create room renderer (walls, floors)
5. Add basic UI layout (editor + preview)
**Deliverables:**
- ✅ Canvas displaying grid
- ✅ Rooms rendered as rectangles
- ✅ Paperly style applied
- ✅ Split editor/preview layout
### Week 2: Complete BMS Support
#### **Days 8-10: Element Rendering**
**Tasks:**
1. Implement door renderer
- Open/closed states
- Hinge side indicators
- Connection lines
2. Create object renderer
- Simple geometric icons
- Labels and tooltips
- State indicators
3. Build hazard renderer
- Visual patterns
- Color coding
- DC indicators
**Deliverables:**
- ✅ Doors render with correct states
- ✅ Objects display as icons
- ✅ Hazards show visual patterns
- ✅ Real-time updates on YAML changes
#### **Days 11-12: Advanced Features**
**Tasks:**
1. Implement lighting renderer
- Light radius visualization
- Bright/dim zones
- Glow effects
2. Create annotation renderer
- Text labels
- DM notes
- Connection arrows
3. Build export system
- PNG export (canvas to image)
- JSON export (serialized BMS)
- BMS YAML export
**Deliverables:**
- ✅ Lighting effects rendered
- ✅ Annotations displayed
- ✅ Export functionality working
- ✅ Complete BMS 1.0 support
#### **Day 13: UI Polish**
**Tasks:**
1. Refine UI components
2. Add error reporting
3. Implement zoom/pan controls
4. Add keyboard shortcuts
5. Improve user experience
**Deliverables:**
- ✅ Polished UI
- ✅ Error messages for invalid BMS
- ✅ Zoom/pan functionality
- ✅ Keyboard shortcuts
### Week 3: Testing & Deployment
#### **Days 14-15: Testing & Bug Fixes**
**Tasks:**
1. Test with sample BMS files
2. Fix rendering issues
3. Optimize performance
4. Test on different browsers
5. Create documentation
**Deliverables:**
- ✅ All sample BMS files render correctly
- ✅ Performance optimized for large maps
- ✅ Cross-browser compatibility
- ✅ User documentation
#### **Days 16-17: Alpha Deployment**
**Tasks:**
1. Build production version
2. Deploy to maps.bouncypixel.com
3. Set up basic analytics (optional)
4. Create alpha testing guide
5. Invite initial testers
**Deliverables:**
- ✅ Production build completed
- ✅ Deployed to maps.bouncypixel.com
- ✅ Alpha testing environment ready
- ✅ Testers invited and briefed
## Phase 2: Texture Pack System (3-4 weeks)
### **Week 1: Texture Pack Architecture**
1. Design texture pack file structure
2. Create style configuration format
3. Implement texture loading system
4. Add texture pack manager UI
### **Week 2: Texture Rendering**
1. Modify renderer to use textures
2. Implement texture tiling
3. Add texture blending options
4. Create texture preview system
### **Week 3: Pack Management**
1. Implement pack purchase/download
2. Add pack activation/deactivation
3. Create pack marketplace UI
4. Add user account integration
### **Week 4: Polish & Release**
1. Create default texture packs
2. Test texture rendering
3. Optimize texture loading
4. Release to production
## Phase 3: AI Integration (4-5 weeks)
### **Week 1: AI API Integration**
1. Set up OpenAI API integration
2. Create BMS generation prompts
3. Implement basic AI generation
4. Add API key management
### **Week 2: Chat Interface**
1. Design chat-based UI
2. Implement conversation history
3. Add context preservation
4. Create iterative editing workflow
### **Week 3: Advanced AI Features**
1. Implement schema modification
2. Add suggestion system
3. Create validation feedback
4. Implement rate limiting
### **Week 4: Monetization Integration**
1. Add subscription system
2. Implement API usage tracking
3. Create tiered feature access
4. Add payment processing
### **Week 5: Testing & Release**
1. Test AI generation quality
2. Optimize prompt engineering
3. Performance testing
4. Release to production
## Phase 4: VTT Export & Community Features (4-5 weeks)
### **Week 1: VTT Export Formats**
1. Implement FoundryVTT export
2. Add Roll20 compatibility
3. Create universal VTT format
4. Test with popular VTTs
### **Week 2: Community Features**
1. Add template sharing
2. Create user profiles
3. Implement rating system
4. Add search functionality
### **Week 3: Advanced Tools**
1. Add map editing tools
2. Implement undo/redo
3. Create layer management
4. Add measurement tools
### **Week 4: Performance & Scaling**
1. Optimize for large maps
2. Implement caching
3. Add CDN for assets
4. Scale infrastructure
### **Week 5: Polish & Release**
1. User feedback implementation
2. Bug fixes
3. Documentation updates
4. Production release
## Success Metrics
### Phase 1 (MVP)
- ✅ BMS 1.0 YAML parsing
- ✅ Complete rendering with Paperly style
- ✅ Real-time editor + preview
- ✅ PNG/JSON export
- ✅ Alpha testing with 5-10 users
- ✅ Positive feedback on usability
### Phase 2 (Texture Packs)
- ✅ Texture pack system working
- ✅ At least 3 quality texture packs
- ✅ Users can purchase/activate packs
- ✅ Texture rendering performs well
### Phase 3 (AI Integration)
- ✅ AI generates valid BMS schemas
- ✅ Chat interface intuitive
- ✅ Iterative editing works smoothly
- ✅ Subscription system functional
### Phase 4 (Complete Platform)
- ✅ VTT export works with major platforms
- ✅ Community templates shared
- ✅ Performance handles large maps
- ✅ Platform scales with users
## Risk Mitigation
### Technical Risks
1. **Canvas Performance**: Use Konva.js optimizations, implement viewport culling
2. **Large Map Rendering**: Implement level-of-detail, progressive rendering
3. **Browser Compatibility**: Focus on modern browsers, polyfill if needed
4. **YAML Parsing**: Robust error handling, helpful error messages
### Business Risks
1. **User Adoption**: Free tier lowers barrier, focus on useful core features
2. **Monetization**: Multiple revenue streams (packs, subscription, AI)
3. **Competition**: Unique BMS format, AI integration, community focus
4. **Technical Debt**: Clean architecture, modular design, regular refactoring
## Resource Requirements
### Development
- **Frontend Developer**: 3-4 months full-time (or equivalent)
- **Designer**: 1-2 months for UI/UX and texture packs
- **AI/Backend**: 1-2 months for AI integration (optional backend)
### Infrastructure
- **Hosting**: Static site hosting (Vercel, Netlify, self-hosted)
- **Storage**: CDN for texture packs
- **API**: Optional for AI features
- **Database**: Optional for user accounts/templates
### Testing
- **Alpha Testers**: 5-10 trusted users
- **Beta Testers**: 50-100 users
- **Production Testing**: Continuous user feedback
## Future Considerations
### Technical Debt
- Regular code reviews
- Comprehensive testing
- Documentation updates
- Performance monitoring
### Feature Backlog
1. 3D top-down view
2. Mobile app
3. Collaborative editing
4. Plugin system
5. Advanced generation algorithms
6. Integration with other TTRPG tools
7. Print-friendly formats
8. Custom asset import
9. Advanced lighting simulation
10. Sound effects integration
---
*Last Updated: March 19, 2026*
*Next Review: After Phase 1 Completion*

244
docs/PROJECT_PLAN.md Normal file
View file

@ -0,0 +1,244 @@
# BMS BattleMap Generator - Project Plan
## Project Overview
A web-based tool for rendering DnD/TTRPG battlemaps using the Battlemap Notation Schema (BMS) format.
**Primary Goal**: Create a frontend renderer that parses BMS YAML and renders battlemaps with a default "Paperly" style.
**Monetization Strategy**:
- **Free Tier**: Basic rendering with Paperly style
- **Paid Features**: Texture packs, AI integration (future)
- **Subscription**: Access to all texture packs + AI features
## Technology Stack
### Frontend
- **Framework**: React 19 + TypeScript
- **Build Tool**: Vite
- **Canvas**: Konva.js (via react-konva)
- **Validation**: Zod
- **YAML Parsing**: js-yaml
- **Editor**: Monaco Editor
- **Styling**: Tailwind CSS
### Optional Backend (Future)
- **Runtime**: .NET 8 Minimal API
- **Validation**: JSON Schema.NET
- **YAML**: YamlDotNet
- **AI**: OpenAI SDK
## Project Structure
```
bms-forge/
├── public/
├── src/
│ ├── types/ # BMS TypeScript interfaces
│ ├── schema/ # Zod validation schemas
│ ├── parser/ # YAML parsing utilities
│ ├── renderer/ # Konva.js rendering engine
│ │ ├── layers/ # Individual layer renderers
│ │ └── styles/ # Paperly + future texture packs
│ ├── components/ # React components
│ ├── hooks/ # Custom React hooks
│ ├── utils/ # Helper functions
│ └── App.tsx
├── package.json
└── vite.config.ts
```
## Phase 1: Frontend Renderer MVP (2-3 weeks)
### Week 1: Core Foundation
**Days 1-2**: Project Setup
- Initialize repository with Vite + React + TypeScript
- Install core dependencies
- Configure development environment
**Days 3-4**: BMS Schema Implementation
- Complete TypeScript interfaces for BMS 1.0
- Zod validation schemas
- YAML parser with error handling
**Days 5-7**: Basic Rendering Engine
- Grid renderer
- Paperly style system
- Room rendering
### Week 2: Complete BMS Support
**Days 8-10**: Element Rendering
- Door renderer (open/closed states)
- Object renderer (simple geometric icons)
- Hazard renderer (visual patterns)
**Days 11-12**: Advanced Features
- Lighting renderer (radius visualization)
- Annotation renderer (text labels)
- Export system (PNG, JSON, BMS YAML)
### Week 3: Polish & Alpha Testing
- UI refinement
- Performance optimization
- Alpha testing with trusted users
- Bug fixes and improvements
## BMS Schema Implementation
### Core Types
```typescript
type Coordinate = [number, number]; // (x, y)
type Rectangle = { from: Coordinate; to: Coordinate };
interface BMSHeader {
bmsVersion: string;
mapName: string;
gridSize: { width: number; height: number };
gridUnit: string;
style: string;
lighting: string;
ambientSound?: string;
temperature?: string;
}
interface BMSSchema extends BMSHeader {
rooms?: Room[];
doors?: Door[];
objects?: MapObject[];
hazards?: Hazard[];
lightingSources?: LightSource[];
connections?: Connection[];
annotations?: Annotation[];
}
```
### Paperly Style Definition
```typescript
const paperlyStyle = {
grid: { color: "#ccc", opacity: 0.3, lineWidth: 1 },
rooms: { fill: "#f8f8f8", stroke: "#666", strokeWidth: 2 },
walls: { color: "#333", thickness: 3 },
doors: {
open: { fill: "#8BC34A", stroke: "#689F38" },
closed: { fill: "#795548", stroke: "#5D4037" }
},
objects: { color: "#666", size: 20 },
hazards: { color: "#f44336", pattern: "diagonal" },
lighting: { glow: "#FFEB3B", opacity: 0.3 }
};
```
## Rendering Engine Architecture
### Layered Rendering System
1. **Layer 7**: Annotations (text, labels)
2. **Layer 6**: Lighting effects (glows, shadows)
3. **Layer 5**: Objects & Hazards (icons, patterns)
4. **Layer 4**: Doors (with state indicators)
5. **Layer 3**: Room interiors (floor textures, features)
6. **Layer 2**: Walls (with material styles)
7. **Layer 1**: Grid & Background
### Renderer Class Structure
```typescript
class PaperlyRenderer {
private stage: Konva.Stage;
private layers: Map<string, Konva.Layer>;
render(schema: BMSSchema): void {
this.setupCanvas(schema.gridSize);
this.renderGrid();
this.renderRooms(schema.rooms);
this.renderDoors(schema.doors);
// ... other elements
}
}
```
## UI Component Structure
### Main Layout
```
Header
└── App Title, Version, Links
Main Content (flex)
├── EditorPanel (Monaco Editor with YAML)
├── PreviewPanel (Konva Canvas)
└── ToolsPanel (Export buttons, options)
StatusBar
└── Grid info, errors, zoom level
```
### Key Components
1. **`EditorPanel`**: Monaco editor with YAML syntax highlighting
2. **`PreviewPanel`**: Konva canvas with rendered map
3. **`ToolsPanel`**: Export buttons, style options, validation status
4. **`Header`**: App title, version, links
5. **`StatusBar`**: Grid info, errors, zoom level
## Future Features
### Texture Pack System
- Individual texture pack purchases ($4.99 each)
- Subscription access to all packs
- Pack structure:
```
texture-packs/
├── medieval-stone/
│ ├── pack.json
│ ├── walls/
│ ├── floors/
│ └── objects/
```
### AI Integration
- **Free tier**: Bring-your-own-API key
- **Paid tier**: Managed API with rate limits
- **Chat-based iteration**: Natural language to modify BMS schemas
### VTT Export
- FoundryVTT scene format
- Roll20 compatibility
- Universal VTT format
## Deployment
### Alpha Testing
- **URL**: maps.bouncypixel.com
- **Build**: Vite production build
- **Hosting**: Self-hosted subdomain
- **Testing**: 5-10 trusted alpha testers
### Production Considerations
- **Custom Domain**: bms-forge.com or similar
- **CDN**: For texture packs and assets
- **Analytics**: Basic usage tracking (optional)
- **CI/CD**: Automated build and deployment
## Success Criteria
### MVP Completion
- [ ] Parse and validate BMS 1.0 YAML
- [ ] Render all BMS elements with Paperly style
- [ ] Real-time editor + preview
- [ ] Export to PNG and JSON
- [ ] Deploy to maps.bouncypixel.com
- [ ] Gather feedback from alpha testers
### Phase 2 Goals
- [ ] Texture pack system
- [ ] AI integration
- [ ] VTT export functionality
- [ ] Community template sharing
- [ ] Monetization system
## Notes
- **Version Control**: Private Forgejo at git.bouncypixel.com
- **Analytics**: Optional future feature
- **Community**: Discord for user feedback
- **Browser Support**: Modern browsers only for V1
- **Mobile**: Desktop-focused initially
---
*Last Updated: March 19, 2026*

View file

@ -0,0 +1,419 @@
# BMS BattleMap Generator - Technical Architecture
## System Overview
```
┌─────────────────────────────────────────────────────────────┐
│ Frontend Application │
├─────────────────────────────────────────────────────────────┤
│ Editor Interface Preview Canvas Tools Panel │
│ (Monaco Editor) (Konva.js Canvas) (Export/Options) │
├─────────────────────────────────────────────────────────────┤
│ State Management │
│ (React Hooks/Context) │
├─────────────────────────────────────────────────────────────┤
│ YAML Parser │ Schema Validator │ Renderer Engine │
│ (js-yaml) │ (Zod) │ (Konva.js Layers) │
└─────────────────────────────────────────────────────────────┘
```
## Component Architecture
### 1. BMS Schema Module (`src/schema/`)
```
schema/
├── types.ts # TypeScript interfaces
├── validators.ts # Zod validation schemas
├── parsers.ts # Coordinate/range parsing
├── transformers.ts # Data transformation
└── index.ts # Public API
```
### 2. Parser Module (`src/parser/`)
```
parser/
├── yaml-parser.ts # BMS YAML parsing
├── coordinate-parser.ts # Grid coordinate parsing
├── validation.ts # Schema validation logic
├── errors.ts # Error types and messages
└── index.ts
```
### 3. Renderer Module (`src/renderer/`)
```
renderer/
├── base-renderer.ts # Abstract base class
├── paperly-renderer.ts # Paperly style implementation
├── texture-renderer.ts # Future: texture pack rendering
├── layers/ # Individual layer renderers
│ ├── grid-layer.ts
│ ├── room-layer.ts
│ ├── door-layer.ts
│ ├── object-layer.ts
│ ├── hazard-layer.ts
│ ├── lighting-layer.ts
│ └── annotation-layer.ts
├── styles/ # Style definitions
│ ├── paperly-style.ts
│ └── texture-styles.ts # Future
└── utils/ # Rendering utilities
├── geometry.ts
├── colors.ts
└── textures.ts
```
### 4. UI Components (`src/components/`)
```
components/
├── Editor/
│ ├── YAMLEditor.tsx # Monaco editor wrapper
│ └── EditorPanel.tsx # Editor with error display
├── Preview/
│ ├── MapCanvas.tsx # Konva canvas wrapper
│ └── PreviewPanel.tsx # Preview with controls
├── Tools/
│ ├── ExportMenu.tsx # Export options
│ ├── StylePicker.tsx # Style selection
│ └── ValidationPanel.tsx # Validation status
├── Layout/
│ ├── Header.tsx
│ ├── Sidebar.tsx
│ └── StatusBar.tsx
└── Common/
├── ErrorDisplay.tsx
├── LoadingSpinner.tsx
└── Tooltip.tsx
```
### 5. State Management (`src/hooks/`, `src/context/`)
```
hooks/
├── useBMS.ts # BMS schema state
├── useRenderer.ts # Renderer instance
├── useExport.ts # Export functionality
└── useUndoRedo.ts # Undo/redo history
context/
├── AppContext.tsx # Global app state
└── ThemeContext.tsx # Theme/stylesheet
```
## Data Flow
### 1. Parsing Pipeline
```
User Input (YAML)
YAML Parser (js-yaml)
Schema Validator (Zod)
BMSSchema Object
Renderer Engine
Canvas Output
```
### 2. Rendering Pipeline
```
BMSSchema
Setup Canvas (grid size, scale)
Render Layers (in order):
1. Grid Layer
2. Room Layer (walls, floors)
3. Door Layer
4. Object Layer
5. Hazard Layer
6. Lighting Layer
7. Annotation Layer
Canvas Output
```
### 3. Export Pipeline
```
Canvas State
Export Format Selection
Format-Specific Exporter:
- PNG: canvas.toDataURL()
- JSON: JSON.stringify(BMSSchema)
- BMS: serializeBMSToYAML()
File Download
```
## Key Technical Decisions
### 1. Canvas Library: Konva.js
**Why Konva.js over alternatives:**
- **React Integration**: react-konva provides React component wrappers
- **Performance**: Hardware acceleration, efficient rendering
- **Features**: Layers, events, transforms, filters
- **Size**: Smaller than PIXI.js, more focused than raw Canvas API
- **Maintenance**: Active development, good documentation
### 2. Validation: Zod
**Why Zod over alternatives:**
- **TypeScript Integration**: Automatic type inference
- **Runtime Safety**: Comprehensive validation at runtime
- **Error Messages**: Customizable error messages
- **Size**: Small bundle size
- **Schema Composition**: Easy to compose and extend schemas
### 3. YAML Parsing: js-yaml
**Why js-yaml:**
- **Browser Support**: Works in browser environments
- **Safety**: Secure parsing options
- **Performance**: Fast and efficient
- **Features**: Full YAML 1.2 support
### 4. State Management: React Hooks
**Why React Hooks over Redux/MobX:**
- **Simplicity**: Less boilerplate for MVP
- **Performance**: Optimized re-renders with useMemo/useCallback
- **Scalability**: Can migrate to Context or Zustand if needed
- **Learning Curve**: Familiar to React developers
## Performance Considerations
### 1. Rendering Optimization
```typescript
// Use Konva.js optimizations
const optimizedRender = () => {
// Batch updates
stage.batchDraw();
// Use caching for static elements
shape.cache();
// Implement viewport culling
if (isInViewport(element)) {
renderElement(element);
}
};
```
### 2. Memory Management
```typescript
// Clean up resources
useEffect(() => {
const renderer = new PaperlyRenderer();
return () => {
renderer.destroy(); // Clean up Konva objects
};
}, []);
```
### 3. Large Map Handling
```typescript
// Implement level of detail
const getDetailLevel = (zoom: number): DetailLevel => {
if (zoom < 0.5) return 'low';
if (zoom < 1.0) return 'medium';
return 'high';
};
// Progressive rendering
const renderProgressive = async (schema: BMSSchema) => {
// Render grid first
renderGrid();
// Render rooms
await renderRooms(schema.rooms);
// Render details progressively
requestIdleCallback(() => {
renderDoors(schema.doors);
renderObjects(schema.objects);
});
};
```
## Security Considerations
### 1. YAML Parsing Security
```typescript
import { load } from 'js-yaml';
// Safe YAML parsing
const parseBMSYAML = (yaml: string): BMSSchema => {
try {
// Use safeLoad to prevent code execution
const parsed = load(yaml, {
schema: yaml.DEFAULT_SAFE_SCHEMA,
onWarning: (warning) => console.warn(warning)
});
// Validate structure
return bmsSchema.parse(parsed);
} catch (error) {
throw new BMSValidationError('Invalid BMS YAML', error);
}
};
```
### 2. File Export Security
```typescript
// Safe file naming
const sanitizeFilename = (name: string): string => {
return name.replace(/[^a-z0-9-_.]/gi, '_');
};
// Content security for downloads
const downloadFile = (content: string, filename: string) => {
const blob = new Blob([content], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
// Use safe download pattern
const a = document.createElement('a');
a.href = url;
a.download = sanitizeFilename(filename);
a.click();
URL.revokeObjectURL(url);
};
```
## Testing Strategy
### 1. Unit Tests
```typescript
// Test coordinate parsing
describe('parseCoordinate', () => {
it('should parse valid coordinates', () => {
expect(parseCoordinate('(5,10)')).toEqual([5, 10]);
});
it('should throw error for invalid format', () => {
expect(() => parseCoordinate('5,10')).toThrow();
});
});
```
### 2. Integration Tests
```typescript
// Test full parsing pipeline
describe('BMS Parser Integration', () => {
it('should parse and validate complete BMS', () => {
const yaml = sampleBMSYAML;
const schema = parseBMSYAML(yaml);
expect(schema.mapName).toBe('Guard Room');
expect(schema.rooms).toHaveLength(1);
});
});
```
### 3. Visual Regression Tests
```typescript
// Compare rendered output
describe('Renderer Visual Tests', () => {
it('should render rooms correctly', async () => {
const schema = createTestSchema();
const renderer = new PaperlyRenderer();
const image = await renderer.renderToImage(schema);
// Compare with baseline
expect(image).toMatchImageSnapshot();
});
});
```
## Deployment Architecture
### 1. Development Environment
```
Local Machine → Vite Dev Server → Browser
```
### 2. Alpha Deployment
```
Git Repository → Build (Vite) → Static Files → maps.bouncypixel.com
```
### 3. Future Production Deployment
```
GitHub Actions → Build & Test → CDN (Vercel/Netlify) → Custom Domain
↘ Texture Packs → Cloud Storage (S3/Cloudflare)
```
## Scalability Considerations
### 1. Frontend Scaling
- **Code Splitting**: Lazy load components
- **Bundle Optimization**: Tree shaking, compression
- **CDN**: Static assets via CDN
### 2. Rendering Scaling
- **Virtualization**: Only render visible area
- **Web Workers**: Offload heavy computation
- **Caching**: Cache rendered elements
### 3. Data Scaling
- **IndexedDB**: Local storage for large maps
- **Compression**: Compress BMS data
- **Streaming**: Progressive loading
## Monitoring & Analytics
### 1. Performance Monitoring
```typescript
// Track rendering performance
const measureRender = (schema: BMSSchema) => {
performance.mark('render-start');
renderer.render(schema);
performance.mark('render-end');
const measure = performance.measure('render', 'render-start', 'render-end');
console.log(`Render time: ${measure.duration}ms`);
};
```
### 2. Error Tracking
```typescript
// Centralized error handling
class ErrorTracker {
static track(error: Error, context?: any) {
console.error('BMS Error:', error, context);
// Send to error tracking service (future)
}
}
```
### 3. Usage Analytics (Optional)
```typescript
// Basic usage tracking
const trackEvent = (event: string, data?: any) => {
if (process.env.NODE_ENV === 'production') {
// Send to analytics service
}
};
```
## Future Technical Considerations
### 1. Plugin System
```typescript
interface BMSPlugin {
name: string;
version: string;
init: (app: BMSApp) => void;
render?: (context: RenderContext) => void;
export?: (schema: BMSSchema) => any;
}
```
### 2. Collaborative Features
- **WebSocket**: Real-time collaboration
- **Conflict Resolution**: Operational transforms
- **Presence**: User cursor/selection
### 3. Offline Support
- **Service Workers**: Offline functionality
- **Local Storage**: Save work locally
- **Sync**: Background synchronization
---
*Last Updated: March 19, 2026*

28
eslint.config.js Normal file
View file

@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)

14
index.html Normal file
View file

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BMS Forge - BattleMap Generator</title>
<meta name="description" content="Render and edit DnD/TTRPG battlemaps using the Battlemap Notation Schema (BMS) format">
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

4377
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

39
package.json Normal file
View file

@ -0,0 +1,39 @@
{
"name": "bms-forge",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@monaco-editor/react": "^4.6.0",
"js-yaml": "^4.1.0",
"konva": "^9.3.16",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-konva": "^18.2.10",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.10.6",
"@types/react": "^19.0.7",
"@types/react-dom": "^19.0.3",
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"eslint": "^9.17.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.18",
"postcss": "^8.5.3",
"tailwindcss": "^3.4.1",
"typescript": "^5.7.3",
"vite": "^6.0.5"
}
}

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

3
public/vite.svg Normal file
View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="currentColor" d="M8 0l6.5 4v8L8 16l-6.5-4V4z"/>
</svg>

After

Width:  |  Height:  |  Size: 152 B

70
src/App.tsx Normal file
View file

@ -0,0 +1,70 @@
function App() {
return (
<div className="min-h-screen bg-gray-900 text-gray-100">
<header className="border-b border-gray-800 p-4">
<div className="container mx-auto">
<h1 className="text-2xl font-bold">BMS Forge</h1>
<p className="text-gray-400">BattleMap Notation Schema Renderer</p>
</div>
</header>
<main className="container mx-auto p-4">
<div className="rounded-lg border border-gray-800 bg-gray-800/50 p-8 text-center">
<h2 className="text-xl font-semibold mb-4">🚧 Under Construction</h2>
<p className="text-gray-300 mb-4">
The BMS Forge project is being initialized. Check back soon for the complete battlemap rendering tool.
</p>
<div className="inline-flex items-center gap-2 rounded-lg bg-gray-700 px-4 py-2 text-sm">
<div className="h-2 w-2 rounded-full bg-green-500 animate-pulse"></div>
<span>Initializing project...</span>
</div>
</div>
<div className="mt-8 grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="rounded-lg border border-gray-800 p-6">
<h3 className="font-semibold mb-2"> Features Coming Soon</h3>
<ul className="text-gray-300 space-y-1 text-sm">
<li> BMS YAML parsing and validation</li>
<li> Real-time battlemap rendering</li>
<li> "Paperly" style default theme</li>
<li> Export to PNG and JSON</li>
<li> Texture pack system (future)</li>
</ul>
</div>
<div className="rounded-lg border border-gray-800 p-6">
<h3 className="font-semibold mb-2">📋 Project Status</h3>
<div className="space-y-3">
<div>
<div className="flex justify-between text-sm mb-1">
<span>Project Setup</span>
<span className="text-green-400">Complete</span>
</div>
<div className="h-2 bg-gray-700 rounded-full overflow-hidden">
<div className="h-full w-full bg-green-500"></div>
</div>
</div>
<div>
<div className="flex justify-between text-sm mb-1">
<span>BMS Schema Implementation</span>
<span className="text-yellow-400">Next</span>
</div>
<div className="h-2 bg-gray-700 rounded-full overflow-hidden">
<div className="h-full w-1/4 bg-yellow-500"></div>
</div>
</div>
</div>
</div>
</div>
</main>
<footer className="border-t border-gray-800 p-4 mt-8">
<div className="container mx-auto text-center text-gray-500 text-sm">
<p>BMS Forge v0.1.0 Built with React + TypeScript + Konva.js</p>
</div>
</footer>
</div>
)
}
export default App

31
src/index.css Normal file
View file

@ -0,0 +1,31 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
min-width: 320px;
min-height: 100vh;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
}

10
src/main.tsx Normal file
View file

@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import './index.css'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)

11
tailwind.config.js Normal file
View file

@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

25
tsconfig.json Normal file
View file

@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

11
tsconfig.node.json Normal file
View file

@ -0,0 +1,11 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true
},
"include": ["vite.config.ts"]
}

20
vite.config.ts Normal file
View file

@ -0,0 +1,20 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 3000,
open: true,
},
build: {
outDir: 'dist',
sourcemap: true,
},
})