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:
- Name (required)
- Description (text area)
- Backstory (text area)
- Status (dropdown: Alive, Deceased, Missing, Unknown)
- Traits (comma-separated)
- Profile image (gallery picker)
- Tags
- Groups (multi-select)
- Relationships (add/edit/remove)
- Custom fields
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:
- Timeline View - Point-in-time or range display
- Gantt Chart - Duration bars with dependencies
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.