diff --git a/PlotLine/Models/ManuscriptScanModels.cs b/PlotLine/Models/ManuscriptScanModels.cs index a4f03df..4f3764b 100644 --- a/PlotLine/Models/ManuscriptScanModels.cs +++ b/PlotLine/Models/ManuscriptScanModels.cs @@ -88,6 +88,10 @@ public sealed class ManuscriptScanCharacterCandidatePreview public string TemporaryCharacterKey { get; init; } = string.Empty; public string Name { get; init; } = string.Empty; public int MentionCount { get; init; } + public int QualityScore { get; init; } + public string Category { get; init; } = "PossibleCharacter"; + public string? Reason { get; init; } + public bool IsExistingCharacterMatch { get; init; } public string? SuggestedImportance { get; init; } } diff --git a/PlotLine/Models/WordCompanionApiModels.cs b/PlotLine/Models/WordCompanionApiModels.cs index 835a55a..d00bd55 100644 --- a/PlotLine/Models/WordCompanionApiModels.cs +++ b/PlotLine/Models/WordCompanionApiModels.cs @@ -485,13 +485,17 @@ public sealed class WordCompanionCharacterCandidateDto public string Text { get; init; } = string.Empty; public int MentionCount { get; init; } public string Confidence { get; init; } = "Medium"; + public int QualityScore { get; init; } + public string Category { get; init; } = "PossibleCharacter"; public string Reason { get; init; } = string.Empty; + public bool IsExistingCharacterMatch { get; init; } } public sealed class WordCompanionDiscoverCharactersRequest { public int ProjectId { get; set; } public string? DocumentText { get; set; } + public bool IncludeExcluded { get; set; } } public sealed class WordCompanionDiscoverCharactersResponse diff --git a/PlotLine/Services/OnboardingService.cs b/PlotLine/Services/OnboardingService.cs index f1506ab..4729773 100644 --- a/PlotLine/Services/OnboardingService.cs +++ b/PlotLine/Services/OnboardingService.cs @@ -153,7 +153,9 @@ public sealed class OnboardingService( }) .ToList(), CharacterCandidates = preview.CharacterCandidates - .OrderByDescending(candidate => candidate.MentionCount) + .OrderByDescending(candidate => CandidateCategorySortValue(candidate.Category)) + .ThenByDescending(candidate => candidate.QualityScore) + .ThenByDescending(candidate => candidate.MentionCount) .ThenBy(candidate => candidate.Name) .ToList() }; @@ -507,6 +509,12 @@ public sealed class OnboardingService( PreviewID = state.PreviewID }; + private static int CandidateCategorySortValue(string? category) + => string.Equals(category, "ProbableCharacter", StringComparison.OrdinalIgnoreCase) ? 4 + : string.Equals(category, "PossibleCharacter", StringComparison.OrdinalIgnoreCase) ? 3 + : string.Equals(category, "RelationshipTitle", StringComparison.OrdinalIgnoreCase) ? 2 + : 1; + private static string? CleanOptional(string? value) { var cleaned = Clean(value); diff --git a/PlotLine/Services/WordCompanionService.cs b/PlotLine/Services/WordCompanionService.cs index a9ec136..d798957 100644 --- a/PlotLine/Services/WordCompanionService.cs +++ b/PlotLine/Services/WordCompanionService.cs @@ -42,26 +42,42 @@ public sealed class WordCompanionService( IManuscriptDocumentRepository manuscriptDocuments, ICurrentUserService currentUser) : IWordCompanionService { - private const int CharacterDiscoveryMinimumMentions = 5; + private const int CharacterDiscoveryMinimumMentions = 2; private const int CharacterDiscoverySuggestionLimit = 50; + private const int CharacterDiscoveryExcludedLimit = 30; private static readonly Regex CharacterCandidateRegex = new( - @"(? CharacterDiscoveryStopWords = new(StringComparer.OrdinalIgnoreCase) { - "A", "An", "And", "As", "At", "But", "By", "For", "From", "He", "Her", "His", "I", "If", "In", "Into", - "It", "Its", "Of", "On", "Or", "Our", "She", "So", "That", "The", "Their", "Then", "There", "They", - "This", "To", "We", "When", "Where", "Who", "With", "You", "Your", + "A", "An", "As", "At", "By", "He", "Her", "Hers", "Him", "His", "I", "If", "In", "It", "Its", "Me", + "Mine", "My", "Of", "On", "Our", "Ours", "She", "The", "Their", "Theirs", "They", "Them", "To", "Us", + "We", "You", "Your", "Yours", + "What", "When", "Where", "Why", "Who", "How", "Which", "Whose", "Whom", + "Yes", "No", "Yeah", "Yep", "Nope", "Okay", "OK", "Ok", "Well", "Right", + "There", "Here", "This", "That", "These", "Those", "Then", "Now", + "Not", "Just", "Only", "Very", "Really", "Maybe", "Perhaps", "Probably", + "Are", "Is", "Was", "Were", "Be", "Been", "Being", "Am", + "Can", "Could", "Should", "Would", "Will", "Won", "Shall", "May", "Might", "Must", + "For", "From", "With", "Without", "Into", "Onto", "Over", "Under", "Between", "Before", "After", + "During", "Through", "Around", "About", + "And", "But", "Or", "So", "Because", "Though", "Although", + "One", "Two", "Three", "First", "Second", "Third", + "Come", "Go", "Get", "Got", "Let", "Look", "See", "Tell", "Ask", "Asked", "Said", "Say", "Think", + "Thought", "Know", "Knew", "Want", "Wanted", "Need", "Needed", + "Please", "Thank", "Thanks", "Sorry", "Hello", "Hi", "Hey", "Goodbye", "Bye", + "Chapter", "Scene", "Book", "Part", "Page", "Prologue", "Epilogue", "Volume", "Act", "Christmas", "Easter", "Morning", "Afternoon", "Evening", "Night", "Today", "Tomorrow", "Yesterday", - "Chapter", "Scene", "Part", "Prologue", "Epilogue", "Book", "Volume", "Act", - "Mum", "Dad", "Mother", "Father", "Grandma", "Grandad", "Nan", "Nana", "Granny", - "Yes", "No", "Okay", "Ok", "Hello", "Goodbye", - "What", "Not", "Just", "Yeah", "How", "Well", "Are", "Like", "One", "Can", "Thank", "Why", "Come", - "Let", "Maybe", "Please", "God", "Something", "After", "Did", "Even", "All", "Nothing", "Have", - "Don", "Won", "Could", "Would", "Should", "Do", "Does", "Had", "Has", "Was", "Were", "Is", "Am", - "Right", "Really", "Now", "Look", "Listen", "Sorry", "Thanks", "Fine", "Great", "Sure", "Course", - "Thing", "Things", "Everyone", "Everything", "Anything", "Someone", "Nobody", "People", - "Still", "Err", "Er", "Erm", "Alright", "Allright", "Alrite", "New", "Somewhere", "Coke" + "Tonight", "Tomorrow", "Yesterday", "God", "Something", "Anything", "Nothing", "Everything", "Everyone", + "Somebody", "Someone", "Anybody", "Nobody", "People", "Person", "Man", "Woman", "Boy", "Girl", + "Did", "Do", "Does", "Done", "Had", "Has", "Have", "Having", "Even", "All", "Fine", "Great", "Sure", + "Course", "Thing", "Things", "Still", "Err", "Er", "Erm", "Alright", "Allright", "Alrite", "New", + "Somewhere", "Coke", "Like", "Don", "Isn" + }; + private static readonly HashSet CharacterDiscoveryRelationshipTitles = new(StringComparer.OrdinalIgnoreCase) + { + "Mum", "Dad", "Mother", "Father", "Gran", "Grandma", "Grandad", "Nan", "Nana", "Granny", "Brother", + "Sister", "Aunt", "Auntie", "Uncle", "Cousin" }; private static readonly HashSet CharacterDiscoveryTemporalWords = new(StringComparer.OrdinalIgnoreCase) { @@ -83,6 +99,12 @@ public sealed class WordCompanionService( "said", "asked", "replied", "answered", "whispered", "shouted", "called", "muttered", "cried", "snapped", "sighed", "laughed", "continued", "began", "told", "yelled" }; + private static readonly HashSet CharacterDiscoveryHumanActionVerbs = new(StringComparer.OrdinalIgnoreCase) + { + "walked", "turned", "smiled", "looked", "stared", "nodded", "sat", "stood", "stepped", "ran", "came", + "went", "opened", "closed", "held", "took", "put", "gave", "grabbed", "shrugged", "frowned", "laughed", + "sighed", "watched", "waited", "leaned", "knelt", "waved", "pointed", "followed", "paused", "breathed" + }; private static readonly HashSet CharacterDiscoveryLocationIndicators = new(StringComparer.OrdinalIgnoreCase) { "Road", "Lane", "Street", "Close", "Avenue", "Drive", "Way", "Hill", "Hospital", "Inn", "Church", "School", @@ -512,7 +534,7 @@ public sealed class WordCompanionService( return null; } - var candidates = DiscoverCharacterCandidates(request.DocumentText ?? string.Empty); + var candidates = DiscoverCharacterCandidates(request.DocumentText ?? string.Empty, request.IncludeExcluded); return new WordCompanionDiscoverCharactersResponse { ProjectId = request.ProjectId, @@ -551,18 +573,19 @@ public sealed class WordCompanionService( private static bool HasInvalidIds(IEnumerable ids) => ids.Any(id => id <= 0); - private static IReadOnlyList DiscoverCharacterCandidates(string manuscriptText) + private static IReadOnlyList DiscoverCharacterCandidates(string manuscriptText, bool includeExcluded = false) { if (string.IsNullOrWhiteSpace(manuscriptText)) { return []; } + manuscriptText = NormalizeApostrophes(manuscriptText); var candidates = new Dictionary(StringComparer.Ordinal); foreach (Match match in CharacterCandidateRegex.Matches(manuscriptText)) { var candidate = NormalizeCandidateName(match.Value); - if (!IsLikelyCharacterCandidate(candidate)) + if (string.IsNullOrWhiteSpace(candidate)) { continue; } @@ -583,50 +606,71 @@ public sealed class WordCompanionService( { evidence.DialogueEvidenceCount += 1; } + + if (HasPossessiveEvidence(manuscriptText, match.Index, match.Length)) + { + evidence.PossessiveEvidenceCount += 1; + } + + if (HasHumanActionEvidence(manuscriptText, match.Index, match.Length)) + { + evidence.HumanActionEvidenceCount += 1; + } } - return candidates.Values - .Where(item => item.MentionCount >= CharacterDiscoveryMinimumMentions) + var builtCandidates = candidates.Values .Select(BuildCandidateDto) .Where(item => item is not null) .Select(item => item!) - .OrderByDescending(item => ConfidenceSortValue(item.Confidence)) + .ToList(); + + var visible = builtCandidates + .Where(item => !string.Equals(item.Category, "Excluded", StringComparison.OrdinalIgnoreCase)) + .OrderByDescending(item => CategorySortValue(item.Category)) + .ThenByDescending(item => item.QualityScore) + .ThenByDescending(item => ConfidenceSortValue(item.Confidence)) .ThenByDescending(item => item.MentionCount) .ThenBy(item => item.Text, StringComparer.OrdinalIgnoreCase) .Take(CharacterDiscoverySuggestionLimit) .ToList(); - } - private static bool IsLikelyCharacterCandidate(string candidate) - { - if (string.IsNullOrWhiteSpace(candidate) || candidate.Length > 200) + if (includeExcluded) { - return false; + visible.AddRange(builtCandidates + .Where(item => string.Equals(item.Category, "Excluded", StringComparison.OrdinalIgnoreCase)) + .OrderByDescending(item => item.MentionCount) + .ThenBy(item => item.Text, StringComparer.OrdinalIgnoreCase) + .Take(CharacterDiscoveryExcludedLimit)); } - var words = candidate.Split(' ', StringSplitOptions.RemoveEmptyEntries); - if (words.Length == 0 || words.Any(word => CharacterDiscoveryStopWords.Contains(TrimHonorificPunctuation(word)))) - { - return false; - } - - var first = TrimHonorificPunctuation(words[0]); - return words.Length > 1 || first.Length > 2; + return visible; } private static WordCompanionCharacterCandidateDto? BuildCandidateDto(CharacterCandidateEvidence evidence) { - var score = 0; + if (string.IsNullOrWhiteSpace(evidence.Text) || evidence.Text.Length > 200) + { + return null; + } + var words = evidence.Text.Split(' ', StringSplitOptions.RemoveEmptyEntries); var first = words.Length > 0 ? TrimHonorificPunctuation(words[0]) : string.Empty; + if (words.Length == 0) + { + return null; + } + var hasHonorific = words.Length > 1 && CharacterDiscoveryHonorifics.Contains(first); var hasMultiWordName = words.Length > 1; var hasNameLikeWord = words.Any(word => CharacterDiscoveryNameLikeWords.Contains(TrimHonorificPunctuation(word))); var hasTemporalWord = words.Any(word => CharacterDiscoveryTemporalWords.Contains(TrimHonorificPunctuation(word))); var hasLocationIndicator = words.Any(word => CharacterDiscoveryLocationIndicators.Contains(TrimHonorificPunctuation(word))); + var hasStopWord = words.Any(word => CharacterDiscoveryStopWords.Contains(TrimHonorificPunctuation(word))); + var isRelationshipTitle = words.Length == 1 && CharacterDiscoveryRelationshipTitles.Contains(first); var nonSentenceStartMentions = evidence.MentionCount - evidence.SentenceStartMentions; var mostlySentenceStart = evidence.MentionCount > 0 && evidence.SentenceStartMentions >= Math.Ceiling(evidence.MentionCount * 0.8m); + var score = 0; if (evidence.MentionCount >= CharacterDiscoveryMinimumMentions) { score += 1; @@ -635,6 +679,10 @@ public sealed class WordCompanionService( { score += 1; } + if (evidence.MentionCount >= 25) + { + score += 1; + } if (nonSentenceStartMentions > 0) { score += 2; @@ -651,6 +699,14 @@ public sealed class WordCompanionService( { score += 4; } + if (evidence.PossessiveEvidenceCount > 0) + { + score += 2; + } + if (evidence.HumanActionEvidenceCount > 0) + { + score += 2; + } if (hasNameLikeWord) { score += 3; @@ -667,43 +723,107 @@ public sealed class WordCompanionService( { score -= 2; } - if (!hasMultiWordName && evidence.DialogueEvidenceCount == 0 && nonSentenceStartMentions == 0) + if (!hasMultiWordName + && evidence.DialogueEvidenceCount == 0 + && evidence.PossessiveEvidenceCount == 0 + && evidence.HumanActionEvidenceCount == 0 + && nonSentenceStartMentions == 0) { score -= 1; } - - if (score < 0 && !hasLocationIndicator) + if (evidence.MentionCount < CharacterDiscoveryMinimumMentions) { - return null; + score -= 2; + } + if (first.Length <= 2 && !hasHonorific) + { + score -= 3; } + var category = CandidateCategory(score, hasStopWord, isRelationshipTitle, hasTemporalWord, hasLocationIndicator, evidence); var confidence = score >= 4 ? "High" : score >= 2 ? "Medium" : "Low"; if (hasLocationIndicator && evidence.DialogueEvidenceCount == 0 && !hasHonorific) { confidence = score >= 4 ? "Medium" : "Low"; } + if (string.Equals(category, "Excluded", StringComparison.Ordinal)) + { + confidence = "Low"; + } return new WordCompanionCharacterCandidateDto { Text = evidence.Text, MentionCount = evidence.MentionCount, Confidence = confidence, - Reason = BuildCandidateReason(evidence, hasHonorific, hasMultiWordName, hasTemporalWord, hasLocationIndicator, mostlySentenceStart) + QualityScore = score, + Category = category, + Reason = BuildCandidateReason(evidence, hasStopWord, isRelationshipTitle, hasHonorific, hasMultiWordName, hasTemporalWord, hasLocationIndicator, mostlySentenceStart) }; } + private static string CandidateCategory( + int score, + bool hasStopWord, + bool isRelationshipTitle, + bool hasTemporalWord, + bool hasLocationIndicator, + CharacterCandidateEvidence evidence) + { + if (hasStopWord) + { + return "Excluded"; + } + if (isRelationshipTitle) + { + return "RelationshipTitle"; + } + if (hasTemporalWord && evidence.DialogueEvidenceCount == 0 && evidence.PossessiveEvidenceCount == 0) + { + return "Excluded"; + } + if (hasLocationIndicator && score < 4) + { + return "Excluded"; + } + if (evidence.MentionCount < CharacterDiscoveryMinimumMentions && evidence.DialogueEvidenceCount == 0) + { + return "Excluded"; + } + + return score >= 4 ? "ProbableCharacter" : "PossibleCharacter"; + } + private static string BuildCandidateReason( CharacterCandidateEvidence evidence, + bool hasStopWord, + bool isRelationshipTitle, bool hasHonorific, bool hasMultiWordName, bool hasTemporalWord, bool hasLocationIndicator, bool mostlySentenceStart) { + if (hasStopWord) + { + return "Excluded word"; + } + if (isRelationshipTitle) + { + return "Relationship title"; + } if (evidence.DialogueEvidenceCount > 0) { return "Dialogue evidence"; } + if (evidence.PossessiveEvidenceCount > 0) + { + return "Possessive use"; + } + if (evidence.HumanActionEvidenceCount > 0) + { + return "Human action nearby"; + } if (hasHonorific) { return "Title or honorific"; @@ -735,6 +855,14 @@ public sealed class WordCompanionService( : 1; } + private static int CategorySortValue(string category) + { + return string.Equals(category, "ProbableCharacter", StringComparison.OrdinalIgnoreCase) ? 4 + : string.Equals(category, "PossibleCharacter", StringComparison.OrdinalIgnoreCase) ? 3 + : string.Equals(category, "RelationshipTitle", StringComparison.OrdinalIgnoreCase) ? 2 + : 1; + } + private static bool AppearsAtSentenceStart(string manuscriptText, int matchIndex) { for (var i = matchIndex - 1; i >= 0; i--) @@ -768,6 +896,22 @@ public sealed class WordCompanionService( return HasTagAfterCandidate(after) || HasTagBeforeCandidate(before); } + private static bool HasPossessiveEvidence(string manuscriptText, int matchIndex, int matchLength) + { + var afterStart = matchIndex + matchLength; + return afterStart + 1 < manuscriptText.Length + && manuscriptText[afterStart] == '\'' + && char.ToLowerInvariant(manuscriptText[afterStart + 1]) == 's'; + } + + private static bool HasHumanActionEvidence(string manuscriptText, int matchIndex, int matchLength) + { + var afterStart = matchIndex + matchLength; + var after = manuscriptText[afterStart..Math.Min(manuscriptText.Length, afterStart + 36)]; + var match = Regex.Match(after, @"^\s+(?[a-z]+)\b", RegexOptions.IgnoreCase); + return match.Success && CharacterDiscoveryHumanActionVerbs.Contains(match.Groups["verb"].Value); + } + private static bool HasTagAfterCandidate(string textAfterCandidate) { var match = Regex.Match(textAfterCandidate, @"^\s*,?\s*(?[a-z]+)\b", RegexOptions.IgnoreCase); @@ -782,7 +926,12 @@ public sealed class WordCompanionService( private static string NormalizeCandidateName(string candidate) { - return Regex.Replace(candidate ?? string.Empty, @"\s+", " ").Trim().Trim(',', '.', ';', ':', '!', '?', '"', '\'', '’'); + return Regex.Replace(NormalizeApostrophes(candidate), @"\s+", " ").Trim().Trim(',', '.', ';', ':', '!', '?', '"', '\''); + } + + private static string NormalizeApostrophes(string? value) + { + return (value ?? string.Empty).Replace('’', '\'').Replace('‘', '\''); } private static string TrimHonorificPunctuation(string word) @@ -796,6 +945,8 @@ public sealed class WordCompanionService( public int MentionCount { get; set; } public int SentenceStartMentions { get; set; } public int DialogueEvidenceCount { get; set; } + public int PossessiveEvidenceCount { get; set; } + public int HumanActionEvidenceCount { get; set; } } private static WordCompanionManuscriptDocumentDto ToDto(ManuscriptDocumentModel document) => new() diff --git a/PlotLine/Views/Onboarding/ScanReview.cshtml b/PlotLine/Views/Onboarding/ScanReview.cshtml index f67e015..de1fddf 100644 --- a/PlotLine/Views/Onboarding/ScanReview.cshtml +++ b/PlotLine/Views/Onboarding/ScanReview.cshtml @@ -1,6 +1,18 @@ @model ManuscriptScanReviewViewModel @{ ViewData["Title"] = "Review manuscript scan"; + var probableCharacters = Model.CharacterCandidates + .Where(candidate => string.Equals(candidate.Category, "ProbableCharacter", StringComparison.OrdinalIgnoreCase)) + .ToList(); + var possibleCharacters = Model.CharacterCandidates + .Where(candidate => string.Equals(candidate.Category, "PossibleCharacter", StringComparison.OrdinalIgnoreCase)) + .ToList(); + var relationshipTitles = Model.CharacterCandidates + .Where(candidate => string.Equals(candidate.Category, "RelationshipTitle", StringComparison.OrdinalIgnoreCase)) + .ToList(); + var excludedCharacters = Model.CharacterCandidates + .Where(candidate => string.Equals(candidate.Category, "Excluded", StringComparison.OrdinalIgnoreCase)) + .ToList(); }
@@ -76,21 +88,81 @@
diff --git a/PlotLine/wwwroot/css/onboarding.css b/PlotLine/wwwroot/css/onboarding.css index 4e0f2df..93843cb 100644 --- a/PlotLine/wwwroot/css/onboarding.css +++ b/PlotLine/wwwroot/css/onboarding.css @@ -360,7 +360,8 @@ } .onboarding-review-chapter ol, -.onboarding-character-candidates { +.onboarding-character-candidates, +.onboarding-character-groups { display: grid; gap: .65rem; margin: 0; @@ -393,6 +394,31 @@ gap: .75rem; } +.onboarding-character-groups section { + display: grid; + gap: .5rem; +} + +.onboarding-character-groups h3 { + margin: 0; + font-size: 1rem; +} + +.onboarding-excluded-candidates { + border-top: 1px solid rgba(31, 42, 68, .12); + padding-top: .65rem; +} + +.onboarding-excluded-candidates summary { + cursor: pointer; + color: var(--bs-secondary-color); + font-weight: 700; +} + +.onboarding-excluded-candidates .onboarding-character-candidates { + margin-top: .65rem; +} + .onboarding-summary div { display: flex; align-items: center; diff --git a/PlotLine/wwwroot/js/word-companion-host.js b/PlotLine/wwwroot/js/word-companion-host.js index 41907bb..2c4e621 100644 --- a/PlotLine/wwwroot/js/word-companion-host.js +++ b/PlotLine/wwwroot/js/word-companion-host.js @@ -1893,38 +1893,20 @@ }; }; - const properNameStopWords = new Set([ - "Chapter", "Scene", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", - "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", - "November", "December", "The", "A", "An", "And", "But", "Or", "If", "Then", "When", "Where", - "This", "That", "These", "Those", "He", "She", "They", "We", "I", "You", "It", "His", "Her", - "Their", "Our", "Your", "My", "Word", "PlotDirector" - ]); + const onboardingCandidateValue = (candidate, camel, pascal) => candidate?.[camel] ?? candidate?.[pascal] ?? null; - const candidateCharacterNames = (text) => { - const counts = new Map(); - const matches = String(text || "").match(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,2}\b/g) || []; - for (const match of matches) { - const name = match.trim(); - const parts = name.split(/\s+/); - if (parts.some((part) => properNameStopWords.has(part)) || name.length < 3) { - continue; - } - - counts.set(name, (counts.get(name) || 0) + 1); - } - - return [...counts.entries()] - .filter(([, count]) => count >= 2) - .sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])) - .slice(0, 40) - .map(([name, count], index) => ({ - temporaryCharacterKey: `character-${index + 1}`, - name, - mentionCount: count, - suggestedImportance: null - })); - }; + const mapOnboardingCharacterCandidates = (candidates) => (Array.isArray(candidates) ? candidates : []) + .map((candidate, index) => ({ + temporaryCharacterKey: `character-${index + 1}`, + name: onboardingCandidateValue(candidate, "text", "Text") || onboardingCandidateValue(candidate, "name", "Name") || "", + mentionCount: Number.parseInt(onboardingCandidateValue(candidate, "mentionCount", "MentionCount") || "0", 10) || 0, + qualityScore: Number.parseInt(onboardingCandidateValue(candidate, "qualityScore", "QualityScore") || "0", 10) || 0, + category: onboardingCandidateValue(candidate, "category", "Category") || "PossibleCharacter", + reason: onboardingCandidateValue(candidate, "reason", "Reason") || "", + isExistingCharacterMatch: !!onboardingCandidateValue(candidate, "isExistingCharacterMatch", "IsExistingCharacterMatch"), + suggestedImportance: null + })) + .filter((candidate) => candidate.name); const buildOnboardingScanPreview = (paragraphs, command) => { const chapters = []; @@ -2032,7 +2014,7 @@ throw new Error("No chapters were detected. Make sure your chapter headings use Heading 1, or add clear chapter headings."); } - const characterCandidates = candidateCharacterNames(documentText.join("\n")); + const documentTextForDiscovery = documentText.join("\n"); return { previewID: "00000000-0000-0000-0000-000000000000", userID: command.userID || command.UserID || 0, @@ -2045,11 +2027,12 @@ totalWordCount, chapterCount: chapters.length, sceneCount: scenes.length, - characterCandidateCount: characterCandidates.length, + characterCandidateCount: 0, createdUtc: new Date().toISOString(), chapters, scenes, - characterCandidates + characterCandidates: [], + documentTextForDiscovery }; }; @@ -2111,6 +2094,23 @@ await reportOnboardingScanProgress(command, "Detecting scene breaks", 50, preview); await reportOnboardingScanProgress(command, `Scene ${preview.sceneCount} of ${preview.sceneCount} scanned`, 70, preview); await reportOnboardingScanProgress(command, "Finding character candidates", 85, preview); + const documentTextForDiscovery = preview.documentTextForDiscovery || ""; + delete preview.documentTextForDiscovery; + try { + const discovery = await postJson("/api/word-companion/manuscript/discover-characters", { + projectId: preview.projectID, + documentText: documentTextForDiscovery, + includeExcluded: true + }); + preview.characterCandidates = mapOnboardingCharacterCandidates(discovery?.candidates); + preview.characterCandidateCount = preview.characterCandidates + .filter((candidate) => String(candidate.category || "").toLowerCase() !== "excluded") + .length; + } catch (discoveryError) { + console.warn("Unable to refine onboarding character candidates.", discoveryError); + preview.characterCandidates = []; + preview.characterCandidateCount = 0; + } await reportOnboardingScanProgress(command, "Preparing preview", 95, preview); await companionPresenceConnection.invoke("CompleteOnboardingScan", preview); } catch (error) { diff --git a/PlotLine/wwwroot/js/word-companion-host.min.js b/PlotLine/wwwroot/js/word-companion-host.min.js index 56f8fa0..3a7b7a2 100644 --- a/PlotLine/wwwroot/js/word-companion-host.min.js +++ b/PlotLine/wwwroot/js/word-companion-host.min.js @@ -1,25 +1 @@ -(()=>{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"},vl=new Set(["scene","review","maintenance","diagnostics"]),gs=document.querySelector(".word-companion-shell"),yp=document.querySelector(".word-companion-tabs"),nh=[...document.querySelectorAll("[data-tab-button]")],yl=[...document.querySelectorAll("[data-tab-panel]")],pp=document.querySelector("[data-linked-manuscript-card]"),wp=document.querySelector("[data-linked-project-title]"),bp=document.querySelector("[data-linked-book-title]"),kp=document.querySelector("[data-first-run-wizard]"),et=document.querySelector("[data-first-run-project-select]"),d=document.querySelector("[data-first-run-book-select]"),df=document.querySelector("[data-first-run-new-book-title]"),fo=document.querySelector("[data-first-run-create-book]"),sr=document.querySelector("[data-first-run-scan]"),dp=document.querySelector("[data-first-run-cancel]"),gp=document.querySelector("[data-first-run-status]"),hr=document.querySelector("[data-first-run-summary]"),pu=document.querySelector("[data-first-run-character-section]"),wu=document.querySelector("[data-first-run-character-status]"),ki=document.querySelector("[data-first-run-character-candidates]"),bu=document.querySelector("[data-first-run-character-actions]"),eo=document.querySelector("[data-first-run-create-characters]"),pl=document.querySelector("[data-first-run-skip-characters]"),wl=document.querySelector("[data-first-run-continue]"),th=document.querySelector("[data-first-run-import-actions]"),ih=document.querySelector("[data-first-run-import]"),nw=document.querySelector("[data-first-run-import-cancel]"),ku=document.querySelector("[data-first-run-replace-dialog]"),bl=document.querySelector("[data-first-run-replace-confirm]"),kl=document.querySelector("[data-first-run-replace-cancel]"),du=document.querySelector("[data-unlink-word-document-dialog]"),dl=document.querySelector("[data-unlink-word-document-confirm]"),gl=document.querySelector("[data-unlink-word-document-cancel]"),na=document.querySelector("[data-office-status]"),ta=document.querySelector("[data-connection-status]"),ia=document.querySelector("[data-summary-connection]"),ra=document.querySelector("[data-summary-project]"),ua=document.querySelector("[data-summary-book]"),p=document.querySelector("[data-project-select]"),ot=document.querySelector("[data-book-select]"),tw=document.querySelector("[data-runtime-book-selectors]"),fa=document.querySelector("[data-project-message]"),ea=document.querySelector("[data-book-message]"),oo=document.querySelector("[data-refresh-current-scene]"),nu=document.querySelector("[data-refresh-document]"),oa=document.querySelector("[data-current-chapter]"),sa=document.querySelector("[data-current-scene]"),ha=document.querySelector("[data-current-word-count]"),ca=document.querySelector("[data-scene-tracking-status]"),la=document.querySelector("[data-document-message]"),aa=document.querySelector("[data-sync-note]"),gf=document.querySelector("[data-document-outline]"),cr=document.querySelector("[data-analyse-manuscript-structure]"),rh=document.querySelector("[data-apply-structure-sync]"),iw=document.querySelector("[data-structure-preview-status]"),gu=document.querySelector("[data-structure-preview-results]"),tu=document.querySelector("[data-refresh-plotdirector-scene]"),va=document.querySelector("[data-plotdirector-scene-status]"),rw=document.querySelector("[data-anchor-status]"),uw=document.querySelector("[data-anchor-book-id]"),fw=document.querySelector("[data-anchor-chapter-id]"),ew=document.querySelector("[data-anchor-scene-id]"),ai=document.querySelector("[data-attach-plotdirector-ids]"),lr=document.querySelector("[data-unlink-word-document]"),ya=document.querySelector("[data-plotdirector-scene-details]"),pa=document.querySelector("[data-plotdirector-scene-title]"),wa=document.querySelector("[data-plotdirector-revision-status]"),ba=document.querySelector("[data-plotdirector-estimated-words]"),uh=document.querySelector("[data-plotdirector-actual-words]"),ka=document.querySelector("[data-plotdirector-blocked-status]"),fh=document.querySelector("[data-plotdirector-blocked-reason-row]"),eh=document.querySelector("[data-plotdirector-blocked-reason]"),da=document.querySelector("[data-runtime-last-sync]"),ar=document.querySelector("[data-sync-scene-progress]"),ow=document.querySelector("[data-sync-word-count]"),oh=document.querySelector("[data-sync-plotdirector-count]"),ga=document.querySelector("[data-sync-last-synced]"),sw=document.querySelector("[data-sync-status]"),nv=document.querySelector("[data-writing-brief-block]"),tv=document.querySelector("[data-writing-brief]"),iv=document.querySelector("[data-plotdirector-link-groups]"),hw=document.querySelector("[data-character-review-summary]"),cw=document.querySelector("[data-asset-review-summary]"),lw=document.querySelector("[data-location-review-summary]"),ne=document.querySelector("[data-character-chips]"),te=document.querySelector("[data-asset-chips]"),ptt=document.querySelector("[data-location-chips]"),vr=document.querySelector("[data-primary-location-select]"),di=document.querySelector("[data-save-scene-links]"),aw=document.querySelector("[data-scene-links-status]"),rv=document.querySelector("[data-links-editing-scene]"),uv=document.querySelector("[data-chapter-create-link]"),vw=document.querySelector("[data-chapter-create-link-chapter]"),yt=document.querySelector("[data-create-plotdirector-chapter]"),pt=document.querySelector("[data-link-existing-chapter]"),yw=document.querySelector("[data-chapter-create-link-status]"),fv=document.querySelector("[data-scene-create-link]"),pw=document.querySelector("[data-create-link-chapter]"),ww=document.querySelector("[data-create-link-scene]"),wt=document.querySelector("[data-create-plotdirector-scene]"),bt=document.querySelector("[data-link-existing-scene]"),bw=document.querySelector("[data-create-link-status]"),yr=document.querySelector("[data-link-existing-dialog]"),so=document.querySelector("[data-link-scene-search]"),ho=document.querySelector("[data-link-scene-options]"),oi=document.querySelector("[data-link-selected-scene]"),kw=document.querySelector("[data-link-dialog-cancel]"),sh=document.querySelector("[data-link-dialog-status]"),gi=document.querySelector("[data-create-scene-dialog]"),dw=document.querySelector("[data-create-dialog-scene]"),gw=document.querySelector("[data-create-dialog-chapter]"),nb=document.querySelector("[data-create-dialog-confirm]"),tb=document.querySelector("[data-create-dialog-cancel]"),pr=document.querySelector("[data-link-existing-chapter-dialog]"),co=document.querySelector("[data-link-chapter-search]"),lo=document.querySelector("[data-link-chapter-options]"),si=document.querySelector("[data-link-selected-chapter]"),ib=document.querySelector("[data-link-chapter-dialog-cancel]"),hh=document.querySelector("[data-link-chapter-dialog-status]"),nr=document.querySelector("[data-create-chapter-dialog]"),rb=document.querySelector("[data-create-chapter-dialog-chapter]"),ub=document.querySelector("[data-create-chapter-dialog-book]"),fb=document.querySelector("[data-create-chapter-dialog-confirm]"),eb=document.querySelector("[data-create-chapter-dialog-cancel]"),tr=document.querySelector("[data-apply-structure-sync-dialog]"),ch=document.querySelector("[data-apply-structure-sync-summary]"),ob=document.querySelector("[data-apply-structure-sync-confirm]"),sb=document.querySelector("[data-apply-structure-sync-cancel]"),hb=document.querySelector("[data-resolve-project-id]"),cb=document.querySelector("[data-resolve-project-title]"),lb=document.querySelector("[data-resolve-book-id]"),ab=document.querySelector("[data-resolve-book-title]"),vb=document.querySelector("[data-resolve-detected-chapter]"),yb=document.querySelector("[data-resolve-detected-scene]"),pb=document.querySelector("[data-resolve-request-chapter]"),wb=document.querySelector("[data-resolve-request-scene]"),bb=document.querySelector("[data-resolve-result]"),kb=document.querySelector("[data-resolve-chapter-id]"),db=document.querySelector("[data-resolve-scene-id]"),iu=document.querySelector("[data-run-diagnostics]"),gb=document.querySelector("[data-diagnostic-host]"),nk=document.querySelector("[data-diagnostic-platform]"),tk=document.querySelector("[data-diagnostic-ready]"),ik=document.querySelector("[data-diagnostic-word-api]"),rk=document.querySelector("[data-diagnostic-last-scan]"),uk=document.querySelector("[data-diagnostic-last-error]"),fk=document.querySelector("[data-diagnostic-anchor-type]"),ek=document.querySelector("[data-diagnostic-stored-scene-id]"),ok=document.querySelector("[data-diagnostic-stored-chapter-id]"),sk=document.querySelector("[data-diagnostic-resolution-method]"),hk=document.querySelector("[data-diagnostic-paragraphs]"),ck=document.querySelector("[data-diagnostic-heading1]"),lk=document.querySelector("[data-diagnostic-heading2]"),nf=gs?.dataset.authenticated==="true",lh=Number.parseInt(gs?.dataset.userId||"",10),ak=String(gs?.dataset.companionVersion||"20C").trim();let ir=[],ii=[],kt=!1,it=!1,ao="Unknown",vo="Unknown",w="",rt="",ev=null,ie=null,b=null,g=null,u=null,o=null,re="",rr="Title Match",dt=!1,ah=[],ue=null,yo=null,vh=[],fe=null,po=null,vi=null,ri=null,ur=null,l=null,ee=null,wo=null,wr=null,oe=null,hi=[],bo=!1,tf=!1,se=0,he=!1,yh=!1,ce=!1,ko=null,ph="Manual",st=!1,ru=null,br=!1,ov=0,rf=!1,le=0,ae=null;const ve=1200;let wh=!1,go=!1,ye=null,uf=!1,pe=!1,bh=null,kh=null,we=null,ff=!1,ef=!1,dh=null,gh="",sf=[],nc=null,tc="",ic=null,rc="",uu=[],uc=null,fc="",ec=null,oc="",fu=[],sc=null,hc="",cc="",ns="",lc="",sv=0,nt=null,eu=null,ht={characters:[],assets:[],locations:[],primaryLocationId:null};const i=n=>{const t=Date.now();t-sv<1e3||(sv=t,console.debug(`[Word Companion Runtime] ${n}`))},ci=n=>{console.debug(`[Word Companion Timing] ${n}`)},ac=n=>{na&&(na.textContent=n)},vk=n=>{try{return window.localStorage.getItem(n)||""}catch{return""}},yk=(n,t)=>{try{t?window.localStorage.setItem(n,String(t)):window.localStorage.removeItem(n)}catch{}},hv=n=>n==="links"?"review":n==="structure"?"maintenance":n,cv=(n,t=true)=>{const i=hv(n),r=vl.has(i)&&nh.some(n=>n.dataset.tabButton===i)?i:"scene";for(const n of nh){const t=n.dataset.tabButton===r;n.classList.toggle("is-active",t);n.setAttribute("aria-selected",t?"true":"false")}for(const n of yl){const t=n.dataset.tabPanel===r;n.classList.toggle("is-active",t);n.hidden=!t}t&&yk(s.activeTab,r)},lv=()=>{const n=hv(vk(s.activeTab));cv(vl.has(n)?n:"scene",!1)},be=n=>{ta&&(ta.textContent=n),ia&&(ia.textContent=n)},vc=n=>{fa&&(fa.textContent=n)},hf=n=>{ea&&(ea.textContent=n)},t=n=>{la&&(la.textContent=n)},cf=n=>{va&&(va.textContent=n)},yc=t=>{const i=n=>Object.prototype.hasOwnProperty.call(t,n);i("anchorType")&&n(fk,a(t.anchorType));i("storedSceneId")&&n(ek,a(t.storedSceneId));i("storedChapterId")&&n(ok,a(t.storedChapterId));i("resolutionMethod")&&n(sk,a(t.resolutionMethod))},fr=()=>{const i=h(),f=Number.isInteger(u)&&u>0&&g===u,e=!Number.isInteger(o)||o<=0||b===o,t=f&&e;n(rw,t?"Attached to PlotDirector":"Not attached");n(uw,i?.bookId?String(i.bookId):"-");n(fw,Number.isInteger(o)&&o>0?String(o):"-");n(ew,Number.isInteger(u)&&u>0?String(u):"-");ai&&(r(ai,t&&!dt),ai.disabled=!u||t&&!dt,ai.textContent=g&&!t?"Reattach PlotDirector IDs":"Attach PlotDirector IDs");yc({anchorType:rr||"Title Match",storedSceneId:g,storedChapterId:b,resolutionMethod:re})},lf=(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&&ev===h&&ie===c&&b===l&&g===a;(w=t||"",rt=i||"",ev=Number.isInteger(r)?r:null,ie=Number.isInteger(e)&&e>0?e:null,b=Number.isInteger(u)&&u>0?u:null,g=Number.isInteger(f)&&f>0?f:null,v)||(oa&&(oa.textContent=t||"Not detected"),sa&&(sa.textContent=i||"Not detected"),ha&&(ha.textContent=Number.isInteger(r)?String(r):"-"),n(ow,Number.isInteger(r)?String(r):"-"),fr())},af=()=>{ye&&(window.clearTimeout(ye),ye=null)},r=(n,t)=>{n&&(n.hidden=t)},pc=()=>{r(lr,!ri)},gt=t=>{n(sw,t)},er=t=>{n(iw,t)},vf=(n="Select a project and book to analyse manuscript structure.")=>{if(vi=null,gu){gu.innerHTML="";const n=document.createElement("p");n.textContent="No preview generated.";gu.append(n)}er(n);ts()},pk=(n=vi)=>ay.flatMap(t=>Array.isArray(n?.[t.key])?n[t.key]:[]),ou=(n=vi)=>pk(n).filter(n=>n.category==="couldLink"||n.category==="wouldCreateChapters"||n.category==="wouldCreateScenes"),ts=()=>{rh&&(rh.disabled=ce||ou().length===0)},is=()=>{cr&&(cr.disabled=ce||!ni()||!h()),ts()},wc=()=>{rv&&(rv.textContent=u?`Editing links for: ${ns||rt||"Current scene"}`:"Link a PlotDirector scene first.")},bc=()=>{const n=st||!u,t=n||!o||uf;ar&&(ar.disabled=t);di&&(di.disabled=n);vr&&(vr.disabled=n);for(const t of[ne,te])for(const i of t?[...t.querySelectorAll("input[type='checkbox']")]:[])i.disabled=n},k=t=>{(t==="Live"||t==="Manual")&&(ph=t),n(ca,t||ph||"Manual")},li=(i,r="")=>{st=!!i,st&&(af(),ro()),n(ca,st?"Stale":ph),bc(),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,re=i||"",u!==bh&&(kh=null),u||(af(),ro()),bc(),gt(st?"Refresh Current Scene before syncing.":u?"Ready to sync.":"No resolved PlotDirector scene."),hu(st?"Refresh Current Scene before saving.":u?"Ready to save.":"Link a PlotDirector scene first."),wc(),fr()},av=n=>{oo&&(oo.disabled=n,oo.textContent=n?"Refreshing...":"Refresh Current Scene")},a=n=>n===null||n===undefined||n===""?"-":String(n),wtt=n=>{if(!n)return"-";const t=new Date(n);return Number.isNaN(t.getTime())?"-":t.toLocaleString()},rs=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"})}`},vv=t=>{ur=t||null;const i=ur?.lastSyncUtc||ur?.manuscriptDocument?.lastSyncUtc;n(da,rs(i));n(ga,rs(i));gc()},yf=()=>{l=null,ee=null,wo=null,le+=1,ae=null,kn(),dn(),gn(),vv(null)},su=()=>{ee=null,wo=null},hu=t=>{n(aw,t)},cu=t=>{n(bw,t)},yi=t=>{n(yw,t)},kc=()=>!!h()&&!!w&&!o,dc=()=>!!h()&&!!rt&&Number.isInteger(o)&&o>0,ke=()=>{r(uv,!0),yi(""),yt&&(yt.disabled=!0,yt.textContent="Create Chapter in PlotDirector"),pt&&(pt.disabled=!0,pt.textContent="Link to Existing Chapter")},us=(t="")=>{n(vw,a(w));r(uv,!1);yi(t);const i=kc();yt&&(yt.disabled=!i,yt.textContent="Create Chapter in PlotDirector");pt&&(pt.disabled=!i,pt.textContent="Link to Existing Chapter")},fs=()=>{r(fv,!0),cu(""),wt&&(wt.disabled=!0,wt.textContent="Create Scene in PlotDirector"),bt&&(bt.disabled=!0,bt.textContent="Link to Existing Scene")},de=(t="")=>{n(pw,a(w));n(ww,a(rt));r(fv,!1);cu(t);const i=dc();wt&&(wt.disabled=!i,wt.textContent="Create Scene in PlotDirector");bt&&(bt.disabled=!i,bt.textContent="Link to Existing Scene")},tt=t=>{cf(t),r(ya,!0),r(nv,!0),r(iv,!1),ke(),fs(),re="",ns="",lc="",rr=g?"SceneID Anchor":b?"ChapterID Anchor":"Title Match",dt=!1,ht={characters:[],assets:[],locations:[],primaryLocationId:null},os(ne,[],"character"),os(te,[],"asset"),yv([],null,!1),uy(),kr(null),n(oh,"-"),n(pa,"-"),n(wa,"-"),n(ba,"-"),n(uh,"-"),n(ka,"-"),n(eh,"-"),r(fh,!0),fr(),wc()},es=(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,os=(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(es(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)}}}},yv=(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(es(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}},wk=t=>{ns=t?.sceneTitle||rt||"",lc=t?.chapterTitle||w||"",n(pa,a(t?.sceneTitle)),n(wa,a(t?.revisionStatus||t?.revisionStatusName||"Not provided")),n(ba,a(t?.estimatedWords)),n(uh,a(t?.actualWords)),n(oh,a(t?.actualWords)),n(ka,t?.blocked?"Blocked":"Not blocked"),t?.blockedReason?(n(eh,t.blockedReason),r(fh,!1)):(n(eh,"-"),r(fh,!0)),tv&&(tv.textContent=t?.writingBrief||"No writing brief has been added for this scene."),ht={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},os(ne,ht.characters,"character",!!u&&!st),os(te,ht.assets,"asset",!!u&&!st),yv(ht.locations,ht.primaryLocationId,!!u&&!st),uy(),wc(),hu(st?"Refresh Current Scene before saving.":u?"Ready to save.":"No resolved PlotDirector scene."),r(ya,!1),r(nv,!1),r(iv,!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(hb,a(t.projectId));i("projectTitle")&&n(cb,a(t.projectTitle));i("bookId")&&n(lb,a(t.bookId));i("bookTitle")&&n(ab,a(t.bookTitle));i("detectedChapter")&&n(vb,a(t.detectedChapter));i("detectedScene")&&n(yb,a(t.detectedScene));i("requestChapter")&&n(pb,a(t.requestChapter));i("requestScene")&&n(wb,a(t.requestScene));i("result")&&n(bb,a(t.result));i("chapterId")&&n(kb,a(t.chapterId));i("sceneId")&&n(db,a(t.sceneId))},bk=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""},kk=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},ss=async n=>{if(wo===n&&Array.isArray(ee?.chapters))return ee.chapters;const t=await at(`/api/word-companion/books/${n}/structure`);return wo=n,ee=t||null,Array.isArray(t?.chapters)?t.chapters:[]},pv=async n=>{const t=await at(`/api/word-companion/books/${n}/chapters`);return Array.isArray(t?.chapters)?t.chapters:[]},wv=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},bv=async()=>{const n=h();return n?wv(await ss(n.bookId)):null},hs=async(n,i,r,u,f)=>{const e=await at(`/api/word-companion/scenes/${i}/companion`);try{e.revisionStatus=await bk(n,i)}catch{e.revisionStatus=""}rr=f||"Title Match";dt=!1;li(!1);cf("Linked to PlotDirector");t("");ke();fs();kr(i,r,u);wk(e);nn(i,r);ks();ds()},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(gb,t.host);i("platform")&&n(nk,t.platform);i("ready")&&n(tk,t.ready);i("wordApi")&&n(ik,t.wordApi);i("lastScan")&&n(rk,t.lastScan);i("lastError")&&n(uk,t.lastError);i("paragraphs")&&n(hk,t.paragraphs);i("heading1")&&n(ck,t.heading1);i("heading2")&&n(lk,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)},kv=async()=>{if(!window.Office||typeof window.Office.onReady!="function")return kt=!1,it=!1,ao="Unavailable",vo="Unavailable",f({host:ao,platform:vo,ready:"No",wordApi:"No"}),null;const n=await window.Office.onReady();return kt=!0,ao=n?.host||"Unknown",vo=n?.platform||"Unknown",it=!!window.Word&&!!window.Office.HostType&&n?.host===window.Office.HostType.Word,f({host:ao,platform:vo,ready:"Yes",wordApi:it?"Yes":"No"}),n},cs=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}},ls=(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},dk=()=>ur?.projectTitle||ni()?.title||"Project",gk=()=>ur?.bookTitle||dr(h())||"Book",gc=()=>{const t=!!ri;r(pp,!t);r(tw,t);t&&(n(wp,dk()),n(bp,gk()))},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(d?.value||"",10);return ii.find(t=>t.bookId===n)||null},nd=()=>{const n=ri?.projectId||gr()?.projectId||ni()?.projectId||cs(s.projectId);return Number.isInteger(n)&&n>0?n:null},td=()=>{const n=ri?.bookId||au()?.bookId||h()?.bookId||cs(s.bookId);return Number.isInteger(n)&&n>0?n:null},dv=()=>{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}},gv=()=>({userID:Number.isInteger(lh)&&lh>0?lh:null,companionVersion:ak,machineName:"",documentOpen:!!it,currentDocumentName:dv(),linkedProjectID:nd(),linkedBookID:td()}),id=async()=>{nt&&nt.state===signalR.HubConnectionState.Connected&&await nt.invoke("CompanionHeartbeat",gv())},pf=()=>{id().catch(n=>{console.debug("Word Companion heartbeat unavailable.",n)})},ny=async()=>{if(nf&&window.signalR&&!nt){nt=(new signalR.HubConnectionBuilder).withUrl("/hubs/word-companion-follow").withAutomaticReconnect().build();nt.onreconnected(pf);nt.on("ScanCurrentDocument",n=>{void wd(n)});nt.onclose(()=>{eu&&(window.clearInterval(eu),eu=null)});await nt.start();await nt.invoke("RegisterCompanion",gv());eu=window.setInterval(pf,25e3)}};window.addEventListener("beforeunload",()=>{eu&&(window.clearInterval(eu),eu=null),nt&&nt.stop().catch(()=>{})});const ui=t=>n(gp,t),ft=()=>{const n=gr(),t=au(),i=tf&&Date.now()-se>=750;sr&&(sr.disabled=!n||!t||he);fo&&(fo.disabled=!n||!String(df?.value||"").trim()||he);ih&&(ih.disabled=!wr?.canImport||!oe||he);eo&&(eo.disabled=!i||yh||ty().length===0)},vu=n=>{r(kp,!n);r(yp,n);pc();gc();for(const t of yl)r(t,n?!0:t.dataset.tabPanel!=="scene"&&!t.classList.contains("is-active"));n||lv()},nl=()=>{if(et){if(ir.length===0){c(et,"No projects available.");return}ls(et,"Choose a project",ir,"projectId","title");const n=Number.parseInt(p?.value||"",10);Number.isInteger(n)&&n>0&&(et.value=String(n))}},rd=(n=null)=>{if(d){if(ii.length===0){c(d,"No books available.");return}ls(d,"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&&(d.value=String(t.bookId));ft()}},ct=(t="Select a Project and Book to begin.")=>{wr=null,oe=null,hi=[],bo=!1,tf=!1,se=0,r(hr,!0),r(th,!0),r(pu,!0),r(bu,!0),hr&&(hr.innerHTML=""),ki&&(ki.innerHTML=""),n(wu,""),ui(t),ft()},ty=()=>ki?[...ki.querySelectorAll("input[type='checkbox']:checked")].map(n=>String(n.value||"").trim()).filter(Boolean):[],ud=t=>{if(hi=Array.isArray(t)?t:[],pu&&ki){if(ki.innerHTML="",r(pu,!1),r(bu,!bo||hi.length===0),r(eo,!1),r(pl,!1),r(wl,!0),hi.length===0){n(wu,"No likely major characters found.");ft();return}n(wu,`${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()}},fd=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(th,!1);ui(n.message||"Preview generated.");ft()}},tl=()=>{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}},ed=()=>{const i=ri||tl();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},od=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."))})}),iy=()=>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."))})}),ry=()=>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||tl()?.documentGuid||"",pi=()=>{const t=ni(),n=h();ra&&(ra.textContent=t?.title||"(Not connected)");ua&&(ua.textContent=n?dr(n):"(Not connected)");aa&&(aa.textContent=n?"":"Select a PlotDirector book before refreshing.");is();gc()},uy=()=>{const t=Array.isArray(ht.characters)?ht.characters.length:0,i=Array.isArray(ht.assets)?ht.assets.length:0,r=Array.isArray(ht.locations)?ht.locations.length:0;n(hw,`Characters (${t})`);n(cw,`Assets (${i})`);n(lw,`Locations (${r})`)},ge=n=>{const t=n?.styleBuiltIn||n?.style||"";return String(t).replace(/\s+/g,"").toLowerCase()},wi=(n,t)=>{const i=ge(n);return i===`heading${t}`||i.endsWith(`.heading${t}`)||i.includes(`heading${t}`)},wf=n=>{const t=String(n||"").trim().split(/\s+/).filter(Boolean);return t.length},sd=(n,t)=>String(n?.text||"").trim()===String(t?.text||"").trim()&&ge(n)===ge(t),fy=(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},ey=n=>{try{return Array.isArray(n?.contentControls?.items)?n.contentControls.items:[]}catch(t){return console.warn("Word paragraph content controls were not available.",t),[]}},as=(n,t)=>{const i=ey(n);for(const n of i){const i=fy(n.tag,t);if(i)return i}return null},hd=(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:as(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:as(n,"PD-SCENE"),wordCount:0};i.scenes.push(r);return}r&&(r.wordCount+=wf(f))}});const h=t.find(n=>String(n.text||"").trim()),f=h?n.findIndex(n=>sd(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}},cd=(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+=wf(h);return}if(i&&u){if(t&&s.has(h)){u=o();return}const c=wf(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")}},vs=new Set(["***","###"]),ld=(n,t)=>{const i=String(n?.text||"").trim();return{index:t,text:i,styleBuiltIn:n?.styleBuiltIn||"",style:n?.style||"",wordCount:wf(i),chapterAnchorId:null,sceneAnchorId:null}},oy=(n,t)=>{n&&(n.endParagraphIndex=t)},sy=(n,t)=>{n&&(n.endParagraphIndex=t,oy(n.scenes.at(-1),t))},ad=(n,t)=>{const u=n.map(ld),f=[];let o=0,s=0,i=null,r=null,e=0;const h=(n,t=null,u=null)=>i?(oy(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)){sy(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&&vs.has(n.text)){s+=1;h(n);continue}i.wordCount+=n.wordCount;r&&(r.wordCount+=n.wordCount);e+=n.wordCount}}return sy(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}},vd=new Set(["Chapter","Scene","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday","January","February","March","April","May","June","July","August","September","October","November","December","The","A","An","And","But","Or","If","Then","When","Where","This","That","These","Those","He","She","They","We","I","You","It","His","Her","Their","Our","Your","My","Word","PlotDirector"]),yd=n=>{const t=new Map,i=String(n||"").match(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,2}\b/g)||[];for(const n of i){const i=n.trim(),r=i.split(/\s+/);r.some(n=>vd.has(n))||i.length<3||t.set(i,(t.get(i)||0)+1)}return[...t.entries()].filter(([,n])=>n>=2).sort((n,t)=>t[1]-n[1]||n[0].localeCompare(t[0])).slice(0,40).map(([t,n],i)=>({temporaryCharacterKey:`character-${i+1}`,name:t,mentionCount:n,suggestedImportance:null}))},pd=(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,startPosition:n?.index??null,existingChapterID:r?null:as(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?as(n,"PD-SCENE"):null},f.push(r),r):null,a=(n,t)=>{!i||!r||t<=0||(i.wordCount+=t,r.wordCount+=t,r.openingTextPreview||!n||vs.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=wf(r);if(wi(n,1)){h+=1;l(n,r);e+=u;i.wordCount+=u;return}if(i||l(n,"Opening pages",!0),wi(n,2)){o(n,r);e+=u;a(r,u);return}if(vs.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.");const v=yd(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:dv()||"Word manuscript",companionDocumentIdentifier:or(),totalWordCount:e,chapterCount:u.length,sceneCount:f.length,characterCandidateCount:v.length,createdUtc:(new Date).toISOString(),chapters:u,scenes:f,characterCandidates:v}},ys=(n,t,i)=>n?.[t]??n?.[i]??null,bf=async(n,t,i,r={})=>{nt&&nt.state===signalR.HubConnectionState.Connected&&await nt.invoke("ReportOnboardingScanProgress",{userID:ys(n,"userID","UserID"),onboardingID:ys(n,"onboardingID","OnboardingID"),message:t,percentComplete:i,chapterCount:r.chapterCount||0,sceneCount:r.sceneCount||0,characterCandidateCount:r.characterCandidateCount||0,totalWordCount:r.totalWordCount||0})},hy=async(n,t)=>{nt&&nt.state===signalR.HubConnectionState.Connected&&await nt.invoke("FailOnboardingScan",{userID:ys(n,"userID","UserID"),onboardingID:ys(n,"onboardingID","OnboardingID"),message:t})},wd=async n=>{if(!it||!window.Word){await hy(n,"Open your manuscript in Microsoft Word, then try the scan again.");return}try{await bf(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 bf(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=pd(i,n);await bf(n,"Detecting scene breaks",50,t);await bf(n,`Scene ${t.sceneCount} of ${t.sceneCount} scanned`,70,t);await bf(n,"Finding character candidates",85,t);await bf(n,"Preparing preview",95,t);await nt.invoke("CompleteOnboardingScan",t)}catch(t){console.error("Unable to complete onboarding scan.",t);await hy(n,bd(t))}},bd=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}`},kd=(n,t)=>String(n?.text||"").trim()===String(t?.text||"").trim()&&ge(n)===ge(t),cy=(n,t)=>{const r=(t||[]).find(n=>String(n.text||"").trim());if(!n||!r)return n?.currentParagraphIndex??-1;const i=n.paragraphs.filter(n=>kd(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,gd=(n,t)=>!n||!Number.isInteger(t)||t<0?null:n.scenes.filter(n=>n.startParagraphIndex<=t).at(-1)||null,ly=n=>{if(!l)return!1;const t=dd(l,n),i=gd(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:(lf(t?.title,i?.title,i?.wordCount,t?.anchorId,i?.anchorId,t?.index),r)},il=n=>{if(gf){if(gf.innerHTML="",n.chapters.length===0){const n=document.createElement("p");n.textContent="No chapters detected. Use Heading 1 for chapter titles.";gf.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.";gf.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)}gf.append(n)}}},ay=[{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"}],ng={alreadyLinked:"Already Linked",couldLink:"Could Link",wouldCreateChapter:"Would Create Chapter",wouldCreateScene:"Would Create Scene",manualReview:"Manual Review Required"},tg={alreadyLinked:"✓",couldLink:"~",wouldCreateChapter:"+",wouldCreateScene:"+",manualReview:"?"},ps=(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)},rl=n=>Array.isArray(n?.scenes)?n.scenes:[],ig=(n,t)=>{for(const i of n){const n=rl(i).find(n=>n.sceneId===t);if(n)return{chapter:i,scene:n}}return null},rg=(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=>ps(t.title,"chapter")===ps(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},ug=(n,t,i,r)=>{const u=n.anchorId?`PD-SCENE-${n.anchorId}`:"None";if(n.anchorId){const f=ig(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=rl(t.pdChapter).filter(t=>ps(t.title,"scene")===ps(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}))},fg=(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=rg(t,r,i);for(const u of rl(t))ug(u,n,r,i)}return i},eg=n=>{const t=document.createElement("article");t.className="word-companion-preview-item";const i=document.createElement("strong");i.textContent=`${tg[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: ${ng[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},vy=n=>{if(gu){gu.innerHTML="";for(const t of ay){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(eg(n));gu.append(i)}ts()}},kf=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=hd(t.items,i.items);return r.bodyParagraphs=t.items,r},yy=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=ad(u.items,t),e=cy(r,f.items);return l=r,ly(e),i("Cache rebuilt."),r},btt=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===le&&n.documentGuid===or()&&n.sceneId===u,py=async(n={})=>{const r=!!n.includeSelection,w=n.updateDisplay!==!1,f=le,b=y(),k=ni(),d=h(),e=l?.currentScene;if(!e||!Number.isInteger(e.startParagraphIndex)||!Number.isInteger(e.endParagraphIndex)||!kt||!it||!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!==le)return ci(`Office snapshot: ${c}ms stale`),{stale:!0,generation:f};let a=!1;if(r){const n=cy(l,s.selectionParagraphs);a=ly(n)}const t=l?.currentScene;if(!t||!Number.isInteger(t.startParagraphIndex)||!Number.isInteger(t.endParagraphIndex))return null;const v=[];let i=0;const nt=Math.min(t.endParagraphIndex,s.bodyTexts.length-1),tt=l.currentChapter?.startParagraphIndex;for(let n=t.startParagraphIndex;n<=nt;n+=1){const t=String(s.bodyTexts[n]||"").trim();t&&!vs.has(t)&&n!==tt&&(v.push(t),i+=wf(t))}t.wordCount=i;w&&lf(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 ae=p,ci(`Office snapshot: ${c}ms; scene words: ${i}; total snapshot: ${Math.round(y()-b)}ms`),p},wy=async()=>ti(ae)?ae:await py(),by=async()=>{if(t(""),!kt||!it||!window.Word||typeof window.Word.run!="function")return lf(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 yy(n,!!t.usesExplicitScenes):(l=null,await kf(n))});return l||lf(n.currentChapter?.title,n.currentScene?.title,n.currentScene?.wordCount,n.currentChapter?.anchorId,n.currentScene?.anchorId,n.currentChapter?.index),tt("Not detected"),il(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),lf(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")}},ky=async()=>{const n=h();if(!ni()||!n)return er("Select a project and book to analyse manuscript structure."),!1;if(!kt||!it||!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 kf(n)),i=await at(`/api/word-companion/books/${n.bookId}/structure`);return lf(t.currentChapter?.title,t.currentScene?.title,t.currentScene?.wordCount,t.currentChapter?.anchorId,t.currentScene?.anchorId,t.currentChapter?.index),il(t),vi=fg(t,i),vy(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",is())}},og=(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}},sg=n=>{if(ch){ch.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)}ch.append(t)}},ul=n=>{ko&&(ko(n),ko=null),tr?.close?tr.close():tr&&(tr.hidden=!0)},hg=()=>{const n=og();return(sg(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=>{ko=n,tr.showModal?tr.showModal():tr.hidden=!1})},no=(n,t,i="")=>{n.result=t,n.error=i},dy=n=>Number.isInteger(n?.wordChapter?.index)&&n.wordChapter.index>0?n.wordChapter.index*10:null,gy=n=>Number.isInteger(n?.wordScene?.index)&&n.wordScene.index>0?n.wordScene.index*10:null,cg=async(n,t)=>{const i=await lt(`/api/word-companion/books/${n}/chapters`,{title:lu(t.wordTitle),sortOrder:dy(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||dy(t)||0,scenes:[]};t.match=`Chapter ${t.pdChapter.chapterId}: ${t.pdChapter.title}`},lg=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 lt(`/api/word-companion/books/${n}/chapters/${i.chapterId}/scenes`,{sceneTitle:lu(t.wordTitle),sortOrder:gy(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:gy(t)||0};t.match=`Scene ${t.pdScene.sceneId}: ${t.pdScene.title}`},ag=async n=>{if(!kt||!it||!window.Word||typeof window.Word.run!="function")throw new Error("Word document access is unavailable.");await window.Word.run(async t=>{const u=await kf(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.");to(i,"PlotDirector Chapter",`PD-CHAPTER-${t}`,"PD-CHAPTER")}else{const t=n.pdScene?.sceneId;if(!t)throw new Error("Scene ID is not available.");to(i,"PlotDirector Scene",`PD-SCENE-${t}`,"PD-SCENE")}await t.sync()})},vg=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.`,yg=async()=>{const r=h();if(!r||ou().length===0||ce){ts();return}const f=await hg();if(f){ce=!0;is();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 ag(t);no(t,"Success");n[i]+=1}catch(r){no(t,"Failed",e(r));n.failed+=1}};try{for(const n of o)i(n,"linkedChapters");for(const t of s)try{await cg(r.bookId,t);i(t,"createdChapters")}catch(u){no(t,"Failed",e(u));n.failed+=1}for(const n of c)i(n,"linkedScenes");for(const t of l)try{await lg(r.bookId,t);i(t,"createdScenes")}catch(u){no(t,"Failed",e(u));n.failed+=1}for(const n of u)await a(n.item,n.counterName);for(const n of vi.manualReview||[])no(n,"Skipped");vy(vi);t=vg(n);er(t)}finally{ce=!1;is();t&&(su(),await ky(),er(`${t} Preview refreshed.`))}}},pg=async()=>{iu&&(iu.disabled=!0,iu.textContent="Running...");try{if(await kv(),!kt||!it||!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()},lt=(n,t)=>at(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),wg=async()=>{const n=gr(),t=au();if(!n||!t){ct("Select a Project and Book to begin.");return}if(!kt||!it||!window.Word||typeof window.Word.run!="function"){ct("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(),cd(i.items,!!t.usesExplicitScenes)});oe=i;bo=!1;const u=await lt("/api/word-companion/manuscript/analyse",{projectId:n.projectId,bookId:t.bookId,documentGuid:ry(),chapterCount:i.chapterCount,sceneCount:i.sceneCount,wordCount:i.wordCount});wr=u;fd(u);try{const t=await lt("/api/word-companion/manuscript/discover-characters",{projectId:n.projectId,documentText:i.documentText||""});hi=Array.isArray(t?.candidates)?t.candidates:[];r(pu,!0);r(bu,!0)}catch(e){console.error("Unable to discover character candidates.",e);hi=[];r(pu,!0);r(bu,!0)}f({lastScan:"Success",lastError:"-",paragraphs:"-",heading1:String(i.chapterCount),heading2:"-"})}catch(i){console.error("Unable to scan manuscript for first-run import.",i);ct(`Unable to scan manuscript: ${e(i)}`);f({lastScan:"Failure",lastError:e(i)})}finally{sr&&(sr.textContent="Scan Manuscript");ft()}},bg=()=>new Promise(n=>{if(!ku||typeof ku.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=>{bl?.removeEventListener("click",i),kl?.removeEventListener("click",r),ku?.removeEventListener("cancel",u),ku?.close(),n(t)},i=()=>t(!0),r=()=>t(!1),u=n=>{n.preventDefault(),t(!1)};bl?.addEventListener("click",i);kl?.addEventListener("click",r);ku?.addEventListener("cancel",u);ku.showModal()}),kg=()=>new Promise(n=>{if(!du||typeof du.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=>{dl?.removeEventListener("click",i),gl?.removeEventListener("click",r),du?.removeEventListener("cancel",u),du?.close(),n(t)},i=()=>t(!0),r=()=>t(!1),u=n=>{n.preventDefault(),t(!1)};dl?.addEventListener("click",i);gl?.addEventListener("click",r);du?.addEventListener("cancel",u);du.showModal()}),dg=async()=>{const i=gr(),u=au();if(!i||!u||!oe||!wr?.canImport){ft();return}let n=!1;if(wr.requiresReplaceConfirmation&&(n=await bg(),!n)){ui("Import cancelled.");return}he=!0;ft();ui("Importing manuscript structure...");try{const h=ry(),f=await lt("/api/word-companion/manuscript/import",{projectId:i.projectId,bookId:u.bookId,documentGuid:h,bindingVersion:1,replacePlanned:n,chapters:oe.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 od({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 yu(f.projectId,f.bookId);be("Connected");bo=!0;tf=o.length>0;se=tf?Date.now():0;hi=o;o.length>0?(vu(!0),ud(o),r(th,!0),r(bu,!1),ui(f.message||"Manuscript structure imported."),t(f.message||"Manuscript structure imported."),window.setTimeout(ft,750)):await ws(f.message||"Manuscript structure imported.")}catch(f){console.error("Unable to import manuscript structure.",f);ui(`Unable to import manuscript: ${e(f)}`)}finally{he=!1;ft()}},ws=async(n="")=>{tf=!1;se=0;hi=[];ki&&(ki.innerHTML="");r(pu,!0);vu(!1);ui("");r(bu,!0);n&&t(n);const i=await np();n&&i&&w&&t(n)},gg=async()=>{const i=gr()||ni(),r=ty();if(!tf||Date.now()-se<750||!i||r.length===0){ft();return}yh=!0;ft();n(wu,"Creating selected characters...");try{const u=await lt("/api/word-companion/manuscript/create-discovered-characters",{projectId:i.projectId,candidateNames:r});t(u.message||"Characters created.");n(wu,`${u.createdCount||0} created, ${u.skippedCount||0} already existed.`);await ws(u.message||"Characters created.")}catch(u){console.error("Unable to create discovered characters.",u);n(wu,`Unable to create characters: ${e(u)}`)}finally{yh=!1;ft()}},nn=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(cc!==l){cc=l;try{await lt("/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){cc="";console.debug("Unable to notify PlotDirector of current scene.",a)}}}},np=async()=>{const n=h();if(!n||!kt||!it||!window.Word||typeof window.Word.run!="function")return!1;t("Reading manuscript position...");try{su();await Promise.all([ol(!0),sl(!0),hl(!0)]);await ss(n.bookId);const i=await window.Word.run(async t=>await yy(t,!!n.usesExplicitScenes));return(il(i),f({lastScan:"Success",lastError:"-",paragraphs:String(i.paragraphCount),heading1:String(i.heading1Count),heading2:String(i.heading2Count)}),!w)?(tt("Not detected"),li(!1),k("Live"),t("Manuscript linked. Click inside a chapter to show the current scene."),!0):(rt?await uo():await ll(),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}},tn=async()=>{if(!nf||!it){vu(!1);return}const n=tl();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;vv(t);v(s.projectId,t.projectId);v(s.bookId,t.bookId);p&&(p.value=String(t.projectId));await yu(t.projectId,t.bookId);vu(!1);be("Connected");hf("Bound manuscript detected.");await np();return}}catch{}ri=null;yf();nl();gr()?await yu(gr().projectId,null):c(d,"Select a project first");ct("Select a Project and Book to begin.");vu(!0)},tp=async n=>{ri=null,ap(),af(),yf(),tt("Not detected"),k("Manual"),be("Connected"),v(s.projectId,null),v(s.bookId,null),p&&(p.value=""),c(ot,"Select a project first"),nl(),et&&(et.value=""),c(d,"Select a project first"),ct(n||"Select a Project and Book to begin."),t(n||"Manuscript unlinked."),gt("No resolved PlotDirector scene."),vu(!0),pc()},rn=async()=>{console.debug("Word Companion unlink clicked");t("Preparing to unlink manuscript...");const n=ed();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 kg();if(!r){t("Unlink cancelled.");return}t("Unlinking manuscript...");lr&&(lr.disabled=!0,lr.textContent="Unlinking...");try{await lt("/api/word-companion/manuscript/unlink",{documentGuid:n.documentGuid,bookId:n.bookId});await iy();await tp("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 iy();await tp("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");pc()}},ktt=(n,t)=>at(n,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),un=(n,t)=>at(n,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),bs=n=>n?[...n.querySelectorAll("input[type='checkbox']:checked")].map(n=>Number.parseInt(n.value||"",10)).filter(n=>Number.isInteger(n)&&n>0):[],ip=()=>{const n=Number.parseInt(vr?.value||"",10);return Number.isInteger(n)&&n>0?n:null},fn=()=>{const n=new Set(bs(ne)),t=new Set(bs(te));ht={characters:ht.characters.map(t=>({...t,linked:n.has(Number.parseInt(es(t,"character"),10))})),assets:ht.assets.map(n=>({...n,linked:t.has(Number.parseInt(es(n,"asset"),10))})),locations:ht.locations,primaryLocationId:ip()}},en=async()=>{if(!u)return!1;if(!kt||!it||!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 kf(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(ns||rt,"scene").toLocaleLowerCase(),c=ut(r,"chapter").toLocaleLowerCase()===ut(lc||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}},on=async()=>{if(!u){hu("Link a PlotDirector scene first.");return}if(st||!await en()){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 un(`/api/word-companion/scenes/${u}/links`,{characterIds:bs(ne),assetIds:bs(te),locationIds:[],primaryLocationId:ip()});fn();hu("Scene links saved successfully.");f({lastError:"-"})}catch(n){hu("Unable to save scene links.");f({lastError:e(n)})}finally{di&&(di.disabled=!u||st,di.textContent="Save Scene Links")}},sn=(n,t)=>{const i=ey(n);return i.find(n=>fy(n.tag,t))||null},to=(n,t,i,r)=>{const f=sn(n,r),u=f||n.getRange().insertContentControl();return u.title=t,u.tag=i,u.appearance="Hidden",u},rp=async(n="PlotDirector IDs attached successfully.")=>{if(!u||!o)return t("No resolved PlotDirector scene."),!1;const r=g&&g!==u||b&&b!==o;if(r&&!window.confirm("This scene is already linked. Replace the existing PlotDirector link?"))return!1;if(!kt||!it||!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 kf(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.");to(i,"PlotDirector Chapter",`PD-CHAPTER-${o}`,"PD-CHAPTER");to(r,"PlotDirector Scene",`PD-SCENE-${u}`,"PD-SCENE");await n.sync()}),b=o,g=u,dt=!1,rr="SceneID Anchor",re="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=g===u?"Attach PlotDirector IDs":"Reattach PlotDirector IDs",fr())}},up=async n=>{if(!o)return yi("No resolved PlotDirector chapter."),!1;if(!kt||!it||!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 kf(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.");to(i,"PlotDirector Chapter",`PD-CHAPTER-${o}`,"PD-CHAPTER");await n.sync()}),b=o,dt=!1,rr="ChapterID Anchor",re="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}},hn=async()=>{await rp()},fp=async n=>{ke(),await uo(),t(n),yi(n)},fl=n=>{po&&(po(n),po=null),nr?.close?nr.close():nr&&(nr.hidden=!0)},cn=(t,i)=>(n(rb,`"${t}"`),n(ub,`"${i}"`),!nr)?Promise.resolve(window.confirm(`Create PlotDirector chapter: - -"${t}" - -in book: - -"${i}" - -?`)):new Promise(n=>{po=n,nr.showModal?nr.showModal():nr.hidden=!1}),ln=async()=>{const n=h();if(!n||!w||o){us("Chapter not found in PlotDirector.");return}const t=lu(w),i=await cn(t,dr(n)||"selected book");if(i){yt&&(yt.disabled=!0,yt.textContent="Creating...");try{const r=Number.isInteger(ie)&&ie>0?ie*10:null,i=await lt(`/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 up("Chapter created and linked successfully.");if(!u)return;await fp("Chapter created and linked successfully.")}catch(r){yi("Unable to create chapter.");f({lastError:e(r)})}finally{yt&&(yt.disabled=!kc(),yt.textContent="Create Chapter in PlotDirector")}}},ep=()=>{if(lo){const n=lu(co?.value||"").toLocaleLowerCase(),t=vh.filter(t=>!n||String(t.title||"").toLocaleLowerCase().includes(n));if(lo.innerHTML="",fe=null,si&&(si.disabled=!0),t.length===0){const n=document.createElement("p");n.className="word-companion-empty-list";n.textContent="No chapters found.";lo.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",()=>{fe=i,si&&(si.disabled=!1)});const u=document.createElement("span");u.textContent=n.title||"Untitled chapter";r.append(t,u);lo.append(r)}}}},an=async()=>{const t=h();if(!t||!w||o){us("Chapter not found in PlotDirector.");return}pt&&(pt.disabled=!0,pt.textContent="Loading...");try{vh=await pv(t.bookId);fe=null;co&&(co.value="");n(hh,"");ep();pr?.showModal?pr.showModal():pr&&(pr.hidden=!1)}catch(i){yi("Unable to load chapters.");f({lastError:e(i)})}finally{pt&&(pt.disabled=!kc(),pt.textContent="Link to Existing Chapter")}},op=()=>{pr?.close?pr.close():pr&&(pr.hidden=!0)},vn=async()=>{const t=vh.find(n=>n.chapterId===fe);if(!t){n(hh,"Select a chapter.");return}si&&(si.disabled=!0,si.textContent="Linking...");try{kr(null,t.chapterId,"Manual chapter link");const n=await up("Chapter linked successfully.");if(!n)return;op();await fp("Chapter linked successfully.")}catch(i){n(hh,"Unable to link chapter.");yi("Unable to link chapter.");f({lastError:e(i)})}finally{si&&(si.disabled=!fe,si.textContent="Link Chapter")}},sp=async(n,t,i)=>{const r=h();if(!r)return cu("Select a PlotDirector book first."),!1;await hs(r.bookId,n,t,"Manual link","Title Match");const u=await rp(i);return u?(cu(i),fs(),!0):!1},el=n=>{yo&&(yo(n),yo=null),gi?.close?gi.close():gi&&(gi.hidden=!0)},yn=(t,i)=>(n(dw,`"${t}"`),n(gw,`"${i}"`),!gi)?Promise.resolve(window.confirm(`Create PlotDirector scene: - -"${t}" - -within chapter: - -"${i}" - -?`)):new Promise(n=>{yo=n,gi.showModal?gi.showModal():gi.hidden=!1}),pn=async()=>{const n=h();if(!n||!rt){de("Scene not found in PlotDirector.");return}const t=ut(rt,"scene"),i=ut(w,"chapter"),r=await yn(t,i);if(r){wt&&(wt.disabled=!0,wt.textContent="Creating...");try{const r=await bv();if(!r){de("This chapter does not exist in PlotDirector. Create or link the chapter first.");return}const i=await lt(`/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 sp(i.sceneId,i.chapterId,"Scene created and linked successfully.")}catch(u){cu("Unable to create scene.");f({lastError:e(u)})}finally{wt&&(wt.disabled=!dc(),wt.textContent="Create Scene in PlotDirector")}}},hp=()=>{if(ho){const n=lu(so?.value||"").toLocaleLowerCase(),t=ah.filter(t=>!n||String(t.title||"").toLocaleLowerCase().includes(n));if(ho.innerHTML="",ue=null,oi&&(oi.disabled=!0),t.length===0){const n=document.createElement("p");n.className="word-companion-empty-list";n.textContent="No scenes found.";ho.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",()=>{ue=i,oi&&(oi.disabled=!1)});const u=document.createElement("span");u.textContent=n.title||"Untitled scene";r.append(t,u);ho.append(r)}}}},wn=async()=>{const t=h();if(!t||!rt){de("Scene not found in PlotDirector.");return}bt&&(bt.disabled=!0,bt.textContent="Loading...");try{const r=await ss(t.bookId),i=wv(r);if(!i){de("This chapter does not exist in PlotDirector. Create or link the chapter first.");return}ah=Array.isArray(i.scenes)?i.scenes:[];ue=null;so&&(so.value="");n(sh,"");hp();yr?.showModal?yr.showModal():yr&&(yr.hidden=!1)}catch(i){cu("Unable to load scenes.");f({lastError:e(i)})}finally{bt&&(bt.disabled=!dc(),bt.textContent="Link to Existing Scene")}},cp=()=>{yr?.close?yr.close():yr&&(yr.hidden=!0)},bn=async()=>{const i=h(),t=ah.find(n=>n.sceneId===ue);if(!i||!t){n(sh,"Select a scene.");return}oi&&(oi.disabled=!0,oi.textContent="Linking...");try{const n=await bv();if(!n)throw new Error("This chapter does not exist in PlotDirector. Create or link the chapter first.");cp();await sp(t.sceneId,n.chapterId,"Scene linked successfully.")}catch(r){n(sh,"Unable to link scene.");cu("Unable to link scene.");f({lastError:e(r)})}finally{oi&&(oi.disabled=!ue,oi.textContent="Link Scene")}},io=()=>{uf=!1,bc(),pe&&(pe=!1,ks(1e3))},lp=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(st){gt("Refresh Current Scene before syncing.");return}const c=h(),l=or();if(!c||!l){gt("Bound manuscript metadata is unavailable.");return}if(uf){pe=!0;i("Scene sync deferred; sync already running.");return}af();uf=!0;pe=!1;let r=t.snapshot||null;try{r=ti(r)?r:await wy()}catch(v){gt("Sync failed");f({lastError:e(v)});i("Scene sync failed.");io();return}if(!ti(r)){gt("Waiting for typing to pause");i("Scene sync skipped (stale snapshot).");io();return}const s=r.wordCount;if(!Number.isInteger(s)||s<0){gt("Sync failed");i("Scene sync failed.");io();return}if(bh===r.sceneId&&kh===s){gt("Synced");i("Sync skipped (count unchanged).");io();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 lt("/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;bh=r.sceneId;kh=t;n(oh,a(t));n(uh,a(t));n(ga,rs(u));n(da,rs(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");io()}},ks=(n=4e3)=>{(af(),u&&o&&!st&&!uf)&&(ye=window.setTimeout(()=>{ye=null,void lp({source:"automatic"})},n))},ro=()=>{we&&(window.clearTimeout(we),we=null)},kn=()=>{dh=null,gh="",sf=[],nc=null,tc="",ro()},dn=()=>{ic=null,rc="",uu=[],uc=null,fc=""},gn=()=>{ec=null,oc="",fu=[],sc=null,hc=""},ol=async(n=false)=>{const t=ni(),r=or();if(!t||!r)return sf=[],[];if(!n&&dh===t.projectId&&gh===r&&sf.length>0)return sf;const u=await at(`/api/word-companion/runtime/characters?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(r)}`);return dh=t.projectId,gh=r,sf=Array.isArray(u?.characters)?u.characters:[],i("Character alias cache refreshed."),sf},sl=async(n=false)=>{const t=ni(),r=or();if(!t||!r)return uu=[],[];if(!n&&ic===t.projectId&&rc===r&&uu.length>0)return uu;const u=await at(`/api/word-companion/runtime/assets?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(r)}`);return ic=t.projectId,rc=r,uu=Array.isArray(u?.assets)?u.assets:[],i(`Asset cache loaded: ${uu.length} aliases.`),uu},hl=async(n=false)=>{const t=ni(),r=or();if(!t||!r)return fu=[],[];if(!n&&ec===t.projectId&&oc===r&&fu.length>0)return fu;const u=await at(`/api/word-companion/runtime/locations?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(r)}`);return ec=t.projectId,oc=r,fu=Array.isArray(u?.locations)?u.locations:[],i(`Location cache loaded: ${fu.length} aliases.`),fu},ntt=n=>String(n||"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),cl=(n,t)=>{const i=String(t||"").trim();if(!i)return!1;const r=ntt(i).replace(/\s+/g,"\\s+");return new RegExp(`(^|[^\\p{L}\\p{N}_])${r}(?=$|[^\\p{L}\\p{N}_])`,"iu").test(n)},ttt=(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)||cl(n,r.matchText)&&i.set(t,r.name||r.matchText||"Character")}return[...i.entries()].map(([n,t])=>({characterId:n,name:t}))},itt=(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)||cl(n,r.matchText)&&i.set(t,r.name||r.matchText||"Asset")}return[...i.entries()].map(([n,t])=>({assetId:n,name:t}))},rtt=n=>String(n||"").trim().split(/\s+/).filter(Boolean).length,utt=n=>{if(n?.excludeFromCompanionDetection)return!1;const t=Number.parseInt(n?.detectionPriority||"0",10);return rtt(n?.matchText)>=2||Number.isInteger(t)&&t>=75},ftt=(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)||!utt(r)||cl(n,r.matchText)&&i.set(t,r.name||r.matchText||"Location")}return[...i.entries()].map(([n,t])=>({locationId:n,name:t}))},ett=()=>{ff=!1,ef&&(ef=!1,ds(1500))},ott=async()=>{if(we=null,u&&!st&&or()){if(ff){ef=!0;i("Character suggestion detection deferred; detection already running.");return}ff=!0;ef=!1;const r=y();try{const n=await wy();if(!ti(n)){i("Runtime suggestions skipped (stale snapshot).");return}const[w,b,k]=await Promise.all([ol(),sl(),hl()]);if(!ti(n)){i("Runtime suggestions skipped after cache load (stale snapshot).");return}const s=n.sceneText||"",d=y(),o=ttt(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=itt(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=ftt(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&&(nc!==u||tc!==h)){if(!ti(n)){i("Character suggestions skipped before submit (stale snapshot).");return}const u=y(),r=await lt("/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(nc=n.sceneId,tc=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&&(uc!==u||fc!==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 lt("/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(uc=n.sceneId,fc=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&&(sc!==u||hc!==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 lt("/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(sc=n.sceneId,hc=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{ett()}}},ds=(n=2500)=>{if(ro(),!u||st||ff){ff&&(ef=!0);return}we=window.setTimeout(()=>{void ott()},n)},ll=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 tt("Not detected"),t("Select a PlotDirector book first."),!1;if(!w)return tt("Not detected"),t("No chapter detected in Word. Use Heading 1 for chapters."),!1;tt("Not detected");try{const f=await pv(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?(ke(),kr(null,u.chapterId,i?"Chapter anchor":"Chapter title"),rr=i?"ChapterID Anchor":"Title Match",cf("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),cf("Chapter not found in PlotDirector."),t("Chapter not found in PlotDirector."),vt({result:"Chapter not matched",chapterId:null,sceneId:null}),us("Chapter not found in PlotDirector."),!1)}catch(u){return tt("Not found in PlotDirector"),f({lastError:e(u)}),t("Unable to load PlotDirector chapter data."),!1}},uo=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}),yc({anchorType:g?"SceneID Anchor":b?"ChapterID Anchor":"Title Match",storedSceneId:g,storedChapterId:b,resolutionMethod:"-"}),!n)return tt("Not detected"),t("Select a PlotDirector book first."),vt({result:"Error"}),!1;if(!w||!rt)return tt("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")};tt("Not detected");t("Resolving PlotDirector scene...");try{if(g){const i=await kk(n.bookId,(n,t)=>t.sceneId===g);if(i)return vt({result:"Matched",chapterId:i.chapter.chapterId,sceneId:i.scene.sceneId}),await hs(n.bookId,i.scene.sceneId,i.chapter.chapterId,"Anchor","SceneID Anchor"),!0;dt=!0;rr="SceneID Anchor";cf("Not found in PlotDirector");t("Stored PlotDirector ID is invalid.");vt({result:"Error",chapterId:b,sceneId:g});yc({anchorType:"SceneID Anchor",storedSceneId:g,storedChapterId:b,resolutionMethod:"Anchor"})}if(b){const u=await ss(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 hs(n.bookId,r.sceneId,t.chapterId,"Anchor","ChapterID Anchor"),!0;t&&!g?(rr="ChapterID Anchor",kr(null,b,"Chapter anchor"),vt({result:"Chapter matched",chapterId:b,sceneId:null})):g||(rr="ChapterID Anchor",vt({result:"Chapter anchor not found",chapterId:b,sceneId:null}))}const r=await lt(`/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 tt("Not found in PlotDirector"),dt=u,i&&(kr(null,n,n===e?"Chapter anchor":"Chapter title"),cf("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?(ke(),de("Scene not found in PlotDirector.")):(fs(),us("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 hs(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)}),tt("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()}},dtt=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!==g},stt=async()=>{if(ru=null,go){rf=!0;i("Runtime update deferred; update already running.");return}if(!nf||!h()){br=!1;i("Runtime update skipped.");return}if(!kt||!it||!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()-ov;if(br&&n{ru&&window.clearTimeout(ru),ru=window.setTimeout(stt,Math.max(0,n)),k("Waiting for typing to pause"),i("Runtime update scheduled.")},htt=(n="selection",t=ve)=>{if(br=!0,ov=Date.now(),le+=1,ae=null,af(),ro(),uf&&(pe=!0),ff&&(ef=!0),i(`${n} changed.`),go){rf=!0;k("Waiting for typing to pause");return}al(t)},ap=()=>{ru&&(window.clearTimeout(ru),ru=null),br=!1,rf=!1},vp=()=>{htt("Selection")},ctt=()=>{if(wh&&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:vp});wh=!1}catch(n){console.warn("Unable to remove Word selection tracking handler.",n)}},ltt=()=>{if(nf){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,vp,n=>{n?.status===window.Office.AsyncResultStatus.Succeeded?(wh=!0,k("Live")):(k("Manual"),t("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene."))});window.addEventListener("beforeunload",ctt)}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.")}}},att=async()=>{if(!h()){t("Select a PlotDirector book first.");tt("Not detected");return}ap();av(!0);k("Updating...");try{await Promise.all([ol(!0),sl(!0),hl(!0)]);const n=await by();if(!n)return;if(li(!1),!rt){await ll();k("Current scene updated");return}await uo();k("Current scene updated")}finally{av(!1)}},yu=async(n,t)=>{if(l=null,su(),!n){ii=[];c(ot,"Select a project first");c(d,"Select a project first");hf("");v(s.bookId,null);pi();tt("Not detected");vf();return}c(ot,"Loading books...");hf("");tt("Not detected");vf("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(d,"No books available.");hf("No books available.");v(s.bookId,null);pi();ct("No books available.");return}ls(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);rd(i?.bookId||t);pi();tt(h()?"Not detected":"Not detected");vf(h()?"Ready to analyse manuscript structure.":"Select a book to analyse manuscript structure.");ct(au()?"Ready to scan manuscript structure.":"Select a Book to begin.")}catch{ii=[];c(ot,"Unable to load books.");c(d,"Unable to load books.");hf("Unable to load books.");v(s.bookId,null);pi();tt("Not detected");vf("Unable to load books.");ct("Unable to load books.")}},vtt=async()=>{c(p,"Loading projects...");c(ot,"Select a project first");c(et,"Loading projects...");c(d,"Select a project first");vc("");hf("");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.");vc("No projects available.");v(s.projectId,null);v(s.bookId,null);pi();ct("No projects available.");return}ls(p,"Choose a project",ir,"projectId","title");nl();const i=cs(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 yu(n.projectId,cs(s.bookId))):(v(s.projectId,null),v(s.bookId,null),pi(),ct("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(d,"Select a project first");be("Unable to connect to PlotDirector.");vc("Unable to connect to PlotDirector.");v(s.projectId,null);v(s.bookId,null);pi();ct("Unable to connect to PlotDirector.")}};for(const n of nh)n.addEventListener("click",()=>cv(n.dataset.tabButton));lv();p?.addEventListener("change",async()=>{const n=Number.parseInt(p.value||"",10);yf();et&&Number.isInteger(n)&&n>0&&(et.value=String(n));v(s.projectId,Number.isInteger(n)&&n>0?n:null);v(s.bookId,null);tt("Not detected");vf();ct("Select a Book to begin.");await yu(n,null);pf()});ot?.addEventListener("change",()=>{const n=Number.parseInt(ot.value||"",10);yf();v(s.bookId,Number.isInteger(n)&&n>0?n:null);d&&Number.isInteger(n)&&n>0&&(d.value=String(n));pi();tt("Not detected");vf(h()?"Ready to analyse manuscript structure.":"Select a book to analyse manuscript structure.");ct(au()?"Ready to scan manuscript structure.":"Select a Book to begin.");pf()});et?.addEventListener("change",async()=>{const n=Number.parseInt(et.value||"",10);yf();p&&Number.isInteger(n)&&n>0&&(p.value=String(n));v(s.projectId,Number.isInteger(n)&&n>0?n:null);v(s.bookId,null);ct("Select a Book to begin.");await yu(n,null);pf()});d?.addEventListener("change",()=>{const n=Number.parseInt(d.value||"",10);yf();ot&&Number.isInteger(n)&&n>0&&(ot.value=String(n));v(s.bookId,Number.isInteger(n)&&n>0?n:null);pi();ct(au()?"Ready to scan manuscript structure.":"Select a Book to begin.");pf()});df?.addEventListener("input",ft);fo?.addEventListener("click",async()=>{const n=gr(),t=String(df?.value||"").trim();if(!n||!t){ft();return}fo.disabled=!0;ui("Creating Book...");try{const i=await lt(`/api/word-companion/projects/${n.projectId}/books`,{title:t});df&&(df.value="");p&&(p.value=String(n.projectId));await yu(n.projectId,i.bookId);d&&(d.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",wg);ih?.addEventListener("click",dg);eo?.addEventListener("click",gg);pl?.addEventListener("click",()=>ws("Character creation skipped."));wl?.addEventListener("click",()=>ws());dp?.addEventListener("click",()=>vu(!1));nw?.addEventListener("click",()=>ct("Import cancelled."));nu?.addEventListener("click",by);cr?.addEventListener("click",ky);rh?.addEventListener("click",yg);ob?.addEventListener("click",()=>ul(!0));sb?.addEventListener("click",()=>ul(!1));tr?.addEventListener("cancel",n=>{n.preventDefault(),ul(!1)});tu?.addEventListener("click",uo);oo?.addEventListener("click",att);ar?.addEventListener("click",()=>lp({source:"manual"}));di?.addEventListener("click",on);ai?.addEventListener("click",hn);lr?.addEventListener("click",rn);yt?.addEventListener("click",ln);pt?.addEventListener("click",an);co?.addEventListener("input",ep);si?.addEventListener("click",vn);ib?.addEventListener("click",op);fb?.addEventListener("click",()=>fl(!0));eb?.addEventListener("click",()=>fl(!1));nr?.addEventListener("cancel",n=>{n.preventDefault(),fl(!1)});wt?.addEventListener("click",pn);bt?.addEventListener("click",wn);so?.addEventListener("input",hp);oi?.addEventListener("click",bn);kw?.addEventListener("click",cp);nb?.addEventListener("click",()=>el(!0));tb?.addEventListener("click",()=>el(!1));gi?.addEventListener("cancel",n=>{n.preventDefault(),el(!1)});iu?.addEventListener("click",pg);const ytt=nf?vtt():Promise.resolve();if(nf||(c(p,"Please sign in to PlotDirector."),c(ot,"Please sign in to PlotDirector."),c(et,"Please sign in to PlotDirector."),c(d,"Please sign in to PlotDirector."),pi()),!window.Office||typeof window.Office.onReady!="function"){ac("Word Companion ready");t("Word document access is unavailable.");k("Manual");f({host:"Unavailable",platform:"Unavailable",ready:"No",wordApi:"No"});ny().catch(n=>{console.debug("Word Companion presence unavailable.",n)});return}kv().then(async()=>{ac("Word Companion ready"),await ytt,await tn(),await ny(),it?ltt():(t("Word document access is unavailable."),k("Manual"))}).catch(n=>{console.error("Office readiness check failed.",n),be("Unable to connect to PlotDirector."),ac("Word Companion unavailable"),t("Word document access is unavailable."),f({ready:"No",wordApi:"No",lastScan:"Failure"})})})(); \ No newline at end of file +(()=>{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]"),g=document.querySelector("[data-first-run-new-book-title]"),y=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]"),ge=document.querySelector("[data-plotdirector-scene-title]"),ye=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]"),gt=document.querySelector("[data-create-chapter-dialog-chapter]"),yt=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,gn=[],yn=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=!1,qn=!1,An=0,$n=!1,Tn=!1,jn=!1,Wn=null,Rn="Manual",Un=!1,Mn=null,On=!1,Bn=0,Hn=!1,Gn=0,Fn=null;const Vn=1200;let Kn=!1,Yn=!1,Jn=null,_n=!1,zn=!1,Qn=null,Xn=null,Zn=null,er=!1,tr=!1,nr=null,rr="",or=[],ar=null,cr="",ir=null,sr="",dr=[],lr=null,ur="",pr=null,hr="",mr=[],gr=null,yr="",wr="",fr="",Ir="",br=0,Sr=null,Cr=null,kr={characters:[],assets:[],locations:[],primaryLocationId:null};const vr=e=>{const t=Date.now();t-br<1e3||(br=t,console.debug(`[Word Companion Runtime] ${e}`))},xr=e=>{console.debug(`[Word Companion Timing] ${e}`)},Nr=e=>{R&&(R.textContent=e)},Er=e=>"links"===e?"review":"structure"===e?"maintenance":e,Dr=(e,t=!0)=>{const r=Er(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)},Pr=()=>{const e=Er((e=>{try{return window.localStorage.getItem(e)||""}catch{return""}})(n));Dr(o.has(e)?e:"scene",!1)},Lr=e=>{U&&(U.textContent=e),M&&(M.textContent=e)},qr=e=>{V&&(V.textContent=e)},Ar=e=>{K&&(K.textContent=e)},$r=e=>{Z&&(Z.textContent=e)},Tr=e=>{ie&&(ie.textContent=e)},jr=e=>{const t=t=>Object.prototype.hasOwnProperty.call(e,t);t("anchorType")&&No(Bt,Zr(e.anchorType)),t("storedSceneId")&&No(Ht,Zr(e.storedSceneId)),t("storedChapterId")&&No(Gt,Zr(e.storedChapterId)),t("resolutionMethod")&&No(Ft,Zr(e.resolutionMethod))},Wr=()=>{const e=Ro(),t=Number.isInteger(ln)&&ln>0&&dn===ln,n=!Number.isInteger(un)||un<=0||sn===un,r=t&&n;No(se,r?"Attached to PlotDirector":"Not attached"),No(de,e?.bookId?String(e.bookId):"-"),No(le,Number.isInteger(un)&&un>0?String(un):"-"),No(ue,Number.isInteger(ln)&&ln>0?String(ln):"-"),pe&&(Mr(pe,r&&!mn),pe.disabled=!ln||r&&!mn,pe.textContent=dn&&!r?"Reattach PlotDirector IDs":"Attach PlotDirector IDs"),jr({anchorType:hn||"Title Match",storedSceneId:dn,storedChapterId:sn,resolutionMethod:pn})},Rr=(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):"-"),No(ve,Number.isInteger(n)?String(n):"-"),Wr())},Ur=()=>{Jn&&(window.clearTimeout(Jn),Jn=null)},Mr=(e,t)=>{e&&(e.hidden=t)},Or=()=>{Mr(he,!Cn)},Br=e=>{No(Ee,e)},Hr=e=>{No(oe,e)},Gr=(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)}Hr(e),Vr()},Fr=(e=Sn)=>((e=Sn)=>xa.flatMap(t=>Array.isArray(e?.[t.key])?e[t.key]:[]))(e).filter(e=>"couldLink"===e.category||"wouldCreateChapters"===e.category||"wouldCreateScenes"===e.category),Vr=()=>{re&&(re.disabled=jn||0===Fr().length)},Kr=()=>{ne&&(ne.disabled=jn||!Wo()||!Ro()),Vr()},Yr=()=>{Me&&(Me.textContent=ln?`Editing links for: ${fr||on||"Current scene"}`:"Link a PlotDirector scene first.")},Jr=()=>{const e=Un||!ln,t=e||!un||_n;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},_r=e=>{"Live"!==e&&"Manual"!==e||(Rn=e),No(X,e||Rn||"Manual")},zr=(e,t="")=>{Un=!!e,Un&&(Ur(),Ic()),No(X,Un?"Stale":Rn),Jr(),t&&($r(t),oo(t))},Qr=(e,t=null,n="")=>{ln=Number.isInteger(e)&&e>0?e:null,un=Number.isInteger(t)&&t>0?t:null,pn=n||"",ln!==Qn&&(Xn=null),ln||(Ur(),Ic()),Jr(),Br(Un?"Refresh Current Scene before syncing.":ln?"Ready to sync.":"No resolved PlotDirector scene."),oo(Un?"Refresh Current Scene before saving.":ln?"Ready to save.":"Link a PlotDirector scene first."),Yr(),Wr()},Xr=e=>{Y&&(Y.disabled=e,Y.textContent=e?"Refreshing...":"Refresh Current Scene")},Zr=e=>null==e||""===e?"-":String(e),eo=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"})}`},to=e=>{kn=e||null;const t=kn?.lastSyncUtc||kn?.manuscriptDocument?.lastSyncUtc;No(Ce,eo(t)),No(Ne,eo(t)),jo()},no=()=>{vn=null,xn=null,Nn=null,Gn+=1,Fn=null,bc(),Sc(),Cc(),to(null)},ro=()=>{xn=null,Nn=null},oo=e=>{No(Ue,e)},ao=e=>{No(ze,e)},co=e=>{No(Fe,e)},io=()=>!!Ro()&&!!rn&&!un,so=()=>!!Ro()&&!!on&&Number.isInteger(un)&&un>0,lo=()=>{Mr(Oe,!0),co(""),He&&(He.disabled=!0,He.textContent="Create Chapter in PlotDirector"),Ge&&(Ge.disabled=!0,Ge.textContent="Link to Existing Chapter")},uo=(e="")=>{No(Be,Zr(rn)),Mr(Oe,!1),co(e);const t=io();He&&(He.disabled=!t,He.textContent="Create Chapter in PlotDirector"),Ge&&(Ge.disabled=!t,Ge.textContent="Link to Existing Chapter")},po=()=>{Mr(Ve,!0),ao(""),Je&&(Je.disabled=!0,Je.textContent="Create Scene in PlotDirector"),_e&&(_e.disabled=!0,_e.textContent="Link to Existing Scene")},ho=(e="")=>{No(Ke,Zr(rn)),No(Ye,Zr(on)),Mr(Ve,!1),ao(e);const t=so();Je&&(Je.disabled=!t,Je.textContent="Create Scene in PlotDirector"),_e&&(_e.disabled=!t,_e.textContent="Link to Existing Scene")},mo=e=>{Tr(e),Mr(me,!0),Mr(De,!0),Mr(Le,!1),lo(),po(),pn="",fr="",Ir="",hn=dn?"SceneID Anchor":sn?"ChapterID Anchor":"Title Match",mn=!1,kr={characters:[],assets:[],locations:[],primaryLocationId:null},yo(Te,[],"character"),yo(je,[],"asset"),wo([],null,!1),oa(),Qr(null),No(xe,"-"),No(ge,"-"),No(ye,"-"),No(we,"-"),No(fe,"-"),No(Ie,"-"),No(Se,"-"),Mr(be,!0),Wr(),Yr()},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,yo=(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)}},wo=(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},fo=e=>String(e||"").trim().replace(/\s+/g," "),Io=(e,t)=>{const n=fo(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 fo(n.replace(r,""))||n},bo=e=>{const t=t=>Object.prototype.hasOwnProperty.call(e,t);t("projectId")&&No(kt,Zr(e.projectId)),t("projectTitle")&&No(vt,Zr(e.projectTitle)),t("bookId")&&No(xt,Zr(e.bookId)),t("bookTitle")&&No(Nt,Zr(e.bookTitle)),t("detectedChapter")&&No(Et,Zr(e.detectedChapter)),t("detectedScene")&&No(Dt,Zr(e.detectedScene)),t("requestChapter")&&No(Pt,Zr(e.requestChapter)),t("requestScene")&&No(Lt,Zr(e.requestScene)),t("result")&&No(qt,Zr(e.result)),t("chapterId")&&No(At,Zr(e.chapterId)),t("sceneId")&&No($t,Zr(e.sceneId))},So=async e=>{if(Nn===e&&Array.isArray(xn?.chapters))return xn.chapters;const t=await Qa(`/api/word-companion/books/${e}/structure`);return Nn=e,xn=t||null,Array.isArray(t?.chapters)?t.chapters:[]},Co=async e=>{const t=await Qa(`/api/word-companion/books/${e}/chapters`);return Array.isArray(t?.chapters)?t.chapters:[]},ko=e=>{if(Number.isInteger(un)&&un>0){const t=e.find(e=>e.chapterId===un);if(t)return t}const t=Io(rn,"chapter").toLocaleLowerCase();return t&&e.find(e=>Io(e.title,"chapter").toLocaleLowerCase()===t)||null},vo=async()=>{const e=Ro();return e?ko(await So(e.bookId)):null},xo=async(e,t,n,r,o)=>{const a=await Qa(`/api/word-companion/scenes/${t}/companion`);try{a.revisionStatus=await(async(e,t)=>{const n=await Qa(`/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,zr(!1),Tr("Linked to PlotDirector"),$r(""),lo(),po(),Qr(t,n,r),c=a,fr=c?.sceneTitle||on||"",Ir=c?.chapterTitle||rn||"",No(ge,Zr(c?.sceneTitle)),No(ye,Zr(c?.revisionStatus||c?.revisionStatusName||"Not provided")),No(we,Zr(c?.estimatedWords)),No(fe,Zr(c?.actualWords)),No(xe,Zr(c?.actualWords)),No(Ie,c?.blocked?"Blocked":"Not blocked"),c?.blockedReason?(No(Se,c.blockedReason),Mr(be,!1)):(No(Se,"-"),Mr(be,!0)),Pe&&(Pe.textContent=c?.writingBrief||"No writing brief has been added for this scene."),kr={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},yo(Te,kr.characters,"character",!!ln&&!Un),yo(je,kr.assets,"asset",!!ln&&!Un),wo(kr.locations,kr.primaryLocationId,!!ln&&!Un),oa(),Yr(),oo(Un?"Refresh Current Scene before saving.":ln?"Ready to save.":"No resolved PlotDirector scene."),Mr(me,!1),Mr(De,!1),Mr(Le,!1),ec(t,n),fc(),Pc()},No=(e,t)=>{if(e){const n=null==t?"":String(t);e.textContent!==n&&(e.textContent=n)}},Eo=e=>{const t=t=>Object.prototype.hasOwnProperty.call(e,t);t("host")&&No(jt,e.host),t("platform")&&No(Wt,e.platform),t("ready")&&No(Rt,e.ready),t("wordApi")&&No(Ut,e.wordApi),t("lastScan")&&No(Mt,e.lastScan),t("lastError")&&No(Ot,e.lastError),t("paragraphs")&&No(Vt,e.paragraphs),t("heading1")&&No(Kt,e.heading1),t("heading2")&&No(Yt,e.heading2)},Do=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)},Po=async()=>{if(!window.Office||"function"!=typeof window.Office.onReady)return Zt=!1,en=!1,tn="Unavailable",nn="Unavailable",Eo({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,Eo({host:tn,platform:nn,ready:"Yes",wordApi:en?"Yes":"No"}),e},Lo=e=>{try{const t=window.localStorage.getItem(e),n=Number.parseInt(t||"",10);return Number.isInteger(n)&&n>0?n:null}catch{return null}},qo=(e,t)=>{try{t?window.localStorage.setItem(e,String(t)):window.localStorage.removeItem(e)}catch{}},Ao=(e,t)=>{if(!e)return;e.innerHTML="";const n=document.createElement("option");n.value="",n.textContent=t,e.append(n),e.disabled=!0},$o=(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},To=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},jo=()=>{const e=!!Cn;Mr(d,!e),Mr(F,e),e&&(No(l,kn?.projectTitle||Wo()?.title||"Project"),No(u,kn?.bookTitle||To(Ro())||"Book"))},Wo=()=>{const e=Number.parseInt(H?.value||"",10);return Qt.find(t=>t.projectId===e)||null},Ro=()=>{const e=Number.parseInt(G?.value||"",10);return Xt.find(t=>t.bookId===e)||null},Uo=()=>{const e=Number.parseInt(h?.value||"",10);return Qt.find(t=>t.projectId===e)||null},Mo=()=>{const e=Number.parseInt(m?.value||"",10);return Xt.find(t=>t.bookId===e)||null},Oo=()=>{const t=Cn?.projectId||Uo()?.projectId||Wo()?.projectId||Lo(e);return Number.isInteger(t)&&t>0?t:null},Bo=()=>{const e=Cn?.bookId||Mo()?.bookId||Ro()?.bookId||Lo(t);return Number.isInteger(e)&&e>0?e:null},Ho=()=>{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}},Go=()=>({userID:Number.isInteger(_t)&&_t>0?_t:null,companionVersion:zt,machineName:"",documentOpen:!!en,currentDocumentName:Ho(),linkedProjectID:Oo(),linkedBookID:Bo()}),Fo=()=>{(async()=>{Sr&&Sr.state===signalR.HubConnectionState.Connected&&await Sr.invoke("CompanionHeartbeat",Go())})().catch(e=>{console.debug("Word Companion heartbeat unavailable.",e)})},Vo=async()=>{Jt&&window.signalR&&!Sr&&(Sr=(new signalR.HubConnectionBuilder).withUrl("/hubs/word-companion-follow").withAutomaticReconnect().build(),Sr.onreconnected(Fo),Sr.on("ScanCurrentDocument",e=>{ba(e)}),Sr.onclose(()=>{Cr&&(window.clearInterval(Cr),Cr=null)}),await Sr.start(),await Sr.invoke("RegisterCompanion",Go()),Cr=window.setInterval(Fo,25e3))};window.addEventListener("beforeunload",()=>{Cr&&(window.clearInterval(Cr),Cr=null),Sr&&Sr.stop().catch(()=>{})});const Ko=e=>No(I,e),Yo=()=>{const e=Uo(),t=Mo(),n=qn&&Date.now()-An>=750;w&&(w.disabled=!e||!t||$n),y&&(y.disabled=!e||!String(g?.value||"").trim()||$n),P&&(P.disabled=!En?.canImport||!Dn||$n),x&&(x.disabled=!n||Tn||0===Qo().length)},Jo=e=>{Mr(p,!e),Mr(c,e),Or(),jo();for(const t of s)Mr(t,!!e||"scene"!==t.dataset.tabPanel&&!t.classList.contains("is-active"));e||Pr()},_o=()=>{if(!h)return;if(0===Qt.length)return void Ao(h,"No projects available.");$o(h,"Choose a project",Qt,"projectId","title");const e=Number.parseInt(H?.value||"",10);Number.isInteger(e)&&e>0&&(h.value=String(e))},zo=(e="Select a Project and Book to begin.")=>{En=null,Dn=null,Pn=[],Ln=!1,qn=!1,An=0,Mr(b,!0),Mr(D,!0),Mr(S,!0),Mr(v,!0),b&&(b.innerHTML=""),k&&(k.innerHTML=""),No(C,""),Ko(e),Yo()},Qo=()=>k?[...k.querySelectorAll("input[type='checkbox']:checked")].map(e=>String(e.value||"").trim()).filter(Boolean):[],Xo=e=>{if(Pn=Array.isArray(e)?e:[],S&&k){if(k.innerHTML="",Mr(S,!1),Mr(v,!Ln||0===Pn.length),Mr(x,!1),Mr(N,!1),Mr(E,!0),0===Pn.length)return No(C,"No likely major characters found."),void Yo();No(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",Yo);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)}Yo()}},Zo=()=>{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}},ea=()=>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."))}),ta=()=>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)),na=()=>Cn?.documentGuid||Zo()?.documentGuid||"",ra=()=>{const e=Wo(),t=Ro();O&&(O.textContent=e?.title||"(Not connected)"),B&&(B.textContent=t?To(t):"(Not connected)"),ee&&(ee.textContent=t?"":"Select a PlotDirector book before refreshing."),Kr(),jo()},oa=()=>{const e=Array.isArray(kr.characters)?kr.characters.length:0,t=Array.isArray(kr.assets)?kr.assets.length:0,n=Array.isArray(kr.locations)?kr.locations.length:0;No(qe,`Characters (${e})`),No(Ae,`Assets (${t})`),No($e,`Locations (${n})`)},aa=e=>String(e?.styleBuiltIn||e?.style||"").replace(/\s+/g,"").toLowerCase(),ca=(e,t)=>{const n=aa(e);return n===`heading${t}`||n.endsWith(`.heading${t}`)||n.includes(`heading${t}`)},ia=e=>String(e||"").trim().split(/\s+/).filter(Boolean).length,sa=(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},da=e=>{try{return Array.isArray(e?.contentControls?.items)?e.contentControls.items:[]}catch(e){return console.warn("Word paragraph content controls were not available.",e),[]}},la=(e,t)=>{const n=da(e);for(const e of n){const n=sa(e.tag,t);if(n)return n}return null},ua=(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(ca(e,1))return r+=1,a={index:n.length+1,paragraphIndex:t,title:i,anchorId:la(e,"PD-CHAPTER"),scenes:[]},n.push(a),void(c=null);if(ca(e,2)){if(o+=1,!a)return;return c={index:a.scenes.length+1,paragraphIndex:t,title:i,anchorId:la(e,"PD-SCENE"),wordCount:0},void a.scenes.push(c)}c&&(c.wordCount+=ia(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()&&aa(t)===aa(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}},pa=new Set(["***","###"]),ha=(e,t)=>{const n=String(e?.text||"").trim();return{index:t,text:n,styleBuiltIn:e?.styleBuiltIn||"",style:e?.style||"",wordCount:ia(n),chapterAnchorId:null,sceneAnchorId:null}},ma=(e,t)=>{e&&(e.endParagraphIndex=t)},ga=(e,t)=>{e&&(e.endParagraphIndex=t,ma(e.scenes.at(-1),t))},ya=(e,t,n)=>e?.[t]??e?.[n]??null,wa=(e,t,n)=>e?.[t]??e?.[n]??null,fa=async(e,t,n,r={})=>{Sr&&Sr.state===signalR.HubConnectionState.Connected&&await Sr.invoke("ReportOnboardingScanProgress",{userID:wa(e,"userID","UserID"),onboardingID:wa(e,"onboardingID","OnboardingID"),message:t,percentComplete:n,chapterCount:r.chapterCount||0,sceneCount:r.sceneCount||0,characterCandidateCount:r.characterCandidateCount||0,totalWordCount:r.totalWordCount||0})},Ia=async(e,t)=>{Sr&&Sr.state===signalR.HubConnectionState.Connected&&await Sr.invoke("FailOnboardingScan",{userID:wa(e,"userID","UserID"),onboardingID:wa(e,"onboardingID","OnboardingID"),message:t})},ba=async e=>{var t;if(en&&window.Word)try{await fa(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:la(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?la(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||pa.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=ia(n);return ca(e,1)?(s+=1,l(e,n),i+=r,void(a.wordCount+=r)):(a||l(e,"Opening pages",!0),ca(e,2)?(u(e,n),i+=r,void p(n,r)):void(pa.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:Ho()||"Word manuscript",companionDocumentIdentifier:na(),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 fa(e,"Detecting chapters",20);for(const e of n.items)(ca(e,1)||ca(e,2))&&e.contentControls.load("items/tag,title");return await t.sync(),n.items}),e);await fa(e,"Detecting scene breaks",50,n),await fa(e,`Scene ${n.sceneCount} of ${n.sceneCount} scanned`,70,n),await fa(e,"Finding character candidates",85,n);const r=n.documentTextForDiscovery||"";delete n.documentTextForDiscovery;try{const e=await Xa("/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:ya(e,"text","Text")||ya(e,"name","Name")||"",mentionCount:Number.parseInt(ya(e,"mentionCount","MentionCount")||"0",10)||0,qualityScore:Number.parseInt(ya(e,"qualityScore","QualityScore")||"0",10)||0,category:ya(e,"category","Category")||"PossibleCharacter",reason:ya(e,"reason","Reason")||"",isExistingCharacterMatch:!!ya(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 fa(e,"Preparing preview",95,n),await Sr.invoke("CompleteOnboardingScan",n)}catch(t){console.error("Unable to complete onboarding scan.",t),await Ia(e,Sa(t))}else await Ia(e,"Open your manuscript in Microsoft Word, then try the scan again.")},Sa=e=>{const t=Do(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}`},Ca=(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()&&aa(e)===aa(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&&(Rr(t?.title,n?.title,n?.wordCount,t?.anchorId,n?.anchorId,t?.index),r)},va=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)}},xa=[{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"}],Na={alreadyLinked:"Already Linked",couldLink:"Could Link",wouldCreateChapter:"Would Create Chapter",wouldCreateScene:"Would Create Scene",manualReview:"Manual Review Required"},Ea={alreadyLinked:"✓",couldLink:"~",wouldCreateChapter:"+",wouldCreateScene:"+",manualReview:"?"},Da=(e,t)=>Io(e,t).toLocaleLowerCase(),Pa=({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:""}),La=(e,t)=>{Array.isArray(e[t.category])&&e[t.category].push(t)},qa=e=>Array.isArray(e?.scenes)?e.scenes:[],Aa=(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=Pa({category:"alreadyLinked",action:"alreadyLinked",type:"Chapter",wordTitle:e.title,match:`Chapter ${o.chapterId}: ${o.title}`,anchorStatus:r,pdChapter:o,wordChapter:e});return La(n,t),t}const a=Pa({category:"manualReview",action:"manualReview",type:"Chapter",wordTitle:e.title,anchorStatus:`${r} not found`,matches:[],pdChapter:null,wordChapter:e});return La(n,a),a}const o=t.filter(t=>Da(t.title,"chapter")===Da(e.title,"chapter"));if(1===o.length){const t=Pa({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 La(n,t),t}if(o.length>1){const t=Pa({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 La(n,t),t}const a=Pa({category:"wouldCreateChapters",action:"wouldCreateChapter",type:"Chapter",wordTitle:e.title,anchorStatus:r,pdChapter:null,wordChapter:e});return La(n,a),a},$a=(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=qa(n).find(e=>e.sceneId===t);if(e)return{chapter:n,scene:e}}return null})(n,e.anchorId);return a?void La(r,Pa({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 La(r,Pa({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 La(r,Pa({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 La(r,Pa({category:"wouldCreateScenes",action:"wouldCreateScene",type:"Scene",wordTitle:e.title,anchorStatus:o,wordChapter:t?.wordChapter,wordScene:e,parentItem:t}));const a=qa(t.pdChapter).filter(t=>Da(t.title,"scene")===Da(e.title,"scene"));1!==a.length?a.length>1?La(r,Pa({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})):La(r,Pa({category:"wouldCreateScenes",action:"wouldCreateScene",type:"Scene",wordTitle:e.title,anchorStatus:o,pdChapter:t.pdChapter,wordChapter:t.wordChapter,wordScene:e,parentItem:t})):La(r,Pa({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}))},Ta=e=>{const t=document.createElement("article");t.className="word-companion-preview-item";const n=document.createElement("strong");n.textContent=`${Ea[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: ${Na[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},ja=e=>{if(ae){ae.innerHTML="";for(const t of xa){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(Ta(e));ae.append(n)}Vr()}},Wa=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)(ca(e,1)||ca(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=ua(t.items,n.items);return r.bodyParagraphs=t.items,r},Ra=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(ha),r=[];let o=0,a=0,c=null,i=null,s=0;const d=(e,t=null,n=null)=>c?(ma(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&&(ca(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&&pa.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=Ca(o,r.items);return vn=o,ka(a),vr("Cache rebuilt."),o},Ua=()=>window.performance&&"function"==typeof window.performance.now?window.performance.now():Date.now(),Ma=e=>!!e&&!e.stale&&e.generation===Gn&&e.documentGuid===na()&&e.sceneId===ln,Oa=async(e={})=>{const t=!!e.includeSelection,n=!1!==e.updateDisplay,r=Gn,o=Ua(),a=Wo(),c=Ro(),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=Ua(),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(Ua()-s);if(r!==Gn)return xr(`Office snapshot: ${l}ms stale`),{stale:!0,generation:r};let u=!1;if(t){const e=Ca(vn,d.selectionParagraphs);u=ka(e)}const p=vn?.currentScene;if(!p||!Number.isInteger(p.startParagraphIndex)||!Number.isInteger(p.endParagraphIndex))return null;const h=[];let m=0;const g=Math.min(p.endParagraphIndex,d.bodyTexts.length-1),y=vn.currentChapter?.startParagraphIndex;for(let e=p.startParagraphIndex;e<=g;e+=1){const t=String(d.bodyTexts[e]||"").trim();t&&!pa.has(t)&&e!==y&&(h.push(t),m+=ia(t))}p.wordCount=m,n&&Rr(vn.currentChapter?.title,p.title,m,vn.currentChapter?.anchorId,p.anchorId,vn.currentChapter?.index);const w={documentGuid:na(),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 Fn=w,xr(`Office snapshot: ${l}ms; scene words: ${m}; total snapshot: ${Math.round(Ua()-o)}ms`),w},Ba=async()=>Ma(Fn)?Fn:await Oa(),Ha=async()=>{if($r(""),!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return Rr(null,null,null),$r("Word document access is unavailable."),Eo({lastScan:"Failure",lastError:"Word document access is unavailable."}),!1;J&&(J.disabled=!0,J.textContent="Scanning..."),$r("Scanning document structure...");try{const e=await window.Word.run(async e=>{const t=Ro();return t?await Ra(e,!!t.usesExplicitScenes):(vn=null,await Wa(e))});return vn||Rr(e.currentChapter?.title,e.currentScene?.title,e.currentScene?.wordCount,e.currentChapter?.anchorId,e.currentScene?.anchorId,e.currentChapter?.index),mo("Not detected"),va(e),$r(""),Eo({lastScan:"Success",lastError:"-",paragraphs:String(e.paragraphCount),heading1:String(e.heading1Count),heading2:String(e.heading2Count)}),!0}catch(e){const t=Do(e);return console.error("Unable to scan the Word document.",e),Rr(null,null,null),$r(`Unable to scan the Word document: ${t}`),Eo({lastScan:"Failure",lastError:t}),!1}finally{J&&(J.disabled=!1,J.textContent="Refresh Document Structure")}},Ga=async()=>{const e=Ro();if(!Wo()||!e)return Hr("Select a project and book to analyse manuscript structure."),!1;if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return Hr("Word document access is unavailable."),Eo({lastScan:"Failure",lastError:"Word document access is unavailable."}),!1;ne&&(ne.disabled=!0,ne.textContent="Analysing..."),Hr("Analysing manuscript structure...");try{const t=await window.Word.run(async e=>await Wa(e)),n=await Qa(`/api/word-companion/books/${e.bookId}/structure`);return Rr(t.currentChapter?.title,t.currentScene?.title,t.currentScene?.wordCount,t.currentChapter?.anchorId,t.currentScene?.anchorId,t.currentChapter?.index),va(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=Aa(t,r,n);for(const o of qa(t))$a(o,e,r,n)}return n})(t,n),ja(Sn),Hr("Preview generated. No changes have been applied."),Eo({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),Hr("Unable to analyse manuscript structure."),Eo({lastScan:"Failure",lastError:Do(e)}),!1}finally{ne&&(ne.textContent="Analyse Manuscript Structure",Kr())}},Fa=e=>{Wn&&(Wn(e),Wn=null),It?.close?It.close():It&&(It.hidden=!0)},Va=()=>{const e=((e=Sn)=>{const t=Fr(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=>{Wn=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.`))},Ka=(e,t,n="")=>{e.result=t,e.error=n},Ya=e=>Number.isInteger(e?.wordChapter?.index)&&e.wordChapter.index>0?10*e.wordChapter.index:null,Ja=e=>Number.isInteger(e?.wordScene?.index)&&e.wordScene.index>0?10*e.wordScene.index:null,_a=async(e,t)=>{const n=await Xa(`/api/word-companion/books/${e}/chapters`,{title:fo(t.wordTitle),sortOrder:Ya(t)});if(!n?.chapterId)throw new Error("Chapter creation did not return a chapter ID.");ro(),t.pdChapter={chapterId:n.chapterId,title:n.title||t.wordTitle,sortOrder:n.sortOrder||Ya(t)||0,scenes:[]},t.match=`Chapter ${t.pdChapter.chapterId}: ${t.pdChapter.title}`},za=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 Xa(`/api/word-companion/books/${e}/chapters/${n.chapterId}/scenes`,{sceneTitle:fo(t.wordTitle),sortOrder:Ja(t)});if(!r?.sceneId)throw new Error("Scene creation did not return a scene ID.");ro(),t.pdChapter=n,t.pdScene={sceneId:r.sceneId,title:t.wordTitle,sortOrder:Ja(t)||0},t.match=`Scene ${t.pdScene.sceneId}: ${t.pdScene.title}`},Qa=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()},Xa=(e,t)=>Qa(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),Za=async(e="")=>{qn=!1,An=0,Pn=[],k&&(k.innerHTML=""),Mr(S,!0),Jo(!1),Ko(""),Mr(v,!0),e&&$r(e);const t=await tc();e&&t&&rn&&$r(e)},ec=async(e=ln,t=un)=>{const n=Wo(),r=Ro(),o=na(),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(wr!==i){wr=i;try{await Xa("/api/word-companion/runtime/current-scene",{documentGuid:o,projectId:n.projectId,bookId:r.bookId,chapterId:c,sceneId:a}),vr("Current scene follow event sent.")}catch(e){wr="",console.debug("Unable to notify PlotDirector of current scene.",e)}}},tc=async()=>{const e=Ro();if(!(e&&Zt&&en&&window.Word&&"function"==typeof window.Word.run))return!1;$r("Reading manuscript position...");try{ro(),await Promise.all([kc(!0),vc(!0),xc(!0)]),await So(e.bookId);const t=await window.Word.run(async t=>await Ra(t,!!e.usesExplicitScenes));return va(t),Eo({lastScan:"Success",lastError:"-",paragraphs:String(t.paragraphCount),heading1:String(t.heading1Count),heading2:String(t.heading2Count)}),rn?(on?await qc():await Lc(),zr(!1),_r("Live"),$r(""),!0):(mo("Not detected"),zr(!1),_r("Live"),$r("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,_r("Manual"),$r(`Unable to read manuscript position: ${Do(e)}`),Eo({lastScan:"Failure",lastError:Do(e)}),!1}},nc=async n=>{Cn=null,Tc(),Ur(),no(),mo("Not detected"),_r("Manual"),Lr("Connected"),qo(e,null),qo(t,null),H&&(H.value=""),Ao(G,"Select a project first"),_o(),h&&(h.value=""),Ao(m,"Select a project first"),zo(n||"Select a Project and Book to begin."),$r(n||"Manuscript unlinked."),Br("No resolved PlotDirector scene."),Jo(!0),Or()},rc=e=>e?[...e.querySelectorAll("input[type='checkbox']:checked")].map(e=>Number.parseInt(e.value||"",10)).filter(e=>Number.isInteger(e)&&e>0):[],oc=()=>{const e=Number.parseInt(We?.value||"",10);return Number.isInteger(e)&&e>0?e:null},ac=(e,t,n,r)=>{const o=((e,t)=>da(e).find(e=>sa(e.tag,t))||null)(e,r),a=o||e.getRange().insertContentControl();return a.title=t,a.tag=n,a.appearance="Hidden",a},cc=async(e="PlotDirector IDs attached successfully.")=>{if(!ln||!un)return $r("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 $r("Word document access is unavailable."),!1;pe&&(pe.disabled=!0,pe.textContent="Attaching...");try{return await window.Word.run(async e=>{const t=await Wa(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.");ac(n,"PlotDirector Chapter",`PD-CHAPTER-${un}`,"PD-CHAPTER"),ac(r,"PlotDirector Scene",`PD-SCENE-${ln}`,"PD-SCENE"),await e.sync()}),sn=un,dn=ln,mn=!1,hn="SceneID Anchor",pn="Anchor",$r(e),Eo({lastError:"-"}),Wr(),!0}catch(e){return console.error("Unable to attach PlotDirector IDs.",e),$r("Unable to attach PlotDirector IDs."),Eo({lastError:Do(e)}),Wr(),!1}finally{pe&&(pe.textContent=dn===ln?"Attach PlotDirector IDs":"Reattach PlotDirector IDs",Wr())}},ic=async e=>{if(!un)return co("No resolved PlotDirector chapter."),!1;if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return co("Word document access is unavailable."),!1;try{return await window.Word.run(async e=>{const t=await Wa(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.");ac(n,"PlotDirector Chapter",`PD-CHAPTER-${un}`,"PD-CHAPTER"),await e.sync()}),sn=un,mn=!1,hn="ChapterID Anchor",pn="Chapter anchor",co(e),$r(e),Eo({lastError:"-"}),Wr(),!0}catch(e){return console.error("Unable to attach PlotDirector chapter ID.",e),co("Unable to attach PlotDirector chapter ID."),Eo({lastError:Do(e)}),Wr(),!1}},sc=async e=>{lo(),await qc(),$r(e),co(e)},dc=e=>{bn&&(bn(e),bn=null),mt?.close?mt.close():mt&&(mt.hidden=!0)},lc=()=>{if(!lt)return;const e=fo(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)}},uc=()=>{st?.close?st.close():st&&(st.hidden=!0)},pc=async(e,t,n)=>{const r=Ro();if(!r)return ao("Select a PlotDirector book first."),!1;await xo(r.bookId,e,t,"Manual link","Title Match");return!!await cc(n)&&(ao(n),po(),!0)},hc=e=>{wn&&(wn(e),wn=null),rt?.close?rt.close():rt&&(rt.hidden=!0)},mc=()=>{if(!Ze)return;const e=fo(Xe?.value||"").toLocaleLowerCase(),t=gn.filter(t=>!e||String(t.title||"").toLocaleLowerCase().includes(e));if(Ze.innerHTML="",yn=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",()=>{yn=t,et&&(et.disabled=!1)});const o=document.createElement("span");o.textContent=e.title||"Untitled scene",n.append(r,o),Ze.append(n)}},gc=()=>{Qe?.close?Qe.close():Qe&&(Qe.hidden=!0)},yc=()=>{_n=!1,Jr(),zn&&(zn=!1,fc(1e3))},wc=async(e={})=>{const t=e.source||"manual",n=Ua();if(!ln)return void Br("No resolved PlotDirector scene.");if(!un)return void Br("No resolved PlotDirector chapter.");if(Un)return void Br("Refresh Current Scene before syncing.");const r=Ro(),o=na();if(!r||!o)return void Br("Bound manuscript metadata is unavailable.");if(_n)return zn=!0,void vr("Scene sync deferred; sync already running.");Ur(),_n=!0,zn=!1;let a=e.snapshot||null;try{a=Ma(a)?a:await Ba()}catch(e){return Br("Sync failed"),Eo({lastError:Do(e)}),vr("Scene sync failed."),void yc()}if(!Ma(a))return Br("Waiting for typing to pause"),vr("Scene sync skipped (stale snapshot)."),void yc();const c=a.wordCount;if(!Number.isInteger(c)||c<0)return Br("Sync failed"),vr("Scene sync failed."),void yc();if(Qn===a.sceneId&&Xn===c)return Br("Synced"),vr("Sync skipped (count unchanged)."),void yc();vr("Scene word count changed."),ke&&(ke.disabled=!0,ke.textContent="Syncing..."),Br("Syncing..."),vr(`Scene sync started (${t}).`);try{const e=Ua(),t=await Xa("/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(xr(`Word count sync API: ${Math.round(Ua()-e)}ms`),!Ma(a))return void vr("Scene sync result ignored (stale snapshot).");const i=t?.actualWords??c,s=new Date;Qn=a.sceneId,Xn=i,No(xe,Zr(i)),No(fe,Zr(i)),No(Ne,eo(s)),No(Ce,eo(s)),Br("Synced"),vr("Scene sync succeeded."),xr(`Total word count sync: ${Math.round(Ua()-n)}ms`)}catch(e){Br("Sync failed"),Eo({lastError:Do(e)}),vr("Scene sync failed.")}finally{ke&&(ke.textContent="Sync Current Scene"),yc()}},fc=(e=4e3)=>{Ur(),ln&&un&&!Un&&!_n&&(Jn=window.setTimeout(()=>{Jn=null,wc({source:"automatic"})},e))},Ic=()=>{Zn&&(window.clearTimeout(Zn),Zn=null)},bc=()=>{nr=null,rr="",or=[],ar=null,cr="",Ic()},Sc=()=>{ir=null,sr="",dr=[],lr=null,ur=""},Cc=()=>{pr=null,hr="",mr=[],gr=null,yr=""},kc=async(e=!1)=>{const t=Wo(),n=na();if(!t||!n)return or=[],[];if(!e&&nr===t.projectId&&rr===n&&or.length>0)return or;const r=await Qa(`/api/word-companion/runtime/characters?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(n)}`);return nr=t.projectId,rr=n,or=Array.isArray(r?.characters)?r.characters:[],vr("Character alias cache refreshed."),or},vc=async(e=!1)=>{const t=Wo(),n=na();if(!t||!n)return dr=[],[];if(!e&&ir===t.projectId&&sr===n&&dr.length>0)return dr;const r=await Qa(`/api/word-companion/runtime/assets?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(n)}`);return ir=t.projectId,sr=n,dr=Array.isArray(r?.assets)?r.assets:[],vr(`Asset cache loaded: ${dr.length} aliases.`),dr},xc=async(e=!1)=>{const t=Wo(),n=na();if(!t||!n)return mr=[],[];if(!e&&pr===t.projectId&&hr===n&&mr.length>0)return mr;const r=await Qa(`/api/word-companion/runtime/locations?projectId=${encodeURIComponent(t.projectId)}&documentGuid=${encodeURIComponent(n)}`);return pr=t.projectId,hr=n,mr=Array.isArray(r?.locations)?r.locations:[],vr(`Location cache loaded: ${mr.length} aliases.`),mr},Nc=(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)},Ec=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},Dc=async()=>{if(Zn=null,!ln||Un||!na())return;if(er)return tr=!0,void vr("Character suggestion detection deferred; detection already running.");er=!0,tr=!1;const e=Ua();try{const t=await Ba();if(!Ma(t))return void vr("Runtime suggestions skipped (stale snapshot).");const[n,r,o]=await Promise.all([kc(),vc(),xc()]);if(!Ma(t))return void vr("Runtime suggestions skipped after cache load (stale snapshot).");const a=t.sceneText||"",c=Ua(),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)||Nc(e,r.matchText)&&n.set(t,r.name||r.matchText||"Character")}return[...n.entries()].map(([e,t])=>({characterId:e,name:t}))})(a,n);xr(`Character detection: ${Math.round(Ua()-c)}ms`);const s=i.map(e=>e.characterId),d=s.slice().sort((e,t)=>e-t).join(","),l=Ua(),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)||Nc(e,r.matchText)&&n.set(t,r.name||r.matchText||"Asset")}return[...n.entries()].map(([e,t])=>({assetId:e,name:t}))})(a,r);xr(`Asset detection: ${Math.round(Ua()-l)}ms`);const p=u.map(e=>e.assetId),h=p.slice().sort((e,t)=>e-t).join(","),m=Ua(),g=((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)||!Ec(r)||Nc(e,r.matchText)&&n.set(t,r.name||r.matchText||"Location")}return[...n.entries()].map(([e,t])=>({locationId:e,name:t}))})(a,o);xr(`Location detection: ${Math.round(Ua()-m)}ms`);const y=g.map(e=>e.locationId),w=y.slice().sort((e,t)=>e-t).join(",");if(!d||ar===ln&&cr===d)vr("Character suggestions skipped.");else{if(!Ma(t))return void vr("Character suggestions skipped before submit (stale snapshot).");const e=Ua(),n=await Xa("/api/word-companion/runtime/character-suggestions",{documentGuid:t.documentGuid,sceneId:t.sceneId,characterIds:s});if(xr(`Character suggestion API: ${Math.round(Ua()-e)}ms`),!Ma(t))return void vr("Character suggestion result ignored (stale snapshot).");if(ar=t.sceneId,cr=d,(n?.createdCount||0)>0){const e=i.slice(0,3).map(e=>e.name).join(", "),t=i.length>3?" +"+(i.length-3):"";$r(e?`Characters detected: ${e}${t}`:n.message)}vr(n?.message||"Character suggestions submitted.")}if(!h||lr===ln&&ur===h)vr("Asset suggestions skipped.");else{if(!Ma(t))return void vr("Asset suggestions skipped before submit (stale snapshot).");vr(`Asset matches found: ${u.map(e=>e.name).join(", ")}`);const e=Ua(),n=await Xa("/api/word-companion/runtime/asset-suggestions",{documentGuid:t.documentGuid,sceneId:t.sceneId,assetIds:p});if(xr(`Asset suggestion API: ${Math.round(Ua()-e)}ms`),!Ma(t))return void vr("Asset suggestion result ignored (stale snapshot).");if(lr=t.sceneId,ur=h,(n?.createdCount||0)>0){const e=u.slice(0,3).map(e=>e.name).join(", "),t=u.length>3?" +"+(u.length-3):"";$r(e?`Assets detected: ${e}${t}`:n.message)}vr(n?.message||"Asset suggestions submitted.")}if(!w||gr===ln&&yr===w)vr("Location suggestions skipped.");else{if(!Ma(t))return void vr("Location suggestions skipped before submit (stale snapshot).");vr(`Location matches found: ${g.map(e=>e.name).join(", ")}`);const e=Ua(),n=await Xa("/api/word-companion/runtime/location-suggestions",{documentGuid:t.documentGuid,sceneId:t.sceneId,locationIds:y});if(xr(`Location suggestion API: ${Math.round(Ua()-e)}ms`),!Ma(t))return void vr("Location suggestion result ignored (stale snapshot).");if(gr=t.sceneId,yr=w,(n?.createdCount||0)>0){const e=g.slice(0,3).map(e=>e.name).join(", "),t=g.length>3?" +"+(g.length-3):"";$r(e?`Locations detected: ${e}${t}`:n.message)}vr(n?.message||"Location suggestions submitted.")}xr(`Total suggestion pass: ${Math.round(Ua()-e)}ms`)}catch(e){console.error("Unable to detect runtime suggestions.",e),Eo({lastError:Do(e)})}finally{er=!1,tr&&(tr=!1,Pc(1500))}},Pc=(e=2500)=>{Ic(),!ln||Un||er?er&&(tr=!0):Zn=window.setTimeout(()=>{Dc()},e)},Lc=async()=>{const e=Wo(),t=Ro(),n=Io(rn,"chapter");if(bo({projectId:e?.projectId,projectTitle:e?.title,bookId:t?.bookId,bookTitle:t?To(t):void 0,detectedChapter:rn,detectedScene:on,requestChapter:n,requestScene:"",result:"Not run",chapterId:null,sceneId:null}),!t)return mo("Not detected"),$r("Select a PlotDirector book first."),!1;if(!rn)return mo("Not detected"),$r("No chapter detected in Word. Use Heading 1 for chapters."),!1;mo("Not detected");try{const e=await Co(t.bookId),r=sn?e.find(e=>e.chapterId===sn):null,o=n.toLocaleLowerCase(),a=o?e.find(e=>Io(e.title,"chapter").toLocaleLowerCase()===o):null,c=r||a;return c?(lo(),Qr(null,c.chapterId,r?"Chapter anchor":"Chapter title"),hn=r?"ChapterID Anchor":"Title Match",Tr("Chapter linked to PlotDirector"),$r("No scene detected in Word. Use Heading 2 for scenes."),bo({result:"Chapter matched",chapterId:c.chapterId,sceneId:null}),!0):(Qr(null),Tr("Chapter not found in PlotDirector."),$r("Chapter not found in PlotDirector."),bo({result:"Chapter not matched",chapterId:null,sceneId:null}),uo("Chapter not found in PlotDirector."),!1)}catch(e){return mo("Not found in PlotDirector"),Eo({lastError:Do(e)}),$r("Unable to load PlotDirector chapter data."),!1}},qc=async()=>{const e=Wo(),t=Ro(),n=Io(rn,"chapter"),r=Io(on,"scene");if(bo({projectId:e?.projectId,projectTitle:e?.title,bookId:t?.bookId,bookTitle:t?To(t):void 0,detectedChapter:rn,detectedScene:on,requestChapter:n,requestScene:r,result:"Not run",chapterId:null,sceneId:null}),jr({anchorType:dn?"SceneID Anchor":sn?"ChapterID Anchor":"Title Match",storedSceneId:dn,storedChapterId:sn,resolutionMethod:"-"}),!t)return mo("Not detected"),$r("Select a PlotDirector book first."),bo({result:"Error"}),!1;if(!rn||!on)return mo("Not detected"),$r("No scene detected in Word. Use Heading 2 for scenes."),bo({result:"Error"}),!1;ce&&(ce.disabled=!0,ce.textContent="Refreshing...");const o=()=>{ce&&(ce.disabled=!1,ce.textContent="Refresh PlotDirector Scene")};mo("Not detected"),$r("Resolving PlotDirector scene...");try{if(dn){const e=await(async(e,t)=>{const n=await Qa(`/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 bo({result:"Matched",chapterId:e.chapter.chapterId,sceneId:e.scene.sceneId}),await xo(t.bookId,e.scene.sceneId,e.chapter.chapterId,"Anchor","SceneID Anchor"),!0;mn=!0,hn="SceneID Anchor",Tr("Not found in PlotDirector"),$r("Stored PlotDirector ID is invalid."),bo({result:"Error",chapterId:sn,sceneId:dn}),jr({anchorType:"SceneID Anchor",storedSceneId:dn,storedChapterId:sn,resolutionMethod:"Anchor"})}if(sn){const e=(await So(t.bookId)).find(e=>e.chapterId===sn),n=e?(Array.isArray(e.scenes)?e.scenes:[]).find(e=>Io(e.title,"scene").toLocaleLowerCase()===r.toLocaleLowerCase()):null;if(n)return bo({result:"Matched",chapterId:e.chapterId,sceneId:n.sceneId}),await xo(t.bookId,n.sceneId,e.chapterId,"Anchor","ChapterID Anchor"),!0;e&&!dn?(hn="ChapterID Anchor",Qr(null,sn,"Chapter anchor"),bo({result:"Chapter matched",chapterId:sn,sceneId:null})):dn||(hn="ChapterID Anchor",bo({result:"Chapter anchor not found",chapterId:sn,sceneId:null}))}const e=await Xa(`/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 mo("Not found in PlotDirector"),mn=t,c&&(Qr(null,a,a===r?"Chapter anchor":"Chapter title"),Tr("Scene not found in PlotDirector.")),$r(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."),bo({result:"Not matched",chapterId:c?a:e?.chapterId,sceneId:e?.sceneId}),t||(c?(lo(),ho("Scene not found in PlotDirector.")):(po(),uo("Chapter not found in PlotDirector."))),o(),!1}const a=mn;return bo({result:a?"Matched by title fallback":"Matched",chapterId:e.chapterId,sceneId:e.sceneId}),await xo(t.bookId,e.sceneId,e.chapterId,a?"Title fallback":"Title","Title Match"),a&&(mn=!0,$r("Stored PlotDirector ID is invalid."),Wr()),!0}catch(e){const t=mn;return Eo({lastError:Do(e)}),mo("Not found in PlotDirector"),mn=t,$r(t?"Stored PlotDirector ID is invalid.":"Unable to load PlotDirector scene data."),bo({result:"Error"}),!1}finally{o()}},Ac=async()=>{if(Mn=null,Yn)return Hn=!0,void vr("Runtime update deferred; update already running.");if(!Jt||!Ro())return On=!1,void vr("Runtime update skipped.");if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return On=!1,_r("Manual"),void $r("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene.");if(!vn)return On=!1,_r("Manual"),void $r("Document structure is not cached yet. Use Refresh Current Scene.");const e=Date.now()-Bn;if(On&&e{Mn&&window.clearTimeout(Mn),Mn=window.setTimeout(Ac,Math.max(0,e)),_r("Waiting for typing to pause"),vr("Runtime update scheduled.")},Tc=()=>{Mn&&(window.clearTimeout(Mn),Mn=null),On=!1,Hn=!1},jc=()=>{((e="selection",t=1200)=>{if(On=!0,Bn=Date.now(),Gn+=1,Fn=null,Ur(),Ic(),_n&&(zn=!0),er&&(tr=!0),vr(`${e} changed.`),Yn)return Hn=!0,void _r("Waiting for typing to pause");$c(t)})("Selection")},Wc=()=>{if(Kn&&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:jc}),Kn=!1}catch(e){console.warn("Unable to remove Word selection tracking handler.",e)}},Rc=async(e,n)=>{if(vn=null,ro(),!e)return Xt=[],Ao(G,"Select a project first"),Ao(m,"Select a project first"),Ar(""),qo(t,null),ra(),mo("Not detected"),void Gr();Ao(G,"Loading books..."),Ar(""),mo("Not detected"),Gr("Select a book to analyse manuscript structure.");try{const r=await Qa(`/api/word-companion/projects/${e}/books`);if(Xt=Array.isArray(r.books)?r.books:[],0===Xt.length)return Ao(G,"No books available."),Ao(m,"No books available."),Ar("No books available."),qo(t,null),ra(),void zo("No books available.");$o(G,"Choose a book",Xt.map(e=>({...e,displayTitle:To(e)})),"bookId","displayTitle");const o=Xt.find(e=>e.bookId===n);o&&G?(G.value=String(o.bookId),qo(t,o.bookId)):qo(t,null),((e=null)=>{if(!m)return;if(0===Xt.length)return void Ao(m,"No books available.");$o(m,"Choose a book",Xt.map(e=>({...e,displayTitle:To(e)})),"bookId","displayTitle");const t=e||Number.parseInt(G?.value||"",10),n=Xt.find(e=>e.bookId===t);n&&(m.value=String(n.bookId)),Yo()})(o?.bookId||n),ra(),mo((Ro(),"Not detected")),Gr(Ro()?"Ready to analyse manuscript structure.":"Select a book to analyse manuscript structure."),zo(Mo()?"Ready to scan manuscript structure.":"Select a Book to begin.")}catch{Xt=[],Ao(G,"Unable to load books."),Ao(m,"Unable to load books."),Ar("Unable to load books."),qo(t,null),ra(),mo("Not detected"),Gr("Unable to load books."),zo("Unable to load books.")}};for(const e of i)e.addEventListener("click",()=>Dr(e.dataset.tabButton));Pr(),H?.addEventListener("change",async()=>{const n=Number.parseInt(H.value||"",10);no(),h&&Number.isInteger(n)&&n>0&&(h.value=String(n)),qo(e,Number.isInteger(n)&&n>0?n:null),qo(t,null),mo("Not detected"),Gr(),zo("Select a Book to begin."),await Rc(n,null),Fo()}),G?.addEventListener("change",()=>{const e=Number.parseInt(G.value||"",10);no(),qo(t,Number.isInteger(e)&&e>0?e:null),m&&Number.isInteger(e)&&e>0&&(m.value=String(e)),ra(),mo("Not detected"),Gr(Ro()?"Ready to analyse manuscript structure.":"Select a book to analyse manuscript structure."),zo(Mo()?"Ready to scan manuscript structure.":"Select a Book to begin."),Fo()}),h?.addEventListener("change",async()=>{const n=Number.parseInt(h.value||"",10);no(),H&&Number.isInteger(n)&&n>0&&(H.value=String(n)),qo(e,Number.isInteger(n)&&n>0?n:null),qo(t,null),zo("Select a Book to begin."),await Rc(n,null),Fo()}),m?.addEventListener("change",()=>{const e=Number.parseInt(m.value||"",10);no(),G&&Number.isInteger(e)&&e>0&&(G.value=String(e)),qo(t,Number.isInteger(e)&&e>0?e:null),ra(),zo(Mo()?"Ready to scan manuscript structure.":"Select a Book to begin."),Fo()}),g?.addEventListener("input",Yo),y?.addEventListener("click",async()=>{const e=Uo(),t=String(g?.value||"").trim();if(e&&t){y.disabled=!0,Ko("Creating Book...");try{const n=await Xa(`/api/word-companion/projects/${e.projectId}/books`,{title:t});g&&(g.value=""),H&&(H.value=String(e.projectId)),await Rc(e.projectId,n.bookId),m&&(m.value=String(n.bookId)),Ko("Ready to scan manuscript structure.")}catch(e){console.error("Unable to create Book from Word Companion.",e),Ko(`Unable to create Book: ${Do(e)}`)}finally{Yo()}}else Yo()}),w?.addEventListener("click",async()=>{const e=Uo(),t=Mo();if(e&&t)if(Zt&&en&&window.Word&&"function"==typeof window.Word.run){w&&(w.disabled=!0,w.textContent="Scanning..."),Ko("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),ca(e,1))return r={title:d,sortOrder:10*(n.length+1),wordCount:0,scenes:[]},n.push(r),o=s(),void(a+=ia(d));if(!r||!o)return;if(t&&i.has(d))return void(o=s());const l=ia(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,Ln=!1;const r=await Xa("/api/word-companion/manuscript/analyse",{projectId:e.projectId,bookId:t.bookId,documentGuid:ta(),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",To({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),Mr(b,!1),Mr(D,!1),Ko(e.message||"Preview generated."),Yo()})(r);try{const t=await Xa("/api/word-companion/manuscript/discover-characters",{projectId:e.projectId,documentText:n.documentText||""});Pn=Array.isArray(t?.candidates)?t.candidates:[],Mr(S,!0),Mr(v,!0)}catch(e){console.error("Unable to discover character candidates.",e),Pn=[],Mr(S,!0),Mr(v,!0)}Eo({lastScan:"Success",lastError:"-",paragraphs:"-",heading1:String(n.chapterCount),heading2:"-"})}catch(e){console.error("Unable to scan manuscript for first-run import.",e),zo(`Unable to scan manuscript: ${Do(e)}`),Eo({lastScan:"Failure",lastError:Do(e)})}finally{w&&(w.textContent="Scan Manuscript"),Yo()}}else zo("Word document access is unavailable.");else zo("Select a Project and Book to begin.")}),P?.addEventListener("click",async()=>{const n=Uo(),o=Mo();if(!(n&&o&&Dn&&En?.canImport))return void Yo();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)){$n=!0,Yo(),Ko("Importing manuscript structure...");try{const i=ta(),s=await Xa("/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},Ko(s.message||"Import was not completed."),void Yo();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},qo(e,s.projectId),qo(t,s.bookId),H&&(H.value=String(s.projectId));const l=Pn;await Rc(s.projectId,s.bookId),Lr("Connected"),Ln=!0,qn=l.length>0,An=qn?Date.now():0,Pn=l,l.length>0?(Jo(!0),Xo(l),Mr(D,!0),Mr(v,!1),Ko(s.message||"Manuscript structure imported."),$r(s.message||"Manuscript structure imported."),window.setTimeout(Yo,750)):await Za(s.message||"Manuscript structure imported.")}catch(e){console.error("Unable to import manuscript structure.",e),Ko(`Unable to import manuscript: ${Do(e)}`)}finally{$n=!1,Yo()}var c}else Ko("Import cancelled.")}),x?.addEventListener("click",async()=>{const e=Uo()||Wo(),t=Qo();if(!qn||Date.now()-An<750||!e||0===t.length)Yo();else{Tn=!0,Yo(),No(C,"Creating selected characters...");try{const n=await Xa("/api/word-companion/manuscript/create-discovered-characters",{projectId:e.projectId,candidateNames:t});$r(n.message||"Characters created."),No(C,`${n.createdCount||0} created, ${n.skippedCount||0} already existed.`),await Za(n.message||"Characters created.")}catch(e){console.error("Unable to create discovered characters.",e),No(C,`Unable to create characters: ${Do(e)}`)}finally{Tn=!1,Yo()}}}),N?.addEventListener("click",()=>Za("Character creation skipped.")),E?.addEventListener("click",()=>Za()),f?.addEventListener("click",()=>Jo(!1)),L?.addEventListener("click",()=>zo("Import cancelled.")),J?.addEventListener("click",Ha),ne?.addEventListener("click",Ga),re?.addEventListener("click",async()=>{const e=Ro();if(!e||0===Fr().length||jn)return void Vr();if(!await Va())return;jn=!0,Kr(),Hr("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=Fr().filter(e=>"couldLink"===e.category&&"Chapter"===e.type),r=Fr().filter(e=>"wouldCreateChapters"===e.category),o=Fr().filter(e=>"couldLink"===e.category&&"Scene"===e.type),a=Fr().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 Wa(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.");ac(o,"PlotDirector Chapter",`PD-CHAPTER-${t}`,"PD-CHAPTER")}else{const t=e.pdScene?.sceneId;if(!t)throw new Error("Scene ID is not available.");ac(o,"PlotDirector Scene",`PD-SCENE-${t}`,"PD-SCENE")}await t.sync()})})(e),Ka(e,"Success"),t[n]+=1}catch(n){Ka(e,"Failed",Do(n)),t.failed+=1}};try{for(const e of n)s(e,"linkedChapters");for(const n of r)try{await _a(e.bookId,n),s(n,"createdChapters")}catch(e){Ka(n,"Failed",Do(e)),t.failed+=1}for(const e of o)s(e,"linkedScenes");for(const n of a)try{await za(e.bookId,n),s(n,"createdScenes")}catch(e){Ka(n,"Failed",Do(e)),t.failed+=1}for(const e of c)await d(e.item,e.counterName);for(const e of Sn.manualReview||[])Ka(e,"Skipped");ja(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),Hr(i)}finally{jn=!1,Kr(),i&&(ro(),await Ga(),Hr(`${i} Preview refreshed.`))}}),St?.addEventListener("click",()=>Fa(!0)),Ct?.addEventListener("click",()=>Fa(!1)),It?.addEventListener("cancel",e=>{e.preventDefault(),Fa(!1)}),ce?.addEventListener("click",qc),Y?.addEventListener("click",async()=>{if(!Ro())return $r("Select a PlotDirector book first."),void mo("Not detected");Tc(),Xr(!0),_r("Updating...");try{await Promise.all([kc(!0),vc(!0),xc(!0)]);if(!await Ha())return;if(zr(!1),!on)return await Lc(),void _r("Current scene updated");await qc(),_r("Current scene updated")}finally{Xr(!1)}}),ke?.addEventListener("click",()=>wc({source:"manual"})),Re?.addEventListener("click",async()=>{if(ln)if(!Un&&await(async()=>{if(!ln)return!1;if(!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return zr(!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 Wa(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=Io(n,"scene").toLocaleLowerCase()===Io(fr||on,"scene").toLocaleLowerCase(),s=Io(t,"chapter").toLocaleLowerCase()===Io(Ir||rn,"chapter").toLocaleLowerCase();return a||i&&(c||s)?(zr(!1),!0):(zr(!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),Eo({lastError:Do(e)}),zr(!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:rc(Te),assetIds:rc(je),locationIds:[],primaryLocationId:oc()},Qa(e,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})),(()=>{const e=new Set(rc(Te)),t=new Set(rc(je));kr={characters:kr.characters.map(t=>({...t,linked:e.has(Number.parseInt(go(t,"character"),10))})),assets:kr.assets.map(e=>({...e,linked:t.has(Number.parseInt(go(e,"asset"),10))})),locations:kr.locations,primaryLocationId:oc()}})(),oo("Scene links saved successfully."),Eo({lastError:"-"})}catch(e){oo("Unable to save scene links."),Eo({lastError:Do(e)})}finally{Re&&(Re.disabled=!ln||Un,Re.textContent="Save Scene Links")}var e,t}else oo("The Word cursor is now in a different scene. Refresh Current Scene before saving.");else oo("Link a PlotDirector scene first.")}),pe?.addEventListener("click",async()=>{await cc()}),he?.addEventListener("click",async()=>{console.debug("Word Companion unlink clicked"),$r("Preparing to unlink manuscript...");const e=(()=>{const e=Cn||Zo();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 $r("This Word document is not linked.");if(!e.documentGuid||!Number.isInteger(e.bookId)||e.bookId<=0)return void $r("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()})){$r("Unlinking manuscript..."),he&&(he.disabled=!0,he.textContent="Unlinking...");try{await Xa("/api/word-companion/manuscript/unlink",{documentGuid:e.documentGuid,bookId:e.bookId}),await ea(),await nc("Manuscript unlinked.")}catch(e){console.error("Unable to unlink manuscript binding.",e),$r("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 ea(),await nc("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),$r(`Unable to remove Word metadata: ${Do(e)}`)}else $r(`Unable to unlink manuscript: ${Do(e)}`)}finally{he&&(he.disabled=!1,he.textContent="Unlink this Word document"),Or()}}else $r("Unlink cancelled.")}),He?.addEventListener("click",async()=>{const e=Ro();if(!e||!rn||un)return void uo("Chapter not found in PlotDirector.");const t=fo(rn),n=await((e,t)=>(No(gt,`"${e}"`),No(yt,`"${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,To(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 Xa(`/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.");ro(),Qr(null,r.chapterId,"Manual chapter link");if(!await ic("Chapter created and linked successfully."))return;await sc("Chapter created and linked successfully.")}catch(e){co("Unable to create chapter."),Eo({lastError:Do(e)})}finally{He&&(He.disabled=!io(),He.textContent="Create Chapter in PlotDirector")}}}),Ge?.addEventListener("click",async()=>{const e=Ro();if(e&&rn&&!un){Ge&&(Ge.disabled=!0,Ge.textContent="Loading...");try{fn=await Co(e.bookId),In=null,dt&&(dt.value=""),No(ht,""),lc(),st?.showModal?st.showModal():st&&(st.hidden=!1)}catch(e){co("Unable to load chapters."),Eo({lastError:Do(e)})}finally{Ge&&(Ge.disabled=!io(),Ge.textContent="Link to Existing Chapter")}}else uo("Chapter not found in PlotDirector.")}),dt?.addEventListener("input",lc),ut?.addEventListener("click",async()=>{const e=fn.find(e=>e.chapterId===In);if(e){ut&&(ut.disabled=!0,ut.textContent="Linking...");try{Qr(null,e.chapterId,"Manual chapter link");if(!await ic("Chapter linked successfully."))return;uc(),await sc("Chapter linked successfully.")}catch(e){No(ht,"Unable to link chapter."),co("Unable to link chapter."),Eo({lastError:Do(e)})}finally{ut&&(ut.disabled=!In,ut.textContent="Link Chapter")}}else No(ht,"Select a chapter.")}),pt?.addEventListener("click",uc),wt?.addEventListener("click",()=>dc(!0)),ft?.addEventListener("click",()=>dc(!1)),mt?.addEventListener("cancel",e=>{e.preventDefault(),dc(!1)}),Je?.addEventListener("click",async()=>{const e=Ro();if(!e||!on)return void ho("Scene not found in PlotDirector.");const t=Io(on,"scene"),n=Io(rn,"chapter"),r=await((e,t)=>(No(ot,`"${e}"`),No(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 vo();if(!n)return void ho("This chapter does not exist in PlotDirector. Create or link the chapter first.");const r=await Xa(`/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.");ro(),await pc(r.sceneId,r.chapterId,"Scene created and linked successfully.")}catch(e){ao("Unable to create scene."),Eo({lastError:Do(e)})}finally{Je&&(Je.disabled=!so(),Je.textContent="Create Scene in PlotDirector")}}}),_e?.addEventListener("click",async()=>{const e=Ro();if(e&&on){_e&&(_e.disabled=!0,_e.textContent="Loading...");try{const t=await So(e.bookId),n=ko(t);if(!n)return void ho("This chapter does not exist in PlotDirector. Create or link the chapter first.");gn=Array.isArray(n.scenes)?n.scenes:[],yn=null,Xe&&(Xe.value=""),No(nt,""),mc(),Qe?.showModal?Qe.showModal():Qe&&(Qe.hidden=!1)}catch(e){ao("Unable to load scenes."),Eo({lastError:Do(e)})}finally{_e&&(_e.disabled=!so(),_e.textContent="Link to Existing Scene")}}else ho("Scene not found in PlotDirector.")}),Xe?.addEventListener("input",mc),et?.addEventListener("click",async()=>{const e=Ro(),t=gn.find(e=>e.sceneId===yn);if(e&&t){et&&(et.disabled=!0,et.textContent="Linking...");try{const e=await vo();if(!e)throw new Error("This chapter does not exist in PlotDirector. Create or link the chapter first.");gc(),await pc(t.sceneId,e.chapterId,"Scene linked successfully.")}catch(e){No(nt,"Unable to link scene."),ao("Unable to link scene."),Eo({lastError:Do(e)})}finally{et&&(et.disabled=!yn,et.textContent="Link Scene")}}else No(nt,"Select a scene.")}),tt?.addEventListener("click",gc),ct?.addEventListener("click",()=>hc(!0)),it?.addEventListener("click",()=>hc(!1)),rt?.addEventListener("cancel",e=>{e.preventDefault(),hc(!1)}),Tt?.addEventListener("click",async()=>{Tt&&(Tt.disabled=!0,Tt.textContent="Running...");try{if(await Po(),!Zt||!en||!window.Word||"function"!=typeof window.Word.run)return void Eo({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});Eo({lastScan:"Success",lastError:"-",paragraphs:String(e)})}catch(e){console.error("Word Companion diagnostics failed.",e);const t=Do(e);Eo({lastScan:"Failure",lastError:t}),$r(`Unable to scan the Word document: ${t}`)}finally{Tt&&(Tt.disabled=!1,Tt.textContent="Run Diagnostics")}});const Uc=Jt?(async()=>{Ao(H,"Loading projects..."),Ao(G,"Select a project first"),Ao(h,"Loading projects..."),Ao(m,"Select a project first"),qr(""),Ar("");try{const n=await Qa("/api/word-companion/projects");if(Qt=Array.isArray(n.projects)?n.projects:[],0===Qt.length)return Ao(H,"No projects available."),Ao(h,"No projects available."),qr("No projects available."),qo(e,null),qo(t,null),ra(),void zo("No projects available.");$o(H,"Choose a project",Qt,"projectId","title"),_o();const r=Lo(e),o=Qt.find(e=>e.projectId===r);o&&H?(H.value=String(o.projectId),h&&(h.value=String(o.projectId)),qo(e,o.projectId),await Rc(o.projectId,Lo(t))):(qo(e,null),qo(t,null),ra(),zo("Select a Project and Book to begin."))}catch{Qt=[],Xt=[],Ao(H,"Unable to connect to PlotDirector."),Ao(G,"Select a project first"),Ao(h,"Unable to connect to PlotDirector."),Ao(m,"Select a project first"),Lr("Unable to connect to PlotDirector."),qr("Unable to connect to PlotDirector."),qo(e,null),qo(t,null),ra(),zo("Unable to connect to PlotDirector.")}})():Promise.resolve();if(Jt||(Ao(H,"Please sign in to PlotDirector."),Ao(G,"Please sign in to PlotDirector."),Ao(h,"Please sign in to PlotDirector."),Ao(m,"Please sign in to PlotDirector."),ra()),!window.Office||"function"!=typeof window.Office.onReady)return Nr("Word Companion ready"),$r("Word document access is unavailable."),_r("Manual"),Eo({host:"Unavailable",platform:"Unavailable",ready:"No",wordApi:"No"}),void Vo().catch(e=>{console.debug("Word Companion presence unavailable.",e)});Po().then(async()=>{Nr("Word Companion ready"),await Uc,await(async()=>{if(!Jt||!en)return void Jo(!1);const n=Zo();if(n)try{const r=await Qa(`/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,to(r),qo(e,r.projectId),qo(t,r.bookId),H&&(H.value=String(r.projectId)),await Rc(r.projectId,r.bookId),Jo(!1),Lr("Connected"),Ar("Bound manuscript detected."),void await tc()}catch{}Cn=null,no(),_o(),Uo()?await Rc(Uo().projectId,null):Ao(m,"Select a project first"),zo("Select a Project and Book to begin."),Jo(!0)})(),await Vo(),en?(()=>{if(Jt){if(!window.Office?.context?.document||"function"!=typeof window.Office.context.document.addHandlerAsync||!window.Office?.EventType?.DocumentSelectionChanged)return _r("Manual"),void $r("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene.");try{window.Office.context.document.addHandlerAsync(window.Office.EventType.DocumentSelectionChanged,jc,e=>{e?.status===window.Office.AsyncResultStatus.Succeeded?(Kn=!0,_r("Live")):(_r("Manual"),$r("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene."))}),window.addEventListener("beforeunload",Wc)}catch(e){console.warn("Unable to register Word selection tracking handler.",e),_r("Manual"),$r("Automatic scene tracking is unavailable in this Word version. Use Refresh Current Scene.")}}})():($r("Word document access is unavailable."),_r("Manual"))}).catch(e=>{console.error("Office readiness check failed.",e),Lr("Unable to connect to PlotDirector."),Nr("Word Companion unavailable"),$r("Word document access is unavailable."),Eo({ready:"No",wordApi:"No",lastScan:"Failure"})})})(); \ No newline at end of file