diff --git a/PlotLine/Views/WordCompanionHost/Index.cshtml b/PlotLine/Views/WordCompanionHost/Index.cshtml
index 21d57e4..b64a73c 100644
--- a/PlotLine/Views/WordCompanionHost/Index.cshtml
+++ b/PlotLine/Views/WordCompanionHost/Index.cshtml
@@ -82,6 +82,52 @@
Scene Status
Select a PlotDirector book.
+
+
+ Selected Project ID
+ -
+
+
+ Selected Project
+ -
+
+
+ Selected Book ID
+ -
+
+
+ Selected Book
+ -
+
+
+ Detected Chapter
+ -
+
+
+ Detected Scene
+ -
+
+
+ Resolve Request Chapter
+ -
+
+
+ Resolve Request Scene
+ -
+
+
+ Resolve Result
+ Not run
+
+
+ Chapter ID
+ -
+
+
+ Scene ID
+ -
+
+
Scene Title
diff --git a/PlotLine/wwwroot/css/word-companion.css b/PlotLine/wwwroot/css/word-companion.css
index 66a3394..c7cc761 100644
--- a/PlotLine/wwwroot/css/word-companion.css
+++ b/PlotLine/wwwroot/css/word-companion.css
@@ -174,18 +174,29 @@ body {
font-size: 0.88rem;
}
+.word-companion-resolve-diagnostics,
.word-companion-scene-details,
.word-companion-link-groups {
display: grid;
gap: 8px;
}
+.word-companion-resolve-diagnostics div,
.word-companion-scene-details div,
.word-companion-link-groups div {
display: grid;
gap: 3px;
}
+.word-companion-resolve-diagnostics {
+ border-top: 1px solid var(--companion-line);
+ padding-top: 8px;
+}
+
+.word-companion-resolve-diagnostics strong {
+ font-size: 0.86rem;
+}
+
.word-companion-brief {
display: grid;
gap: 5px;
diff --git a/PlotLine/wwwroot/js/word-companion-host.js b/PlotLine/wwwroot/js/word-companion-host.js
index 3c44693..4ebe566 100644
--- a/PlotLine/wwwroot/js/word-companion-host.js
+++ b/PlotLine/wwwroot/js/word-companion-host.js
@@ -37,6 +37,17 @@
const characterChips = document.querySelector("[data-character-chips]");
const assetChips = document.querySelector("[data-asset-chips]");
const locationChips = document.querySelector("[data-location-chips]");
+ const resolveProjectId = document.querySelector("[data-resolve-project-id]");
+ const resolveProjectTitle = document.querySelector("[data-resolve-project-title]");
+ const resolveBookId = document.querySelector("[data-resolve-book-id]");
+ const resolveBookTitle = document.querySelector("[data-resolve-book-title]");
+ const resolveDetectedChapter = document.querySelector("[data-resolve-detected-chapter]");
+ const resolveDetectedScene = document.querySelector("[data-resolve-detected-scene]");
+ const resolveRequestChapter = document.querySelector("[data-resolve-request-chapter]");
+ const resolveRequestScene = document.querySelector("[data-resolve-request-scene]");
+ const resolveResult = document.querySelector("[data-resolve-result]");
+ const resolveChapterId = document.querySelector("[data-resolve-chapter-id]");
+ const resolveSceneId = document.querySelector("[data-resolve-scene-id]");
const runDiagnosticsButton = document.querySelector("[data-run-diagnostics]");
const diagnosticHost = document.querySelector("[data-diagnostic-host]");
const diagnosticPlatform = document.querySelector("[data-diagnostic-platform]");
@@ -181,6 +192,37 @@
setHidden(plotDirectorLinkGroups, false);
};
+ const normalizeWhitespace = (value) => String(value || "").trim().replace(/\s+/g, " ");
+
+ const normalizeTitleForResolve = (title, type) => {
+ const original = normalizeWhitespace(title);
+ if (!original) {
+ return "";
+ }
+
+ const label = type === "scene" ? "scene" : "chapter";
+ const ordinal = "(?:\\d+|[ivxlcdm]+|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)";
+ const prefix = new RegExp(`^${label}\\s+${ordinal}\\s*(?::|-|\\u2013|\\u2014)\\s*`, "i");
+ const normalized = normalizeWhitespace(original.replace(prefix, ""));
+ return normalized || original;
+ };
+
+ const setResolveDiagnostics = (values) => {
+ const hasValue = (name) => Object.prototype.hasOwnProperty.call(values, name);
+
+ if (hasValue("projectId")) setText(resolveProjectId, displayValue(values.projectId));
+ if (hasValue("projectTitle")) setText(resolveProjectTitle, displayValue(values.projectTitle));
+ if (hasValue("bookId")) setText(resolveBookId, displayValue(values.bookId));
+ if (hasValue("bookTitle")) setText(resolveBookTitle, displayValue(values.bookTitle));
+ if (hasValue("detectedChapter")) setText(resolveDetectedChapter, displayValue(values.detectedChapter));
+ if (hasValue("detectedScene")) setText(resolveDetectedScene, displayValue(values.detectedScene));
+ if (hasValue("requestChapter")) setText(resolveRequestChapter, displayValue(values.requestChapter));
+ if (hasValue("requestScene")) setText(resolveRequestScene, displayValue(values.requestScene));
+ if (hasValue("result")) setText(resolveResult, displayValue(values.result));
+ if (hasValue("chapterId")) setText(resolveChapterId, displayValue(values.chapterId));
+ if (hasValue("sceneId")) setText(resolveSceneId, displayValue(values.sceneId));
+ };
+
const findSceneRevisionStatus = async (bookId, sceneId) => {
const structure = await fetchJson(`/api/word-companion/books/${bookId}/structure`);
const chapters = Array.isArray(structure?.chapters) ? structure.chapters : [];
@@ -475,14 +517,14 @@
chapterBlock.className = "word-companion-outline-chapter";
const chapterTitle = document.createElement("strong");
- chapterTitle.textContent = `Chapter ${chapter.index}: ${chapter.title}`;
+ chapterTitle.textContent = chapter.title;
chapterBlock.append(chapterTitle);
if (chapter.scenes.length > 0) {
const sceneList = document.createElement("ul");
for (const sceneItem of chapter.scenes) {
const sceneRow = document.createElement("li");
- sceneRow.textContent = `Scene ${sceneItem.index}: ${sceneItem.title}`;
+ sceneRow.textContent = sceneItem.title;
sceneList.append(sceneRow);
}
chapterBlock.append(sceneList);
@@ -613,14 +655,34 @@
});
const refreshPlotDirectorScene = async () => {
+ const project = selectedProject();
const book = selectedBook();
+ const requestChapterTitle = normalizeTitleForResolve(detectedChapterTitle, "chapter");
+ const requestSceneTitle = normalizeTitleForResolve(detectedSceneTitle, "scene");
+
+ setResolveDiagnostics({
+ projectId: project?.projectId,
+ projectTitle: project?.title,
+ bookId: book?.bookId,
+ bookTitle: book?.title,
+ detectedChapter: detectedChapterTitle,
+ detectedScene: detectedSceneTitle,
+ requestChapter: requestChapterTitle,
+ requestScene: requestSceneTitle,
+ result: "Not run",
+ chapterId: null,
+ sceneId: null
+ });
+
if (!book) {
resetPlotDirectorScene("Select a PlotDirector book.");
+ setResolveDiagnostics({ result: "Error" });
return;
}
if (!detectedChapterTitle || !detectedSceneTitle) {
resetPlotDirectorScene("No scene detected in Word.");
+ setResolveDiagnostics({ result: "Error" });
return;
}
@@ -639,19 +701,30 @@
let resolvedSceneId = null;
try {
const resolveResponse = await postJson(`/api/word-companion/books/${book.bookId}/resolve-scene`, {
- chapterTitle: detectedChapterTitle,
- sceneTitle: detectedSceneTitle
+ chapterTitle: requestChapterTitle,
+ sceneTitle: requestSceneTitle
});
if (!resolveResponse?.matched || !resolveResponse.sceneId) {
- resetPlotDirectorScene("Scene not found in PlotDirector.");
+ resetPlotDirectorScene("No exact match found for this chapter/scene in the selected book.");
+ setResolveDiagnostics({
+ result: "Not matched",
+ chapterId: resolveResponse?.chapterId,
+ sceneId: resolveResponse?.sceneId
+ });
restorePlotDirectorSceneButton();
return;
}
resolvedSceneId = resolveResponse.sceneId;
+ setResolveDiagnostics({
+ result: "Matched",
+ chapterId: resolveResponse.chapterId,
+ sceneId: resolveResponse.sceneId
+ });
} catch {
resetPlotDirectorScene("Unable to resolve scene.");
+ setResolveDiagnostics({ result: "Error" });
restorePlotDirectorSceneButton();
return;
}
@@ -667,6 +740,7 @@
renderCompanionData(companionData);
} catch {
resetPlotDirectorScene("Unable to load PlotDirector scene data.");
+ setResolveDiagnostics({ result: "Error" });
} finally {
restorePlotDirectorSceneButton();
}