Phase 10G: Word Companion UX Consolidation
This commit is contained in:
parent
b813b187d7
commit
8d6399358e
@ -1,7 +1,7 @@
|
|||||||
@model PlotLine.ViewModels.WordCompanionHostViewModel
|
@model PlotLine.ViewModels.WordCompanionHostViewModel
|
||||||
@{
|
@{
|
||||||
Layout = null;
|
Layout = null;
|
||||||
var statusText = Model.IsAuthenticated ? "Connected to PlotDirector" : "Please sign in to PlotDirector";
|
var statusText = Model.IsAuthenticated ? "Connected" : "Not signed in";
|
||||||
}
|
}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@ -15,8 +15,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<main class="word-companion-shell" data-authenticated="@Model.IsAuthenticated.ToString().ToLowerInvariant()">
|
<main class="word-companion-shell" data-authenticated="@Model.IsAuthenticated.ToString().ToLowerInvariant()">
|
||||||
<header class="word-companion-header">
|
<header class="word-companion-header">
|
||||||
<p class="word-companion-eyebrow">PlotDirector</p>
|
<h1>PlotDirector Companion</h1>
|
||||||
<h1>Companion</h1>
|
|
||||||
<p class="word-companion-status" data-connection-status>@statusText</p>
|
<p class="word-companion-status" data-connection-status>@statusText</p>
|
||||||
@if (Model.IsAuthenticated)
|
@if (Model.IsAuthenticated)
|
||||||
{
|
{
|
||||||
@ -44,17 +43,17 @@
|
|||||||
<p class="word-companion-message" data-book-message></p>
|
<p class="word-companion-message" data-book-message></p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="word-companion-section" aria-label="Current Scene">
|
<section class="word-companion-section word-companion-current-scene" aria-label="Current Scene">
|
||||||
<span>Current Scene</span>
|
|
||||||
<strong>(Not connected)</strong>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="word-companion-section word-companion-document" aria-label="Document Structure">
|
|
||||||
<div class="word-companion-section-header">
|
<div class="word-companion-section-header">
|
||||||
<span>Document Structure</span>
|
<span>Current Scene</span>
|
||||||
<button type="button" data-refresh-document>Refresh Document Structure</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="word-companion-structure-grid">
|
<div class="word-companion-actions">
|
||||||
|
<button type="button" class="word-companion-primary-action" data-refresh-current-scene>Refresh Current Scene</button>
|
||||||
|
<button type="button" data-sync-scene-progress disabled>Sync Progress</button>
|
||||||
|
</div>
|
||||||
|
<p class="word-companion-message" data-document-message></p>
|
||||||
|
<p class="word-companion-message" data-sync-status>No resolved PlotDirector scene.</p>
|
||||||
|
<div class="word-companion-scene-card">
|
||||||
<div>
|
<div>
|
||||||
<span>Current Chapter</span>
|
<span>Current Chapter</span>
|
||||||
<strong data-current-chapter>Not detected</strong>
|
<strong data-current-chapter>Not detected</strong>
|
||||||
@ -64,24 +63,80 @@
|
|||||||
<strong data-current-scene>Not detected</strong>
|
<strong data-current-scene>Not detected</strong>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>Current Scene Word Count</span>
|
<span>Link Status</span>
|
||||||
|
<strong data-plotdirector-scene-status>Not detected</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Word Count in Word</span>
|
||||||
<strong data-current-word-count>-</strong>
|
<strong data-current-word-count>-</strong>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>PlotDirector Actual Words</span>
|
||||||
|
<strong data-plotdirector-actual-words>-</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Estimated Words</span>
|
||||||
|
<strong data-plotdirector-estimated-words>-</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Revision Status</span>
|
||||||
|
<strong data-plotdirector-revision-status>-</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Blocked Status</span>
|
||||||
|
<strong data-plotdirector-blocked-status>-</strong>
|
||||||
|
</div>
|
||||||
|
<div data-plotdirector-blocked-reason-row hidden>
|
||||||
|
<span>Blocked Reason</span>
|
||||||
|
<strong data-plotdirector-blocked-reason>-</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="word-companion-sync-meta">
|
||||||
|
<div>
|
||||||
|
<span>Last Synced</span>
|
||||||
|
<strong data-sync-last-synced>-</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Sync Count</span>
|
||||||
|
<strong data-sync-plotdirector-count>-</strong>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="word-companion-message" data-document-message></p>
|
|
||||||
<p class="word-companion-message" data-sync-note></p>
|
<p class="word-companion-message" data-sync-note></p>
|
||||||
<div class="word-companion-outline" data-document-outline>
|
</section>
|
||||||
<p>No structure scanned.</p>
|
|
||||||
|
<section class="word-companion-section word-companion-brief" data-writing-brief-block hidden aria-label="Writing Brief">
|
||||||
|
<span>Writing Brief</span>
|
||||||
|
<p data-writing-brief></p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="word-companion-section word-companion-link-groups" data-plotdirector-link-groups hidden aria-label="Scene Links">
|
||||||
|
<div>
|
||||||
|
<span>Characters</span>
|
||||||
|
<div class="word-companion-chip-list" data-character-chips></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Assets</span>
|
||||||
|
<div class="word-companion-chip-list" data-asset-chips></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Locations</span>
|
||||||
|
<div class="word-companion-chip-list" data-location-chips></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="word-companion-section word-companion-plotdirector-scene" aria-label="PlotDirector Scene">
|
<details class="word-companion-section word-companion-collapsible">
|
||||||
<div class="word-companion-section-header">
|
<summary>Document Outline</summary>
|
||||||
<span>PlotDirector Scene</span>
|
<div class="word-companion-outline" data-document-outline>
|
||||||
<button type="button" data-refresh-plotdirector-scene>Refresh PlotDirector Scene</button>
|
<p>No structure scanned.</p>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details class="word-companion-section word-companion-collapsible">
|
||||||
|
<summary>Diagnostics</summary>
|
||||||
|
<div class="word-companion-section-header">
|
||||||
|
<span>Diagnostics</span>
|
||||||
|
<button type="button" data-run-diagnostics>Run Diagnostics</button>
|
||||||
</div>
|
</div>
|
||||||
<span>Scene Status</span>
|
|
||||||
<p class="word-companion-message" data-plotdirector-scene-status>Select a PlotDirector book.</p>
|
|
||||||
<div class="word-companion-resolve-diagnostics" aria-label="Resolve diagnostics">
|
<div class="word-companion-resolve-diagnostics" aria-label="Resolve diagnostics">
|
||||||
<div>
|
<div>
|
||||||
<span>Selected Project ID</span>
|
<span>Selected Project ID</span>
|
||||||
@ -127,83 +182,6 @@
|
|||||||
<span>Scene ID</span>
|
<span>Scene ID</span>
|
||||||
<strong data-resolve-scene-id>-</strong>
|
<strong data-resolve-scene-id>-</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="word-companion-scene-details" data-plotdirector-scene-details hidden>
|
|
||||||
<div>
|
|
||||||
<span>Scene Title</span>
|
|
||||||
<strong data-plotdirector-scene-title>-</strong>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Revision Status</span>
|
|
||||||
<strong data-plotdirector-revision-status>-</strong>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Estimated Words</span>
|
|
||||||
<strong data-plotdirector-estimated-words>-</strong>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Actual Words</span>
|
|
||||||
<strong data-plotdirector-actual-words>-</strong>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Blocked Status</span>
|
|
||||||
<strong data-plotdirector-blocked-status>-</strong>
|
|
||||||
</div>
|
|
||||||
<div data-plotdirector-blocked-reason-row hidden>
|
|
||||||
<span>Blocked Reason</span>
|
|
||||||
<strong data-plotdirector-blocked-reason>-</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="word-companion-progress-sync">
|
|
||||||
<div class="word-companion-section-header">
|
|
||||||
<span>Progress Sync</span>
|
|
||||||
<button type="button" data-sync-scene-progress disabled>Sync Scene Progress</button>
|
|
||||||
</div>
|
|
||||||
<div class="word-companion-progress-grid">
|
|
||||||
<div>
|
|
||||||
<span>Word Document Count</span>
|
|
||||||
<strong data-sync-word-count>-</strong>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>PlotDirector Count</span>
|
|
||||||
<strong data-sync-plotdirector-count>-</strong>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Last Synced</span>
|
|
||||||
<strong data-sync-last-synced>-</strong>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Sync Status</span>
|
|
||||||
<strong data-sync-status>No resolved PlotDirector scene.</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="word-companion-brief" data-writing-brief-block hidden>
|
|
||||||
<span>Writing Brief</span>
|
|
||||||
<p data-writing-brief></p>
|
|
||||||
</div>
|
|
||||||
<div class="word-companion-link-groups" data-plotdirector-link-groups hidden>
|
|
||||||
<div>
|
|
||||||
<span>Characters</span>
|
|
||||||
<div class="word-companion-chip-list" data-character-chips></div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Assets</span>
|
|
||||||
<div class="word-companion-chip-list" data-asset-chips></div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Locations</span>
|
|
||||||
<div class="word-companion-chip-list" data-location-chips></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="word-companion-section" aria-label="Diagnostics">
|
|
||||||
<div class="word-companion-section-header">
|
|
||||||
<span>Diagnostics</span>
|
|
||||||
<button type="button" data-run-diagnostics>Run Diagnostics</button>
|
|
||||||
</div>
|
|
||||||
<div class="word-companion-diagnostics-grid">
|
|
||||||
<div>
|
<div>
|
||||||
<span>Office Host</span>
|
<span>Office Host</span>
|
||||||
<strong data-diagnostic-host>Unknown</strong>
|
<strong data-diagnostic-host>Unknown</strong>
|
||||||
@ -224,6 +202,10 @@
|
|||||||
<span>Last Scan Result</span>
|
<span>Last Scan Result</span>
|
||||||
<strong data-diagnostic-last-scan>Not run</strong>
|
<strong data-diagnostic-last-scan>Not run</strong>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Last Error</span>
|
||||||
|
<strong data-diagnostic-last-error>-</strong>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>Paragraphs Found</span>
|
<span>Paragraphs Found</span>
|
||||||
<strong data-diagnostic-paragraphs>-</strong>
|
<strong data-diagnostic-paragraphs>-</strong>
|
||||||
@ -237,22 +219,7 @@
|
|||||||
<strong data-diagnostic-heading2>-</strong>
|
<strong data-diagnostic-heading2>-</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</details>
|
||||||
|
|
||||||
<section class="word-companion-summary" aria-label="Connection summary">
|
|
||||||
<div>
|
|
||||||
<span>Connection Status</span>
|
|
||||||
<strong data-summary-connection>@statusText</strong>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Current Project</span>
|
|
||||||
<strong data-summary-project>(Not connected)</strong>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Current Book</span>
|
|
||||||
<strong data-summary-book>(Not connected)</strong>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<footer class="word-companion-footer">
|
<footer class="word-companion-footer">
|
||||||
<span data-office-status>Word Companion loading</span>
|
<span data-office-status>Word Companion loading</span>
|
||||||
|
|||||||
@ -13,6 +13,10 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-width: 280px;
|
min-width: 280px;
|
||||||
@ -137,12 +141,42 @@ body {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.word-companion-actions button:disabled,
|
||||||
.word-companion-section-header button:disabled {
|
.word-companion-section-header button:disabled {
|
||||||
border-color: var(--companion-line);
|
border-color: var(--companion-line);
|
||||||
background: var(--companion-soft);
|
background: var(--companion-soft);
|
||||||
color: var(--companion-muted);
|
color: var(--companion-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.word-companion-actions {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-companion-actions button {
|
||||||
|
min-height: 36px;
|
||||||
|
border: 1px solid var(--companion-accent);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
color: var(--companion-accent-dark);
|
||||||
|
font: inherit;
|
||||||
|
font-size: 0.84rem;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-companion-actions .word-companion-primary-action {
|
||||||
|
background: var(--companion-accent);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-companion-actions button:disabled {
|
||||||
|
border-color: var(--companion-line);
|
||||||
|
background: var(--companion-soft);
|
||||||
|
color: var(--companion-muted);
|
||||||
|
}
|
||||||
|
|
||||||
.word-companion-message:empty {
|
.word-companion-message:empty {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -153,17 +187,33 @@ body {
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.word-companion-scene-card,
|
||||||
.word-companion-structure-grid {
|
.word-companion-structure-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.word-companion-scene-card {
|
||||||
|
border: 1px solid rgba(47, 111, 99, 0.18);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
background: var(--companion-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-companion-scene-card div,
|
||||||
|
.word-companion-sync-meta div,
|
||||||
.word-companion-structure-grid div,
|
.word-companion-structure-grid div,
|
||||||
.word-companion-diagnostics-grid div {
|
.word-companion-diagnostics-grid div {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.word-companion-sync-meta {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.word-companion-diagnostics-grid {
|
.word-companion-diagnostics-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@ -189,8 +239,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.word-companion-resolve-diagnostics {
|
.word-companion-resolve-diagnostics {
|
||||||
border-top: 1px solid var(--companion-line);
|
padding-top: 4px;
|
||||||
padding-top: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.word-companion-resolve-diagnostics strong {
|
.word-companion-resolve-diagnostics strong {
|
||||||
@ -221,8 +270,6 @@ body {
|
|||||||
.word-companion-brief {
|
.word-companion-brief {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
border-top: 1px solid var(--companion-line);
|
|
||||||
padding-top: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.word-companion-brief p {
|
.word-companion-brief p {
|
||||||
@ -263,8 +310,7 @@ body {
|
|||||||
.word-companion-outline {
|
.word-companion-outline {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
border-top: 1px solid var(--companion-line);
|
padding-top: 4px;
|
||||||
padding-top: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.word-companion-outline p {
|
.word-companion-outline p {
|
||||||
@ -310,6 +356,31 @@ body {
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.word-companion-collapsible {
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-companion-collapsible:not([open]) > :not(summary) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-companion-collapsible summary {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--companion-ink);
|
||||||
|
font-size: 0.92rem;
|
||||||
|
font-weight: 800;
|
||||||
|
list-style-position: outside;
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-companion-collapsible[open] summary {
|
||||||
|
padding-bottom: 6px;
|
||||||
|
border-bottom: 1px solid var(--companion-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.word-companion-collapsible .word-companion-section-header {
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.word-companion-footer {
|
.word-companion-footer {
|
||||||
border-top: 1px solid var(--companion-line);
|
border-top: 1px solid var(--companion-line);
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
const bookSelect = document.querySelector("[data-book-select]");
|
const bookSelect = document.querySelector("[data-book-select]");
|
||||||
const projectMessage = document.querySelector("[data-project-message]");
|
const projectMessage = document.querySelector("[data-project-message]");
|
||||||
const bookMessage = document.querySelector("[data-book-message]");
|
const bookMessage = document.querySelector("[data-book-message]");
|
||||||
|
const refreshCurrentSceneButton = document.querySelector("[data-refresh-current-scene]");
|
||||||
const refreshDocumentButton = document.querySelector("[data-refresh-document]");
|
const refreshDocumentButton = document.querySelector("[data-refresh-document]");
|
||||||
const currentChapter = document.querySelector("[data-current-chapter]");
|
const currentChapter = document.querySelector("[data-current-chapter]");
|
||||||
const currentScene = document.querySelector("[data-current-scene]");
|
const currentScene = document.querySelector("[data-current-scene]");
|
||||||
@ -59,6 +60,7 @@
|
|||||||
const diagnosticReady = document.querySelector("[data-diagnostic-ready]");
|
const diagnosticReady = document.querySelector("[data-diagnostic-ready]");
|
||||||
const diagnosticWordApi = document.querySelector("[data-diagnostic-word-api]");
|
const diagnosticWordApi = document.querySelector("[data-diagnostic-word-api]");
|
||||||
const diagnosticLastScan = document.querySelector("[data-diagnostic-last-scan]");
|
const diagnosticLastScan = document.querySelector("[data-diagnostic-last-scan]");
|
||||||
|
const diagnosticLastError = document.querySelector("[data-diagnostic-last-error]");
|
||||||
const diagnosticParagraphs = document.querySelector("[data-diagnostic-paragraphs]");
|
const diagnosticParagraphs = document.querySelector("[data-diagnostic-paragraphs]");
|
||||||
const diagnosticHeading1 = document.querySelector("[data-diagnostic-heading1]");
|
const diagnosticHeading1 = document.querySelector("[data-diagnostic-heading1]");
|
||||||
const diagnosticHeading2 = document.querySelector("[data-diagnostic-heading2]");
|
const diagnosticHeading2 = document.querySelector("[data-diagnostic-heading2]");
|
||||||
@ -148,6 +150,15 @@
|
|||||||
setSyncStatus(resolvedSceneId ? "Ready to sync." : "No resolved PlotDirector scene.");
|
setSyncStatus(resolvedSceneId ? "Ready to sync." : "No resolved PlotDirector scene.");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setRefreshCurrentSceneBusy = (isBusy) => {
|
||||||
|
if (!refreshCurrentSceneButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshCurrentSceneButton.disabled = isBusy;
|
||||||
|
refreshCurrentSceneButton.textContent = isBusy ? "Refreshing..." : "Refresh Current Scene";
|
||||||
|
};
|
||||||
|
|
||||||
const displayValue = (value) => {
|
const displayValue = (value) => {
|
||||||
if (value === null || value === undefined || value === "") {
|
if (value === null || value === undefined || value === "") {
|
||||||
return "-";
|
return "-";
|
||||||
@ -163,6 +174,13 @@
|
|||||||
setHidden(plotDirectorLinkGroups, true);
|
setHidden(plotDirectorLinkGroups, true);
|
||||||
setResolvedScene(null);
|
setResolvedScene(null);
|
||||||
setText(syncPlotDirectorCount, "-");
|
setText(syncPlotDirectorCount, "-");
|
||||||
|
setText(plotDirectorSceneTitle, "-");
|
||||||
|
setText(plotDirectorRevisionStatus, "-");
|
||||||
|
setText(plotDirectorEstimatedWords, "-");
|
||||||
|
setText(plotDirectorActualWords, "-");
|
||||||
|
setText(plotDirectorBlockedStatus, "-");
|
||||||
|
setText(plotDirectorBlockedReason, "-");
|
||||||
|
setHidden(plotDirectorBlockedReasonRow, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderLinkedItems = (container, items) => {
|
const renderLinkedItems = (container, items) => {
|
||||||
@ -205,7 +223,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (writingBrief) {
|
if (writingBrief) {
|
||||||
writingBrief.textContent = data?.writingBrief || "No writing brief available.";
|
writingBrief.textContent = data?.writingBrief || "No writing brief has been added for this scene.";
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLinkedItems(characterChips, data?.characters);
|
renderLinkedItems(characterChips, data?.characters);
|
||||||
@ -285,6 +303,9 @@
|
|||||||
if (hasValue("lastScan")) {
|
if (hasValue("lastScan")) {
|
||||||
setText(diagnosticLastScan, values.lastScan);
|
setText(diagnosticLastScan, values.lastScan);
|
||||||
}
|
}
|
||||||
|
if (hasValue("lastError")) {
|
||||||
|
setText(diagnosticLastError, values.lastError);
|
||||||
|
}
|
||||||
if (hasValue("paragraphs")) {
|
if (hasValue("paragraphs")) {
|
||||||
setText(diagnosticParagraphs, values.paragraphs);
|
setText(diagnosticParagraphs, values.paragraphs);
|
||||||
}
|
}
|
||||||
@ -423,7 +444,7 @@
|
|||||||
summaryBook.textContent = book?.title || "(Not connected)";
|
summaryBook.textContent = book?.title || "(Not connected)";
|
||||||
}
|
}
|
||||||
if (syncNote) {
|
if (syncNote) {
|
||||||
syncNote.textContent = book ? "" : "Select a PlotDirector book before syncing in a future step.";
|
syncNote.textContent = book ? "" : "Select a PlotDirector book before refreshing.";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -564,8 +585,8 @@
|
|||||||
if (!officeReady || !wordHostAvailable || !window.Word || typeof window.Word.run !== "function") {
|
if (!officeReady || !wordHostAvailable || !window.Word || typeof window.Word.run !== "function") {
|
||||||
setCurrentStructure(null, null, null);
|
setCurrentStructure(null, null, null);
|
||||||
setDocumentMessage("Word document access is unavailable.");
|
setDocumentMessage("Word document access is unavailable.");
|
||||||
setDiagnostics({ lastScan: "Failure" });
|
setDiagnostics({ lastScan: "Failure", lastError: "Word document access is unavailable." });
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refreshDocumentButton) {
|
if (refreshDocumentButton) {
|
||||||
@ -591,21 +612,24 @@
|
|||||||
structure.currentScene?.title,
|
structure.currentScene?.title,
|
||||||
structure.currentScene?.wordCount
|
structure.currentScene?.wordCount
|
||||||
);
|
);
|
||||||
resetPlotDirectorScene(selectedBook() ? "No resolved PlotDirector scene." : "Select a PlotDirector book.");
|
resetPlotDirectorScene("Not detected");
|
||||||
renderOutline(structure);
|
renderOutline(structure);
|
||||||
setDocumentMessage("");
|
setDocumentMessage("");
|
||||||
setDiagnostics({
|
setDiagnostics({
|
||||||
lastScan: "Success",
|
lastScan: "Success",
|
||||||
|
lastError: "-",
|
||||||
paragraphs: String(structure.paragraphCount),
|
paragraphs: String(structure.paragraphCount),
|
||||||
heading1: String(structure.heading1Count),
|
heading1: String(structure.heading1Count),
|
||||||
heading2: String(structure.heading2Count)
|
heading2: String(structure.heading2Count)
|
||||||
});
|
});
|
||||||
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = errorText(error);
|
const message = errorText(error);
|
||||||
console.error("Unable to scan the Word document.", error);
|
console.error("Unable to scan the Word document.", error);
|
||||||
setCurrentStructure(null, null, null);
|
setCurrentStructure(null, null, null);
|
||||||
setDocumentMessage(`Unable to scan the Word document: ${message}`);
|
setDocumentMessage("Unable to scan the Word document.");
|
||||||
setDiagnostics({ lastScan: "Failure" });
|
setDiagnostics({ lastScan: "Failure", lastError: message });
|
||||||
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
if (refreshDocumentButton) {
|
if (refreshDocumentButton) {
|
||||||
refreshDocumentButton.disabled = false;
|
refreshDocumentButton.disabled = false;
|
||||||
@ -626,6 +650,7 @@
|
|||||||
if (!officeReady || !wordHostAvailable || !window.Word || typeof window.Word.run !== "function") {
|
if (!officeReady || !wordHostAvailable || !window.Word || typeof window.Word.run !== "function") {
|
||||||
setDiagnostics({
|
setDiagnostics({
|
||||||
lastScan: "Failure",
|
lastScan: "Failure",
|
||||||
|
lastError: "Word document access is unavailable.",
|
||||||
paragraphs: "-",
|
paragraphs: "-",
|
||||||
heading1: "-",
|
heading1: "-",
|
||||||
heading2: "-"
|
heading2: "-"
|
||||||
@ -642,12 +667,13 @@
|
|||||||
|
|
||||||
setDiagnostics({
|
setDiagnostics({
|
||||||
lastScan: "Success",
|
lastScan: "Success",
|
||||||
|
lastError: "-",
|
||||||
paragraphs: String(paragraphCount)
|
paragraphs: String(paragraphCount)
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Word Companion diagnostics failed.", error);
|
console.error("Word Companion diagnostics failed.", error);
|
||||||
setDiagnostics({ lastScan: "Failure" });
|
setDiagnostics({ lastScan: "Failure", lastError: errorText(error) });
|
||||||
setDocumentMessage(`Unable to scan the Word document: ${errorText(error)}`);
|
setDocumentMessage("Unable to scan the Word document.");
|
||||||
} finally {
|
} finally {
|
||||||
if (runDiagnosticsButton) {
|
if (runDiagnosticsButton) {
|
||||||
runDiagnosticsButton.disabled = false;
|
runDiagnosticsButton.disabled = false;
|
||||||
@ -716,7 +742,7 @@
|
|||||||
} finally {
|
} finally {
|
||||||
if (syncSceneProgressButton) {
|
if (syncSceneProgressButton) {
|
||||||
syncSceneProgressButton.disabled = !resolvedSceneId;
|
syncSceneProgressButton.disabled = !resolvedSceneId;
|
||||||
syncSceneProgressButton.textContent = "Sync Scene Progress";
|
syncSceneProgressButton.textContent = "Sync Progress";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -742,15 +768,17 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!book) {
|
if (!book) {
|
||||||
resetPlotDirectorScene("Select a PlotDirector book.");
|
resetPlotDirectorScene("Not detected");
|
||||||
|
setDocumentMessage("Select a PlotDirector book first.");
|
||||||
setResolveDiagnostics({ result: "Error" });
|
setResolveDiagnostics({ result: "Error" });
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!detectedChapterTitle || !detectedSceneTitle) {
|
if (!detectedChapterTitle || !detectedSceneTitle) {
|
||||||
resetPlotDirectorScene("No scene detected in Word.");
|
resetPlotDirectorScene("Not detected");
|
||||||
|
setDocumentMessage("No scene detected in Word. Use Heading 2 for scenes.");
|
||||||
setResolveDiagnostics({ result: "Error" });
|
setResolveDiagnostics({ result: "Error" });
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refreshPlotDirectorSceneButton) {
|
if (refreshPlotDirectorSceneButton) {
|
||||||
@ -763,7 +791,8 @@
|
|||||||
refreshPlotDirectorSceneButton.textContent = "Refresh PlotDirector Scene";
|
refreshPlotDirectorSceneButton.textContent = "Refresh PlotDirector Scene";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
resetPlotDirectorScene("Resolving PlotDirector scene...");
|
resetPlotDirectorScene("Not detected");
|
||||||
|
setDocumentMessage("Resolving PlotDirector scene...");
|
||||||
|
|
||||||
let resolvedSceneId = null;
|
let resolvedSceneId = null;
|
||||||
try {
|
try {
|
||||||
@ -773,14 +802,15 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!resolveResponse?.matched || !resolveResponse.sceneId) {
|
if (!resolveResponse?.matched || !resolveResponse.sceneId) {
|
||||||
resetPlotDirectorScene("No exact match found for this chapter/scene in the selected book.");
|
resetPlotDirectorScene("Not found in PlotDirector");
|
||||||
|
setDocumentMessage("Scene not found in PlotDirector.");
|
||||||
setResolveDiagnostics({
|
setResolveDiagnostics({
|
||||||
result: "Not matched",
|
result: "Not matched",
|
||||||
chapterId: resolveResponse?.chapterId,
|
chapterId: resolveResponse?.chapterId,
|
||||||
sceneId: resolveResponse?.sceneId
|
sceneId: resolveResponse?.sceneId
|
||||||
});
|
});
|
||||||
restorePlotDirectorSceneButton();
|
restorePlotDirectorSceneButton();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedSceneId = resolveResponse.sceneId;
|
resolvedSceneId = resolveResponse.sceneId;
|
||||||
@ -790,10 +820,11 @@
|
|||||||
sceneId: resolveResponse.sceneId
|
sceneId: resolveResponse.sceneId
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
resetPlotDirectorScene("Unable to resolve scene.");
|
resetPlotDirectorScene("Not found in PlotDirector");
|
||||||
|
setDocumentMessage("Scene not found in PlotDirector.");
|
||||||
setResolveDiagnostics({ result: "Error" });
|
setResolveDiagnostics({ result: "Error" });
|
||||||
restorePlotDirectorSceneButton();
|
restorePlotDirectorSceneButton();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -804,16 +835,46 @@
|
|||||||
companionData.revisionStatus = "";
|
companionData.revisionStatus = "";
|
||||||
}
|
}
|
||||||
setPlotDirectorSceneStatus("Linked to PlotDirector");
|
setPlotDirectorSceneStatus("Linked to PlotDirector");
|
||||||
|
setDocumentMessage("");
|
||||||
renderCompanionData(companionData);
|
renderCompanionData(companionData);
|
||||||
setResolvedScene(resolvedSceneId);
|
setResolvedScene(resolvedSceneId);
|
||||||
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
resetPlotDirectorScene("Unable to load PlotDirector scene data.");
|
resetPlotDirectorScene("Not found in PlotDirector");
|
||||||
|
setDocumentMessage("Unable to load PlotDirector scene data.");
|
||||||
setResolveDiagnostics({ result: "Error" });
|
setResolveDiagnostics({ result: "Error" });
|
||||||
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
restorePlotDirectorSceneButton();
|
restorePlotDirectorSceneButton();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const refreshCurrentScene = async () => {
|
||||||
|
if (!selectedBook()) {
|
||||||
|
setDocumentMessage("Select a PlotDirector book first.");
|
||||||
|
resetPlotDirectorScene("Not detected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRefreshCurrentSceneBusy(true);
|
||||||
|
try {
|
||||||
|
const scanned = await refreshDocumentStructure();
|
||||||
|
if (!scanned) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!detectedSceneTitle) {
|
||||||
|
resetPlotDirectorScene("Not detected");
|
||||||
|
setDocumentMessage("No scene detected in Word. Use Heading 2 for scenes.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await refreshPlotDirectorScene();
|
||||||
|
} finally {
|
||||||
|
setRefreshCurrentSceneBusy(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const loadBooks = async (projectId, preferredBookId) => {
|
const loadBooks = async (projectId, preferredBookId) => {
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
books = [];
|
books = [];
|
||||||
@ -821,13 +882,13 @@
|
|||||||
setBookMessage("");
|
setBookMessage("");
|
||||||
writeStoredId(storageKeys.bookId, null);
|
writeStoredId(storageKeys.bookId, null);
|
||||||
updateSummary();
|
updateSummary();
|
||||||
resetPlotDirectorScene("Select a PlotDirector book.");
|
resetPlotDirectorScene("Not detected");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetSelect(bookSelect, "Loading books...");
|
resetSelect(bookSelect, "Loading books...");
|
||||||
setBookMessage("");
|
setBookMessage("");
|
||||||
resetPlotDirectorScene("Select a PlotDirector book.");
|
resetPlotDirectorScene("Not detected");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await fetchJson(`/api/word-companion/projects/${projectId}/books`);
|
const data = await fetchJson(`/api/word-companion/projects/${projectId}/books`);
|
||||||
@ -850,14 +911,14 @@
|
|||||||
writeStoredId(storageKeys.bookId, null);
|
writeStoredId(storageKeys.bookId, null);
|
||||||
}
|
}
|
||||||
updateSummary();
|
updateSummary();
|
||||||
resetPlotDirectorScene(selectedBook() ? "No scene detected in Word." : "Select a PlotDirector book.");
|
resetPlotDirectorScene(selectedBook() ? "Not detected" : "Not detected");
|
||||||
} catch {
|
} catch {
|
||||||
books = [];
|
books = [];
|
||||||
resetSelect(bookSelect, "Unable to load books.");
|
resetSelect(bookSelect, "Unable to load books.");
|
||||||
setBookMessage("Unable to load books.");
|
setBookMessage("Unable to load books.");
|
||||||
writeStoredId(storageKeys.bookId, null);
|
writeStoredId(storageKeys.bookId, null);
|
||||||
updateSummary();
|
updateSummary();
|
||||||
resetPlotDirectorScene("Select a PlotDirector book.");
|
resetPlotDirectorScene("Not detected");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -909,7 +970,7 @@
|
|||||||
const projectId = Number.parseInt(projectSelect.value || "", 10);
|
const projectId = Number.parseInt(projectSelect.value || "", 10);
|
||||||
writeStoredId(storageKeys.projectId, Number.isInteger(projectId) && projectId > 0 ? projectId : null);
|
writeStoredId(storageKeys.projectId, Number.isInteger(projectId) && projectId > 0 ? projectId : null);
|
||||||
writeStoredId(storageKeys.bookId, null);
|
writeStoredId(storageKeys.bookId, null);
|
||||||
resetPlotDirectorScene("Select a PlotDirector book.");
|
resetPlotDirectorScene("Not detected");
|
||||||
await loadBooks(projectId, null);
|
await loadBooks(projectId, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -917,11 +978,12 @@
|
|||||||
const bookId = Number.parseInt(bookSelect.value || "", 10);
|
const bookId = Number.parseInt(bookSelect.value || "", 10);
|
||||||
writeStoredId(storageKeys.bookId, Number.isInteger(bookId) && bookId > 0 ? bookId : null);
|
writeStoredId(storageKeys.bookId, Number.isInteger(bookId) && bookId > 0 ? bookId : null);
|
||||||
updateSummary();
|
updateSummary();
|
||||||
resetPlotDirectorScene(selectedBook() ? "No scene detected in Word." : "Select a PlotDirector book.");
|
resetPlotDirectorScene("Not detected");
|
||||||
});
|
});
|
||||||
|
|
||||||
refreshDocumentButton?.addEventListener("click", refreshDocumentStructure);
|
refreshDocumentButton?.addEventListener("click", refreshDocumentStructure);
|
||||||
refreshPlotDirectorSceneButton?.addEventListener("click", refreshPlotDirectorScene);
|
refreshPlotDirectorSceneButton?.addEventListener("click", refreshPlotDirectorScene);
|
||||||
|
refreshCurrentSceneButton?.addEventListener("click", refreshCurrentScene);
|
||||||
syncSceneProgressButton?.addEventListener("click", syncSceneProgress);
|
syncSceneProgressButton?.addEventListener("click", syncSceneProgress);
|
||||||
runDiagnosticsButton?.addEventListener("click", runDiagnostics);
|
runDiagnosticsButton?.addEventListener("click", runDiagnostics);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user