PlotDirector Phase 17A - Scene Inspector V2 Shell, Layout and Summary View
This commit is contained in:
parent
ea4a38a472
commit
ab9455ac4e
File diff suppressed because it is too large
Load Diff
@ -54,10 +54,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="edit-panel scene-editor">
|
||||
<partial name="_SceneInspector" model="Model" />
|
||||
<section class="edit-panel scene-editor scene-editor-v2-workspace">
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneInspectorEditPage.cshtml" model="Model" />
|
||||
</section>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
<script src="~/js/scene-inspector-v2.js" asp-append-version="true"></script>
|
||||
}
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
@model SceneEditViewModel
|
||||
@{
|
||||
var warningCount = Model.Warnings.Count + Model.AgeContinuityWarnings.Count;
|
||||
var dependencyCount = Model.DependenciesThisSceneNeeds.Count + Model.DependenciesNeedingThisScene.Count;
|
||||
var summary = warningCount == 0
|
||||
? "No active continuity warnings for this scene."
|
||||
: $"{warningCount} warning{(warningCount == 1 ? "" : "s")} need review.";
|
||||
}
|
||||
|
||||
<article class="scene-inspector-v2-card">
|
||||
<div class="scene-inspector-v2-card-header">
|
||||
<div>
|
||||
<p class="eyebrow">Continuity</p>
|
||||
<h3>Scene health</h3>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="warnings" data-scene-id="@Model.SceneID">Review warnings</button>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-counts">
|
||||
<div class="@(warningCount > 0 ? "attention" : "")">
|
||||
<span>Warnings</span>
|
||||
<strong>@warningCount</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Dependencies</span>
|
||||
<strong>@dependencyCount</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-section">
|
||||
<span>Continuity health summary</span>
|
||||
<p>@summary</p>
|
||||
</div>
|
||||
</article>
|
||||
@ -0,0 +1,25 @@
|
||||
@model SceneEditViewModel
|
||||
|
||||
<div class="scene-inspector-v2 scene-inspector-v2-edit" data-scene-inspector-v2 data-scene-id="@Model.SceneID">
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneInspectorHealthStrip.cshtml" model="Model" />
|
||||
|
||||
<div class="scene-inspector-v2-grid">
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneOverviewCard.cshtml" model="Model" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneStoryCard.cshtml" model="Model" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_ScenePeopleCard.cshtml" model="Model" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneWorldCard.cshtml" model="Model" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneWriterCard.cshtml" model="Model" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneContinuityCard.cshtml" model="Model" />
|
||||
</div>
|
||||
|
||||
<section class="scene-inspector-editor-overlay" data-scene-editor-overlay hidden aria-hidden="true">
|
||||
<div class="scene-inspector-editor-shell">
|
||||
<div>
|
||||
<p class="eyebrow">Editor placeholder</p>
|
||||
<h3 data-scene-editor-title>Focused editor</h3>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-scene-editor-close>Cancel</button>
|
||||
</div>
|
||||
<div class="scene-inspector-editor-empty" data-scene-editor-body></div>
|
||||
</section>
|
||||
</div>
|
||||
@ -0,0 +1,43 @@
|
||||
@model SceneEditViewModel
|
||||
@{
|
||||
var status = Model.RevisionStatuses.FirstOrDefault(x => x.Value == Model.RevisionStatusID.ToString())?.Text ?? "Not set";
|
||||
var pov = Model.SceneCharacters.FirstOrDefault(x => x.RoleInSceneTypeName?.Contains("POV", StringComparison.OrdinalIgnoreCase) == true)?.CharacterName
|
||||
?? "No POV";
|
||||
var wordCount = Model.Workflow.ActualWordCount ?? Model.Workflow.EstimatedWordCount ?? 0;
|
||||
var locationCount = Model.SceneCharacters.Where(x => x.LocationID.HasValue).Select(x => x.LocationID!.Value)
|
||||
.Concat(Model.SceneAssetLocations.Select(x => x.LocationID))
|
||||
.Distinct()
|
||||
.Count();
|
||||
var warningCount = Model.Warnings.Count + Model.AgeContinuityWarnings.Count;
|
||||
}
|
||||
|
||||
<section class="scene-inspector-v2-health" aria-label="Scene health">
|
||||
<div>
|
||||
<span>Status</span>
|
||||
<strong>@status</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>POV</span>
|
||||
<strong>@pov</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Words</span>
|
||||
<strong>@wordCount.ToString("N0")</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Characters</span>
|
||||
<strong>@Model.SceneCharacters.Count</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Assets</span>
|
||||
<strong>@Model.AssetEvents.Select(x => x.StoryAssetID).Distinct().Count()</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Locations</span>
|
||||
<strong>@locationCount</strong>
|
||||
</div>
|
||||
<div class="@(warningCount > 0 ? "attention" : "")">
|
||||
<span>Warnings</span>
|
||||
<strong>@warningCount</strong>
|
||||
</div>
|
||||
</section>
|
||||
@ -0,0 +1,25 @@
|
||||
@model SceneEditViewModel
|
||||
|
||||
<div class="scene-inspector-v2 scene-inspector-v2-timeline" data-scene-inspector-v2 data-scene-id="@Model.SceneID">
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneInspectorHealthStrip.cshtml" model="Model" />
|
||||
|
||||
<div class="scene-inspector-v2-stack">
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneOverviewCard.cshtml" model="Model" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneStoryCard.cshtml" model="Model" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_ScenePeopleCard.cshtml" model="Model" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneWorldCard.cshtml" model="Model" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneWriterCard.cshtml" model="Model" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneContinuityCard.cshtml" model="Model" />
|
||||
</div>
|
||||
|
||||
<section class="scene-inspector-editor-overlay" data-scene-editor-overlay hidden aria-hidden="true">
|
||||
<div class="scene-inspector-editor-shell">
|
||||
<div>
|
||||
<p class="eyebrow">Editor placeholder</p>
|
||||
<h3 data-scene-editor-title>Focused editor</h3>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-scene-editor-close>Cancel</button>
|
||||
</div>
|
||||
<div class="scene-inspector-editor-empty" data-scene-editor-body></div>
|
||||
</section>
|
||||
</div>
|
||||
@ -0,0 +1,65 @@
|
||||
@model SceneEditViewModel
|
||||
@{
|
||||
var status = Model.RevisionStatuses.FirstOrDefault(x => x.Value == Model.RevisionStatusID.ToString())?.Text ?? "Not set";
|
||||
var pov = Model.SceneCharacters.FirstOrDefault(x => x.RoleInSceneTypeName?.Contains("POV", StringComparison.OrdinalIgnoreCase) == true)?.CharacterName
|
||||
?? "No POV character";
|
||||
var purposeLabels = Model.ScenePurposeTypes
|
||||
.Where(x => Model.SelectedPurposeTypeIds.Contains(x.ScenePurposeTypeID))
|
||||
.Select(x => x.PurposeName)
|
||||
.ToList();
|
||||
var sceneType = purposeLabels.FirstOrDefault() ?? "No scene type set";
|
||||
var primaryLocation = Model.LocationOptions.FirstOrDefault(x => x.Value == Model.PrimaryLocationID?.ToString())?.Text?.Trim() ?? "No primary location";
|
||||
var timeLabel = !string.IsNullOrWhiteSpace(Model.RelativeTimeText)
|
||||
? Model.RelativeTimeText
|
||||
: Model.StartDateTime?.ToString("d MMM yyyy HH:mm")
|
||||
?? "Time not set";
|
||||
}
|
||||
|
||||
<article class="scene-inspector-v2-card scene-overview-v2-card">
|
||||
<div class="scene-inspector-v2-card-header">
|
||||
<div>
|
||||
<p class="eyebrow">Overview</p>
|
||||
<h3>@(string.IsNullOrWhiteSpace(Model.SceneTitle) ? "Untitled scene" : Model.SceneTitle)</h3>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="scene-details" data-scene-id="@Model.SceneID">Manage scene details</button>
|
||||
</div>
|
||||
|
||||
<dl class="scene-inspector-v2-facts">
|
||||
<div>
|
||||
<dt>Position</dt>
|
||||
<dd>Chapter @Model.Chapter?.ChapterNumber, Scene @Model.SceneNumber</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Revision status</dt>
|
||||
<dd>@status</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>POV character</dt>
|
||||
<dd>@pov</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Scene type</dt>
|
||||
<dd>@sceneType</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Date/time</dt>
|
||||
<dd>@timeLabel</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Primary location</dt>
|
||||
<dd>@primaryLocation</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<div class="scene-inspector-v2-summary">
|
||||
<span>Synopsis</span>
|
||||
@if (string.IsNullOrWhiteSpace(Model.Summary))
|
||||
{
|
||||
<p class="muted">No synopsis has been added yet.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>@Model.Summary</p>
|
||||
}
|
||||
</div>
|
||||
</article>
|
||||
@ -0,0 +1,58 @@
|
||||
@model SceneEditViewModel
|
||||
@{
|
||||
var povCharacterId = Model.SceneCharacters.FirstOrDefault(x => x.RoleInSceneTypeName?.Contains("POV", StringComparison.OrdinalIgnoreCase) == true)?.CharacterID;
|
||||
}
|
||||
|
||||
<article class="scene-inspector-v2-card">
|
||||
<div class="scene-inspector-v2-card-header">
|
||||
<div>
|
||||
<p class="eyebrow">People</p>
|
||||
<h3>Characters and relationships</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-section">
|
||||
<span>Characters in scene</span>
|
||||
@if (!Model.SceneCharacters.Any())
|
||||
{
|
||||
<p class="muted">No characters have been added to this scene.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="scene-inspector-v2-list">
|
||||
@foreach (var character in Model.SceneCharacters.Take(6))
|
||||
{
|
||||
<li>
|
||||
<strong>@character.CharacterName</strong>
|
||||
<span>@(character.CharacterID == povCharacterId ? "POV" : character.RoleInSceneTypeName ?? "In scene")</span>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
@if (Model.SceneCharacters.Count > 6)
|
||||
{
|
||||
<p class="muted">+ @(Model.SceneCharacters.Count - 6) more</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-counts">
|
||||
<div>
|
||||
<span>Character count</span>
|
||||
<strong>@Model.SceneCharacters.Count</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Knowledge changes</span>
|
||||
<strong>@Model.CharacterKnowledge.Count</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Relationship changes</span>
|
||||
<strong>@Model.RelationshipEvents.Count</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-actions">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="characters" data-scene-id="@Model.SceneID">Manage characters</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="knowledge" data-scene-id="@Model.SceneID">Manage knowledge</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="relationships" data-scene-id="@Model.SceneID">Manage relationships</button>
|
||||
</div>
|
||||
</article>
|
||||
@ -0,0 +1,74 @@
|
||||
@model SceneEditViewModel
|
||||
@{
|
||||
var purposeLabels = Model.ScenePurposeTypes
|
||||
.Where(x => Model.SelectedPurposeTypeIds.Contains(x.ScenePurposeTypeID))
|
||||
.Select(x => x.PurposeName)
|
||||
.ToList();
|
||||
var threadCount = Model.ThreadEvents.Count;
|
||||
}
|
||||
|
||||
<article class="scene-inspector-v2-card">
|
||||
<div class="scene-inspector-v2-card-header">
|
||||
<div>
|
||||
<p class="eyebrow">Story</p>
|
||||
<h3>Purpose and structure</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-section">
|
||||
<span>Purpose</span>
|
||||
@if (purposeLabels.Any())
|
||||
{
|
||||
<div class="scene-inspector-v2-chip-row">
|
||||
@foreach (var purpose in purposeLabels)
|
||||
{
|
||||
<span>@purpose</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="muted">No purpose selected yet.</p>
|
||||
}
|
||||
@if (!string.IsNullOrWhiteSpace(Model.ScenePurposeNotes))
|
||||
{
|
||||
<p>@Model.ScenePurposeNotes</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-section">
|
||||
<span>Outcome</span>
|
||||
<p class="@(string.IsNullOrWhiteSpace(Model.SceneOutcomeNotes) ? "muted" : "")">@(string.IsNullOrWhiteSpace(Model.SceneOutcomeNotes) ? "No outcome has been captured yet." : Model.SceneOutcomeNotes)</p>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-section">
|
||||
<span>Plot threads</span>
|
||||
<p>@threadCount thread event@(threadCount == 1 ? "" : "s") in this scene.</p>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-section">
|
||||
<span>Metrics</span>
|
||||
@if (!Model.Metrics.Any())
|
||||
{
|
||||
<p class="muted">No active scene metrics are configured.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="scene-inspector-v2-metrics">
|
||||
@foreach (var metric in Model.Metrics)
|
||||
{
|
||||
<div>
|
||||
<span>@metric.MetricName</span>
|
||||
<strong>@metric.Value</strong>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-actions">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="purpose" data-scene-id="@Model.SceneID">Manage purpose</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="metrics" data-scene-id="@Model.SceneID">Manage metrics</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="threads" data-scene-id="@Model.SceneID">Manage threads</button>
|
||||
</div>
|
||||
</article>
|
||||
@ -0,0 +1,72 @@
|
||||
@model SceneEditViewModel
|
||||
@{
|
||||
var locationNames = Model.SceneCharacters.Where(x => !string.IsNullOrWhiteSpace(x.LocationName)).Select(x => x.LocationName!)
|
||||
.Concat(Model.SceneAssetLocations.Where(x => !string.IsNullOrWhiteSpace(x.LocationName)).Select(x => x.LocationName!))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
var assetNames = Model.AssetEvents.Select(x => x.AssetName).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
||||
}
|
||||
|
||||
<article class="scene-inspector-v2-card">
|
||||
<div class="scene-inspector-v2-card-header">
|
||||
<div>
|
||||
<p class="eyebrow">World</p>
|
||||
<h3>Locations and assets</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-section">
|
||||
<span>Locations</span>
|
||||
@if (!locationNames.Any())
|
||||
{
|
||||
<p class="muted">No scene locations have been assigned yet.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="scene-inspector-v2-chip-row">
|
||||
@foreach (var location in locationNames.Take(8))
|
||||
{
|
||||
<span>@location</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-section">
|
||||
<span>Assets</span>
|
||||
@if (!assetNames.Any())
|
||||
{
|
||||
<p class="muted">No story assets appear in this scene yet.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="scene-inspector-v2-list">
|
||||
@foreach (var asset in assetNames.Take(6))
|
||||
{
|
||||
<li><strong>@asset</strong></li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-counts">
|
||||
<div>
|
||||
<span>Asset events</span>
|
||||
<strong>@Model.AssetEvents.Count</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Custody entries</span>
|
||||
<strong>@Model.AssetCustodyEvents.Count</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Placed assets</span>
|
||||
<strong>@Model.SceneAssetLocations.Count</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-actions">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="locations" data-scene-id="@Model.SceneID">Manage locations</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="assets" data-scene-id="@Model.SceneID">Manage assets</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="custody" data-scene-id="@Model.SceneID">Manage custody</button>
|
||||
</div>
|
||||
</article>
|
||||
@ -0,0 +1,44 @@
|
||||
@model SceneEditViewModel
|
||||
@{
|
||||
var completed = Model.ChecklistItems.Count(x => x.IsCompleted);
|
||||
var total = Model.ChecklistItems.Count;
|
||||
var checklistLabel = total == 0 ? "No checklist yet" : $"{completed} of {total} complete";
|
||||
}
|
||||
|
||||
<article class="scene-inspector-v2-card">
|
||||
<div class="scene-inspector-v2-card-header">
|
||||
<div>
|
||||
<p class="eyebrow">Writer</p>
|
||||
<h3>Drafting support</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scene-inspector-v2-counts">
|
||||
<div>
|
||||
<span>Notes</span>
|
||||
<strong>@Model.Notes.Count</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Checklist</span>
|
||||
<strong>@checklistLabel</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Attachments</span>
|
||||
<strong>@Model.Attachments.Count</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.Workflow.IsBlocked)
|
||||
{
|
||||
<div class="scene-inspector-v2-callout">
|
||||
<strong>Blocked</strong>
|
||||
<p>@(string.IsNullOrWhiteSpace(Model.Workflow.BlockedReason) ? "No blocked reason has been recorded." : Model.Workflow.BlockedReason)</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="scene-inspector-v2-actions">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="notes" data-scene-id="@Model.SceneID">Manage notes</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="checklist" data-scene-id="@Model.SceneID">Manage checklist</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" data-scene-editor="attachments" data-scene-id="@Model.SceneID">Manage attachments</button>
|
||||
</div>
|
||||
</article>
|
||||
@ -1552,7 +1552,7 @@ else
|
||||
<a class="btn btn-outline-secondary btn-sm" href="@Url.Action("Index", "Timeline", new { projectId = Model.Project.ProjectID, bookId = Model.SelectedBookID })" data-inspector-close>Close</a>
|
||||
</div>
|
||||
<div class="scene-inspector-body">
|
||||
<partial name="~/Views/Scenes/_SceneInspector.cshtml" model="Model.SelectedScene" />
|
||||
<partial name="~/Views/Shared/SceneInspectorV2/_SceneInspectorTimeline.cshtml" model="Model.SelectedScene" />
|
||||
</div>
|
||||
</aside>
|
||||
}
|
||||
@ -1561,4 +1561,5 @@ else
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
<script src="~/js/scene-inspector-v2.js" asp-append-version="true"></script>
|
||||
}
|
||||
|
||||
@ -4628,6 +4628,294 @@ body.dragging-location [data-drag-type="location"] {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* Scene Inspector V2: summary-first shell scoped to the new hosts only. */
|
||||
.scene-editor-v2-workspace {
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.scene-inspector-v2 {
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
color: var(--plotline-ink);
|
||||
}
|
||||
|
||||
.scene-inspector-v2-edit {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-timeline {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-stack {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-health {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, minmax(110px, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-health > div,
|
||||
.scene-inspector-v2-card,
|
||||
.scene-inspector-editor-overlay {
|
||||
border: 1px solid var(--plotline-line);
|
||||
background: var(--plotline-panel);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 8px 24px rgba(30, 37, 43, 0.06);
|
||||
}
|
||||
|
||||
.scene-inspector-v2-health > div {
|
||||
min-width: 0;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-health span,
|
||||
.scene-inspector-v2-section > span,
|
||||
.scene-inspector-v2-counts span,
|
||||
.scene-inspector-v2-summary > span,
|
||||
.scene-inspector-v2-facts dt {
|
||||
display: block;
|
||||
color: var(--plotline-muted);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-health strong {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
color: var(--plotline-ink);
|
||||
font-size: 0.94rem;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-health .attention strong,
|
||||
.scene-inspector-v2-counts .attention strong {
|
||||
color: var(--plotline-danger, #b35f3b);
|
||||
}
|
||||
|
||||
.scene-inspector-v2-card {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 14px;
|
||||
min-width: 0;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-card-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-card-header h3 {
|
||||
margin: 0;
|
||||
font-size: 1.08rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-card p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-facts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-facts div {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-facts dd {
|
||||
margin: 2px 0 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-summary,
|
||||
.scene-inspector-v2-section {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-chip-row,
|
||||
.scene-inspector-v2-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-chip-row span {
|
||||
border: 1px solid rgba(47, 111, 99, 0.18);
|
||||
border-radius: 999px;
|
||||
background: rgba(47, 111, 99, 0.08);
|
||||
padding: 3px 9px;
|
||||
color: var(--plotline-accent-dark);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-metrics,
|
||||
.scene-inspector-v2-counts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-metrics div,
|
||||
.scene-inspector-v2-counts div {
|
||||
min-width: 0;
|
||||
border: 1px solid rgba(47, 111, 99, 0.12);
|
||||
border-radius: 8px;
|
||||
background: rgba(47, 111, 99, 0.05);
|
||||
padding: 9px 10px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-metrics span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
color: var(--plotline-muted);
|
||||
font-size: 0.78rem;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-metrics strong,
|
||||
.scene-inspector-v2-counts strong {
|
||||
display: block;
|
||||
color: var(--plotline-ink);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-list {
|
||||
display: grid;
|
||||
gap: 7px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-list li {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
border-bottom: 1px solid rgba(47, 111, 99, 0.1);
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-list li:last-child {
|
||||
border-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-list span {
|
||||
color: var(--plotline-muted);
|
||||
font-size: 0.82rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-callout {
|
||||
border-left: 4px solid var(--plotline-warning, #c58b2b);
|
||||
border-radius: 8px;
|
||||
background: rgba(197, 139, 43, 0.1);
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.scene-inspector-editor-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 4;
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.scene-inspector-editor-overlay[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scene-inspector-editor-shell {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.scene-inspector-editor-shell h3 {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.scene-inspector-editor-empty {
|
||||
min-height: 180px;
|
||||
border: 1px dashed rgba(47, 111, 99, 0.24);
|
||||
border-radius: 8px;
|
||||
background: rgba(47, 111, 99, 0.04);
|
||||
}
|
||||
|
||||
.scene-inspector-v2-timeline .scene-inspector-v2-health {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.scene-inspector-v2-timeline .scene-inspector-v2-card {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-timeline .scene-inspector-v2-card-header,
|
||||
.scene-inspector-v2-timeline .scene-inspector-v2-list li {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-timeline .scene-inspector-v2-facts,
|
||||
.scene-inspector-v2-timeline .scene-inspector-v2-metrics,
|
||||
.scene-inspector-v2-timeline .scene-inspector-v2-counts {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.scene-inspector-v2-grid,
|
||||
.scene-inspector-v2-health {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.scene-inspector-v2-grid,
|
||||
.scene-inspector-v2-health,
|
||||
.scene-inspector-v2-facts,
|
||||
.scene-inspector-v2-metrics,
|
||||
.scene-inspector-v2-counts {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.scene-inspector-v2-card-header {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.floor-plan-toolbar-floor-group {
|
||||
flex: 1 1 420px;
|
||||
min-width: min(100%, 320px);
|
||||
|
||||
2
PlotLine/wwwroot/css/site.min.css
vendored
2
PlotLine/wwwroot/css/site.min.css
vendored
File diff suppressed because one or more lines are too long
55
PlotLine/wwwroot/js/scene-inspector-v2.js
Normal file
55
PlotLine/wwwroot/js/scene-inspector-v2.js
Normal file
@ -0,0 +1,55 @@
|
||||
(() => {
|
||||
const titles = {
|
||||
"scene-details": "Manage scene details",
|
||||
purpose: "Manage purpose",
|
||||
metrics: "Manage metrics",
|
||||
threads: "Manage threads",
|
||||
characters: "Manage characters",
|
||||
knowledge: "Manage knowledge",
|
||||
relationships: "Manage relationships",
|
||||
locations: "Manage locations",
|
||||
assets: "Manage assets",
|
||||
custody: "Manage custody",
|
||||
notes: "Manage notes",
|
||||
checklist: "Manage checklist",
|
||||
attachments: "Manage attachments",
|
||||
warnings: "Review warnings"
|
||||
};
|
||||
|
||||
document.addEventListener("click", event => {
|
||||
const openButton = event.target.closest("[data-scene-editor]");
|
||||
if (openButton) {
|
||||
const root = openButton.closest("[data-scene-inspector-v2]");
|
||||
const overlay = root?.querySelector("[data-scene-editor-overlay]");
|
||||
if (!overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = openButton.dataset.sceneEditor;
|
||||
const title = overlay.querySelector("[data-scene-editor-title]");
|
||||
const body = overlay.querySelector("[data-scene-editor-body]");
|
||||
if (title) {
|
||||
title.textContent = titles[key] || "Focused editor";
|
||||
}
|
||||
if (body) {
|
||||
body.textContent = "";
|
||||
}
|
||||
overlay.hidden = false;
|
||||
overlay.setAttribute("aria-hidden", "false");
|
||||
root.classList.add("editor-overlay-open");
|
||||
overlay.querySelector("[data-scene-editor-close]")?.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const closeButton = event.target.closest("[data-scene-editor-close]");
|
||||
if (closeButton) {
|
||||
const overlay = closeButton.closest("[data-scene-editor-overlay]");
|
||||
const root = closeButton.closest("[data-scene-inspector-v2]");
|
||||
if (overlay) {
|
||||
overlay.hidden = true;
|
||||
overlay.setAttribute("aria-hidden", "true");
|
||||
}
|
||||
root?.classList.remove("editor-overlay-open");
|
||||
}
|
||||
});
|
||||
})();
|
||||
Loading…
x
Reference in New Issue
Block a user