Phase 10I: Word Companion Manuscript Anchors (Hidden PlotDirector IDs)
This commit is contained in:
parent
6dd46c2632
commit
cda6279d9f
@ -66,6 +66,22 @@
|
||||
<span>Link Status</span>
|
||||
<strong data-plotdirector-scene-status>Not detected</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Anchor Status</span>
|
||||
<strong data-anchor-status>Not attached</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Book ID</span>
|
||||
<strong data-anchor-book-id>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Chapter ID</span>
|
||||
<strong data-anchor-chapter-id>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Scene ID</span>
|
||||
<strong data-anchor-scene-id>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Word Count in Word</span>
|
||||
<strong data-current-word-count>-</strong>
|
||||
@ -101,6 +117,7 @@
|
||||
<strong data-sync-plotdirector-count>-</strong>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" data-attach-plotdirector-ids disabled>Attach PlotDirector IDs</button>
|
||||
<p class="word-companion-message" data-sync-note></p>
|
||||
</section>
|
||||
|
||||
@ -189,6 +206,22 @@
|
||||
<span>Scene ID</span>
|
||||
<strong data-resolve-scene-id>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Anchor Type</span>
|
||||
<strong data-diagnostic-anchor-type>Title Match</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Stored Scene ID</span>
|
||||
<strong data-diagnostic-stored-scene-id>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Stored Chapter ID</span>
|
||||
<strong data-diagnostic-stored-chapter-id>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Current Resolution Method</span>
|
||||
<strong data-diagnostic-resolution-method>-</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Office Host</span>
|
||||
<strong data-diagnostic-host>Unknown</strong>
|
||||
|
||||
@ -177,6 +177,23 @@ body {
|
||||
color: var(--companion-muted);
|
||||
}
|
||||
|
||||
.word-companion-current-scene > button {
|
||||
min-height: 34px;
|
||||
border: 1px solid var(--companion-line);
|
||||
border-radius: 6px;
|
||||
padding: 6px 8px;
|
||||
background: #ffffff;
|
||||
color: var(--companion-accent-dark);
|
||||
font: inherit;
|
||||
font-size: 0.84rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.word-companion-current-scene > button:disabled {
|
||||
background: var(--companion-soft);
|
||||
color: var(--companion-muted);
|
||||
}
|
||||
|
||||
.word-companion-message:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -24,6 +24,11 @@
|
||||
const documentOutline = document.querySelector("[data-document-outline]");
|
||||
const refreshPlotDirectorSceneButton = document.querySelector("[data-refresh-plotdirector-scene]");
|
||||
const plotDirectorSceneStatus = document.querySelector("[data-plotdirector-scene-status]");
|
||||
const anchorStatus = document.querySelector("[data-anchor-status]");
|
||||
const anchorBookId = document.querySelector("[data-anchor-book-id]");
|
||||
const anchorChapterId = document.querySelector("[data-anchor-chapter-id]");
|
||||
const anchorSceneId = document.querySelector("[data-anchor-scene-id]");
|
||||
const attachPlotDirectorIdsButton = document.querySelector("[data-attach-plotdirector-ids]");
|
||||
const plotDirectorSceneDetails = document.querySelector("[data-plotdirector-scene-details]");
|
||||
const plotDirectorSceneTitle = document.querySelector("[data-plotdirector-scene-title]");
|
||||
const plotDirectorRevisionStatus = document.querySelector("[data-plotdirector-revision-status]");
|
||||
@ -64,6 +69,10 @@
|
||||
const diagnosticWordApi = document.querySelector("[data-diagnostic-word-api]");
|
||||
const diagnosticLastScan = document.querySelector("[data-diagnostic-last-scan]");
|
||||
const diagnosticLastError = document.querySelector("[data-diagnostic-last-error]");
|
||||
const diagnosticAnchorType = document.querySelector("[data-diagnostic-anchor-type]");
|
||||
const diagnosticStoredSceneId = document.querySelector("[data-diagnostic-stored-scene-id]");
|
||||
const diagnosticStoredChapterId = document.querySelector("[data-diagnostic-stored-chapter-id]");
|
||||
const diagnosticResolutionMethod = document.querySelector("[data-diagnostic-resolution-method]");
|
||||
const diagnosticParagraphs = document.querySelector("[data-diagnostic-paragraphs]");
|
||||
const diagnosticHeading1 = document.querySelector("[data-diagnostic-heading1]");
|
||||
const diagnosticHeading2 = document.querySelector("[data-diagnostic-heading2]");
|
||||
@ -77,7 +86,13 @@
|
||||
let detectedChapterTitle = "";
|
||||
let detectedSceneTitle = "";
|
||||
let detectedSceneWordCount = null;
|
||||
let detectedChapterAnchorId = null;
|
||||
let detectedSceneAnchorId = null;
|
||||
let resolvedSceneId = null;
|
||||
let resolvedChapterId = null;
|
||||
let currentResolutionMethod = "";
|
||||
let currentResolutionAnchorType = "Title Match";
|
||||
let storedAnchorInvalid = false;
|
||||
let currentSceneLinks = {
|
||||
characters: [],
|
||||
assets: [],
|
||||
@ -124,10 +139,51 @@
|
||||
}
|
||||
};
|
||||
|
||||
const setCurrentStructure = (chapterTitle, sceneTitle, wordCount) => {
|
||||
const setAnchorDiagnostics = (values) => {
|
||||
const hasValue = (name) => Object.prototype.hasOwnProperty.call(values, name);
|
||||
|
||||
if (hasValue("anchorType")) setText(diagnosticAnchorType, displayValue(values.anchorType));
|
||||
if (hasValue("storedSceneId")) setText(diagnosticStoredSceneId, displayValue(values.storedSceneId));
|
||||
if (hasValue("storedChapterId")) setText(diagnosticStoredChapterId, displayValue(values.storedChapterId));
|
||||
if (hasValue("resolutionMethod")) setText(diagnosticResolutionMethod, displayValue(values.resolutionMethod));
|
||||
};
|
||||
|
||||
const updateAnchorDisplay = () => {
|
||||
const book = selectedBook();
|
||||
const hasMatchingSceneAnchor = Number.isInteger(resolvedSceneId)
|
||||
&& resolvedSceneId > 0
|
||||
&& detectedSceneAnchorId === resolvedSceneId;
|
||||
const hasMatchingChapterAnchor = !Number.isInteger(resolvedChapterId)
|
||||
|| resolvedChapterId <= 0
|
||||
|| detectedChapterAnchorId === resolvedChapterId;
|
||||
const attached = hasMatchingSceneAnchor && hasMatchingChapterAnchor;
|
||||
|
||||
setText(anchorStatus, attached ? "Attached to PlotDirector" : "Not attached");
|
||||
setText(anchorBookId, book?.bookId ? String(book.bookId) : "-");
|
||||
setText(anchorChapterId, Number.isInteger(resolvedChapterId) && resolvedChapterId > 0 ? String(resolvedChapterId) : "-");
|
||||
setText(anchorSceneId, Number.isInteger(resolvedSceneId) && resolvedSceneId > 0 ? String(resolvedSceneId) : "-");
|
||||
|
||||
if (attachPlotDirectorIdsButton) {
|
||||
attachPlotDirectorIdsButton.disabled = !resolvedSceneId || (attached && !storedAnchorInvalid);
|
||||
attachPlotDirectorIdsButton.textContent = detectedSceneAnchorId && !attached
|
||||
? "Reattach PlotDirector IDs"
|
||||
: "Attach PlotDirector IDs";
|
||||
}
|
||||
|
||||
setAnchorDiagnostics({
|
||||
anchorType: currentResolutionAnchorType || "Title Match",
|
||||
storedSceneId: detectedSceneAnchorId,
|
||||
storedChapterId: detectedChapterAnchorId,
|
||||
resolutionMethod: currentResolutionMethod
|
||||
});
|
||||
};
|
||||
|
||||
const setCurrentStructure = (chapterTitle, sceneTitle, wordCount, chapterAnchorId = null, sceneAnchorId = null) => {
|
||||
detectedChapterTitle = chapterTitle || "";
|
||||
detectedSceneTitle = sceneTitle || "";
|
||||
detectedSceneWordCount = Number.isInteger(wordCount) ? wordCount : null;
|
||||
detectedChapterAnchorId = Number.isInteger(chapterAnchorId) && chapterAnchorId > 0 ? chapterAnchorId : null;
|
||||
detectedSceneAnchorId = Number.isInteger(sceneAnchorId) && sceneAnchorId > 0 ? sceneAnchorId : null;
|
||||
|
||||
if (currentChapter) {
|
||||
currentChapter.textContent = chapterTitle || "Not detected";
|
||||
@ -139,6 +195,7 @@
|
||||
currentWordCount.textContent = Number.isInteger(wordCount) ? String(wordCount) : "-";
|
||||
}
|
||||
setText(syncWordCount, Number.isInteger(wordCount) ? String(wordCount) : "-");
|
||||
updateAnchorDisplay();
|
||||
};
|
||||
|
||||
const setHidden = (element, hidden) => {
|
||||
@ -151,8 +208,10 @@
|
||||
setText(syncStatus, message);
|
||||
};
|
||||
|
||||
const setResolvedScene = (sceneId) => {
|
||||
const setResolvedScene = (sceneId, chapterId = null, resolutionMethod = "") => {
|
||||
resolvedSceneId = Number.isInteger(sceneId) && sceneId > 0 ? sceneId : null;
|
||||
resolvedChapterId = Number.isInteger(chapterId) && chapterId > 0 ? chapterId : null;
|
||||
currentResolutionMethod = resolutionMethod || "";
|
||||
if (syncSceneProgressButton) {
|
||||
syncSceneProgressButton.disabled = !resolvedSceneId;
|
||||
}
|
||||
@ -161,6 +220,7 @@
|
||||
}
|
||||
setSyncStatus(resolvedSceneId ? "Ready to sync." : "No resolved PlotDirector scene.");
|
||||
setSceneLinksStatus(resolvedSceneId ? "Ready to save." : "No resolved PlotDirector scene.");
|
||||
updateAnchorDisplay();
|
||||
};
|
||||
|
||||
const setRefreshCurrentSceneBusy = (isBusy) => {
|
||||
@ -189,6 +249,9 @@
|
||||
setHidden(plotDirectorSceneDetails, true);
|
||||
setHidden(writingBriefBlock, true);
|
||||
setHidden(plotDirectorLinkGroups, true);
|
||||
currentResolutionMethod = "";
|
||||
currentResolutionAnchorType = detectedSceneAnchorId ? "SceneID Anchor" : detectedChapterAnchorId ? "ChapterID Anchor" : "Title Match";
|
||||
storedAnchorInvalid = false;
|
||||
currentSceneLinks = { characters: [], assets: [], locations: [], primaryLocationId: null };
|
||||
renderSceneLinkList(characterChips, [], "character");
|
||||
renderSceneLinkList(assetChips, [], "asset");
|
||||
@ -202,6 +265,7 @@
|
||||
setText(plotDirectorBlockedStatus, "-");
|
||||
setText(plotDirectorBlockedReason, "-");
|
||||
setHidden(plotDirectorBlockedReasonRow, true);
|
||||
updateAnchorDisplay();
|
||||
};
|
||||
|
||||
const linkItemId = (item, type) => {
|
||||
@ -362,6 +426,36 @@
|
||||
return "";
|
||||
};
|
||||
|
||||
const findSceneInBookStructure = async (bookId, predicate) => {
|
||||
const structure = await fetchJson(`/api/word-companion/books/${bookId}/structure`);
|
||||
const chapters = Array.isArray(structure?.chapters) ? structure.chapters : [];
|
||||
for (const chapter of chapters) {
|
||||
const scenes = Array.isArray(chapter.scenes) ? chapter.scenes : [];
|
||||
for (const scene of scenes) {
|
||||
if (predicate(chapter, scene)) {
|
||||
return { chapter, scene };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const loadResolvedCompanionData = async (bookId, sceneId, chapterId, resolutionMethod, anchorType) => {
|
||||
const companionData = await fetchJson(`/api/word-companion/scenes/${sceneId}/companion`);
|
||||
try {
|
||||
companionData.revisionStatus = await findSceneRevisionStatus(bookId, sceneId);
|
||||
} catch {
|
||||
companionData.revisionStatus = "";
|
||||
}
|
||||
currentResolutionAnchorType = anchorType || "Title Match";
|
||||
storedAnchorInvalid = false;
|
||||
setPlotDirectorSceneStatus("Linked to PlotDirector");
|
||||
setDocumentMessage("");
|
||||
setResolvedScene(sceneId, chapterId, resolutionMethod);
|
||||
renderCompanionData(companionData);
|
||||
};
|
||||
|
||||
const setText = (element, value) => {
|
||||
if (element) {
|
||||
element.textContent = value;
|
||||
@ -551,6 +645,28 @@
|
||||
&& normalizedStyleName(left) === normalizedStyleName(right);
|
||||
};
|
||||
|
||||
const anchorIdFromTag = (tag, prefix) => {
|
||||
const match = String(tag || "").match(new RegExp(`^${prefix}-(\\d+)$`, "i"));
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const id = Number.parseInt(match[1], 10);
|
||||
return Number.isInteger(id) && id > 0 ? id : null;
|
||||
};
|
||||
|
||||
const paragraphAnchorId = (paragraph, prefix) => {
|
||||
const controls = Array.isArray(paragraph?.contentControls?.items) ? paragraph.contentControls.items : [];
|
||||
for (const control of controls) {
|
||||
const id = anchorIdFromTag(control.tag, prefix);
|
||||
if (id) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const buildDocumentStructure = (paragraphs, selectionParagraphs) => {
|
||||
const chapters = [];
|
||||
let heading1Count = 0;
|
||||
@ -570,6 +686,7 @@
|
||||
index: chapters.length + 1,
|
||||
paragraphIndex: index,
|
||||
title,
|
||||
anchorId: paragraphAnchorId(paragraph, "PD-CHAPTER"),
|
||||
scenes: []
|
||||
};
|
||||
chapters.push(chapter);
|
||||
@ -587,6 +704,7 @@
|
||||
index: chapter.scenes.length + 1,
|
||||
paragraphIndex: index,
|
||||
title,
|
||||
anchorId: paragraphAnchorId(paragraph, "PD-SCENE"),
|
||||
wordCount: 0
|
||||
};
|
||||
chapter.scenes.push(scene);
|
||||
@ -662,6 +780,26 @@
|
||||
}
|
||||
};
|
||||
|
||||
const readDocumentStructure = async (context) => {
|
||||
const bodyParagraphs = context.document.body.paragraphs;
|
||||
const selectionParagraphs = context.document.getSelection().paragraphs;
|
||||
|
||||
bodyParagraphs.load("text,styleBuiltIn,style");
|
||||
selectionParagraphs.load("text,styleBuiltIn,style");
|
||||
await context.sync();
|
||||
|
||||
for (const paragraph of bodyParagraphs.items) {
|
||||
if (isBuiltInHeading(paragraph, 1) || isBuiltInHeading(paragraph, 2)) {
|
||||
paragraph.contentControls.load("items/tag,title");
|
||||
}
|
||||
}
|
||||
await context.sync();
|
||||
|
||||
const structure = buildDocumentStructure(bodyParagraphs.items, selectionParagraphs.items);
|
||||
structure.bodyParagraphs = bodyParagraphs.items;
|
||||
return structure;
|
||||
};
|
||||
|
||||
const refreshDocumentStructure = async () => {
|
||||
setDocumentMessage("");
|
||||
|
||||
@ -680,20 +818,15 @@
|
||||
|
||||
try {
|
||||
const structure = await window.Word.run(async (context) => {
|
||||
const bodyParagraphs = context.document.body.paragraphs;
|
||||
const selectionParagraphs = context.document.getSelection().paragraphs;
|
||||
|
||||
bodyParagraphs.load("text,styleBuiltIn,style");
|
||||
selectionParagraphs.load("text,styleBuiltIn,style");
|
||||
await context.sync();
|
||||
|
||||
return buildDocumentStructure(bodyParagraphs.items, selectionParagraphs.items);
|
||||
return await readDocumentStructure(context);
|
||||
});
|
||||
|
||||
setCurrentStructure(
|
||||
structure.currentChapter?.title,
|
||||
structure.currentScene?.title,
|
||||
structure.currentScene?.wordCount
|
||||
structure.currentScene?.wordCount,
|
||||
structure.currentChapter?.anchorId,
|
||||
structure.currentScene?.anchorId
|
||||
);
|
||||
resetPlotDirectorScene("Not detected");
|
||||
renderOutline(structure);
|
||||
@ -859,6 +992,83 @@
|
||||
}
|
||||
};
|
||||
|
||||
const findAnchorControl = (paragraph, prefix) => {
|
||||
const controls = Array.isArray(paragraph?.contentControls?.items) ? paragraph.contentControls.items : [];
|
||||
return controls.find((control) => anchorIdFromTag(control.tag, prefix)) || null;
|
||||
};
|
||||
|
||||
const ensureAnchorControl = (paragraph, title, tag, prefix) => {
|
||||
const existing = findAnchorControl(paragraph, prefix);
|
||||
const control = existing || paragraph.getRange().insertContentControl();
|
||||
control.title = title;
|
||||
control.tag = tag;
|
||||
control.appearance = "Hidden";
|
||||
return control;
|
||||
};
|
||||
|
||||
const attachPlotDirectorIds = async () => {
|
||||
if (!resolvedSceneId || !resolvedChapterId) {
|
||||
setDocumentMessage("No resolved PlotDirector scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
const replacingExistingAnchor = (detectedSceneAnchorId && detectedSceneAnchorId !== resolvedSceneId)
|
||||
|| (detectedChapterAnchorId && detectedChapterAnchorId !== resolvedChapterId);
|
||||
if (replacingExistingAnchor && !window.confirm("This scene is already linked. Replace the existing PlotDirector link?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!officeReady || !wordHostAvailable || !window.Word || typeof window.Word.run !== "function") {
|
||||
setDocumentMessage("Word document access is unavailable.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (attachPlotDirectorIdsButton) {
|
||||
attachPlotDirectorIdsButton.disabled = true;
|
||||
attachPlotDirectorIdsButton.textContent = "Attaching...";
|
||||
}
|
||||
|
||||
try {
|
||||
await window.Word.run(async (context) => {
|
||||
const structure = await readDocumentStructure(context);
|
||||
if (!structure.currentChapter || !structure.currentScene) {
|
||||
throw new Error("No scene detected in Word. Use Heading 2 for scenes.");
|
||||
}
|
||||
|
||||
const chapterParagraph = structure.bodyParagraphs[structure.currentChapter.paragraphIndex];
|
||||
const sceneParagraph = structure.bodyParagraphs[structure.currentScene.paragraphIndex];
|
||||
if (!chapterParagraph || !sceneParagraph) {
|
||||
throw new Error("Unable to locate the current Word headings.");
|
||||
}
|
||||
|
||||
ensureAnchorControl(chapterParagraph, "PlotDirector Chapter", `PD-CHAPTER-${resolvedChapterId}`, "PD-CHAPTER");
|
||||
ensureAnchorControl(sceneParagraph, "PlotDirector Scene", `PD-SCENE-${resolvedSceneId}`, "PD-SCENE");
|
||||
await context.sync();
|
||||
});
|
||||
|
||||
detectedChapterAnchorId = resolvedChapterId;
|
||||
detectedSceneAnchorId = resolvedSceneId;
|
||||
storedAnchorInvalid = false;
|
||||
currentResolutionAnchorType = "SceneID Anchor";
|
||||
currentResolutionMethod = "Anchor";
|
||||
setDocumentMessage("PlotDirector IDs attached successfully.");
|
||||
setDiagnostics({ lastError: "-" });
|
||||
updateAnchorDisplay();
|
||||
} catch (error) {
|
||||
console.error("Unable to attach PlotDirector IDs.", error);
|
||||
setDocumentMessage("Unable to attach PlotDirector IDs.");
|
||||
setDiagnostics({ lastError: errorText(error) });
|
||||
updateAnchorDisplay();
|
||||
} finally {
|
||||
if (attachPlotDirectorIdsButton) {
|
||||
attachPlotDirectorIdsButton.textContent = detectedSceneAnchorId === resolvedSceneId
|
||||
? "Attach PlotDirector IDs"
|
||||
: "Reattach PlotDirector IDs";
|
||||
updateAnchorDisplay();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const syncSceneProgress = async () => {
|
||||
if (!resolvedSceneId) {
|
||||
setSyncStatus("No resolved PlotDirector scene.");
|
||||
@ -914,6 +1124,12 @@
|
||||
chapterId: null,
|
||||
sceneId: null
|
||||
});
|
||||
setAnchorDiagnostics({
|
||||
anchorType: detectedSceneAnchorId ? "SceneID Anchor" : detectedChapterAnchorId ? "ChapterID Anchor" : "Title Match",
|
||||
storedSceneId: detectedSceneAnchorId,
|
||||
storedChapterId: detectedChapterAnchorId,
|
||||
resolutionMethod: "-"
|
||||
});
|
||||
|
||||
if (!book) {
|
||||
resetPlotDirectorScene("Not detected");
|
||||
@ -942,16 +1158,80 @@
|
||||
resetPlotDirectorScene("Not detected");
|
||||
setDocumentMessage("Resolving PlotDirector scene...");
|
||||
|
||||
let resolvedSceneId = null;
|
||||
try {
|
||||
if (detectedSceneAnchorId) {
|
||||
const anchored = await findSceneInBookStructure(
|
||||
book.bookId,
|
||||
(_chapter, scene) => scene.sceneId === detectedSceneAnchorId
|
||||
);
|
||||
|
||||
if (anchored) {
|
||||
setResolveDiagnostics({
|
||||
result: "Matched",
|
||||
chapterId: anchored.chapter.chapterId,
|
||||
sceneId: anchored.scene.sceneId
|
||||
});
|
||||
await loadResolvedCompanionData(book.bookId, anchored.scene.sceneId, anchored.chapter.chapterId, "Anchor", "SceneID Anchor");
|
||||
return true;
|
||||
}
|
||||
|
||||
storedAnchorInvalid = true;
|
||||
currentResolutionAnchorType = "SceneID Anchor";
|
||||
setPlotDirectorSceneStatus("Not found in PlotDirector");
|
||||
setDocumentMessage("Stored PlotDirector ID is invalid.");
|
||||
setResolveDiagnostics({
|
||||
result: "Error",
|
||||
chapterId: detectedChapterAnchorId,
|
||||
sceneId: detectedSceneAnchorId
|
||||
});
|
||||
setAnchorDiagnostics({
|
||||
anchorType: "SceneID Anchor",
|
||||
storedSceneId: detectedSceneAnchorId,
|
||||
storedChapterId: detectedChapterAnchorId,
|
||||
resolutionMethod: "Anchor"
|
||||
});
|
||||
}
|
||||
|
||||
if (detectedChapterAnchorId) {
|
||||
const anchoredChapterScene = await findSceneInBookStructure(
|
||||
book.bookId,
|
||||
(chapter, scene) => chapter.chapterId === detectedChapterAnchorId
|
||||
&& normalizeTitleForResolve(scene.title, "scene").toLocaleLowerCase() === requestSceneTitle.toLocaleLowerCase()
|
||||
);
|
||||
|
||||
if (anchoredChapterScene) {
|
||||
setResolveDiagnostics({
|
||||
result: "Matched",
|
||||
chapterId: anchoredChapterScene.chapter.chapterId,
|
||||
sceneId: anchoredChapterScene.scene.sceneId
|
||||
});
|
||||
await loadResolvedCompanionData(book.bookId, anchoredChapterScene.scene.sceneId, anchoredChapterScene.chapter.chapterId, "Anchor", "ChapterID Anchor");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!detectedSceneAnchorId) {
|
||||
storedAnchorInvalid = true;
|
||||
currentResolutionAnchorType = "ChapterID Anchor";
|
||||
setPlotDirectorSceneStatus("Not found in PlotDirector");
|
||||
setDocumentMessage("Stored PlotDirector ID is invalid.");
|
||||
setResolveDiagnostics({
|
||||
result: "Error",
|
||||
chapterId: detectedChapterAnchorId,
|
||||
sceneId: null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const resolveResponse = await postJson(`/api/word-companion/books/${book.bookId}/resolve-scene`, {
|
||||
chapterTitle: requestChapterTitle,
|
||||
sceneTitle: requestSceneTitle
|
||||
});
|
||||
|
||||
if (!resolveResponse?.matched || !resolveResponse.sceneId) {
|
||||
const hadInvalidAnchor = storedAnchorInvalid;
|
||||
resetPlotDirectorScene("Not found in PlotDirector");
|
||||
setDocumentMessage("Scene not found in PlotDirector.");
|
||||
storedAnchorInvalid = hadInvalidAnchor;
|
||||
setDocumentMessage(hadInvalidAnchor ? "Stored PlotDirector ID is invalid." : "Scene not found in PlotDirector.");
|
||||
setResolveDiagnostics({
|
||||
result: "Not matched",
|
||||
chapterId: resolveResponse?.chapterId,
|
||||
@ -961,35 +1241,26 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
resolvedSceneId = resolveResponse.sceneId;
|
||||
const hadInvalidAnchor = storedAnchorInvalid;
|
||||
|
||||
setResolveDiagnostics({
|
||||
result: "Matched",
|
||||
result: hadInvalidAnchor ? "Matched by title fallback" : "Matched",
|
||||
chapterId: resolveResponse.chapterId,
|
||||
sceneId: resolveResponse.sceneId
|
||||
});
|
||||
} catch {
|
||||
resetPlotDirectorScene("Not found in PlotDirector");
|
||||
setDocumentMessage("Scene not found in PlotDirector.");
|
||||
setResolveDiagnostics({ result: "Error" });
|
||||
restorePlotDirectorSceneButton();
|
||||
return false;
|
||||
await loadResolvedCompanionData(book.bookId, resolveResponse.sceneId, resolveResponse.chapterId, hadInvalidAnchor ? "Title fallback" : "Title", "Title Match");
|
||||
if (hadInvalidAnchor) {
|
||||
storedAnchorInvalid = true;
|
||||
setDocumentMessage("Stored PlotDirector ID is invalid.");
|
||||
updateAnchorDisplay();
|
||||
}
|
||||
|
||||
try {
|
||||
const companionData = await fetchJson(`/api/word-companion/scenes/${resolvedSceneId}/companion`);
|
||||
try {
|
||||
companionData.revisionStatus = await findSceneRevisionStatus(book.bookId, resolvedSceneId);
|
||||
} catch {
|
||||
companionData.revisionStatus = "";
|
||||
}
|
||||
setPlotDirectorSceneStatus("Linked to PlotDirector");
|
||||
setDocumentMessage("");
|
||||
setResolvedScene(resolvedSceneId);
|
||||
renderCompanionData(companionData);
|
||||
return true;
|
||||
} catch {
|
||||
} catch (error) {
|
||||
const hadInvalidAnchor = storedAnchorInvalid;
|
||||
setDiagnostics({ lastError: errorText(error) });
|
||||
resetPlotDirectorScene("Not found in PlotDirector");
|
||||
setDocumentMessage("Unable to load PlotDirector scene data.");
|
||||
storedAnchorInvalid = hadInvalidAnchor;
|
||||
setDocumentMessage(hadInvalidAnchor ? "Stored PlotDirector ID is invalid." : "Unable to load PlotDirector scene data.");
|
||||
setResolveDiagnostics({ result: "Error" });
|
||||
return false;
|
||||
} finally {
|
||||
@ -1134,6 +1405,7 @@
|
||||
refreshCurrentSceneButton?.addEventListener("click", refreshCurrentScene);
|
||||
syncSceneProgressButton?.addEventListener("click", syncSceneProgress);
|
||||
saveSceneLinksButton?.addEventListener("click", saveSceneLinks);
|
||||
attachPlotDirectorIdsButton?.addEventListener("click", attachPlotDirectorIds);
|
||||
runDiagnosticsButton?.addEventListener("click", runDiagnostics);
|
||||
|
||||
if (isAuthenticated) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user