PlotDirector/PlotLine/wwwroot/js/writing-workspace-launcher.js

124 lines
5.5 KiB
JavaScript

(() => {
const root = document.querySelector("[data-writing-workspace-launcher]");
const workspace = window.PlotDirectorWritingWorkspace;
if (!root || !workspace) {
return;
}
const statusNodes = {
book: root.querySelector("[data-writing-workspace-book]"),
word: root.querySelector("[data-writing-workspace-word]"),
context: root.querySelector("[data-writing-workspace-context]"),
intent: root.querySelector("[data-writing-workspace-intent]"),
layout: root.querySelector("[data-writing-workspace-layout]"),
message: root.querySelector("[data-writing-workspace-message]")
};
const startButton = root.querySelector("[data-start-writing-workspace]");
const contextButton = root.querySelector("[data-open-writing-context]");
const intentButton = root.querySelector("[data-open-writing-intent]");
const focusContextButton = root.querySelector("[data-focus-writing-context]");
const focusIntentButton = root.querySelector("[data-focus-writing-intent]");
const fallbackScope = workspace.scopeFrom({
projectID: root.dataset.projectId,
bookID: root.dataset.bookId
});
let activeScope = { ...fallbackScope };
let latestStatus = null;
const text = (node, value) => {
if (node) node.textContent = value;
};
const statusText = (status, role) => {
const match = (status?.windows || []).find((windowPresence) =>
String(windowPresence.writingRole || windowPresence.WritingRole || "").toLowerCase() === role.toLowerCase()
&& (!activeScope.bookID || Number(windowPresence.bookID || windowPresence.BookID) === activeScope.bookID));
if (!match) return "Not open";
const id = match.browserWindowID || match.BrowserWindowID || "";
return id === workspace.getBrowserWindowId() ? "This window" : "Open";
};
const setMessage = (message) => text(statusNodes.message, message || "");
const render = (status = latestStatus) => {
latestStatus = status;
activeScope = workspace.statusScope(status, activeScope);
text(statusNodes.book, activeScope.bookID ? `Book ${activeScope.bookID}` : "Waiting for book");
text(statusNodes.word, status?.wordCompanionConnected || status?.WordCompanionConnected ? "Connected" : "Not connected");
text(statusNodes.context, statusText(status, "Context"));
text(statusNodes.intent, statusText(status, "Intent"));
const contextOpen = statusText(status, "Context") !== "Not open";
const intentOpen = statusText(status, "Intent") !== "Not open";
if (focusContextButton) focusContextButton.hidden = !contextOpen;
if (focusIntentButton) focusIntentButton.hidden = !intentOpen;
if (contextButton) contextButton.textContent = contextOpen ? "Focus Context" : "Open Context";
if (intentButton) intentButton.textContent = intentOpen ? "Focus Intent" : "Open Intent";
};
const refresh = async () => {
try {
render(await workspace.watchWorkspace(activeScope));
} catch (error) {
setMessage("Writing workspace presence is unavailable.");
console.debug("Writing workspace presence unavailable.", error);
}
};
const openOrFocus = async (role) => {
const scope = workspace.statusScope(latestStatus, activeScope);
if (!scope.bookID) {
setMessage("Open a linked Word manuscript or focus a book before opening writing windows.");
return;
}
await workspace.focusRole(role, scope);
await refresh();
};
startButton?.addEventListener("click", async () => {
startButton.disabled = true;
setMessage("Starting writing workspace...");
try {
const result = await workspace.startWorkspace(activeScope, { requestScreens: true });
if (!result.ok) {
setMessage(result.message || `Pop-up blocked for ${result.blocked?.join(", ") || "a writing window"}. Use the explicit open buttons.`);
} else if (result.reusedCurrentWindowAs) {
setMessage("Reusing this window for Writing Intent and opening Context.");
} else {
setMessage("Writing workspace ready.");
}
text(statusNodes.layout, result.screenInfo?.layout?.mode || "single-screen");
await refresh();
} catch (error) {
console.debug("Unable to start writing workspace.", error);
setMessage("Writing workspace could not be started. Use the direct open buttons.");
} finally {
startButton.disabled = false;
}
});
contextButton?.addEventListener("click", () => openOrFocus(workspace.roles.context));
intentButton?.addEventListener("click", () => openOrFocus(workspace.roles.intent));
focusContextButton?.addEventListener("click", () => openOrFocus(workspace.roles.context));
focusIntentButton?.addEventListener("click", () => openOrFocus(workspace.roles.intent));
document.addEventListener("plotdirector:writing-workspace-status", (event) => render(event.detail));
document.addEventListener(window.PlotDirectorStoryPosition?.eventName || "plotdirector:story-position-changed", (event) => {
const position = window.PlotDirectorStoryPosition?.normalise?.(event.detail, event.detail?.source || event.detail?.Source) || event.detail;
if (position?.projectId && position?.bookId) {
activeScope = { projectID: position.projectId, bookID: position.bookId };
void workspace.registerWindow({
...activeScope,
writingRole: workspace.roles.launcher,
displayLabel: "Writing Workspace"
}).then(render).catch(() => null);
}
});
void workspace.registerWindow({
...activeScope,
writingRole: workspace.roles.launcher,
displayLabel: "Writing Workspace"
}).then(render).catch(() => null);
})();