@model SceneEditViewModel
@{
var revisionStatus = Model.RevisionStatuses.FirstOrDefault(x => x.Value == Model.RevisionStatusID.ToString())?.Text ?? "Not set";
var primaryLocation = Model.LocationOptions.FirstOrDefault(x => x.Value == Model.PrimaryLocationID?.ToString())?.Text ?? "No primary location";
var povCharacter = Model.SceneCharacters.FirstOrDefault(x => x.RoleInSceneTypeName?.Contains("POV", StringComparison.OrdinalIgnoreCase) == true)?.CharacterName
?? Model.SceneCharacters.FirstOrDefault()?.CharacterName
?? "No POV";
var timeLabel = !string.IsNullOrWhiteSpace(Model.RelativeTimeText)
? Model.RelativeTimeText
: Model.StartDateTime?.ToString("dd MMM yyyy HH:mm")
?? Model.StartDateTime?.ToString("dd MMM yyyy")
?? "Time not set";
var selectedPurposeLabels = Model.ScenePurposeTypes
.Where(x => Model.SelectedPurposeTypeIds.Contains(x.ScenePurposeTypeID))
.Select(x => x.PurposeName)
.ToList();
var dependencyCount = Model.DependenciesThisSceneNeeds.Count + Model.DependenciesNeedingThisScene.Count;
}
Working context
@(string.IsNullOrWhiteSpace(Model.SceneTitle) ? "Untitled scene" : Model.SceneTitle)
@if (Model.Chapter is not null)
{
Chapter @Model.Chapter.ChapterNumber
}
Scene @Model.SceneNumber
@povCharacter
@primaryLocation
@revisionStatus
@timeLabel
@Model.Warnings.Count warning@(Model.Warnings.Count == 1 ? "" : "s")
@Model.SceneCharacters.Count character@(Model.SceneCharacters.Count == 1 ? "" : "s")
@Model.AssetEvents.Count asset event@(Model.AssetEvents.Count == 1 ? "" : "s")
@Model.ThreadEvents.Count thread event@(Model.ThreadEvents.Count == 1 ? "" : "s")
@dependencyCount dependenc@(dependencyCount == 1 ? "y" : "ies")
@if (selectedPurposeLabels.Any())
{
@foreach (var purpose in selectedPurposeLabels)
{
@purpose
}
}
@if (!string.IsNullOrWhiteSpace(Model.SceneOutcomeNotes))
{
@Model.SceneOutcomeNotes
}
@if (Model.SceneID > 0)
{
}
@if (TempData["WriterWorkspaceError"] is string writerWorkspaceError)
{
@writerWorkspaceError
}
Attachments / Research
@if (!Model.Attachments.Any())
{
No scene references yet. Add images, links, floorplans, research notes, or files that help you write this scene.
}
else
{
@foreach (var attachment in Model.Attachments)
{
var hasFile = !string.IsNullOrWhiteSpace(attachment.FilePath);
var fileHref = hasFile && attachment.FilePath!.StartsWith("http", StringComparison.OrdinalIgnoreCase)
? attachment.FilePath
: hasFile && attachment.FilePath!.StartsWith("/")
? attachment.FilePath
: hasFile && !System.IO.Path.IsPathRooted(attachment.FilePath!)
? "/" + attachment.FilePath!.TrimStart('/', '\\')
: null;
var fileExtension = hasFile ? System.IO.Path.GetExtension(attachment.FilePath!) : string.Empty;
var isImageAttachment = !string.IsNullOrWhiteSpace(fileHref)
&& new[] { ".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp" }.Contains(fileExtension, StringComparer.OrdinalIgnoreCase);
@(hasFile ? "File" : "Link")
@attachment.TypeName
@attachment.Title
@if (isImageAttachment)
{
}
else
{
@(hasFile ? "File" : "Link")
}
@if (!string.IsNullOrWhiteSpace(attachment.Notes))
{
@attachment.Notes
}
@if (hasFile)
{
@System.IO.Path.GetFileName(attachment.FilePath)
}
}
}
+ Add Attachment
Continuity Warnings
@if (Model.SceneID > 0 && Model.ReturnProjectID.HasValue)
{
}
@if (!Model.Warnings.Any())
{
No active warnings for this scene. Run validation again after major story changes.
}
else
{
@foreach (var warning in Model.Warnings)
{
@warning.SeverityName
@warning.WarningTypeName
@warning.Message
}
}
Scene Dependencies
This scene depends on
@if (!Model.DependenciesThisSceneNeeds.Any())
{
No dependencies yet. Add one when this scene needs another moment to happen first.
}
else
{
@foreach (var dependency in Model.DependenciesThisSceneNeeds)
{
Ch @dependency.SourceChapterNumber, Scene @dependency.SourceSceneNumber
@dependency.SourceSceneTitle
@dependency.SceneDependencyTypeName @(dependency.IsHardDependency ? "/ hard" : "/ soft")
}
}
Scenes depending on this
@if (!Model.DependenciesNeedingThisScene.Any())
{
No dependent scenes yet. Later scenes that rely on this one will appear here.
}
else
{
@foreach (var dependency in Model.DependenciesNeedingThisScene)
{
Ch @dependency.TargetChapterNumber, Scene @dependency.TargetSceneNumber
@dependency.TargetSceneTitle
@dependency.SceneDependencyTypeName @(dependency.IsHardDependency ? "/ hard" : "/ soft")
}
}
Move / Reorder
Plot Thread Events
@if (!Model.ThreadEvents.Any())
{
No plot thread events attached to this scene yet. Add clues, reveals, promises, payoffs, or reversals when the scene touches a thread.
}
else
{
@foreach (var threadEvent in Model.ThreadEvents)
{
@threadEvent.MarkerText
@threadEvent.EventTitle
@threadEvent.PlotLineName / @threadEvent.ThreadTitle
}
}
Story Assets
@if (!Model.AssetEvents.Any())
{
No asset events attached to this scene yet. Add one when an object, clue, secret, or important item changes here.
}
else
{
@foreach (var assetEvent in Model.AssetEvents)
{
@assetEvent.MarkerText
@assetEvent.EventTitle
@assetEvent.AssetName / @assetEvent.AssetEventTypeName
@if (!string.IsNullOrWhiteSpace(assetEvent.ToStateName))
{
State: @(assetEvent.FromStateName ?? "Any") -> @assetEvent.ToStateName
}
}
}
Asset Locations
@if (!Model.SceneAssetLocations.Any())
{
No assets have been placed in a location for this scene yet.
}
else
{
@foreach (var assetLocation in Model.SceneAssetLocations)
{
@assetLocation.AssetName
@assetLocation.LocationName
@assetLocation.Description
}
}
Asset Custody
@if (!Model.AssetCustodyEvents.Any())
{
No custody entries attached to this scene yet.
}
else
{
@foreach (var custody in Model.AssetCustodyEvents)
{
@custody.CustodyEventTypeName
@custody.AssetName
@custody.CustodianSummary
}
}
Characters
@if (!Model.SceneCharacters.Any())
{
No characters attached to this scene yet. Add the people who appear, are mentioned, or meaningfully affect the scene.
}
else
{
@foreach (var character in Model.SceneCharacters)
{
var editId = $"scene-character-edit-{character.SceneCharacterID}";
@character.CharacterName
@((character.RoleInSceneTypeName ?? "Role not set")) • @((character.PresenceTypeName ?? "Presence not set"))
Location: @(character.LocationName ?? "Not set")
Entry: @(character.EntryLocationName ?? "Not set")
Exit: @(character.ExitLocationName ?? "Not set")
@if (!string.IsNullOrWhiteSpace(character.OutfitDescription) || !string.IsNullOrWhiteSpace(character.PhysicalCondition) || !string.IsNullOrWhiteSpace(character.EmotionalState))
{
@character.OutfitDescription @character.PhysicalCondition @character.EmotionalState
}
}
}
Character Attributes
@if (Model.CharacterAttributeEvents.Any())
{
@foreach (var item in Model.CharacterAttributeEvents)
{
@item.CharacterName: @item.AttributeName
@item.AttributeValue
@item.Description
}
}
else
{
Character attribute history for this scene will appear here when available.
}
Character Knowledge
@if (Model.CharacterKnowledge.Any())
{
@foreach (var item in Model.CharacterKnowledge)
{
@item.CharacterName @item.KnowledgeStateName
@(item.AssetName ?? item.ThreadTitle)
@item.Description
}
}
Relationship Events
@if (Model.RelationshipEvents.Any())
{
@foreach (var item in Model.RelationshipEvents)
{
@item.CharacterAName -> @item.CharacterBName
@item.RelationshipStateName / intensity @item.Intensity
@item.Description
}
}