Storyteller Suite - Entity Management

Entity Types Overview

Entity File Location Purpose
Character {story}/Characters/ People, creatures, NPCs
Location {story}/Locations/ Places, settings, areas
Event {story}/Events/ Timeline events, incidents
Scene {story}/Scenes/ Narrative scenes
Chapter {story}/Chapters/ Story chapters
Item {story}/Items/ Objects, artifacts
Group {story}/Groups/ Factions, organizations
Reference {story}/References/ Misc notes, lore
Map {story}/Maps/ Visual maps

File Format

Every entity is stored as a Markdown file with YAML frontmatter:

---
name: "Entity Name"
id: "entity-123"
description: "Brief description"
tags: [tag1, tag2]
image: "path/to/image.png"
customFields:
  customKey: "value"
---

## Description
Full description content...

## History
Historical content...

Character Entity

Frontmatter Fields

name: "Sir Aldric"
id: "char-001"
description: "A noble knight"
status: "Alive"
traits:
  - Brave
  - Loyal
  - Stubborn
image: "gallery/aldric.png"
tags: [protagonist, knight, human]
groups:
  - "group-royal-guard"
relationships:
  - type: "ally"
    targetId: "char-002"
    label: "Battle companion"
  - type: "family"
    targetId: "char-003"
    label: "Brother"
customFields:
  weapon: "Longsword"
  homeland: "Northern Kingdom"

Markdown Sections

## Description
A tall, weathered knight with graying temples...

## Backstory
Born to a minor noble house, Aldric rose through the ranks...

## Notes
- Key plot point in Chapter 3
- Has hidden motivation

Character Modal (CharacterModal.ts)

The modal provides fields for:

Location Entity

Frontmatter Fields

name: "Castle Stormhaven"
id: "loc-001"
description: "Ancient fortress"
locationType: "Castle"
status: "Active"
parentLocationId: "loc-000"  # Kingdom
childLocationIds:
  - "loc-002"  # Throne Room
  - "loc-003"  # Dungeon
correspondingMapId: "map-001"
coordinates:
  lat: 45.5
  lng: -122.6
image: "gallery/castle.png"
tags: [capital, fortress, royal]
groups:
  - "group-kingdom"

Markdown Sections

## Description
A massive stone fortress perched atop a cliff...

## History
Built 300 years ago by the first king...

Location Hierarchy

Locations support parent/child relationships:

// LocationService.ts
class LocationService {
    async getLocation(id: string): Promise<Location | null>
    async getChildLocations(parentId: string): Promise<Location[]>
    async getLocationPath(id: string): Promise<Location[]>  // Root to target
    async setParentLocation(childId: string, parentId: string): Promise<void>
}

Event Entity

Frontmatter Fields

name: "The Battle of Dawn"
id: "event-001"
description: "Decisive battle"
dateTime: "1225-06-15"
endDateTime: "1225-06-17"
outcome: "Kingdom victory"
isMilestone: true
progress: 100
dependencies:
  - "event-000"  # Previous event
charactersInvolved:
  - "char-001"
  - "char-002"
location: "loc-005"
image: "gallery/battle.png"
tags: [battle, war, turning-point]

Date Parsing

Events support natural language dates via chrono-node:

// DateParsing.ts
parseDate("next Friday")           // → ISO date
parseDate("June 15, 1225")         // → ISO date
parseDate("three days ago")        // → ISO date
parseDate("in two weeks")          // → ISO date

Timeline Integration

Events are visualized in:

Scene Entity

Frontmatter Fields

name: "The Confrontation"
id: "scene-001"
status: "wip"
priority: "high"
chapterId: "chapter-003"
linkedCharacters:
  - "char-001"
  - "char-002"
linkedLocations:
  - "loc-001"
linkedEvents:
  - "event-001"
linkedItems:
  - "item-001"
tags: [climax, action]
image: "gallery/scene1.png"

Markdown Sections

## Content
The door burst open as Aldric stepped into the throne room...

## Beats
- Aldric enters throne room
- Confronts the usurper
- Reveals the truth about his brother
- Fight begins

## Notes
- Pacing: fast
- POV: Third person limited

Scene Status

Status Description
draft Initial draft
wip Work in progress
review Ready for review
final Completed

Chapter Entity

Frontmatter Fields

name: "The Storm Breaks"
id: "chapter-003"
number: 3
summary: "The heroes face their greatest challenge"
sceneOrder:
  - "scene-001"
  - "scene-002"
  - "scene-003"
tags: [act-2, rising-action]
image: "gallery/chapter3.png"

Scene Order Management

// SceneOrderManager.ts
class SceneOrderManager {
    async getOrderedScenes(draft: StoryDraft): Promise<OrderedScene[]>
    async reorderScenes(draft: StoryDraft, newOrder: string[]): Promise<void>
    async moveScene(draft: StoryDraft, sceneId: string, newIndex: number): Promise<void>
}

Item Entity

Frontmatter Fields

name: "Crown of Arendor"
id: "item-001"
description: "Ancient golden crown"
isPlotCritical: true
currentOwner: "char-001"
currentLocation: "loc-002"
associatedEvents:
  - "event-001"
groups:
  - "group-001"
tags: [artifact, royal, magical]
image: "gallery/crown.png"
customFields:
  material: "Gold, sapphires"
  magicalProperties: "Enhances authority"

Markdown Sections

## Description
An ornate golden crown set with seven sapphires...

## History
Forged 300 years ago for the first king...

Group Entity

Frontmatter Fields

name: "The Royal Guard"
id: "group-001"
groupType: "military"
color: "#4a90d9"
description: "Elite protectors of the crown"
structure: "Military hierarchy"
goals: "Protect the royal family"
members:
  - type: "character"
    id: "char-001"
    rank: "Captain"
    joinDate: "1220-01-15"
    loyalty: "devoted"
linkedEvents:
  - "event-001"
territories:
  - "loc-001"
tags: [military, royal, elite]
customFields:
  motto: "Ever Vigilant"
  sigil: "Golden lion"

Member Structure

interface GroupMember {
    type: 'character' | 'location';
    id: string;
    name?: string;
    rank?: string;
    joinDate?: string;
    loyalty?: 'devoted' | 'loyal' | 'neutral' | 'wavering' | 'hostile';
}

Custom Fields

All entities support custom fields:

Adding Custom Fields

// In modal
addCustomField(name: string, value: any): void {
    this.entity.customFields = this.entity.customFields || {};
    this.entity.customFields[name] = value;
}

Serialization Modes

# Mode: "flatten" (recommended)
---
name: "Character"
weapon: "Sword"        # Custom field at root level
allegiance: "Kingdom"  # Custom field at root level
---

# Mode: "nested"
---
name: "Character"
customFields:
  weapon: "Sword"
  allegiance: "Kingdom"
---

Configure in settings: Custom fields serialization

Entity Discovery

The plugin discovers entities by scanning folders:

async listCharacters(): Promise<Character[]> {
    const folder = this.getEntityFolder('character');
    const files = await this.app.vault.getMarkdownFiles()
        .filter(f => f.path.startsWith(folder));
    
    return Promise.all(files.map(f => this.loadCharacter(f)));
}

Entity Linking

Entities reference each other by ID or name:

// Link character to event
character.events.push(event.id);
await this.saveCharacter(character);

// Link event to location
event.location = location.id;
await this.saveEvent(event);

YAML Section Parsing

EntitySections.ts handles markdown section extraction:

function parseSectionsFromMarkdown(content: string): Record<string, string> {
    // Extracts sections like:
    // ## Description
    // Content here...
    //
    // ## Backstory
    // More content...
}

Entity Templates

Entities can be created from templates:

// EntityTemplates.ts
const characterTemplate = {
    name: "{{name}}",
    status: "Alive",
    traits: [],
    sections: {
        Description: "{{description}}",
        Backstory: ""
    }
};

See 07-Template-System for full template documentation.