@model ContinuityExplorerViewModel @{ ViewData["Title"] = $"Continuity Explorer - {Model.Project.ProjectName}"; ViewData["ProjectSection"] = "Continuity Explorer"; } @functions { private static string StateBadgeClass(string state) => state switch { "Explicit" => "bg-success", "Inferred" => "bg-secondary", _ => "bg-warning text-dark" }; private static string WarningBadgeClass(string severity) => severity switch { "Critical" => "bg-danger", "Warning" => "bg-warning text-dark", _ => "bg-info" }; private bool IsEntitySelected(string entityType) => Model.Filter.EntityTypes.Contains(entityType, StringComparer.OrdinalIgnoreCase); }

Continuity

Continuity Explorer

Investigate where characters, assets, and locations stand across the story.

Filters
Reset
@if (Model.Filter.DisplayMode == "StoryState") {

Story State

@if (!Model.StoryState.Characters.Any() && !Model.StoryState.Assets.Any() && !Model.StoryState.Locations.Any()) {

No matching continuity information found.

} else { @if (Model.Filter.IncludeCharacters) {

Characters

@foreach (var row in Model.StoryState.Characters) { }
CharacterCurrent LocationStateLast Confirmed
@row.CharacterName @row.CurrentLocation @row.State @if (row.LastConfirmedSceneID.HasValue) { @row.LastConfirmed } else { @row.LastConfirmed }
} @if (Model.Filter.IncludeAssets) {

Assets

@foreach (var row in Model.StoryState.Assets) { }
AssetHolder / LocationStateLast Confirmed
@row.AssetName @row.CurrentHolderOrLocation @row.State @if (row.LastConfirmedSceneID.HasValue) { @row.LastConfirmed } else { @row.LastConfirmed }
} @if (Model.Filter.IncludeLocations) {

Locations

@if (!Model.StoryState.Locations.Any()) {

No matching location presence found.

} else {
@foreach (var location in Model.StoryState.Locations) {

@location.LocationPath

Characters: @(location.CharactersPresent.Any() ? string.Join(", ", location.CharactersPresent) : "None")

Assets: @(location.AssetsPresent.Any() ? string.Join(", ", location.AssetsPresent) : "None")

}
} } }
} else if (Model.Filter.DisplayMode == "Journey") {

Journey

@if (!Model.CharacterJourney.Any() && !Model.AssetJourney.Any()) {

No movement recorded.

} else { @foreach (var group in Model.CharacterJourney.GroupBy(x => new { x.CharacterID, x.CharacterName })) {

@group.Key.CharacterName

@if (Model.ShowBookContext) { } @if (Model.ShowChapterContext) { } @foreach (var row in group) { @if (Model.ShowBookContext) { } @if (Model.ShowChapterContext) { } }
BookChapterScene Location
@row.BookDisplayName@row.ChapterDisplayName@row.SceneDisplayName @row.Location
} @foreach (var group in Model.AssetJourney.GroupBy(x => new { x.StoryAssetID, x.AssetName })) {

@group.Key.AssetName

@if (Model.ShowBookContext) { } @if (Model.ShowChapterContext) { } @foreach (var row in group) { @if (Model.ShowBookContext) { } @if (Model.ShowChapterContext) { } }
BookChapterScene Holder / Location
@row.BookDisplayName@row.ChapterDisplayName@row.SceneDisplayName @row.HolderOrLocation
} }
} else if (Model.Filter.DisplayMode == "ContinuityWarnings") {

Continuity Warnings

@if (!Model.ContinuityWarnings.Any()) {

No continuity concerns detected.

Continuity warnings highlight potential issues but cannot replace author judgement.

} else {
@foreach (var warning in Model.ContinuityWarnings) { }
Severity Status Type Character Knowledge Established Earlier Reference Message Scene Actions
@warning.Severity @if (warning.IsAcknowledged) { Acknowledged } else { Active } @warning.WarningTypeDisplayName @if (!string.IsNullOrWhiteSpace(warning.CharacterName)) { @warning.CharacterName } else { - } @if (!string.IsNullOrWhiteSpace(warning.KnowledgeTitle)) { @warning.KnowledgeTitle } else { - } @if (warning.EstablishedSceneId.HasValue) { @warning.EstablishedSceneDisplayName } else { - } @if (warning.ReferencedSceneId.HasValue) { @warning.ReferencedSceneDisplayName } else { - }
@warning.Message
@if (warning.IsAcknowledged && !string.IsNullOrWhiteSpace(warning.AcknowledgementNotes)) { Note: @warning.AcknowledgementNotes }
@if (warning.SceneID.HasValue) { @warning.SceneDisplayName } else { Project-wide } @if (warning.IsAcknowledged) {
@foreach (var entityType in Model.Filter.EntityTypes) { } @foreach (var characterId in Model.Filter.CharacterIDs) { } @foreach (var assetId in Model.Filter.AssetIDs) { } @foreach (var locationId in Model.Filter.LocationIDs) { }
} else {
@foreach (var entityType in Model.Filter.EntityTypes) { } @foreach (var characterId in Model.Filter.CharacterIDs) { } @foreach (var assetId in Model.Filter.AssetIDs) { } @foreach (var locationId in Model.Filter.LocationIDs) { }
}
}
} else {

Location Activity

@if (!Model.LocationActivity.Any()) {

No matching continuity information found.

} else {
@if (Model.ShowBookContext) { } @if (Model.ShowChapterContext) { } @foreach (var row in Model.LocationActivity) { @if (Model.ShowBookContext) { } @if (Model.ShowChapterContext) { } }
LocationBookChapterScene Characters Present Assets Present
@row.LocationName@row.BookDisplayName@row.ChapterDisplayName@row.SceneDisplayName @if (!row.Characters.Any()) { None } else {
@foreach (var character in row.Characters) {
@character.EntityName @character.StateType @if (!string.IsNullOrWhiteSpace(character.LastConfirmedSceneDisplayName)) { Last confirmed: @if (character.LastConfirmedSceneID.HasValue) { @character.LastConfirmedSceneDisplayName } else { @character.LastConfirmedSceneDisplayName } }
}
}
@if (!row.Assets.Any()) { None } else {
@foreach (var asset in row.Assets) {
@asset.EntityName @asset.StateType @if (!string.IsNullOrWhiteSpace(asset.Note)) { @asset.Note } @if (!string.IsNullOrWhiteSpace(asset.LastConfirmedSceneDisplayName)) { Last confirmed: @if (asset.LastConfirmedSceneID.HasValue) { @asset.LastConfirmedSceneDisplayName } else { @asset.LastConfirmedSceneDisplayName } }
}
}
}
}