From 4b7af17b42bc5008c80aeff4c1f450e7beb1fdb0 Mon Sep 17 00:00:00 2001 From: Nick Beckley Date: Sun, 5 Jul 2026 22:38:59 +0100 Subject: [PATCH] =?UTF-8?q?Phase=2020AA=20=E2=80=93=20Connect=20Onboarding?= =?UTF-8?q?=20Wizard=20To=20Story=20Intelligence=20Scene=20Import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PlotLine/Controllers/OnboardingController.cs | 48 ++- PlotLine/Models/ManuscriptScanModels.cs | 1 + PlotLine/Program.cs | 2 + PlotLine/Services/OnboardingService.cs | 54 +-- .../OnboardingStoryIntelligenceService.cs | 382 ++++++++++++++++++ PlotLine/ViewModels/OnboardingViewModels.cs | 53 ++- .../Views/Onboarding/BuildComplete.cshtml | 26 +- PlotLine/Views/Onboarding/Index.cshtml | 6 +- PlotLine/Views/Onboarding/ScanReview.cshtml | 6 +- .../Views/Onboarding/StoryIntelligence.cshtml | 69 ++-- .../StoryIntelligenceComplete.cshtml | 31 +- .../StoryIntelligenceProgress.cshtml | 188 +++++++-- PlotLine/Views/Projects/Index.cshtml | 4 +- PlotLine/wwwroot/js/word-companion-host.js | 11 + .../wwwroot/js/word-companion-host.min.js | 26 +- .../wwwroot/js/word-companion-presence.js | 6 +- 16 files changed, 758 insertions(+), 155 deletions(-) create mode 100644 PlotLine/Services/OnboardingStoryIntelligenceService.cs diff --git a/PlotLine/Controllers/OnboardingController.cs b/PlotLine/Controllers/OnboardingController.cs index acd2971..3bc1f81 100644 --- a/PlotLine/Controllers/OnboardingController.cs +++ b/PlotLine/Controllers/OnboardingController.cs @@ -8,7 +8,7 @@ namespace PlotLine.Controllers; [Authorize] [Route("onboarding")] -public sealed class OnboardingController(IOnboardingService onboarding, IStoryIntelligenceService storyIntelligence) : Controller +public sealed class OnboardingController(IOnboardingService onboarding, IOnboardingStoryIntelligenceService storyIntelligence) : Controller { [HttpGet("")] public async Task Index() @@ -45,11 +45,11 @@ public sealed class OnboardingController(IOnboardingService onboarding, IStoryIn var job = await storyIntelligence.StartAsync(); if (job is null) { - TempData["ArchiveError"] = "Choose a project and book before starting Story Intelligence."; - return RedirectToAction(nameof(Index)); + TempData["OnboardingStoryIntelligenceError"] = "Review the manuscript scan before starting Story Intelligence."; + return RedirectToAction(nameof(StoryIntelligence)); } - return RedirectToAction(nameof(StoryIntelligenceProgress), new { jobId = job.JobID }); + return RedirectToAction(nameof(StoryIntelligenceProgress), new { batchId = job.BatchID }); } [HttpPost("story-intelligence/skip")] @@ -62,30 +62,50 @@ public sealed class OnboardingController(IOnboardingService onboarding, IStoryIn } [HttpGet("story-intelligence/progress")] - public async Task StoryIntelligenceProgress(Guid jobId) + public async Task StoryIntelligenceProgress(Guid batchId) { - var model = await storyIntelligence.GetProgressAsync(jobId); + var model = await storyIntelligence.GetProgressAsync(batchId); return model is null ? NotFound() : View(model); } [HttpPost("story-intelligence/cancel")] [ValidateAntiForgeryToken] - public async Task CancelStoryIntelligence(Guid jobId) + public async Task CancelStoryIntelligence(Guid batchId) { - var job = await storyIntelligence.CancelAsync(jobId); - if (job is null) + var progress = await storyIntelligence.CancelAsync(batchId); + if (progress is null) { return NotFound(); } - TempData["ArchiveMessage"] = "Story Intelligence setup was cancelled."; - return RedirectToAction("Index", "Projects"); + TempData["OnboardingStoryIntelligenceMessage"] = "Analysis cancellation requested."; + return RedirectToAction(nameof(StoryIntelligenceProgress), new { batchId }); + } + + [HttpPost("story-intelligence/commit")] + [ValidateAntiForgeryToken] + public async Task CommitStoryIntelligence(Guid batchId, int runId) + { + var (progress, result) = await storyIntelligence.CommitAsync(batchId, runId); + if (progress is null) + { + return NotFound(); + } + + TempData[result.Success ? "OnboardingStoryIntelligenceMessage" : "OnboardingStoryIntelligenceError"] = result.Message; + if (result.Success && progress.AllCommitted) + { + TempData["WriterWorkspaceMessage"] = $"{progress.CommittedChapterCount} chapter(s) and {progress.Chapters.Sum(chapter => chapter.ScenesCreated)} scene(s) created from your manuscript."; + return RedirectToAction("Index", "Writer", new { projectId = progress.ProjectID }); + } + + return RedirectToAction(nameof(StoryIntelligenceProgress), new { batchId }); } [HttpGet("story-intelligence/complete")] - public async Task StoryIntelligenceComplete(Guid jobId) + public async Task StoryIntelligenceComplete(Guid batchId) { - var model = await storyIntelligence.GetCompletionAsync(jobId); + var model = await storyIntelligence.GetCompletionAsync(batchId); return model is null ? NotFound() : View(model); } @@ -103,7 +123,7 @@ public sealed class OnboardingController(IOnboardingService onboarding, IStoryIn } TempData["OnboardingReviewMessage"] = readyToImport - ? "Review saved. Next: import into PlotDirector." + ? "Review saved. Next: prepare chapters and analyse manuscript." : "Review choices saved."; return readyToImport ? RedirectToAction(nameof(Index)) diff --git a/PlotLine/Models/ManuscriptScanModels.cs b/PlotLine/Models/ManuscriptScanModels.cs index 03a873b..b03116a 100644 --- a/PlotLine/Models/ManuscriptScanModels.cs +++ b/PlotLine/Models/ManuscriptScanModels.cs @@ -75,6 +75,7 @@ public sealed class ManuscriptScanChapterPreview public int ChapterNumber { get; init; } public string Title { get; init; } = string.Empty; public int WordCount { get; init; } + public string? ChapterText { get; init; } public int? StartPosition { get; init; } public int? ExistingChapterID { get; init; } } diff --git a/PlotLine/Program.cs b/PlotLine/Program.cs index 4b632da..cd94c17 100644 --- a/PlotLine/Program.cs +++ b/PlotLine/Program.cs @@ -186,8 +186,10 @@ public class Program builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddHostedService(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); diff --git a/PlotLine/Services/OnboardingService.cs b/PlotLine/Services/OnboardingService.cs index 92fd24c..1883be8 100644 --- a/PlotLine/Services/OnboardingService.cs +++ b/PlotLine/Services/OnboardingService.cs @@ -198,24 +198,15 @@ public sealed class OnboardingService( var (userId, preview, review, _, _, _) = context.Value; if (!string.Equals(review.Status, ManuscriptScanReviewStatuses.ReadyToImport, StringComparison.Ordinal)) { - throw new InvalidOperationException("Review the scan and choose Save and continue before building the project structure."); + throw new InvalidOperationException("Review the scan and choose Save and continue before preparing chapters."); } await PublishBuildProgress(userId, preview.PreviewID, "Creating chapters...", 10, progress); ValidateScanReview(review, preview); var chapterDecisions = review.Chapters.Where(chapter => chapter.Include).OrderBy(chapter => chapter.ChapterNumber).ToList(); - var chapterKeys = chapterDecisions.Select(chapter => chapter.TemporaryChapterKey).ToHashSet(StringComparer.Ordinal); - var sceneDecisions = review.Scenes - .Where(scene => scene.Include && chapterKeys.Contains(scene.TemporaryChapterKey)) - .OrderBy(scene => chapterDecisions.FindIndex(chapter => chapter.TemporaryChapterKey == scene.TemporaryChapterKey)) - .ThenBy(scene => scene.SceneNumberWithinChapter) - .ToList(); - var characterDecisions = review.Characters - .Where(character => character.Include && !string.Equals(character.Category, "Excluded", StringComparison.OrdinalIgnoreCase)) - .ToList(); - await PublishBuildProgress(userId, preview.PreviewID, "Creating scenes...", 35, progress); + await PublishBuildProgress(userId, preview.PreviewID, "Preparing chapters for analysis...", 35, progress); var request = new OnboardingManuscriptBuildRequest { PreviewID = preview.PreviewID, @@ -229,25 +220,34 @@ public sealed class OnboardingService( SortOrder = (index + 1) * 10, WordCount = preview.Chapters.FirstOrDefault(item => item.TemporaryChapterKey == chapter.TemporaryChapterKey)?.WordCount ?? 0 }).ToList(), - Scenes = sceneDecisions.Select((scene, index) => new OnboardingBuildSceneRequest - { - TemporarySceneKey = scene.TemporarySceneKey, - TemporaryChapterKey = scene.TemporaryChapterKey, - Title = string.IsNullOrWhiteSpace(scene.Title) ? $"Scene {scene.SceneNumberWithinChapter}" : scene.Title!, - SortOrder = (index + 1) * 10, - WordCount = preview.Scenes.FirstOrDefault(item => item.TemporarySceneKey == scene.TemporarySceneKey)?.WordCount ?? 0 - }).ToList(), - Characters = characterDecisions.Select(character => new OnboardingBuildCharacterRequest - { - TemporaryCharacterKey = character.TemporaryCharacterKey, - Name = character.Name, - ExistingCharacterID = character.ExistingCharacterID - }).ToList() + Scenes = [], + Characters = [] }; - await PublishBuildProgress(userId, preview.PreviewID, "Creating characters...", 60, progress); + await PublishBuildProgress(userId, preview.PreviewID, "Saving chapters...", 60, progress); var result = await builds.BuildAsync(userId, request) - ?? throw new InvalidOperationException($"The project structure could not be built. Approved chapters: {request.Chapters.Count}, scenes: {request.Scenes.Count}, characters: {request.Characters.Count}. Return to review, save your selections, and try Build Project again."); + ?? throw new InvalidOperationException($"The project chapters could not be prepared. Approved chapters: {request.Chapters.Count}. Return to review, save your selections, and try again."); + result = new OnboardingManuscriptBuildResult + { + BuildID = result.BuildID, + PreviewID = result.PreviewID, + ProjectID = result.ProjectID, + BookID = result.BookID, + ManuscriptDocumentID = result.ManuscriptDocumentID, + Status = result.Status, + MarkerStatus = result.MarkerStatus, + ChaptersCreated = result.ChaptersCreated, + ScenesCreated = result.ScenesCreated, + CharactersCreated = result.CharactersCreated, + CharactersReused = result.CharactersReused, + SceneAppearancesCreated = result.SceneAppearancesCreated, + AlreadyBuilt = result.AlreadyBuilt, + Message = "Approved chapters are ready for Story Intelligence.", + ChapterMappings = result.ChapterMappings, + SceneMappings = result.SceneMappings, + CharacterMappings = result.CharacterMappings, + MarkerWarning = result.MarkerWarning + }; await scanPreviews.SaveBuildResultAsync(userId, result); await PublishBuildProgress(userId, preview.PreviewID, "Finalising project...", 90, progress); await PublishBuildProgress(userId, preview.PreviewID, result.Message, 100, progress, result); diff --git a/PlotLine/Services/OnboardingStoryIntelligenceService.cs b/PlotLine/Services/OnboardingStoryIntelligenceService.cs new file mode 100644 index 0000000..0cb7caf --- /dev/null +++ b/PlotLine/Services/OnboardingStoryIntelligenceService.cs @@ -0,0 +1,382 @@ +using System.Collections.Concurrent; +using PlotLine.Data; +using PlotLine.Models; +using PlotLine.ViewModels; + +namespace PlotLine.Services; + +public interface IOnboardingStoryIntelligenceService +{ + Task GetOverviewAsync(); + Task StartAsync(); + Task GetProgressAsync(Guid batchId); + Task CancelAsync(Guid batchId); + Task<(StoryIntelligenceProgressViewModel? Progress, StoryIntelligenceImportCommitResult Result)> CommitAsync(Guid batchId, int runId); + Task GetCompletionAsync(Guid batchId); +} + +public sealed class OnboardingStoryIntelligenceService( + IOnboardingRepository onboardingRepository, + IOnboardingService onboarding, + IManuscriptScanPreviewStore scanStore, + IStoryIntelligenceResultRepository runs, + IStoryIntelligenceImportCommitService commits, + IStoryIntelligenceClient client, + IOnboardingStoryIntelligenceBatchStore batchStore, + ICurrentUserService currentUser, + ILogger logger) : IOnboardingStoryIntelligenceService +{ + private const string PromptVersionsSummary = "Chapter=Chapter-Structure-Prompt-V2; Scene=Scene-Prompt-V2"; + private const string SourceType = "OnboardingWordCompanionChapter"; + + public async Task GetOverviewAsync() + { + var userId = RequireUserId(); + var context = await GetContextAsync(userId); + if (context is null) + { + return new StoryIntelligenceOverviewViewModel + { + Message = "Finish the Word Companion scan and review before analysing your manuscript." + }; + } + + var (_, preview, review, build, wizard) = context.Value; + var included = IncludedChapters(preview, review); + var existingBatch = await batchStore.GetLatestForPreviewAsync(userId, preview.PreviewID); + var missingTextCount = included.Count(chapter => string.IsNullOrWhiteSpace(chapter.Preview.ChapterText)); + return new StoryIntelligenceOverviewViewModel + { + PreviewID = preview.PreviewID, + ProjectID = preview.ProjectID, + BookID = preview.BookID, + SelectedProjectName = wizard.SelectedProjectName, + SelectedBookTitle = wizard.SelectedBookTitle, + ApprovedChapterCount = included.Count, + ApprovedWordCount = included.Sum(chapter => chapter.Preview.WordCount), + MissingChapterTextCount = missingTextCount, + ExistingBatchID = existingBatch?.BatchID, + CanStart = included.Count > 0 && missingTextCount == 0, + Message = build is null + ? "PlotDirector will prepare the approved chapters, then analyse them with Story Intelligence." + : "The approved chapters are ready to analyse." + }; + } + + public async Task StartAsync() + { + var userId = RequireUserId(); + var context = await GetContextAsync(userId); + if (context is null) + { + return null; + } + + var (state, preview, review, build, wizard) = context.Value; + var existing = await batchStore.GetLatestForPreviewAsync(userId, preview.PreviewID); + if (existing is not null) + { + return existing; + } + + build ??= await onboarding.BuildApprovedStructureAsync(preview.PreviewID) + ?? throw new InvalidOperationException("The approved chapters could not be prepared for analysis."); + + var chapterIds = build.ChapterMappings.ToDictionary(chapter => chapter.TemporaryChapterKey, chapter => chapter.ChapterID, StringComparer.Ordinal); + var included = IncludedChapters(preview, review); + if (included.Count == 0) + { + throw new InvalidOperationException("Choose at least one chapter before analysing the manuscript."); + } + + if (included.Any(chapter => string.IsNullOrWhiteSpace(chapter.Preview.ChapterText))) + { + throw new InvalidOperationException("The Word Companion scan did not include chapter text. Scan the manuscript again before analysing it."); + } + + var clientStatus = client.GetConfigurationStatus(); + var modelSummary = StoryIntelligenceOptions.BuildModelSummary( + clientStatus.ChapterStructureModel, + clientStatus.SceneIntelligenceModel); + var batchItems = new List(); + + foreach (var chapter in included) + { + if (!chapterIds.TryGetValue(chapter.Preview.TemporaryChapterKey, out var chapterId)) + { + chapterId = chapter.Preview.ExistingChapterID ?? 0; + } + + if (chapterId <= 0) + { + throw new InvalidOperationException($"PlotDirector could not prepare chapter '{chapter.Decision.Title}' for analysis."); + } + + var text = chapter.Preview.ChapterText!.Trim(); + var paragraphs = StoryIntelligenceParagraphs.Split(text); + var runId = await runs.QueueAdminTextAsync(new StoryIntelligenceRunQueueRequest + { + UserID = userId, + ProjectID = preview.ProjectID, + BookID = preview.BookID, + ChapterID = chapterId, + ChapterNumber = chapter.Decision.ChapterNumber, + SourceType = SourceType, + SourceLabel = $"{wizard.SelectedBookTitle}: {chapter.Decision.Title}", + ChapterText = text, + SourceFileName = preview.DocumentTitle, + SourceWordCount = CountWords(text), + SourceCharacterCount = text.Length, + SourceParagraphCount = paragraphs.Count, + SourceChapterCount = 1, + Model = modelSummary, + PromptVersionsSummary = PromptVersionsSummary + }); + + batchItems.Add(new OnboardingStoryIntelligenceBatchItem + { + TemporaryChapterKey = chapter.Preview.TemporaryChapterKey, + ChapterNumber = chapter.Decision.ChapterNumber, + ChapterTitle = chapter.Decision.Title, + ChapterID = chapterId, + RunID = runId + }); + } + + var batch = new OnboardingStoryIntelligenceBatch + { + UserID = userId, + OnboardingID = state.UserOnboardingStateID, + PreviewID = preview.PreviewID, + ProjectID = preview.ProjectID, + BookID = preview.BookID, + ProjectName = wizard.SelectedProjectName, + BookTitle = wizard.SelectedBookTitle, + Items = batchItems + }; + + await batchStore.SaveAsync(batch); + logger.LogInformation( + "Queued onboarding Story Intelligence batch {BatchID}. UserID={UserID} ProjectID={ProjectID} BookID={BookID} Chapters={ChapterCount}", + batch.BatchID, + userId, + preview.ProjectID, + preview.BookID, + batchItems.Count); + + return batch; + } + + public async Task GetProgressAsync(Guid batchId) + { + var batch = await batchStore.GetAsync(RequireUserId(), batchId); + if (batch is null) + { + return null; + } + + var chapters = new List(); + foreach (var item in batch.Items) + { + var run = await runs.GetRunAsync(item.RunID); + if (run is null) + { + chapters.Add(new StoryIntelligenceOnboardingChapterViewModel + { + TemporaryChapterKey = item.TemporaryChapterKey, + ChapterNumber = item.ChapterNumber, + ChapterTitle = item.ChapterTitle, + ChapterID = item.ChapterID, + RunID = item.RunID, + Status = "Missing", + ErrorMessage = "This analysis run could not be found." + }); + continue; + } + + var commit = await runs.GetImportCommitAsync(item.RunID); + var confirmation = run.Status is StoryIntelligenceRunStatuses.Completed or StoryIntelligenceRunStatuses.CompletedWithWarnings + ? await commits.BuildConfirmationAsync(item.RunID) + : null; + + chapters.Add(new StoryIntelligenceOnboardingChapterViewModel + { + TemporaryChapterKey = item.TemporaryChapterKey, + ChapterNumber = item.ChapterNumber, + ChapterTitle = item.ChapterTitle, + ChapterID = item.ChapterID, + RunID = item.RunID, + Status = run.Status, + CurrentStage = run.CurrentStage, + CurrentMessage = run.CurrentMessage, + TotalDetectedScenes = run.TotalDetectedScenes, + CompletedScenes = run.CompletedScenes, + FailedScenes = run.FailedScenes, + TotalTokens = run.TotalTokens, + TotalDurationMs = run.TotalDurationMs, + FailureStage = run.FailureStage, + ErrorMessage = run.ErrorMessage, + HasCompletedCommit = commit is not null && string.Equals(commit.Status, StoryIntelligenceImportCommitStatuses.Completed, StringComparison.OrdinalIgnoreCase), + CanCommit = confirmation?.CanCommit == true, + ScenesCreated = commit?.ScenesCreated ?? 0, + CommitMessage = commit?.Notes, + Blockers = confirmation?.Blockers ?? [], + Warnings = confirmation?.Warnings ?? [] + }); + } + + return new StoryIntelligenceProgressViewModel + { + BatchID = batch.BatchID, + PreviewID = batch.PreviewID, + ProjectID = batch.ProjectID, + BookID = batch.BookID, + ProjectName = batch.ProjectName, + BookTitle = batch.BookTitle, + Chapters = chapters + }; + } + + public async Task CancelAsync(Guid batchId) + { + var progress = await GetProgressAsync(batchId); + if (progress is null) + { + return null; + } + + foreach (var chapter in progress.Chapters.Where(chapter => chapter.IsActive)) + { + await runs.RequestCancelAsync(chapter.RunID); + } + + return await GetProgressAsync(batchId); + } + + public async Task<(StoryIntelligenceProgressViewModel? Progress, StoryIntelligenceImportCommitResult Result)> CommitAsync(Guid batchId, int runId) + { + var progress = await GetProgressAsync(batchId); + if (progress is null || progress.Chapters.All(chapter => chapter.RunID != runId)) + { + return (progress, new StoryIntelligenceImportCommitResult { Success = false, Message = "This chapter analysis could not be found." }); + } + + var result = await commits.CommitAsync(runId, RequireUserId()); + return (await GetProgressAsync(batchId), result); + } + + public async Task GetCompletionAsync(Guid batchId) + { + var progress = await GetProgressAsync(batchId); + if (progress is null) + { + return null; + } + + return new StoryIntelligenceCompletionViewModel + { + BatchID = progress.BatchID, + ProjectID = progress.ProjectID, + BookID = progress.BookID, + ProjectName = progress.ProjectName, + BookTitle = progress.BookTitle, + ChaptersCommitted = progress.CommittedChapterCount, + ScenesCreated = progress.Chapters.Sum(chapter => chapter.ScenesCreated) + }; + } + + private async Task<(UserOnboardingState State, ManuscriptScanPreview Preview, ManuscriptScanReviewDecision Review, OnboardingManuscriptBuildResult? Build, OnboardingWizardViewModel Wizard)?> GetContextAsync(int userId) + { + var state = await onboardingRepository.GetAsync(userId); + if (state is null || !state.ProjectID.HasValue || !state.BookID.HasValue) + { + return null; + } + + var preview = await scanStore.GetLatestPreviewAsync(userId, state.UserOnboardingStateID); + if (preview is null) + { + return null; + } + + var review = await scanStore.GetReviewAsync(userId, preview.PreviewID); + if (review is null || !string.Equals(review.Status, ManuscriptScanReviewStatuses.ReadyToImport, StringComparison.Ordinal)) + { + return null; + } + + var build = await scanStore.GetBuildResultAsync(userId, preview.PreviewID); + var wizard = await onboarding.GetWizardAsync(); + return (state, preview, review, build, wizard); + } + + private int RequireUserId() + => currentUser.UserId ?? throw new InvalidOperationException("Sign in before using Story Intelligence."); + + private static IReadOnlyList IncludedChapters(ManuscriptScanPreview preview, ManuscriptScanReviewDecision review) + { + var chapters = preview.Chapters.ToDictionary(chapter => chapter.TemporaryChapterKey, StringComparer.Ordinal); + return review.Chapters + .Where(chapter => chapter.Include && chapters.ContainsKey(chapter.TemporaryChapterKey)) + .OrderBy(chapter => chapter.ChapterNumber) + .Select(chapter => new IncludedChapter(chapter, chapters[chapter.TemporaryChapterKey])) + .ToList(); + } + + private static int CountWords(string value) + => string.IsNullOrWhiteSpace(value) + ? 0 + : value.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries).Length; + + private sealed record IncludedChapter(ManuscriptScanChapterReviewDecision Decision, ManuscriptScanChapterPreview Preview); +} + +public interface IOnboardingStoryIntelligenceBatchStore +{ + Task SaveAsync(OnboardingStoryIntelligenceBatch batch); + Task GetAsync(int userId, Guid batchId); + Task GetLatestForPreviewAsync(int userId, Guid previewId); +} + +public sealed class OnboardingStoryIntelligenceBatchStore : IOnboardingStoryIntelligenceBatchStore +{ + private readonly ConcurrentDictionary batches = new(); + + public Task SaveAsync(OnboardingStoryIntelligenceBatch batch) + { + batches[batch.BatchID] = batch; + return Task.CompletedTask; + } + + public Task GetAsync(int userId, Guid batchId) + => Task.FromResult(batches.TryGetValue(batchId, out var batch) && batch.UserID == userId ? batch : null); + + public Task GetLatestForPreviewAsync(int userId, Guid previewId) + => Task.FromResult(batches.Values + .Where(batch => batch.UserID == userId && batch.PreviewID == previewId) + .OrderByDescending(batch => batch.CreatedUtc) + .FirstOrDefault()); +} + +public sealed class OnboardingStoryIntelligenceBatch +{ + public Guid BatchID { get; init; } = Guid.NewGuid(); + public int UserID { get; init; } + public int OnboardingID { get; init; } + public Guid PreviewID { get; init; } + public int ProjectID { get; init; } + public int BookID { get; init; } + public string? ProjectName { get; init; } + public string? BookTitle { get; init; } + public DateTime CreatedUtc { get; init; } = DateTime.UtcNow; + public IReadOnlyList Items { get; init; } = []; +} + +public sealed class OnboardingStoryIntelligenceBatchItem +{ + public string TemporaryChapterKey { get; init; } = string.Empty; + public int ChapterNumber { get; init; } + public string ChapterTitle { get; init; } = string.Empty; + public int ChapterID { get; init; } + public int RunID { get; init; } +} diff --git a/PlotLine/ViewModels/OnboardingViewModels.cs b/PlotLine/ViewModels/OnboardingViewModels.cs index 141fad0..42897c9 100644 --- a/PlotLine/ViewModels/OnboardingViewModels.cs +++ b/PlotLine/ViewModels/OnboardingViewModels.cs @@ -106,28 +106,79 @@ public sealed class OnboardingNudgeViewModel public sealed class StoryIntelligenceOverviewViewModel { + public Guid? PreviewID { get; init; } public int? ProjectID { get; init; } public int? BookID { get; init; } public string? SelectedProjectName { get; init; } public string? SelectedBookTitle { get; init; } + public int ApprovedChapterCount { get; init; } + public int ApprovedWordCount { get; init; } + public int MissingChapterTextCount { get; init; } + public string? Message { get; init; } + public Guid? ExistingBatchID { get; init; } public StoryIntelligenceJobProgress? ExistingJob { get; init; } - public bool CanStart => ProjectID.HasValue && BookID.HasValue && ExistingJob?.IsActive != true; + public bool CanStart { get; init; } } public sealed class StoryIntelligenceProgressViewModel { public StoryIntelligenceJobProgress Job { get; init; } = new(); + public Guid? BatchID { get; init; } + public Guid? PreviewID { get; init; } + public int ProjectID { get; init; } + public int BookID { get; init; } public string? ProjectName { get; init; } public string? BookTitle { get; init; } + public IReadOnlyList Chapters { get; init; } = []; + public int ChapterCount => Chapters.Count; + public int CompletedChapterCount => Chapters.Count(chapter => chapter.IsRunComplete); + public int CommittedChapterCount => Chapters.Count(chapter => chapter.HasCompletedCommit); + public int FailedChapterCount => Chapters.Count(chapter => chapter.IsFailed); + public int TotalDetectedScenes => Chapters.Sum(chapter => chapter.TotalDetectedScenes ?? 0); + public int TotalCompletedScenes => Chapters.Sum(chapter => chapter.CompletedScenes ?? 0); + public int TotalFailedScenes => Chapters.Sum(chapter => chapter.FailedScenes ?? 0); + public bool HasActiveRuns => Chapters.Any(chapter => chapter.IsActive); + public bool AllCommitted => Chapters.Count > 0 && Chapters.All(chapter => chapter.HasCompletedCommit); } public sealed class StoryIntelligenceCompletionViewModel { public StoryIntelligenceJobProgress Job { get; init; } = new(); + public Guid? BatchID { get; init; } public int ProjectID { get; init; } public int BookID { get; init; } public string? ProjectName { get; init; } public string? BookTitle { get; init; } + public int ChaptersCommitted { get; init; } + public int ScenesCreated { get; init; } +} + +public sealed class StoryIntelligenceOnboardingChapterViewModel +{ + public string TemporaryChapterKey { get; init; } = string.Empty; + public int ChapterNumber { get; init; } + public string ChapterTitle { get; init; } = string.Empty; + public int ChapterID { get; init; } + public int RunID { get; init; } + public string Status { get; init; } = string.Empty; + public string? CurrentStage { get; init; } + public string? CurrentMessage { get; init; } + public int? TotalDetectedScenes { get; init; } + public int? CompletedScenes { get; init; } + public int? FailedScenes { get; init; } + public int? TotalTokens { get; init; } + public long? TotalDurationMs { get; init; } + public string? FailureStage { get; init; } + public string? ErrorMessage { get; init; } + public bool HasCompletedCommit { get; init; } + public bool CanCommit { get; init; } + public int ScenesCreated { get; init; } + public string? CommitMessage { get; init; } + public IReadOnlyList Blockers { get; init; } = []; + public IReadOnlyList Warnings { get; init; } = []; + public bool IsActive => Status is StoryIntelligenceRunStatuses.Pending or StoryIntelligenceRunStatuses.Running; + public bool IsRunComplete => Status is StoryIntelligenceRunStatuses.Completed or StoryIntelligenceRunStatuses.CompletedWithWarnings; + public bool IsFailed => Status is StoryIntelligenceRunStatuses.Failed or StoryIntelligenceRunStatuses.Cancelled; } public sealed class StoryIntelligenceDashboardViewModel diff --git a/PlotLine/Views/Onboarding/BuildComplete.cshtml b/PlotLine/Views/Onboarding/BuildComplete.cshtml index 78f48ad..ef3b646 100644 --- a/PlotLine/Views/Onboarding/BuildComplete.cshtml +++ b/PlotLine/Views/Onboarding/BuildComplete.cshtml @@ -1,12 +1,12 @@ @model OnboardingManuscriptBuildResult @{ - ViewData["Title"] = "Project structure created"; + ViewData["Title"] = "Chapters prepared"; }
    - @foreach (var label in new[] { "Welcome", "Writing Preferences", "Project", "Book", "Connect Word", "Scan", "Review", "Build Project", "Complete" }) + @foreach (var label in new[] { "Welcome", "Writing Preferences", "Project", "Book", "Connect Word", "Scan", "Review", "Prepare Chapters", "Complete" }) {
  1. @@ -15,12 +15,12 @@ }
-

Build Project

+

Prepare Chapters

-

Your project structure has been created.

+

Your chapters are ready.

-

@(Model.AlreadyBuilt ? "This manuscript has already been built into PlotDirector, so nothing was duplicated." : "PlotDirector created the approved chapters, scenes, characters and scene appearances.")

+

@(Model.AlreadyBuilt ? "These chapters have already been prepared in PlotDirector, so nothing was duplicated." : "PlotDirector prepared the approved chapters. Story Intelligence will create scenes after you review the analysis.")

@if (!string.IsNullOrWhiteSpace(Model.MarkerWarning)) {

@Model.MarkerWarning

@@ -33,28 +33,28 @@ @Model.ChaptersCreated
- Scenes + Scenes created now @Model.ScenesCreated
- Characters + Characters created now @(Model.CharactersCreated + Model.CharactersReused)
- Scene appearances + Scene appearances now @Model.SceneAppearancesCreated
-

Ready for your next pass

-

Your project now has a working story map. Open the overview to inspect the structure, or head straight into the writer workspace to keep drafting.

+

Next: analyse manuscript

+

Story Intelligence will detect scenes from the approved chapter text. You will review the results before PlotDirector creates scenes.

diff --git a/PlotLine/Views/Onboarding/Index.cshtml b/PlotLine/Views/Onboarding/Index.cshtml index 3c390f7..5963179 100644 --- a/PlotLine/Views/Onboarding/Index.cshtml +++ b/PlotLine/Views/Onboarding/Index.cshtml @@ -12,7 +12,7 @@ new { Label = "Connect Word", Order = 5 }, new { Label = "Scan", Order = 6 }, new { Label = "Review", Order = 7 }, - new { Label = "Build Project", Order = 8 }, + new { Label = "Prepare Chapters", Order = 8 }, new { Label = "Complete", Order = 9 } }; } @@ -340,7 +340,7 @@

Review saved. PlotDirector can now create the approved structure.

-

Ready to build chapters, scenes, characters and scene appearances.

+

Ready to prepare approved chapters for Story Intelligence.

diff --git a/PlotLine/Views/Onboarding/StoryIntelligenceProgress.cshtml b/PlotLine/Views/Onboarding/StoryIntelligenceProgress.cshtml index 42bd12c..ffe426d 100644 --- a/PlotLine/Views/Onboarding/StoryIntelligenceProgress.cshtml +++ b/PlotLine/Views/Onboarding/StoryIntelligenceProgress.cshtml @@ -1,60 +1,170 @@ @model StoryIntelligenceProgressViewModel @{ - ViewData["Title"] = "Preparing Story Intelligence"; + ViewData["Title"] = "Review scenes"; }
-
+
+ @if (Model.HasActiveRuns) + { + + } +

@Model.BookTitle

-

Preparing Story Intelligence

-

PlotDirector is preparing the analysis engine. This phase does not run AI analysis or generate story suggestions.

+

Review scenes

+

PlotDirector is analysing the approved chapters. When a chapter is ready, you can create its scenes in PlotDirector.

-
-
- @Model.Job.CurrentMessage - @Model.Job.ProgressPercent% + @if (TempData["OnboardingStoryIntelligenceError"] is string error) + { +
@error
+ } + @if (TempData["OnboardingStoryIntelligenceMessage"] is string message) + { +
@message
+ } + +
+
+ Chapters + @Model.CompletedChapterCount / @Model.ChapterCount
-
-@section Scripts { - +@functions { + private static string ChapterStatusLabel(StoryIntelligenceOnboardingChapterViewModel chapter) + { + if (chapter.HasCompletedCommit) + { + return "Scenes created"; + } + + if (chapter.CanCommit) + { + return "Ready to create"; + } + + if (chapter.IsActive) + { + return "Analysing"; + } + + if (chapter.IsFailed) + { + return "Needs attention"; + } + + return "Reviewing"; + } } diff --git a/PlotLine/Views/Projects/Index.cshtml b/PlotLine/Views/Projects/Index.cshtml index e139118..c436cc3 100644 --- a/PlotLine/Views/Projects/Index.cshtml +++ b/PlotLine/Views/Projects/Index.cshtml @@ -70,11 +70,11 @@
@if (Model.StoryIntelligence.Job?.IsActive == true) { - @Model.StoryIntelligence.ButtonText + @Model.StoryIntelligence.ButtonText } else if (Model.StoryIntelligence.Job?.IsCompleted == true) { - @Model.StoryIntelligence.ButtonText + @Model.StoryIntelligence.ButtonText } else { diff --git a/PlotLine/wwwroot/js/word-companion-host.js b/PlotLine/wwwroot/js/word-companion-host.js index 1e8b042..512cc14 100644 --- a/PlotLine/wwwroot/js/word-companion-host.js +++ b/PlotLine/wwwroot/js/word-companion-host.js @@ -1932,6 +1932,8 @@ chapterNumber: chapters.length + 1, title, wordCount: 0, + chapterTextParagraphs: [], + chapterText: "", startPosition: paragraph?.index ?? null, existingChapterID: isOpening ? null : paragraphAnchorId(paragraph, "PD-CHAPTER") }; @@ -1966,6 +1968,7 @@ } chapter.wordCount += words; + chapter.chapterTextParagraphs.push(text); scene.wordCount += words; if (!scene.openingTextPreview && text && !sceneSeparatorTexts.has(text)) { scene.openingTextPreview = text.length > 180 ? `${text.slice(0, 177)}...` : text; @@ -1987,6 +1990,7 @@ startChapter(paragraph, text); totalWordCount += words; chapter.wordCount += words; + chapter.chapterTextParagraphs.push(text); return; } @@ -2018,6 +2022,13 @@ throw new Error("No chapters were detected. Make sure your chapter headings use Heading 1, or add clear chapter headings."); } + chapters.forEach((item) => { + item.chapterText = Array.isArray(item.chapterTextParagraphs) + ? item.chapterTextParagraphs.join("\n\n") + : ""; + delete item.chapterTextParagraphs; + }); + const documentTextForDiscovery = documentText.join("\n"); return { previewID: "00000000-0000-0000-0000-000000000000", diff --git a/PlotLine/wwwroot/js/word-companion-host.min.js b/PlotLine/wwwroot/js/word-companion-host.min.js index 0d51bcb..273d8b8 100644 --- a/PlotLine/wwwroot/js/word-companion-host.min.js +++ b/PlotLine/wwwroot/js/word-companion-host.min.js @@ -1 +1,25 @@ -(()=>{const e="plotdirector.word.projectId",t="plotdirector.word.bookId",n="plotdirector.word.activeTab",r={documentGuid:"plotdirector.word.documentGuid",bookId:"plotdirector.word.boundBookId",projectId:"plotdirector.word.boundProjectId",bindingVersion:"plotdirector.word.bindingVersion"},o=new Set(["scene","review","maintenance","diagnostics"]),a=document.querySelector(".word-companion-shell"),c=document.querySelector(".word-companion-tabs"),i=[...document.querySelectorAll("[data-tab-button]")],s=[...document.querySelectorAll("[data-tab-panel]")],d=document.querySelector("[data-linked-manuscript-card]"),l=document.querySelector("[data-linked-project-title]"),u=document.querySelector("[data-linked-book-title]"),p=document.querySelector("[data-first-run-wizard]"),h=document.querySelector("[data-first-run-project-select]"),m=document.querySelector("[data-first-run-book-select]"),y=document.querySelector("[data-first-run-new-book-title]"),g=document.querySelector("[data-first-run-create-book]"),w=document.querySelector("[data-first-run-scan]"),f=document.querySelector("[data-first-run-cancel]"),I=document.querySelector("[data-first-run-status]"),b=document.querySelector("[data-first-run-summary]"),S=document.querySelector("[data-first-run-character-section]"),C=document.querySelector("[data-first-run-character-status]"),k=document.querySelector("[data-first-run-character-candidates]"),v=document.querySelector("[data-first-run-character-actions]"),x=document.querySelector("[data-first-run-create-characters]"),N=document.querySelector("[data-first-run-skip-characters]"),E=document.querySelector("[data-first-run-continue]"),D=document.querySelector("[data-first-run-import-actions]"),P=document.querySelector("[data-first-run-import]"),L=document.querySelector("[data-first-run-import-cancel]"),q=document.querySelector("[data-first-run-replace-dialog]"),A=document.querySelector("[data-first-run-replace-confirm]"),$=document.querySelector("[data-first-run-replace-cancel]"),T=document.querySelector("[data-unlink-word-document-dialog]"),j=document.querySelector("[data-unlink-word-document-confirm]"),W=document.querySelector("[data-unlink-word-document-cancel]"),R=document.querySelector("[data-office-status]"),U=document.querySelector("[data-connection-status]"),M=document.querySelector("[data-summary-connection]"),O=document.querySelector("[data-summary-project]"),B=document.querySelector("[data-summary-book]"),H=document.querySelector("[data-project-select]"),G=document.querySelector("[data-book-select]"),F=document.querySelector("[data-runtime-book-selectors]"),V=document.querySelector("[data-project-message]"),K=document.querySelector("[data-book-message]"),Y=document.querySelector("[data-refresh-current-scene]"),J=document.querySelector("[data-refresh-document]"),_=document.querySelector("[data-current-chapter]"),z=document.querySelector("[data-current-scene]"),Q=document.querySelector("[data-current-word-count]"),X=document.querySelector("[data-scene-tracking-status]"),Z=document.querySelector("[data-document-message]"),ee=document.querySelector("[data-sync-note]"),te=document.querySelector("[data-document-outline]"),ne=document.querySelector("[data-analyse-manuscript-structure]"),re=document.querySelector("[data-apply-structure-sync]"),oe=document.querySelector("[data-structure-preview-status]"),ae=document.querySelector("[data-structure-preview-results]"),ce=document.querySelector("[data-refresh-plotdirector-scene]"),ie=document.querySelector("[data-plotdirector-scene-status]"),se=document.querySelector("[data-anchor-status]"),de=document.querySelector("[data-anchor-book-id]"),le=document.querySelector("[data-anchor-chapter-id]"),ue=document.querySelector("[data-anchor-scene-id]"),pe=document.querySelector("[data-attach-plotdirector-ids]"),he=document.querySelector("[data-unlink-word-document]"),me=document.querySelector("[data-plotdirector-scene-details]"),ye=document.querySelector("[data-plotdirector-scene-title]"),ge=document.querySelector("[data-plotdirector-revision-status]"),we=document.querySelector("[data-plotdirector-estimated-words]"),fe=document.querySelector("[data-plotdirector-actual-words]"),Ie=document.querySelector("[data-plotdirector-blocked-status]"),be=document.querySelector("[data-plotdirector-blocked-reason-row]"),Se=document.querySelector("[data-plotdirector-blocked-reason]"),Ce=document.querySelector("[data-runtime-last-sync]"),ke=document.querySelector("[data-sync-scene-progress]"),ve=document.querySelector("[data-sync-word-count]"),xe=document.querySelector("[data-sync-plotdirector-count]"),Ne=document.querySelector("[data-sync-last-synced]"),Ee=document.querySelector("[data-sync-status]"),De=document.querySelector("[data-writing-brief-block]"),Pe=document.querySelector("[data-writing-brief]"),Le=document.querySelector("[data-plotdirector-link-groups]"),qe=document.querySelector("[data-character-review-summary]"),Ae=document.querySelector("[data-asset-review-summary]"),$e=document.querySelector("[data-location-review-summary]"),Te=document.querySelector("[data-character-chips]"),je=document.querySelector("[data-asset-chips]"),We=(document.querySelector("[data-location-chips]"),document.querySelector("[data-primary-location-select]")),Re=document.querySelector("[data-save-scene-links]"),Ue=document.querySelector("[data-scene-links-status]"),Me=document.querySelector("[data-links-editing-scene]"),Oe=document.querySelector("[data-chapter-create-link]"),Be=document.querySelector("[data-chapter-create-link-chapter]"),He=document.querySelector("[data-create-plotdirector-chapter]"),Ge=document.querySelector("[data-link-existing-chapter]"),Fe=document.querySelector("[data-chapter-create-link-status]"),Ve=document.querySelector("[data-scene-create-link]"),Ke=document.querySelector("[data-create-link-chapter]"),Ye=document.querySelector("[data-create-link-scene]"),Je=document.querySelector("[data-create-plotdirector-scene]"),_e=document.querySelector("[data-link-existing-scene]"),ze=document.querySelector("[data-create-link-status]"),Qe=document.querySelector("[data-link-existing-dialog]"),Xe=document.querySelector("[data-link-scene-search]"),Ze=document.querySelector("[data-link-scene-options]"),et=document.querySelector("[data-link-selected-scene]"),tt=document.querySelector("[data-link-dialog-cancel]"),nt=document.querySelector("[data-link-dialog-status]"),rt=document.querySelector("[data-create-scene-dialog]"),ot=document.querySelector("[data-create-dialog-scene]"),at=document.querySelector("[data-create-dialog-chapter]"),ct=document.querySelector("[data-create-dialog-confirm]"),it=document.querySelector("[data-create-dialog-cancel]"),st=document.querySelector("[data-link-existing-chapter-dialog]"),dt=document.querySelector("[data-link-chapter-search]"),lt=document.querySelector("[data-link-chapter-options]"),ut=document.querySelector("[data-link-selected-chapter]"),pt=document.querySelector("[data-link-chapter-dialog-cancel]"),ht=document.querySelector("[data-link-chapter-dialog-status]"),mt=document.querySelector("[data-create-chapter-dialog]"),yt=document.querySelector("[data-create-chapter-dialog-chapter]"),gt=document.querySelector("[data-create-chapter-dialog-book]"),wt=document.querySelector("[data-create-chapter-dialog-confirm]"),ft=document.querySelector("[data-create-chapter-dialog-cancel]"),It=document.querySelector("[data-apply-structure-sync-dialog]"),bt=document.querySelector("[data-apply-structure-sync-summary]"),St=document.querySelector("[data-apply-structure-sync-confirm]"),Ct=document.querySelector("[data-apply-structure-sync-cancel]"),kt=document.querySelector("[data-resolve-project-id]"),vt=document.querySelector("[data-resolve-project-title]"),xt=document.querySelector("[data-resolve-book-id]"),Nt=document.querySelector("[data-resolve-book-title]"),Et=document.querySelector("[data-resolve-detected-chapter]"),Dt=document.querySelector("[data-resolve-detected-scene]"),Pt=document.querySelector("[data-resolve-request-chapter]"),Lt=document.querySelector("[data-resolve-request-scene]"),qt=document.querySelector("[data-resolve-result]"),At=document.querySelector("[data-resolve-chapter-id]"),$t=document.querySelector("[data-resolve-scene-id]"),Tt=document.querySelector("[data-run-diagnostics]"),jt=document.querySelector("[data-diagnostic-host]"),Wt=document.querySelector("[data-diagnostic-platform]"),Rt=document.querySelector("[data-diagnostic-ready]"),Ut=document.querySelector("[data-diagnostic-word-api]"),Mt=document.querySelector("[data-diagnostic-last-scan]"),Ot=document.querySelector("[data-diagnostic-last-error]"),Bt=document.querySelector("[data-diagnostic-anchor-type]"),Ht=document.querySelector("[data-diagnostic-stored-scene-id]"),Gt=document.querySelector("[data-diagnostic-stored-chapter-id]"),Ft=document.querySelector("[data-diagnostic-resolution-method]"),Vt=document.querySelector("[data-diagnostic-paragraphs]"),Kt=document.querySelector("[data-diagnostic-heading1]"),Yt=document.querySelector("[data-diagnostic-heading2]"),Jt="true"===a?.dataset.authenticated,_t=Number.parseInt(a?.dataset.userId||"",10),zt=String(a?.dataset.companionVersion||"20C").trim();let Qt=[],Xt=[],Zt=!1,en=!1,tn="Unknown",nn="Unknown",rn="",on="",an=null,cn=null,sn=null,dn=null,ln=null,un=null,pn="",hn="Title Match",mn=!1,yn=[],gn=null,wn=null,fn=[],In=null,bn=null,Sn=null,Cn=null,kn=null,vn=null,xn=null,Nn=null,En=null,Dn=null,Pn=[],Ln=null,qn=!1,An=!1,$n=0,Tn=!1,jn=!1,Wn=!1,Rn=null,Un="Manual",Mn=!1,On=null,Bn=!1,Hn=0,Gn=!1,Fn=0,Vn=null;const Kn=1200;let Yn=!1,Jn=!1,_n=null,zn=!1,Qn=!1,Xn=null,Zn=null,er=null,tr=!1,nr=!1,rr=null,or="",ar=[],cr=null,ir="",sr=null,dr="",lr=[],ur=null,pr="",hr=null,mr="",yr=[],gr=null,wr="",fr="",Ir="",br="",Sr=0,Cr=null,kr=null,vr={characters:[],assets:[],locations:[],primaryLocationId:null};const xr=e=>{const t=Date.now();t-Sr<1e3||(Sr=t,console.debug(`[Word Companion Runtime] ${e}`))},Nr=e=>{console.debug(`[Word Companion Timing] ${e}`)},Er=e=>{R&&(R.textContent=e)},Dr=e=>"links"===e?"review":"structure"===e?"maintenance":e,Pr=(e,t=!0)=>{const r=Dr(e),a=o.has(r)&&i.some(e=>e.dataset.tabButton===r)?r:"scene";for(const e of i){const t=e.dataset.tabButton===a;e.classList.toggle("is-active",t),e.setAttribute("aria-selected",t?"true":"false")}for(const e of s){const t=e.dataset.tabPanel===a;e.classList.toggle("is-active",t),e.hidden=!t}t&&((e,t)=>{try{t?window.localStorage.setItem(e,String(t)):window.localStorage.removeItem(e)}catch{}})(n,a)},Lr=()=>{const e=Dr((e=>{try{return window.localStorage.getItem(e)||""}catch{return""}})(n));Pr(o.has(e)?e:"scene",!1)},qr=e=>{U&&(U.textContent=e),M&&(M.textContent=e)},Ar=e=>{V&&(V.textContent=e)},$r=e=>{K&&(K.textContent=e)},Tr=e=>{Z&&(Z.textContent=e)},jr=e=>{ie&&(ie.textContent=e)},Wr=e=>{const t=t=>Object.prototype.hasOwnProperty.call(e,t);t("anchorType")&&Eo(Bt,eo(e.anchorType)),t("storedSceneId")&&Eo(Ht,eo(e.storedSceneId)),t("storedChapterId")&&Eo(Gt,eo(e.storedChapterId)),t("resolutionMethod")&&Eo(Ft,eo(e.resolutionMethod))},Rr=()=>{const e=Uo(),t=Number.isInteger(ln)&&ln>0&&dn===ln,n=!Number.isInteger(un)||un<=0||sn===un,r=t&&n;Eo(se,r?"Attached to PlotDirector":"Not attached"),Eo(de,e?.bookId?String(e.bookId):"-"),Eo(le,Number.isInteger(un)&&un>0?String(un):"-"),Eo(ue,Number.isInteger(ln)&&ln>0?String(ln):"-"),pe&&(Or(pe,r&&!mn),pe.disabled=!ln||r&&!mn,pe.textContent=dn&&!r?"Reattach PlotDirector IDs":"Attach PlotDirector IDs"),Wr({anchorType:hn||"Title Match",storedSceneId:dn,storedChapterId:sn,resolutionMethod:pn})},Ur=(e,t,n,r=null,o=null,a=null)=>{const c=e||"",i=t||"",s=Number.isInteger(n)?n:null,d=Number.isInteger(a)&&a>0?a:null,l=Number.isInteger(r)&&r>0?r:null,u=Number.isInteger(o)&&o>0?o:null,p=rn===c&&on===i&&an===s&&cn===d&&sn===l&&dn===u;rn=e||"",on=t||"",an=Number.isInteger(n)?n:null,cn=Number.isInteger(a)&&a>0?a:null,sn=Number.isInteger(r)&&r>0?r:null,dn=Number.isInteger(o)&&o>0?o:null,p||(_&&(_.textContent=e||"Not detected"),z&&(z.textContent=t||"Not detected"),Q&&(Q.textContent=Number.isInteger(n)?String(n):"-"),Eo(ve,Number.isInteger(n)?String(n):"-"),Rr())},Mr=()=>{_n&&(window.clearTimeout(_n),_n=null)},Or=(e,t)=>{e&&(e.hidden=t)},Br=()=>{Or(he,!Cn)},Hr=e=>{Eo(Ee,e)},Gr=e=>{Eo(oe,e)},Fr=(e="Select a project and book to analyse manuscript structure.")=>{if(Sn=null,ae){ae.innerHTML="";const e=document.createElement("p");e.textContent="No preview generated.",ae.append(e)}Gr(e),Kr()},Vr=(e=Sn)=>((e=Sn)=>Ea.flatMap(t=>Array.isArray(e?.[t.key])?e[t.key]:[]))(e).filter(e=>"couldLink"===e.category||"wouldCreateChapters"===e.category||"wouldCreateScenes"===e.category),Kr=()=>{re&&(re.disabled=Wn||0===Vr().length)},Yr=()=>{ne&&(ne.disabled=Wn||!Ro()||!Uo()),Kr()},Jr=()=>{Me&&(Me.textContent=ln?`Editing links for: ${Ir||on||"Current scene"}`:"Link a PlotDirector scene first.")},_r=()=>{const e=Mn||!ln,t=e||!un||zn;ke&&(ke.disabled=t),Re&&(Re.disabled=e),We&&(We.disabled=e);for(const t of[Te,je])for(const n of t?[...t.querySelectorAll("input[type='checkbox']")]:[])n.disabled=e},zr=e=>{"Live"!==e&&"Manual"!==e||(Un=e),Eo(X,e||Un||"Manual")},Qr=(e,t="")=>{Mn=!!e,Mn&&(Mr(),Sc()),Eo(X,Mn?"Stale":Un),_r(),t&&(Tr(t),ao(t))},Xr=(e,t=null,n="")=>{ln=Number.isInteger(e)&&e>0?e:null,un=Number.isInteger(t)&&t>0?t:null,pn=n||"",ln!==Xn&&(Zn=null),ln||(Mr(),Sc()),_r(),Hr(Mn?"Refresh Current Scene before syncing.":ln?"Ready to sync.":"No resolved PlotDirector scene."),ao(Mn?"Refresh Current Scene before saving.":ln?"Ready to save.":"Link a PlotDirector scene first."),Jr(),Rr()},Zr=e=>{Y&&(Y.disabled=e,Y.textContent=e?"Refreshing...":"Refresh Current Scene")},eo=e=>null==e||""===e?"-":String(e),to=e=>{if(!e)return"-";const t=e instanceof Date?e:new Date(e);if(Number.isNaN(t.getTime()))return"-";const n=new Date;return`Last synced: ${t.toDateString()===n.toDateString()?"Today":t.toLocaleDateString()} ${t.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`},no=e=>{kn=e||null;const t=kn?.lastSyncUtc||kn?.manuscriptDocument?.lastSyncUtc;Eo(Ce,to(t)),Eo(Ne,to(t)),Wo()},ro=()=>{vn=null,xn=null,Nn=null,Fn+=1,Vn=null,Cc(),kc(),vc(),no(null)},oo=()=>{xn=null,Nn=null},ao=e=>{Eo(Ue,e)},co=e=>{Eo(ze,e)},io=e=>{Eo(Fe,e)},so=()=>!!Uo()&&!!rn&&!un,lo=()=>!!Uo()&&!!on&&Number.isInteger(un)&&un>0,uo=()=>{Or(Oe,!0),io(""),He&&(He.disabled=!0,He.textContent="Create Chapter in PlotDirector"),Ge&&(Ge.disabled=!0,Ge.textContent="Link to Existing Chapter")},po=(e="")=>{Eo(Be,eo(rn)),Or(Oe,!1),io(e);const t=so();He&&(He.disabled=!t,He.textContent="Create Chapter in PlotDirector"),Ge&&(Ge.disabled=!t,Ge.textContent="Link to Existing Chapter")},ho=()=>{Or(Ve,!0),co(""),Je&&(Je.disabled=!0,Je.textContent="Create Scene in PlotDirector"),_e&&(_e.disabled=!0,_e.textContent="Link to Existing Scene")},mo=(e="")=>{Eo(Ke,eo(rn)),Eo(Ye,eo(on)),Or(Ve,!1),co(e);const t=lo();Je&&(Je.disabled=!t,Je.textContent="Create Scene in PlotDirector"),_e&&(_e.disabled=!t,_e.textContent="Link to Existing Scene")},yo=e=>{jr(e),Or(me,!0),Or(De,!0),Or(Le,!1),uo(),ho(),pn="",Ir="",br="",hn=dn?"SceneID Anchor":sn?"ChapterID Anchor":"Title Match",mn=!1,vr={characters:[],assets:[],locations:[],primaryLocationId:null},wo(Te,[],"character"),wo(je,[],"asset"),fo([],null,!1),aa(),Xr(null),Eo(xe,"-"),Eo(ye,"-"),Eo(ge,"-"),Eo(we,"-"),Eo(fe,"-"),Eo(Ie,"-"),Eo(Se,"-"),Or(be,!0),Rr(),Jr()},go=(e,t)=>"asset"===t?e?.assetId||e?.AssetId||e?.storyAssetId||e?.StoryAssetId||0:"location"===t?e?.locationId||e?.LocationId||0:e?.characterId||e?.CharacterId||0,wo=(e,t,n,r=!1)=>{if(!e)return;e.innerHTML="";const o=Array.isArray(t)?t:[];if(0===o.length){const t=document.createElement("p");return t.className="word-companion-empty-list",t.textContent="None",void e.append(t)}for(const t of o){const o=Number.parseInt(go(t,n),10);if(!Number.isInteger(o)||o<=0)continue;const a=document.createElement("label");a.className="word-companion-check-item";const c=document.createElement("input");c.type="checkbox",c.value=String(o),c.checked=!!t.linked,c.disabled=!r;const i=document.createElement("span");i.textContent=t.name||"Unnamed",a.append(c,i),e.append(a)}},fo=(e,t,n=!1)=>{if(!We)return;We.innerHTML="";const r=document.createElement("option");r.value="",r.textContent="None",We.append(r);const o=Number.parseInt(t||"",10);for(const t of Array.isArray(e)?e:[]){const e=Number.parseInt(go(t,"location"),10);if(!Number.isInteger(e)||e<=0)continue;const n=document.createElement("option");n.value=String(e),n.textContent=t.name||"Unnamed",n.selected=e===o,We.append(n)}We.disabled=!n},Io=e=>String(e||"").trim().replace(/\s+/g," "),bo=(e,t)=>{const n=Io(e);if(!n)return"";const r=new RegExp(`^${"scene"===t?"scene":"chapter"}\\s+(?:\\d+|[ivxlcdm]+|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)\\s*(?::|-|\\u2013|\\u2014)\\s*`,"i");return Io(n.replace(r,""))||n},So=e=>{const t=t=>Object.prototype.hasOwnProperty.call(e,t);t("projectId")&&Eo(kt,eo(e.projectId)),t("projectTitle")&&Eo(vt,eo(e.projectTitle)),t("bookId")&&Eo(xt,eo(e.bookId)),t("bookTitle")&&Eo(Nt,eo(e.bookTitle)),t("detectedChapter")&&Eo(Et,eo(e.detectedChapter)),t("detectedScene")&&Eo(Dt,eo(e.detectedScene)),t("requestChapter")&&Eo(Pt,eo(e.requestChapter)),t("requestScene")&&Eo(Lt,eo(e.requestScene)),t("result")&&Eo(qt,eo(e.result)),t("chapterId")&&Eo(At,eo(e.chapterId)),t("sceneId")&&Eo($t,eo(e.sceneId))},Co=async e=>{if(Nn===e&&Array.isArray(xn?.chapters))return xn.chapters;const t=await Za(`/api/word-companion/books/${e}/structure`);return Nn=e,xn=t||null,Array.isArray(t?.chapters)?t.chapters:[]},ko=async e=>{const t=await Za(`/api/word-companion/books/${e}/chapters`);return Array.isArray(t?.chapters)?t.chapters:[]},vo=e=>{if(Number.isInteger(un)&&un>0){const t=e.find(e=>e.chapterId===un);if(t)return t}const t=bo(rn,"chapter").toLocaleLowerCase();return t&&e.find(e=>bo(e.title,"chapter").toLocaleLowerCase()===t)||null},xo=async()=>{const e=Uo();return e?vo(await Co(e.bookId)):null},No=async(e,t,n,r,o)=>{const a=await Za(`/api/word-companion/scenes/${t}/companion`);try{a.revisionStatus=await(async(e,t)=>{const n=await Za(`/api/word-companion/books/${e}/structure`),r=Array.isArray(n?.chapters)?n.chapters:[];for(const e of r){const n=(Array.isArray(e.scenes)?e.scenes:[]).find(e=>e.sceneId===t);if(n)return n.revisionStatus||""}return""})(e,t)}catch{a.revisionStatus=""}var c;hn=o||"Title Match",mn=!1,Qr(!1),jr("Linked to PlotDirector"),Tr(""),uo(),ho(),Xr(t,n,r),c=a,Ir=c?.sceneTitle||on||"",br=c?.chapterTitle||rn||"",Eo(ye,eo(c?.sceneTitle)),Eo(ge,eo(c?.revisionStatus||c?.revisionStatusName||"Not provided")),Eo(we,eo(c?.estimatedWords)),Eo(fe,eo(c?.actualWords)),Eo(xe,eo(c?.actualWords)),Eo(Ie,c?.blocked?"Blocked":"Not blocked"),c?.blockedReason?(Eo(Se,c.blockedReason),Or(be,!1)):(Eo(Se,"-"),Or(be,!0)),Pe&&(Pe.textContent=c?.writingBrief||"No writing brief has been added for this scene."),vr={characters:Array.isArray(c?.characters)?c.characters:[],assets:Array.isArray(c?.assets)?c.assets:[],locations:Array.isArray(c?.locations)?c.locations:[],primaryLocationId:c?.primaryLocationId??null},wo(Te,vr.characters,"character",!!ln&&!Mn),wo(je,vr.assets,"asset",!!ln&&!Mn),fo(vr.locations,vr.primaryLocationId,!!ln&&!Mn),aa(),Jr(),ao(Mn?"Refresh Current Scene before saving.":ln?"Ready to save.":"No resolved PlotDirector scene."),Or(me,!1),Or(De,!1),Or(Le,!1),nc(t,n),bc(),qc()},Eo=(e,t)=>{if(e){const n=null==t?"":String(t);e.textContent!==n&&(e.textContent=n)}},Do=e=>{const t=t=>Object.prototype.hasOwnProperty.call(e,t);t("host")&&Eo(jt,e.host),t("platform")&&Eo(Wt,e.platform),t("ready")&&Eo(Rt,e.ready),t("wordApi")&&Eo(Ut,e.wordApi),t("lastScan")&&Eo(Mt,e.lastScan),t("lastError")&&Eo(Ot,e.lastError),t("paragraphs")&&Eo(Vt,e.paragraphs),t("heading1")&&Eo(Kt,e.heading1),t("heading2")&&Eo(Yt,e.heading2)},Po=e=>{const t=[];return e?.name&&t.push(e.name),e?.code&&e.code!==e.name&&t.push(e.code),e?.message&&t.push(e.message),e?.debugInfo?.errorLocation&&t.push(`Location: ${e.debugInfo.errorLocation}`),t.length>0?t.join(": "):String(e)},Lo=async()=>{if(!window.Office||"function"!=typeof window.Office.onReady)return Zt=!1,en=!1,tn="Unavailable",nn="Unavailable",Do({host:tn,platform:nn,ready:"No",wordApi:"No"}),null;const e=await window.Office.onReady();return Zt=!0,tn=e?.host||"Unknown",nn=e?.platform||"Unknown",en=!!window.Word&&!!window.Office.HostType&&e?.host===window.Office.HostType.Word,Do({host:tn,platform:nn,ready:"Yes",wordApi:en?"Yes":"No"}),e},qo=e=>{try{const t=window.localStorage.getItem(e),n=Number.parseInt(t||"",10);return Number.isInteger(n)&&n>0?n:null}catch{return null}},Ao=(e,t)=>{try{t?window.localStorage.setItem(e,String(t)):window.localStorage.removeItem(e)}catch{}},$o=(e,t)=>{if(!e)return;e.innerHTML="";const n=document.createElement("option");n.value="",n.textContent=t,e.append(n),e.disabled=!0},To=(e,t,n,r,o)=>{if(!e)return;e.innerHTML="";const a=document.createElement("option");a.value="",a.textContent=t,e.append(a);for(const t of n){const n=document.createElement("option");n.value=String(t[r]),n.textContent=t[o],e.append(n)}e.disabled=0===n.length},jo=e=>{const t=String(e?.displayTitle||"").trim();if(t)return t;const n=String(e?.title||"").trim(),r=String(e?.subtitle||"").trim();return r?`${n}: ${r}`:n},Wo=()=>{const e=!!Cn;Or(d,!e),Or(F,e),e&&(Eo(l,kn?.projectTitle||Ro()?.title||"Project"),Eo(u,kn?.bookTitle||jo(Uo())||"Book"))},Ro=()=>{const e=Number.parseInt(H?.value||"",10);return Qt.find(t=>t.projectId===e)||null},Uo=()=>{const e=Number.parseInt(G?.value||"",10);return Xt.find(t=>t.bookId===e)||null},Mo=()=>{const e=Number.parseInt(h?.value||"",10);return Qt.find(t=>t.projectId===e)||null},Oo=()=>{const e=Number.parseInt(m?.value||"",10);return Xt.find(t=>t.bookId===e)||null},Bo=()=>{const t=Cn?.projectId||Mo()?.projectId||Ro()?.projectId||qo(e);return Number.isInteger(t)&&t>0?t:null},Ho=()=>{const e=Cn?.bookId||Oo()?.bookId||Uo()?.bookId||qo(t);return Number.isInteger(e)&&e>0?e:null},Go=()=>{const e=String(window.Office?.context?.document?.url||"").trim();if(!e)return"";const t=e.split(/[\\/]/).filter(Boolean).pop()||"";try{return decodeURIComponent(t)}catch{return t}},Fo=()=>({userID:Number.isInteger(_t)&&_t>0?_t:null,companionVersion:zt,machineName:"",documentOpen:!!en,currentDocumentName:Go(),linkedProjectID:Bo(),linkedBookID:Ho()}),Vo=()=>{(async()=>{Cr&&Cr.state===signalR.HubConnectionState.Connected&&await Cr.invoke("CompanionHeartbeat",Fo())})().catch(e=>{console.debug("Word Companion heartbeat unavailable.",e)})},Ko=async()=>{Jt&&window.signalR&&!Cr&&(Cr=(new signalR.HubConnectionBuilder).withUrl("/hubs/word-companion-follow").withAutomaticReconnect().build(),Cr.onreconnected(Vo),Cr.on("ScanCurrentDocument",e=>{Sa(e)}),Cr.on("UpdateOnboardingBuildMarkers",e=>{Ca(e)}),Cr.onclose(()=>{kr&&(window.clearInterval(kr),kr=null)}),await Cr.start(),await Cr.invoke("RegisterCompanion",Fo()),kr=window.setInterval(Vo,25e3))};window.addEventListener("beforeunload",()=>{kr&&(window.clearInterval(kr),kr=null),Cr&&Cr.stop().catch(()=>{})});const Yo=e=>Eo(I,e),Jo=()=>{const e=Mo(),t=Oo(),n=An&&Date.now()-$n>=750;w&&(w.disabled=!e||!t||Tn),g&&(g.disabled=!e||!String(y?.value||"").trim()||Tn),P&&(P.disabled=!En?.canImport||!Dn||Tn),x&&(x.disabled=!n||jn||0===Xo().length)},_o=e=>{Or(p,!e),Or(c,e),Br(),Wo();for(const t of s)Or(t,!!e||"scene"!==t.dataset.tabPanel&&!t.classList.contains("is-active"));e||Lr()},zo=()=>{if(!h)return;if(0===Qt.length)return void $o(h,"No projects available.");To(h,"Choose a project",Qt,"projectId","title");const e=Number.parseInt(H?.value||"",10);Number.isInteger(e)&&e>0&&(h.value=String(e))},Qo=(e="Select a Project and Book to begin.")=>{En=null,Dn=null,Pn=[],qn=!1,An=!1,$n=0,Or(b,!0),Or(D,!0),Or(S,!0),Or(v,!0),b&&(b.innerHTML=""),k&&(k.innerHTML=""),Eo(C,""),Yo(e),Jo()},Xo=()=>k?[...k.querySelectorAll("input[type='checkbox']:checked")].map(e=>String(e.value||"").trim()).filter(Boolean):[],Zo=e=>{if(Pn=Array.isArray(e)?e:[],S&&k){if(k.innerHTML="",Or(S,!1),Or(v,!qn||0===Pn.length),Or(x,!1),Or(N,!1),Or(E,!0),0===Pn.length)return Eo(C,"No likely major characters found."),void Jo();Eo(C,`${Pn.length} likely major characters found.`);for(const e of Pn){const t=document.createElement("label");t.className="word-companion-character-candidate";const n=document.createElement("input");n.type="checkbox",n.value=e.text||"",n.checked="high"===String(e.confidence||"").trim().toLowerCase(),n.addEventListener("change",Jo);const r=document.createElement("strong");r.textContent=e.text||"";const o=document.createElement("em"),a=String(e.reason||"").trim();o.textContent=a?`${Number(e.mentionCount||0).toLocaleString()} mentions · ${a}`:`${Number(e.mentionCount||0).toLocaleString()} mentions`;const c=document.createElement("span");c.className="word-companion-character-candidate-text",c.append(r,o),t.append(n,c),k.append(t)}Jo()}},ea=()=>{const e=window.Office?.context?.document?.settings;if(!e)return null;const t=String(e.get(r.documentGuid)||"").trim(),n=Number.parseInt(e.get(r.bookId)||"",10),o=Number.parseInt(e.get(r.projectId)||"",10),a=Number.parseInt(e.get(r.bindingVersion)||"",10);return!t||!Number.isInteger(n)||n<=0||!Number.isInteger(o)||o<=0?null:{documentGuid:t,bookId:n,projectId:o,bindingVersion:Number.isInteger(a)&&a>0?a:1}},ta=()=>new Promise((e,t)=>{const n=window.Office?.context?.document?.settings;if(n){for(const e of Object.values(r))"function"==typeof n.remove?n.remove(e):n.set(e,"");n.saveAsync(n=>{n.status===window.Office.AsyncResultStatus.Succeeded?e():t(n.error||new Error("Unable to remove Word document metadata."))})}else t(new Error("Word document settings are unavailable."))}),na=()=>Cn?.documentGuid?Cn.documentGuid:window.crypto?.randomUUID?window.crypto.randomUUID():"10000000-1000-4000-8000-100000000000".replace(/[018]/g,e=>(Number(e)^16*Math.random()>>Number(e)/4).toString(16)),ra=()=>Cn?.documentGuid||ea()?.documentGuid||"",oa=()=>{const e=Ro(),t=Uo();O&&(O.textContent=e?.title||"(Not connected)"),B&&(B.textContent=t?jo(t):"(Not connected)"),ee&&(ee.textContent=t?"":"Select a PlotDirector book before refreshing."),Yr(),Wo()},aa=()=>{const e=Array.isArray(vr.characters)?vr.characters.length:0,t=Array.isArray(vr.assets)?vr.assets.length:0,n=Array.isArray(vr.locations)?vr.locations.length:0;Eo(qe,`Characters (${e})`),Eo(Ae,`Assets (${t})`),Eo($e,`Locations (${n})`)},ca=e=>String(e?.styleBuiltIn||e?.style||"").replace(/\s+/g,"").toLowerCase(),ia=(e,t)=>{const n=ca(e);return n===`heading${t}`||n.endsWith(`.heading${t}`)||n.includes(`heading${t}`)},sa=e=>String(e||"").trim().split(/\s+/).filter(Boolean).length,da=(e,t)=>{const n=String(e||"").match(new RegExp(`^${t}-(\\d+)$`,"i"));if(!n)return null;const r=Number.parseInt(n[1],10);return Number.isInteger(r)&&r>0?r:null},la=e=>{try{return Array.isArray(e?.contentControls?.items)?e.contentControls.items:[]}catch(e){return console.warn("Word paragraph content controls were not available.",e),[]}},ua=(e,t)=>{const n=la(e);for(const e of n){const n=da(e.tag,t);if(n)return n}return null},pa=(e,t)=>{const n=[];let r=0,o=0,a=null,c=null;e.forEach((e,t)=>{const i=String(e.text||"").trim();if(i){if(ia(e,1))return r+=1,a={index:n.length+1,paragraphIndex:t,title:i,anchorId:ua(e,"PD-CHAPTER"),scenes:[]},n.push(a),void(c=null);if(ia(e,2)){if(o+=1,!a)return;return c={index:a.scenes.length+1,paragraphIndex:t,title:i,anchorId:ua(e,"PD-SCENE"),wordCount:0},void a.scenes.push(c)}c&&(c.wordCount+=sa(i))}});const i=t.find(e=>String(e.text||"").trim()),s=i?e.findIndex(e=>{return t=e,n=i,String(t?.text||"").trim()===String(n?.text||"").trim()&&ca(t)===ca(n);var t,n}):-1,d=s>=0&&n.filter(e=>e.paragraphIndex<=s).at(-1)||null,l=d&&s>=0&&d.scenes.filter(e=>e.paragraphIndex<=s).at(-1)||null;return{chapters:n,currentChapter:d,currentScene:l,paragraphCount:e.length,heading1Count:r,heading2Count:o}},ha=new Set(["***","###"]),ma=(e,t)=>{const n=String(e?.text||"").trim();return{index:t,text:n,styleBuiltIn:e?.styleBuiltIn||"",style:e?.style||"",wordCount:sa(n),chapterAnchorId:null,sceneAnchorId:null}},ya=(e,t)=>{e&&(e.endParagraphIndex=t)},ga=(e,t)=>{e&&(e.endParagraphIndex=t,ya(e.scenes.at(-1),t))},wa=(e,t,n)=>e?.[t]??e?.[n]??null,fa=(e,t,n)=>e?.[t]??e?.[n]??null,Ia=async(e,t,n,r={})=>{Cr&&Cr.state===signalR.HubConnectionState.Connected&&await Cr.invoke("ReportOnboardingScanProgress",{userID:fa(e,"userID","UserID"),onboardingID:fa(e,"onboardingID","OnboardingID"),message:t,percentComplete:n,chapterCount:r.chapterCount||0,sceneCount:r.sceneCount||0,characterCandidateCount:r.characterCandidateCount||0,totalWordCount:r.totalWordCount||0})},ba=async(e,t)=>{Cr&&Cr.state===signalR.HubConnectionState.Connected&&await Cr.invoke("FailOnboardingScan",{userID:fa(e,"userID","UserID"),onboardingID:fa(e,"onboardingID","OnboardingID"),message:t})},Sa=async e=>{var t;if(en&&window.Word)try{await Ia(e,"Preparing document scan",5);const n=((e,t)=>{const n=[],r=[],o=[];let a=null,c=null,i=0,s=0;const d=()=>{c=null},l=(e,t,r=!1)=>(d(),a={temporaryChapterKey:`chapter-${n.length+1}`,chapterNumber:n.length+1,title:t,wordCount:0,startPosition:e?.index??null,existingChapterID:r?null:ua(e,"PD-CHAPTER")},n.push(a),u(e,null),a),u=(e,t=null)=>a?(d(),c={temporarySceneKey:`scene-${r.length+1}`,temporaryChapterKey:a.temporaryChapterKey,sceneNumberWithinChapter:r.filter(e=>e.temporaryChapterKey===a.temporaryChapterKey).length+1,title:t,wordCount:0,openingTextPreview:"",startPosition:e?.index??null,existingSceneID:e?ua(e,"PD-SCENE"):null},r.push(c),c):null,p=(e,t)=>{!a||!c||t<=0||(a.wordCount+=t,c.wordCount+=t,c.openingTextPreview||!e||ha.has(e)||(c.openingTextPreview=e.length>180?`${e.slice(0,177)}...`:e))};if(e.forEach((e,t)=>{e.index=t;const n=String(e.text||"").trim();if(!n)return;o.push(n);const r=sa(n);return ia(e,1)?(s+=1,l(e,n),i+=r,void(a.wordCount+=r)):(a||l(e,"Opening pages",!0),ia(e,2)?(u(e,n),i+=r,void p(n,r)):void(ha.has(n)?u(e,null):(i+=r,p(n,r))))}),0===i)throw new Error("The document appears to be empty. Add manuscript text, then try again.");if(0===s)throw new Error("No chapters were detected. Make sure your chapter headings use Heading 1, or add clear chapter headings.");const h=o.join("\n");return{previewID:"00000000-0000-0000-0000-000000000000",userID:t.userID||t.UserID||0,onboardingID:t.onboardingID||t.OnboardingID||0,projectID:t.projectID||t.ProjectID||0,bookID:t.bookID||t.BookID||0,source:"WordCompanion",documentTitle:Go()||"Word manuscript",companionDocumentIdentifier:ra(),totalWordCount:i,chapterCount:n.length,sceneCount:r.length,characterCandidateCount:0,createdUtc:(new Date).toISOString(),chapters:n,scenes:r,characterCandidates:[],documentTextForDiscovery:h}})(await window.Word.run(async t=>{const n=t.document.body.paragraphs;n.load("items/text,items/style,items/styleBuiltIn"),await t.sync(),await Ia(e,"Detecting chapters",20);for(const e of n.items)(ia(e,1)||ia(e,2))&&e.contentControls.load("items/tag,title");return await t.sync(),n.items}),e);await Ia(e,"Detecting scene breaks",50,n),await Ia(e,`Scene ${n.sceneCount} of ${n.sceneCount} scanned`,70,n),await Ia(e,"Finding character candidates",85,n);const r=n.documentTextForDiscovery||"";delete n.documentTextForDiscovery;try{const e=await ec("/api/word-companion/manuscript/discover-characters",{projectId:n.projectID,documentText:r,includeExcluded:!0});n.characterCandidates=(t=e?.candidates,(Array.isArray(t)?t:[]).map((e,t)=>({temporaryCharacterKey:`character-${t+1}`,name:wa(e,"text","Text")||wa(e,"name","Name")||"",mentionCount:Number.parseInt(wa(e,"mentionCount","MentionCount")||"0",10)||0,qualityScore:Number.parseInt(wa(e,"qualityScore","QualityScore")||"0",10)||0,category:wa(e,"category","Category")||"PossibleCharacter",reason:wa(e,"reason","Reason")||"",isExistingCharacterMatch:!!wa(e,"isExistingCharacterMatch","IsExistingCharacterMatch"),suggestedImportance:null})).filter(e=>e.name)),n.characterCandidateCount=n.characterCandidates.filter(e=>"excluded"!==String(e.category||"").toLowerCase()).length}catch(e){console.warn("Unable to refine onboarding character candidates.",e),n.characterCandidates=[],n.characterCandidateCount=0}await Ia(e,"Preparing preview",95,n),Ln=n,await Cr.invoke("CompleteOnboardingScan",n)}catch(t){console.error("Unable to complete onboarding scan.",t),await ba(e,ka(t))}else await ba(e,"Open your manuscript in Microsoft Word, then try the scan again.")},Ca=async e=>{if(!Ln||!en||!window.Word||"function"!=typeof window.Word.run)throw new Error("Word document access is unavailable.");const t=new Map((e?.chapterMappings||e?.ChapterMappings||[]).map(e=>[e.temporaryChapterKey||e.TemporaryChapterKey,e.chapterID||e.ChapterID])),n=new Map((e?.sceneMappings||e?.SceneMappings||[]).map(e=>[e.temporarySceneKey||e.TemporarySceneKey,e.sceneID||e.SceneID]));await window.Word.run(async e=>{const r=e.document.body.paragraphs;r.load("items/text,items/style,items/styleBuiltIn"),await e.sync();for(const e of Ln.chapters||[]){const n=t.get(e.temporaryChapterKey),o=Number.parseInt(e.startPosition,10);n&&Number.isInteger(o)&&r.items[o]&&ic(r.items[o],"PlotDirector Chapter",`PD-CHAPTER-${n}`,"PD-CHAPTER")}for(const e of Ln.scenes||[]){const t=n.get(e.temporarySceneKey),o=Number.parseInt(e.startPosition,10);t&&Number.isInteger(o)&&r.items[o]&&ic(r.items[o],"PlotDirector Scene",`PD-SCENE-${t}`,"PD-SCENE")}await e.sync()})},ka=e=>{const t=Po(e);return/No chapters were detected/i.test(t)?"No chapters were detected. Make sure your chapter headings use Heading 1, or add clear chapter headings.":/empty/i.test(t)?"The document appears to be empty. Add manuscript text, then try again.":/network|disconnect|connection/i.test(t)?"The Word Companion disconnected before the scan finished. Reopen Word and try again.":`The scan could not finish: ${t}`},va=(e,t)=>{const n=(t||[]).find(e=>String(e.text||"").trim());if(!e||!n)return e?.currentParagraphIndex??-1;const r=e.paragraphs.filter(e=>((e,t)=>String(e?.text||"").trim()===String(t?.text||"").trim()&&ca(e)===ca(t))(e,n)).map(e=>e.index);return 0===r.length?e.currentParagraphIndex??-1:Number.isInteger(e.currentParagraphIndex)?r.reduce((t,n)=>Math.abs(n-e.currentParagraphIndex){if(!vn)return!1;const t=((e,t)=>!e||!Number.isInteger(t)||t<0?null:e.chapters.filter(e=>e.startParagraphIndex<=t).at(-1)||null)(vn,e),n=((e,t)=>!e||!Number.isInteger(t)||t<0?null:e.scenes.filter(e=>e.startParagraphIndex<=t).at(-1)||null)(t,e),r=t?.startParagraphIndex!==vn.currentChapter?.startParagraphIndex||n?.startParagraphIndex!==vn.currentScene?.startParagraphIndex||t?.anchorId!==vn.currentChapter?.anchorId||n?.anchorId!==vn.currentScene?.anchorId;return vn.currentParagraphIndex=e,vn.currentChapter=t,vn.currentScene=n,!!r&&(Ur(t?.title,n?.title,n?.wordCount,t?.anchorId,n?.anchorId,t?.index),r)},Na=e=>{if(!te)return;if(te.innerHTML="",0===e.chapters.length){const e=document.createElement("p");return e.textContent="No chapters detected. Use Heading 1 for chapter titles.",void te.append(e)}if(!e.chapters.some(e=>e.scenes.length>0)){const e=document.createElement("p");e.textContent="No scenes detected. Use Heading 2 for scene titles.",te.append(e)}for(const t of e.chapters){const e=document.createElement("div");e.className="word-companion-outline-chapter";const n=document.createElement("strong");if(n.textContent=t.title,e.append(n),t.scenes.length>0){const n=document.createElement("ul");for(const e of t.scenes){const t=document.createElement("li");t.textContent=e.title,n.append(t)}e.append(n)}te.append(e)}},Ea=[{key:"alreadyLinked",title:"Already Linked"},{key:"couldLink",title:"Could Link"},{key:"wouldCreateChapters",title:"Would Create Chapters"},{key:"wouldCreateScenes",title:"Would Create Scenes"},{key:"manualReview",title:"Manual Review Required"}],Da={alreadyLinked:"Already Linked",couldLink:"Could Link",wouldCreateChapter:"Would Create Chapter",wouldCreateScene:"Would Create Scene",manualReview:"Manual Review Required"},Pa={alreadyLinked:"✓",couldLink:"~",wouldCreateChapter:"+",wouldCreateScene:"+",manualReview:"?"},La=(e,t)=>bo(e,t).toLocaleLowerCase(),qa=({category:e,action:t,type:n,wordTitle:r,match:o="",anchorStatus:a="None",matches:c=[],pdChapter:i=null,pdScene:s=null,wordChapter:d=null,wordScene:l=null,parentItem:u=null})=>({id:`${n}-${d?.index||0}-${l?.index||0}-${d?.paragraphIndex??l?.paragraphIndex??0}`,category:e,action:t,type:n,wordTitle:r,match:o,anchorStatus:a,matches:c,pdChapter:i,pdScene:s,wordChapter:d,wordScene:l,parentItem:u,result:"Pending",error:""}),Aa=(e,t)=>{Array.isArray(e[t.category])&&e[t.category].push(t)},$a=e=>Array.isArray(e?.scenes)?e.scenes:[],Ta=(e,t,n)=>{const r=e.anchorId?`PD-CHAPTER-${e.anchorId}`:"None";if(e.anchorId){const o=t.find(t=>t.chapterId===e.anchorId);if(o){const t=qa({category:"alreadyLinked",action:"alreadyLinked",type:"Chapter",wordTitle:e.title,match:`Chapter ${o.chapterId}: ${o.title}`,anchorStatus:r,pdChapter:o,wordChapter:e});return Aa(n,t),t}const a=qa({category:"manualReview",action:"manualReview",type:"Chapter",wordTitle:e.title,anchorStatus:`${r} not found`,matches:[],pdChapter:null,wordChapter:e});return Aa(n,a),a}const o=t.filter(t=>La(t.title,"chapter")===La(e.title,"chapter"));if(1===o.length){const t=qa({category:"couldLink",action:"couldLink",type:"Chapter",wordTitle:e.title,match:`Chapter ${o[0].chapterId}: ${o[0].title}`,anchorStatus:r,pdChapter:o[0],wordChapter:e});return Aa(n,t),t}if(o.length>1){const t=qa({category:"manualReview",action:"manualReview",type:"Chapter",wordTitle:e.title,anchorStatus:r,matches:o.map(e=>`Chapter ${e.chapterId}: ${e.title}`),pdChapter:null,wordChapter:e});return Aa(n,t),t}const a=qa({category:"wouldCreateChapters",action:"wouldCreateChapter",type:"Chapter",wordTitle:e.title,anchorStatus:r,pdChapter:null,wordChapter:e});return Aa(n,a),a},ja=(e,t,n,r)=>{const o=e.anchorId?`PD-SCENE-${e.anchorId}`:"None";if(e.anchorId){const a=((e,t)=>{for(const n of e){const e=$a(n).find(e=>e.sceneId===t);if(e)return{chapter:n,scene:e}}return null})(n,e.anchorId);return a?void Aa(r,qa({category:"alreadyLinked",action:"alreadyLinked",type:"Scene",wordTitle:e.title,match:`Scene ${a.scene.sceneId}: ${a.scene.title}`,anchorStatus:o,pdChapter:a.chapter,pdScene:a.scene,wordChapter:t?.wordChapter,wordScene:e,parentItem:t})):void Aa(r,qa({category:"manualReview",action:"manualReview",type:"Scene",wordTitle:e.title,anchorStatus:`${o} not found`,wordChapter:t?.wordChapter,wordScene:e,parentItem:t}))}if(!t?.pdChapter&&"manualReview"===t?.category)return void Aa(r,qa({category:"manualReview",action:"manualReview",type:"Scene",wordTitle:e.title,anchorStatus:o,matches:["Parent chapter requires manual review."],wordChapter:t?.wordChapter,wordScene:e,parentItem:t}));if(!t?.pdChapter)return void Aa(r,qa({category:"wouldCreateScenes",action:"wouldCreateScene",type:"Scene",wordTitle:e.title,anchorStatus:o,wordChapter:t?.wordChapter,wordScene:e,parentItem:t}));const a=$a(t.pdChapter).filter(t=>La(t.title,"scene")===La(e.title,"scene"));1!==a.length?a.length>1?Aa(r,qa({category:"manualReview",action:"manualReview",type:"Scene",wordTitle:e.title,anchorStatus:o,matches:a.map(e=>`Scene ${e.sceneId}: ${e.title}`),wordChapter:t.wordChapter,wordScene:e,parentItem:t})):Aa(r,qa({category:"wouldCreateScenes",action:"wouldCreateScene",type:"Scene",wordTitle:e.title,anchorStatus:o,pdChapter:t.pdChapter,wordChapter:t.wordChapter,wordScene:e,parentItem:t})):Aa(r,qa({category:"couldLink",action:"couldLink",type:"Scene",wordTitle:e.title,match:`Scene ${a[0].sceneId}: ${a[0].title}`,anchorStatus:o,pdChapter:t.pdChapter,pdScene:a[0],wordChapter:t.wordChapter,wordScene:e,parentItem:t}))},Wa=e=>{const t=document.createElement("article");t.className="word-companion-preview-item";const n=document.createElement("strong");n.textContent=`${Pa[e.action]||""} ${e.type}: ${e.wordTitle}`,t.append(n);const r=document.createElement("span");if(r.textContent=`Anchor: ${e.anchorStatus||"None"}`,t.append(r),e.match){const n=document.createElement("span");n.textContent=`Match: ${e.match}`,t.append(n)}if(Array.isArray(e.matches)&&e.matches.length>0){const n=document.createElement("div");n.className="word-companion-preview-matches";const r=document.createElement("span");r.textContent="Matches:",n.append(r);const o=document.createElement("ul");for(const t of e.matches){const e=document.createElement("li");e.textContent=t,o.append(e)}n.append(o),t.append(n)}const o=document.createElement("span");if(o.textContent=`Action: ${Da[e.action]||e.action}`,t.append(o),e.result&&"Pending"!==e.result){const n=document.createElement("span");n.textContent=e.error?`Result: ${e.result} - ${e.error}`:`Result: ${e.result}`,t.append(n)}return t},Ra=e=>{if(ae){ae.innerHTML="";for(const t of Ea){const n=document.createElement("section");n.className="word-companion-preview-group";const r=document.createElement("strong");r.textContent=t.title,n.append(r);const o=Array.isArray(e?.[t.key])?e[t.key]:[];if(0===o.length){const e=document.createElement("p");e.className="word-companion-empty-list",e.textContent="None",n.append(e)}else for(const e of o)n.append(Wa(e));ae.append(n)}Kr()}},Ua=async e=>{const t=e.document.body.paragraphs,n=e.document.getSelection().paragraphs;t.load("text,styleBuiltIn,style"),n.load("text,styleBuiltIn,style"),await e.sync();try{for(const e of t.items)(ia(e,1)||ia(e,2))&&e.contentControls.load("items/tag,title");await e.sync()}catch(e){console.warn("Unable to load Word heading content controls. Continuing without PlotDirector anchors.",e)}const r=pa(t.items,n.items);return r.bodyParagraphs=t.items,r},Ma=async(e,t)=>{const n=e.document.body.paragraphs,r=e.document.getSelection().paragraphs;n.load("text,styleBuiltIn,style"),r.load("text,styleBuiltIn,style"),await e.sync();const o=((e,t)=>{const n=e.map(ma),r=[];let o=0,a=0,c=null,i=null,s=0;const d=(e,t=null,n=null)=>c?(ya(i,e.index-1),i={index:c.scenes.length+1,paragraphIndex:e.index,startParagraphIndex:e.index,endParagraphIndex:e.index,title:t||`Scene ${c.scenes.length+1}`,anchorId:n,wordCount:0},c.scenes.push(i),i):null;for(const e of n)e.text&&(ia(e,1)?(ga(c,e.index-1),o+=1,c={index:r.length+1,paragraphIndex:e.index,startParagraphIndex:e.index,endParagraphIndex:e.index,title:e.text,anchorId:e.chapterAnchorId,wordCount:0,scenes:[]},r.push(c),i=null,d(e,"Scene 1",e.sceneAnchorId),s+=e.wordCount):c&&(t&&ha.has(e.text)?(a+=1,d(e)):(c.wordCount+=e.wordCount,i&&(i.wordCount+=e.wordCount),s+=e.wordCount)));return ga(c,n.length-1),{paragraphs:n,chapters:r,usesExplicitScenes:!!t,paragraphCount:n.length,heading1Count:o,heading2Count:a,documentWordCount:s,currentParagraphIndex:null,currentChapter:null,currentScene:null}})(n.items,t),a=va(o,r.items);return vn=o,xa(a),xr("Cache rebuilt."),o},Oa=()=>window.performance&&"function"==typeof window.performance.now?window.performance.now():Date.now(),Ba=e=>!!e&&!e.stale&&e.generation===Fn&&e.documentGuid===ra()&&e.sceneId===ln,Ha=async(e={})=>{const t=!!e.includeSelection,n=!1!==e.updateDisplay,r=Fn,o=Oa(),a=Ro(),c=Uo(),i=vn?.currentScene;if(!i||!Number.isInteger(i.startParagraphIndex)||!Number.isInteger(i.endParagraphIndex))return null;if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return null;const s=Oa(),d=await window.Word.run(async e=>{const n=e.document.body.paragraphs;n.load("text");let r=null;return t&&(r=e.document.getSelection().paragraphs,r.load("text,styleBuiltIn,style")),await e.sync(),{bodyTexts:n.items.map(e=>String(e.text||"")),selectionParagraphs:t&&r?r.items.map(e=>({text:String(e.text||""),styleBuiltIn:e.styleBuiltIn,style:e.style})):[]}}),l=Math.round(Oa()-s);if(r!==Fn)return Nr(`Office snapshot: ${l}ms stale`),{stale:!0,generation:r};let u=!1;if(t){const e=va(vn,d.selectionParagraphs);u=xa(e)}const p=vn?.currentScene;if(!p||!Number.isInteger(p.startParagraphIndex)||!Number.isInteger(p.endParagraphIndex))return null;const h=[];let m=0;const y=Math.min(p.endParagraphIndex,d.bodyTexts.length-1),g=vn.currentChapter?.startParagraphIndex;for(let e=p.startParagraphIndex;e<=y;e+=1){const t=String(d.bodyTexts[e]||"").trim();t&&!ha.has(t)&&e!==g&&(h.push(t),m+=sa(t))}p.wordCount=m,n&&Ur(vn.currentChapter?.title,p.title,m,vn.currentChapter?.anchorId,p.anchorId,vn.currentChapter?.index);const w={documentGuid:ra(),projectId:a?.projectId||null,bookId:c?.bookId||null,chapterId:un,sceneId:ln,chapterTitle:vn.currentChapter?.title||"",sceneTitle:p.title||"",sceneText:h.join("\n"),wordCount:m,selectionSignature:`${vn.currentParagraphIndex??-1}:${p.startParagraphIndex}:${p.endParagraphIndex}`,selectionChanged:u,generation:r};return Vn=w,Nr(`Office snapshot: ${l}ms; scene words: ${m}; total snapshot: ${Math.round(Oa()-o)}ms`),w},Ga=async()=>Ba(Vn)?Vn:await Ha(),Fa=async()=>{if(Tr(""),!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return Ur(null,null,null),Tr("Word document access is unavailable."),Do({lastScan:"Failure",lastError:"Word document access is unavailable."}),!1;J&&(J.disabled=!0,J.textContent="Scanning..."),Tr("Scanning document structure...");try{const e=await window.Word.run(async e=>{const t=Uo();return t?await Ma(e,!!t.usesExplicitScenes):(vn=null,await Ua(e))});return vn||Ur(e.currentChapter?.title,e.currentScene?.title,e.currentScene?.wordCount,e.currentChapter?.anchorId,e.currentScene?.anchorId,e.currentChapter?.index),yo("Not detected"),Na(e),Tr(""),Do({lastScan:"Success",lastError:"-",paragraphs:String(e.paragraphCount),heading1:String(e.heading1Count),heading2:String(e.heading2Count)}),!0}catch(e){const t=Po(e);return console.error("Unable to scan the Word document.",e),Ur(null,null,null),Tr(`Unable to scan the Word document: ${t}`),Do({lastScan:"Failure",lastError:t}),!1}finally{J&&(J.disabled=!1,J.textContent="Refresh Document Structure")}},Va=async()=>{const e=Uo();if(!Ro()||!e)return Gr("Select a project and book to analyse manuscript structure."),!1;if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return Gr("Word document access is unavailable."),Do({lastScan:"Failure",lastError:"Word document access is unavailable."}),!1;ne&&(ne.disabled=!0,ne.textContent="Analysing..."),Gr("Analysing manuscript structure...");try{const t=await window.Word.run(async e=>await Ua(e)),n=await Za(`/api/word-companion/books/${e.bookId}/structure`);return Ur(t.currentChapter?.title,t.currentScene?.title,t.currentScene?.wordCount,t.currentChapter?.anchorId,t.currentScene?.anchorId,t.currentChapter?.index),Na(t),Sn=((e,t)=>{const n={alreadyLinked:[],couldLink:[],wouldCreateChapters:[],wouldCreateScenes:[],manualReview:[]},r=Array.isArray(t?.chapters)?t.chapters:[];for(const t of Array.isArray(e?.chapters)?e.chapters:[]){const e=Ta(t,r,n);for(const o of $a(t))ja(o,e,r,n)}return n})(t,n),Ra(Sn),Gr("Preview generated. No changes have been applied."),Do({lastScan:"Success",lastError:"-",paragraphs:String(t.paragraphCount),heading1:String(t.heading1Count),heading2:String(t.heading2Count)}),!0}catch(e){return console.error("Unable to analyse manuscript structure.",e),Gr("Unable to analyse manuscript structure."),Do({lastScan:"Failure",lastError:Po(e)}),!1}finally{ne&&(ne.textContent="Analyse Manuscript Structure",Yr())}},Ka=e=>{Rn&&(Rn(e),Rn=null),It?.close?It.close():It&&(It.hidden=!0)},Ya=()=>{const e=((e=Sn)=>{const t=Vr(e);return{linkChapters:t.filter(e=>"couldLink"===e.category&&"Chapter"===e.type).length,linkScenes:t.filter(e=>"couldLink"===e.category&&"Scene"===e.type).length,createChapters:t.filter(e=>"wouldCreateChapters"===e.category).length,createScenes:t.filter(e=>"wouldCreateScenes"===e.category).length,manualReview:Array.isArray(e?.manualReview)?e.manualReview.length:0}})();return(e=>{if(!bt)return;bt.innerHTML="";const t=[`Link ${e.linkChapters+e.linkScenes} chapters/scenes`,`Create ${e.createChapters} chapters`,`Create ${e.createScenes} scenes`],n=document.createElement("ul");for(const e of t){const t=document.createElement("li");t.textContent=e,n.append(t)}bt.append(n)})(e),It?new Promise(e=>{Rn=e,It.showModal?It.showModal():It.hidden=!1}):Promise.resolve(window.confirm(`Apply Structure Sync?\n\nThis will:\n\n- Link ${e.linkChapters+e.linkScenes} chapters/scenes\n- Create ${e.createChapters} chapters\n- Create ${e.createScenes} scenes\n\nManual review items will be skipped.`))},Ja=(e,t,n="")=>{e.result=t,e.error=n},_a=e=>Number.isInteger(e?.wordChapter?.index)&&e.wordChapter.index>0?10*e.wordChapter.index:null,za=e=>Number.isInteger(e?.wordScene?.index)&&e.wordScene.index>0?10*e.wordScene.index:null,Qa=async(e,t)=>{const n=await ec(`/api/word-companion/books/${e}/chapters`,{title:Io(t.wordTitle),sortOrder:_a(t)});if(!n?.chapterId)throw new Error("Chapter creation did not return a chapter ID.");oo(),t.pdChapter={chapterId:n.chapterId,title:n.title||t.wordTitle,sortOrder:n.sortOrder||_a(t)||0,scenes:[]},t.match=`Chapter ${t.pdChapter.chapterId}: ${t.pdChapter.title}`},Xa=async(e,t)=>{const n=t.pdChapter||t.parentItem?.pdChapter;if(!n?.chapterId)throw new Error("Scene parent chapter is not available.");const r=await ec(`/api/word-companion/books/${e}/chapters/${n.chapterId}/scenes`,{sceneTitle:Io(t.wordTitle),sortOrder:za(t)});if(!r?.sceneId)throw new Error("Scene creation did not return a scene ID.");oo(),t.pdChapter=n,t.pdScene={sceneId:r.sceneId,title:t.wordTitle,sortOrder:za(t)||0},t.match=`Scene ${t.pdScene.sceneId}: ${t.pdScene.title}`},Za=async(e,t={})=>{const n=await window.fetch(e,{credentials:"same-origin",...t,headers:{Accept:"application/json",...t.headers||{}}});if(!n.ok)throw new Error(`Request failed: ${n.status}`);return await n.json()},ec=(e,t)=>Za(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),tc=async(e="")=>{An=!1,$n=0,Pn=[],k&&(k.innerHTML=""),Or(S,!0),_o(!1),Yo(""),Or(v,!0),e&&Tr(e);const t=await rc();e&&t&&rn&&Tr(e)},nc=async(e=ln,t=un)=>{const n=Ro(),r=Uo(),o=ra(),a=Number.parseInt(e||"",10),c=Number.parseInt(t||"",10);if(!n||!r||!o||!Number.isInteger(a)||a<=0||!Number.isInteger(c)||c<=0)return;const i=`${n.projectId}:${r.bookId}:${a}`;if(fr!==i){fr=i;try{await ec("/api/word-companion/runtime/current-scene",{documentGuid:o,projectId:n.projectId,bookId:r.bookId,chapterId:c,sceneId:a}),xr("Current scene follow event sent.")}catch(e){fr="",console.debug("Unable to notify PlotDirector of current scene.",e)}}},rc=async()=>{const e=Uo();if(!(e&&Zt&&en&&window.Word&&"function"==typeof window.Word.run))return!1;Tr("Reading manuscript position...");try{oo(),await Promise.all([xc(!0),Nc(!0),Ec(!0)]),await Co(e.bookId);const t=await window.Word.run(async t=>await Ma(t,!!e.usesExplicitScenes));return Na(t),Do({lastScan:"Success",lastError:"-",paragraphs:String(t.paragraphCount),heading1:String(t.heading1Count),heading2:String(t.heading2Count)}),rn?(on?await $c():await Ac(),Qr(!1),zr("Live"),Tr(""),!0):(yo("Not detected"),Qr(!1),zr("Live"),Tr("Manuscript linked. Click inside a chapter to show the current scene."),!0)}catch(e){return console.error("Unable to initialise bound manuscript runtime.",e),vn=null,zr("Manual"),Tr(`Unable to read manuscript position: ${Po(e)}`),Do({lastScan:"Failure",lastError:Po(e)}),!1}},oc=async n=>{Cn=null,Wc(),Mr(),ro(),yo("Not detected"),zr("Manual"),qr("Connected"),Ao(e,null),Ao(t,null),H&&(H.value=""),$o(G,"Select a project first"),zo(),h&&(h.value=""),$o(m,"Select a project first"),Qo(n||"Select a Project and Book to begin."),Tr(n||"Manuscript unlinked."),Hr("No resolved PlotDirector scene."),_o(!0),Br()},ac=e=>e?[...e.querySelectorAll("input[type='checkbox']:checked")].map(e=>Number.parseInt(e.value||"",10)).filter(e=>Number.isInteger(e)&&e>0):[],cc=()=>{const e=Number.parseInt(We?.value||"",10);return Number.isInteger(e)&&e>0?e:null},ic=(e,t,n,r)=>{const o=((e,t)=>la(e).find(e=>da(e.tag,t))||null)(e,r),a=o||e.getRange().insertContentControl();return a.title=t,a.tag=n,a.appearance="Hidden",a},sc=async(e="PlotDirector IDs attached successfully.")=>{if(!ln||!un)return Tr("No resolved PlotDirector scene."),!1;if((dn&&dn!==ln||sn&&sn!==un)&&!window.confirm("This scene is already linked. Replace the existing PlotDirector link?"))return!1;if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return Tr("Word document access is unavailable."),!1;pe&&(pe.disabled=!0,pe.textContent="Attaching...");try{return await window.Word.run(async e=>{const t=await Ua(e);if(!t.currentChapter||!t.currentScene)throw new Error("No scene detected in Word. Use Heading 2 for scenes.");const n=t.bodyParagraphs[t.currentChapter.paragraphIndex],r=t.bodyParagraphs[t.currentScene.paragraphIndex];if(!n||!r)throw new Error("Unable to locate the current Word headings.");ic(n,"PlotDirector Chapter",`PD-CHAPTER-${un}`,"PD-CHAPTER"),ic(r,"PlotDirector Scene",`PD-SCENE-${ln}`,"PD-SCENE"),await e.sync()}),sn=un,dn=ln,mn=!1,hn="SceneID Anchor",pn="Anchor",Tr(e),Do({lastError:"-"}),Rr(),!0}catch(e){return console.error("Unable to attach PlotDirector IDs.",e),Tr("Unable to attach PlotDirector IDs."),Do({lastError:Po(e)}),Rr(),!1}finally{pe&&(pe.textContent=dn===ln?"Attach PlotDirector IDs":"Reattach PlotDirector IDs",Rr())}},dc=async e=>{if(!un)return io("No resolved PlotDirector chapter."),!1;if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return io("Word document access is unavailable."),!1;try{return await window.Word.run(async e=>{const t=await Ua(e);if(!t.currentChapter)throw new Error("No chapter detected in Word. Use Heading 1 for chapters.");const n=t.bodyParagraphs[t.currentChapter.paragraphIndex];if(!n)throw new Error("Unable to locate the current Word chapter heading.");ic(n,"PlotDirector Chapter",`PD-CHAPTER-${un}`,"PD-CHAPTER"),await e.sync()}),sn=un,mn=!1,hn="ChapterID Anchor",pn="Chapter anchor",io(e),Tr(e),Do({lastError:"-"}),Rr(),!0}catch(e){return console.error("Unable to attach PlotDirector chapter ID.",e),io("Unable to attach PlotDirector chapter ID."),Do({lastError:Po(e)}),Rr(),!1}},lc=async e=>{uo(),await $c(),Tr(e),io(e)},uc=e=>{bn&&(bn(e),bn=null),mt?.close?mt.close():mt&&(mt.hidden=!0)},pc=()=>{if(!lt)return;const e=Io(dt?.value||"").toLocaleLowerCase(),t=fn.filter(t=>!e||String(t.title||"").toLocaleLowerCase().includes(e));if(lt.innerHTML="",In=null,ut&&(ut.disabled=!0),0===t.length){const e=document.createElement("p");return e.className="word-companion-empty-list",e.textContent="No chapters found.",void lt.append(e)}for(const e of t){const t=Number.parseInt(e.chapterId||"",10);if(!Number.isInteger(t)||t<=0)continue;const n=document.createElement("label");n.className="word-companion-scene-option";const r=document.createElement("input");r.type="radio",r.name="word-companion-link-chapter",r.value=String(t),r.addEventListener("change",()=>{In=t,ut&&(ut.disabled=!1)});const o=document.createElement("span");o.textContent=e.title||"Untitled chapter",n.append(r,o),lt.append(n)}},hc=()=>{st?.close?st.close():st&&(st.hidden=!0)},mc=async(e,t,n)=>{const r=Uo();if(!r)return co("Select a PlotDirector book first."),!1;await No(r.bookId,e,t,"Manual link","Title Match");return!!await sc(n)&&(co(n),ho(),!0)},yc=e=>{wn&&(wn(e),wn=null),rt?.close?rt.close():rt&&(rt.hidden=!0)},gc=()=>{if(!Ze)return;const e=Io(Xe?.value||"").toLocaleLowerCase(),t=yn.filter(t=>!e||String(t.title||"").toLocaleLowerCase().includes(e));if(Ze.innerHTML="",gn=null,et&&(et.disabled=!0),0===t.length){const e=document.createElement("p");return e.className="word-companion-empty-list",e.textContent="No scenes found.",void Ze.append(e)}for(const e of t){const t=Number.parseInt(e.sceneId||"",10);if(!Number.isInteger(t)||t<=0)continue;const n=document.createElement("label");n.className="word-companion-scene-option";const r=document.createElement("input");r.type="radio",r.name="word-companion-link-scene",r.value=String(t),r.addEventListener("change",()=>{gn=t,et&&(et.disabled=!1)});const o=document.createElement("span");o.textContent=e.title||"Untitled scene",n.append(r,o),Ze.append(n)}},wc=()=>{Qe?.close?Qe.close():Qe&&(Qe.hidden=!0)},fc=()=>{zn=!1,_r(),Qn&&(Qn=!1,bc(1e3))},Ic=async(e={})=>{const t=e.source||"manual",n=Oa();if(!ln)return void Hr("No resolved PlotDirector scene.");if(!un)return void Hr("No resolved PlotDirector chapter.");if(Mn)return void Hr("Refresh Current Scene before syncing.");const r=Uo(),o=ra();if(!r||!o)return void Hr("Bound manuscript metadata is unavailable.");if(zn)return Qn=!0,void xr("Scene sync deferred; sync already running.");Mr(),zn=!0,Qn=!1;let a=e.snapshot||null;try{a=Ba(a)?a:await Ga()}catch(e){return Hr("Sync failed"),Do({lastError:Po(e)}),xr("Scene sync failed."),void fc()}if(!Ba(a))return Hr("Waiting for typing to pause"),xr("Scene sync skipped (stale snapshot)."),void fc();const c=a.wordCount;if(!Number.isInteger(c)||c<0)return Hr("Sync failed"),xr("Scene sync failed."),void fc();if(Xn===a.sceneId&&Zn===c)return Hr("Synced"),xr("Sync skipped (count unchanged)."),void fc();xr("Scene word count changed."),ke&&(ke.disabled=!0,ke.textContent="Syncing..."),Hr("Syncing..."),xr(`Scene sync started (${t}).`);try{const e=Oa(),t=await ec("/api/word-companion/runtime/scene-word-count",{documentGuid:a.documentGuid||o,bookId:a.bookId||r.bookId,chapterId:a.chapterId,sceneId:a.sceneId,wordCount:c});if(Nr(`Word count sync API: ${Math.round(Oa()-e)}ms`),!Ba(a))return void xr("Scene sync result ignored (stale snapshot).");const i=t?.actualWords??c,s=new Date;Xn=a.sceneId,Zn=i,Eo(xe,eo(i)),Eo(fe,eo(i)),Eo(Ne,to(s)),Eo(Ce,to(s)),Hr("Synced"),xr("Scene sync succeeded."),Nr(`Total word count sync: ${Math.round(Oa()-n)}ms`)}catch(e){Hr("Sync failed"),Do({lastError:Po(e)}),xr("Scene sync failed.")}finally{ke&&(ke.textContent="Sync Current Scene"),fc()}},bc=(e=4e3)=>{Mr(),ln&&un&&!Mn&&!zn&&(_n=window.setTimeout(()=>{_n=null,Ic({source:"automatic"})},e))},Sc=()=>{er&&(window.clearTimeout(er),er=null)},Cc=()=>{rr=null,or="",ar=[],cr=null,ir="",Sc()},kc=()=>{sr=null,dr="",lr=[],ur=null,pr=""},vc=()=>{hr=null,mr="",yr=[],gr=null,wr=""},xc=async(e=!1)=>{const t=Ro(),n=ra();if(!t||!n)return ar=[],[];if(!e&&rr===t.projectId&&or===n&&ar.length>0)return ar;const r=await Za(`/api/word-companion/runtime/characters?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(n)}`);return rr=t.projectId,or=n,ar=Array.isArray(r?.characters)?r.characters:[],xr("Character alias cache refreshed."),ar},Nc=async(e=!1)=>{const t=Ro(),n=ra();if(!t||!n)return lr=[],[];if(!e&&sr===t.projectId&&dr===n&&lr.length>0)return lr;const r=await Za(`/api/word-companion/runtime/assets?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(n)}`);return sr=t.projectId,dr=n,lr=Array.isArray(r?.assets)?r.assets:[],xr(`Asset cache loaded: ${lr.length} aliases.`),lr},Ec=async(e=!1)=>{const t=Ro(),n=ra();if(!t||!n)return yr=[],[];if(!e&&hr===t.projectId&&mr===n&&yr.length>0)return yr;const r=await Za(`/api/word-companion/runtime/locations?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(n)}`);return hr=t.projectId,mr=n,yr=Array.isArray(r?.locations)?r.locations:[],xr(`Location cache loaded: ${yr.length} aliases.`),yr},Dc=(e,t)=>{const n=String(t||"").trim();if(!n)return!1;const r=(o=n,String(o||"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).replace(/\s+/g,"\\s+");var o;return new RegExp(`(^|[^\\p{L}\\p{N}_])${r}(?=$|[^\\p{L}\\p{N}_])`,"iu").test(e)},Pc=e=>{if(e?.excludeFromCompanionDetection)return!1;const t=Number.parseInt(e?.detectionPriority||"0",10);return n=e?.matchText,String(n||"").trim().split(/\s+/).filter(Boolean).length>=2||Number.isInteger(t)&&t>=75;var n},Lc=async()=>{if(er=null,!ln||Mn||!ra())return;if(tr)return nr=!0,void xr("Character suggestion detection deferred; detection already running.");tr=!0,nr=!1;const e=Oa();try{const t=await Ga();if(!Ba(t))return void xr("Runtime suggestions skipped (stale snapshot).");const[n,r,o]=await Promise.all([xc(),Nc(),Ec()]);if(!Ba(t))return void xr("Runtime suggestions skipped after cache load (stale snapshot).");const a=t.sceneText||"",c=Oa(),i=((e,t)=>{if(!e||!Array.isArray(t)||0===t.length)return[];const n=new Map;for(const r of t){const t=Number.parseInt(r.characterId||"",10);!Number.isInteger(t)||t<=0||n.has(t)||Dc(e,r.matchText)&&n.set(t,r.name||r.matchText||"Character")}return[...n.entries()].map(([e,t])=>({characterId:e,name:t}))})(a,n);Nr(`Character detection: ${Math.round(Oa()-c)}ms`);const s=i.map(e=>e.characterId),d=s.slice().sort((e,t)=>e-t).join(","),l=Oa(),u=((e,t)=>{if(!e||!Array.isArray(t)||0===t.length)return[];const n=new Map;for(const r of t){const t=Number.parseInt(r.assetId||"",10);!Number.isInteger(t)||t<=0||n.has(t)||Dc(e,r.matchText)&&n.set(t,r.name||r.matchText||"Asset")}return[...n.entries()].map(([e,t])=>({assetId:e,name:t}))})(a,r);Nr(`Asset detection: ${Math.round(Oa()-l)}ms`);const p=u.map(e=>e.assetId),h=p.slice().sort((e,t)=>e-t).join(","),m=Oa(),y=((e,t)=>{if(!e||!Array.isArray(t)||0===t.length)return[];const n=new Map;for(const r of t){const t=Number.parseInt(r.locationId||"",10);!Number.isInteger(t)||t<=0||n.has(t)||!Pc(r)||Dc(e,r.matchText)&&n.set(t,r.name||r.matchText||"Location")}return[...n.entries()].map(([e,t])=>({locationId:e,name:t}))})(a,o);Nr(`Location detection: ${Math.round(Oa()-m)}ms`);const g=y.map(e=>e.locationId),w=g.slice().sort((e,t)=>e-t).join(",");if(!d||cr===ln&&ir===d)xr("Character suggestions skipped.");else{if(!Ba(t))return void xr("Character suggestions skipped before submit (stale snapshot).");const e=Oa(),n=await ec("/api/word-companion/runtime/character-suggestions",{documentGuid:t.documentGuid,sceneId:t.sceneId,characterIds:s});if(Nr(`Character suggestion API: ${Math.round(Oa()-e)}ms`),!Ba(t))return void xr("Character suggestion result ignored (stale snapshot).");if(cr=t.sceneId,ir=d,(n?.createdCount||0)>0){const e=i.slice(0,3).map(e=>e.name).join(", "),t=i.length>3?" +"+(i.length-3):"";Tr(e?`Characters detected: ${e}${t}`:n.message)}xr(n?.message||"Character suggestions submitted.")}if(!h||ur===ln&&pr===h)xr("Asset suggestions skipped.");else{if(!Ba(t))return void xr("Asset suggestions skipped before submit (stale snapshot).");xr(`Asset matches found: ${u.map(e=>e.name).join(", ")}`);const e=Oa(),n=await ec("/api/word-companion/runtime/asset-suggestions",{documentGuid:t.documentGuid,sceneId:t.sceneId,assetIds:p});if(Nr(`Asset suggestion API: ${Math.round(Oa()-e)}ms`),!Ba(t))return void xr("Asset suggestion result ignored (stale snapshot).");if(ur=t.sceneId,pr=h,(n?.createdCount||0)>0){const e=u.slice(0,3).map(e=>e.name).join(", "),t=u.length>3?" +"+(u.length-3):"";Tr(e?`Assets detected: ${e}${t}`:n.message)}xr(n?.message||"Asset suggestions submitted.")}if(!w||gr===ln&&wr===w)xr("Location suggestions skipped.");else{if(!Ba(t))return void xr("Location suggestions skipped before submit (stale snapshot).");xr(`Location matches found: ${y.map(e=>e.name).join(", ")}`);const e=Oa(),n=await ec("/api/word-companion/runtime/location-suggestions",{documentGuid:t.documentGuid,sceneId:t.sceneId,locationIds:g});if(Nr(`Location suggestion API: ${Math.round(Oa()-e)}ms`),!Ba(t))return void xr("Location suggestion result ignored (stale snapshot).");if(gr=t.sceneId,wr=w,(n?.createdCount||0)>0){const e=y.slice(0,3).map(e=>e.name).join(", "),t=y.length>3?" +"+(y.length-3):"";Tr(e?`Locations detected: ${e}${t}`:n.message)}xr(n?.message||"Location suggestions submitted.")}Nr(`Total suggestion pass: ${Math.round(Oa()-e)}ms`)}catch(e){console.error("Unable to detect runtime suggestions.",e),Do({lastError:Po(e)})}finally{tr=!1,nr&&(nr=!1,qc(1500))}},qc=(e=2500)=>{Sc(),!ln||Mn||tr?tr&&(nr=!0):er=window.setTimeout(()=>{Lc()},e)},Ac=async()=>{const e=Ro(),t=Uo(),n=bo(rn,"chapter");if(So({projectId:e?.projectId,projectTitle:e?.title,bookId:t?.bookId,bookTitle:t?jo(t):void 0,detectedChapter:rn,detectedScene:on,requestChapter:n,requestScene:"",result:"Not run",chapterId:null,sceneId:null}),!t)return yo("Not detected"),Tr("Select a PlotDirector book first."),!1;if(!rn)return yo("Not detected"),Tr("No chapter detected in Word. Use Heading 1 for chapters."),!1;yo("Not detected");try{const e=await ko(t.bookId),r=sn?e.find(e=>e.chapterId===sn):null,o=n.toLocaleLowerCase(),a=o?e.find(e=>bo(e.title,"chapter").toLocaleLowerCase()===o):null,c=r||a;return c?(uo(),Xr(null,c.chapterId,r?"Chapter anchor":"Chapter title"),hn=r?"ChapterID Anchor":"Title Match",jr("Chapter linked to PlotDirector"),Tr("No scene detected in Word. Use Heading 2 for scenes."),So({result:"Chapter matched",chapterId:c.chapterId,sceneId:null}),!0):(Xr(null),jr("Chapter not found in PlotDirector."),Tr("Chapter not found in PlotDirector."),So({result:"Chapter not matched",chapterId:null,sceneId:null}),po("Chapter not found in PlotDirector."),!1)}catch(e){return yo("Not found in PlotDirector"),Do({lastError:Po(e)}),Tr("Unable to load PlotDirector chapter data."),!1}},$c=async()=>{const e=Ro(),t=Uo(),n=bo(rn,"chapter"),r=bo(on,"scene");if(So({projectId:e?.projectId,projectTitle:e?.title,bookId:t?.bookId,bookTitle:t?jo(t):void 0,detectedChapter:rn,detectedScene:on,requestChapter:n,requestScene:r,result:"Not run",chapterId:null,sceneId:null}),Wr({anchorType:dn?"SceneID Anchor":sn?"ChapterID Anchor":"Title Match",storedSceneId:dn,storedChapterId:sn,resolutionMethod:"-"}),!t)return yo("Not detected"),Tr("Select a PlotDirector book first."),So({result:"Error"}),!1;if(!rn||!on)return yo("Not detected"),Tr("No scene detected in Word. Use Heading 2 for scenes."),So({result:"Error"}),!1;ce&&(ce.disabled=!0,ce.textContent="Refreshing...");const o=()=>{ce&&(ce.disabled=!1,ce.textContent="Refresh PlotDirector Scene")};yo("Not detected"),Tr("Resolving PlotDirector scene...");try{if(dn){const e=await(async(e,t)=>{const n=await Za(`/api/word-companion/books/${e}/structure`),r=Array.isArray(n?.chapters)?n.chapters:[];for(const e of r){const n=Array.isArray(e.scenes)?e.scenes:[];for(const r of n)if(t(e,r))return{chapter:e,scene:r}}return null})(t.bookId,(e,t)=>t.sceneId===dn);if(e)return So({result:"Matched",chapterId:e.chapter.chapterId,sceneId:e.scene.sceneId}),await No(t.bookId,e.scene.sceneId,e.chapter.chapterId,"Anchor","SceneID Anchor"),!0;mn=!0,hn="SceneID Anchor",jr("Not found in PlotDirector"),Tr("Stored PlotDirector ID is invalid."),So({result:"Error",chapterId:sn,sceneId:dn}),Wr({anchorType:"SceneID Anchor",storedSceneId:dn,storedChapterId:sn,resolutionMethod:"Anchor"})}if(sn){const e=(await Co(t.bookId)).find(e=>e.chapterId===sn),n=e?(Array.isArray(e.scenes)?e.scenes:[]).find(e=>bo(e.title,"scene").toLocaleLowerCase()===r.toLocaleLowerCase()):null;if(n)return So({result:"Matched",chapterId:e.chapterId,sceneId:n.sceneId}),await No(t.bookId,n.sceneId,e.chapterId,"Anchor","ChapterID Anchor"),!0;e&&!dn?(hn="ChapterID Anchor",Xr(null,sn,"Chapter anchor"),So({result:"Chapter matched",chapterId:sn,sceneId:null})):dn||(hn="ChapterID Anchor",So({result:"Chapter anchor not found",chapterId:sn,sceneId:null}))}const e=await ec(`/api/word-companion/books/${t.bookId}/resolve-scene`,{chapterTitle:n,sceneTitle:r});if(!e?.matched||!e.sceneId){const t=mn,n=Number.parseInt(e?.chapterId||"",10),r=Number.isInteger(un)&&un>0?un:null,a=Number.isInteger(n)&&n>0?n:r,c=!!e?.chapterMatched||!!a;return yo("Not found in PlotDirector"),mn=t,c&&(Xr(null,a,a===r?"Chapter anchor":"Chapter title"),jr("Scene not found in PlotDirector.")),Tr(t?"Stored PlotDirector ID is invalid.":c?"Scene not found in PlotDirector.":"This chapter does not exist in PlotDirector. Create or link the chapter first."),So({result:"Not matched",chapterId:c?a:e?.chapterId,sceneId:e?.sceneId}),t||(c?(uo(),mo("Scene not found in PlotDirector.")):(ho(),po("Chapter not found in PlotDirector."))),o(),!1}const a=mn;return So({result:a?"Matched by title fallback":"Matched",chapterId:e.chapterId,sceneId:e.sceneId}),await No(t.bookId,e.sceneId,e.chapterId,a?"Title fallback":"Title","Title Match"),a&&(mn=!0,Tr("Stored PlotDirector ID is invalid."),Rr()),!0}catch(e){const t=mn;return Do({lastError:Po(e)}),yo("Not found in PlotDirector"),mn=t,Tr(t?"Stored PlotDirector ID is invalid.":"Unable to load PlotDirector scene data."),So({result:"Error"}),!1}finally{o()}},Tc=async()=>{if(On=null,Jn)return Gn=!0,void xr("Runtime update deferred; update already running.");if(!Jt||!Uo())return Bn=!1,void xr("Runtime update skipped.");if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return Bn=!1,zr("Manual"),void Tr("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene.");if(!vn)return Bn=!1,zr("Manual"),void Tr("Document structure is not cached yet. Use Refresh Current Scene.");const e=Date.now()-Hn;if(Bn&&e{On&&window.clearTimeout(On),On=window.setTimeout(Tc,Math.max(0,e)),zr("Waiting for typing to pause"),xr("Runtime update scheduled.")},Wc=()=>{On&&(window.clearTimeout(On),On=null),Bn=!1,Gn=!1},Rc=()=>{((e="selection",t=1200)=>{if(Bn=!0,Hn=Date.now(),Fn+=1,Vn=null,Mr(),Sc(),zn&&(Qn=!0),tr&&(nr=!0),xr(`${e} changed.`),Jn)return Gn=!0,void zr("Waiting for typing to pause");jc(t)})("Selection")},Uc=()=>{if(Yn&&window.Office?.context?.document&&"function"==typeof window.Office.context.document.removeHandlerAsync&&window.Office?.EventType?.DocumentSelectionChanged)try{window.Office.context.document.removeHandlerAsync(window.Office.EventType.DocumentSelectionChanged,{handler:Rc}),Yn=!1}catch(e){console.warn("Unable to remove Word selection tracking handler.",e)}},Mc=async(e,n)=>{if(vn=null,oo(),!e)return Xt=[],$o(G,"Select a project first"),$o(m,"Select a project first"),$r(""),Ao(t,null),oa(),yo("Not detected"),void Fr();$o(G,"Loading books..."),$r(""),yo("Not detected"),Fr("Select a book to analyse manuscript structure.");try{const r=await Za(`/api/word-companion/projects/${e}/books`);if(Xt=Array.isArray(r.books)?r.books:[],0===Xt.length)return $o(G,"No books available."),$o(m,"No books available."),$r("No books available."),Ao(t,null),oa(),void Qo("No books available.");To(G,"Choose a book",Xt.map(e=>({...e,displayTitle:jo(e)})),"bookId","displayTitle");const o=Xt.find(e=>e.bookId===n);o&&G?(G.value=String(o.bookId),Ao(t,o.bookId)):Ao(t,null),((e=null)=>{if(!m)return;if(0===Xt.length)return void $o(m,"No books available.");To(m,"Choose a book",Xt.map(e=>({...e,displayTitle:jo(e)})),"bookId","displayTitle");const t=e||Number.parseInt(G?.value||"",10),n=Xt.find(e=>e.bookId===t);n&&(m.value=String(n.bookId)),Jo()})(o?.bookId||n),oa(),yo((Uo(),"Not detected")),Fr(Uo()?"Ready to analyse manuscript structure.":"Select a book to analyse manuscript structure."),Qo(Oo()?"Ready to scan manuscript structure.":"Select a Book to begin.")}catch{Xt=[],$o(G,"Unable to load books."),$o(m,"Unable to load books."),$r("Unable to load books."),Ao(t,null),oa(),yo("Not detected"),Fr("Unable to load books."),Qo("Unable to load books.")}};for(const e of i)e.addEventListener("click",()=>Pr(e.dataset.tabButton));Lr(),H?.addEventListener("change",async()=>{const n=Number.parseInt(H.value||"",10);ro(),h&&Number.isInteger(n)&&n>0&&(h.value=String(n)),Ao(e,Number.isInteger(n)&&n>0?n:null),Ao(t,null),yo("Not detected"),Fr(),Qo("Select a Book to begin."),await Mc(n,null),Vo()}),G?.addEventListener("change",()=>{const e=Number.parseInt(G.value||"",10);ro(),Ao(t,Number.isInteger(e)&&e>0?e:null),m&&Number.isInteger(e)&&e>0&&(m.value=String(e)),oa(),yo("Not detected"),Fr(Uo()?"Ready to analyse manuscript structure.":"Select a book to analyse manuscript structure."),Qo(Oo()?"Ready to scan manuscript structure.":"Select a Book to begin."),Vo()}),h?.addEventListener("change",async()=>{const n=Number.parseInt(h.value||"",10);ro(),H&&Number.isInteger(n)&&n>0&&(H.value=String(n)),Ao(e,Number.isInteger(n)&&n>0?n:null),Ao(t,null),Qo("Select a Book to begin."),await Mc(n,null),Vo()}),m?.addEventListener("change",()=>{const e=Number.parseInt(m.value||"",10);ro(),G&&Number.isInteger(e)&&e>0&&(G.value=String(e)),Ao(t,Number.isInteger(e)&&e>0?e:null),oa(),Qo(Oo()?"Ready to scan manuscript structure.":"Select a Book to begin."),Vo()}),y?.addEventListener("input",Jo),g?.addEventListener("click",async()=>{const e=Mo(),t=String(y?.value||"").trim();if(e&&t){g.disabled=!0,Yo("Creating Book...");try{const n=await ec(`/api/word-companion/projects/${e.projectId}/books`,{title:t});y&&(y.value=""),H&&(H.value=String(e.projectId)),await Mc(e.projectId,n.bookId),m&&(m.value=String(n.bookId)),Yo("Ready to scan manuscript structure.")}catch(e){console.error("Unable to create Book from Word Companion.",e),Yo(`Unable to create Book: ${Po(e)}`)}finally{Jo()}}else Jo()}),w?.addEventListener("click",async()=>{const e=Mo(),t=Oo();if(e&&t)if(Zt&&en&&window.Word&&"function"==typeof window.Word.run){w&&(w.disabled=!0,w.textContent="Scanning..."),Yo("Scanning manuscript structure...");try{const n=await window.Word.run(async e=>{const n=e.document.body.paragraphs;return n.load("text,styleBuiltIn,style"),await e.sync(),((e,t)=>{const n=[];let r=null,o=null,a=0;const c=[],i=new Set(["***","###"]),s=()=>{if(!r)return null;const e={title:`Scene ${r.scenes.length+1}`,sortOrder:10*(r.scenes.length+1),wordCount:0};return r.scenes.push(e),e};return e.forEach(e=>{const d=String(e.text||"").trim();if(!d)return;if(c.push(d),ia(e,1))return r={title:d,sortOrder:10*(n.length+1),wordCount:0,scenes:[]},n.push(r),o=s(),void(a+=sa(d));if(!r||!o)return;if(t&&i.has(d))return void(o=s());const l=sa(d);r.wordCount+=l,o.wordCount+=l,a+=l}),{chapters:n,chapterCount:n.length,sceneCount:n.reduce((e,t)=>e+t.scenes.length,0),wordCount:a,documentText:c.join("\n")}})(n.items,!!t.usesExplicitScenes)});Dn=n,qn=!1;const r=await ec("/api/word-companion/manuscript/analyse",{projectId:e.projectId,bookId:t.bookId,documentGuid:na(),chapterCount:n.chapterCount,sceneCount:n.sceneCount,wordCount:n.wordCount});En=r,(e=>{if(!b)return;b.innerHTML="";const t=document.createElement("strong");t.textContent="Manuscript Analysis",b.append(t);const n=document.createElement("dl"),r=[["Project",e.projectTitle],["Book",jo({title:e.bookTitle,subtitle:e.bookSubtitle})],["Chapters",Number(e.chapterCount||0).toLocaleString()],["Scenes",Number(e.sceneCount||0).toLocaleString()],["Words",Number(e.wordCount||0).toLocaleString()]];for(const[e,t]of r){const r=document.createElement("dt");r.textContent=e;const o=document.createElement("dd");o.textContent=t||"-",n.append(r,o)}b.append(n),Or(b,!1),Or(D,!1),Yo(e.message||"Preview generated."),Jo()})(r);try{const t=await ec("/api/word-companion/manuscript/discover-characters",{projectId:e.projectId,documentText:n.documentText||""});Pn=Array.isArray(t?.candidates)?t.candidates:[],Or(S,!0),Or(v,!0)}catch(e){console.error("Unable to discover character candidates.",e),Pn=[],Or(S,!0),Or(v,!0)}Do({lastScan:"Success",lastError:"-",paragraphs:"-",heading1:String(n.chapterCount),heading2:"-"})}catch(e){console.error("Unable to scan manuscript for first-run import.",e),Qo(`Unable to scan manuscript: ${Po(e)}`),Do({lastScan:"Failure",lastError:Po(e)})}finally{w&&(w.textContent="Scan Manuscript"),Jo()}}else Qo("Word document access is unavailable.");else Qo("Select a Project and Book to begin.")}),P?.addEventListener("click",async()=>{const n=Mo(),o=Oo();if(!(n&&o&&Dn&&En?.canImport))return void Jo();let a=!1;if(!En.requiresReplaceConfirmation||(a=await new Promise(e=>{if(!q||"function"!=typeof q.showModal)return void e(window.confirm("This Book already contains planned chapters and scenes.\n\nImporting this manuscript will replace the planned structure.\n\nContinue?"));const t=t=>{A?.removeEventListener("click",n),$?.removeEventListener("click",r),q?.removeEventListener("cancel",o),q?.close(),e(t)},n=()=>t(!0),r=()=>t(!1),o=e=>{e.preventDefault(),t(!1)};A?.addEventListener("click",n),$?.addEventListener("click",r),q?.addEventListener("cancel",o),q.showModal()}),a)){Tn=!0,Jo(),Yo("Importing manuscript structure...");try{const i=na(),s=await ec("/api/word-companion/manuscript/import",{projectId:n.projectId,bookId:o.bookId,documentGuid:i,bindingVersion:1,replacePlanned:a,chapters:Dn.chapters});if(!s.imported)return En={...En,canImport:!s.requiresReplaceConfirmation,requiresReplaceConfirmation:!!s.requiresReplaceConfirmation,message:s.message||En.message},Yo(s.message||"Import was not completed."),void Jo();const d=s.manuscriptDocument;await(c={documentGuid:d?.documentGuid||i,bookId:s.bookId,projectId:s.projectId,bindingVersion:d?.bindingVersion||1},new Promise((e,t)=>{const n=window.Office?.context?.document?.settings;n?(n.set(r.documentGuid,c.documentGuid),n.set(r.bookId,String(c.bookId)),n.set(r.projectId,String(c.projectId)),n.set(r.bindingVersion,String(c.bindingVersion||1)),n.saveAsync(n=>{n.status===window.Office.AsyncResultStatus.Succeeded?e():t(n.error||new Error("Unable to save Word document metadata."))})):t(new Error("Word document settings are unavailable."))})),Cn={documentGuid:d?.documentGuid||i,bookId:s.bookId,projectId:s.projectId,bindingVersion:d?.bindingVersion||1},Ao(e,s.projectId),Ao(t,s.bookId),H&&(H.value=String(s.projectId));const l=Pn;await Mc(s.projectId,s.bookId),qr("Connected"),qn=!0,An=l.length>0,$n=An?Date.now():0,Pn=l,l.length>0?(_o(!0),Zo(l),Or(D,!0),Or(v,!1),Yo(s.message||"Manuscript structure imported."),Tr(s.message||"Manuscript structure imported."),window.setTimeout(Jo,750)):await tc(s.message||"Manuscript structure imported.")}catch(e){console.error("Unable to import manuscript structure.",e),Yo(`Unable to import manuscript: ${Po(e)}`)}finally{Tn=!1,Jo()}var c}else Yo("Import cancelled.")}),x?.addEventListener("click",async()=>{const e=Mo()||Ro(),t=Xo();if(!An||Date.now()-$n<750||!e||0===t.length)Jo();else{jn=!0,Jo(),Eo(C,"Creating selected characters...");try{const n=await ec("/api/word-companion/manuscript/create-discovered-characters",{projectId:e.projectId,candidateNames:t});Tr(n.message||"Characters created."),Eo(C,`${n.createdCount||0} created, ${n.skippedCount||0} already existed.`),await tc(n.message||"Characters created.")}catch(e){console.error("Unable to create discovered characters.",e),Eo(C,`Unable to create characters: ${Po(e)}`)}finally{jn=!1,Jo()}}}),N?.addEventListener("click",()=>tc("Character creation skipped.")),E?.addEventListener("click",()=>tc()),f?.addEventListener("click",()=>_o(!1)),L?.addEventListener("click",()=>Qo("Import cancelled.")),J?.addEventListener("click",Fa),ne?.addEventListener("click",Va),re?.addEventListener("click",async()=>{const e=Uo();if(!e||0===Vr().length||Wn)return void Kr();if(!await Ya())return;Wn=!0,Yr(),Gr("Applying structure sync...");const t={createdChapters:0,createdScenes:0,linkedChapters:0,linkedScenes:0,skipped:Array.isArray(Sn?.manualReview)?Sn.manualReview.length:0,failed:0},n=Vr().filter(e=>"couldLink"===e.category&&"Chapter"===e.type),r=Vr().filter(e=>"wouldCreateChapters"===e.category),o=Vr().filter(e=>"couldLink"===e.category&&"Scene"===e.type),a=Vr().filter(e=>"wouldCreateScenes"===e.category),c=[];let i="";const s=(e,t)=>{c.push({item:e,counterName:t})},d=async(e,n)=>{try{await(async e=>{if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)throw new Error("Word document access is unavailable.");await window.Word.run(async t=>{const n=await Ua(t),r="Chapter"===e.type?e.wordChapter:e.wordScene,o=Number.isInteger(r?.paragraphIndex)?n.bodyParagraphs[r.paragraphIndex]:null;if(!o)throw new Error("Unable to locate the Word heading.");if("Chapter"===e.type){const t=e.pdChapter?.chapterId;if(!t)throw new Error("Chapter ID is not available.");ic(o,"PlotDirector Chapter",`PD-CHAPTER-${t}`,"PD-CHAPTER")}else{const t=e.pdScene?.sceneId;if(!t)throw new Error("Scene ID is not available.");ic(o,"PlotDirector Scene",`PD-SCENE-${t}`,"PD-SCENE")}await t.sync()})})(e),Ja(e,"Success"),t[n]+=1}catch(n){Ja(e,"Failed",Po(n)),t.failed+=1}};try{for(const e of n)s(e,"linkedChapters");for(const n of r)try{await Qa(e.bookId,n),s(n,"createdChapters")}catch(e){Ja(n,"Failed",Po(e)),t.failed+=1}for(const e of o)s(e,"linkedScenes");for(const n of a)try{await Xa(e.bookId,n),s(n,"createdScenes")}catch(e){Ja(n,"Failed",Po(e)),t.failed+=1}for(const e of c)await d(e.item,e.counterName);for(const e of Sn.manualReview||[])Ja(e,"Skipped");Ra(Sn),i=(e=>`Structure Sync Complete. Created: ${e.createdChapters} Chapters, ${e.createdScenes} Scenes. Linked: ${e.linkedChapters} Chapters, ${e.linkedScenes} Scenes. Skipped: ${e.skipped} Manual Review items. Failed: ${e.failed} Items.`)(t),Gr(i)}finally{Wn=!1,Yr(),i&&(oo(),await Va(),Gr(`${i} Preview refreshed.`))}}),St?.addEventListener("click",()=>Ka(!0)),Ct?.addEventListener("click",()=>Ka(!1)),It?.addEventListener("cancel",e=>{e.preventDefault(),Ka(!1)}),ce?.addEventListener("click",$c),Y?.addEventListener("click",async()=>{if(!Uo())return Tr("Select a PlotDirector book first."),void yo("Not detected");Wc(),Zr(!0),zr("Updating...");try{await Promise.all([xc(!0),Nc(!0),Ec(!0)]);if(!await Fa())return;if(Qr(!1),!on)return await Ac(),void zr("Current scene updated");await $c(),zr("Current scene updated")}finally{Zr(!1)}}),ke?.addEventListener("click",()=>Ic({source:"manual"})),Re?.addEventListener("click",async()=>{if(ln)if(!Mn&&await(async()=>{if(!ln)return!1;if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return Qr(!0,"The Word cursor is now in a different scene. Refresh Current Scene before saving."),!1;try{const e=await window.Word.run(async e=>await Ua(e)),t=e.currentChapter?.title||"",n=e.currentScene?.title||"",r=e.currentScene?.anchorId||null,o=e.currentChapter?.anchorId||null,a=Number.isInteger(r)&&r===ln,c=Number.isInteger(o)&&o===un,i=bo(n,"scene").toLocaleLowerCase()===bo(Ir||on,"scene").toLocaleLowerCase(),s=bo(t,"chapter").toLocaleLowerCase()===bo(br||rn,"chapter").toLocaleLowerCase();return a||i&&(c||s)?(Qr(!1),!0):(Qr(!0,"The Word cursor is now in a different scene. Refresh Current Scene before saving."),!1)}catch(e){return console.error("Unable to verify current Word scene before saving.",e),Do({lastError:Po(e)}),Qr(!0,"The Word cursor is now in a different scene. Refresh Current Scene before saving."),!1}})()){Re&&(Re.disabled=!0,Re.textContent="Saving...");try{await(e=`/api/word-companion/scenes/${ln}/links`,t={characterIds:ac(Te),assetIds:ac(je),locationIds:[],primaryLocationId:cc()},Za(e,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})),(()=>{const e=new Set(ac(Te)),t=new Set(ac(je));vr={characters:vr.characters.map(t=>({...t,linked:e.has(Number.parseInt(go(t,"character"),10))})),assets:vr.assets.map(e=>({...e,linked:t.has(Number.parseInt(go(e,"asset"),10))})),locations:vr.locations,primaryLocationId:cc()}})(),ao("Scene links saved successfully."),Do({lastError:"-"})}catch(e){ao("Unable to save scene links."),Do({lastError:Po(e)})}finally{Re&&(Re.disabled=!ln||Mn,Re.textContent="Save Scene Links")}var e,t}else ao("The Word cursor is now in a different scene. Refresh Current Scene before saving.");else ao("Link a PlotDirector scene first.")}),pe?.addEventListener("click",async()=>{await sc()}),he?.addEventListener("click",async()=>{console.debug("Word Companion unlink clicked"),Tr("Preparing to unlink manuscript...");const e=(()=>{const e=Cn||ea();if(e)return e;const t=kn?.manuscriptDocument?.documentGuid||"",n=Number.parseInt(kn?.bookId||"",10),r=Number.parseInt(kn?.projectId||"",10);return t&&Number.isInteger(n)&&n>0&&Number.isInteger(r)&&r>0?{documentGuid:t,bookId:n,projectId:r,bindingVersion:Number.parseInt(kn?.manuscriptDocument?.bindingVersion||"",10)||1}:null})();if(!e)return void Tr("This Word document is not linked.");if(!e.documentGuid||!Number.isInteger(e.bookId)||e.bookId<=0)return void Tr("Unable to unlink: bound document metadata is incomplete.");if(await new Promise(e=>{if(!T||"function"!=typeof T.showModal)return void e(window.confirm("This will remove PlotDirector binding metadata from this Word document.\n\nIf PlotDirector is reachable, it will also unlink this Book in PlotDirector.\n\nYour manuscript text will not be changed.\n\nContinue?"));const t=t=>{j?.removeEventListener("click",n),W?.removeEventListener("click",r),T?.removeEventListener("cancel",o),T?.close(),e(t)},n=()=>t(!0),r=()=>t(!1),o=e=>{e.preventDefault(),t(!1)};j?.addEventListener("click",n),W?.addEventListener("click",r),T?.addEventListener("cancel",o),T.showModal()})){Tr("Unlinking manuscript..."),he&&(he.disabled=!0,he.textContent="Unlinking...");try{await ec("/api/word-companion/manuscript/unlink",{documentGuid:e.documentGuid,bookId:e.bookId}),await ta(),await oc("Manuscript unlinked.")}catch(e){console.error("Unable to unlink manuscript binding.",e),Tr("Unable to unlink from PlotDirector.");if(window.confirm("PlotDirector could not be updated, but you can still remove the binding from this Word document.\n\nYou may need to unlink the Book manually in PlotDirector later.\n\nRemove binding from this Word document only?"))try{await ta(),await oc("Word metadata removed. Unlink the Book manually in PlotDirector if it still shows as linked.")}catch(e){console.error("Unable to remove Word document metadata.",e),Tr(`Unable to remove Word metadata: ${Po(e)}`)}else Tr(`Unable to unlink manuscript: ${Po(e)}`)}finally{he&&(he.disabled=!1,he.textContent="Unlink this Word document"),Br()}}else Tr("Unlink cancelled.")}),He?.addEventListener("click",async()=>{const e=Uo();if(!e||!rn||un)return void po("Chapter not found in PlotDirector.");const t=Io(rn),n=await((e,t)=>(Eo(yt,`"${e}"`),Eo(gt,`"${t}"`),mt?new Promise(e=>{bn=e,mt.showModal?mt.showModal():mt.hidden=!1}):Promise.resolve(window.confirm(`Create PlotDirector chapter:\n\n"${e}"\n\nin book:\n\n"${t}"\n\n?`))))(t,jo(e)||"selected book");if(n){He&&(He.disabled=!0,He.textContent="Creating...");try{const n=Number.isInteger(cn)&&cn>0?10*cn:null,r=await ec(`/api/word-companion/books/${e.bookId}/chapters`,{title:t,sortOrder:n});if(!r?.chapterId)throw new Error("Chapter creation did not return a chapter ID.");oo(),Xr(null,r.chapterId,"Manual chapter link");if(!await dc("Chapter created and linked successfully."))return;await lc("Chapter created and linked successfully.")}catch(e){io("Unable to create chapter."),Do({lastError:Po(e)})}finally{He&&(He.disabled=!so(),He.textContent="Create Chapter in PlotDirector")}}}),Ge?.addEventListener("click",async()=>{const e=Uo();if(e&&rn&&!un){Ge&&(Ge.disabled=!0,Ge.textContent="Loading...");try{fn=await ko(e.bookId),In=null,dt&&(dt.value=""),Eo(ht,""),pc(),st?.showModal?st.showModal():st&&(st.hidden=!1)}catch(e){io("Unable to load chapters."),Do({lastError:Po(e)})}finally{Ge&&(Ge.disabled=!so(),Ge.textContent="Link to Existing Chapter")}}else po("Chapter not found in PlotDirector.")}),dt?.addEventListener("input",pc),ut?.addEventListener("click",async()=>{const e=fn.find(e=>e.chapterId===In);if(e){ut&&(ut.disabled=!0,ut.textContent="Linking...");try{Xr(null,e.chapterId,"Manual chapter link");if(!await dc("Chapter linked successfully."))return;hc(),await lc("Chapter linked successfully.")}catch(e){Eo(ht,"Unable to link chapter."),io("Unable to link chapter."),Do({lastError:Po(e)})}finally{ut&&(ut.disabled=!In,ut.textContent="Link Chapter")}}else Eo(ht,"Select a chapter.")}),pt?.addEventListener("click",hc),wt?.addEventListener("click",()=>uc(!0)),ft?.addEventListener("click",()=>uc(!1)),mt?.addEventListener("cancel",e=>{e.preventDefault(),uc(!1)}),Je?.addEventListener("click",async()=>{const e=Uo();if(!e||!on)return void mo("Scene not found in PlotDirector.");const t=bo(on,"scene"),n=bo(rn,"chapter"),r=await((e,t)=>(Eo(ot,`"${e}"`),Eo(at,`"${t}"`),rt?new Promise(e=>{wn=e,rt.showModal?rt.showModal():rt.hidden=!1}):Promise.resolve(window.confirm(`Create PlotDirector scene:\n\n"${e}"\n\nwithin chapter:\n\n"${t}"\n\n?`))))(t,n);if(r){Je&&(Je.disabled=!0,Je.textContent="Creating...");try{const n=await xo();if(!n)return void mo("This chapter does not exist in PlotDirector. Create or link the chapter first.");const r=await ec(`/api/word-companion/books/${e.bookId}/chapters/${n.chapterId}/scenes`,{sceneTitle:t});if(!r?.sceneId||!r?.chapterId)throw new Error("Scene creation did not return a scene ID.");oo(),await mc(r.sceneId,r.chapterId,"Scene created and linked successfully.")}catch(e){co("Unable to create scene."),Do({lastError:Po(e)})}finally{Je&&(Je.disabled=!lo(),Je.textContent="Create Scene in PlotDirector")}}}),_e?.addEventListener("click",async()=>{const e=Uo();if(e&&on){_e&&(_e.disabled=!0,_e.textContent="Loading...");try{const t=await Co(e.bookId),n=vo(t);if(!n)return void mo("This chapter does not exist in PlotDirector. Create or link the chapter first.");yn=Array.isArray(n.scenes)?n.scenes:[],gn=null,Xe&&(Xe.value=""),Eo(nt,""),gc(),Qe?.showModal?Qe.showModal():Qe&&(Qe.hidden=!1)}catch(e){co("Unable to load scenes."),Do({lastError:Po(e)})}finally{_e&&(_e.disabled=!lo(),_e.textContent="Link to Existing Scene")}}else mo("Scene not found in PlotDirector.")}),Xe?.addEventListener("input",gc),et?.addEventListener("click",async()=>{const e=Uo(),t=yn.find(e=>e.sceneId===gn);if(e&&t){et&&(et.disabled=!0,et.textContent="Linking...");try{const e=await xo();if(!e)throw new Error("This chapter does not exist in PlotDirector. Create or link the chapter first.");wc(),await mc(t.sceneId,e.chapterId,"Scene linked successfully.")}catch(e){Eo(nt,"Unable to link scene."),co("Unable to link scene."),Do({lastError:Po(e)})}finally{et&&(et.disabled=!gn,et.textContent="Link Scene")}}else Eo(nt,"Select a scene.")}),tt?.addEventListener("click",wc),ct?.addEventListener("click",()=>yc(!0)),it?.addEventListener("click",()=>yc(!1)),rt?.addEventListener("cancel",e=>{e.preventDefault(),yc(!1)}),Tt?.addEventListener("click",async()=>{Tt&&(Tt.disabled=!0,Tt.textContent="Running...");try{if(await Lo(),!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return void Do({lastScan:"Failure",lastError:"Word document access is unavailable.",paragraphs:"-",heading1:"-",heading2:"-"});const e=await window.Word.run(async e=>{const t=e.document.body.paragraphs;return t.load("items"),await e.sync(),t.items.length});Do({lastScan:"Success",lastError:"-",paragraphs:String(e)})}catch(e){console.error("Word Companion diagnostics failed.",e);const t=Po(e);Do({lastScan:"Failure",lastError:t}),Tr(`Unable to scan the Word document: ${t}`)}finally{Tt&&(Tt.disabled=!1,Tt.textContent="Run Diagnostics")}});const Oc=Jt?(async()=>{$o(H,"Loading projects..."),$o(G,"Select a project first"),$o(h,"Loading projects..."),$o(m,"Select a project first"),Ar(""),$r("");try{const n=await Za("/api/word-companion/projects");if(Qt=Array.isArray(n.projects)?n.projects:[],0===Qt.length)return $o(H,"No projects available."),$o(h,"No projects available."),Ar("No projects available."),Ao(e,null),Ao(t,null),oa(),void Qo("No projects available.");To(H,"Choose a project",Qt,"projectId","title"),zo();const r=qo(e),o=Qt.find(e=>e.projectId===r);o&&H?(H.value=String(o.projectId),h&&(h.value=String(o.projectId)),Ao(e,o.projectId),await Mc(o.projectId,qo(t))):(Ao(e,null),Ao(t,null),oa(),Qo("Select a Project and Book to begin."))}catch{Qt=[],Xt=[],$o(H,"Unable to connect to PlotDirector."),$o(G,"Select a project first"),$o(h,"Unable to connect to PlotDirector."),$o(m,"Select a project first"),qr("Unable to connect to PlotDirector."),Ar("Unable to connect to PlotDirector."),Ao(e,null),Ao(t,null),oa(),Qo("Unable to connect to PlotDirector.")}})():Promise.resolve();if(Jt||($o(H,"Please sign in to PlotDirector."),$o(G,"Please sign in to PlotDirector."),$o(h,"Please sign in to PlotDirector."),$o(m,"Please sign in to PlotDirector."),oa()),!window.Office||"function"!=typeof window.Office.onReady)return Er("Word Companion ready"),Tr("Word document access is unavailable."),zr("Manual"),Do({host:"Unavailable",platform:"Unavailable",ready:"No",wordApi:"No"}),void Ko().catch(e=>{console.debug("Word Companion presence unavailable.",e)});Lo().then(async()=>{Er("Word Companion ready"),await Oc,await(async()=>{if(!Jt||!en)return void _o(!1);const n=ea();if(n)try{const r=await Za(`/api/word-companion/runtime/book/${n.bookId}?documentGuid=${encodeURIComponent(n.documentGuid)}`),o=r?.manuscriptDocument;if(r?.bookId===n.bookId&&r?.projectId===n.projectId&&o&&String(o.documentGuid).toLowerCase()===n.documentGuid.toLowerCase())return Cn=n,no(r),Ao(e,r.projectId),Ao(t,r.bookId),H&&(H.value=String(r.projectId)),await Mc(r.projectId,r.bookId),_o(!1),qr("Connected"),$r("Bound manuscript detected."),void await rc()}catch{}Cn=null,ro(),zo(),Mo()?await Mc(Mo().projectId,null):$o(m,"Select a project first"),Qo("Select a Project and Book to begin."),_o(!0)})(),await Ko(),en?(()=>{if(Jt){if(!window.Office?.context?.document||"function"!=typeof window.Office.context.document.addHandlerAsync||!window.Office?.EventType?.DocumentSelectionChanged)return zr("Manual"),void Tr("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene.");try{window.Office.context.document.addHandlerAsync(window.Office.EventType.DocumentSelectionChanged,Rc,e=>{e?.status===window.Office.AsyncResultStatus.Succeeded?(Yn=!0,zr("Live")):(zr("Manual"),Tr("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene."))}),window.addEventListener("beforeunload",Uc)}catch(e){console.warn("Unable to register Word selection tracking handler.",e),zr("Manual"),Tr("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene.")}}})():(Tr("Word document access is unavailable."),zr("Manual"))}).catch(e=>{console.error("Office readiness check failed.",e),qr("Unable to connect to PlotDirector."),Er("Word Companion unavailable"),Tr("Word document access is unavailable."),Do({ready:"No",wordApi:"No",lastScan:"Failure"})})})(); \ No newline at end of file +(()=>{const s={projectId:"plotdirector.word.projectId",bookId:"plotdirector.word.bookId",activeTab:"plotdirector.word.activeTab"},bi={documentGuid:"plotdirector.word.documentGuid",bookId:"plotdirector.word.boundBookId",projectId:"plotdirector.word.boundProjectId",bindingVersion:"plotdirector.word.bindingVersion"},pl=new Set(["scene","review","maintenance","diagnostics"]),th=document.querySelector(".word-companion-shell"),wp=document.querySelector(".word-companion-tabs"),ih=[...document.querySelectorAll("[data-tab-button]")],wl=[...document.querySelectorAll("[data-tab-panel]")],bp=document.querySelector("[data-linked-manuscript-card]"),kp=document.querySelector("[data-linked-project-title]"),dp=document.querySelector("[data-linked-book-title]"),gp=document.querySelector("[data-first-run-wizard]"),et=document.querySelector("[data-first-run-project-select]"),g=document.querySelector("[data-first-run-book-select]"),ne=document.querySelector("[data-first-run-new-book-title]"),eo=document.querySelector("[data-first-run-create-book]"),sr=document.querySelector("[data-first-run-scan]"),nw=document.querySelector("[data-first-run-cancel]"),tw=document.querySelector("[data-first-run-status]"),hr=document.querySelector("[data-first-run-summary]"),bu=document.querySelector("[data-first-run-character-section]"),ku=document.querySelector("[data-first-run-character-status]"),ki=document.querySelector("[data-first-run-character-candidates]"),du=document.querySelector("[data-first-run-character-actions]"),oo=document.querySelector("[data-first-run-create-characters]"),bl=document.querySelector("[data-first-run-skip-characters]"),kl=document.querySelector("[data-first-run-continue]"),rh=document.querySelector("[data-first-run-import-actions]"),uh=document.querySelector("[data-first-run-import]"),iw=document.querySelector("[data-first-run-import-cancel]"),gu=document.querySelector("[data-first-run-replace-dialog]"),dl=document.querySelector("[data-first-run-replace-confirm]"),gl=document.querySelector("[data-first-run-replace-cancel]"),nf=document.querySelector("[data-unlink-word-document-dialog]"),na=document.querySelector("[data-unlink-word-document-confirm]"),ta=document.querySelector("[data-unlink-word-document-cancel]"),ia=document.querySelector("[data-office-status]"),ra=document.querySelector("[data-connection-status]"),ua=document.querySelector("[data-summary-connection]"),fa=document.querySelector("[data-summary-project]"),ea=document.querySelector("[data-summary-book]"),p=document.querySelector("[data-project-select]"),ot=document.querySelector("[data-book-select]"),rw=document.querySelector("[data-runtime-book-selectors]"),oa=document.querySelector("[data-project-message]"),sa=document.querySelector("[data-book-message]"),so=document.querySelector("[data-refresh-current-scene]"),nu=document.querySelector("[data-refresh-document]"),ha=document.querySelector("[data-current-chapter]"),ca=document.querySelector("[data-current-scene]"),la=document.querySelector("[data-current-word-count]"),aa=document.querySelector("[data-scene-tracking-status]"),va=document.querySelector("[data-document-message]"),ya=document.querySelector("[data-sync-note]"),te=document.querySelector("[data-document-outline]"),cr=document.querySelector("[data-analyse-manuscript-structure]"),fh=document.querySelector("[data-apply-structure-sync]"),uw=document.querySelector("[data-structure-preview-status]"),tf=document.querySelector("[data-structure-preview-results]"),tu=document.querySelector("[data-refresh-plotdirector-scene]"),pa=document.querySelector("[data-plotdirector-scene-status]"),fw=document.querySelector("[data-anchor-status]"),ew=document.querySelector("[data-anchor-book-id]"),ow=document.querySelector("[data-anchor-chapter-id]"),sw=document.querySelector("[data-anchor-scene-id]"),ai=document.querySelector("[data-attach-plotdirector-ids]"),lr=document.querySelector("[data-unlink-word-document]"),wa=document.querySelector("[data-plotdirector-scene-details]"),ba=document.querySelector("[data-plotdirector-scene-title]"),ka=document.querySelector("[data-plotdirector-revision-status]"),da=document.querySelector("[data-plotdirector-estimated-words]"),eh=document.querySelector("[data-plotdirector-actual-words]"),ga=document.querySelector("[data-plotdirector-blocked-status]"),oh=document.querySelector("[data-plotdirector-blocked-reason-row]"),sh=document.querySelector("[data-plotdirector-blocked-reason]"),nv=document.querySelector("[data-runtime-last-sync]"),ar=document.querySelector("[data-sync-scene-progress]"),hw=document.querySelector("[data-sync-word-count]"),hh=document.querySelector("[data-sync-plotdirector-count]"),tv=document.querySelector("[data-sync-last-synced]"),cw=document.querySelector("[data-sync-status]"),iv=document.querySelector("[data-writing-brief-block]"),rv=document.querySelector("[data-writing-brief]"),uv=document.querySelector("[data-plotdirector-link-groups]"),lw=document.querySelector("[data-character-review-summary]"),aw=document.querySelector("[data-asset-review-summary]"),vw=document.querySelector("[data-location-review-summary]"),ie=document.querySelector("[data-character-chips]"),re=document.querySelector("[data-asset-chips]"),btt=document.querySelector("[data-location-chips]"),vr=document.querySelector("[data-primary-location-select]"),di=document.querySelector("[data-save-scene-links]"),yw=document.querySelector("[data-scene-links-status]"),fv=document.querySelector("[data-links-editing-scene]"),ev=document.querySelector("[data-chapter-create-link]"),pw=document.querySelector("[data-chapter-create-link-chapter]"),yt=document.querySelector("[data-create-plotdirector-chapter]"),pt=document.querySelector("[data-link-existing-chapter]"),ww=document.querySelector("[data-chapter-create-link-status]"),ov=document.querySelector("[data-scene-create-link]"),bw=document.querySelector("[data-create-link-chapter]"),kw=document.querySelector("[data-create-link-scene]"),wt=document.querySelector("[data-create-plotdirector-scene]"),bt=document.querySelector("[data-link-existing-scene]"),dw=document.querySelector("[data-create-link-status]"),yr=document.querySelector("[data-link-existing-dialog]"),ho=document.querySelector("[data-link-scene-search]"),co=document.querySelector("[data-link-scene-options]"),oi=document.querySelector("[data-link-selected-scene]"),gw=document.querySelector("[data-link-dialog-cancel]"),ch=document.querySelector("[data-link-dialog-status]"),gi=document.querySelector("[data-create-scene-dialog]"),nb=document.querySelector("[data-create-dialog-scene]"),tb=document.querySelector("[data-create-dialog-chapter]"),ib=document.querySelector("[data-create-dialog-confirm]"),rb=document.querySelector("[data-create-dialog-cancel]"),pr=document.querySelector("[data-link-existing-chapter-dialog]"),lo=document.querySelector("[data-link-chapter-search]"),ao=document.querySelector("[data-link-chapter-options]"),si=document.querySelector("[data-link-selected-chapter]"),ub=document.querySelector("[data-link-chapter-dialog-cancel]"),lh=document.querySelector("[data-link-chapter-dialog-status]"),nr=document.querySelector("[data-create-chapter-dialog]"),fb=document.querySelector("[data-create-chapter-dialog-chapter]"),eb=document.querySelector("[data-create-chapter-dialog-book]"),ob=document.querySelector("[data-create-chapter-dialog-confirm]"),sb=document.querySelector("[data-create-chapter-dialog-cancel]"),tr=document.querySelector("[data-apply-structure-sync-dialog]"),ah=document.querySelector("[data-apply-structure-sync-summary]"),hb=document.querySelector("[data-apply-structure-sync-confirm]"),cb=document.querySelector("[data-apply-structure-sync-cancel]"),lb=document.querySelector("[data-resolve-project-id]"),ab=document.querySelector("[data-resolve-project-title]"),vb=document.querySelector("[data-resolve-book-id]"),yb=document.querySelector("[data-resolve-book-title]"),pb=document.querySelector("[data-resolve-detected-chapter]"),wb=document.querySelector("[data-resolve-detected-scene]"),bb=document.querySelector("[data-resolve-request-chapter]"),kb=document.querySelector("[data-resolve-request-scene]"),db=document.querySelector("[data-resolve-result]"),gb=document.querySelector("[data-resolve-chapter-id]"),nk=document.querySelector("[data-resolve-scene-id]"),iu=document.querySelector("[data-run-diagnostics]"),tk=document.querySelector("[data-diagnostic-host]"),ik=document.querySelector("[data-diagnostic-platform]"),rk=document.querySelector("[data-diagnostic-ready]"),uk=document.querySelector("[data-diagnostic-word-api]"),fk=document.querySelector("[data-diagnostic-last-scan]"),ek=document.querySelector("[data-diagnostic-last-error]"),ok=document.querySelector("[data-diagnostic-anchor-type]"),sk=document.querySelector("[data-diagnostic-stored-scene-id]"),hk=document.querySelector("[data-diagnostic-stored-chapter-id]"),ck=document.querySelector("[data-diagnostic-resolution-method]"),lk=document.querySelector("[data-diagnostic-paragraphs]"),ak=document.querySelector("[data-diagnostic-heading1]"),vk=document.querySelector("[data-diagnostic-heading2]"),rf=th?.dataset.authenticated==="true",vh=Number.parseInt(th?.dataset.userId||"",10),yk=String(th?.dataset.companionVersion||"20C").trim();let ir=[],ii=[],kt=!1,nt=!1,vo="Unknown",yo="Unknown",w="",rt="",sv=null,ue=null,b=null,tt=null,u=null,o=null,fe="",rr="Title Match",dt=!1,yh=[],ee=null,po=null,ph=[],oe=null,wo=null,vi=null,ri=null,ur=null,l=null,se=null,bo=null,wr=null,he=null,hi=[],ko=null,go=!1,uf=!1,ce=0,le=!1,wh=!1,ae=!1,ns=null,bh="Manual",ht=!1,ru=null,br=!1,hv=0,ff=!1,ve=0,ye=null;const pe=1200;let kh=!1,ts=!1,we=null,ef=!1,be=!1,dh=null,gh=null,ke=null,sf=!1,hf=!1,nc=null,tc="",cf=[],ic=null,rc="",uc=null,fc="",uu=[],ec=null,oc="",sc=null,hc="",fu=[],cc=null,lc="",ac="",is="",vc="",cv=0,d=null,eu=null,ct={characters:[],assets:[],locations:[],primaryLocationId:null};const i=n=>{const t=Date.now();t-cv<1e3||(cv=t,console.debug(`[Word Companion Runtime] ${n}`))},ci=n=>{console.debug(`[Word Companion Timing] ${n}`)},yc=n=>{ia&&(ia.textContent=n)},pk=n=>{try{return window.localStorage.getItem(n)||""}catch{return""}},wk=(n,t)=>{try{t?window.localStorage.setItem(n,String(t)):window.localStorage.removeItem(n)}catch{}},lv=n=>n==="links"?"review":n==="structure"?"maintenance":n,av=(n,t=true)=>{const i=lv(n),r=pl.has(i)&&ih.some(n=>n.dataset.tabButton===i)?i:"scene";for(const n of ih){const t=n.dataset.tabButton===r;n.classList.toggle("is-active",t);n.setAttribute("aria-selected",t?"true":"false")}for(const n of wl){const t=n.dataset.tabPanel===r;n.classList.toggle("is-active",t);n.hidden=!t}t&&wk(s.activeTab,r)},vv=()=>{const n=lv(pk(s.activeTab));av(pl.has(n)?n:"scene",!1)},de=n=>{ra&&(ra.textContent=n),ua&&(ua.textContent=n)},pc=n=>{oa&&(oa.textContent=n)},lf=n=>{sa&&(sa.textContent=n)},t=n=>{va&&(va.textContent=n)},af=n=>{pa&&(pa.textContent=n)},wc=t=>{const i=n=>Object.prototype.hasOwnProperty.call(t,n);i("anchorType")&&n(ok,a(t.anchorType));i("storedSceneId")&&n(sk,a(t.storedSceneId));i("storedChapterId")&&n(hk,a(t.storedChapterId));i("resolutionMethod")&&n(ck,a(t.resolutionMethod))},fr=()=>{const i=h(),f=Number.isInteger(u)&&u>0&&tt===u,e=!Number.isInteger(o)||o<=0||b===o,t=f&&e;n(fw,t?"Attached to PlotDirector":"Not attached");n(ew,i?.bookId?String(i.bookId):"-");n(ow,Number.isInteger(o)&&o>0?String(o):"-");n(sw,Number.isInteger(u)&&u>0?String(u):"-");ai&&(r(ai,t&&!dt),ai.disabled=!u||t&&!dt,ai.textContent=tt&&!t?"Reattach PlotDirector IDs":"Attach PlotDirector IDs");wc({anchorType:rr||"Title Match",storedSceneId:tt,storedChapterId:b,resolutionMethod:fe})},vf=(t,i,r,u=null,f=null,e=null)=>{const o=t||"",s=i||"",h=Number.isInteger(r)?r:null,c=Number.isInteger(e)&&e>0?e:null,l=Number.isInteger(u)&&u>0?u:null,a=Number.isInteger(f)&&f>0?f:null,v=w===o&&rt===s&&sv===h&&ue===c&&b===l&&tt===a;(w=t||"",rt=i||"",sv=Number.isInteger(r)?r:null,ue=Number.isInteger(e)&&e>0?e:null,b=Number.isInteger(u)&&u>0?u:null,tt=Number.isInteger(f)&&f>0?f:null,v)||(ha&&(ha.textContent=t||"Not detected"),ca&&(ca.textContent=i||"Not detected"),la&&(la.textContent=Number.isInteger(r)?String(r):"-"),n(hw,Number.isInteger(r)?String(r):"-"),fr())},yf=()=>{we&&(window.clearTimeout(we),we=null)},r=(n,t)=>{n&&(n.hidden=t)},bc=()=>{r(lr,!ri)},gt=t=>{n(cw,t)},er=t=>{n(uw,t)},pf=(n="Select a project and book to analyse manuscript structure.")=>{if(vi=null,tf){tf.innerHTML="";const n=document.createElement("p");n.textContent="No preview generated.";tf.append(n)}er(n);rs()},bk=(n=vi)=>yy.flatMap(t=>Array.isArray(n?.[t.key])?n[t.key]:[]),ou=(n=vi)=>bk(n).filter(n=>n.category==="couldLink"||n.category==="wouldCreateChapters"||n.category==="wouldCreateScenes"),rs=()=>{fh&&(fh.disabled=ae||ou().length===0)},us=()=>{cr&&(cr.disabled=ae||!ni()||!h()),rs()},kc=()=>{fv&&(fv.textContent=u?`Editing links for: ${is||rt||"Current scene"}`:"Link a PlotDirector scene first.")},dc=()=>{const n=ht||!u,t=n||!o||ef;ar&&(ar.disabled=t);di&&(di.disabled=n);vr&&(vr.disabled=n);for(const t of[ie,re])for(const i of t?[...t.querySelectorAll("input[type='checkbox']")]:[])i.disabled=n},k=t=>{(t==="Live"||t==="Manual")&&(bh=t),n(aa,t||bh||"Manual")},li=(i,r="")=>{ht=!!i,ht&&(yf(),uo()),n(aa,ht?"Stale":bh),dc(),r&&(t(r),hu(r))},kr=(n,t=null,i="")=>{u=Number.isInteger(n)&&n>0?n:null,o=Number.isInteger(t)&&t>0?t:null,fe=i||"",u!==dh&&(gh=null),u||(yf(),uo()),dc(),gt(ht?"Refresh Current Scene before syncing.":u?"Ready to sync.":"No resolved PlotDirector scene."),hu(ht?"Refresh Current Scene before saving.":u?"Ready to save.":"Link a PlotDirector scene first."),kc(),fr()},yv=n=>{so&&(so.disabled=n,so.textContent=n?"Refreshing...":"Refresh Current Scene")},a=n=>n===null||n===undefined||n===""?"-":String(n),ktt=n=>{if(!n)return"-";const t=new Date(n);return Number.isNaN(t.getTime())?"-":t.toLocaleString()},fs=n=>{if(!n)return"-";const t=n instanceof Date?n:new Date(n);if(Number.isNaN(t.getTime()))return"-";const i=new Date,r=t.toDateString()===i.toDateString()?"Today":t.toLocaleDateString();return`Last synced: ${r} ${t.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`},pv=t=>{ur=t||null;const i=ur?.lastSyncUtc||ur?.manuscriptDocument?.lastSyncUtc;n(nv,fs(i));n(tv,fs(i));tl()},wf=()=>{l=null,se=null,bo=null,ve+=1,ye=null,gn(),ntt(),ttt(),pv(null)},su=()=>{se=null,bo=null},hu=t=>{n(yw,t)},cu=t=>{n(dw,t)},yi=t=>{n(ww,t)},gc=()=>!!h()&&!!w&&!o,nl=()=>!!h()&&!!rt&&Number.isInteger(o)&&o>0,ge=()=>{r(ev,!0),yi(""),yt&&(yt.disabled=!0,yt.textContent="Create Chapter in PlotDirector"),pt&&(pt.disabled=!0,pt.textContent="Link to Existing Chapter")},es=(t="")=>{n(pw,a(w));r(ev,!1);yi(t);const i=gc();yt&&(yt.disabled=!i,yt.textContent="Create Chapter in PlotDirector");pt&&(pt.disabled=!i,pt.textContent="Link to Existing Chapter")},os=()=>{r(ov,!0),cu(""),wt&&(wt.disabled=!0,wt.textContent="Create Scene in PlotDirector"),bt&&(bt.disabled=!0,bt.textContent="Link to Existing Scene")},no=(t="")=>{n(bw,a(w));n(kw,a(rt));r(ov,!1);cu(t);const i=nl();wt&&(wt.disabled=!i,wt.textContent="Create Scene in PlotDirector");bt&&(bt.disabled=!i,bt.textContent="Link to Existing Scene")},it=t=>{af(t),r(wa,!0),r(iv,!0),r(uv,!1),ge(),os(),fe="",is="",vc="",rr=tt?"SceneID Anchor":b?"ChapterID Anchor":"Title Match",dt=!1,ct={characters:[],assets:[],locations:[],primaryLocationId:null},hs(ie,[],"character"),hs(re,[],"asset"),wv([],null,!1),ey(),kr(null),n(hh,"-"),n(ba,"-"),n(ka,"-"),n(da,"-"),n(eh,"-"),n(ga,"-"),n(sh,"-"),r(oh,!0),fr(),kc()},ss=(n,t)=>t==="asset"?n?.assetId||n?.AssetId||n?.storyAssetId||n?.StoryAssetId||0:t==="location"?n?.locationId||n?.LocationId||0:n?.characterId||n?.CharacterId||0,hs=(n,t,i,r=false)=>{if(n){n.innerHTML="";const u=Array.isArray(t)?t:[];if(u.length===0){const t=document.createElement("p");t.className="word-companion-empty-list";t.textContent="None";n.append(t);return}for(const t of u){const f=Number.parseInt(ss(t,i),10);if(Number.isInteger(f)&&!(f<=0)){const e=document.createElement("label");e.className="word-companion-check-item";const u=document.createElement("input");u.type="checkbox";u.value=String(f);u.checked=!!t.linked;u.disabled=!r;const o=document.createElement("span");o.textContent=t.name||"Unnamed";e.append(u,o);n.append(e)}}}},wv=(n,t,i=false)=>{if(vr){vr.innerHTML="";const r=document.createElement("option");r.value="";r.textContent="None";vr.append(r);const u=Number.parseInt(t||"",10);for(const t of Array.isArray(n)?n:[]){const n=Number.parseInt(ss(t,"location"),10);if(Number.isInteger(n)&&!(n<=0)){const i=document.createElement("option");i.value=String(n);i.textContent=t.name||"Unnamed";i.selected=n===u;vr.append(i)}}vr.disabled=!i}},kk=t=>{is=t?.sceneTitle||rt||"",vc=t?.chapterTitle||w||"",n(ba,a(t?.sceneTitle)),n(ka,a(t?.revisionStatus||t?.revisionStatusName||"Not provided")),n(da,a(t?.estimatedWords)),n(eh,a(t?.actualWords)),n(hh,a(t?.actualWords)),n(ga,t?.blocked?"Blocked":"Not blocked"),t?.blockedReason?(n(sh,t.blockedReason),r(oh,!1)):(n(sh,"-"),r(oh,!0)),rv&&(rv.textContent=t?.writingBrief||"No writing brief has been added for this scene."),ct={characters:Array.isArray(t?.characters)?t.characters:[],assets:Array.isArray(t?.assets)?t.assets:[],locations:Array.isArray(t?.locations)?t.locations:[],primaryLocationId:t?.primaryLocationId??null},hs(ie,ct.characters,"character",!!u&&!ht),hs(re,ct.assets,"asset",!!u&&!ht),wv(ct.locations,ct.primaryLocationId,!!u&&!ht),ey(),kc(),hu(ht?"Refresh Current Scene before saving.":u?"Ready to save.":"No resolved PlotDirector scene."),r(wa,!1),r(iv,!1),r(uv,!1)},lu=n=>String(n||"").trim().replace(/\s+/g," "),ut=(n,t)=>{const i=lu(n);if(!i)return"";const r=t==="scene"?"scene":"chapter",u=new RegExp(`^${r}\\s+${"(?:\\d+|[ivxlcdm]+|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)"}\\s*(?::|-|\\u2013|\\u2014)\\s*`,"i"),f=lu(i.replace(u,""));return f||i},vt=t=>{const i=n=>Object.prototype.hasOwnProperty.call(t,n);i("projectId")&&n(lb,a(t.projectId));i("projectTitle")&&n(ab,a(t.projectTitle));i("bookId")&&n(vb,a(t.bookId));i("bookTitle")&&n(yb,a(t.bookTitle));i("detectedChapter")&&n(pb,a(t.detectedChapter));i("detectedScene")&&n(wb,a(t.detectedScene));i("requestChapter")&&n(bb,a(t.requestChapter));i("requestScene")&&n(kb,a(t.requestScene));i("result")&&n(db,a(t.result));i("chapterId")&&n(gb,a(t.chapterId));i("sceneId")&&n(nk,a(t.sceneId))},dk=async(n,t)=>{const i=await at(`/api/word-companion/books/${n}/structure`),r=Array.isArray(i?.chapters)?i.chapters:[];for(const n of r){const r=Array.isArray(n.scenes)?n.scenes:[],i=r.find(n=>n.sceneId===t);if(i)return i.revisionStatus||""}return""},gk=async(n,t)=>{const i=await at(`/api/word-companion/books/${n}/structure`),r=Array.isArray(i?.chapters)?i.chapters:[];for(const n of r){const i=Array.isArray(n.scenes)?n.scenes:[];for(const r of i)if(t(n,r))return{chapter:n,scene:r}}return null},cs=async n=>{if(bo===n&&Array.isArray(se?.chapters))return se.chapters;const t=await at(`/api/word-companion/books/${n}/structure`);return bo=n,se=t||null,Array.isArray(t?.chapters)?t.chapters:[]},bv=async n=>{const t=await at(`/api/word-companion/books/${n}/chapters`);return Array.isArray(t?.chapters)?t.chapters:[]},kv=n=>{if(Number.isInteger(o)&&o>0){const t=n.find(n=>n.chapterId===o);if(t)return t}const t=ut(w,"chapter").toLocaleLowerCase();return t?n.find(n=>ut(n.title,"chapter").toLocaleLowerCase()===t)||null:null},dv=async()=>{const n=h();return n?kv(await cs(n.bookId)):null},ls=async(n,i,r,u,f)=>{const e=await at(`/api/word-companion/scenes/${i}/companion`);try{e.revisionStatus=await dk(n,i)}catch{e.revisionStatus=""}rr=f||"Title Match";dt=!1;li(!1);af("Linked to PlotDirector");t("");ge();os();kr(i,r,u);kk(e);rn(i,r);gs();nh()},n=(n,t)=>{if(n){const i=t===null||t===undefined?"":String(t);n.textContent!==i&&(n.textContent=i)}},f=t=>{const i=n=>Object.prototype.hasOwnProperty.call(t,n);i("host")&&n(tk,t.host);i("platform")&&n(ik,t.platform);i("ready")&&n(rk,t.ready);i("wordApi")&&n(uk,t.wordApi);i("lastScan")&&n(fk,t.lastScan);i("lastError")&&n(ek,t.lastError);i("paragraphs")&&n(lk,t.paragraphs);i("heading1")&&n(ak,t.heading1);i("heading2")&&n(vk,t.heading2)},e=n=>{const t=[];return n?.name&&t.push(n.name),n?.code&&n.code!==n.name&&t.push(n.code),n?.message&&t.push(n.message),n?.debugInfo?.errorLocation&&t.push(`Location: ${n.debugInfo.errorLocation}`),t.length>0?t.join(": "):String(n)},gv=async()=>{if(!window.Office||typeof window.Office.onReady!="function")return kt=!1,nt=!1,vo="Unavailable",yo="Unavailable",f({host:vo,platform:yo,ready:"No",wordApi:"No"}),null;const n=await window.Office.onReady();return kt=!0,vo=n?.host||"Unknown",yo=n?.platform||"Unknown",nt=!!window.Word&&!!window.Office.HostType&&n?.host===window.Office.HostType.Word,f({host:vo,platform:yo,ready:"Yes",wordApi:nt?"Yes":"No"}),n},as=n=>{try{const i=window.localStorage.getItem(n),t=Number.parseInt(i||"",10);return Number.isInteger(t)&&t>0?t:null}catch{return null}},v=(n,t)=>{try{t?window.localStorage.setItem(n,String(t)):window.localStorage.removeItem(n)}catch{}},c=(n,t)=>{if(n){n.innerHTML="";const i=document.createElement("option");i.value="";i.textContent=t;n.append(i);n.disabled=!0}},vs=(n,t,i,r,u)=>{if(n){n.innerHTML="";const f=document.createElement("option");f.value="";f.textContent=t;n.append(f);for(const t of i){const i=document.createElement("option");i.value=String(t[r]);i.textContent=t[u];n.append(i)}n.disabled=i.length===0}},dr=n=>{const t=String(n?.displayTitle||"").trim();if(t)return t;const i=String(n?.title||"").trim(),r=String(n?.subtitle||"").trim();return r?`${i}: ${r}`:i},nd=()=>ur?.projectTitle||ni()?.title||"Project",td=()=>ur?.bookTitle||dr(h())||"Book",tl=()=>{const t=!!ri;r(bp,!t);r(rw,t);t&&(n(kp,nd()),n(dp,td()))},ni=()=>{const n=Number.parseInt(p?.value||"",10);return ir.find(t=>t.projectId===n)||null},h=()=>{const n=Number.parseInt(ot?.value||"",10);return ii.find(t=>t.bookId===n)||null},gr=()=>{const n=Number.parseInt(et?.value||"",10);return ir.find(t=>t.projectId===n)||null},au=()=>{const n=Number.parseInt(g?.value||"",10);return ii.find(t=>t.bookId===n)||null},id=()=>{const n=ri?.projectId||gr()?.projectId||ni()?.projectId||as(s.projectId);return Number.isInteger(n)&&n>0?n:null},rd=()=>{const n=ri?.bookId||au()?.bookId||h()?.bookId||as(s.bookId);return Number.isInteger(n)&&n>0?n:null},ny=()=>{const n=String(window.Office?.context?.document?.url||"").trim();if(!n)return"";const t=n.split(/[\\/]/).filter(Boolean).pop()||"";try{return decodeURIComponent(t)}catch{return t}},ty=()=>({userID:Number.isInteger(vh)&&vh>0?vh:null,companionVersion:yk,machineName:"",documentOpen:!!nt,currentDocumentName:ny(),linkedProjectID:id(),linkedBookID:rd()}),ud=async()=>{d&&d.state===signalR.HubConnectionState.Connected&&await d.invoke("CompanionHeartbeat",ty())},bf=()=>{ud().catch(n=>{console.debug("Word Companion heartbeat unavailable.",n)})},iy=async()=>{if(rf&&window.signalR&&!d){d=(new signalR.HubConnectionBuilder).withUrl("/hubs/word-companion-follow").withAutomaticReconnect().build();d.onreconnected(bf);d.on("ScanCurrentDocument",n=>{void bd(n)});d.on("UpdateOnboardingBuildMarkers",n=>{void kd(n)});d.onclose(()=>{eu&&(window.clearInterval(eu),eu=null)});await d.start();await d.invoke("RegisterCompanion",ty());eu=window.setInterval(bf,25e3)}};window.addEventListener("beforeunload",()=>{eu&&(window.clearInterval(eu),eu=null),d&&d.stop().catch(()=>{})});const ui=t=>n(tw,t),ft=()=>{const n=gr(),t=au(),i=uf&&Date.now()-ce>=750;sr&&(sr.disabled=!n||!t||le);eo&&(eo.disabled=!n||!String(ne?.value||"").trim()||le);uh&&(uh.disabled=!wr?.canImport||!he||le);oo&&(oo.disabled=!i||wh||ry().length===0)},vu=n=>{r(gp,!n);r(wp,n);bc();tl();for(const t of wl)r(t,n?!0:t.dataset.tabPanel!=="scene"&&!t.classList.contains("is-active"));n||vv()},il=()=>{if(et){if(ir.length===0){c(et,"No projects available.");return}vs(et,"Choose a project",ir,"projectId","title");const n=Number.parseInt(p?.value||"",10);Number.isInteger(n)&&n>0&&(et.value=String(n))}},fd=(n=null)=>{if(g){if(ii.length===0){c(g,"No books available.");return}vs(g,"Choose a book",ii.map(n=>({...n,displayTitle:dr(n)})),"bookId","displayTitle");const i=n||Number.parseInt(ot?.value||"",10),t=ii.find(n=>n.bookId===i);t&&(g.value=String(t.bookId));ft()}},lt=(t="Select a Project and Book to begin.")=>{wr=null,he=null,hi=[],go=!1,uf=!1,ce=0,r(hr,!0),r(rh,!0),r(bu,!0),r(du,!0),hr&&(hr.innerHTML=""),ki&&(ki.innerHTML=""),n(ku,""),ui(t),ft()},ry=()=>ki?[...ki.querySelectorAll("input[type='checkbox']:checked")].map(n=>String(n.value||"").trim()).filter(Boolean):[],ed=t=>{if(hi=Array.isArray(t)?t:[],bu&&ki){if(ki.innerHTML="",r(bu,!1),r(du,!go||hi.length===0),r(oo,!1),r(bl,!1),r(kl,!0),hi.length===0){n(ku,"No likely major characters found.");ft();return}n(ku,`${hi.length} likely major characters found.`);for(const n of hi){const i=document.createElement("label");i.className="word-companion-character-candidate";const t=document.createElement("input");t.type="checkbox";t.value=n.text||"";t.checked=String(n.confidence||"").trim().toLowerCase()==="high";t.addEventListener("change",ft);const u=document.createElement("strong");u.textContent=n.text||"";const f=document.createElement("em"),e=String(n.reason||"").trim();f.textContent=e?`${Number(n.mentionCount||0).toLocaleString()} mentions · ${e}`:`${Number(n.mentionCount||0).toLocaleString()} mentions`;const r=document.createElement("span");r.className="word-companion-character-candidate-text";r.append(u,f);i.append(t,r);ki.append(i)}ft()}},od=n=>{if(hr){hr.innerHTML="";const t=document.createElement("strong");t.textContent="Manuscript Analysis";hr.append(t);const i=document.createElement("dl"),u=[["Project",n.projectTitle],["Book",dr({title:n.bookTitle,subtitle:n.bookSubtitle})],["Chapters",Number(n.chapterCount||0).toLocaleString()],["Scenes",Number(n.sceneCount||0).toLocaleString()],["Words",Number(n.wordCount||0).toLocaleString()]];for(const[n,t]of u){const r=document.createElement("dt");r.textContent=n;const u=document.createElement("dd");u.textContent=t||"-";i.append(r,u)}hr.append(i);r(hr,!1);r(rh,!1);ui(n.message||"Preview generated.");ft()}},rl=()=>{const n=window.Office?.context?.document?.settings;if(!n)return null;const u=String(n.get(bi.documentGuid)||"").trim(),t=Number.parseInt(n.get(bi.bookId)||"",10),i=Number.parseInt(n.get(bi.projectId)||"",10),r=Number.parseInt(n.get(bi.bindingVersion)||"",10);return!u||!Number.isInteger(t)||t<=0||!Number.isInteger(i)||i<=0?null:{documentGuid:u,bookId:t,projectId:i,bindingVersion:Number.isInteger(r)&&r>0?r:1}},sd=()=>{const i=ri||rl();if(i)return i;const r=ur?.manuscriptDocument?.documentGuid||"",n=Number.parseInt(ur?.bookId||"",10),t=Number.parseInt(ur?.projectId||"",10);return r&&Number.isInteger(n)&&n>0&&Number.isInteger(t)&&t>0?{documentGuid:r,bookId:n,projectId:t,bindingVersion:Number.parseInt(ur?.manuscriptDocument?.bindingVersion||"",10)||1}:null},hd=n=>new Promise((t,i)=>{const r=window.Office?.context?.document?.settings;if(!r){i(new Error("Word document settings are unavailable."));return}r.set(bi.documentGuid,n.documentGuid);r.set(bi.bookId,String(n.bookId));r.set(bi.projectId,String(n.projectId));r.set(bi.bindingVersion,String(n.bindingVersion||1));r.saveAsync(n=>{n.status===window.Office.AsyncResultStatus.Succeeded?t():i(n.error||new Error("Unable to save Word document metadata."))})}),uy=()=>new Promise((n,t)=>{const i=window.Office?.context?.document?.settings;if(!i){t(new Error("Word document settings are unavailable."));return}for(const n of Object.values(bi))typeof i.remove=="function"?i.remove(n):i.set(n,"");i.saveAsync(i=>{i.status===window.Office.AsyncResultStatus.Succeeded?n():t(i.error||new Error("Unable to remove Word document metadata."))})}),fy=()=>ri?.documentGuid?ri.documentGuid:window.crypto?.randomUUID?window.crypto.randomUUID():"10000000-1000-4000-8000-100000000000".replace(/[018]/g,n=>(Number(n)^Math.random()*16>>Number(n)/4).toString(16)),or=()=>ri?.documentGuid||rl()?.documentGuid||"",pi=()=>{const t=ni(),n=h();fa&&(fa.textContent=t?.title||"(Not connected)");ea&&(ea.textContent=n?dr(n):"(Not connected)");ya&&(ya.textContent=n?"":"Select a PlotDirector book before refreshing.");us();tl()},ey=()=>{const t=Array.isArray(ct.characters)?ct.characters.length:0,i=Array.isArray(ct.assets)?ct.assets.length:0,r=Array.isArray(ct.locations)?ct.locations.length:0;n(lw,`Characters (${t})`);n(aw,`Assets (${i})`);n(vw,`Locations (${r})`)},to=n=>{const t=n?.styleBuiltIn||n?.style||"";return String(t).replace(/\s+/g,"").toLowerCase()},wi=(n,t)=>{const i=to(n);return i===`heading${t}`||i.endsWith(`.heading${t}`)||i.includes(`heading${t}`)},kf=n=>{const t=String(n||"").trim().split(/\s+/).filter(Boolean);return t.length},cd=(n,t)=>String(n?.text||"").trim()===String(t?.text||"").trim()&&to(n)===to(t),oy=(n,t)=>{const r=String(n||"").match(new RegExp(`^${t}-(\\d+)$`,"i"));if(!r)return null;const i=Number.parseInt(r[1],10);return Number.isInteger(i)&&i>0?i:null},sy=n=>{try{return Array.isArray(n?.contentControls?.items)?n.contentControls.items:[]}catch(t){return console.warn("Word paragraph content controls were not available.",t),[]}},ys=(n,t)=>{const i=sy(n);for(const n of i){const i=oy(n.tag,t);if(i)return i}return null},ld=(n,t)=>{const u=[];let o=0,s=0,i=null,r=null;n.forEach((n,t)=>{const f=String(n.text||"").trim();if(f){if(wi(n,1)){o+=1;i={index:u.length+1,paragraphIndex:t,title:f,anchorId:ys(n,"PD-CHAPTER"),scenes:[]};u.push(i);r=null;return}if(wi(n,2)){if(s+=1,!i)return;r={index:i.scenes.length+1,paragraphIndex:t,title:f,anchorId:ys(n,"PD-SCENE"),wordCount:0};i.scenes.push(r);return}r&&(r.wordCount+=kf(f))}});const h=t.find(n=>String(n.text||"").trim()),f=h?n.findIndex(n=>cd(n,h)):-1,e=f>=0?u.filter(n=>n.paragraphIndex<=f).at(-1)||null:null,c=e&&f>=0?e.scenes.filter(n=>n.paragraphIndex<=f).at(-1)||null:null;return{chapters:u,currentChapter:e,currentScene:c,paragraphCount:n.length,heading1Count:o,heading2Count:s}},ad=(n,t)=>{const r=[];let i=null,u=null,f=0;const e=[],s=new Set(["***","###"]),o=()=>{if(!i)return null;const n={title:`Scene ${i.scenes.length+1}`,sortOrder:(i.scenes.length+1)*10,wordCount:0};return i.scenes.push(n),n};return n.forEach(n=>{const h=String(n.text||"").trim();if(h){if(e.push(h),wi(n,1)){i={title:h,sortOrder:(r.length+1)*10,wordCount:0,scenes:[]};r.push(i);u=o();f+=kf(h);return}if(i&&u){if(t&&s.has(h)){u=o();return}const c=kf(h);i.wordCount+=c;u.wordCount+=c;f+=c}}}),{chapters:r,chapterCount:r.length,sceneCount:r.reduce((n,t)=>n+t.scenes.length,0),wordCount:f,documentText:e.join("\n")}},ps=new Set(["***","###"]),vd=(n,t)=>{const i=String(n?.text||"").trim();return{index:t,text:i,styleBuiltIn:n?.styleBuiltIn||"",style:n?.style||"",wordCount:kf(i),chapterAnchorId:null,sceneAnchorId:null}},hy=(n,t)=>{n&&(n.endParagraphIndex=t)},cy=(n,t)=>{n&&(n.endParagraphIndex=t,hy(n.scenes.at(-1),t))},yd=(n,t)=>{const u=n.map(vd),f=[];let o=0,s=0,i=null,r=null,e=0;const h=(n,t=null,u=null)=>i?(hy(r,n.index-1),r={index:i.scenes.length+1,paragraphIndex:n.index,startParagraphIndex:n.index,endParagraphIndex:n.index,title:t||`Scene ${i.scenes.length+1}`,anchorId:u,wordCount:0},i.scenes.push(r),r):null;for(const n of u)if(n.text){if(wi(n,1)){cy(i,n.index-1);o+=1;i={index:f.length+1,paragraphIndex:n.index,startParagraphIndex:n.index,endParagraphIndex:n.index,title:n.text,anchorId:n.chapterAnchorId,wordCount:0,scenes:[]};f.push(i);r=null;h(n,t?"Scene 1":"Scene 1",n.sceneAnchorId);e+=n.wordCount;continue}if(i){if(t&&ps.has(n.text)){s+=1;h(n);continue}i.wordCount+=n.wordCount;r&&(r.wordCount+=n.wordCount);e+=n.wordCount}}return cy(i,u.length-1),{paragraphs:u,chapters:f,usesExplicitScenes:!!t,paragraphCount:u.length,heading1Count:o,heading2Count:s,documentWordCount:e,currentParagraphIndex:null,currentChapter:null,currentScene:null}},yu=(n,t,i)=>n?.[t]??n?.[i]??null,pd=n=>(Array.isArray(n)?n:[]).map((n,t)=>({temporaryCharacterKey:`character-${t+1}`,name:yu(n,"text","Text")||yu(n,"name","Name")||"",mentionCount:Number.parseInt(yu(n,"mentionCount","MentionCount")||"0",10)||0,qualityScore:Number.parseInt(yu(n,"qualityScore","QualityScore")||"0",10)||0,category:yu(n,"category","Category")||"PossibleCharacter",reason:yu(n,"reason","Reason")||"",isExistingCharacterMatch:!!yu(n,"isExistingCharacterMatch","IsExistingCharacterMatch"),suggestedImportance:null})).filter(n=>n.name),wd=(n,t)=>{const u=[],f=[],s=[];let i=null,r=null,e=0,h=0;const c=()=>{r=null},l=(n,t,r=false)=>(c(),i={temporaryChapterKey:`chapter-${u.length+1}`,chapterNumber:u.length+1,title:t,wordCount:0,chapterTextParagraphs:[],chapterText:"",startPosition:n?.index??null,existingChapterID:r?null:ys(n,"PD-CHAPTER")},u.push(i),o(n,null),i),o=(n,t=null)=>i?(c(),r={temporarySceneKey:`scene-${f.length+1}`,temporaryChapterKey:i.temporaryChapterKey,sceneNumberWithinChapter:f.filter(n=>n.temporaryChapterKey===i.temporaryChapterKey).length+1,title:t,wordCount:0,openingTextPreview:"",startPosition:n?.index??null,existingSceneID:n?ys(n,"PD-SCENE"):null},f.push(r),r):null,a=(n,t)=>{!i||!r||t<=0||(i.wordCount+=t,i.chapterTextParagraphs.push(n),r.wordCount+=t,r.openingTextPreview||!n||ps.has(n)||(r.openingTextPreview=n.length>180?`${n.slice(0,177)}...`:n))};if(n.forEach((n,t)=>{n.index=t;const r=String(n.text||"").trim();if(r){s.push(r);const u=kf(r);if(wi(n,1)){h+=1;l(n,r);e+=u;i.wordCount+=u;i.chapterTextParagraphs.push(r);return}if(i||l(n,"Opening pages",!0),wi(n,2)){o(n,r);e+=u;a(r,u);return}if(ps.has(r)){o(n,null);return}e+=u;a(r,u)}}),e===0)throw new Error("The document appears to be empty. Add manuscript text, then try again.");if(h===0)throw new Error("No chapters were detected. Make sure your chapter headings use Heading 1, or add clear chapter headings.");u.forEach(n=>{n.chapterText=Array.isArray(n.chapterTextParagraphs)?n.chapterTextParagraphs.join("\n\n"):"",delete n.chapterTextParagraphs});const v=s.join("\n");return{previewID:"00000000-0000-0000-0000-000000000000",userID:t.userID||t.UserID||0,onboardingID:t.onboardingID||t.OnboardingID||0,projectID:t.projectID||t.ProjectID||0,bookID:t.bookID||t.BookID||0,source:"WordCompanion",documentTitle:ny()||"Word manuscript",companionDocumentIdentifier:or(),totalWordCount:e,chapterCount:u.length,sceneCount:f.length,characterCandidateCount:0,createdUtc:(new Date).toISOString(),chapters:u,scenes:f,characterCandidates:[],documentTextForDiscovery:v}},ws=(n,t,i)=>n?.[t]??n?.[i]??null,df=async(n,t,i,r={})=>{d&&d.state===signalR.HubConnectionState.Connected&&await d.invoke("ReportOnboardingScanProgress",{userID:ws(n,"userID","UserID"),onboardingID:ws(n,"onboardingID","OnboardingID"),message:t,percentComplete:i,chapterCount:r.chapterCount||0,sceneCount:r.sceneCount||0,characterCandidateCount:r.characterCandidateCount||0,totalWordCount:r.totalWordCount||0})},ly=async(n,t)=>{d&&d.state===signalR.HubConnectionState.Connected&&await d.invoke("FailOnboardingScan",{userID:ws(n,"userID","UserID"),onboardingID:ws(n,"onboardingID","OnboardingID"),message:t})},bd=async n=>{if(!nt||!window.Word){await ly(n,"Open your manuscript in Microsoft Word, then try the scan again.");return}try{await df(n,"Preparing document scan",5);const i=await window.Word.run(async t=>{const i=t.document.body.paragraphs;i.load("items/text,items/style,items/styleBuiltIn");await t.sync();await df(n,"Detecting chapters",20);for(const n of i.items)(wi(n,1)||wi(n,2))&&n.contentControls.load("items/tag,title");return await t.sync(),i.items}),t=wd(i,n);await df(n,"Detecting scene breaks",50,t);await df(n,`Scene ${t.sceneCount} of ${t.sceneCount} scanned`,70,t);await df(n,"Finding character candidates",85,t);const r=t.documentTextForDiscovery||"";delete t.documentTextForDiscovery;try{const n=await st("/api/word-companion/manuscript/discover-characters",{projectId:t.projectID,documentText:r,includeExcluded:!0});t.characterCandidates=pd(n?.candidates);t.characterCandidateCount=t.characterCandidates.filter(n=>String(n.category||"").toLowerCase()!=="excluded").length}catch(u){console.warn("Unable to refine onboarding character candidates.",u);t.characterCandidates=[];t.characterCandidateCount=0}await df(n,"Preparing preview",95,t);ko=t;await d.invoke("CompleteOnboardingScan",t)}catch(t){console.error("Unable to complete onboarding scan.",t);await ly(n,dd(t))}},kd=async n=>{if(!ko||!nt||!window.Word||typeof window.Word.run!="function")throw new Error("Word document access is unavailable.");const t=new Map((n?.chapterMappings||n?.ChapterMappings||[]).map(n=>[n.temporaryChapterKey||n.TemporaryChapterKey,n.chapterID||n.ChapterID])),i=new Map((n?.sceneMappings||n?.SceneMappings||[]).map(n=>[n.temporarySceneKey||n.TemporarySceneKey,n.sceneID||n.SceneID]));await window.Word.run(async n=>{const r=n.document.body.paragraphs;r.load("items/text,items/style,items/styleBuiltIn");await n.sync();for(const n of ko.chapters||[]){const u=t.get(n.temporaryChapterKey),i=Number.parseInt(n.startPosition,10);u&&Number.isInteger(i)&&r.items[i]&&pu(r.items[i],"PlotDirector Chapter",`PD-CHAPTER-${u}`,"PD-CHAPTER")}for(const n of ko.scenes||[]){const u=i.get(n.temporarySceneKey),t=Number.parseInt(n.startPosition,10);u&&Number.isInteger(t)&&r.items[t]&&pu(r.items[t],"PlotDirector Scene",`PD-SCENE-${u}`,"PD-SCENE")}await n.sync()})},dd=n=>{const t=e(n);return/No chapters were detected/i.test(t)?"No chapters were detected. Make sure your chapter headings use Heading 1, or add clear chapter headings.":/empty/i.test(t)?"The document appears to be empty. Add manuscript text, then try again.":/network|disconnect|connection/i.test(t)?"The Word Companion disconnected before the scan finished. Reopen Word and try again.":`The scan could not finish: ${t}`},gd=(n,t)=>String(n?.text||"").trim()===String(t?.text||"").trim()&&to(n)===to(t),ay=(n,t)=>{const r=(t||[]).find(n=>String(n.text||"").trim());if(!n||!r)return n?.currentParagraphIndex??-1;const i=n.paragraphs.filter(n=>gd(n,r)).map(n=>n.index);return i.length===0?n.currentParagraphIndex??-1:Number.isInteger(n.currentParagraphIndex)?i.reduce((t,i)=>Math.abs(i-n.currentParagraphIndex)!n||!Number.isInteger(t)||t<0?null:n.chapters.filter(n=>n.startParagraphIndex<=t).at(-1)||null,tg=(n,t)=>!n||!Number.isInteger(t)||t<0?null:n.scenes.filter(n=>n.startParagraphIndex<=t).at(-1)||null,vy=n=>{if(!l)return!1;const t=ng(l,n),i=tg(t,n),r=t?.startParagraphIndex!==l.currentChapter?.startParagraphIndex||i?.startParagraphIndex!==l.currentScene?.startParagraphIndex||t?.anchorId!==l.currentChapter?.anchorId||i?.anchorId!==l.currentScene?.anchorId;return(l.currentParagraphIndex=n,l.currentChapter=t,l.currentScene=i,!r)?!1:(vf(t?.title,i?.title,i?.wordCount,t?.anchorId,i?.anchorId,t?.index),r)},ul=n=>{if(te){if(te.innerHTML="",n.chapters.length===0){const n=document.createElement("p");n.textContent="No chapters detected. Use Heading 1 for chapter titles.";te.append(n);return}const t=n.chapters.some(n=>n.scenes.length>0);if(!t){const n=document.createElement("p");n.textContent="No scenes detected. Use Heading 2 for scene titles.";te.append(n)}for(const t of n.chapters){const n=document.createElement("div");n.className="word-companion-outline-chapter";const i=document.createElement("strong");if(i.textContent=t.title,n.append(i),t.scenes.length>0){const i=document.createElement("ul");for(const n of t.scenes){const t=document.createElement("li");t.textContent=n.title;i.append(t)}n.append(i)}te.append(n)}}},yy=[{key:"alreadyLinked",title:"Already Linked"},{key:"couldLink",title:"Could Link"},{key:"wouldCreateChapters",title:"Would Create Chapters"},{key:"wouldCreateScenes",title:"Would Create Scenes"},{key:"manualReview",title:"Manual Review Required"}],ig={alreadyLinked:"Already Linked",couldLink:"Could Link",wouldCreateChapter:"Would Create Chapter",wouldCreateScene:"Would Create Scene",manualReview:"Manual Review Required"},rg={alreadyLinked:"✓",couldLink:"~",wouldCreateChapter:"+",wouldCreateScene:"+",manualReview:"?"},bs=(n,t)=>ut(n,t).toLocaleLowerCase(),fi=({category:i,action:n,type:s,wordTitle:l,match:r="",anchorStatus:t="None",matches:u=[],pdChapter:e=null,pdScene:o=null,wordChapter:h=null,wordScene:c=null,parentItem:f=null})=>({id:`${s}-${h?.index||0}-${c?.index||0}-${h?.paragraphIndex??c?.paragraphIndex??0}`,category:i,action:n,type:s,wordTitle:l,match:r,anchorStatus:t,matches:u,pdChapter:e,pdScene:o,wordChapter:h,wordScene:c,parentItem:f,result:"Pending",error:""}),ei=(n,t)=>{Array.isArray(n[t.category])&&n[t.category].push(t)},fl=n=>Array.isArray(n?.scenes)?n.scenes:[],ug=(n,t)=>{for(const i of n){const n=fl(i).find(n=>n.sceneId===t);if(n)return{chapter:i,scene:n}}return null},fg=(n,t,i)=>{const u=n.anchorId?`PD-CHAPTER-${n.anchorId}`:"None";if(n.anchorId){const r=t.find(t=>t.chapterId===n.anchorId);if(r){const t=fi({category:"alreadyLinked",action:"alreadyLinked",type:"Chapter",wordTitle:n.title,match:`Chapter ${r.chapterId}: ${r.title}`,anchorStatus:u,pdChapter:r,wordChapter:n});return ei(i,t),t}const f=fi({category:"manualReview",action:"manualReview",type:"Chapter",wordTitle:n.title,anchorStatus:`${u} not found`,matches:[],pdChapter:null,wordChapter:n});return ei(i,f),f}const r=t.filter(t=>bs(t.title,"chapter")===bs(n.title,"chapter"));if(r.length===1){const t=fi({category:"couldLink",action:"couldLink",type:"Chapter",wordTitle:n.title,match:`Chapter ${r[0].chapterId}: ${r[0].title}`,anchorStatus:u,pdChapter:r[0],wordChapter:n});return ei(i,t),t}if(r.length>1){const t=fi({category:"manualReview",action:"manualReview",type:"Chapter",wordTitle:n.title,anchorStatus:u,matches:r.map(n=>`Chapter ${n.chapterId}: ${n.title}`),pdChapter:null,wordChapter:n});return ei(i,t),t}const f=fi({category:"wouldCreateChapters",action:"wouldCreateChapter",type:"Chapter",wordTitle:n.title,anchorStatus:u,pdChapter:null,wordChapter:n});return ei(i,f),f},eg=(n,t,i,r)=>{const u=n.anchorId?`PD-SCENE-${n.anchorId}`:"None";if(n.anchorId){const f=ug(i,n.anchorId);if(f){ei(r,fi({category:"alreadyLinked",action:"alreadyLinked",type:"Scene",wordTitle:n.title,match:`Scene ${f.scene.sceneId}: ${f.scene.title}`,anchorStatus:u,pdChapter:f.chapter,pdScene:f.scene,wordChapter:t?.wordChapter,wordScene:n,parentItem:t}));return}ei(r,fi({category:"manualReview",action:"manualReview",type:"Scene",wordTitle:n.title,anchorStatus:`${u} not found`,wordChapter:t?.wordChapter,wordScene:n,parentItem:t}));return}if(!t?.pdChapter&&t?.category==="manualReview"){ei(r,fi({category:"manualReview",action:"manualReview",type:"Scene",wordTitle:n.title,anchorStatus:u,matches:["Parent chapter requires manual review."],wordChapter:t?.wordChapter,wordScene:n,parentItem:t}));return}if(!t?.pdChapter){ei(r,fi({category:"wouldCreateScenes",action:"wouldCreateScene",type:"Scene",wordTitle:n.title,anchorStatus:u,wordChapter:t?.wordChapter,wordScene:n,parentItem:t}));return}const f=fl(t.pdChapter).filter(t=>bs(t.title,"scene")===bs(n.title,"scene"));if(f.length===1){ei(r,fi({category:"couldLink",action:"couldLink",type:"Scene",wordTitle:n.title,match:`Scene ${f[0].sceneId}: ${f[0].title}`,anchorStatus:u,pdChapter:t.pdChapter,pdScene:f[0],wordChapter:t.wordChapter,wordScene:n,parentItem:t}));return}if(f.length>1){ei(r,fi({category:"manualReview",action:"manualReview",type:"Scene",wordTitle:n.title,anchorStatus:u,matches:f.map(n=>`Scene ${n.sceneId}: ${n.title}`),wordChapter:t.wordChapter,wordScene:n,parentItem:t}));return}ei(r,fi({category:"wouldCreateScenes",action:"wouldCreateScene",type:"Scene",wordTitle:n.title,anchorStatus:u,pdChapter:t.pdChapter,wordChapter:t.wordChapter,wordScene:n,parentItem:t}))},og=(n,t)=>{const i={alreadyLinked:[],couldLink:[],wouldCreateChapters:[],wouldCreateScenes:[],manualReview:[]},r=Array.isArray(t?.chapters)?t.chapters:[];for(const t of Array.isArray(n?.chapters)?n.chapters:[]){const n=fg(t,r,i);for(const u of fl(t))eg(u,n,r,i)}return i},sg=n=>{const t=document.createElement("article");t.className="word-companion-preview-item";const i=document.createElement("strong");i.textContent=`${rg[n.action]||""} ${n.type}: ${n.wordTitle}`;t.append(i);const r=document.createElement("span");if(r.textContent=`Anchor: ${n.anchorStatus||"None"}`,t.append(r),n.match){const i=document.createElement("span");i.textContent=`Match: ${n.match}`;t.append(i)}if(Array.isArray(n.matches)&&n.matches.length>0){const i=document.createElement("div");i.className="word-companion-preview-matches";const r=document.createElement("span");r.textContent="Matches:";i.append(r);const u=document.createElement("ul");for(const t of n.matches){const n=document.createElement("li");n.textContent=t;u.append(n)}i.append(u);t.append(i)}const u=document.createElement("span");if(u.textContent=`Action: ${ig[n.action]||n.action}`,t.append(u),n.result&&n.result!=="Pending"){const i=document.createElement("span");i.textContent=n.error?`Result: ${n.result} - ${n.error}`:`Result: ${n.result}`;t.append(i)}return t},py=n=>{if(tf){tf.innerHTML="";for(const t of yy){const i=document.createElement("section");i.className="word-companion-preview-group";const r=document.createElement("strong");r.textContent=t.title;i.append(r);const u=Array.isArray(n?.[t.key])?n[t.key]:[];if(u.length===0){const n=document.createElement("p");n.className="word-companion-empty-list";n.textContent="None";i.append(n)}else for(const n of u)i.append(sg(n));tf.append(i)}rs()}},gf=async n=>{const t=n.document.body.paragraphs,i=n.document.getSelection().paragraphs;t.load("text,styleBuiltIn,style");i.load("text,styleBuiltIn,style");await n.sync();try{for(const n of t.items)(wi(n,1)||wi(n,2))&&n.contentControls.load("items/tag,title");await n.sync()}catch(u){console.warn("Unable to load Word heading content controls. Continuing without PlotDirector anchors.",u)}const r=ld(t.items,i.items);return r.bodyParagraphs=t.items,r},wy=async(n,t)=>{const u=n.document.body.paragraphs,f=n.document.getSelection().paragraphs;u.load("text,styleBuiltIn,style");f.load("text,styleBuiltIn,style");await n.sync();const r=yd(u.items,t),e=ay(r,f.items);return l=r,vy(e),i("Cache rebuilt."),r},dtt=async n=>{const t=n.document.getSelection().paragraphs;return t.load("text,styleBuiltIn,style"),await n.sync(),t.items},y=()=>window.performance&&typeof window.performance.now=="function"?window.performance.now():Date.now(),ti=n=>!!n&&!n.stale&&n.generation===ve&&n.documentGuid===or()&&n.sceneId===u,by=async(n={})=>{const r=!!n.includeSelection,w=n.updateDisplay!==!1,f=ve,b=y(),k=ni(),d=h(),e=l?.currentScene;if(!e||!Number.isInteger(e.startParagraphIndex)||!Number.isInteger(e.endParagraphIndex)||!kt||!nt||!window.Word||typeof window.Word.run!="function")return null;const g=y(),s=await window.Word.run(async n=>{const i=n.document.body.paragraphs;i.load("text");let t=null;return r&&(t=n.document.getSelection().paragraphs,t.load("text,styleBuiltIn,style")),await n.sync(),{bodyTexts:i.items.map(n=>String(n.text||"")),selectionParagraphs:r&&t?t.items.map(n=>({text:String(n.text||""),styleBuiltIn:n.styleBuiltIn,style:n.style})):[]}}),c=Math.round(y()-g);if(f!==ve)return ci(`Office snapshot: ${c}ms stale`),{stale:!0,generation:f};let a=!1;if(r){const n=ay(l,s.selectionParagraphs);a=vy(n)}const t=l?.currentScene;if(!t||!Number.isInteger(t.startParagraphIndex)||!Number.isInteger(t.endParagraphIndex))return null;const v=[];let i=0;const tt=Math.min(t.endParagraphIndex,s.bodyTexts.length-1),it=l.currentChapter?.startParagraphIndex;for(let n=t.startParagraphIndex;n<=tt;n+=1){const t=String(s.bodyTexts[n]||"").trim();t&&!ps.has(t)&&n!==it&&(v.push(t),i+=kf(t))}t.wordCount=i;w&&vf(l.currentChapter?.title,t.title,i,l.currentChapter?.anchorId,t.anchorId,l.currentChapter?.index);const p={documentGuid:or(),projectId:k?.projectId||null,bookId:d?.bookId||null,chapterId:o,sceneId:u,chapterTitle:l.currentChapter?.title||"",sceneTitle:t.title||"",sceneText:v.join("\n"),wordCount:i,selectionSignature:`${l.currentParagraphIndex??-1}:${t.startParagraphIndex}:${t.endParagraphIndex}`,selectionChanged:a,generation:f};return ye=p,ci(`Office snapshot: ${c}ms; scene words: ${i}; total snapshot: ${Math.round(y()-b)}ms`),p},ky=async()=>ti(ye)?ye:await by(),dy=async()=>{if(t(""),!kt||!nt||!window.Word||typeof window.Word.run!="function")return vf(null,null,null),t("Word document access is unavailable."),f({lastScan:"Failure",lastError:"Word document access is unavailable."}),!1;nu&&(nu.disabled=!0,nu.textContent="Scanning...");t("Scanning document structure...");try{const n=await window.Word.run(async n=>{const t=h();return t?await wy(n,!!t.usesExplicitScenes):(l=null,await gf(n))});return l||vf(n.currentChapter?.title,n.currentScene?.title,n.currentScene?.wordCount,n.currentChapter?.anchorId,n.currentScene?.anchorId,n.currentChapter?.index),it("Not detected"),ul(n),t(""),f({lastScan:"Success",lastError:"-",paragraphs:String(n.paragraphCount),heading1:String(n.heading1Count),heading2:String(n.heading2Count)}),!0}catch(n){const i=e(n);return console.error("Unable to scan the Word document.",n),vf(null,null,null),t(`Unable to scan the Word document: ${i}`),f({lastScan:"Failure",lastError:i}),!1}finally{nu&&(nu.disabled=!1,nu.textContent="Refresh Document Structure")}},gy=async()=>{const n=h();if(!ni()||!n)return er("Select a project and book to analyse manuscript structure."),!1;if(!kt||!nt||!window.Word||typeof window.Word.run!="function")return er("Word document access is unavailable."),f({lastScan:"Failure",lastError:"Word document access is unavailable."}),!1;cr&&(cr.disabled=!0,cr.textContent="Analysing...");er("Analysing manuscript structure...");try{const t=await window.Word.run(async n=>await gf(n)),i=await at(`/api/word-companion/books/${n.bookId}/structure`);return vf(t.currentChapter?.title,t.currentScene?.title,t.currentScene?.wordCount,t.currentChapter?.anchorId,t.currentScene?.anchorId,t.currentChapter?.index),ul(t),vi=og(t,i),py(vi),er("Preview generated. No changes have been applied."),f({lastScan:"Success",lastError:"-",paragraphs:String(t.paragraphCount),heading1:String(t.heading1Count),heading2:String(t.heading2Count)}),!0}catch(t){return console.error("Unable to analyse manuscript structure.",t),er("Unable to analyse manuscript structure."),f({lastScan:"Failure",lastError:e(t)}),!1}finally{cr&&(cr.textContent="Analyse Manuscript Structure",us())}},hg=(n=vi)=>{const t=ou(n),i=t.filter(n=>n.category==="couldLink"&&n.type==="Chapter").length,r=t.filter(n=>n.category==="couldLink"&&n.type==="Scene").length,u=t.filter(n=>n.category==="wouldCreateChapters").length,f=t.filter(n=>n.category==="wouldCreateScenes").length,e=Array.isArray(n?.manualReview)?n.manualReview.length:0;return{linkChapters:i,linkScenes:r,createChapters:u,createScenes:f,manualReview:e}},cg=n=>{if(ah){ah.innerHTML="";const i=[`Link ${n.linkChapters+n.linkScenes} chapters/scenes`,`Create ${n.createChapters} chapters`,`Create ${n.createScenes} scenes`],t=document.createElement("ul");for(const n of i){const i=document.createElement("li");i.textContent=n;t.append(i)}ah.append(t)}},el=n=>{ns&&(ns(n),ns=null),tr?.close?tr.close():tr&&(tr.hidden=!0)},lg=()=>{const n=hg();return(cg(n),!tr)?Promise.resolve(window.confirm(`Apply Structure Sync? + +This will: + +- Link ${n.linkChapters+n.linkScenes} chapters/scenes +- Create ${n.createChapters} chapters +- Create ${n.createScenes} scenes + +Manual review items will be skipped.`)):new Promise(n=>{ns=n,tr.showModal?tr.showModal():tr.hidden=!1})},io=(n,t,i="")=>{n.result=t,n.error=i},np=n=>Number.isInteger(n?.wordChapter?.index)&&n.wordChapter.index>0?n.wordChapter.index*10:null,tp=n=>Number.isInteger(n?.wordScene?.index)&&n.wordScene.index>0?n.wordScene.index*10:null,ag=async(n,t)=>{const i=await st(`/api/word-companion/books/${n}/chapters`,{title:lu(t.wordTitle),sortOrder:np(t)});if(!i?.chapterId)throw new Error("Chapter creation did not return a chapter ID.");su();t.pdChapter={chapterId:i.chapterId,title:i.title||t.wordTitle,sortOrder:i.sortOrder||np(t)||0,scenes:[]};t.match=`Chapter ${t.pdChapter.chapterId}: ${t.pdChapter.title}`},vg=async(n,t)=>{const i=t.pdChapter||t.parentItem?.pdChapter;if(!i?.chapterId)throw new Error("Scene parent chapter is not available.");const r=await st(`/api/word-companion/books/${n}/chapters/${i.chapterId}/scenes`,{sceneTitle:lu(t.wordTitle),sortOrder:tp(t)});if(!r?.sceneId)throw new Error("Scene creation did not return a scene ID.");su();t.pdChapter=i;t.pdScene={sceneId:r.sceneId,title:t.wordTitle,sortOrder:tp(t)||0};t.match=`Scene ${t.pdScene.sceneId}: ${t.pdScene.title}`},yg=async n=>{if(!kt||!nt||!window.Word||typeof window.Word.run!="function")throw new Error("Word document access is unavailable.");await window.Word.run(async t=>{const u=await gf(t),r=n.type==="Chapter"?n.wordChapter:n.wordScene,i=Number.isInteger(r?.paragraphIndex)?u.bodyParagraphs[r.paragraphIndex]:null;if(!i)throw new Error("Unable to locate the Word heading.");if(n.type==="Chapter"){const t=n.pdChapter?.chapterId;if(!t)throw new Error("Chapter ID is not available.");pu(i,"PlotDirector Chapter",`PD-CHAPTER-${t}`,"PD-CHAPTER")}else{const t=n.pdScene?.sceneId;if(!t)throw new Error("Scene ID is not available.");pu(i,"PlotDirector Scene",`PD-SCENE-${t}`,"PD-SCENE")}await t.sync()})},pg=n=>`Structure Sync Complete. Created: ${n.createdChapters} Chapters, ${n.createdScenes} Scenes. Linked: ${n.linkedChapters} Chapters, ${n.linkedScenes} Scenes. Skipped: ${n.skipped} Manual Review items. Failed: ${n.failed} Items.`,wg=async()=>{const r=h();if(!r||ou().length===0||ae){rs();return}const f=await lg();if(f){ae=!0;us();er("Applying structure sync...");const n={createdChapters:0,createdScenes:0,linkedChapters:0,linkedScenes:0,skipped:Array.isArray(vi?.manualReview)?vi.manualReview.length:0,failed:0},o=ou().filter(n=>n.category==="couldLink"&&n.type==="Chapter"),s=ou().filter(n=>n.category==="wouldCreateChapters"),c=ou().filter(n=>n.category==="couldLink"&&n.type==="Scene"),l=ou().filter(n=>n.category==="wouldCreateScenes"),u=[];let t="";const i=(n,t)=>{u.push({item:n,counterName:t})},a=async(t,i)=>{try{await yg(t);io(t,"Success");n[i]+=1}catch(r){io(t,"Failed",e(r));n.failed+=1}};try{for(const n of o)i(n,"linkedChapters");for(const t of s)try{await ag(r.bookId,t);i(t,"createdChapters")}catch(u){io(t,"Failed",e(u));n.failed+=1}for(const n of c)i(n,"linkedScenes");for(const t of l)try{await vg(r.bookId,t);i(t,"createdScenes")}catch(u){io(t,"Failed",e(u));n.failed+=1}for(const n of u)await a(n.item,n.counterName);for(const n of vi.manualReview||[])io(n,"Skipped");py(vi);t=pg(n);er(t)}finally{ae=!1;us();t&&(su(),await gy(),er(`${t} Preview refreshed.`))}}},bg=async()=>{iu&&(iu.disabled=!0,iu.textContent="Running...");try{if(await gv(),!kt||!nt||!window.Word||typeof window.Word.run!="function"){f({lastScan:"Failure",lastError:"Word document access is unavailable.",paragraphs:"-",heading1:"-",heading2:"-"});return}const n=await window.Word.run(async n=>{const t=n.document.body.paragraphs;return t.load("items"),await n.sync(),t.items.length});f({lastScan:"Success",lastError:"-",paragraphs:String(n)})}catch(n){console.error("Word Companion diagnostics failed.",n);const i=e(n);f({lastScan:"Failure",lastError:i});t(`Unable to scan the Word document: ${i}`)}finally{iu&&(iu.disabled=!1,iu.textContent="Run Diagnostics")}},at=async(n,t={})=>{const i=await window.fetch(n,{credentials:"same-origin",...t,headers:{Accept:"application/json",...(t.headers||{})}});if(!i.ok)throw new Error(`Request failed: ${i.status}`);return await i.json()},st=(n,t)=>at(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),kg=async()=>{const n=gr(),t=au();if(!n||!t){lt("Select a Project and Book to begin.");return}if(!kt||!nt||!window.Word||typeof window.Word.run!="function"){lt("Word document access is unavailable.");return}sr&&(sr.disabled=!0,sr.textContent="Scanning...");ui("Scanning manuscript structure...");try{const i=await window.Word.run(async n=>{const i=n.document.body.paragraphs;return i.load("text,styleBuiltIn,style"),await n.sync(),ad(i.items,!!t.usesExplicitScenes)});he=i;go=!1;const u=await st("/api/word-companion/manuscript/analyse",{projectId:n.projectId,bookId:t.bookId,documentGuid:fy(),chapterCount:i.chapterCount,sceneCount:i.sceneCount,wordCount:i.wordCount});wr=u;od(u);try{const t=await st("/api/word-companion/manuscript/discover-characters",{projectId:n.projectId,documentText:i.documentText||""});hi=Array.isArray(t?.candidates)?t.candidates:[];r(bu,!0);r(du,!0)}catch(e){console.error("Unable to discover character candidates.",e);hi=[];r(bu,!0);r(du,!0)}f({lastScan:"Success",lastError:"-",paragraphs:"-",heading1:String(i.chapterCount),heading2:"-"})}catch(i){console.error("Unable to scan manuscript for first-run import.",i);lt(`Unable to scan manuscript: ${e(i)}`);f({lastScan:"Failure",lastError:e(i)})}finally{sr&&(sr.textContent="Scan Manuscript");ft()}},dg=()=>new Promise(n=>{if(!gu||typeof gu.showModal!="function"){n(window.confirm("This Book already contains planned chapters and scenes.\n\nImporting this manuscript will replace the planned structure.\n\nContinue?"));return}const t=t=>{dl?.removeEventListener("click",i),gl?.removeEventListener("click",r),gu?.removeEventListener("cancel",u),gu?.close(),n(t)},i=()=>t(!0),r=()=>t(!1),u=n=>{n.preventDefault(),t(!1)};dl?.addEventListener("click",i);gl?.addEventListener("click",r);gu?.addEventListener("cancel",u);gu.showModal()}),gg=()=>new Promise(n=>{if(!nf||typeof nf.showModal!="function"){n(window.confirm("This will remove PlotDirector binding metadata from this Word document.\n\nIf PlotDirector is reachable, it will also unlink this Book in PlotDirector.\n\nYour manuscript text will not be changed.\n\nContinue?"));return}const t=t=>{na?.removeEventListener("click",i),ta?.removeEventListener("click",r),nf?.removeEventListener("cancel",u),nf?.close(),n(t)},i=()=>t(!0),r=()=>t(!1),u=n=>{n.preventDefault(),t(!1)};na?.addEventListener("click",i);ta?.addEventListener("click",r);nf?.addEventListener("cancel",u);nf.showModal()}),nn=async()=>{const i=gr(),u=au();if(!i||!u||!he||!wr?.canImport){ft();return}let n=!1;if(wr.requiresReplaceConfirmation&&(n=await dg(),!n)){ui("Import cancelled.");return}le=!0;ft();ui("Importing manuscript structure...");try{const h=fy(),f=await st("/api/word-companion/manuscript/import",{projectId:i.projectId,bookId:u.bookId,documentGuid:h,bindingVersion:1,replacePlanned:n,chapters:he.chapters});if(!f.imported){wr={...wr,canImport:!f.requiresReplaceConfirmation,requiresReplaceConfirmation:!!f.requiresReplaceConfirmation,message:f.message||wr.message};ui(f.message||"Import was not completed.");ft();return}const e=f.manuscriptDocument;await hd({documentGuid:e?.documentGuid||h,bookId:f.bookId,projectId:f.projectId,bindingVersion:e?.bindingVersion||1});ri={documentGuid:e?.documentGuid||h,bookId:f.bookId,projectId:f.projectId,bindingVersion:e?.bindingVersion||1};v(s.projectId,f.projectId);v(s.bookId,f.bookId);p&&(p.value=String(f.projectId));const o=hi;await wu(f.projectId,f.bookId);de("Connected");go=!0;uf=o.length>0;ce=uf?Date.now():0;hi=o;o.length>0?(vu(!0),ed(o),r(rh,!0),r(du,!1),ui(f.message||"Manuscript structure imported."),t(f.message||"Manuscript structure imported."),window.setTimeout(ft,750)):await ks(f.message||"Manuscript structure imported.")}catch(f){console.error("Unable to import manuscript structure.",f);ui(`Unable to import manuscript: ${e(f)}`)}finally{le=!1;ft()}},ks=async(n="")=>{uf=!1;ce=0;hi=[];ki&&(ki.innerHTML="");r(bu,!0);vu(!1);ui("");r(du,!0);n&&t(n);const i=await ip();n&&i&&w&&t(n)},tn=async()=>{const i=gr()||ni(),r=ry();if(!uf||Date.now()-ce<750||!i||r.length===0){ft();return}wh=!0;ft();n(ku,"Creating selected characters...");try{const u=await st("/api/word-companion/manuscript/create-discovered-characters",{projectId:i.projectId,candidateNames:r});t(u.message||"Characters created.");n(ku,`${u.createdCount||0} created, ${u.skippedCount||0} already existed.`);await ks(u.message||"Characters created.")}catch(u){console.error("Unable to create discovered characters.",u);n(ku,`Unable to create characters: ${e(u)}`)}finally{wh=!1;ft()}},rn=async(n=u,t=o)=>{const f=ni(),e=h(),c=or(),r=Number.parseInt(n||"",10),s=Number.parseInt(t||"",10);if(f&&e&&c&&Number.isInteger(r)&&!(r<=0)&&Number.isInteger(s)&&!(s<=0)){const l=`${f.projectId}:${e.bookId}:${r}`;if(ac!==l){ac=l;try{await st("/api/word-companion/runtime/current-scene",{documentGuid:c,projectId:f.projectId,bookId:e.bookId,chapterId:s,sceneId:r});i("Current scene follow event sent.")}catch(a){ac="";console.debug("Unable to notify PlotDirector of current scene.",a)}}}},ip=async()=>{const n=h();if(!n||!kt||!nt||!window.Word||typeof window.Word.run!="function")return!1;t("Reading manuscript position...");try{su();await Promise.all([hl(!0),cl(!0),ll(!0)]);await cs(n.bookId);const i=await window.Word.run(async t=>await wy(t,!!n.usesExplicitScenes));return(ul(i),f({lastScan:"Success",lastError:"-",paragraphs:String(i.paragraphCount),heading1:String(i.heading1Count),heading2:String(i.heading2Count)}),!w)?(it("Not detected"),li(!1),k("Live"),t("Manuscript linked. Click inside a chapter to show the current scene."),!0):(rt?await fo():await vl(),li(!1),k("Live"),t(""),!0)}catch(i){return console.error("Unable to initialise bound manuscript runtime.",i),l=null,k("Manual"),t(`Unable to read manuscript position: ${e(i)}`),f({lastScan:"Failure",lastError:e(i)}),!1}},un=async()=>{if(!rf||!nt){vu(!1);return}const n=rl();if(n)try{const t=await at(`/api/word-companion/runtime/book/${n.bookId}?documentGuid=${encodeURIComponent(n.documentGuid)}`),i=t?.manuscriptDocument;if(t?.bookId===n.bookId&&t?.projectId===n.projectId&&i&&String(i.documentGuid).toLowerCase()===n.documentGuid.toLowerCase()){ri=n;pv(t);v(s.projectId,t.projectId);v(s.bookId,t.bookId);p&&(p.value=String(t.projectId));await wu(t.projectId,t.bookId);vu(!1);de("Connected");lf("Bound manuscript detected.");await ip();return}}catch{}ri=null;wf();il();gr()?await wu(gr().projectId,null):c(g,"Select a project first");lt("Select a Project and Book to begin.");vu(!0)},rp=async n=>{ri=null,yp(),yf(),wf(),it("Not detected"),k("Manual"),de("Connected"),v(s.projectId,null),v(s.bookId,null),p&&(p.value=""),c(ot,"Select a project first"),il(),et&&(et.value=""),c(g,"Select a project first"),lt(n||"Select a Project and Book to begin."),t(n||"Manuscript unlinked."),gt("No resolved PlotDirector scene."),vu(!0),bc()},fn=async()=>{console.debug("Word Companion unlink clicked");t("Preparing to unlink manuscript...");const n=sd();if(!n){t("This Word document is not linked.");return}if(!n.documentGuid||!Number.isInteger(n.bookId)||n.bookId<=0){t("Unable to unlink: bound document metadata is incomplete.");return}const r=await gg();if(!r){t("Unlink cancelled.");return}t("Unlinking manuscript...");lr&&(lr.disabled=!0,lr.textContent="Unlinking...");try{await st("/api/word-companion/manuscript/unlink",{documentGuid:n.documentGuid,bookId:n.bookId});await uy();await rp("Manuscript unlinked.")}catch(i){console.error("Unable to unlink manuscript binding.",i);t("Unable to unlink from PlotDirector.");const r=window.confirm("PlotDirector could not be updated, but you can still remove the binding from this Word document.\n\nYou may need to unlink the Book manually in PlotDirector later.\n\nRemove binding from this Word document only?");if(r)try{await uy();await rp("Word metadata removed. Unlink the Book manually in PlotDirector if it still shows as linked.")}catch(n){console.error("Unable to remove Word document metadata.",n);t(`Unable to remove Word metadata: ${e(n)}`)}else t(`Unable to unlink manuscript: ${e(i)}`)}finally{lr&&(lr.disabled=!1,lr.textContent="Unlink this Word document");bc()}},gtt=(n,t)=>at(n,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),en=(n,t)=>at(n,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),ds=n=>n?[...n.querySelectorAll("input[type='checkbox']:checked")].map(n=>Number.parseInt(n.value||"",10)).filter(n=>Number.isInteger(n)&&n>0):[],up=()=>{const n=Number.parseInt(vr?.value||"",10);return Number.isInteger(n)&&n>0?n:null},on=()=>{const n=new Set(ds(ie)),t=new Set(ds(re));ct={characters:ct.characters.map(t=>({...t,linked:n.has(Number.parseInt(ss(t,"character"),10))})),assets:ct.assets.map(n=>({...n,linked:t.has(Number.parseInt(ss(n,"asset"),10))})),locations:ct.locations,primaryLocationId:up()}},sn=async()=>{if(!u)return!1;if(!kt||!nt||!window.Word||typeof window.Word.run!="function")return li(!0,"The Word cursor is now in a different scene. Refresh Current Scene before saving."),!1;try{const n=await window.Word.run(async n=>await gf(n)),r=n.currentChapter?.title||"",f=n.currentScene?.title||"",t=n.currentScene?.anchorId||null,i=n.currentChapter?.anchorId||null,e=Number.isInteger(t)&&t===u,s=Number.isInteger(i)&&i===o,h=ut(f,"scene").toLocaleLowerCase()===ut(is||rt,"scene").toLocaleLowerCase(),c=ut(r,"chapter").toLocaleLowerCase()===ut(vc||w,"chapter").toLocaleLowerCase(),l=e||h&&(s||c);return l?(li(!1),!0):(li(!0,"The Word cursor is now in a different scene. Refresh Current Scene before saving."),!1)}catch(n){return console.error("Unable to verify current Word scene before saving.",n),f({lastError:e(n)}),li(!0,"The Word cursor is now in a different scene. Refresh Current Scene before saving."),!1}},hn=async()=>{if(!u){hu("Link a PlotDirector scene first.");return}if(ht||!await sn()){hu("The Word cursor is now in a different scene. Refresh Current Scene before saving.");return}di&&(di.disabled=!0,di.textContent="Saving...");try{await en(`/api/word-companion/scenes/${u}/links`,{characterIds:ds(ie),assetIds:ds(re),locationIds:[],primaryLocationId:up()});on();hu("Scene links saved successfully.");f({lastError:"-"})}catch(n){hu("Unable to save scene links.");f({lastError:e(n)})}finally{di&&(di.disabled=!u||ht,di.textContent="Save Scene Links")}},cn=(n,t)=>{const i=sy(n);return i.find(n=>oy(n.tag,t))||null},pu=(n,t,i,r)=>{const f=cn(n,r),u=f||n.getRange().insertContentControl();return u.title=t,u.tag=i,u.appearance="Hidden",u},fp=async(n="PlotDirector IDs attached successfully.")=>{if(!u||!o)return t("No resolved PlotDirector scene."),!1;const r=tt&&tt!==u||b&&b!==o;if(r&&!window.confirm("This scene is already linked. Replace the existing PlotDirector link?"))return!1;if(!kt||!nt||!window.Word||typeof window.Word.run!="function")return t("Word document access is unavailable."),!1;ai&&(ai.disabled=!0,ai.textContent="Attaching...");try{return await window.Word.run(async n=>{const t=await gf(n);if(!t.currentChapter||!t.currentScene)throw new Error("No scene detected in Word. Use Heading 2 for scenes.");const i=t.bodyParagraphs[t.currentChapter.paragraphIndex],r=t.bodyParagraphs[t.currentScene.paragraphIndex];if(!i||!r)throw new Error("Unable to locate the current Word headings.");pu(i,"PlotDirector Chapter",`PD-CHAPTER-${o}`,"PD-CHAPTER");pu(r,"PlotDirector Scene",`PD-SCENE-${u}`,"PD-SCENE");await n.sync()}),b=o,tt=u,dt=!1,rr="SceneID Anchor",fe="Anchor",t(n),f({lastError:"-"}),fr(),!0}catch(i){return console.error("Unable to attach PlotDirector IDs.",i),t("Unable to attach PlotDirector IDs."),f({lastError:e(i)}),fr(),!1}finally{ai&&(ai.textContent=tt===u?"Attach PlotDirector IDs":"Reattach PlotDirector IDs",fr())}},ep=async n=>{if(!o)return yi("No resolved PlotDirector chapter."),!1;if(!kt||!nt||!window.Word||typeof window.Word.run!="function")return yi("Word document access is unavailable."),!1;try{return await window.Word.run(async n=>{const t=await gf(n);if(!t.currentChapter)throw new Error("No chapter detected in Word. Use Heading 1 for chapters.");const i=t.bodyParagraphs[t.currentChapter.paragraphIndex];if(!i)throw new Error("Unable to locate the current Word chapter heading.");pu(i,"PlotDirector Chapter",`PD-CHAPTER-${o}`,"PD-CHAPTER");await n.sync()}),b=o,dt=!1,rr="ChapterID Anchor",fe="Chapter anchor",yi(n),t(n),f({lastError:"-"}),fr(),!0}catch(i){return console.error("Unable to attach PlotDirector chapter ID.",i),yi("Unable to attach PlotDirector chapter ID."),f({lastError:e(i)}),fr(),!1}},ln=async()=>{await fp()},op=async n=>{ge(),await fo(),t(n),yi(n)},ol=n=>{wo&&(wo(n),wo=null),nr?.close?nr.close():nr&&(nr.hidden=!0)},an=(t,i)=>(n(fb,`"${t}"`),n(eb,`"${i}"`),!nr)?Promise.resolve(window.confirm(`Create PlotDirector chapter: + +"${t}" + +in book: + +"${i}" + +?`)):new Promise(n=>{wo=n,nr.showModal?nr.showModal():nr.hidden=!1}),vn=async()=>{const n=h();if(!n||!w||o){es("Chapter not found in PlotDirector.");return}const t=lu(w),i=await an(t,dr(n)||"selected book");if(i){yt&&(yt.disabled=!0,yt.textContent="Creating...");try{const r=Number.isInteger(ue)&&ue>0?ue*10:null,i=await st(`/api/word-companion/books/${n.bookId}/chapters`,{title:t,sortOrder:r});if(!i?.chapterId)throw new Error("Chapter creation did not return a chapter ID.");su();kr(null,i.chapterId,"Manual chapter link");const u=await ep("Chapter created and linked successfully.");if(!u)return;await op("Chapter created and linked successfully.")}catch(r){yi("Unable to create chapter.");f({lastError:e(r)})}finally{yt&&(yt.disabled=!gc(),yt.textContent="Create Chapter in PlotDirector")}}},sp=()=>{if(ao){const n=lu(lo?.value||"").toLocaleLowerCase(),t=ph.filter(t=>!n||String(t.title||"").toLocaleLowerCase().includes(n));if(ao.innerHTML="",oe=null,si&&(si.disabled=!0),t.length===0){const n=document.createElement("p");n.className="word-companion-empty-list";n.textContent="No chapters found.";ao.append(n);return}for(const n of t){const i=Number.parseInt(n.chapterId||"",10);if(Number.isInteger(i)&&!(i<=0)){const r=document.createElement("label");r.className="word-companion-scene-option";const t=document.createElement("input");t.type="radio";t.name="word-companion-link-chapter";t.value=String(i);t.addEventListener("change",()=>{oe=i,si&&(si.disabled=!1)});const u=document.createElement("span");u.textContent=n.title||"Untitled chapter";r.append(t,u);ao.append(r)}}}},yn=async()=>{const t=h();if(!t||!w||o){es("Chapter not found in PlotDirector.");return}pt&&(pt.disabled=!0,pt.textContent="Loading...");try{ph=await bv(t.bookId);oe=null;lo&&(lo.value="");n(lh,"");sp();pr?.showModal?pr.showModal():pr&&(pr.hidden=!1)}catch(i){yi("Unable to load chapters.");f({lastError:e(i)})}finally{pt&&(pt.disabled=!gc(),pt.textContent="Link to Existing Chapter")}},hp=()=>{pr?.close?pr.close():pr&&(pr.hidden=!0)},pn=async()=>{const t=ph.find(n=>n.chapterId===oe);if(!t){n(lh,"Select a chapter.");return}si&&(si.disabled=!0,si.textContent="Linking...");try{kr(null,t.chapterId,"Manual chapter link");const n=await ep("Chapter linked successfully.");if(!n)return;hp();await op("Chapter linked successfully.")}catch(i){n(lh,"Unable to link chapter.");yi("Unable to link chapter.");f({lastError:e(i)})}finally{si&&(si.disabled=!oe,si.textContent="Link Chapter")}},cp=async(n,t,i)=>{const r=h();if(!r)return cu("Select a PlotDirector book first."),!1;await ls(r.bookId,n,t,"Manual link","Title Match");const u=await fp(i);return u?(cu(i),os(),!0):!1},sl=n=>{po&&(po(n),po=null),gi?.close?gi.close():gi&&(gi.hidden=!0)},wn=(t,i)=>(n(nb,`"${t}"`),n(tb,`"${i}"`),!gi)?Promise.resolve(window.confirm(`Create PlotDirector scene: + +"${t}" + +within chapter: + +"${i}" + +?`)):new Promise(n=>{po=n,gi.showModal?gi.showModal():gi.hidden=!1}),bn=async()=>{const n=h();if(!n||!rt){no("Scene not found in PlotDirector.");return}const t=ut(rt,"scene"),i=ut(w,"chapter"),r=await wn(t,i);if(r){wt&&(wt.disabled=!0,wt.textContent="Creating...");try{const r=await dv();if(!r){no("This chapter does not exist in PlotDirector. Create or link the chapter first.");return}const i=await st(`/api/word-companion/books/${n.bookId}/chapters/${r.chapterId}/scenes`,{sceneTitle:t});if(!i?.sceneId||!i?.chapterId)throw new Error("Scene creation did not return a scene ID.");su();await cp(i.sceneId,i.chapterId,"Scene created and linked successfully.")}catch(u){cu("Unable to create scene.");f({lastError:e(u)})}finally{wt&&(wt.disabled=!nl(),wt.textContent="Create Scene in PlotDirector")}}},lp=()=>{if(co){const n=lu(ho?.value||"").toLocaleLowerCase(),t=yh.filter(t=>!n||String(t.title||"").toLocaleLowerCase().includes(n));if(co.innerHTML="",ee=null,oi&&(oi.disabled=!0),t.length===0){const n=document.createElement("p");n.className="word-companion-empty-list";n.textContent="No scenes found.";co.append(n);return}for(const n of t){const i=Number.parseInt(n.sceneId||"",10);if(Number.isInteger(i)&&!(i<=0)){const r=document.createElement("label");r.className="word-companion-scene-option";const t=document.createElement("input");t.type="radio";t.name="word-companion-link-scene";t.value=String(i);t.addEventListener("change",()=>{ee=i,oi&&(oi.disabled=!1)});const u=document.createElement("span");u.textContent=n.title||"Untitled scene";r.append(t,u);co.append(r)}}}},kn=async()=>{const t=h();if(!t||!rt){no("Scene not found in PlotDirector.");return}bt&&(bt.disabled=!0,bt.textContent="Loading...");try{const r=await cs(t.bookId),i=kv(r);if(!i){no("This chapter does not exist in PlotDirector. Create or link the chapter first.");return}yh=Array.isArray(i.scenes)?i.scenes:[];ee=null;ho&&(ho.value="");n(ch,"");lp();yr?.showModal?yr.showModal():yr&&(yr.hidden=!1)}catch(i){cu("Unable to load scenes.");f({lastError:e(i)})}finally{bt&&(bt.disabled=!nl(),bt.textContent="Link to Existing Scene")}},ap=()=>{yr?.close?yr.close():yr&&(yr.hidden=!0)},dn=async()=>{const i=h(),t=yh.find(n=>n.sceneId===ee);if(!i||!t){n(ch,"Select a scene.");return}oi&&(oi.disabled=!0,oi.textContent="Linking...");try{const n=await dv();if(!n)throw new Error("This chapter does not exist in PlotDirector. Create or link the chapter first.");ap();await cp(t.sceneId,n.chapterId,"Scene linked successfully.")}catch(r){n(ch,"Unable to link scene.");cu("Unable to link scene.");f({lastError:e(r)})}finally{oi&&(oi.disabled=!ee,oi.textContent="Link Scene")}},ro=()=>{ef=!1,dc(),be&&(be=!1,gs(1e3))},vp=async(t={})=>{const p=t.source||"manual",w=y();if(!u){gt("No resolved PlotDirector scene.");return}if(!o){gt("No resolved PlotDirector chapter.");return}if(ht){gt("Refresh Current Scene before syncing.");return}const c=h(),l=or();if(!c||!l){gt("Bound manuscript metadata is unavailable.");return}if(ef){be=!0;i("Scene sync deferred; sync already running.");return}yf();ef=!0;be=!1;let r=t.snapshot||null;try{r=ti(r)?r:await ky()}catch(v){gt("Sync failed");f({lastError:e(v)});i("Scene sync failed.");ro();return}if(!ti(r)){gt("Waiting for typing to pause");i("Scene sync skipped (stale snapshot).");ro();return}const s=r.wordCount;if(!Number.isInteger(s)||s<0){gt("Sync failed");i("Scene sync failed.");ro();return}if(dh===r.sceneId&&gh===s){gt("Synced");i("Sync skipped (count unchanged).");ro();return}i("Scene word count changed.");ar&&(ar.disabled=!0,ar.textContent="Syncing...");gt("Syncing...");i(`Scene sync started (${p}).`);try{const f=y(),e=await st("/api/word-companion/runtime/scene-word-count",{documentGuid:r.documentGuid||l,bookId:r.bookId||c.bookId,chapterId:r.chapterId,sceneId:r.sceneId,wordCount:s});if(ci(`Word count sync API: ${Math.round(y()-f)}ms`),!ti(r)){i("Scene sync result ignored (stale snapshot).");return}const t=e?.actualWords??s,u=new Date;dh=r.sceneId;gh=t;n(hh,a(t));n(eh,a(t));n(tv,fs(u));n(nv,fs(u));gt("Synced");i("Scene sync succeeded.");ci(`Total word count sync: ${Math.round(y()-w)}ms`)}catch(v){gt("Sync failed");f({lastError:e(v)});i("Scene sync failed.")}finally{ar&&(ar.textContent="Sync Current Scene");ro()}},gs=(n=4e3)=>{(yf(),u&&o&&!ht&&!ef)&&(we=window.setTimeout(()=>{we=null,void vp({source:"automatic"})},n))},uo=()=>{ke&&(window.clearTimeout(ke),ke=null)},gn=()=>{nc=null,tc="",cf=[],ic=null,rc="",uo()},ntt=()=>{uc=null,fc="",uu=[],ec=null,oc=""},ttt=()=>{sc=null,hc="",fu=[],cc=null,lc=""},hl=async(n=false)=>{const t=ni(),r=or();if(!t||!r)return cf=[],[];if(!n&&nc===t.projectId&&tc===r&&cf.length>0)return cf;const u=await at(`/api/word-companion/runtime/characters?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(r)}`);return nc=t.projectId,tc=r,cf=Array.isArray(u?.characters)?u.characters:[],i("Character alias cache refreshed."),cf},cl=async(n=false)=>{const t=ni(),r=or();if(!t||!r)return uu=[],[];if(!n&&uc===t.projectId&&fc===r&&uu.length>0)return uu;const u=await at(`/api/word-companion/runtime/assets?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(r)}`);return uc=t.projectId,fc=r,uu=Array.isArray(u?.assets)?u.assets:[],i(`Asset cache loaded: ${uu.length} aliases.`),uu},ll=async(n=false)=>{const t=ni(),r=or();if(!t||!r)return fu=[],[];if(!n&&sc===t.projectId&&hc===r&&fu.length>0)return fu;const u=await at(`/api/word-companion/runtime/locations?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(r)}`);return sc=t.projectId,hc=r,fu=Array.isArray(u?.locations)?u.locations:[],i(`Location cache loaded: ${fu.length} aliases.`),fu},itt=n=>String(n||"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),al=(n,t)=>{const i=String(t||"").trim();if(!i)return!1;const r=itt(i).replace(/\s+/g,"\\s+");return new RegExp(`(^|[^\\p{L}\\p{N}_])${r}(?=$|[^\\p{L}\\p{N}_])`,"iu").test(n)},rtt=(n,t)=>{if(!n||!Array.isArray(t)||t.length===0)return[];const i=new Map;for(const r of t){const t=Number.parseInt(r.characterId||"",10);!Number.isInteger(t)||t<=0||i.has(t)||al(n,r.matchText)&&i.set(t,r.name||r.matchText||"Character")}return[...i.entries()].map(([n,t])=>({characterId:n,name:t}))},utt=(n,t)=>{if(!n||!Array.isArray(t)||t.length===0)return[];const i=new Map;for(const r of t){const t=Number.parseInt(r.assetId||"",10);!Number.isInteger(t)||t<=0||i.has(t)||al(n,r.matchText)&&i.set(t,r.name||r.matchText||"Asset")}return[...i.entries()].map(([n,t])=>({assetId:n,name:t}))},ftt=n=>String(n||"").trim().split(/\s+/).filter(Boolean).length,ett=n=>{if(n?.excludeFromCompanionDetection)return!1;const t=Number.parseInt(n?.detectionPriority||"0",10);return ftt(n?.matchText)>=2||Number.isInteger(t)&&t>=75},ott=(n,t)=>{if(!n||!Array.isArray(t)||t.length===0)return[];const i=new Map;for(const r of t){const t=Number.parseInt(r.locationId||"",10);!Number.isInteger(t)||t<=0||i.has(t)||!ett(r)||al(n,r.matchText)&&i.set(t,r.name||r.matchText||"Location")}return[...i.entries()].map(([n,t])=>({locationId:n,name:t}))},stt=()=>{sf=!1,hf&&(hf=!1,nh(1500))},htt=async()=>{if(ke=null,u&&!ht&&or()){if(sf){hf=!0;i("Character suggestion detection deferred; detection already running.");return}sf=!0;hf=!1;const r=y();try{const n=await ky();if(!ti(n)){i("Runtime suggestions skipped (stale snapshot).");return}const[w,b,k]=await Promise.all([hl(),cl(),ll()]);if(!ti(n)){i("Runtime suggestions skipped after cache load (stale snapshot).");return}const s=n.sceneText||"",d=y(),o=rtt(s,w);ci(`Character detection: ${Math.round(y()-d)}ms`);const a=o.map(n=>n.characterId),h=a.slice().sort((n,t)=>n-t).join(","),g=y(),f=utt(s,b);ci(`Asset detection: ${Math.round(y()-g)}ms`);const v=f.map(n=>n.assetId),c=v.slice().sort((n,t)=>n-t).join(","),nt=y(),e=ott(s,k);ci(`Location detection: ${Math.round(y()-nt)}ms`);const p=e.map(n=>n.locationId),l=p.slice().sort((n,t)=>n-t).join(",");if(h&&(ic!==u||rc!==h)){if(!ti(n)){i("Character suggestions skipped before submit (stale snapshot).");return}const u=y(),r=await st("/api/word-companion/runtime/character-suggestions",{documentGuid:n.documentGuid,sceneId:n.sceneId,characterIds:a});if(ci(`Character suggestion API: ${Math.round(y()-u)}ms`),!ti(n)){i("Character suggestion result ignored (stale snapshot).");return}if(ic=n.sceneId,rc=h,(r?.createdCount||0)>0){const n=o.slice(0,3).map(n=>n.name).join(", "),i=o.length>3?` +${o.length-3}`:"";t(n?`Characters detected: ${n}${i}`:r.message)}i(r?.message||"Character suggestions submitted.")}else i("Character suggestions skipped.");if(c&&(ec!==u||oc!==c)){if(!ti(n)){i("Asset suggestions skipped before submit (stale snapshot).");return}i(`Asset matches found: ${f.map(n=>n.name).join(", ")}`);const u=y(),r=await st("/api/word-companion/runtime/asset-suggestions",{documentGuid:n.documentGuid,sceneId:n.sceneId,assetIds:v});if(ci(`Asset suggestion API: ${Math.round(y()-u)}ms`),!ti(n)){i("Asset suggestion result ignored (stale snapshot).");return}if(ec=n.sceneId,oc=c,(r?.createdCount||0)>0){const n=f.slice(0,3).map(n=>n.name).join(", "),i=f.length>3?` +${f.length-3}`:"";t(n?`Assets detected: ${n}${i}`:r.message)}i(r?.message||"Asset suggestions submitted.")}else i("Asset suggestions skipped.");if(l&&(cc!==u||lc!==l)){if(!ti(n)){i("Location suggestions skipped before submit (stale snapshot).");return}i(`Location matches found: ${e.map(n=>n.name).join(", ")}`);const u=y(),r=await st("/api/word-companion/runtime/location-suggestions",{documentGuid:n.documentGuid,sceneId:n.sceneId,locationIds:p});if(ci(`Location suggestion API: ${Math.round(y()-u)}ms`),!ti(n)){i("Location suggestion result ignored (stale snapshot).");return}if(cc=n.sceneId,lc=l,(r?.createdCount||0)>0){const n=e.slice(0,3).map(n=>n.name).join(", "),i=e.length>3?` +${e.length-3}`:"";t(n?`Locations detected: ${n}${i}`:r.message)}i(r?.message||"Location suggestions submitted.")}else i("Location suggestions skipped.");ci(`Total suggestion pass: ${Math.round(y()-r)}ms`)}catch(n){console.error("Unable to detect runtime suggestions.",n);f({lastError:e(n)})}finally{stt()}}},nh=(n=2500)=>{if(uo(),!u||ht||sf){sf&&(hf=!0);return}ke=window.setTimeout(()=>{void htt()},n)},vl=async()=>{const i=ni(),n=h(),r=ut(w,"chapter");if(vt({projectId:i?.projectId,projectTitle:i?.title,bookId:n?.bookId,bookTitle:n?dr(n):undefined,detectedChapter:w,detectedScene:rt,requestChapter:r,requestScene:"",result:"Not run",chapterId:null,sceneId:null}),!n)return it("Not detected"),t("Select a PlotDirector book first."),!1;if(!w)return it("Not detected"),t("No chapter detected in Word. Use Heading 1 for chapters."),!1;it("Not detected");try{const f=await bv(n.bookId),i=b?f.find(n=>n.chapterId===b):null,e=r.toLocaleLowerCase(),o=e?f.find(n=>ut(n.title,"chapter").toLocaleLowerCase()===e):null,u=i||o;return u?(ge(),kr(null,u.chapterId,i?"Chapter anchor":"Chapter title"),rr=i?"ChapterID Anchor":"Title Match",af("Chapter linked to PlotDirector"),t("No scene detected in Word. Use Heading 2 for scenes."),vt({result:"Chapter matched",chapterId:u.chapterId,sceneId:null}),!0):(kr(null),af("Chapter not found in PlotDirector."),t("Chapter not found in PlotDirector."),vt({result:"Chapter not matched",chapterId:null,sceneId:null}),es("Chapter not found in PlotDirector."),!1)}catch(u){return it("Not found in PlotDirector"),f({lastError:e(u)}),t("Unable to load PlotDirector chapter data."),!1}},fo=async()=>{const r=ni(),n=h(),u=ut(w,"chapter"),i=ut(rt,"scene");if(vt({projectId:r?.projectId,projectTitle:r?.title,bookId:n?.bookId,bookTitle:n?dr(n):undefined,detectedChapter:w,detectedScene:rt,requestChapter:u,requestScene:i,result:"Not run",chapterId:null,sceneId:null}),wc({anchorType:tt?"SceneID Anchor":b?"ChapterID Anchor":"Title Match",storedSceneId:tt,storedChapterId:b,resolutionMethod:"-"}),!n)return it("Not detected"),t("Select a PlotDirector book first."),vt({result:"Error"}),!1;if(!w||!rt)return it("Not detected"),t("No scene detected in Word. Use Heading 2 for scenes."),vt({result:"Error"}),!1;tu&&(tu.disabled=!0,tu.textContent="Refreshing...");const s=()=>{tu&&(tu.disabled=!1,tu.textContent="Refresh PlotDirector Scene")};it("Not detected");t("Resolving PlotDirector scene...");try{if(tt){const i=await gk(n.bookId,(n,t)=>t.sceneId===tt);if(i)return vt({result:"Matched",chapterId:i.chapter.chapterId,sceneId:i.scene.sceneId}),await ls(n.bookId,i.scene.sceneId,i.chapter.chapterId,"Anchor","SceneID Anchor"),!0;dt=!0;rr="SceneID Anchor";af("Not found in PlotDirector");t("Stored PlotDirector ID is invalid.");vt({result:"Error",chapterId:b,sceneId:tt});wc({anchorType:"SceneID Anchor",storedSceneId:tt,storedChapterId:b,resolutionMethod:"Anchor"})}if(b){const u=await cs(n.bookId),t=u.find(n=>n.chapterId===b),r=t?(Array.isArray(t.scenes)?t.scenes:[]).find(n=>ut(n.title,"scene").toLocaleLowerCase()===i.toLocaleLowerCase()):null;if(r)return vt({result:"Matched",chapterId:t.chapterId,sceneId:r.sceneId}),await ls(n.bookId,r.sceneId,t.chapterId,"Anchor","ChapterID Anchor"),!0;t&&!tt?(rr="ChapterID Anchor",kr(null,b,"Chapter anchor"),vt({result:"Chapter matched",chapterId:b,sceneId:null})):tt||(rr="ChapterID Anchor",vt({result:"Chapter anchor not found",chapterId:b,sceneId:null}))}const r=await st(`/api/word-companion/books/${n.bookId}/resolve-scene`,{chapterTitle:u,sceneTitle:i});if(!r?.matched||!r.sceneId){const u=dt,f=Number.parseInt(r?.chapterId||"",10),e=Number.isInteger(o)&&o>0?o:null,n=Number.isInteger(f)&&f>0?f:e,i=!!r?.chapterMatched||!!n;return it("Not found in PlotDirector"),dt=u,i&&(kr(null,n,n===e?"Chapter anchor":"Chapter title"),af("Scene not found in PlotDirector.")),t(u?"Stored PlotDirector ID is invalid.":i?"Scene not found in PlotDirector.":"This chapter does not exist in PlotDirector. Create or link the chapter first."),vt({result:"Not matched",chapterId:i?n:r?.chapterId,sceneId:r?.sceneId}),u||(i?(ge(),no("Scene not found in PlotDirector.")):(os(),es("Chapter not found in PlotDirector."))),s(),!1}const f=dt;return vt({result:f?"Matched by title fallback":"Matched",chapterId:r.chapterId,sceneId:r.sceneId}),await ls(n.bookId,r.sceneId,r.chapterId,f?"Title fallback":"Title","Title Match"),f&&(dt=!0,t("Stored PlotDirector ID is invalid."),fr()),!0}catch(c){const n=dt;return f({lastError:e(c)}),it("Not found in PlotDirector"),dt=n,t(n?"Stored PlotDirector ID is invalid.":"Unable to load PlotDirector scene data."),vt({result:"Error"}),!1}finally{s()}},nit=n=>{const t=n.currentChapter?.title||"",i=n.currentScene?.title||"",r=n.currentChapter?.anchorId||null,u=n.currentScene?.anchorId||null;return ut(t,"chapter")!==ut(w,"chapter")||ut(i,"scene")!==ut(rt,"scene")||r!==b||u!==tt},ctt=async()=>{if(ru=null,ts){ff=!0;i("Runtime update deferred; update already running.");return}if(!rf||!h()){br=!1;i("Runtime update skipped.");return}if(!kt||!nt||!window.Word||typeof window.Word.run!="function"){br=!1;k("Manual");t("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene.");return}if(!l){br=!1;k("Manual");t("Document structure is not cached yet. Use Refresh Current Scene.");return}const n=Date.now()-hv;if(br&&n{ru&&window.clearTimeout(ru),ru=window.setTimeout(ctt,Math.max(0,n)),k("Waiting for typing to pause"),i("Runtime update scheduled.")},ltt=(n="selection",t=pe)=>{if(br=!0,hv=Date.now(),ve+=1,ye=null,yf(),uo(),ef&&(be=!0),sf&&(hf=!0),i(`${n} changed.`),ts){ff=!0;k("Waiting for typing to pause");return}yl(t)},yp=()=>{ru&&(window.clearTimeout(ru),ru=null),br=!1,ff=!1},pp=()=>{ltt("Selection")},att=()=>{if(kh&&window.Office?.context?.document&&typeof window.Office.context.document.removeHandlerAsync=="function"&&window.Office?.EventType?.DocumentSelectionChanged)try{window.Office.context.document.removeHandlerAsync(window.Office.EventType.DocumentSelectionChanged,{handler:pp});kh=!1}catch(n){console.warn("Unable to remove Word selection tracking handler.",n)}},vtt=()=>{if(rf){if(!window.Office?.context?.document||typeof window.Office.context.document.addHandlerAsync!="function"||!window.Office?.EventType?.DocumentSelectionChanged){k("Manual");t("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene.");return}try{window.Office.context.document.addHandlerAsync(window.Office.EventType.DocumentSelectionChanged,pp,n=>{n?.status===window.Office.AsyncResultStatus.Succeeded?(kh=!0,k("Live")):(k("Manual"),t("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene."))});window.addEventListener("beforeunload",att)}catch(n){console.warn("Unable to register Word selection tracking handler.",n);k("Manual");t("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene.")}}},ytt=async()=>{if(!h()){t("Select a PlotDirector book first.");it("Not detected");return}yp();yv(!0);k("Updating...");try{await Promise.all([hl(!0),cl(!0),ll(!0)]);const n=await dy();if(!n)return;if(li(!1),!rt){await vl();k("Current scene updated");return}await fo();k("Current scene updated")}finally{yv(!1)}},wu=async(n,t)=>{if(l=null,su(),!n){ii=[];c(ot,"Select a project first");c(g,"Select a project first");lf("");v(s.bookId,null);pi();it("Not detected");pf();return}c(ot,"Loading books...");lf("");it("Not detected");pf("Select a book to analyse manuscript structure.");try{const r=await at(`/api/word-companion/projects/${n}/books`);if(ii=Array.isArray(r.books)?r.books:[],ii.length===0){c(ot,"No books available.");c(g,"No books available.");lf("No books available.");v(s.bookId,null);pi();lt("No books available.");return}vs(ot,"Choose a book",ii.map(n=>({...n,displayTitle:dr(n)})),"bookId","displayTitle");const i=ii.find(n=>n.bookId===t);i&&ot?(ot.value=String(i.bookId),v(s.bookId,i.bookId)):v(s.bookId,null);fd(i?.bookId||t);pi();it(h()?"Not detected":"Not detected");pf(h()?"Ready to analyse manuscript structure.":"Select a book to analyse manuscript structure.");lt(au()?"Ready to scan manuscript structure.":"Select a Book to begin.")}catch{ii=[];c(ot,"Unable to load books.");c(g,"Unable to load books.");lf("Unable to load books.");v(s.bookId,null);pi();it("Not detected");pf("Unable to load books.");lt("Unable to load books.")}},ptt=async()=>{c(p,"Loading projects...");c(ot,"Select a project first");c(et,"Loading projects...");c(g,"Select a project first");pc("");lf("");try{const t=await at("/api/word-companion/projects");if(ir=Array.isArray(t.projects)?t.projects:[],ir.length===0){c(p,"No projects available.");c(et,"No projects available.");pc("No projects available.");v(s.projectId,null);v(s.bookId,null);pi();lt("No projects available.");return}vs(p,"Choose a project",ir,"projectId","title");il();const i=as(s.projectId),n=ir.find(n=>n.projectId===i);n&&p?(p.value=String(n.projectId),et&&(et.value=String(n.projectId)),v(s.projectId,n.projectId),await wu(n.projectId,as(s.bookId))):(v(s.projectId,null),v(s.bookId,null),pi(),lt("Select a Project and Book to begin."))}catch{ir=[];ii=[];c(p,"Unable to connect to PlotDirector.");c(ot,"Select a project first");c(et,"Unable to connect to PlotDirector.");c(g,"Select a project first");de("Unable to connect to PlotDirector.");pc("Unable to connect to PlotDirector.");v(s.projectId,null);v(s.bookId,null);pi();lt("Unable to connect to PlotDirector.")}};for(const n of ih)n.addEventListener("click",()=>av(n.dataset.tabButton));vv();p?.addEventListener("change",async()=>{const n=Number.parseInt(p.value||"",10);wf();et&&Number.isInteger(n)&&n>0&&(et.value=String(n));v(s.projectId,Number.isInteger(n)&&n>0?n:null);v(s.bookId,null);it("Not detected");pf();lt("Select a Book to begin.");await wu(n,null);bf()});ot?.addEventListener("change",()=>{const n=Number.parseInt(ot.value||"",10);wf();v(s.bookId,Number.isInteger(n)&&n>0?n:null);g&&Number.isInteger(n)&&n>0&&(g.value=String(n));pi();it("Not detected");pf(h()?"Ready to analyse manuscript structure.":"Select a book to analyse manuscript structure.");lt(au()?"Ready to scan manuscript structure.":"Select a Book to begin.");bf()});et?.addEventListener("change",async()=>{const n=Number.parseInt(et.value||"",10);wf();p&&Number.isInteger(n)&&n>0&&(p.value=String(n));v(s.projectId,Number.isInteger(n)&&n>0?n:null);v(s.bookId,null);lt("Select a Book to begin.");await wu(n,null);bf()});g?.addEventListener("change",()=>{const n=Number.parseInt(g.value||"",10);wf();ot&&Number.isInteger(n)&&n>0&&(ot.value=String(n));v(s.bookId,Number.isInteger(n)&&n>0?n:null);pi();lt(au()?"Ready to scan manuscript structure.":"Select a Book to begin.");bf()});ne?.addEventListener("input",ft);eo?.addEventListener("click",async()=>{const n=gr(),t=String(ne?.value||"").trim();if(!n||!t){ft();return}eo.disabled=!0;ui("Creating Book...");try{const i=await st(`/api/word-companion/projects/${n.projectId}/books`,{title:t});ne&&(ne.value="");p&&(p.value=String(n.projectId));await wu(n.projectId,i.bookId);g&&(g.value=String(i.bookId));ui("Ready to scan manuscript structure.")}catch(i){console.error("Unable to create Book from Word Companion.",i);ui(`Unable to create Book: ${e(i)}`)}finally{ft()}});sr?.addEventListener("click",kg);uh?.addEventListener("click",nn);oo?.addEventListener("click",tn);bl?.addEventListener("click",()=>ks("Character creation skipped."));kl?.addEventListener("click",()=>ks());nw?.addEventListener("click",()=>vu(!1));iw?.addEventListener("click",()=>lt("Import cancelled."));nu?.addEventListener("click",dy);cr?.addEventListener("click",gy);fh?.addEventListener("click",wg);hb?.addEventListener("click",()=>el(!0));cb?.addEventListener("click",()=>el(!1));tr?.addEventListener("cancel",n=>{n.preventDefault(),el(!1)});tu?.addEventListener("click",fo);so?.addEventListener("click",ytt);ar?.addEventListener("click",()=>vp({source:"manual"}));di?.addEventListener("click",hn);ai?.addEventListener("click",ln);lr?.addEventListener("click",fn);yt?.addEventListener("click",vn);pt?.addEventListener("click",yn);lo?.addEventListener("input",sp);si?.addEventListener("click",pn);ub?.addEventListener("click",hp);ob?.addEventListener("click",()=>ol(!0));sb?.addEventListener("click",()=>ol(!1));nr?.addEventListener("cancel",n=>{n.preventDefault(),ol(!1)});wt?.addEventListener("click",bn);bt?.addEventListener("click",kn);ho?.addEventListener("input",lp);oi?.addEventListener("click",dn);gw?.addEventListener("click",ap);ib?.addEventListener("click",()=>sl(!0));rb?.addEventListener("click",()=>sl(!1));gi?.addEventListener("cancel",n=>{n.preventDefault(),sl(!1)});iu?.addEventListener("click",bg);const wtt=rf?ptt():Promise.resolve();if(rf||(c(p,"Please sign in to PlotDirector."),c(ot,"Please sign in to PlotDirector."),c(et,"Please sign in to PlotDirector."),c(g,"Please sign in to PlotDirector."),pi()),!window.Office||typeof window.Office.onReady!="function"){yc("Word Companion ready");t("Word document access is unavailable.");k("Manual");f({host:"Unavailable",platform:"Unavailable",ready:"No",wordApi:"No"});iy().catch(n=>{console.debug("Word Companion presence unavailable.",n)});return}gv().then(async()=>{yc("Word Companion ready"),await wtt,await un(),await iy(),nt?vtt():(t("Word document access is unavailable."),k("Manual"))}).catch(n=>{console.error("Office readiness check failed.",n),de("Unable to connect to PlotDirector."),yc("Word Companion unavailable"),t("Word document access is unavailable."),f({ready:"No",wordApi:"No",lastScan:"Failure"})})})(); \ No newline at end of file diff --git a/PlotLine/wwwroot/js/word-companion-presence.js b/PlotLine/wwwroot/js/word-companion-presence.js index bba30e3..2094cd7 100644 --- a/PlotLine/wwwroot/js/word-companion-presence.js +++ b/PlotLine/wwwroot/js/word-companion-presence.js @@ -149,7 +149,7 @@ connection.on("OnboardingScanCompleted", applyScanState); connection.on("OnboardingScanFailed", applyScanState); connection.on("OnboardingBuildProgress", (state) => { - const message = field(state, "message", "Message") || "Building project structure..."; + const message = field(state, "message", "Message") || "Preparing chapters..."; const percent = field(state, "percentComplete", "PercentComplete"); const safePercent = Math.max(0, Math.min(100, Number.parseInt(percent || "0", 10) || 0)); document.querySelectorAll("[data-onboarding-build-panel]").forEach((node) => { @@ -207,7 +207,7 @@ node.classList.add("is-running"); }); document.querySelectorAll("[data-onboarding-build-message]").forEach((node) => { - node.textContent = "Creating the approved project structure..."; + node.textContent = "Preparing the approved chapters..."; }); document.querySelectorAll("[data-onboarding-build-percent]").forEach((node) => { node.textContent = "0%"; @@ -225,7 +225,7 @@ node.classList.remove("is-running"); }); document.querySelectorAll("[data-onboarding-build-message]").forEach((node) => { - node.textContent = error?.message || "The project structure could not be built. Check the review and try again."; + node.textContent = error?.message || "The approved chapters could not be prepared. Check the review and try again."; }); document.querySelectorAll("[data-onboarding-build-percent]").forEach((node) => { node.textContent = "";