Phase 10E.1: Word Companion Resolve Diagnostics and Outline Display Cleanup
This commit is contained in:
parent
8c4525d3b3
commit
6049ac2c87
@ -82,6 +82,52 @@
|
||||
</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>
|
||||
<span>Selected Project ID</span>
|
||||
<strong data-resolve-project-id>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Selected Project</span>
|
||||
<strong data-resolve-project-title>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Selected Book ID</span>
|
||||
<strong data-resolve-book-id>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Selected Book</span>
|
||||
<strong data-resolve-book-title>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Detected Chapter</span>
|
||||
<strong data-resolve-detected-chapter>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Detected Scene</span>
|
||||
<strong data-resolve-detected-scene>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Resolve Request Chapter</span>
|
||||
<strong data-resolve-request-chapter>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Resolve Request Scene</span>
|
||||
<strong data-resolve-request-scene>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Resolve Result</span>
|
||||
<strong data-resolve-result>Not run</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Chapter ID</span>
|
||||
<strong data-resolve-chapter-id>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Scene ID</span>
|
||||
<strong data-resolve-scene-id>-</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="word-companion-scene-details" data-plotdirector-scene-details hidden>
|
||||
<div>
|
||||
<span>Scene Title</span>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user