@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; var exactDateTimeModeId = Model.TimeModes.FirstOrDefault(x => string.Equals(x.Text, "Exact DateTime", StringComparison.OrdinalIgnoreCase))?.Value ?? string.Empty; var exactDateModeId = Model.TimeModes.FirstOrDefault(x => string.Equals(x.Text, "Exact Date", StringComparison.OrdinalIgnoreCase))?.Value ?? string.Empty; }

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

}

Timing & Structure

@if (Model.LocationConsistencyMessages.Any()) {

Location checks

    @foreach (var message in Model.LocationConsistencyMessages) {
  • @message
  • }
}

Time

Purposes

@foreach (var purpose in Model.ScenePurposeTypes) { }

Notes

Scene Metrics

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

This project has no scene metrics yet.

@if (Model.ReturnProjectID.HasValue) { Add default metrics }
} else {
@for (var index = 0; index < Model.Metrics.Count; index++) {
@Model.Metrics[index].Value
}
}
@if (Model.SceneID > 0 && Model.ReturnToTimeline) {
} @if (TempData["WriterWorkspaceError"] is string writerWorkspaceError) {
@writerWorkspaceError
}

Scene Notes

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

No working notes yet. Capture drafting thoughts, research reminders, or revision questions as they come up.

} else {
@foreach (var note in Model.Notes) {
@note.TypeName @(string.IsNullOrWhiteSpace(note.NoteTitle) ? note.NoteText : note.NoteTitle) @if (note.IsPinned) { Pinned } @if (note.IsResolved) { Resolved }
}
}

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 }
}
}

Add character to scene

@if (TempData["SceneCharacterAddError"] is string addCharacterError) {

@addCharacterError

}

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
}
}
@if (Model.RelationshipOptions.Count == 1) {
@Model.RelationshipOptions[0].Text
} else {
}

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
}
}

Choose an existing thread, or choose a plot line to create one from the event title.

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 }
}
}

Choose an existing asset, or enter a new asset name to create it from this scene.

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
}
}

Writer Workspace

Chapter workflow

Scene Checklist

@foreach (var item in Model.ChecklistItems) {
}

Attachments / Research

@if (Model.StorageUsage is not null) {

Storage used: @Model.StorageUsage.DisplayLabel

} @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) { @attachment.Title preview } else { }
@if (!string.IsNullOrWhiteSpace(attachment.Notes)) {

@attachment.Notes

} @if (hasFile) {

@System.IO.Path.GetFileName(attachment.FilePath)

}
@if (!string.IsNullOrWhiteSpace(fileHref)) { Open Download } @if (!string.IsNullOrWhiteSpace(attachment.ExternalUrl)) { Open link }
}
}
+ 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