@model StoryIntelligenceProgressViewModel @{ ViewData["Title"] = "Review Plot Lines & Threads"; const int previewLimit = 50; var showAll = string.Equals(Context.Request.Query["view"], "all", StringComparison.OrdinalIgnoreCase); var narrativeCandidates = showAll ? Model.NarrativeReview.Candidates.ToList() : Model.NarrativeReview.Candidates.Take(previewLimit).ToList(); var hiddenCandidateCount = Math.Max(0, Model.NarrativeReview.Candidates.Count - narrativeCandidates.Count); var eventTypes = new[] { "Introduced", "Mentioned", "Developed", "Complicated", "Clue Planted", "Question Raised", "Question Answered", "False Lead", "Revealed", "Payoff", "Resolved", "Contradicted", "Retconned", "Note" }; }

Story Intelligence

Review Plot Lines & Threads

Review recurring narrative arcs and thread candidates from the discovered hierarchy.

@if (TempData["OnboardingStoryIntelligenceError"] is string error) {
@error
} @if (TempData["OnboardingStoryIntelligenceMessage"] is string message) {
@message
} @if (Model.NarrativeReview.Candidates.Count == 0) {
No Plot Line or Thread candidates are waiting.

PlotDirector did not find recurring narrative candidates that require approval, or this review has already been completed.

Back to Review Centre
} else {
@if (hiddenCandidateCount > 0) {
View all @Model.NarrativeReview.Candidates.Count.ToString("N0")
}
@for (var i = 0; i < narrativeCandidates.Count; i++) { var candidate = narrativeCandidates[i];
@candidate.ProposedTitle @candidate.CandidateType @(!string.IsNullOrWhiteSpace(candidate.Category) ? $"- {candidate.Category}" : string.Empty) @candidate.Confidence
@if (!string.IsNullOrWhiteSpace(candidate.AuthorPlotSeedDescription)) {
Author provided

@candidate.AuthorPlotSeedDescription

@if (!string.IsNullOrWhiteSpace(candidate.SeedMatchReason)) {

@candidate.SeedMatchReason

}
} else if (string.Equals(candidate.Source, WholeBookPlotSynthesisSources.WholeBook, StringComparison.OrdinalIgnoreCase)) {

Discovered by PlotDirector

} @if (candidate.ExistingPlotLineID.HasValue && !string.IsNullOrWhiteSpace(candidate.ExistingPlotLineName)) {

Existing Plot Line: @candidate.ExistingPlotLineName

} @if (string.Equals(candidate.CandidateType, StoryIntelligenceNarrativeCandidateTypes.Thread, StringComparison.OrdinalIgnoreCase)) { @if (!string.IsNullOrWhiteSpace(candidate.ParentCandidateTitle)) {

Proposed parent: @candidate.ParentCandidateTitle

}
}
Decision
@if (candidate.ChildThreads.Count > 0) {
Suggested child Threads
    @foreach (var child in candidate.ChildThreads) {
  • @child.ProposedTitle
  • }
} @if (candidate.Events.Count > 0) {
Proposed timeline events @for (var e = 0; e < candidate.Events.Count; e++) { var item = candidate.Events[e];
@if (!string.IsNullOrWhiteSpace(item.Evidence)) {

@item.Evidence

}
}
} else {
Supporting scenes
    @foreach (var evidence in candidate.Evidence) {
  • @evidence.SceneLabel: @(string.IsNullOrWhiteSpace(evidence.Evidence) ? evidence.ContributionType : evidence.Evidence)
  • }
}
}
@if (hiddenCandidateCount > 0) { @hiddenCandidateCount.ToString("N0") more narrative decision@(hiddenCandidateCount == 1 ? string.Empty : "s") available after this batch. } Back to Review Centre
}
@section Scripts { }