@braze/sanity-plugin-ai-translation 0.2.22 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/hooks/useTranslationContext/TranslationContext.ts","../src/hooks/useTranslationContext/useTranslationContext.ts","../src/consts.ts","../src/utils/document/undraftId.ts","../src/hooks/useTranslationMetadata/useTranslationMetadata.ts","../src/utils/common/devLog.ts","../src/utils/feedback/validateSuggestion.ts","../src/utils/feedback/processFeedback.ts","../src/components/ProgressPanel.tsx","../src/components/SuggestionReviewList.tsx","../src/components/GiveFeedbackDialog.tsx","../src/utils/openai/validateApiKey.ts","../src/hooks/useTranslationContext/translationReducer.ts","../src/hooks/useTranslationContext/TranslationContextProvider.tsx","../src/GiveTranslationFeedbackAction.tsx","../src/schemas/translationGlossaryTerm.schema.ts","../src/schemas/translationHistory.schema.ts","../src/schemas/translationSettings.schema.ts","../src/utils/common/Semaphore.ts","../src/utils/document/documentToPlainText.ts","../src/utils/matching/matchTermInDocument.ts","../src/utils/matching/fetchTranslationPrompts.ts","../src/utils/common/findKeyValues/findKeyValues.ts","../src/utils/references/localizeReferences.ts","../src/utils/state/setupTranslationJobStatuses.ts","../src/utils/metadata/getTranslationMetadata.ts","../src/utils/metadata/updateTranslationMetadata.ts","../src/utils/state/setJobStatus.ts","../src/utils/metadata/getExistingTranslationId.ts","../src/utils/translation/determineTranslationTarget.ts","../src/utils/document/analyzeDocumentContent.ts","../src/utils/document/assembleLanguageSpecs.ts","../src/utils/openai/callGPT5.ts","../src/utils/openai/deserializeDocument.ts","../src/utils/openai/serializeDocument.ts","../src/utils/openai/validateStructure.ts","../src/utils/translation/assembleTranslationPrompt.ts","../src/utils/translation/translateEntireDocument.ts","../src/utils/translation/translateDocument.ts","../src/utils/translation/translateDocumentSet.ts","../src/components/ActionButtonBar.tsx","../src/components/LanguageCheckbox.tsx","../src/components/LanguageSelectionPanel.tsx","../src/components/TranslationModePanel.tsx","../src/utils/references/assembleReferences.ts","../src/components/ReferenceConfigPanel.tsx","../src/components/WriteSettingsPanel.tsx","../src/components/TranslationConfigPanel.tsx","../src/components/TranslationStatusPanel.tsx","../src/components/TranslationDialog.tsx","../src/TranslateAction.tsx","../src/index.ts"],"sourcesContent":["import { createContext } from 'react'\nimport type { TranslationJob, TranslationContext as TTranslationContext } from '../../types'\n\nexport const TranslationContext = createContext<TTranslationContext>({\n document: null,\n config: null,\n overallStatus: 'idle',\n selectedTargetLanguages: [],\n translationJobs: new Map<string, TranslationJob>(),\n shouldOverwriteFreshTranslations: false,\n shouldTranslateReferences: false,\n maxReferenceDepth: 1,\n references: [],\n translationMetadata: null,\n translationMode: 'sanity-only',\n openaiValidated: false,\n dispatch: () => {},\n})\n","import { useContext } from 'react'\nimport type { TranslationContext as TTranslationContext } from '../../types'\nimport { TranslationContext } from './TranslationContext'\n\ninterface ValidatedTranslationContext extends TTranslationContext {\n document: NonNullable<TTranslationContext['document']>\n config: NonNullable<TTranslationContext['config']>\n}\n\nexport const useTranslationContext = (): ValidatedTranslationContext => {\n const context = useContext(TranslationContext)\n\n if (!context.document || !context.config) {\n throw new Error(\n 'useTranslationContext must be used within a TranslationContextProvider with document and config props',\n )\n }\n\n return context as ValidatedTranslationContext\n}\n","export const TRANSLATION_METADATA_TYPE = 'translation.metadata'\nexport const UPDATE_EXISTING_TRANSLATION_PROMPT =\n '***IMPORTANT:** This document has an existing, but out-of-date translation. Please review the existing translation before processing and change as little as possible. Existing translation: $extantTranslation'\n","// Remove `drafts.` from a string\nexport const undraftId = (id: string) => id.replace(/^drafts\\./, '')\n\nexport const draftId = (id: string) => `drafts.${undraftId(id)}`\n","import { useEffect } from 'react'\nimport { useListeningQuery } from 'sanity-plugin-utils'\nimport { TRANSLATION_METADATA_TYPE } from '../../consts'\nimport type { TranslationMetadataDocument } from '../../types'\nimport { undraftId } from '../../utils/document/undraftId'\nimport { useTranslationContext } from '../useTranslationContext'\n\nconst TRANSLATION_METADATA_QUERY = '*[_type == $translationMetadataType && references($documentId)][0]'\n\ninterface UseTranslationMetadataProps {\n documentId: string\n}\n\nexport const useTranslationMetadata = ({ documentId }: UseTranslationMetadataProps) => {\n const { dispatch } = useTranslationContext()\n\n const { data, loading, error } = useListeningQuery<TranslationMetadataDocument | null>(TRANSLATION_METADATA_QUERY, {\n params: {\n documentId: undraftId(documentId),\n translationMetadataType: TRANSLATION_METADATA_TYPE,\n },\n initialValue: null,\n })\n\n useEffect(() => {\n if (loading) {\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'fetching_metadata' })\n return\n }\n\n if (error) {\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'error' })\n return\n }\n\n if (data) {\n dispatch({ type: 'SET_TRANSLATION_METADATA', payload: data as TranslationMetadataDocument })\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'idle' })\n return\n }\n\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'idle' })\n }, [data, loading, error, dispatch])\n\n return {\n data,\n loading,\n error,\n }\n}\n","/**\n * Development logging utility for AI Translation plugin troubleshooting\n * Only logs when SANITY_STUDIO_AI_TRANSLATION_PLUGIN_LOGGING environment variable is \"true\"\n */\n\nconst isLoggingEnabled = (): boolean => {\n return process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLATION_PLUGIN_LOGGING === 'true' || process.env.SANITY_STUDIO_AI_TRANSLATION_PLUGIN_LOGGING === 'true'\n}\n\nexport interface DevLogContext {\n component?: string\n action?: string\n documentId?: string\n language?: string\n jobId?: string\n // biome-ignore lint/suspicious/noExplicitAny: not worth the effort\n [key: string]: any\n}\n\nexport const devLog = {\n info: (message: string, context?: DevLogContext) => {\n if (!isLoggingEnabled()) return\n console.info('[AI Translation]', message, context ? JSON.stringify(context, null, 2) : '')\n },\n\n warn: (message: string, context?: DevLogContext) => {\n if (!isLoggingEnabled()) return\n console.warn('[AI Translation]', message, context ? JSON.stringify(context, null, 2) : '')\n },\n\n error: (message: string, error?: Error | unknown, context?: DevLogContext) => {\n if (!isLoggingEnabled()) return\n console.error('[AI Translation]', message, error, context ? JSON.stringify(context, null, 2) : '')\n },\n\n debug: (message: string, data?: unknown, context?: DevLogContext) => {\n if (!isLoggingEnabled()) return\n console.debug('[AI Translation]', message, data, context ? JSON.stringify(context, null, 2) : '')\n },\n\n group: (label: string, context?: DevLogContext) => {\n if (!isLoggingEnabled()) return\n console.group(`[AI Translation] ${label}`, context ? JSON.stringify(context, null, 2) : '')\n },\n\n groupEnd: () => {\n if (!isLoggingEnabled()) return\n console.groupEnd()\n },\n\n time: (label: string) => {\n if (!isLoggingEnabled()) return\n console.time(`[AI Translation] ${label}`)\n },\n\n timeEnd: (label: string) => {\n if (!isLoggingEnabled()) return\n console.timeEnd(`[AI Translation] ${label}`)\n },\n}\n","import type { SanityClient } from 'sanity'\nimport type { FeedbackSuggestion } from '../../types'\n\nexport interface ValidationResult {\n valid: boolean\n status: 'valid' | 'warning' | 'invalid'\n reason?: string\n}\n\n/**\n * Validates a feedback suggestion to check for conflicts and invalid data.\n *\n * This function performs comprehensive validation including:\n * - Glossary term validation (required fields, conflicts with protected phrases)\n * - Protected phrase validation (duplicates, conflicts with glossary)\n * - Prompt validation (empty content, format issues)\n *\n * @param client - Sanity client for checking existing data\n * @param suggestion - The suggestion to validate\n * @returns Validation result with status and reason\n *\n * @example\n * ```typescript\n * const result = await validateSuggestion(client, {\n * type: 'glossaryTerm',\n * sourceValue: 'hello',\n * targetValue: 'hola',\n * sourceLocale: 'en',\n * targetLocale: 'es'\n * })\n * // Returns: { valid: true, status: 'valid' }\n * ```\n */\nexport async function validateSuggestion(\n client: SanityClient,\n suggestion: FeedbackSuggestion,\n): Promise<ValidationResult> {\n // Validate glossary terms\n if (suggestion.type === 'glossaryTerm') {\n return validateGlossaryTerm(client, suggestion)\n }\n\n // Validate protected phrases\n if (suggestion.type === 'protectedPhrase') {\n return validateProtectedPhrase(client, suggestion)\n }\n\n // Validate prompts\n if (suggestion.type === 'globalPrompt' || suggestion.type === 'localePrompt') {\n return validatePrompt(suggestion)\n }\n\n return { valid: true, status: 'valid' }\n}\n\n/**\n * Validates a glossary term suggestion for required fields and conflicts.\n */\nasync function validateGlossaryTerm(client: SanityClient, suggestion: FeedbackSuggestion): Promise<ValidationResult> {\n const { sourceValue, targetValue, sourceLocale, targetLocale } = suggestion\n\n // Check for missing required fields\n if (!sourceValue || !targetValue || !sourceLocale || !targetLocale) {\n return {\n valid: false,\n status: 'invalid',\n reason: 'Missing required fields for glossary term',\n }\n }\n\n // Check if source equals target (should be protected phrase instead)\n if (sourceValue.trim() === targetValue.trim()) {\n return {\n valid: false,\n status: 'invalid',\n reason: `Source and target are identical (\"${sourceValue}\") - this should be a protected phrase instead`,\n }\n }\n\n // Check if the source value is already in protected phrases\n const protectedPhrases = await client.fetch<string[]>('*[_id == \"translation-settings\"][0].protectedPhrases')\n\n if (protectedPhrases?.some((phrase) => phrase.toLowerCase() === sourceValue.toLowerCase())) {\n return {\n valid: false,\n status: 'invalid',\n reason: `Cannot add \"${sourceValue}\" to glossary because it is already a protected phrase. Protected phrases are never translated. If you want to create a translation mapping for this term, please remove it from protected phrases first by editing Translation Settings.`,\n }\n }\n\n // Also check if the target value is in protected phrases (shouldn't translate TO a protected term)\n if (targetValue && protectedPhrases?.some((phrase) => phrase.toLowerCase() === targetValue.toLowerCase())) {\n return {\n valid: false,\n status: 'invalid',\n reason: `Cannot use \"${targetValue}\" as a translation target because it is a protected phrase. Protected phrases should not appear as translation targets.`,\n }\n }\n\n // Check if glossary term already exists\n const existingTerm = await client.fetch<{\n translations: Array<{ _key: string; value: string }>\n } | null>(\n `*[_type == \"translationGlossaryTerm\" && count(translations[_key == $sourceLocale && value == $sourceValue]) > 0][0] {\n translations\n }`,\n { sourceLocale, sourceValue },\n )\n\n if (existingTerm) {\n const existingTarget = existingTerm.translations.find((t) => t._key === targetLocale)\n\n if (existingTarget?.value === targetValue) {\n return {\n valid: true,\n status: 'warning',\n reason: `This translation already exists in the glossary: \"${sourceValue}\" → \"${targetValue}\"`,\n }\n }\n\n if (existingTarget && existingTarget.value !== targetValue) {\n return {\n valid: true,\n status: 'warning',\n reason: `\"${sourceValue}\" already has a different translation in the glossary: \"${existingTarget.value}\". Applying this will update it to \"${targetValue}\".`,\n }\n }\n }\n\n return { valid: true, status: 'valid' }\n}\n\n/**\n * Validates a protected phrase suggestion for conflicts and duplicates.\n */\nasync function validateProtectedPhrase(\n client: SanityClient,\n suggestion: FeedbackSuggestion,\n): Promise<ValidationResult> {\n const phrase = suggestion.description.trim()\n\n if (!phrase) {\n return {\n valid: false,\n status: 'invalid',\n reason: 'Protected phrase cannot be empty',\n }\n }\n\n // Check if already exists\n const protectedPhrases = await client.fetch<string[]>('*[_id == \"translation-settings\"][0].protectedPhrases')\n\n if (protectedPhrases?.some((p) => p.toLowerCase() === phrase.toLowerCase())) {\n return {\n valid: true,\n status: 'warning',\n reason: `\"${phrase}\" is already in protected phrases`,\n }\n }\n\n // Check if this phrase exists in the glossary (conflict)\n const glossaryTermWithPhrase = await client.fetch<{\n _id: string\n translations: Array<{ _key: string; value: string }>\n } | null>(\n `*[_type == \"translationGlossaryTerm\" && count(translations[value == $phrase]) > 0][0] {\n _id,\n translations\n }`,\n { phrase },\n )\n\n if (glossaryTermWithPhrase) {\n // Find which translations use this phrase\n const matchingTranslations = glossaryTermWithPhrase.translations\n .filter((t) => t.value.toLowerCase() === phrase.toLowerCase())\n .map((t) => t._key)\n .join(', ')\n\n return {\n valid: false,\n status: 'invalid',\n reason: `Cannot add \"${phrase}\" as a protected phrase because it exists in the glossary as a translation (${matchingTranslations}). If you want to prevent this term from being translated, please remove it from the glossary first by editing Translation Settings.`,\n }\n }\n\n return { valid: true, status: 'valid' }\n}\n\n/**\n * Validates a prompt suggestion for proper content and format.\n */\nfunction validatePrompt(suggestion: FeedbackSuggestion): ValidationResult {\n const update = suggestion.description.trim()\n\n if (!update) {\n return {\n valid: false,\n status: 'invalid',\n reason: 'Prompt update cannot be empty',\n }\n }\n\n // Warn if the update looks like it's trying to apply rules rather than add them\n if (\n update.toLowerCase().includes('has been') ||\n update.toLowerCase().includes('was changed') ||\n update.toLowerCase().includes('corrected to')\n ) {\n return {\n valid: true,\n status: 'warning',\n reason: 'This update may be describing past changes rather than adding new rules',\n }\n }\n\n return { valid: true, status: 'valid' }\n}\n","import type { SanityClient } from 'sanity'\nimport type { FeedbackSuggestion } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { validateSuggestion } from './validateSuggestion'\n\ninterface ProcessFeedbackOptions {\n client: SanityClient\n schemaId: string\n sourceLanguage: string\n targetLanguage: string\n userFeedback?: string\n shouldIncludeEdits: boolean\n sourceSnapshot?: string\n targetSnapshot?: string\n currentDocument?: Record<string, unknown>\n onProgress?: (state: { stage: 'analyzing' | 'applying'; message: string }) => void\n}\n\ninterface AnalyzeFeedbackOptions {\n client: SanityClient\n schemaId: string\n sourceLanguage: string\n targetLanguage: string\n userFeedback?: string\n shouldIncludeEdits: boolean\n sourceSnapshot?: string\n targetSnapshot?: string\n currentDocument?: Record<string, unknown>\n onProgress?: (state: { stage: 'analyzing'; message: string }) => void\n}\n\ninterface ApplySuggestionsOptions {\n client: SanityClient\n schemaId: string\n sourceLanguage: string\n targetLanguage: string\n suggestions: FeedbackSuggestion[]\n onProgress?: (state: { stage: 'applying'; message: string }) => void\n}\n\ninterface FeedbackAnalysis {\n globalPromptUpdates?: string\n localePromptUpdates?: string\n protectedPhrasesToAdd?: string[]\n glossaryTermsToAdd?: Array<{\n sourceLocale: string\n targetLocale: string\n sourceValue: string\n targetValue: string\n }>\n reasoning: string\n}\n\n/**\n * Analyzes user feedback and inferred edits to generate improvement suggestions.\n *\n * This function:\n * 1. Analyzes explicit user feedback and/or edits made to translated content\n * 2. Uses AI to infer improvements to translation settings\n * 3. Returns a list of suggestions for the user to review\n * 4. Validates suggestions to flag conflicts and invalid data\n *\n * @param options - Configuration for feedback analysis\n * @returns Promise that resolves with array of feedback suggestions\n */\nexport async function analyzeFeedback({\n client,\n sourceLanguage,\n targetLanguage,\n userFeedback,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument,\n onProgress,\n}: AnalyzeFeedbackOptions): Promise<FeedbackSuggestion[]> {\n devLog.info('Starting feedback analysis', {\n sourceLanguage,\n targetLanguage,\n hasUserFeedback: !!userFeedback,\n shouldIncludeEdits,\n })\n\n onProgress?.({ stage: 'analyzing', message: 'Analyzing feedback and edits...' })\n\n const analysisPrompt = buildAnalysisPrompt({\n sourceLanguage,\n targetLanguage,\n userFeedback,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument,\n })\n\n const analysis = await analyzeWithAI(client, analysisPrompt)\n devLog.info('AI analysis complete', { analysis })\n\n const suggestions = convertAnalysisToSuggestions(analysis, sourceLanguage, targetLanguage)\n devLog.info('Generated suggestions', { count: suggestions.length })\n\n // Validate all suggestions\n onProgress?.({ stage: 'analyzing', message: 'Validating suggestions...' })\n const validatedSuggestions = await Promise.all(\n suggestions.map(async (suggestion) => {\n const validation = await validateSuggestion(client, suggestion)\n return {\n ...suggestion,\n warning: validation.reason,\n validationStatus: validation.status,\n // Only auto-approve valid suggestions; invalid ones cannot be approved\n approved: validation.status === 'valid',\n }\n }),\n )\n\n devLog.info('Suggestions validated', {\n valid: validatedSuggestions.filter((s) => s.validationStatus === 'valid').length,\n warnings: validatedSuggestions.filter((s) => s.validationStatus === 'warning').length,\n invalid: validatedSuggestions.filter((s) => s.validationStatus === 'invalid').length,\n })\n\n return validatedSuggestions\n}\n\n/**\n * Applies approved feedback suggestions to translation settings and glossary.\n *\n * @param options - Configuration for applying suggestions\n * @returns Promise that resolves when suggestions have been applied\n */\nexport async function applySuggestions({\n client,\n schemaId,\n sourceLanguage,\n targetLanguage,\n suggestions,\n onProgress,\n}: ApplySuggestionsOptions): Promise<void> {\n devLog.info('Applying suggestions', {\n sourceLanguage,\n targetLanguage,\n targetLanguageType: typeof targetLanguage,\n targetLanguageValue: targetLanguage,\n count: suggestions.filter((s) => s.approved).length,\n })\n\n if (!targetLanguage) {\n devLog.error('targetLanguage is undefined in applySuggestions!', {\n sourceLanguage,\n targetLanguage,\n suggestionsCount: suggestions.length,\n })\n throw new Error('targetLanguage is required but was undefined')\n }\n\n const approvedSuggestions = suggestions.filter((s) => s.approved)\n\n if (approvedSuggestions.length === 0) {\n devLog.info('No approved suggestions to apply')\n return\n }\n\n onProgress?.({ stage: 'applying', message: 'Applying updates to translation settings...' })\n\n await applyImprovementsFromSuggestions(client, schemaId, targetLanguage, approvedSuggestions, onProgress)\n\n devLog.info('Suggestions applied successfully')\n}\n\n/**\n * Processes user feedback and inferred edits to improve translation prompts and glossary.\n * This is a convenience function that combines analysis and application in one step.\n *\n * @deprecated Use analyzeFeedback and applySuggestions separately for better UX\n * @param options - Configuration for feedback processing\n * @returns Promise that resolves when feedback has been processed\n */\nexport async function processFeedback({\n client,\n schemaId,\n sourceLanguage,\n targetLanguage,\n userFeedback,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument,\n onProgress,\n}: ProcessFeedbackOptions): Promise<void> {\n const suggestions = await analyzeFeedback({\n client,\n schemaId,\n sourceLanguage,\n targetLanguage,\n userFeedback,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument,\n onProgress: onProgress ? (state) => onProgress({ ...state, stage: 'analyzing' }) : undefined,\n })\n\n await applySuggestions({\n client,\n schemaId,\n sourceLanguage,\n targetLanguage,\n suggestions,\n onProgress: onProgress ? (state) => onProgress({ ...state, stage: 'applying' }) : undefined,\n })\n}\n\n/**\n * Builds the prompt for AI analysis of user feedback and edits.\n */\nfunction buildAnalysisPrompt({\n sourceLanguage,\n targetLanguage,\n userFeedback,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument,\n}: {\n sourceLanguage: string\n targetLanguage: string\n userFeedback?: string\n shouldIncludeEdits: boolean\n sourceSnapshot?: string\n targetSnapshot?: string\n currentDocument?: Record<string, unknown>\n}): string {\n const parts: string[] = [\n 'You are analyzing feedback about AI translation quality to improve future translations.',\n `Source language: ${sourceLanguage}`,\n `Target language: ${targetLanguage}`,\n '',\n 'Your task is to analyze the feedback and infer specific improvements to:',\n '1. Global translation prompt (general guidance for all translations)',\n '2. Locale-specific prompt (guidance specific to this target language)',\n '3. Protected phrases (terms that should never be translated)',\n '4. Glossary terms (specific term-to-term translation mappings)',\n '',\n ]\n\n if (userFeedback) {\n parts.push('USER FEEDBACK:', userFeedback, '')\n }\n\n if (shouldIncludeEdits && sourceSnapshot && targetSnapshot && currentDocument) {\n parts.push(\n 'EDIT ANALYSIS:',\n 'Compare the original translation with the edited version to understand what the user changed.',\n '',\n 'Original source document:',\n sourceSnapshot,\n '',\n 'Original AI translation:',\n targetSnapshot,\n '',\n 'Current edited document:',\n JSON.stringify(currentDocument, null, 2),\n '',\n )\n }\n\n parts.push(\n 'INSTRUCTIONS:',\n 'Analyze the feedback and/or edits to identify patterns and improvements.',\n '',\n 'For edits, look for:',\n '- Terminology changes that suggest glossary additions',\n '- Phrases that were protected/unprotected incorrectly',\n '- Style, tone, or formatting issues that suggest prompt improvements',\n '- Consistent patterns across multiple changes',\n '',\n 'IMPORTANT RULES:',\n '1. DO NOT create redundant updates. Choose ONE mechanism for each issue:',\n ' - Protected phrases = terms that should NEVER be translated (language-agnostic)',\n ' - Glossary terms = specific translation mappings (e.g., \"chat\" → \"cat\" in French)',\n ' - Locale prompts = guidance specific to this source→target language pair',\n ' - Global prompts = guidance that applies to ALL translations',\n '',\n '2. Be locale-specific:',\n ` - This feedback is specifically about ${sourceLanguage} → ${targetLanguage} translation`,\n ' - DO NOT assume rules apply to other language pairs unless explicitly stated',\n ' - Prefer locale-specific prompts over global prompts unless universally applicable',\n '',\n '3. Protected phrases vs. Locale prompts:',\n ' - Use protected phrases ONLY for brand names, product names, proper nouns, technical terms that should NEVER be translated in ANY language',\n ' - Use locale prompts for language-pair-specific guidance (e.g., \"Keep technical terms in English when translating to German\")',\n ' - NEVER mention specific terms in both protected phrases AND prompts',\n '',\n '4. Glossary vs. Protected phrases:',\n ' - Glossary = \"Always translate X as Y\" (provides the correct translation)',\n ' - Protected = \"Never translate X\" (keeps it as-is)',\n ' - CRITICAL: If sourceValue === targetValue, use PROTECTED PHRASES, NOT glossary',\n ' - Only use glossary when the term actually changes between languages',\n ' - Use protected when the term should remain unchanged across all languages',\n '',\n 'Output a JSON object with this structure:',\n '{',\n ' \"globalPromptUpdates\": \"string | null - ONLY if this applies to ALL language pairs\",',\n ` \"localePromptUpdates\": \"string | null - ONLY if this is specific to ${sourceLanguage}→${targetLanguage}\",`,\n ' \"protectedPhrasesToAdd\": [\"ONLY brand names, products, proper nouns that should NEVER translate\"],',\n ' \"glossaryTermsToAdd\": [{\"sourceLocale\": \"sourceLocale\",\"sourceValue\": \"term\", \"targetLocale\": \"targetLocale\", \"targetValue\": \"translation\"} - ONLY specific term mappings],',\n ' \"reasoning\": \"string - Explain your analysis and which mechanism you chose for each issue\"',\n '}',\n '',\n 'Be conservative: only suggest changes when there is clear evidence.',\n 'If in doubt, prefer locale-specific updates over global updates.',\n 'Avoid redundancy - if you add a protected phrase, do NOT mention it in prompts.',\n '',\n 'CONCRETE EXAMPLES:',\n '',\n 'Example 1 - Brand name protection:',\n 'User feedback: \"Braze should never be translated\"',\n 'Correct output:',\n '{',\n ' \"protectedPhrasesToAdd\": [\"Braze\"],',\n ' \"reasoning\": \"User explicitly stated Braze is a brand name that should not be translated\"',\n '}',\n 'WRONG: Do not add this to glossary or prompts.',\n '',\n 'Example 2 - Terminology correction:',\n 'Edit analysis: User changed \"discussion\" to \"chat\" consistently in French translation',\n 'Correct output:',\n '{',\n ` \"glossaryTermsToAdd\": [{\"sourceLocale\": \"${sourceLanguage}\", \"sourceValue\": \"discussion\", \"targetLocale\": \"${targetLanguage}\", \"targetValue\": \"chat\"}],`,\n ' \"reasoning\": \"User consistently changed discussion→chat, indicating preferred terminology\"',\n '}',\n 'WRONG: Do not add to protected phrases (values differ) or prompts (specific mapping).',\n '',\n 'Example 3 - Style guidance:',\n 'User feedback: \"Translation is too formal, use casual tone\"',\n 'Correct output:',\n '{',\n ` \"localePromptUpdates\": \"Use a casual, conversational tone when translating to ${targetLanguage}. Avoid overly formal language.\",`,\n ' \"reasoning\": \"This is style guidance specific to this language pair\"',\n '}',\n 'WRONG: Do not make global (applies only to this target language).',\n '',\n 'Example 4 - Technical term in English:',\n `User feedback: \"Keep technical terms like 'API' and 'webhook' in English\"`,\n 'Correct output:',\n '{',\n ` \"localePromptUpdates\": \"When translating to ${targetLanguage}, keep technical terms like 'API', 'webhook', and similar technical jargon in English.\",`,\n ' \"reasoning\": \"This is language-pair-specific guidance about handling technical terms\"',\n '}',\n 'WRONG: Do not add \"API\" or \"webhook\" to protected phrases (already covered by prompt guidance).',\n '',\n 'Example 5 - Multiple issues:',\n 'User feedback: \"MyApp is our product name. Also, use more concise language.\"',\n 'Correct output:',\n '{',\n ' \"protectedPhrasesToAdd\": [\"MyApp\"],',\n ` \"localePromptUpdates\": \"Use concise language when translating to ${targetLanguage}. Prefer shorter phrases over lengthy explanations.\",`,\n ' \"reasoning\": \"MyApp is a product name (protected). Conciseness is a style preference for this language pair (locale prompt).\"',\n '}',\n '',\n 'Example 6 - Identical source and target:',\n 'Edit analysis: User kept \"dashboard\" as \"dashboard\" in Spanish',\n 'Correct output:',\n '{',\n ' \"protectedPhrasesToAdd\": [\"dashboard\"],',\n ' \"reasoning\": \"User kept the term unchanged, indicating it should not be translated\"',\n '}',\n 'WRONG: Do not add to glossary when source === target.',\n )\n\n return parts.join('\\n')\n}\n\n/**\n * Converts AI analysis results into user-reviewable suggestions.\n * @param analysis - The AI analysis results\n * @param sourceLanguage - The source language for translations\n * @param targetLanguage - The target language for translations\n */\nfunction convertAnalysisToSuggestions(\n analysis: FeedbackAnalysis,\n sourceLanguage: string,\n targetLanguage: string,\n): FeedbackSuggestion[] {\n const suggestions: FeedbackSuggestion[] = []\n\n if (analysis.globalPromptUpdates) {\n suggestions.push({\n id: crypto.randomUUID(),\n type: 'globalPrompt',\n description: analysis.globalPromptUpdates,\n approved: true,\n })\n }\n\n if (analysis.localePromptUpdates) {\n suggestions.push({\n id: crypto.randomUUID(),\n type: 'localePrompt',\n description: analysis.localePromptUpdates,\n approved: true,\n })\n }\n\n if (analysis.protectedPhrasesToAdd) {\n for (const phrase of analysis.protectedPhrasesToAdd) {\n suggestions.push({\n id: crypto.randomUUID(),\n type: 'protectedPhrase',\n description: phrase,\n approved: true,\n })\n }\n }\n\n if (analysis.glossaryTermsToAdd) {\n for (const term of analysis.glossaryTermsToAdd) {\n suggestions.push({\n id: crypto.randomUUID(),\n type: 'glossaryTerm',\n description: `${term.sourceValue} → ${term.targetValue}`,\n approved: true,\n // Use the provided source/target languages instead of relying on AI to provide them\n sourceLocale: sourceLanguage,\n sourceValue: term.sourceValue,\n targetLocale: targetLanguage,\n targetValue: term.targetValue,\n })\n }\n }\n\n return suggestions\n}\n\n/**\n * Uses Sanity AI Agent to analyze feedback and infer improvements.\n */\nasync function analyzeWithAI(client: SanityClient, prompt: string): Promise<FeedbackAnalysis> {\n try {\n const response = await client.agent.action.prompt({\n instruction: prompt,\n format: 'json',\n })\n\n devLog.debug('AI response received', { responseType: typeof response })\n\n const responseText =\n typeof response === 'string'\n ? response\n : response && typeof response === 'object'\n ? JSON.stringify(response)\n : (() => {\n throw new Error(`Unexpected response type: ${typeof response}`)\n })()\n\n return parseAIResponse(responseText)\n } catch (error) {\n devLog.error('Error during AI analysis', { error })\n throw new Error(`Failed to analyze feedback: ${error instanceof Error ? error.message : 'Unknown error'}`)\n }\n}\n\n/**\n * Parses the AI response text to extract the feedback analysis.\n */\nfunction parseAIResponse(responseText: string): FeedbackAnalysis {\n try {\n let parsed: unknown\n try {\n parsed = JSON.parse(responseText)\n } catch {\n const jsonMatch = responseText.match(/\\{[\\s\\S]*\\}/)\n if (!jsonMatch) throw new Error('No JSON found in AI response')\n parsed = JSON.parse(jsonMatch[0])\n }\n\n if (!parsed || typeof parsed !== 'object') {\n throw new Error('Response is not a valid object')\n }\n\n const analysis = parsed as FeedbackAnalysis\n if (typeof analysis.reasoning !== 'string') {\n throw new Error('Invalid response structure: missing reasoning')\n }\n\n return {\n globalPromptUpdates: analysis.globalPromptUpdates || undefined,\n localePromptUpdates: analysis.localePromptUpdates || undefined,\n protectedPhrasesToAdd: Array.isArray(analysis.protectedPhrasesToAdd) ? analysis.protectedPhrasesToAdd : [],\n glossaryTermsToAdd: Array.isArray(analysis.glossaryTermsToAdd) ? analysis.glossaryTermsToAdd : [],\n reasoning: analysis.reasoning,\n }\n } catch (error) {\n devLog.error('Error parsing AI response', { error, responseText })\n throw new Error('Failed to parse AI response as JSON')\n }\n}\n\n/**\n * Applies the inferred improvements from suggestions to translation settings and glossary.\n */\nasync function applyImprovementsFromSuggestions(\n client: SanityClient,\n schemaId: string,\n targetLanguage: string,\n suggestions: FeedbackSuggestion[],\n onProgress?: (state: { stage: 'applying'; message: string }) => void,\n): Promise<void> {\n devLog.info('Applying improvements from suggestions', {\n count: suggestions.length,\n targetLanguage,\n suggestions: suggestions.map((s) => ({\n type: s.type,\n approved: s.approved,\n sourceLocale: s.sourceLocale,\n targetLocale: s.targetLocale,\n sourceValue: s.sourceValue,\n targetValue: s.targetValue,\n })),\n })\n\n const settingsId = 'translation-settings'\n\n // Create the document if it doesn't exist\n const settings = await client.fetch('*[_id == $id][0]', { id: settingsId })\n if (!settings) {\n await client.createIfNotExists({\n _id: settingsId,\n _type: 'translationSettings',\n })\n }\n\n const updates: Array<Promise<unknown>> = []\n\n // Group suggestions by type and filter out empty values\n const globalPrompts = suggestions.filter((s) => s.type === 'globalPrompt' && s.description.trim())\n const localePrompts = suggestions.filter((s) => s.type === 'localePrompt' && s.description.trim())\n const protectedPhrases = suggestions.filter((s) => s.type === 'protectedPhrase' && s.description.trim())\n\n // Debug logging for glossary terms before filtering\n const glossaryTermsBeforeFilter = suggestions.filter((s) => s.type === 'glossaryTerm')\n devLog.info('Glossary terms before filtering', {\n count: glossaryTermsBeforeFilter.length,\n terms: glossaryTermsBeforeFilter.map((s) => ({\n sourceLocale: s.sourceLocale,\n sourceValue: s.sourceValue,\n targetLocale: s.targetLocale,\n targetValue: s.targetValue,\n approved: s.approved,\n })),\n })\n\n const glossaryTerms = suggestions.filter(\n (s) =>\n s.type === 'glossaryTerm' &&\n s.sourceLocale?.trim() &&\n s.sourceValue?.trim() &&\n s.targetLocale?.trim() &&\n s.targetValue?.trim(),\n )\n\n devLog.info('Filtered suggestions by type', {\n globalPrompts: globalPrompts.length,\n localePrompts: localePrompts.length,\n protectedPhrases: protectedPhrases.length,\n glossaryTerms: glossaryTerms.length,\n })\n\n // Apply both global and locale prompts in a single transform call\n if (globalPrompts.length > 0 || localePrompts.length > 0) {\n onProgress?.({\n stage: 'applying',\n message:\n globalPrompts.length > 0 && localePrompts.length > 0\n ? 'Updating global and locale translation prompts...'\n : globalPrompts.length > 0\n ? 'Updating global translation prompt...'\n : `Updating ${targetLanguage} translation prompt...`,\n })\n\n updates.push(updatePromptsWithTemplate(client, schemaId, settingsId, targetLanguage, globalPrompts, localePrompts))\n }\n\n // Apply protected phrases\n if (protectedPhrases.length > 0) {\n onProgress?.({ stage: 'applying', message: 'Adding protected phrases...' })\n const phrases = protectedPhrases.map((s) => s.description)\n devLog.debug('Applying protected phrases', { phrases })\n updates.push(addProtectedPhrases(client, settingsId, phrases))\n }\n\n // Apply glossary terms\n if (glossaryTerms.length > 0) {\n onProgress?.({ stage: 'applying', message: 'Adding glossary terms...' })\n const terms = glossaryTerms.map((s) => ({\n // biome-ignore-start lint/style/noNonNullAssertion: safely handled\n sourceLocale: s.sourceLocale!,\n sourceValue: s.sourceValue!,\n targetLocale: s.targetLocale!,\n targetValue: s.targetValue!,\n // biome-ignore-end lint/style/noNonNullAssertion: safely handled\n }))\n devLog.debug('Applying glossary terms', { terms })\n updates.push(addGlossaryTerms(client, terms))\n }\n\n if (updates.length === 0) {\n devLog.warn('No valid updates to apply - all suggestions may have been empty')\n }\n\n await Promise.all(updates)\n\n // Add change history entries\n onProgress?.({ stage: 'applying', message: 'Recording changes...' })\n await recordChangeHistory(\n client,\n settingsId,\n targetLanguage,\n suggestions.filter((s) => s.approved),\n )\n\n devLog.info('All improvements applied successfully')\n}\n\n/**\n * Updates both global and locale prompts using a deterministic template-based approach.\n * This appends suggestions with timestamps rather than using AI to merge them.\n */\nasync function updatePromptsWithTemplate(\n client: SanityClient,\n _schemaId: string,\n settingsId: string,\n targetLanguage: string,\n globalPrompts: FeedbackSuggestion[],\n localePrompts: FeedbackSuggestion[],\n): Promise<void> {\n devLog.info('Updating prompts with deterministic template', {\n settingsId,\n targetLanguage,\n hasGlobalUpdates: globalPrompts.length > 0,\n hasLocaleUpdates: localePrompts.length > 0,\n })\n\n if (!targetLanguage) {\n devLog.error('targetLanguage is undefined or empty!', {\n targetLanguage,\n globalPrompts: globalPrompts.length,\n localePrompts: localePrompts.length,\n })\n throw new Error('targetLanguage is required but was undefined')\n }\n\n const timestamp = new Date().toISOString().split('T')[0] // YYYY-MM-DD format\n\n // Fetch current settings\n const currentSettings = await client.fetch<{\n globalPrompt?: string\n localePrompts?: Array<{ _key: string; value: string }>\n }>('*[_id == $settingsId][0]{ globalPrompt, localePrompts }', { settingsId })\n\n // Update global prompt if needed\n if (globalPrompts.length > 0) {\n const currentGlobalPrompt = currentSettings?.globalPrompt || ''\n const updates = globalPrompts.map((s) => s.description.trim()).filter(Boolean)\n\n const newGlobalPrompt = [\n currentGlobalPrompt,\n currentGlobalPrompt ? '\\n' : '',\n `## User Feedback (${timestamp})`,\n ...updates.map((update) => `- ${update}`),\n ]\n .filter(Boolean)\n .join('\\n')\n\n await client.patch(settingsId).set({ globalPrompt: newGlobalPrompt }).commit()\n\n devLog.info('Global prompt updated', {\n previousLength: currentGlobalPrompt.length,\n newLength: newGlobalPrompt.length,\n addedUpdates: updates.length,\n })\n }\n\n // Update locale prompt if needed\n if (localePrompts.length > 0) {\n const updates = localePrompts.map((s) => s.description.trim()).filter(Boolean)\n\n // Check if locale prompt exists\n const existingLocalePrompts = currentSettings?.localePrompts || []\n const existingLocalePrompt = existingLocalePrompts.find((lp) => lp._key === targetLanguage)\n\n if (existingLocalePrompt) {\n // Update existing locale prompt\n const currentValue = existingLocalePrompt.value || ''\n const newValue = [\n currentValue,\n currentValue ? '\\n' : '',\n `## User Feedback (${timestamp})`,\n ...updates.map((update) => `- ${update}`),\n ]\n .filter(Boolean)\n .join('\\n')\n\n // Find the index of this locale prompt in the array\n const localeIndex = existingLocalePrompts.findIndex((lp) => lp._key === targetLanguage)\n\n await client\n .patch(settingsId)\n .set({ [`localePrompts[${localeIndex}].value`]: newValue })\n .commit()\n\n devLog.info('Locale prompt updated', {\n targetLanguage,\n previousLength: currentValue.length,\n newLength: newValue.length,\n addedUpdates: updates.length,\n })\n } else {\n // Create new locale prompt\n const newValue = [`## User Feedback (${timestamp})`, ...updates.map((update) => `- ${update}`)]\n .filter(Boolean)\n .join('\\n')\n\n await client\n .patch(settingsId)\n .setIfMissing({ localePrompts: [] })\n .append('localePrompts', [\n {\n _type: 'internationalizedArrayTextValue',\n _key: targetLanguage,\n value: newValue,\n },\n ])\n .commit()\n\n devLog.info('Locale prompt created', {\n targetLanguage,\n length: newValue.length,\n addedUpdates: updates.length,\n })\n }\n }\n\n devLog.info('Prompts updated successfully', {\n globalUpdated: globalPrompts.length > 0,\n localeUpdated: localePrompts.length > 0,\n })\n}\n\n/**\n * Adds new protected phrases to the translation settings.\n * Validates that phrases don't conflict with existing glossary terms.\n */\nasync function addProtectedPhrases(client: SanityClient, settingsId: string, phrases: string[]): Promise<void> {\n devLog.info('Adding protected phrases', { settingsId, phrasesCount: phrases.length, phrases })\n\n const currentPhrases = await client.fetch<string[]>('*[_id == $settingsId][0].protectedPhrases', { settingsId })\n devLog.debug('Fetched current protected phrases', { currentCount: currentPhrases?.length })\n\n // Filter out phrases that already exist\n let newPhrases = phrases.filter((phrase) => !currentPhrases?.includes(phrase))\n\n // Check for conflicts with glossary terms\n const conflictingPhrases: string[] = []\n for (const phrase of newPhrases) {\n const hasGlossaryConflict = await client.fetch<boolean>(\n `count(*[_type == \"translationGlossaryTerm\" && count(translations[value == $phrase]) > 0]) > 0`,\n { phrase },\n )\n\n if (hasGlossaryConflict) {\n conflictingPhrases.push(phrase)\n devLog.warn('Skipping protected phrase due to glossary conflict', { phrase })\n }\n }\n\n // Remove conflicting phrases\n newPhrases = newPhrases.filter((phrase) => !conflictingPhrases.includes(phrase))\n\n if (newPhrases.length === 0) {\n devLog.info('No new protected phrases to add (all already exist)')\n return\n }\n\n const result = await client\n .patch(settingsId)\n .setIfMissing({ protectedPhrases: [] })\n .append('protectedPhrases', newPhrases)\n .commit()\n\n devLog.info('Protected phrases added', { count: newPhrases.length, phrases: newPhrases, resultId: result._id })\n}\n\n/**\n * Adds new glossary terms or updates existing ones.\n * Validates that terms don't conflict with protected phrases.\n *\n * Behavior:\n * - If term doesn't exist: Creates new glossary term with source and target translations\n * - If term exists but target language missing: Adds the target translation\n * - If term exists with different target translation: Updates the target translation to new value\n * - If term exists with identical translation: Skips (no-op)\n * - If source or target value is a protected phrase: Skips with warning\n */\nasync function addGlossaryTerms(\n client: SanityClient,\n terms: Array<{ sourceLocale: string; sourceValue: string; targetLocale: string; targetValue: string }>,\n): Promise<void> {\n // Get protected phrases once for efficiency\n const protectedPhrases = await client.fetch<string[]>('*[_id == \"translation-settings\"][0].protectedPhrases')\n const protectedPhrasesLower = protectedPhrases?.map((p) => p.toLowerCase()) || []\n\n // Filter out terms where source === target (these should be protected phrases instead)\n const validTerms = terms.filter(({ sourceValue, targetValue }) => {\n if (sourceValue === targetValue) {\n devLog.warn('Skipping glossary term where source === target (should be a protected phrase)', {\n sourceValue,\n targetValue,\n })\n return false\n }\n\n // Check if source or target is a protected phrase\n if (protectedPhrasesLower.includes(sourceValue.toLowerCase())) {\n devLog.warn('Skipping glossary term because source value is a protected phrase', {\n sourceValue,\n targetValue,\n })\n return false\n }\n\n if (protectedPhrasesLower.includes(targetValue.toLowerCase())) {\n devLog.warn('Skipping glossary term because target value is a protected phrase', {\n sourceValue,\n targetValue,\n })\n return false\n }\n\n return true\n })\n\n if (validTerms.length === 0) {\n devLog.info('No valid glossary terms to add')\n return\n }\n\n for (const { sourceLocale, sourceValue, targetLocale, targetValue } of validTerms) {\n const existingTerm = await client.fetch<{\n _id: string\n translations: Array<{ _key: string; value: string }>\n } | null>(\n `*[_type == \"translationGlossaryTerm\" && count(translations[_key == $sourceLocale && value == $sourceValue]) > 0\n ][0] {\n _id,\n translations\n }`,\n { sourceLocale, sourceValue },\n )\n\n if (existingTerm) {\n const existingTargetIndex = existingTerm.translations.findIndex((t) => t._key === targetLocale)\n const existingTargetValue = existingTargetIndex >= 0 ? existingTerm.translations[existingTargetIndex].value : null\n\n if (existingTargetIndex === -1) {\n // Target language doesn't exist yet, add it\n await client\n .patch(existingTerm._id)\n .append('translations', [\n {\n _type: 'internationalizedArrayStringValue',\n _key: targetLocale,\n value: targetValue,\n },\n ])\n .commit()\n\n devLog.info('Added target language to existing glossary term', {\n sourceLocale,\n sourceValue,\n targetLocale,\n targetValue,\n })\n } else if (existingTargetValue !== targetValue) {\n // Target translation exists but is different, update it\n await client\n .patch(existingTerm._id)\n .set({ [`translations[${existingTargetIndex}].value`]: targetValue })\n .commit()\n\n devLog.info('Updated existing glossary term translation', {\n sourceLocale,\n sourceValue,\n targetLocale,\n oldValue: existingTargetValue,\n newValue: targetValue,\n })\n } else {\n // Translation already exists and is identical, skip\n devLog.info('Glossary term already has this exact translation', {\n sourceLocale,\n sourceValue,\n targetLocale,\n targetValue,\n })\n }\n } else {\n await client.create({\n _type: 'translationGlossaryTerm',\n translations: [\n {\n _type: 'internationalizedArrayStringValue',\n _key: sourceLocale,\n value: sourceValue,\n },\n {\n _type: 'internationalizedArrayStringValue',\n _key: targetLocale,\n value: targetValue,\n },\n ],\n })\n\n devLog.info('Created new glossary term', { sourceLocale, sourceValue, targetLocale, targetValue })\n }\n }\n\n devLog.info('Glossary terms processed', {\n totalRequested: terms.length,\n validAdded: validTerms.length,\n skipped: terms.length - validTerms.length,\n })\n}\n\n/**\n * Records change history for applied suggestions in translation settings.\n * This provides an audit trail and enables potential rollback functionality.\n */\nasync function recordChangeHistory(\n client: SanityClient,\n settingsId: string,\n targetLanguage: string,\n suggestions: FeedbackSuggestion[],\n): Promise<void> {\n const timestamp = new Date().toISOString()\n\n const historyEntries = suggestions.map((suggestion) => ({\n _type: 'object',\n _key: crypto.randomUUID(),\n timestamp,\n changeType: suggestion.type,\n description: suggestion.description,\n locale: suggestion.type === 'localePrompt' ? targetLanguage : undefined,\n }))\n\n if (historyEntries.length === 0) {\n return\n }\n\n await client.patch(settingsId).setIfMissing({ changeHistory: [] }).append('changeHistory', historyEntries).commit()\n\n devLog.info('Change history recorded', {\n count: historyEntries.length,\n timestamp,\n })\n}\n","import { CheckmarkCircleIcon, ErrorOutlineIcon } from '@sanity/icons'\nimport { Card, type CardTone, Flex, Spinner, Stack, Text } from '@sanity/ui'\nimport type { FC } from 'react'\n\ntype ProgressPanelStatus = 'in_progress' | 'completed' | 'error'\n\ninterface ProgressPanelProps {\n title?: string\n description?: string\n tone?: CardTone\n status?: ProgressPanelStatus\n icon?: React.ReactNode\n}\n\nconst ICON_SIZE = 64\n\nconst progressIconMap: Record<ProgressPanelStatus, React.ReactNode> = {\n in_progress: <Spinner />,\n completed: <CheckmarkCircleIcon fontSize={ICON_SIZE} />,\n error: <ErrorOutlineIcon fontSize={ICON_SIZE} />,\n}\n\nexport const ProgressPanel: FC<ProgressPanelProps> = ({\n title,\n description,\n tone,\n status = 'in_progress',\n icon,\n ...props\n}) => {\n return (\n <Card tone={tone} padding={4} {...props}>\n <Flex gap={4} align=\"center\" justify=\"center\">\n {icon || progressIconMap[status]}\n <Stack space={3}>\n {title && (\n <Text size={2} weight=\"bold\">\n {title}\n </Text>\n )}\n {description && (\n <Text size={1} muted>\n {description}\n </Text>\n )}\n </Stack>\n </Flex>\n </Card>\n )\n}\n","import { AddIcon, EmptyIcon, InfoOutlineIcon, TrashIcon, WarningOutlineIcon } from '@sanity/icons'\nimport { Box, Button, Card, Checkbox, Flex, Select, Stack, Text, TextArea, TextInput, Tooltip } from '@sanity/ui'\nimport { type FC, useState } from 'react'\nimport type { FeedbackSuggestion, ImprovementType } from '../types'\nimport { ProgressPanel } from './ProgressPanel'\n\ninterface SuggestionReviewListProps {\n suggestions: FeedbackSuggestion[]\n onSuggestionsChange: (suggestions: FeedbackSuggestion[]) => void\n sourceLanguage?: string\n targetLanguage?: string\n}\n\nconst IMPROVEMENT_TYPE_LABELS: Record<ImprovementType, string> = {\n globalPrompt: 'Global Prompt',\n localePrompt: 'Locale Prompt',\n protectedPhrase: 'Protected Phrase',\n glossaryTerm: 'Glossary Term',\n}\n\n/**\n * Creates a new empty suggestion with default values\n */\nconst createNewSuggestion = (\n type: ImprovementType = 'protectedPhrase',\n sourceLanguage?: string,\n targetLanguage?: string,\n): FeedbackSuggestion => {\n const baseSuggestion: FeedbackSuggestion = {\n id: crypto.randomUUID(),\n type,\n description: '',\n approved: true,\n }\n\n if (type === 'glossaryTerm') {\n return {\n ...baseSuggestion,\n sourceValue: '',\n targetValue: '',\n sourceLocale: sourceLanguage,\n targetLocale: targetLanguage,\n }\n }\n\n return baseSuggestion\n}\n\nexport const SuggestionReviewList: FC<SuggestionReviewListProps> = ({\n suggestions,\n onSuggestionsChange,\n sourceLanguage,\n targetLanguage,\n}) => {\n const [editingId, setEditingId] = useState<string | null>(null)\n\n const handleToggleApproval = (id: string) => {\n const updated = suggestions.map((s) => {\n // Don't allow toggling approval for invalid suggestions\n if (s.id === id && s.validationStatus !== 'invalid') {\n return { ...s, approved: !s.approved }\n }\n return s\n })\n onSuggestionsChange(updated)\n }\n\n const handleTypeChange = (id: string, newType: ImprovementType) => {\n const updated = suggestions.map((s) => {\n if (s.id !== id) {\n return s\n }\n\n // If changing to/from glossaryTerm, we need to handle sourceValue/targetValue\n if (newType === 'glossaryTerm' && s.type !== 'glossaryTerm') {\n // Parse description if it looks like \"source → target\"\n const match = s.description.match(/^(.+?)\\s*→\\s*(.+)$/)\n return {\n ...s,\n type: newType,\n sourceValue: match ? match[1].trim() : '',\n targetValue: match ? match[2].trim() : '',\n sourceLocale: sourceLanguage,\n targetLocale: targetLanguage,\n }\n }\n if (newType !== 'glossaryTerm' && s.type === 'glossaryTerm') {\n // Convert back to simple description\n return {\n ...s,\n type: newType,\n description: s.sourceValue && s.targetValue ? `${s.sourceValue} → ${s.targetValue}` : s.description,\n sourceValue: undefined,\n targetValue: undefined,\n }\n }\n return { ...s, type: newType }\n })\n onSuggestionsChange(updated)\n }\n\n const handleDescriptionChange = (id: string, newDescription: string) => {\n const updated = suggestions.map((s) => (s.id === id ? { ...s, description: newDescription } : s))\n onSuggestionsChange(updated)\n }\n\n const handleGlossarySourceChange = (id: string, newSource: string) => {\n const updated = suggestions.map((s) =>\n s.id === id\n ? {\n ...s,\n sourceValue: newSource,\n description: `${newSource} → ${s.targetValue || ''}`,\n // Preserve sourceLocale and targetLocale\n }\n : s,\n )\n onSuggestionsChange(updated)\n }\n\n const handleGlossaryTargetChange = (id: string, newTarget: string) => {\n const updated = suggestions.map((s) =>\n s.id === id\n ? {\n ...s,\n targetValue: newTarget,\n description: `${s.sourceValue || ''} → ${newTarget}`,\n // Preserve sourceLocale and targetLocale\n }\n : s,\n )\n onSuggestionsChange(updated)\n }\n\n const handleRemove = (id: string) => {\n const updated = suggestions.filter((s) => s.id !== id)\n onSuggestionsChange(updated)\n }\n\n const handleAddSuggestion = (type: ImprovementType) => {\n const newSuggestion = createNewSuggestion(type, sourceLanguage, targetLanguage)\n onSuggestionsChange([...suggestions, newSuggestion])\n // Auto-focus on the new suggestion by setting it as editing\n setEditingId(newSuggestion.id)\n }\n\n if (suggestions.length === 0) {\n return (\n <Stack space={3}>\n <ProgressPanel\n status=\"error\"\n tone=\"transparent\"\n title=\"No suggestions generated\"\n description='Try providing more specific feedback or enabling \"Include Edits\".'\n icon={<EmptyIcon fontSize={24} />}\n />\n <Flex justify=\"flex-start\">\n <Button\n mode=\"ghost\"\n tone=\"primary\"\n icon={AddIcon}\n text=\"Add Suggestion\"\n fontSize={1}\n onClick={() => handleAddSuggestion('protectedPhrase')}\n />\n </Flex>\n </Stack>\n )\n }\n\n return (\n <Stack space={3}>\n {suggestions.map((suggestion) => {\n const isEditing = editingId === suggestion.id\n const isGlossaryTerm = suggestion.type === 'glossaryTerm'\n const isPrompt = suggestion.type === 'globalPrompt' || suggestion.type === 'localePrompt'\n const hasWarning = suggestion.warning && suggestion.validationStatus !== 'valid'\n const isInvalid = suggestion.validationStatus === 'invalid'\n\n return (\n <Card\n key={suggestion.id}\n padding={4}\n border\n tone={isInvalid ? 'critical' : hasWarning ? 'caution' : suggestion.approved ? 'default' : 'transparent'}\n style={{ opacity: suggestion.approved ? 1 : 0.5 }}\n >\n <Stack space={3}>\n <Flex align=\"flex-start\" gap={3}>\n <Box paddingTop={1}>\n {isInvalid ? (\n <Tooltip\n content={\n <Box padding={2} style={{ maxWidth: '280px' }}>\n <Text size={1}>\n This suggestion cannot be applied because it conflicts with existing settings. See the error\n message below for details.\n </Text>\n </Box>\n }\n placement=\"top\"\n portal\n >\n <Box>\n <Checkbox checked={false} disabled readOnly />\n </Box>\n </Tooltip>\n ) : (\n <Checkbox checked={suggestion.approved} onChange={() => handleToggleApproval(suggestion.id)} />\n )}\n </Box>\n\n <Stack space={3} flex={1}>\n <Flex gap={2} align=\"center\" wrap=\"wrap\">\n <Box flex={1} style={{ minWidth: '200px' }}>\n <Select\n fontSize={1}\n value={suggestion.type}\n onChange={(event) =>\n handleTypeChange(suggestion.id, event.currentTarget.value as ImprovementType)\n }\n aria-label=\"Suggestion type\"\n >\n {Object.entries(IMPROVEMENT_TYPE_LABELS).map(([value, label]) => (\n <option key={value} value={value}>\n {label}\n </option>\n ))}\n </Select>\n </Box>\n\n <Button\n mode=\"bleed\"\n tone=\"critical\"\n icon={TrashIcon}\n fontSize={1}\n padding={2}\n onClick={() => handleRemove(suggestion.id)}\n aria-label=\"Remove suggestion\"\n title=\"Remove suggestion\"\n />\n </Flex>\n\n {isGlossaryTerm ? (\n <Stack space={3}>\n {/* Language labels */}\n {(suggestion.sourceLocale || suggestion.targetLocale) && (\n <Flex gap={2} align=\"center\">\n <Box flex={1}>\n <Text size={0} muted weight=\"medium\">\n {suggestion.sourceLocale ? `Source (${suggestion.sourceLocale})` : 'Source'}\n </Text>\n </Box>\n <Box style={{ width: '20px' }} />\n <Box flex={1}>\n <Text size={0} muted weight=\"medium\">\n {suggestion.targetLocale ? `Target (${suggestion.targetLocale})` : 'Target'}\n </Text>\n </Box>\n </Flex>\n )}\n\n {/* Term inputs */}\n <Flex gap={2} align=\"center\">\n <Box flex={1}>\n <TextInput\n fontSize={1}\n placeholder=\"Source term\"\n value={suggestion.sourceValue || ''}\n onChange={(event) => handleGlossarySourceChange(suggestion.id, event.currentTarget.value)}\n aria-label={`Source term in ${suggestion.sourceLocale || 'source language'}`}\n />\n </Box>\n <Text size={1} muted aria-hidden=\"true\">\n →\n </Text>\n <Box flex={1}>\n <TextInput\n fontSize={1}\n placeholder=\"Target term\"\n value={suggestion.targetValue || ''}\n onChange={(event) => handleGlossaryTargetChange(suggestion.id, event.currentTarget.value)}\n aria-label={`Target term in ${suggestion.targetLocale || 'target language'}`}\n />\n </Box>\n </Flex>\n </Stack>\n ) : isPrompt ? (\n <Stack space={2}>\n <TextArea\n fontSize={1}\n rows={isEditing ? 6 : 3}\n value={suggestion.description}\n onChange={(event) => handleDescriptionChange(suggestion.id, event.currentTarget.value)}\n onFocus={() => setEditingId(suggestion.id)}\n onBlur={() => setEditingId(null)}\n placeholder=\"Enter prompt guidance...\"\n aria-label={`${IMPROVEMENT_TYPE_LABELS[suggestion.type]} content`}\n />\n </Stack>\n ) : (\n <TextInput\n fontSize={1}\n value={suggestion.description}\n onChange={(event) => handleDescriptionChange(suggestion.id, event.currentTarget.value)}\n placeholder=\"Enter protected phrase...\"\n aria-label=\"Protected phrase\"\n />\n )}\n\n {/* Display validation warning */}\n {hasWarning && (\n <Card padding={3} tone={isInvalid ? 'critical' : 'caution'} border radius={2}>\n <Flex align=\"flex-start\" gap={2}>\n <Text size={1}>{isInvalid ? <WarningOutlineIcon /> : <InfoOutlineIcon />}</Text>\n <Stack space={2} flex={1}>\n <Text size={1} weight=\"medium\">\n {isInvalid ? 'Invalid Suggestion' : 'Warning'}\n </Text>\n <Text size={1} muted>\n {suggestion.warning}\n </Text>\n </Stack>\n </Flex>\n </Card>\n )}\n </Stack>\n </Flex>\n </Stack>\n </Card>\n )\n })}\n\n <Flex justify=\"flex-start\">\n <Button\n mode=\"ghost\"\n tone=\"neutral\"\n icon={AddIcon}\n text=\"Add Suggestion\"\n fontSize={1}\n onClick={() => handleAddSuggestion('protectedPhrase')}\n />\n </Flex>\n </Stack>\n )\n}\n","import { ArrowLeftIcon, EditIcon, InfoOutlineIcon } from '@sanity/icons'\nimport { Box, Button, Card, Checkbox, Flex, Heading, Inline, Stack, Text, TextArea, Tooltip } from '@sanity/ui'\nimport { type FC, useState } from 'react'\nimport { useClient } from 'sanity'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport { useTranslationMetadata } from '../hooks/useTranslationMetadata'\nimport type { FeedbackSuggestion } from '../types'\nimport { analyzeFeedback, applySuggestions } from '../utils/feedback/processFeedback'\nimport { ProgressPanel } from './ProgressPanel'\nimport { SuggestionReviewList } from './SuggestionReviewList'\n\ninterface GiveFeedbackDialogProps {\n setIsDialogOpen: (isOpen: boolean) => void\n}\n\ntype ProcessingState = 'idle' | 'analyzing' | 'reviewing' | 'applying' | 'completed' | 'error'\n\nexport const GiveFeedbackDialog: FC<GiveFeedbackDialogProps> = ({ setIsDialogOpen }) => {\n const client = useClient({ apiVersion: 'vX' })\n const { document, config, translationMetadata } = useTranslationContext()\n const { loading } = useTranslationMetadata({\n documentId: document._id,\n })\n\n const [feedback, setFeedback] = useState('')\n const [shouldIncludeEdits, setShouldIncludeEdits] = useState(false)\n const [processingState, setProcessingState] = useState<ProcessingState>('idle')\n const [suggestions, setSuggestions] = useState<FeedbackSuggestion[]>([])\n const [changeSummary, setChangeSummary] = useState<string>('')\n const [error, setError] = useState<string>('')\n const [sourceLanguage, setSourceLanguage] = useState<string>('')\n const [targetLanguage, setTargetLanguage] = useState<string>('')\n\n const handleShouldIncludeEditsChange = () => {\n setShouldIncludeEdits(!shouldIncludeEdits)\n }\n\n const handleCancel = () => {\n setIsDialogOpen(false)\n }\n\n const handleAnalyze = async () => {\n if (!feedback && !shouldIncludeEdits) {\n setError('Please provide feedback or enable \"Include Edits\"')\n return\n }\n\n if (!config) {\n setError('Missing plugin configuration')\n return\n }\n\n setError('')\n setProcessingState('analyzing')\n setChangeSummary('')\n\n try {\n const translationHistory = translationMetadata?.translationHistory?.find(\n (history) => history.language === document[config.languageFieldName],\n )\n\n if (!translationHistory) {\n throw new Error(\n 'No translation history found for this document. Please ensure this document was translated using the AI Translation plugin.',\n )\n }\n\n const { sourceSnapshot, targetSnapshot, sourceLanguage: srcLang } = translationHistory\n\n if (!sourceSnapshot || !targetSnapshot) {\n throw new Error('Translation snapshots are missing from history.')\n }\n\n const srcLanguage = srcLang || 'en-us'\n const tgtLanguage = document[config.languageFieldName] as string\n\n // Store languages for use in UI\n setSourceLanguage(srcLanguage)\n setTargetLanguage(tgtLanguage)\n\n // Analyze the feedback using the AI agent\n const generatedSuggestions = await analyzeFeedback({\n client,\n schemaId: config.schemaId,\n sourceLanguage: srcLanguage,\n targetLanguage: tgtLanguage,\n userFeedback: feedback || undefined,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument: shouldIncludeEdits ? document : undefined,\n onProgress: ({ stage, message }) => {\n setProcessingState(stage)\n setChangeSummary(message)\n },\n })\n\n setSuggestions(generatedSuggestions)\n setProcessingState('reviewing')\n } catch (err) {\n console.error('Error analyzing feedback:', err)\n setError(err instanceof Error ? err.message : 'An unexpected error occurred')\n setProcessingState('error')\n }\n }\n\n const handleApplySuggestions = async () => {\n if (!config) {\n setError('Missing plugin configuration')\n return\n }\n\n const approvedCount = suggestions.filter((s) => s.approved).length\n if (approvedCount === 0) {\n setError('Please approve at least one suggestion to apply')\n return\n }\n\n setError('')\n setProcessingState('applying')\n\n try {\n const translationHistory = translationMetadata?.translationHistory?.find(\n (history) => history.language === document[config.languageFieldName],\n )\n\n if (!translationHistory) {\n throw new Error('Translation history not found')\n }\n\n await applySuggestions({\n client,\n schemaId: config.schemaId,\n sourceLanguage: translationHistory.sourceLanguage || 'en-us',\n targetLanguage: translationHistory.language,\n suggestions,\n onProgress: ({ stage, message }) => {\n setProcessingState(stage)\n setChangeSummary(message)\n },\n })\n\n setProcessingState('completed')\n } catch (err) {\n console.error('Error applying suggestions:', err)\n setError(err instanceof Error ? err.message : 'An unexpected error occurred')\n setProcessingState('error')\n }\n }\n\n if (loading) {\n return <ProgressPanel title=\"Loading translation metadata\" />\n }\n\n // Show analyzing state\n if (processingState === 'analyzing') {\n return <ProgressPanel title=\"Analyzing feedback with AI...\" description={changeSummary} />\n }\n\n // Show review state\n if (processingState === 'reviewing') {\n return (\n <Stack space={4}>\n <Stack space={4}>\n <Heading>Review Suggestions</Heading>\n <Text muted>\n The AI has analyzed your feedback and generated the following suggestions. Review and edit them before\n applying.\n </Text>\n </Stack>\n\n <SuggestionReviewList\n suggestions={suggestions}\n onSuggestionsChange={setSuggestions}\n sourceLanguage={sourceLanguage}\n targetLanguage={targetLanguage}\n />\n\n {error && (\n <Card padding={3} tone=\"critical\" border>\n <Text size={1}>{error}</Text>\n </Card>\n )}\n\n <Flex justify=\"space-between\" gap={2} paddingTop={2}>\n <Flex gap={2}>\n <Button\n type=\"button\"\n mode=\"bleed\"\n tone=\"neutral\"\n onClick={() => setProcessingState('idle')}\n icon={<ArrowLeftIcon />}\n text=\"Back\"\n />\n </Flex>\n <Button\n type=\"button\"\n mode=\"ghost\"\n tone=\"positive\"\n size={1}\n onClick={handleApplySuggestions}\n disabled={suggestions.filter((s) => s.approved).length === 0}\n >\n Apply {suggestions.filter((s) => s.approved).length} Suggestion\n {suggestions.filter((s) => s.approved).length !== 1 ? 's' : ''}\n </Button>\n </Flex>\n </Stack>\n )\n }\n\n // Show applying state\n if (processingState === 'applying') {\n return <ProgressPanel title=\"Updating translation settings...\" description={changeSummary} icon={<EditIcon />} />\n }\n\n // Show completion state\n if (processingState === 'completed') {\n const appliedCount = suggestions.filter((s) => s.approved).length\n const description = `${appliedCount} suggestion${appliedCount !== 1 ? 's' : ''} applied. Translation prompts and glossary havebeen updated based on your feedback.`\n return (\n <Box padding={4}>\n <ProgressPanel title=\"Translation settings updated\" description={description} status=\"completed\" />\n\n <Flex justify=\"flex-end\" paddingTop={3}>\n <Button type=\"button\" mode=\"ghost\" tone=\"primary\" size={1} onClick={handleCancel}>\n Close\n </Button>\n </Flex>\n </Box>\n )\n }\n\n // Show error state\n if (processingState === 'error') {\n return (\n <Box padding={4}>\n <Stack space={4}>\n <ProgressPanel status=\"error\" title=\"Error processing feedback\" description={error} tone=\"critical\" />\n <Flex justify=\"flex-end\" gap={2}>\n <Button type=\"button\" mode=\"ghost\" onClick={handleCancel}>\n Close\n </Button>\n <Button type=\"reset\" mode=\"ghost\" tone=\"primary\" onClick={() => setProcessingState('idle')}>\n Reset\n </Button>\n </Flex>\n </Stack>\n </Box>\n )\n }\n\n // Default form view\n return (\n <Stack space={4}>\n <Card tone=\"transparent\" padding={4}>\n <Stack space={4}>\n <Stack space={2}>\n <Stack space={4}>\n <Inline space={2}>\n <Text size={2} muted>\n Enter your feedback for the translation AI agent.\n </Text>\n <Tooltip\n portal\n content={\n <Box padding={2} style={{ maxWidth: '280px' }}>\n <Text size={1}>\n The AI agent will use your feedback to improve translation prompts, update protected phrases,\n and modify the glossary. Be specific about what should be changed.\n </Text>\n </Box>\n }\n >\n <Text size={1}>\n <InfoOutlineIcon />\n </Text>\n </Tooltip>\n </Inline>\n <TextArea\n rows={8}\n fontSize={2}\n onChange={(event) => setFeedback(event.currentTarget.value)}\n padding={3}\n placeholder='Example: \"Braze\" is a branded term and should not be translated.'\n value={feedback}\n />\n </Stack>\n </Stack>\n\n <Stack space={2}>\n <Stack space={3}>\n <Button\n type=\"button\"\n mode={shouldIncludeEdits ? 'ghost' : 'bleed'}\n size={2}\n onClick={handleShouldIncludeEditsChange}\n tone={shouldIncludeEdits ? 'primary' : 'default'}\n >\n <Flex align=\"flex-start\" gap={3}>\n <Box paddingTop={1}>\n <Checkbox checked={shouldIncludeEdits} readOnly style={{ pointerEvents: 'none' }} />\n </Box>\n <Stack space={2} flex={1}>\n <Text size={2} weight=\"medium\">\n Include Edits in Feedback\n </Text>\n <Text size={1} muted style={{ textAlign: 'left' }}>\n The AI will analyze edits you made to the translation.\n </Text>\n </Stack>\n </Flex>\n </Button>\n </Stack>\n </Stack>\n </Stack>\n </Card>\n <Flex justify=\"flex-end\" gap={2} paddingTop={2}>\n <Button type=\"button\" mode=\"ghost\" size={1} onClick={handleCancel}>\n Cancel\n </Button>\n <Button\n type=\"submit\"\n mode=\"ghost\"\n tone=\"positive\"\n size={1}\n onClick={handleAnalyze}\n disabled={!feedback.trim() && !shouldIncludeEdits}\n >\n Analyze Feedback\n </Button>\n </Flex>\n </Stack>\n )\n}\n","export interface ValidationResult {\n valid: boolean\n error?: string\n modelAvailable?: string[]\n}\n\n/**\n * Validates an OpenAI API key by making a minimal API call.\n * Checks if the key is valid and if the requested model is available.\n */\nexport const validateOpenAIApiKey = async (apiKey: string, model = 'gpt-5'): Promise<ValidationResult> => {\n try {\n // Make a minimal API call to validate the key\n const response = await fetch('https://api.openai.com/v1/models', {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n },\n })\n\n if (!response.ok) {\n const error = await response.json()\n return {\n valid: false,\n error: error.error?.message || 'Invalid API key',\n }\n }\n\n const data = await response.json()\n const availableModels = data.data.map((m: { id: string }) => m.id)\n\n // Check if requested model is available\n if (!availableModels.includes(model)) {\n return {\n valid: false,\n error: `Model ${model} not available. Try: ${availableModels.slice(0, 5).join(', ')}`,\n }\n }\n\n return {\n valid: true,\n modelAvailable: availableModels,\n }\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : 'Network error',\n }\n }\n}\n","import defaults from 'lodash.defaults'\nimport type { TranslationAction, TranslationContext } from '../../types'\nimport { devLog } from '../../utils/common/devLog'\n\nexport const translationReducer = (\n context: Omit<TranslationContext, 'dispatch'>,\n action: TranslationAction,\n): Omit<TranslationContext, 'dispatch'> => {\n const logContext = {\n documentId: context.document?._id,\n actionType: action.type,\n component: 'translationReducer',\n }\n\n devLog.debug('Translation action dispatched', {\n ...logContext,\n payload: action.payload,\n })\n\n switch (action.type) {\n case 'SET_OVERALL_STATUS':\n return { ...context, overallStatus: action.payload }\n\n case 'SET_TRANSLATION_METADATA':\n return { ...context, translationMetadata: action.payload }\n\n case 'SET_SELECTED_TARGET_LANGUAGES':\n return {\n ...context,\n selectedTargetLanguages: action.payload,\n }\n\n case 'SET_TRANSLATION_MODE':\n return {\n ...context,\n translationMode: action.payload,\n }\n\n case 'SET_OPENAI_VALIDATED':\n return {\n ...context,\n openaiValidated: action.payload,\n }\n\n case 'SET_SHOULD_TRANSLATE_REFERENCES':\n return {\n ...context,\n shouldTranslateReferences: action.payload,\n }\n\n case 'SET_MAX_REFERENCE_DEPTH':\n return {\n ...context,\n maxReferenceDepth: action.payload,\n }\n\n case 'SET_SHOULD_OVERWRITE_FRESH_TRANSLATIONS':\n return {\n ...context,\n shouldOverwriteFreshTranslations: action.payload,\n }\n\n case 'SET_REFERENCES':\n return {\n ...context,\n references: action.payload,\n }\n\n case 'SET_TRANSLATION_JOB_STATUS': {\n // Create a unique key for each job using both document ID and language\n // This ensures concurrent translations to different languages don't conflict\n const jobKey = `${action.payload._id}_${action.payload.language}`\n\n // Create a new Map to trigger React re-render (immutability)\n const newTranslationJobs = new Map(context.translationJobs)\n\n newTranslationJobs.set(jobKey, defaults(action.payload, newTranslationJobs.get(jobKey)))\n\n return {\n ...context,\n translationJobs: newTranslationJobs,\n }\n }\n\n default:\n return context\n }\n}\n","import { type FC, useEffect, useReducer } from 'react'\nimport type { PluginConfig, TranslatableDocument, TranslationContext as TTranslationContext } from '../../types'\nimport { devLog } from '../../utils/common/devLog'\nimport { validateOpenAIApiKey } from '../../utils/openai/validateApiKey'\nimport { TranslationContext } from './TranslationContext'\nimport { translationReducer } from './translationReducer'\n\ninterface TranslationContextProviderProps {\n document: TranslatableDocument\n config: PluginConfig\n children: React.ReactNode\n}\n\nexport const TranslationContextProvider: FC<TranslationContextProviderProps> = ({ document, config, children }) => {\n devLog.debug('TranslationContextProvider initialized', {\n documentId: document._id,\n documentType: document._type,\n supportedLanguages: config.supportedLanguages?.map((l) => l.id),\n component: 'TranslationContextProvider',\n })\n\n const initialContext: Omit<TTranslationContext, 'dispatch'> = {\n document,\n config,\n overallStatus: 'idle',\n translationMetadata: null,\n selectedTargetLanguages: [],\n shouldOverwriteFreshTranslations: false,\n shouldTranslateReferences: false,\n maxReferenceDepth: 1,\n references: [],\n translationJobs: new Map(),\n translationMode: config?.openai?.defaultMode || 'sanity-only',\n openaiValidated: false,\n }\n\n const [context, dispatch] = useReducer(translationReducer, initialContext)\n\n // Validate OpenAI API key on mount if configured\n useEffect(() => {\n const validateKey = async () => {\n if (config?.openai?.enabled && config?.openai?.apiKey) {\n try {\n const validation = await validateOpenAIApiKey(config.openai.apiKey, config.openai.model || 'gpt-5')\n\n dispatch({\n type: 'SET_OPENAI_VALIDATED',\n payload: validation.valid,\n })\n\n if (validation.valid) {\n devLog.info('OpenAI API key validated successfully', {\n model: config.openai.model || 'gpt-5',\n component: 'TranslationContextProvider',\n })\n } else {\n devLog.error('OpenAI API key validation failed', {\n error: validation.error,\n component: 'TranslationContextProvider',\n })\n }\n } catch (error) {\n devLog.error('OpenAI validation error', error instanceof Error ? error : new Error(String(error)), {\n component: 'TranslationContextProvider',\n })\n dispatch({\n type: 'SET_OPENAI_VALIDATED',\n payload: false,\n })\n }\n }\n }\n\n validateKey()\n }, [config?.openai?.apiKey, config?.openai?.enabled, config?.openai?.model])\n\n devLog.debug('Translation context state initialized', {\n documentId: document._id,\n overallStatus: context.overallStatus,\n selectedLanguages: context.selectedTargetLanguages,\n translationMode: context.translationMode,\n openaiEnabled: config?.openai?.enabled,\n component: 'TranslationContextProvider',\n })\n\n return <TranslationContext.Provider value={{ ...context, dispatch }}>{children}</TranslationContext.Provider>\n}\n","import { TranslateIcon } from '@sanity/icons'\nimport { useState } from 'react'\nimport type { DocumentActionDescription, DocumentActionProps } from 'sanity'\nimport { GiveFeedbackDialog } from './components/GiveFeedbackDialog'\nimport { TranslationContextProvider } from './hooks/useTranslationContext/TranslationContextProvider'\nimport type { PluginConfig, TranslatableDocument } from './types'\nimport { devLog } from './utils/common/devLog'\n\ninterface TranslateActionProps extends DocumentActionProps {\n config: PluginConfig\n}\n\nexport const getGiveTranslationFeedbackAction = (config: PluginConfig) => {\n return ({ draft, published }: TranslateActionProps): DocumentActionDescription | null => {\n const document = draft ?? published\n const [isDialogOpen, setIsDialogOpen] = useState(false)\n\n devLog.debug('ImproveTranslationPromptsAction initialized', {\n documentId: document?._id,\n documentType: document?._type,\n })\n\n if (!document || !config.translatableSchemaTypes?.includes(document._type)) {\n devLog.warn('Document not in translatable schema types', {\n documentType: document?._type,\n translatableTypes: config.translatableSchemaTypes,\n })\n return null\n }\n\n if (!config) {\n devLog.error('Missing config in TranslateAction')\n return null\n }\n\n return {\n label: 'Give Translation Feedback',\n icon: TranslateIcon,\n onHandle: () => {\n devLog.info('Give Translation Feedback dialog opened', {\n documentId: document._id,\n documentType: document._type,\n component: 'GiveTranslationFeedbackAction',\n })\n setIsDialogOpen(true)\n },\n dialog: isDialogOpen && {\n type: 'dialog',\n onClose: () => setIsDialogOpen(false),\n header: 'Give Translation Feedback',\n content: (\n <TranslationContextProvider document={document as TranslatableDocument} config={config}>\n <GiveFeedbackDialog setIsDialogOpen={setIsDialogOpen} />\n </TranslationContextProvider>\n ),\n },\n }\n }\n}\n","import { TranslateIcon } from '@sanity/icons'\nimport { defineField, defineType } from 'sanity'\n\nexport const translationGlossaryTerm = defineType({\n name: 'translationGlossaryTerm',\n title: 'Translation Glossary Term',\n icon: TranslateIcon,\n type: 'document',\n fields: [\n defineField({\n name: 'translations',\n title: 'Translations',\n type: 'internationalizedArrayString',\n }),\n ],\n preview: {\n select: {\n translations: 'translations',\n },\n prepare({ translations }: { translations: { locale: string; value: string }[] }) {\n const enTranslation = translations?.find((t) => t.locale === 'en-us')\n const firstTranslation = translations?.[0]\n return {\n title: enTranslation?.value || firstTranslation?.value || 'No translation available',\n }\n },\n },\n})\n","import { defineField } from 'sanity'\n\nexport const translationHistory = defineField({\n name: 'translationHistory',\n type: 'array',\n of: [\n {\n name: 'targetLanguageHistory',\n type: 'object',\n fields: [\n defineField({\n name: 'language',\n title: 'Language',\n description: 'The language of the target document',\n type: 'string',\n }),\n defineField({\n name: 'sourceId',\n title: 'Source ID',\n description: 'The ID of the source document',\n type: 'string',\n }),\n defineField({\n name: 'sourceRev',\n title: 'Source Revision',\n description: 'The revision ID of the source document at the time of translation',\n type: 'string',\n }),\n defineField({\n name: 'sourceLanguage',\n title: 'Source Language',\n description: 'The language of the source document',\n type: 'string',\n }),\n defineField({\n name: 'sourceSnapshot',\n title: 'Source Snapshot',\n description: 'The snapshot of the source document at the time of translation',\n type: 'text',\n }),\n defineField({\n name: 'targetId',\n title: 'Target ID',\n description: 'The ID of the target document',\n type: 'string',\n }),\n ],\n },\n ],\n})\n","import { TranslateIcon } from '@sanity/icons'\nimport { defineField, defineType } from 'sanity'\n\nexport const translationSettings = defineType({\n name: 'translationSettings',\n title: 'Translation Settings',\n icon: TranslateIcon,\n type: 'document',\n groups: [\n {\n name: 'general',\n title: 'General',\n default: true,\n },\n {\n name: 'history',\n title: 'History',\n },\n ],\n fields: [\n defineField({\n name: 'globalPrompt',\n title: 'Global Translation Prompt',\n description: 'A prompt that will given to the Sanity AI Agent for all translation tasks.',\n type: 'text',\n rows: 5,\n group: 'general',\n }),\n defineField({\n name: 'localePrompts',\n title: 'Locale Prompts',\n description: 'Prompts that will be given to the Sanity AI Agent for each locale.',\n type: 'internationalizedArrayText',\n group: 'general',\n }),\n defineField({\n name: 'protectedPhrases',\n title: 'Protected Phrases',\n description: 'Phrases that will never be translated.',\n type: 'array',\n of: [{ type: 'string' }],\n group: 'general',\n }),\n defineField({\n name: 'changeHistory',\n title: 'Change History',\n description: 'History of changes made via user feedback.',\n type: 'array',\n readOnly: true,\n group: 'history',\n of: [\n {\n type: 'object',\n fields: [\n defineField({\n name: 'timestamp',\n title: 'Timestamp',\n type: 'datetime',\n }),\n defineField({\n name: 'changeType',\n title: 'Change Type',\n type: 'string',\n options: {\n list: [\n { title: 'Global Prompt', value: 'globalPrompt' },\n { title: 'Locale Prompt', value: 'localePrompt' },\n { title: 'Protected Phrase', value: 'protectedPhrase' },\n { title: 'Glossary Term', value: 'glossaryTerm' },\n ],\n },\n }),\n defineField({\n name: 'description',\n title: 'Description',\n type: 'text',\n rows: 3,\n }),\n defineField({\n name: 'locale',\n title: 'Locale',\n type: 'string',\n description: 'Target locale for locale-specific changes',\n }),\n ],\n preview: {\n select: {\n timestamp: 'timestamp',\n changeType: 'changeType',\n description: 'description',\n },\n prepare({ timestamp, changeType, description }) {\n return {\n title: `${changeType} - ${new Date(timestamp).toLocaleDateString()}`,\n subtitle: description,\n }\n },\n },\n },\n ],\n }),\n ],\n preview: {\n prepare: () => ({\n title: 'Translation Settings',\n }),\n },\n})\n","/**\n * Semaphore class for rate limiting concurrent operations\n * Ensures that only a specified number of operations can run simultaneously\n */\nexport class Semaphore {\n private permits: number\n private queue: Array<() => void> = []\n\n constructor(permits: number) {\n this.permits = permits\n }\n\n /**\n * Acquire a permit from the semaphore\n * If no permits are available, the caller will wait until one becomes available\n */\n async acquire(): Promise<void> {\n if (this.permits > 0) {\n this.permits--\n return Promise.resolve()\n }\n\n // Wait for a permit to become available\n return new Promise<void>((resolve) => {\n this.queue.push(resolve)\n })\n }\n\n /**\n * Release a permit back to the semaphore\n * If there are waiting callers, one will be woken up\n */\n release(): void {\n const next = this.queue.shift()\n if (next) {\n // Give the permit to the next waiting caller\n next()\n } else {\n // No one is waiting, increment the available permits\n this.permits++\n }\n }\n\n /**\n * Execute a function with semaphore protection\n * Automatically acquires and releases the permit\n */\n async withPermit<T>(fn: () => Promise<T>): Promise<T> {\n await this.acquire()\n try {\n return await fn()\n } finally {\n this.release()\n }\n }\n\n /**\n * Get the number of available permits\n */\n availablePermits(): number {\n return this.permits\n }\n\n /**\n * Get the number of waiting callers\n */\n queueLength(): number {\n return this.queue.length\n }\n\n /**\n * Update the number of permits\n * If increasing permits, waiting callers will be notified\n * If decreasing permits below current available, available permits will be reduced\n */\n updatePermits(newPermits: number): void {\n const currentTotal = this.permits + this.queue.length\n const difference = newPermits - currentTotal\n\n if (difference > 0) {\n // Increasing permits - wake up waiting callers or add to available permits\n for (let i = 0; i < difference && this.queue.length > 0; i++) {\n const next = this.queue.shift()\n if (next) next()\n }\n // Add any remaining permits to available count\n const remainingIncrease = difference - (currentTotal - this.permits - this.queue.length)\n if (remainingIncrease > 0) {\n this.permits += remainingIncrease\n }\n } else if (difference < 0) {\n // Decreasing permits - reduce available permits (but don't interrupt running operations)\n this.permits = Math.max(0, this.permits + difference)\n }\n }\n}\n","import { type PortableTextBlock, toPlainText } from '@portabletext/react'\nimport type { TranslatableDocument } from '../../types'\n\n/**\n * Function to convert a sanity document to plain text.\n * Returns an object following these rules:\n * - All top-level fields are included.\n * - If a field is an array AND contains at least one object with `_type === 'block'`, it is converted to plain text with `toPlainText`.\n */\nexport function documentToPlainText(document?: TranslatableDocument): string {\n if (!document) return ''\n\n const result: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(document)) {\n // Check if the value is an array\n if (Array.isArray(value)) {\n // Check if the array contains at least one Portable Text block\n const hasBlockType = value.some(\n (item) => typeof item === 'object' && item !== null && '_type' in item && item._type === 'block',\n )\n\n if (hasBlockType) {\n // Convert Portable Text blocks to plain text\n result[key] = toPlainText(value as PortableTextBlock[])\n } else {\n // Keep the array as-is\n result[key] = value\n }\n } else {\n // Keep non-array values as-is\n result[key] = value\n }\n }\n\n return JSON.stringify(result)\n}\n","/**\n * Fuzzy glossary-term matching against a single source document.\n *\n * Design goals:\n * - Tokenize the source ONCE, then test many glossary terms cheaply.\n * - Order-agnostic “bag of words” matching inside a sliding window.\n * - Tolerates filler words and a small number of extra words (“window slack”).\n * - Case-insensitive, punctuation-insensitive, and diacritic-insensitive.\n * - Prefers recall (false positives are more acceptable than false negatives).\n */\n\nexport type Match =\n | { matched: true; term: string; windowStart: number; windowEnd: number; matchedRatio: number }\n | { matched: false; term: string; reason: string }\n\nexport interface MatchOptions {\n /**\n * Extra tokens allowed on top of the term length for the sliding window.\n * e.g., term has 3 tokens → default window size = 3 + windowSlack.\n * Larger slack → more forgiving (higher recall, lower precision).\n */\n windowSlack?: number\n\n /**\n * Ratio [0..1] of term tokens that must be satisfied inside a window.\n * 1.0 == all term tokens present (default).\n * 0.8 == at least 80% of term tokens present (useful for partials/variants).\n */\n minTokenMatchRatio?: number\n\n /**\n * Stop/filler words to ignore in term tokenization (e.g., “a”, “the”, “to”).\n * These are removed from the term ONLY (not from the source tokens),\n * so that “navigate using a map” still matches “navigate using the digital map”.\n */\n stopwords?: string[]\n\n /**\n * If true, we’ll also drop very short tokens from the term (length <= 2),\n * after stopword removal. Helps avoid spurious matches on tiny tokens.\n */\n dropVeryShortTermTokens?: boolean\n\n /**\n * If provided, only these characters are considered word chars in tokenization.\n * By default we use a broad “letters/digits/underscore” definition.\n */\n allowedWordCharsRegex?: RegExp\n}\n\nexport interface SourceContext {\n /** Tokenized, normalized source text. */\n tokens: string[]\n}\n\n/** Reasonable defaults tuned for translation glossary recall. */\nconst DEFAULTS: Required<MatchOptions> = {\n windowSlack: 2,\n minTokenMatchRatio: 1.0,\n stopwords: [\n // common English fillers — extend per your locales\n 'a',\n 'an',\n 'the',\n 'and',\n 'or',\n 'to',\n 'of',\n 'for',\n 'in',\n 'on',\n 'at',\n 'by',\n 'with',\n 'using',\n 'from',\n 'into',\n 'over',\n 'under',\n 'as',\n 'is',\n 'are',\n 'be',\n ],\n dropVeryShortTermTokens: true,\n allowedWordCharsRegex: /[\\p{L}\\p{N}_]+/gu, // letters/numbers/underscore (Unicode)\n}\n\n/** Normalize to improve matching across punctuation/case/diacritics. */\nfunction normalizeForMatch(s: string): string {\n // 1) Lowercase\n // 2) Unicode NFKD + strip diacritics (combining marks)\n // 3) Trim\n return s\n .toLowerCase()\n .normalize('NFKD')\n .replace(/\\p{M}+/gu, '') // remove diacritic marks\n .trim()\n}\n\n/** Tokenize a string into normalized word tokens. */\nexport function tokenize(text: string, allowedWordChars = DEFAULTS.allowedWordCharsRegex): string[] {\n const norm = normalizeForMatch(text)\n // Extract “words” per allowedWordChars; fallback to split on whitespace\n const matches = norm.match(allowedWordChars)\n if (!matches) return []\n return matches\n}\n\n/** Build a SourceContext once for a given document. */\nexport function preprocessSource(\n sourceText: string,\n opts?: Pick<MatchOptions, 'allowedWordCharsRegex'>,\n): SourceContext {\n const tokens = tokenize(sourceText, opts?.allowedWordCharsRegex ?? DEFAULTS.allowedWordCharsRegex)\n return { tokens }\n}\n\n/** Prepare a term: tokenize, remove stopwords, optionally drop very short tokens. */\nfunction prepareTermTokens(term: string, opts: Required<MatchOptions>): string[] {\n const raw = tokenize(term, opts.allowedWordCharsRegex)\n const filtered = raw.filter((t) => !opts.stopwords.includes(t))\n if (!opts.dropVeryShortTermTokens) return filtered\n return filtered.filter((t) => t.length > 2)\n}\n\n/** Build a frequency map for tokens. */\nfunction freqMap(tokens: string[]): Map<string, number> {\n const m = new Map<string, number>()\n for (const t of tokens) m.set(t, (m.get(t) ?? 0) + 1)\n return m\n}\n\n/**\n * Core matcher: bag-of-words inside a sliding window.\n * We only count tokens that appear in the (prepared) term.\n *\n * This function can be used for matching glossary terms, protected phrases,\n * and other text patterns within documents.\n */\nexport function matchTermInDocument(term: string, source: SourceContext, options?: MatchOptions): Match {\n const opts: Required<MatchOptions> = { ...DEFAULTS, ...(options ?? {}) }\n\n const termTokens = prepareTermTokens(term, opts)\n if (termTokens.length === 0) {\n return { matched: false, term, reason: 'No substantive tokens after filtering.' }\n }\n\n const termFreq = freqMap(termTokens)\n const requiredCount = Math.ceil(termTokens.length * opts.minTokenMatchRatio)\n const windowSize = Math.max(1, termTokens.length + opts.windowSlack)\n\n // Set of tokens we care about for this term (to keep window counts small).\n const interesting = new Set(termFreq.keys())\n\n // Sliding window counts ONLY for tokens that are in termFreq.\n const windowCounts = new Map<string, number>()\n let satisfiedCount = 0 // sum over tokens: min(windowCounts[token], termFreq[token])\n\n // Helper to add a token to the window (if interesting), updating satisfiedCount.\n const addToken = (tok: string) => {\n if (!interesting.has(tok)) return\n const before = windowCounts.get(tok) ?? 0\n const after = before + 1\n windowCounts.set(tok, after)\n // biome-ignore lint/style/noNonNullAssertion: this is safe\n const cap = termFreq.get(tok)!\n // Only increases satisfiedCount until we reach the term-required cap for this token.\n if (before < cap) satisfiedCount += 1\n }\n\n // Helper to remove a token from the window, updating satisfiedCount.\n const removeToken = (tok: string) => {\n if (!interesting.has(tok)) return\n const before = windowCounts.get(tok) ?? 0\n if (before === 0) return\n const after = before - 1\n windowCounts.set(tok, after)\n\n // biome-ignore lint/style/noNonNullAssertion: this is safe\n const cap = termFreq.get(tok)!\n // If we were contributing to satisfiedCount (i.e., before <= cap), decrement when we drop below cap.\n if (before <= cap) satisfiedCount -= 1\n }\n\n const { tokens } = source\n // Prime the first window\n for (let i = 0; i < Math.min(windowSize, tokens.length); i++) addToken(tokens[i])\n\n if (satisfiedCount >= requiredCount) {\n return {\n matched: true,\n term,\n windowStart: 0,\n windowEnd: Math.min(windowSize, tokens.length) - 1,\n matchedRatio: satisfiedCount / termTokens.length,\n }\n }\n\n // Slide the window across the source\n for (let start = 1; start + windowSize <= tokens.length; start++) {\n const prev = tokens[start - 1]\n const next = tokens[start + windowSize - 1]\n removeToken(prev)\n addToken(next)\n\n if (satisfiedCount >= requiredCount) {\n return {\n matched: true,\n term,\n windowStart: start,\n windowEnd: start + windowSize - 1,\n matchedRatio: satisfiedCount / termTokens.length,\n }\n }\n }\n\n return { matched: false, term, reason: 'No window satisfied the token threshold.' }\n}\n\n/**\n * Batch check: evaluate many terms against the same preprocessed source.\n * Returns match results in the same order as input terms.\n * Can be used for glossary terms, protected phrases, or other text patterns.\n */\nexport function matchAllTermsInDocument(terms: string[], source: SourceContext, options?: MatchOptions): Match[] {\n return terms.map((t) => matchTermInDocument(t, source, options))\n}\n\n/**\n * @deprecated Use matchTermInDocument instead\n */\nexport const matchGlossaryTerm = matchTermInDocument\n\n/**\n * @deprecated Use matchAllTermsInDocument instead\n */\nexport const matchAllGlossaryTerms = matchAllTermsInDocument\n\n/* =========================\n Example usage (pseudo):\n =========================\n\nconst sourceText = `\n You can navigate using the digital map, or rely on landmarks.\n Turn-by-turn directions are available.\n`;\n\nconst source = preprocessSource(sourceText);\n\nconst terms = [\n \"navigate using a map\",\n \"turn by turn directions\",\n \"offline routing\",\n];\n\nconst results = matchAllTermsInDocument(terms, source, {\n windowSlack: 3,\n minTokenMatchRatio: 1.0, // require all term tokens present (after filtering)\n stopwords: [...DEFAULTS.stopwords, \"a\"], // extend if desired\n});\n\nconsole.log(results);\n\n*/\n","import type { SanityClient } from 'sanity'\nimport type { TranslatableDocument, TranslationGlossaryTermDocument, TranslationSettingsDocument } from '../../types'\nimport { documentToPlainText } from '../document/documentToPlainText'\nimport { matchTermInDocument, preprocessSource } from './matchTermInDocument'\n\ninterface FetchTranslationPromptsOptions {\n client: SanityClient\n sourceLanguage?: string\n sourceDocument?: TranslatableDocument\n targetLanguages: Array<string>\n}\n\nexport interface FetchTranslationPromptsResult {\n globalPrompt?: string\n protectedPhrases?: Array<string>\n localeInstructions: Record<\n string,\n {\n prompt?: string\n glossaryTerms?: string\n }\n >\n}\n\nexport const fetchTranslationPrompts = async ({\n client,\n sourceLanguage,\n sourceDocument,\n targetLanguages,\n}: FetchTranslationPromptsOptions): Promise<FetchTranslationPromptsResult> => {\n const sourceDocumentString = documentToPlainText(sourceDocument)\n\n const translationSettings = await client.fetch<TranslationSettingsDocument | null>(\n `*[_id == \"translation-settings\"][0] {\n globalPrompt,\n localePrompts[locale in $targetLanguages],\n protectedPhrases\n }`,\n { targetLanguages },\n )\n\n // Fetch all translation glossary terms that have a translation in both the source and target languages\n const translationGlossaryTerms =\n (await client.fetch<Array<NonNullable<TranslationGlossaryTermDocument['translations']>> | null>(\n `*[\n _type == \"translationGlossaryTerm\"\n && count(translations[_key in $targetLanguages]) > 0\n && count(translations[_key == $sourceLanguage]) > 0\n ].translations`,\n { targetLanguages, sourceLanguage },\n )) || []\n\n const glossaryTermsByLocale: Record<string, Array<{ from: string; to: string }>> = {}\n\n for (const targetLanguage of targetLanguages) {\n const relevantTerms = translationGlossaryTerms.filter((termTranslations) =>\n termTranslations.some((translation) => translation._key === targetLanguage),\n )\n\n interface GlossaryTermMapping {\n from: string\n to: string\n }\n\n let relevantTranslations = relevantTerms\n .map((termTranslations): GlossaryTermMapping | null => {\n const from = termTranslations.find((translation) => translation._key === sourceLanguage)?.value\n const to = termTranslations.find((translation) => translation._key === targetLanguage)?.value\n if (!from || !to) {\n return null\n }\n return { from, to }\n })\n .filter((item): item is GlossaryTermMapping => item !== null)\n\n // Filter to only include terms that appear in the source document\n if (sourceDocument) {\n relevantTranslations = relevantTranslations.filter(({ from }) =>\n glossaryTermAppearsInDocument(from, sourceDocumentString),\n )\n }\n\n glossaryTermsByLocale[targetLanguage] = relevantTranslations\n }\n\n // Filter protected phrases to only include those that appear in the source document\n let filteredProtectedPhrases = translationSettings?.protectedPhrases\n if (sourceDocument && filteredProtectedPhrases) {\n filteredProtectedPhrases = filteredProtectedPhrases.filter((phrase) =>\n protectedPhraseAppearsInDocument(phrase, sourceDocumentString),\n )\n }\n\n return {\n globalPrompt: translationSettings?.globalPrompt,\n protectedPhrases: filteredProtectedPhrases,\n localeInstructions: Object.fromEntries(\n targetLanguages.map((locale) => [\n locale,\n {\n prompt: translationSettings?.localePrompts?.find((lp) => lp._key === locale)?.value,\n glossaryTerms: formatGlossaryTermInstruction(glossaryTermsByLocale[locale]),\n },\n ]),\n ),\n }\n}\n\n/**\n * Checks if a glossary term appears anywhere in the document content using fuzzy matching.\n * Uses order-agnostic bag-of-words matching.\n */\nconst glossaryTermAppearsInDocument = (term: string, documentString: string): boolean => {\n // Handle colon-delimited terms by checking the base term (before the colon)\n const baseTerm = term.split(':')[0].trim()\n\n // Preprocess the document string once for efficient matching\n const source = preprocessSource(documentString)\n\n // Match the term against the document with default options\n // (windowSlack: 10, minTokenMatchRatio: 1.0, stopwords included)\n const result = matchTermInDocument(baseTerm, source, {\n windowSlack: 10,\n })\n\n return result.matched\n}\n\n/**\n * Checks if a protected phrase appears anywhere in the document content using fuzzy matching.\n * Uses order-agnostic bag-of-words matching.\n */\nconst protectedPhraseAppearsInDocument = (phrase: string, documentString: string): boolean => {\n // Preprocess the document string once for efficient matching\n const source = preprocessSource(documentString)\n\n // Match the phrase against the document with the same options as glossary terms\n const result = matchTermInDocument(phrase, source, {\n windowSlack: 10,\n })\n\n return result.matched\n}\n\nconst formatGlossaryTermInstruction = (terms: Array<{ from: string; to: string }> | undefined) => {\n if (!terms || terms.length === 0) {\n return undefined\n }\n\n const instructions = [\n '## Glossary Terms',\n 'Use the translations below whenever the matching source term appears. Treat these mappings as required terminology.',\n 'Guidelines:',\n '- Apply glossary terms when the meaning matches, even if there are slight variations in wording, punctuation, or word order.',\n '- Preserve surrounding punctuation and keep the sentence natural.',\n '- Match the source casing when possible; only adjust when required for correct grammar.',\n '- If the source term meaning is absent, do not introduce a translation from this list.',\n '- When a term includes a colon, translate the text before the colon for general use; use the text after the colon when the feminine form is required.',\n '- Do not remove important words or phrases from the source text when they are essential to the meaning.',\n '- Never add punctuation or symbols that are not present in the source text.',\n '- Never apply glossary terms in code examples.',\n ]\n\n terms.forEach(({ from, to }) => {\n instructions.push(\n `When text conveys the same meaning as \"${from}\", translate it as \"${to}\" unless the term is used in a code example.`,\n )\n })\n\n return instructions.join('\\n')\n}\n","export type RecursiveSearchable =\n | null\n | undefined\n | string\n | number\n | boolean\n | RecursiveSearchable[]\n | { [key: string]: RecursiveSearchable }\n\nexport type FindKeyValueResult<T = unknown> = {\n value: T\n path: Array<string | number>\n}\n\n/**\n * Recursively searches through an object/array structure to find all values\n * associated with a specific key name, along with their paths.\n *\n * @param obj - The object or array to search through\n * @param targetKey - The key name to search for\n * @returns An array of objects containing the value and path for each occurrence of the target key\n */\nexport const findKeyValues = <T = unknown>(obj: RecursiveSearchable, targetKey: string): FindKeyValueResult<T>[] => {\n const results: FindKeyValueResult<T>[] = []\n\n const recursiveSearch = (current: RecursiveSearchable, currentPath: Array<string | number>): void => {\n // Handle null/undefined\n if (current === null || current === undefined) {\n return\n }\n\n // If current item is an array, search each element\n if (Array.isArray(current)) {\n current.forEach((item, index) => {\n recursiveSearch(item, [...currentPath, index])\n })\n return\n }\n\n // If current item is an object\n if (typeof current === 'object') {\n for (const [key, value] of Object.entries(current)) {\n const newPath = [...currentPath, key]\n\n // If we found the target key, add its value and path to our results\n if (key === targetKey) {\n results.push({\n value: value as T,\n path: newPath,\n })\n }\n\n // Continue searching recursively\n recursiveSearch(value, newPath)\n }\n }\n }\n\n recursiveSearch(obj, [])\n return results\n}\n","import { pathToString, type SanityClient } from 'sanity'\nimport { TRANSLATION_METADATA_TYPE } from '../../consts'\nimport type { PluginConfig } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { type FindKeyValueResult, findKeyValues } from '../common/findKeyValues'\nimport { undraftId } from '../document/undraftId'\n\ninterface LocalizeReferenceParams {\n client: SanityClient\n documentId: string\n languageFieldName: PluginConfig['languageFieldName']\n}\n\n/**\n * Localizes all references in a translated document to point to the appropriate\n * localized versions of referenced documents.\n *\n * This function:\n * 1. Fetches the document and extracts its target language\n * 2. Finds all `_ref` fields within the document\n * 3. Batch fetches translation IDs for all references\n * 4. Updates references to point to localized versions\n * 5. Sets weak references with strengthenOnPublish behavior\n *\n * @param params - Configuration for localizing references\n * @param params.client - Sanity client instance\n * @param params.documentId - ID of the document whose references should be localized\n * @param params.languageFieldName - Name of the field containing the language identifier\n *\n * @example\n * ```typescript\n * await localizeReferences({\n * client,\n * documentId: 'post-es',\n * languageFieldName: 'language'\n * })\n * // Updates all references in post-es to point to Spanish versions\n * ```\n */\nexport const localizeReferences = async ({ client, documentId, languageFieldName }: LocalizeReferenceParams) => {\n try {\n const doc = await client.fetch('*[_id == $documentId][0]', { documentId })\n\n if (!doc) {\n devLog.error('Document not found', new Error('Document not found'), {\n documentId,\n component: 'localizeReferences',\n })\n return\n }\n\n const targetLanguage = doc[languageFieldName]\n\n if (!targetLanguage) {\n devLog.error('No value found for language field', new Error('Missing language field'), {\n documentId,\n languageFieldName,\n component: 'localizeReferences',\n })\n return\n }\n\n const refs = findKeyValues<string>(doc, '_ref')\n\n if (!refs?.length) {\n return\n }\n\n await updateReferences(documentId, client, refs, targetLanguage)\n } catch (error) {\n devLog.error('Error localizing references', error instanceof Error ? error : new Error(String(error)), {\n documentId,\n component: 'localizeReferences',\n })\n }\n}\n\n/**\n * Updates document references to their localized versions in a single batch operation.\n *\n * This function performs efficient batch processing:\n * 1. Batch fetches all translation IDs in one query (avoids N+1 problem)\n * 2. Builds a translation map for quick lookup\n * 3. Applies all patches in a single commit\n *\n * References are set as weak with `_strengthenOnPublish` to ensure they become\n * strong references when the document is published.\n *\n * @param documentId - ID of the document to update\n * @param client - Sanity client instance\n * @param refs - Array of reference results from findKeyValues\n * @param targetLanguage - Target language to find localized references for\n *\n * @example\n * ```typescript\n * const refs = findKeyValues(document, '_ref')\n * await updateReferences('doc-es', client, refs, 'es')\n * // All references in doc-es now point to Spanish versions\n * ```\n */\nexport const updateReferences = async (\n documentId: string,\n client: SanityClient,\n refs: Array<FindKeyValueResult<string>>,\n targetLanguage: string,\n) => {\n if (!refs?.length) {\n return []\n }\n\n // Step 1: Batch fetch all reference translations\n const refIds = refs.map((r) => undraftId(r.value))\n const translations = await client.fetch<Array<{ sourceId: string; targetId: string }>>(\n `*[_type == $translationMetadataSchemaType && count(translations[value._ref in $refIds]) > 0]\n .translations[value._ref in $refIds]{\n \"sourceId\": value._ref,\n \"targetId\": ^.translations[_key == $targetLanguage][0].value._ref\n }[defined(targetId)]`,\n {\n translationMetadataSchemaType: TRANSLATION_METADATA_TYPE,\n refIds,\n targetLanguage,\n },\n )\n\n // Step 2: Build translation map\n const translationMap = new Map<string, string>()\n for (const t of translations) {\n if (t.sourceId && t.targetId) {\n translationMap.set(t.sourceId, t.targetId)\n }\n }\n\n // Step 3: Apply all patches in a single commit\n const patch = client.patch(documentId)\n let hasChanges = false\n\n for (const ref of refs) {\n const sourceId = undraftId(ref.value)\n const newId = translationMap.get(sourceId)\n\n if (!newId) {\n // No matching translation found for the reference. The document might not have been translated yet\n // or might not be of a translatable schema type.\n continue\n }\n\n patch.set({ [pathToString(ref.path)]: undraftId(newId) })\n patch.set({ [`${pathToString(ref.path.slice(0, -1))}._weak`]: true })\n patch.set({\n [`${pathToString(ref.path.slice(0, -1))}._strengthenOnPublish`]: {\n _type: 'reference',\n _weak: false,\n },\n })\n hasChanges = true\n }\n\n if (hasChanges) {\n await patch.commit()\n devLog.info('Updated references in batch', {\n documentId,\n count: refs.length,\n translatedCount: translationMap.size,\n component: 'updateReferences',\n })\n }\n}\n","import type { Dispatch } from 'react'\nimport type { SetTranslationJobStatusAction, TranslationAction } from '../../types'\n\nexport const setupTranslationJobStatuses = (\n dispatch: Dispatch<TranslationAction>,\n sourceDocuments: Array<{\n _id: string\n _type: string\n title?: string\n }>,\n targetLanguages: Array<string>,\n) => {\n for (const sourceDocument of sourceDocuments) {\n for (const lang of targetLanguages) {\n const payload: SetTranslationJobStatusAction['payload'] = {\n _id: sourceDocument._id,\n _type: sourceDocument._type,\n language: lang,\n status: 'queued',\n title: sourceDocument.title,\n }\n\n dispatch({\n type: 'SET_TRANSLATION_JOB_STATUS',\n payload,\n })\n }\n }\n}\n","import type { SanityClient } from 'sanity'\nimport { TRANSLATION_METADATA_TYPE } from '../../consts'\nimport type { TranslatableDocument, TranslationMetadataDocument } from '../../types'\nimport { undraftId } from '../document/undraftId'\n\ninterface PrepareTranslationMetadataParams {\n client: SanityClient\n sourceDocument: TranslatableDocument\n sourceLanguage: string\n}\n\nexport const getTranslationMetadata = async ({\n client,\n sourceDocument,\n sourceLanguage,\n}: PrepareTranslationMetadataParams): Promise<TranslationMetadataDocument> => {\n try {\n const existingDoc = await client.fetch<TranslationMetadataDocument | null>(\n '*[_type == $tmType && references($sourceDocumentId)][0]',\n {\n tmType: TRANSLATION_METADATA_TYPE,\n sourceDocumentId: undraftId(sourceDocument._id),\n },\n )\n\n if (existingDoc) {\n return existingDoc\n }\n\n const newDoc = (await client.create({\n _type: TRANSLATION_METADATA_TYPE,\n translations: [\n {\n _key: sourceLanguage,\n _type: 'internationalizedArrayReferenceValue',\n value: {\n _type: 'reference',\n _ref: undraftId(sourceDocument._id),\n _weak: true,\n _strengthenOnPublish: {\n _type: sourceDocument._type,\n _weak: false,\n },\n },\n },\n ],\n translationHistory: [],\n })) as TranslationMetadataDocument\n\n return newDoc\n } catch (error) {\n console.error(sourceDocument._id, 'Error creating translation metadata:', error)\n throw error\n }\n}\n","import type { SanityClient, SanityDocument } from 'sanity'\nimport type { TranslationMetadataDocument } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { undraftId } from '../document/undraftId'\n\ninterface UpdateTranslationMetadataParams {\n client: SanityClient\n sourceDocument: SanityDocument\n sourceLanguage: string\n newDocument: SanityDocument\n targetLanguage: string\n translationMetadata: TranslationMetadataDocument\n isNewTranslation: boolean\n}\n\nexport const updateTranslationMetadata = async ({\n client,\n targetLanguage,\n sourceDocument,\n sourceLanguage,\n newDocument,\n translationMetadata,\n isNewTranslation,\n}: UpdateTranslationMetadataParams) => {\n try {\n const refToNewTranslation: TranslationMetadataDocument['translations'][number] = {\n _key: targetLanguage,\n _type: 'internationalizedArrayReferenceValue',\n value: {\n _type: 'reference',\n _ref: undraftId(newDocument._id),\n _weak: true,\n _strengthenOnPublish: {\n _type: 'reference',\n _weak: false,\n },\n },\n }\n\n const newTranslationHistory: TranslationMetadataDocument['translationHistory'][number] = {\n _key: targetLanguage,\n language: targetLanguage,\n sourceId: undraftId(sourceDocument._id),\n sourceRev: sourceDocument._rev,\n sourceLanguage,\n sourceSnapshot: JSON.stringify(sourceDocument),\n targetId: newDocument._id,\n targetRev: newDocument._rev,\n targetSnapshot: JSON.stringify(newDocument),\n }\n\n const translationPatch = client\n .patch(translationMetadata._id)\n // If there is no value set for `translations`, set it to an empty array\n .setIfMissing({ translations: [] })\n\n if (isNewTranslation) {\n await translationPatch.insert('after', 'translations[-1]', [refToNewTranslation]).commit()\n } else {\n await translationPatch\n .insert('replace', `translations[_key == \"${targetLanguage}\"]`, [refToNewTranslation])\n .commit()\n }\n\n const historyPatch = client.patch(translationMetadata._id).setIfMissing({ translationHistory: [] })\n const translationHistoryExists = !!translationMetadata.translationHistory?.some(\n (history) => history.language === targetLanguage,\n )\n\n if (!translationHistoryExists) {\n await historyPatch.insert('after', 'translationHistory[-1]', [newTranslationHistory]).commit()\n } else {\n await historyPatch\n .insert('replace', `translationHistory[_key == \"${targetLanguage}\"]`, [newTranslationHistory])\n .commit()\n }\n } catch (error) {\n devLog.error('Error updating translation metadata', error instanceof Error ? error : new Error(String(error)), {\n sourceDocumentId: sourceDocument._id,\n targetLanguage,\n newDocumentId: newDocument._id,\n component: 'updateTranslationMetadata',\n })\n throw error\n }\n}\n","import defaults from 'lodash.defaults'\nimport type { Dispatch } from 'react'\nimport type { SetTranslationJobStatusAction, TranslationAction, TranslationJob } from '../../types'\n\nexport const setJobStatus = (\n dispatch: Dispatch<TranslationAction>,\n _id: string,\n language: string,\n status: TranslationJob['status'],\n payload?: Partial<SetTranslationJobStatusAction['payload']>,\n) => {\n dispatch({\n type: 'SET_TRANSLATION_JOB_STATUS',\n payload: defaults(payload, {\n _id,\n language,\n status,\n }),\n })\n}\n","import type { PluginConfig, TranslationMetadataDocument } from '../../types'\n\nexport const getExistingTranslationId = (\n translationMetadata: TranslationMetadataDocument,\n targetLanguage: PluginConfig['supportedLanguages'][number]['id'],\n): string | null => {\n if (!translationMetadata?.translations?.length) {\n return null\n }\n\n const existingTranslation = translationMetadata.translations.find(\n (translation) => translation._key === targetLanguage,\n )\n\n return existingTranslation?.value._ref || null\n}\n","import type { PluginConfig, TranslationMetadataDocument } from '../../types'\nimport { getExistingTranslationId } from '../metadata/getExistingTranslationId'\n\ninterface DetermineTranslationTargetParams {\n translationMetadata: TranslationMetadataDocument\n targetLanguage: PluginConfig['supportedLanguages'][number]['id']\n}\n\n/**\n * Determines the appropriate operation and ID for a translation based on whether\n * a translation already exists for the target language.\n *\n * This function checks the translation metadata to see if a document already exists\n * in the target language. If it exists, returns a 'createOrReplace' operation with\n * the existing ID to update it. Otherwise, returns a 'create' operation for a new document.\n *\n * @param params - Configuration for determining translation target\n * @param params.translationMetadata - Metadata document containing translation references\n * @param params.targetLanguage - Target language ID to check for existing translation\n * @returns Object containing operation type and optional _id\n *\n * @example\n * ```typescript\n * // For existing translation\n * const result = determineTranslationTarget({\n * translationMetadata: { translations: [{ _key: 'es', value: { _ref: 'doc-es' } }] },\n * targetLanguage: 'es'\n * })\n * // Returns: { operation: 'createOrReplace', _id: 'doc-es' }\n *\n * // For new translation\n * const result = determineTranslationTarget({\n * translationMetadata: { translations: [] },\n * targetLanguage: 'fr'\n * })\n * // Returns: { operation: 'create' }\n * ```\n */\nexport const determineTranslationTarget = ({\n translationMetadata,\n targetLanguage,\n}: DetermineTranslationTargetParams) => {\n const existingTranslationId = getExistingTranslationId(translationMetadata, targetLanguage)\n\n return existingTranslationId\n ? {\n // Update existing translation\n operation: 'createOrReplace' as const,\n _id: existingTranslationId,\n }\n : {\n // Create new translation\n operation: 'create' as const,\n }\n}\n","import type { TranslatableDocument } from '../../types'\n\nexport interface DocumentAnalysis {\n hasCodeBlocks: boolean\n hasInlineCode: boolean\n codeBlockCount: number\n inlineCodeCount: number\n hasMarkdown: boolean\n hasPortableText: boolean\n complexity: 'simple' | 'moderate' | 'complex'\n estimatedFieldCount: number\n}\n\n/**\n * Analyzes a document's content to inform translation prompt assembly.\n * Detects code blocks, inline code, content types, and complexity.\n */\nexport const analyzeDocumentContent = (document: TranslatableDocument): DocumentAnalysis => {\n let hasCodeBlocks = false\n let hasInlineCode = false\n let codeBlockCount = 0\n let inlineCodeCount = 0\n const hasMarkdown = false\n let hasPortableText = false\n let estimatedFieldCount = 0\n\n const traverse = (value: unknown, depth = 0): void => {\n if (value === null || value === undefined) {\n return\n }\n\n // Count fields for complexity estimation\n if (depth > 0 && typeof value === 'object') {\n estimatedFieldCount++\n }\n\n // Check for Portable Text blocks and code\n if (Array.isArray(value)) {\n value.forEach((item) => {\n // Detect Portable Text blocks with _type 'code' (code blocks)\n if (item && typeof item === 'object' && '_type' in item && item._type === 'code') {\n hasCodeBlocks = true\n codeBlockCount++\n hasPortableText = true\n }\n\n // Detect Portable Text blocks with spans that have 'code' mark\n if (\n item &&\n typeof item === 'object' &&\n '_type' in item &&\n item._type === 'block' &&\n 'children' in item &&\n Array.isArray(item.children)\n ) {\n hasPortableText = true\n item.children.forEach((child: unknown) => {\n if (\n child &&\n typeof child === 'object' &&\n 'marks' in child &&\n Array.isArray(child.marks) &&\n child.marks.includes('code')\n ) {\n hasInlineCode = true\n inlineCodeCount++\n }\n })\n }\n\n traverse(item, depth + 1)\n })\n } else if (typeof value === 'object') {\n Object.values(value).forEach((val) => traverse(val, depth + 1))\n }\n }\n\n traverse(document)\n\n // Determine complexity based on field count and structure depth\n let complexity: 'simple' | 'moderate' | 'complex' = 'simple'\n if (estimatedFieldCount > 50) {\n complexity = 'complex'\n } else if (estimatedFieldCount > 20) {\n complexity = 'moderate'\n }\n\n return {\n hasCodeBlocks,\n hasInlineCode,\n codeBlockCount,\n inlineCodeCount,\n hasMarkdown,\n hasPortableText,\n complexity,\n estimatedFieldCount,\n }\n}\n","import type { TranslatableDocument } from '../../types'\n\ninterface AssembleLanguageSpecsParams {\n sourceDocument: TranslatableDocument\n languageFieldName: string\n targetLanguage: string\n}\n\n/**\n * Assembles language specification objects for translation operations.\n * Extracts the source language from the document and formats both source and target\n * languages into objects suitable for Sanity Agent Action translation calls.\n *\n * @param params - Configuration for assembling language specs\n * @param params.sourceDocument - The document being translated (contains source language ID)\n * @param params.languageFieldName - Name of the field containing the language identifier\n * @param params.targetLanguage - Target language ID to translate to\n * @returns Object containing fromLanguage and toLanguage specifications\n * @throws Error if source language ID is missing from the document\n *\n * @example\n * ```typescript\n * const specs = assembleLanguageSpecs({\n * sourceDocument: { language: 'en', ... },\n * languageFieldName: 'language',\n * targetLanguage: 'es'\n * })\n * // Returns: { fromLanguage: { id: 'en', title: 'en' }, toLanguage: { id: 'es', title: 'es' } }\n * ```\n */\nexport const assembleLanguageSpecs = ({\n sourceDocument,\n languageFieldName,\n targetLanguage,\n}: AssembleLanguageSpecsParams): {\n toLanguage: { id: string; title: string }\n fromLanguage: { id: string; title: string }\n} => {\n const sourceLanugageId = sourceDocument[languageFieldName] as string | undefined\n\n if (!sourceLanugageId) {\n throw new Error('[assembleLanguages] Source language ID is missing')\n }\n\n return {\n fromLanguage: {\n id: sourceLanugageId,\n title: sourceLanugageId,\n },\n toLanguage: {\n id: targetLanguage,\n title: targetLanguage,\n },\n }\n}\n","export interface GPT5Request {\n messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>\n model?: string\n temperature?: number\n reasoningEffort?: 'minimal' | 'low' | 'medium' | 'high'\n verbosity?: 'low' | 'medium' | 'high'\n onReasoningChunk?: (chunk: string) => void\n}\n\nexport interface GPT5Response {\n content: string\n tokensUsed: {\n prompt: number\n completion: number\n reasoning: number\n total: number\n }\n cost: number\n reasoningSummary?: string\n}\n\n/**\n * Calls the GPT-5 API with the given request parameters.\n * Handles new GPT-5 specific parameters like reasoning_effort and verbosity.\n * Supports streaming to provide real-time reasoning updates.\n */\nexport const callGPT5 = async (apiKey: string, request: GPT5Request): Promise<GPT5Response> => {\n const model = request.model || 'gpt-5'\n const isGPT5 = model === 'gpt-5'\n const useStreaming = !!request.onReasoningChunk\n\n // GPT-5 only supports default temperature (1) - custom values cause errors\n // GPT-4 models support custom temperature values\n const requestBody: Record<string, unknown> = {\n model,\n messages: request.messages,\n stream: useStreaming,\n }\n\n // Only include temperature for non-GPT-5 models\n if (!isGPT5) {\n requestBody.temperature = request.temperature ?? 0.3\n }\n\n // GPT-5 specific parameters\n if (isGPT5) {\n requestBody.reasoning_effort = request.reasoningEffort || 'medium'\n requestBody.verbosity = request.verbosity || 'medium'\n }\n\n const response = await fetch('https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(requestBody),\n })\n\n if (!response.ok) {\n const error = await response.json()\n throw new Error(`GPT-5 API error: ${error.error?.message || 'Unknown error'}`)\n }\n\n // Handle streaming response\n if (useStreaming && response.body && request.onReasoningChunk) {\n return await handleStreamingResponse(response, request.onReasoningChunk, model)\n }\n\n // Handle non-streaming response\n const result = await response.json()\n\n // GPT-5 returns detailed token usage including reasoning tokens\n const usage = result.usage || {}\n const tokensUsed = {\n prompt: usage.prompt_tokens || 0,\n completion: usage.completion_tokens || 0,\n reasoning: usage.reasoning_tokens || 0, // New in GPT-5\n total: usage.total_tokens || 0,\n }\n\n // Calculate cost\n const cost = calculateGPT5Cost(tokensUsed, request.model || 'gpt-5')\n\n // Extract reasoning summary if present\n const messageContent = result.choices[0].message.content\n let reasoningSummary: string | undefined\n let actualContent = messageContent\n\n // Check if response includes reasoning in a structured format\n // GPT-5 may include reasoning when verbosity is set to high\n const reasoningMatch = messageContent.match(/^<reasoning>([\\s\\S]*?)<\\/reasoning>\\n\\n([\\s\\S]*)$/i)\n if (reasoningMatch) {\n reasoningSummary = reasoningMatch[1].trim()\n actualContent = reasoningMatch[2]\n }\n\n return {\n content: actualContent,\n tokensUsed,\n cost,\n reasoningSummary,\n }\n}\n\n/**\n * Handles streaming response from GPT-5, extracting reasoning chunks and content.\n */\nconst handleStreamingResponse = async (\n response: Response,\n onReasoningChunk: (chunk: string) => void,\n model: string,\n): Promise<GPT5Response> => {\n if (!response.body) {\n throw new Error('Response body is null')\n }\n\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n\n let fullContent = ''\n let inReasoning = false\n let reasoningBuffer = ''\n let contentBuffer = ''\n let tokensUsed = { prompt: 0, completion: 0, reasoning: 0, total: 0 }\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n const chunk = decoder.decode(value, { stream: true })\n const lines = chunk.split('\\n').filter((line) => line.trim() !== '')\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6)\n if (data === '[DONE]') continue\n\n try {\n const parsed = JSON.parse(data)\n\n // Extract token usage if present\n if (parsed.usage) {\n tokensUsed = {\n prompt: parsed.usage.prompt_tokens || 0,\n completion: parsed.usage.completion_tokens || 0,\n reasoning: parsed.usage.reasoning_tokens || 0,\n total: parsed.usage.total_tokens || 0,\n }\n }\n\n const delta = parsed.choices?.[0]?.delta?.content\n if (delta) {\n fullContent += delta\n\n // Check for reasoning tags\n if (delta.includes('<reasoning>')) {\n inReasoning = true\n const parts = delta.split('<reasoning>')\n reasoningBuffer += parts[1] || ''\n } else if (delta.includes('</reasoning>')) {\n inReasoning = false\n const parts = delta.split('</reasoning>')\n reasoningBuffer += parts[0] || ''\n contentBuffer += parts[1] || ''\n } else if (inReasoning) {\n reasoningBuffer += delta\n // Send reasoning chunk to callback\n onReasoningChunk(reasoningBuffer)\n } else {\n contentBuffer += delta\n }\n }\n } catch (_e) {\n // Skip invalid JSON lines\n }\n }\n }\n }\n } finally {\n reader.releaseLock()\n }\n\n const cost = calculateGPT5Cost(tokensUsed, model)\n\n return {\n content: contentBuffer || fullContent,\n tokensUsed,\n cost,\n reasoningSummary: reasoningBuffer || undefined,\n }\n}\n\n/**\n * Calculates the cost of a GPT-5 API call based on token usage.\n * GPT-5 has separate pricing for input, output, and reasoning tokens.\n */\nconst calculateGPT5Cost = (\n tokens: { prompt: number; completion: number; reasoning: number },\n model: string,\n): number => {\n // GPT-5 pricing (as of August 2025)\n // Note: Reasoning tokens have their own pricing tier\n const pricing: Record<string, { input: number; output: number; reasoning: number }> = {\n 'gpt-5': {\n input: 0.002 / 1000, // $0.002 per 1k input tokens\n output: 0.01 / 1000, // $0.010 per 1k output tokens\n reasoning: 0.005 / 1000, // $0.005 per 1k reasoning tokens\n },\n 'gpt-4o': {\n input: 0.005 / 1000,\n output: 0.015 / 1000,\n reasoning: 0, // No reasoning tokens in GPT-4\n },\n 'gpt-4-turbo': {\n input: 0.01 / 1000,\n output: 0.03 / 1000,\n reasoning: 0,\n },\n 'gpt-4': {\n input: 0.03 / 1000,\n output: 0.06 / 1000,\n reasoning: 0,\n },\n }\n\n const modelPricing = pricing[model] || pricing['gpt-5']\n\n return (\n tokens.prompt * modelPricing.input +\n tokens.completion * modelPricing.output +\n tokens.reasoning * modelPricing.reasoning\n )\n}\n","import cloneDeep from 'lodash.clonedeep'\nimport get from 'lodash.get'\nimport set from 'lodash.set'\nimport type { SanityDocument } from 'sanity'\nimport { devLog } from '../common/devLog'\n\ninterface ParsedNode {\n type: 'field' | 'block' | 'span'\n name?: string\n text?: string\n attributes: Record<string, string>\n children: ParsedNode[]\n}\n\n/**\n * Deserializes a reviewed text format back into a Sanity document.\n * Only applies text changes - all structure and metadata preserved.\n */\nexport const deserializeDocument = (originalDoc: SanityDocument, reviewedText: string): SanityDocument => {\n // Parse the reviewed XML-like format\n const parsed = parseReviewFormat(reviewedText)\n\n // Deep clone original\n const result = cloneDeep(originalDoc)\n\n // Apply text changes from parsed structure\n applyTextChanges(result, parsed)\n\n return result\n}\n\nconst parseReviewFormat = (text: string): ParsedNode[] => {\n const root: ParsedNode[] = []\n const stack: ParsedNode[] = []\n\n // Parse XML-like format\n const lines = text.split('\\n')\n\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed) continue\n\n // Opening tag\n const openMatch = trimmed.match(/^<(field|block|span)([^>]*)>(.*)$/)\n if (openMatch) {\n const [, tagName, attrsStr, content] = openMatch\n const attributes = parseAttributes(attrsStr)\n\n const node: ParsedNode = {\n type: tagName as 'field' | 'block' | 'span',\n name: attributes.name,\n text: content ? unescapeXml(content) : undefined,\n attributes,\n children: [],\n }\n\n // Check if self-closing (has content on same line)\n const closingTag = `</${tagName}>`\n if (content.endsWith(closingTag.replace(/[<>]/g, ''))) {\n // Extract text before closing tag\n const textContent = content.substring(0, content.lastIndexOf('<'))\n node.text = unescapeXml(textContent)\n if (stack.length > 0) {\n const parent = stack[stack.length - 1]\n if (parent) parent.children.push(node)\n } else {\n root.push(node)\n }\n } else if (content.includes(closingTag)) {\n // Self-closing with immediate close\n node.text = unescapeXml(content.replace(closingTag, ''))\n if (stack.length > 0) {\n const parent = stack[stack.length - 1]\n if (parent) parent.children.push(node)\n } else {\n root.push(node)\n }\n } else {\n // Opening tag for container\n stack.push(node)\n }\n continue\n }\n\n // Closing tag\n const closeMatch = trimmed.match(/^<\\/(field|block|span)>$/)\n if (closeMatch && stack.length > 0) {\n const node = stack.pop()\n if (node) {\n if (stack.length > 0) {\n const parent = stack[stack.length - 1]\n if (parent) parent.children.push(node)\n } else {\n root.push(node)\n }\n }\n }\n }\n\n return root\n}\n\nconst parseAttributes = (attrStr: string): Record<string, string> => {\n const attrs: Record<string, string> = {}\n const attrRegex = /(\\w+)=\"([^\"]*)\"/g\n let match: RegExpExecArray | null = attrRegex.exec(attrStr)\n\n while (match !== null) {\n attrs[match[1]] = unescapeXml(match[2])\n match = attrRegex.exec(attrStr)\n }\n\n return attrs\n}\n\nconst unescapeXml = (str: string): string => {\n return str\n .replace(/&apos;/g, \"'\")\n .replace(/&quot;/g, '\"')\n .replace(/&gt;/g, '>')\n .replace(/&lt;/g, '<')\n .replace(/&amp;/g, '&')\n}\n\nconst applyTextChanges = (doc: Record<string, unknown>, parsedNodes: ParsedNode[]): void => {\n for (const node of parsedNodes) {\n if (node.type === 'field' && node.name) {\n const path = [node.name]\n applyFieldChanges(doc, path, node)\n }\n }\n}\n\nconst applyFieldChanges = (doc: Record<string, unknown>, path: (string | number)[], node: ParsedNode): void => {\n if (node.attributes.type === 'array') {\n // Handle array fields\n node.children.forEach((child, idx) => {\n applyFieldChanges(doc, [...path, idx], child)\n })\n return\n }\n\n if (node.attributes.type === 'object') {\n // Handle object fields\n for (const child of node.children) {\n if (child.name) {\n applyFieldChanges(doc, [...path, child.name], child)\n }\n }\n return\n }\n\n // Check for blocks (Portable Text)\n if (node.children.some((c) => c.type === 'block')) {\n const fieldValue = get(doc, path)\n if (Array.isArray(fieldValue)) {\n node.children.forEach((blockNode, idx) => {\n if (blockNode.type === 'block') {\n applyBlockChanges(fieldValue[idx], blockNode)\n }\n })\n }\n return\n }\n\n // Simple text field\n if (node.text !== undefined && !node.attributes.readonly) {\n set(doc, path, node.text)\n }\n}\n\nconst applyBlockChanges = (block: Record<string, unknown>, blockNode: ParsedNode): void => {\n // Verify metadata matches (safety check)\n if (blockNode.attributes._key && block._key !== blockNode.attributes._key) {\n devLog.warn('Block key mismatch, skipping', {\n expected: block._key,\n found: blockNode.attributes._key,\n component: 'deserializeDocument',\n })\n return\n }\n\n // Handle standard Portable Text blocks with span children\n const children = block.children\n if (Array.isArray(children) && blockNode.children.some((c) => c.type === 'span')) {\n blockNode.children.forEach((spanNode, idx) => {\n const childItem = (children as Record<string, unknown>[])[idx]\n if (spanNode.type === 'span' && childItem) {\n applySpanChanges(childItem, spanNode)\n }\n })\n } else if (blockNode.children.some((c) => c.type === 'field')) {\n // Handle custom block types with nested fields\n blockNode.children.forEach((fieldNode) => {\n if (fieldNode.type === 'field' && fieldNode.name) {\n applyBlockFieldChanges(block, fieldNode.name, fieldNode)\n }\n })\n }\n}\n\nconst applyBlockFieldChanges = (block: Record<string, unknown>, fieldName: string, fieldNode: ParsedNode): void => {\n const currentValue = block[fieldName]\n\n // Handle arrays\n if (fieldNode.attributes.type === 'array') {\n if (Array.isArray(currentValue)) {\n fieldNode.children.forEach((childNode, idx) => {\n if (childNode.type === 'block' && currentValue[idx]) {\n // Nested block in array\n applyBlockChanges(currentValue[idx] as Record<string, unknown>, childNode)\n } else if (childNode.type === 'field') {\n // Nested field in array\n applyBlockFieldChanges(currentValue[idx] as Record<string, unknown>, childNode.name || String(idx), childNode)\n }\n })\n }\n return\n }\n\n // Handle objects\n if (fieldNode.attributes.type === 'object') {\n if (currentValue && typeof currentValue === 'object') {\n fieldNode.children.forEach((childNode) => {\n if (childNode.name) {\n applyBlockFieldChanges(currentValue as Record<string, unknown>, childNode.name, childNode)\n }\n })\n }\n return\n }\n\n // Handle simple text fields\n if (fieldNode.text !== undefined && !fieldNode.attributes.readonly) {\n block[fieldName] = fieldNode.text\n }\n}\n\nconst applySpanChanges = (span: Record<string, unknown>, spanNode: ParsedNode): void => {\n // Verify metadata matches\n if (spanNode.attributes._key && span._key !== spanNode.attributes._key) {\n devLog.warn('Span key mismatch, skipping', {\n expected: span._key,\n found: spanNode.attributes._key,\n component: 'deserializeDocument',\n })\n return\n }\n\n // ONLY update the text property\n if (spanNode.text !== undefined) {\n span.text = spanNode.text\n }\n}\n","import type { SanityDocument } from 'sanity'\n\n/**\n * Converts Sanity documents to a structured text format for OpenAI review.\n * The format is deterministic and preserves all structure and metadata.\n * Only text content can be modified during review.\n */\nexport const serializeDocument = (doc: SanityDocument) => {\n const lines: string[] = []\n\n const serialize = (value: unknown, path: (string | number)[] = [], indent = 0): void => {\n const indentStr = ' '.repeat(indent)\n\n // Skip Sanity metadata at root\n if (path.length === 0) {\n if (isPlainObject(value)) {\n for (const [key, val] of Object.entries(value)) {\n if (!key.startsWith('_') && key !== 'language') {\n serialize(val, [key], indent)\n }\n }\n }\n return\n }\n\n const fieldName = path[path.length - 1]\n\n // Handle arrays\n if (Array.isArray(value)) {\n // Check if it's Portable Text\n const isPortableText = value.some((item) => item?._type === 'block')\n\n if (isPortableText) {\n lines.push(`${indentStr}<field name=\"${fieldName}\">`)\n value.forEach((block, idx) => {\n serializeBlock(block, [...path, idx], indent + 1, lines)\n })\n lines.push(`${indentStr}</field>`)\n } else {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"array\">`)\n value.forEach((item, idx) => {\n serialize(item, [...path, idx], indent + 1)\n })\n lines.push(`${indentStr}</field>`)\n }\n return\n }\n\n // Handle objects\n if (isPlainObject(value)) {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"object\">`)\n for (const [key, val] of Object.entries(value)) {\n if (!key.startsWith('_')) {\n serialize(val, [...path, key], indent + 1)\n }\n }\n lines.push(`${indentStr}</field>`)\n return\n }\n\n // Handle strings (actual translatable content)\n if (typeof value === 'string') {\n if (value.length > 0) {\n lines.push(`${indentStr}<field name=\"${fieldName}\">${escapeXml(value)}</field>`)\n }\n return\n }\n\n // Other primitives (numbers, booleans) - include but mark readonly\n if (typeof value === 'number' || typeof value === 'boolean') {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"${typeof value}\" readonly=\"true\">${value}</field>`)\n }\n }\n\n serialize(doc)\n return lines.join('\\n')\n}\n\nconst serializeBlock = (\n block: Record<string, unknown>,\n path: (string | number)[],\n indent: number,\n lines: string[],\n): void => {\n const indentStr = ' '.repeat(indent)\n\n // Store block metadata\n const metadata: Record<string, unknown> = {}\n if (block._type) metadata._type = block._type\n if (block._key) metadata._key = block._key\n if (block.style) metadata.style = block.style\n if (block.listItem) metadata.listItem = block.listItem\n if (block.level) metadata.level = block.level\n if (block.markDefs) metadata.markDefs = block.markDefs\n\n const metaStr = Object.entries(metadata)\n .map(([k, v]) => `${k}=\"${typeof v === 'string' ? escapeXml(v) : escapeXml(JSON.stringify(v))}\"`)\n .join(' ')\n\n lines.push(`${indentStr}<block ${metaStr}>`)\n\n // Handle standard Portable Text blocks with children (spans)\n if (Array.isArray(block.children) && block._type === 'block') {\n block.children.forEach((child: unknown, idx: number) => {\n serializeSpan(child as Record<string, unknown>, [...path, 'children', idx], indent + 1, lines)\n })\n } else {\n // Handle custom block types by recursively serializing all non-metadata fields\n for (const [key, value] of Object.entries(block)) {\n // Skip metadata fields we already captured\n if (key.startsWith('_') || key === 'style' || key === 'listItem' || key === 'level' || key === 'markDefs') {\n continue\n }\n\n // Recursively serialize the field content\n serializeBlockField(value, key, [...path, key], indent + 1, lines)\n }\n }\n\n lines.push(`${indentStr}</block>`)\n}\n\nconst serializeBlockField = (\n value: unknown,\n fieldName: string,\n path: (string | number)[],\n indent: number,\n lines: string[],\n): void => {\n const indentStr = ' '.repeat(indent)\n\n // Handle arrays\n if (Array.isArray(value)) {\n // Check if array contains blocks (nested Portable Text or custom blocks)\n const hasBlocks = value.some((item) => item?._type)\n\n if (hasBlocks) {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"array\">`)\n value.forEach((item, idx) => {\n if (item?._type === 'block' && item?.children) {\n // Standard Portable Text block\n serializeBlock(item, [...path, idx], indent + 1, lines)\n } else if (item?._type) {\n // Custom block type - treat as block\n serializeBlock(item, [...path, idx], indent + 1, lines)\n } else {\n // Non-block array item\n serializeBlockField(item, String(idx), [...path, idx], indent + 1, lines)\n }\n })\n lines.push(`${indentStr}</field>`)\n } else {\n // Simple array without blocks\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"array\">`)\n value.forEach((item, idx) => {\n serializeBlockField(item, String(idx), [...path, idx], indent + 1, lines)\n })\n lines.push(`${indentStr}</field>`)\n }\n return\n }\n\n // Handle objects\n if (isPlainObject(value)) {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"object\">`)\n for (const [key, val] of Object.entries(value)) {\n if (!key.startsWith('_')) {\n serializeBlockField(val, key, [...path, key], indent + 1, lines)\n }\n }\n lines.push(`${indentStr}</field>`)\n return\n }\n\n // Handle strings\n if (typeof value === 'string' && value.length > 0) {\n lines.push(`${indentStr}<field name=\"${fieldName}\">${escapeXml(value)}</field>`)\n return\n }\n\n // Other primitives\n if (typeof value === 'number' || typeof value === 'boolean') {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"${typeof value}\" readonly=\"true\">${value}</field>`)\n }\n}\n\nconst serializeSpan = (\n span: Record<string, unknown>,\n _path: (string | number)[],\n indent: number,\n lines: string[],\n): void => {\n const indentStr = ' '.repeat(indent)\n\n if (!span || typeof span !== 'object') return\n\n // Store span metadata\n const metadata: Record<string, unknown> = {}\n if (span._type) metadata._type = span._type\n if (span._key) metadata._key = span._key\n if (span.marks && Array.isArray(span.marks)) {\n metadata.marks = span.marks.join(',')\n }\n\n const metaStr = Object.entries(metadata)\n .map(([k, v]) => `${k}=\"${escapeXml(String(v))}\"`)\n .join(' ')\n\n const text = typeof span.text === 'string' ? span.text : ''\n\n if (metaStr) {\n lines.push(`${indentStr}<span ${metaStr}>${escapeXml(text)}</span>`)\n } else {\n lines.push(`${indentStr}<span>${escapeXml(text)}</span>`)\n }\n}\n\nconst escapeXml = (str: string): string => {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;')\n}\n\nconst isPlainObject = (val: unknown): val is Record<string, unknown> => {\n return typeof val === 'object' && val !== null && !Array.isArray(val)\n}\n","import type { SanityDocument } from 'sanity'\n\nexport interface ValidationError {\n path: string[]\n expected: unknown\n actual: unknown\n type: 'missing' | 'type_mismatch' | 'extra' | 'metadata_changed'\n}\n\nexport interface ValidationResult {\n valid: boolean\n errors: ValidationError[]\n}\n\n/**\n * Validates that document structure has not been changed.\n * Extremely strict validation - any structural change is an error.\n * Only text content in 'text' fields is allowed to change.\n */\nexport const validateStructureIntegrity = (original: SanityDocument, modified: SanityDocument): ValidationResult => {\n const errors: ValidationError[] = []\n\n // Phase 1: Check critical metadata\n const criticalFields = ['_id', '_type', '_rev', '_createdAt', '_updatedAt']\n for (const field of criticalFields) {\n if (original[field] !== modified[field]) {\n errors.push({\n path: [field],\n expected: original[field],\n actual: modified[field],\n type: 'metadata_changed',\n })\n }\n }\n\n // Phase 2: Deep structure validation\n validateStructureRecursive(original, modified, [], errors)\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\nconst validateStructureRecursive = (\n original: unknown,\n modified: unknown,\n path: string[],\n errors: ValidationError[],\n): void => {\n // Type check\n if (typeof original !== typeof modified) {\n errors.push({\n path,\n expected: typeof original,\n actual: typeof modified,\n type: 'type_mismatch',\n })\n return\n }\n\n // Array validation\n if (Array.isArray(original)) {\n if (!Array.isArray(modified)) {\n errors.push({\n path,\n expected: 'array',\n actual: typeof modified,\n type: 'type_mismatch',\n })\n return\n }\n\n if (original.length !== modified.length) {\n errors.push({\n path,\n expected: `length ${original.length}`,\n actual: `length ${modified.length}`,\n type: 'type_mismatch',\n })\n return\n }\n\n original.forEach((item, idx) => {\n validateStructureRecursive(item, modified[idx], [...path, String(idx)], errors)\n })\n return\n }\n\n // Object validation\n if (typeof original === 'object' && original !== null && typeof modified === 'object' && modified !== null) {\n const origKeys = Object.keys(original as object).sort()\n const modKeys = Object.keys(modified as object).sort()\n\n // Check for missing keys\n for (const key of origKeys) {\n if (!(key in modified)) {\n errors.push({\n path: [...path, key],\n expected: 'present',\n actual: 'missing',\n type: 'missing',\n })\n }\n }\n\n // Check for extra keys\n for (const key of modKeys) {\n if (!(key in original)) {\n errors.push({\n path: [...path, key],\n expected: 'absent',\n actual: 'present',\n type: 'extra',\n })\n }\n }\n\n // Validate nested structure\n const origObj = original as Record<string, unknown>\n const modObj = modified as Record<string, unknown>\n for (const key of origKeys) {\n if (key in modObj) {\n // For metadata fields, require exact match\n if (key.startsWith('_') || key === 'marks' || key === 'style' || key === 'markDefs') {\n if (JSON.stringify(origObj[key]) !== JSON.stringify(modObj[key])) {\n errors.push({\n path: [...path, key],\n expected: origObj[key],\n actual: modObj[key],\n type: 'metadata_changed',\n })\n }\n } else if (key !== 'text') {\n // For non-text fields, validate structure recursively\n validateStructureRecursive(origObj[key], modObj[key], [...path, key], errors)\n }\n // 'text' fields are allowed to change\n }\n }\n }\n}\n","import type { DocumentAnalysis } from '../document/analyzeDocumentContent'\n\nconst MAX_STYLE_GUIDE_LENGTH = 2000\n\ntype PromptSectionId = 'agentDirectives' | 'criticalRules' | 'updateInstructions' | 'glossaryTerms' | 'styleGuide'\n\ninterface PromptSection {\n id: PromptSectionId\n content: string\n required: boolean\n priority: number\n}\n\nexport interface PromptContext {\n documentAnalysis: DocumentAnalysis\n isUpdate: boolean\n targetLanguage: string\n sourceLanguage: string\n}\n\nexport interface PromptComponents {\n globalPrompt?: string\n localePrompt?: string\n glossaryTerms?: string\n protectedPhrases?: string[]\n}\n\nexport interface AssemblePromptResult {\n styleGuide: string\n omittedSections: PromptSectionId[]\n truncatedSections: PromptSectionId[]\n}\n\n/**\n * Assembles a structured, hierarchical translation prompt with clear priority levels while enforcing a 2000 character cap.\n */\nexport const assembleTranslationPrompt = (\n context: PromptContext,\n components: PromptComponents,\n): AssemblePromptResult => {\n const sections: PromptSection[] = [\n {\n id: 'agentDirectives',\n content: buildAgentDirectivesSection(context),\n required: true,\n priority: 0,\n },\n {\n id: 'criticalRules',\n content: buildCriticalRulesSection(context),\n required: true,\n priority: 1,\n },\n ]\n\n if (context.isUpdate) {\n sections.push({\n id: 'updateInstructions',\n content: buildUpdateInstructionsSection(),\n required: true,\n priority: 2,\n })\n }\n\n if (components.glossaryTerms?.trim()) {\n sections.push({\n id: 'glossaryTerms',\n content: buildGlossarySection(components.glossaryTerms),\n required: false,\n priority: 3,\n })\n }\n\n const styleSection = buildStyleGuideSection(components.globalPrompt, components.localePrompt)\n if (styleSection) {\n sections.push({\n id: 'styleGuide',\n content: styleSection,\n required: false,\n priority: 4,\n })\n }\n\n sections.sort((a, b) => a.priority - b.priority)\n\n let styleGuide = ''\n const omittedSections: PromptSectionId[] = []\n const truncatedSections: PromptSectionId[] = []\n\n for (const section of sections) {\n const prefix = styleGuide.length > 0 ? '\\n\\n---\\n\\n' : ''\n const availableCharacters = MAX_STYLE_GUIDE_LENGTH - (styleGuide.length + prefix.length)\n\n if (availableCharacters <= 0) {\n if (section.required) {\n const truncatedContent = truncateSection(section.content, 0)\n if (truncatedContent) {\n styleGuide += prefix + truncatedContent\n truncatedSections.push(section.id)\n }\n } else {\n omittedSections.push(section.id)\n }\n continue\n }\n\n if (section.content.length <= availableCharacters) {\n styleGuide += prefix + section.content\n continue\n }\n\n if (section.required) {\n const truncatedContent = truncateSection(section.content, availableCharacters)\n styleGuide += prefix + truncatedContent\n truncatedSections.push(section.id)\n } else {\n omittedSections.push(section.id)\n }\n }\n\n return {\n styleGuide,\n omittedSections,\n truncatedSections,\n }\n}\n\nconst truncateSection = (content: string, limit: number): string => {\n if (limit <= 0) {\n return ''\n }\n\n if (content.length <= limit) {\n return content\n }\n\n if (limit <= 1) {\n return content.slice(0, limit)\n }\n\n const ellipsis = '…'\n if (limit <= ellipsis.length) {\n return content.slice(0, limit)\n }\n\n const sliceEnd = limit - ellipsis.length\n const sliced = content.slice(0, sliceEnd)\n const trimmed = sliced.replace(/\\s+\\S*$/, '').trimEnd()\n const safeContent = trimmed.length >= sliceEnd / 2 ? trimmed : sliced\n return `${safeContent}${ellipsis}`\n}\n\n/**\n * SECTION 0: Agent workflow directives to align with GPT-5 best practices\n */\nconst buildAgentDirectivesSection = (context: PromptContext): string => {\n const { targetLanguage, sourceLanguage } = context\n const fromLanguage = sourceLanguage || 'the source language'\n const toLanguage = targetLanguage || 'the target language'\n\n return `# AGENT WORKFLOW DIRECTIVES\n\n- Use only the provided ${fromLanguage} source; never fetch outside context.\n- Scan each field once, then move directly into translation work.\n- Plan updates before editing so document structure remains intact.\n- Default to medium reasoning effort and escalate only for ambiguous passages.\n- Output just the ${toLanguage} translation and mirror the source formatting exactly.`\n}\n\n/**\n * SECTION 1: Critical rules that must never be violated\n */\nconst buildCriticalRulesSection = (context: PromptContext): string => {\n const { sourceLanguage, targetLanguage } = context\n const fromLanguage = sourceLanguage || 'the source language'\n const toLanguage = targetLanguage || 'the target language'\n\n return `# CRITICAL TRANSLATION RULES\n\nFollow these without exception:\n\n1. Translate from ${fromLanguage} into ${toLanguage}; every output token must be ${toLanguage} unless flagged as protected.\n2. Preserve meaning exactly—no additions, deletions, or tone shifts.\n3. Maintain all layout, markdown, rich-text marks, and data structure.\n4. Never invent metadata or field values that are absent in the source.\n5. Leave technical tokens (code, IDs, filenames) untouched.\n6. Keep punctuation, casing, and spacing unless ${toLanguage} grammar forces a change.`\n}\n\n/**\n * SECTION 2: Instructions for updating existing translations\n */\nconst buildUpdateInstructionsSection = (): string => {\n return `# UPDATING EXISTING TRANSLATION\n\nThis document already has a translation; only adjust fields that changed.\n\n- Compare the latest source to the existing target text.\n- Keep prior terminology and style unless the source demands a change.\n- Edit minimally while guaranteeing accuracy and completeness.`\n}\n\n/**\n * SECTION 3: Required glossary term translations\n */\nconst buildGlossarySection = (glossaryTerms: string): string => {\n return `# TERMINOLOGY RULES\n\nApply these mappings immediately after protected phrases and before style preferences.\n\n${glossaryTerms}`\n}\n\n/**\n * SECTION 4: Style guide and user preferences\n */\nconst buildStyleGuideSection = (globalPrompt?: string, localePrompt?: string): string | null => {\n const components: string[] = []\n\n if (globalPrompt || localePrompt) {\n components.push('# TRANSLATION STYLE GUIDE (LOWEST PRIORITY)')\n components.push(\n 'Apply these directions only after honoring critical rules, protected phrases, and glossary terminology.',\n )\n components.push('')\n }\n\n if (globalPrompt) {\n components.push('## General Guidelines')\n components.push(globalPrompt)\n components.push('')\n }\n\n if (localePrompt) {\n components.push('## Language-Specific Guidelines')\n components.push(localePrompt)\n }\n\n return components.length > 0 ? components.join('\\n') : null\n}\n","import type { Dispatch } from 'react'\nimport type { SanityClient, SanityDocument } from 'sanity'\nimport type { PluginConfig, TranslatableDocument, TranslationAction, TranslationMode } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { analyzeDocumentContent } from '../document/analyzeDocumentContent'\nimport { assembleLanguageSpecs } from '../document/assembleLanguageSpecs'\nimport { draftId, undraftId } from '../document/undraftId'\nimport type { FetchTranslationPromptsResult } from '../matching/fetchTranslationPrompts'\nimport { callGPT5 } from '../openai/callGPT5'\nimport { deserializeDocument } from '../openai/deserializeDocument'\nimport { serializeDocument } from '../openai/serializeDocument'\nimport { validateStructureIntegrity } from '../openai/validateStructure'\nimport { setJobStatus } from '../state/setJobStatus'\nimport { assembleTranslationPrompt } from './assembleTranslationPrompt'\nimport type { determineTranslationTarget } from './determineTranslationTarget'\n\ninterface TranslateEntireDocumentOptions {\n dispatch: Dispatch<TranslationAction>\n client: SanityClient\n schemaId: string\n sourceDocument: TranslatableDocument\n targetLanguage: string\n languageFieldName: string\n translationTarget: ReturnType<typeof determineTranslationTarget>\n translationPrompts: FetchTranslationPromptsResult\n translationMode: TranslationMode\n openaiConfig?: PluginConfig['openai']\n releaseId: string\n isNewTranslation: boolean\n}\n\n/**\n * Main translation orchestrator.\n * Creates a version in the release, then routes to the appropriate translation method.\n */\nexport const translateEntireDocument = async ({\n dispatch,\n client,\n schemaId,\n sourceDocument,\n targetLanguage,\n languageFieldName,\n translationTarget,\n translationPrompts,\n translationMode,\n openaiConfig,\n releaseId,\n isNewTranslation,\n}: TranslateEntireDocumentOptions) => {\n const logContext = {\n documentId: sourceDocument._id,\n targetLanguage,\n schemaId,\n operation: translationTarget.operation,\n targetId: translationTarget._id,\n translationMode,\n releaseId,\n isNewTranslation,\n }\n\n devLog.group('translateEntireDocument', logContext)\n devLog.time('translateEntireDocument')\n\n try {\n // STEP 1: Create a version in the release\n let versionedDocumentId: string\n let baseDocumentId: string\n\n if (isNewTranslation) {\n // Step 3: Clone source document into a new document in the content release\n const { _id, _createdAt, _updatedAt, _rev, ...docWithoutMeta } = sourceDocument\n const newTranslationDoc: Record<string, unknown> = {\n ...docWithoutMeta,\n _type: sourceDocument._type,\n [languageFieldName]: targetLanguage,\n }\n\n devLog.info('Creating new translation document in release', logContext)\n\n // Use action API to create version which auto-creates the base document\n baseDocumentId = translationTarget._id || `new-translation-${Date.now()}`\n\n await client.action({\n actionType: 'sanity.action.document.version.create',\n publishedId: baseDocumentId,\n document: {\n ...newTranslationDoc,\n _type: sourceDocument._type,\n _id: `versions.${releaseId}.${baseDocumentId}`,\n },\n })\n\n versionedDocumentId = `versions.${releaseId}.${baseDocumentId}`\n\n devLog.info('New translation version created in release', {\n ...logContext,\n versionedDocumentId,\n baseDocumentId,\n })\n } else {\n // Step 2: Create version of existing translation, then clone source content onto it\n const existingTranslationId = translationTarget._id\n\n if (!existingTranslationId) {\n throw new Error('Translation target ID not found for update operation')\n }\n\n devLog.info('Creating version of existing translation in release', {\n ...logContext,\n existingTranslationId,\n })\n\n // Get existing document - try both with and without drafts prefix\n let existingDoc = await client.getDocument(existingTranslationId)\n let actualDocumentId = existingTranslationId\n\n // If not found, try alternate version (draft vs published)\n if (!existingDoc) {\n const alternateId = existingTranslationId.startsWith('drafts.')\n ? undraftId(existingTranslationId)\n : draftId(existingTranslationId)\n\n devLog.info('Document not found with stored ID, trying alternate ID', {\n ...logContext,\n storedId: existingTranslationId,\n alternateId,\n })\n\n existingDoc = await client.getDocument(alternateId)\n if (existingDoc) {\n actualDocumentId = alternateId\n devLog.info('Found document with alternate ID', {\n ...logContext,\n actualDocumentId,\n })\n }\n }\n\n if (!existingDoc) {\n throw new Error(\n `Existing translation document not found: ${existingTranslationId} (also tried ${existingTranslationId.startsWith('drafts.') ? undraftId(existingTranslationId) : draftId(existingTranslationId)})`,\n )\n }\n\n // Use the published ID (without drafts prefix) for version creation\n const publishedId = undraftId(actualDocumentId)\n\n // Create version using action API\n await client.action({\n actionType: 'sanity.action.document.version.create',\n publishedId,\n document: {\n ...existingDoc,\n _id: `versions.${releaseId}.${publishedId}`,\n },\n })\n\n versionedDocumentId = `versions.${releaseId}.${publishedId}`\n baseDocumentId = publishedId\n\n devLog.info('Existing translation version created', {\n ...logContext,\n versionedDocumentId,\n baseDocumentId,\n })\n\n // Clone source content onto the version (keeping target language)\n const { _id, _createdAt, _updatedAt, _rev, ...sourceContent } = sourceDocument\n await client.createOrReplace({\n ...sourceContent,\n _id: versionedDocumentId,\n [languageFieldName]: targetLanguage,\n })\n\n devLog.info('Source content cloned onto version', {\n ...logContext,\n versionedDocumentId,\n })\n }\n\n // STEP 2: Route to appropriate translation method\n let translatedDocument: SanityDocument | undefined\n\n switch (translationMode) {\n case 'openai-only': {\n // Direct OpenAI translation of versioned document\n if (!openaiConfig?.enabled || !openaiConfig?.apiKey) {\n throw new Error('OpenAI is not configured but openai-only mode was selected')\n }\n\n devLog.info('Using OpenAI-only translation', logContext)\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'translating_with_openai')\n\n // Fetch the versioned document\n const versionedDoc = await client.getDocument(versionedDocumentId)\n if (!versionedDoc) {\n throw new Error(`Versioned document not found: ${versionedDocumentId}`)\n }\n\n // Translate using OpenAI\n const translatedDoc = await translateInPlaceWithOpenAI({\n dispatch,\n sourceDocument: sourceDocument,\n targetDocument: versionedDoc as SanityDocument,\n targetLanguage,\n languageFieldName,\n translationPrompts,\n openaiConfig,\n isReview: false,\n })\n\n // Save translated version back\n if (translatedDoc) {\n translatedDocument = (await client.createOrReplace({\n ...translatedDoc,\n _id: versionedDocumentId,\n })) as SanityDocument\n }\n break\n }\n\n case 'sanity-with-review':\n // Sanity translation + OpenAI review\n if (!openaiConfig?.enabled || !openaiConfig?.apiKey) {\n throw new Error('OpenAI is not configured but sanity-with-review mode was selected')\n }\n\n devLog.info('Using Sanity + OpenAI review', logContext)\n\n // First translate with Sanity\n translatedDocument = await translateWithSanityAgent({\n dispatch,\n client,\n schemaId,\n sourceDocument,\n targetLanguage,\n languageFieldName,\n versionedDocumentId,\n translationPrompts,\n })\n\n // Then review with OpenAI if translation succeeded\n if (translatedDocument) {\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'reviewing_with_openai')\n\n const reviewedDoc = await translateInPlaceWithOpenAI({\n dispatch,\n sourceDocument: sourceDocument,\n targetDocument: translatedDocument,\n targetLanguage,\n languageFieldName,\n translationPrompts,\n openaiConfig,\n isReview: true,\n })\n\n // Save reviewed version\n if (reviewedDoc) {\n translatedDocument = (await client.createOrReplace({\n ...reviewedDoc,\n _id: versionedDocumentId,\n })) as SanityDocument\n }\n }\n break\n\n default:\n // Sanity-only translation (default)\n devLog.info('Using Sanity-only translation', logContext)\n translatedDocument = await translateWithSanityAgent({\n dispatch,\n client,\n schemaId,\n sourceDocument,\n targetLanguage,\n languageFieldName,\n versionedDocumentId,\n translationPrompts,\n })\n break\n }\n\n devLog.timeEnd('translateEntireDocument')\n devLog.groupEnd()\n\n // Return the document with the base ID (not versioned ID) for metadata tracking\n if (translatedDocument) {\n return {\n ...translatedDocument,\n _id: baseDocumentId,\n } as SanityDocument\n }\n\n return undefined\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n devLog.error(\n 'translateEntireDocument failed',\n error instanceof Error ? error : new Error(String(error)),\n logContext,\n )\n\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'error', {\n errorMessage,\n })\n\n devLog.timeEnd('translateEntireDocument')\n devLog.groupEnd()\n return undefined\n }\n}\n\n/**\n * Translates using Sanity's built-in agent.\n * Works with versioned documents in a release.\n */\nconst translateWithSanityAgent = async ({\n dispatch,\n client,\n schemaId,\n sourceDocument,\n targetLanguage,\n languageFieldName,\n versionedDocumentId,\n translationPrompts,\n}: {\n dispatch: Dispatch<TranslationAction>\n client: SanityClient\n schemaId: string\n sourceDocument: TranslatableDocument\n targetLanguage: string\n languageFieldName: string\n versionedDocumentId: string\n translationPrompts: FetchTranslationPromptsResult\n}) => {\n const logContext = {\n documentId: sourceDocument._id,\n targetLanguage,\n versionedDocumentId,\n }\n\n try {\n // Assemble specs to tell the Sanity agent how to translate the document\n const { toLanguage, fromLanguage } = assembleLanguageSpecs({\n sourceDocument,\n languageFieldName,\n targetLanguage,\n })\n\n devLog.debug('Language specs assembled', {\n ...logContext,\n toLanguage,\n fromLanguage,\n })\n\n // Analyze document content to inform prompt construction\n const documentAnalysis = analyzeDocumentContent(sourceDocument)\n devLog.debug('Document analysis completed', {\n ...logContext,\n analysis: documentAnalysis,\n })\n\n // Ask the Sanity agent to translate the document\n devLog.info('Starting Sanity agent translation', logContext)\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'translating')\n\n // Assemble structured, hierarchical prompt\n const { styleGuide, omittedSections, truncatedSections } = assembleTranslationPrompt(\n {\n documentAnalysis,\n isUpdate: false, // Always fresh translation in a version\n targetLanguage,\n sourceLanguage: sourceDocument[languageFieldName] as string,\n },\n {\n globalPrompt: translationPrompts.globalPrompt,\n localePrompt: translationPrompts.localeInstructions[targetLanguage]?.prompt,\n glossaryTerms: translationPrompts.localeInstructions[targetLanguage]?.glossaryTerms,\n protectedPhrases: translationPrompts.protectedPhrases,\n },\n )\n\n devLog.info('Structured style guide generated', {\n ...logContext,\n styleGuideLength: styleGuide.length,\n omittedSections,\n truncatedSections,\n })\n\n if (omittedSections.length > 0 || truncatedSections.length > 0) {\n devLog.warn('Style guide adjusted to fit character limit', {\n ...logContext,\n omittedSections,\n truncatedSections,\n })\n }\n\n // Translate the versioned document\n const newDocument = (await client.agent.action.translate({\n schemaId,\n toLanguage,\n fromLanguage,\n documentId: versionedDocumentId, // Use versioned document ID\n languageFieldPath: [languageFieldName],\n styleGuide,\n protectedPhrases: translationPrompts.protectedPhrases || [],\n conditionalPaths: {\n defaultHidden: false,\n defaultReadOnly: false,\n },\n })) as SanityDocument\n\n devLog.info('Sanity agent translation completed', {\n ...logContext,\n newDocumentId: newDocument._id,\n newDocumentRev: newDocument._rev,\n })\n\n return newDocument\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n devLog.error(\n 'Sanity agent translation failed',\n error instanceof Error ? error : new Error(String(error)),\n logContext,\n )\n\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'error', {\n errorMessage,\n })\n\n return undefined\n }\n}\n\n/**\n * Translates or reviews a document using OpenAI.\n * Works in-place - receives a document, returns translated/reviewed document.\n */\nconst translateInPlaceWithOpenAI = async ({\n dispatch,\n sourceDocument,\n targetDocument,\n targetLanguage,\n languageFieldName,\n translationPrompts,\n openaiConfig,\n isReview,\n}: {\n dispatch: Dispatch<TranslationAction>\n sourceDocument: TranslatableDocument\n targetDocument: SanityDocument\n targetLanguage: string\n languageFieldName: string\n translationPrompts: FetchTranslationPromptsResult\n openaiConfig: NonNullable<PluginConfig['openai']>\n isReview: boolean\n}): Promise<SanityDocument | undefined> => {\n const sourceLanguage = sourceDocument[languageFieldName] as string\n const logContext = {\n sourceId: sourceDocument._id,\n targetId: targetDocument._id,\n targetLanguage,\n sourceLanguage,\n isReview,\n }\n\n try {\n // Serialize documents\n const sourceText = serializeDocument(sourceDocument)\n const targetText = serializeDocument(targetDocument)\n\n devLog.debug('Documents serialized for OpenAI', {\n ...logContext,\n sourceLength: sourceText.length,\n targetLength: targetText.length,\n })\n\n // Build prompt\n const prompt = isReview\n ? buildReviewPrompt({\n sourceText,\n translatedText: targetText,\n sourceLanguage,\n targetLanguage,\n translationPrompts,\n })\n : buildTranslationPrompt({\n sourceText,\n sourceLanguage,\n targetLanguage,\n translationPrompts,\n })\n\n devLog.debug('Calling GPT-5', {\n ...logContext,\n promptLength: prompt.length,\n })\n\n // Call GPT-5 with streaming\n const response = await callGPT5(openaiConfig.apiKey, {\n messages: [\n {\n role: 'system',\n content: isReview\n ? 'You are a translation quality reviewer. Review and refine translations while preserving structure.'\n : 'You are a professional translator. Translate documents while preserving their exact structure.',\n },\n { role: 'user', content: prompt },\n ],\n model: openaiConfig.model,\n reasoningEffort: openaiConfig.reasoningEffort,\n verbosity: openaiConfig.verbosity,\n onReasoningChunk: (reasoning) => {\n const status = isReview ? 'reviewing_with_openai' : 'translating_with_openai'\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, status, {\n reasoningProgress: reasoning,\n })\n },\n })\n\n devLog.info('OpenAI operation completed', {\n ...logContext,\n tokensUsed: response.tokensUsed.total,\n cost: response.cost,\n })\n\n // Deserialize response\n const result = deserializeDocument(targetDocument, response.content)\n\n // Validate structure\n const validation = validateStructureIntegrity(targetDocument, result)\n if (!validation.valid) {\n devLog.error('Structure validation failed', {\n ...logContext,\n errors: validation.errors,\n })\n\n const unexpectedErrors = validation.errors.filter(\n (err) => !(err.path.length === 1 && err.path[0] === languageFieldName),\n )\n\n if (unexpectedErrors.length > 0) {\n throw new Error(\n `Structure validation failed: ${unexpectedErrors.length} errors. First: ${unexpectedErrors[0].type} at ${unexpectedErrors[0].path.join('.')}`,\n )\n }\n }\n\n devLog.info('Structure validation passed', logContext)\n\n // Update job status with final results\n const status = isReview ? 'reviewing_with_openai' : 'translating_with_openai'\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, status, {\n tokensUsed: response.tokensUsed.total,\n estimatedCost: response.cost,\n reasoningSummary: response.reasoningSummary,\n })\n\n return result\n } catch (error) {\n devLog.error('OpenAI translation failed', error instanceof Error ? error : new Error(String(error)), logContext)\n throw error\n }\n}\n\nconst buildTranslationPrompt = ({\n sourceText,\n sourceLanguage,\n targetLanguage,\n translationPrompts,\n}: {\n sourceText: string\n sourceLanguage: string\n targetLanguage: string\n translationPrompts: FetchTranslationPromptsResult\n}): string => {\n const { globalPrompt, localeInstructions, protectedPhrases } = translationPrompts\n const localePrompt = localeInstructions[targetLanguage]?.prompt\n const glossaryTerms = localeInstructions[targetLanguage]?.glossaryTerms\n\n return `# TRANSLATION TASK\n\nTranslate this ${sourceLanguage} document into ${targetLanguage}.\n\n## Guidelines\n\n${globalPrompt ? `### General Guidelines\\n${globalPrompt}\\n` : ''}\n${localePrompt ? `### ${targetLanguage} Guidelines\\n${localePrompt}\\n` : ''}\n${glossaryTerms ? `### Required Terminology\\n${glossaryTerms}\\n` : ''}\n${protectedPhrases?.length ? `### Protected Phrases (do not translate)\\n${protectedPhrases.join(', ')}\\n` : ''}\n\n## Source Document (${sourceLanguage})\n\n\\`\\`\\`\n${sourceText}\n\\`\\`\\`\n\n## Instructions\n\n1. Translate all text content into ${targetLanguage}\n2. Follow the guidelines and terminology requirements above\n3. Preserve protected phrases exactly as they appear\n4. Return the document in THE EXACT SAME FORMAT\n5. **CRITICAL**: Only modify text content between tags\n6. Never add, remove, or change any tags, attributes, or structure\n\nIf you used significant reasoning to make translation decisions, you may include a brief summary:\n<reasoning>\nBrief summary of key translation decisions and reasoning\n</reasoning>\n\nThen return the translated document:`\n}\n\nconst buildReviewPrompt = ({\n sourceText,\n translatedText,\n sourceLanguage,\n targetLanguage,\n translationPrompts,\n}: {\n sourceText: string\n translatedText: string\n sourceLanguage: string\n targetLanguage: string\n translationPrompts: FetchTranslationPromptsResult\n}): string => {\n const { globalPrompt, localeInstructions, protectedPhrases } = translationPrompts\n const localePrompt = localeInstructions[targetLanguage]?.prompt\n const glossaryTerms = localeInstructions[targetLanguage]?.glossaryTerms\n\n return `# TRANSLATION QUALITY REVIEW\n\nReview this ${targetLanguage} translation and return a corrected version.\n\n## Review Criteria\n\n1. **Accuracy**: Does it accurately convey the ${sourceLanguage} source meaning?\n2. **Terminology**: Are glossary terms used correctly?\n3. **Protected Phrases**: Are protected phrases preserved?\n4. **Fluency**: Is it natural and fluent in ${targetLanguage}?\n5. **Style**: Does it follow the style guide?\n6. **Completeness**: Is all content translated?\n\n## Guidelines\n\n${globalPrompt ? `### General Guidelines\\n${globalPrompt}\\n` : ''}\n${localePrompt ? `### ${targetLanguage} Guidelines\\n${localePrompt}\\n` : ''}\n${glossaryTerms ? `### Required Terminology\\n${glossaryTerms}\\n` : ''}\n${protectedPhrases?.length ? `### Protected Phrases (must preserve)\\n${protectedPhrases.join(', ')}\\n` : ''}\n\n## Source Document (${sourceLanguage})\n\n\\`\\`\\`\n${sourceText}\n\\`\\`\\`\n\n## Current Translation (${targetLanguage})\n\n\\`\\`\\`\n${translatedText}\n\\`\\`\\`\n\n## Instructions\n\n1. Review the translation for quality issues\n2. Make corrections where needed to improve accuracy, fluency, or adherence to guidelines\n3. Keep good translations as-is - only fix what needs fixing\n4. Return the corrected translation in THE EXACT SAME FORMAT\n5. **CRITICAL**: Only modify text content between tags\n6. Never add, remove, or change any tags, attributes, or structure\n\nIf you made corrections or have insights about the translation quality, include a brief summary:\n<reasoning>\nBrief summary of review findings and corrections made\n</reasoning>\n\nThen return the corrected translation document:`\n}\n","import type { Dispatch } from 'react'\nimport type { SanityClient } from 'sanity'\nimport type { PluginConfig, TranslatableDocument, TranslationAction, TranslationMode } from '../../types'\nimport { devLog } from '../common/devLog'\nimport type { FetchTranslationPromptsResult } from '../matching/fetchTranslationPrompts'\nimport { getTranslationMetadata } from '../metadata/getTranslationMetadata'\nimport { updateTranslationMetadata } from '../metadata/updateTranslationMetadata'\nimport { setJobStatus } from '../state/setJobStatus'\nimport { determineTranslationTarget } from './determineTranslationTarget'\nimport { translateEntireDocument } from './translateEntireDocument'\n\ninterface TranslateDocumentParams {\n dispatch: Dispatch<TranslationAction>\n client: SanityClient\n sourceDocument: TranslatableDocument\n targetLanguage: PluginConfig['supportedLanguages'][number]['id']\n languageFieldName: PluginConfig['languageFieldName']\n shouldOverwriteFreshTranslations: boolean\n schemaId: string\n translationPrompts: FetchTranslationPromptsResult\n translationMode: TranslationMode\n openaiConfig?: PluginConfig['openai']\n releaseId: string\n}\n\nexport const translateDocument = async ({\n dispatch,\n client,\n sourceDocument,\n targetLanguage,\n languageFieldName,\n shouldOverwriteFreshTranslations,\n schemaId,\n translationPrompts,\n translationMode,\n openaiConfig,\n releaseId,\n}: TranslateDocumentParams) => {\n const logContext = {\n documentId: sourceDocument._id,\n targetLanguage,\n schemaId,\n shouldOverwriteFreshTranslations,\n translationMode,\n }\n\n devLog.group('translateDocument', logContext)\n devLog.time('translateDocument')\n\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'preparing_translation')\n\n try {\n // Confirm that source document has a language\n const sourceLanguage = sourceDocument[languageFieldName] as string | undefined\n\n devLog.debug('Checking source document language', {\n ...logContext,\n sourceLanguage,\n languageFieldName,\n })\n\n if (!sourceLanguage) {\n const error = `Source document ${sourceDocument._id} does not have a language`\n devLog.error(error, undefined, logContext)\n throw new Error(error)\n }\n\n // Get the translation metadata for the document, creating it if necessary\n devLog.debug('Getting translation metadata', logContext)\n const translationMetadata = await getTranslationMetadata({\n client,\n sourceDocument,\n sourceLanguage,\n })\n\n const isAlreadyFreshTranslation =\n translationMetadata.translationHistory?.some(\n (history) => history.language === targetLanguage && history.sourceRev === sourceDocument._rev,\n ) ?? false\n\n devLog.debug('Checking if translation is already fresh', {\n ...logContext,\n isAlreadyFreshTranslation,\n currentRev: sourceDocument._rev,\n })\n\n if (!shouldOverwriteFreshTranslations && isAlreadyFreshTranslation) {\n devLog.info('Skipping fresh translation', logContext)\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'skipped', {\n targetId: translationMetadata.translations.find((t) => t._key === targetLanguage)?.value._ref,\n })\n devLog.timeEnd('translateDocument')\n devLog.groupEnd()\n return\n }\n\n // Determine if we need to update an existing translation or create a new document\n const translationTarget = determineTranslationTarget({\n translationMetadata,\n targetLanguage,\n })\n const isNewTranslation = translationTarget.operation === 'create'\n\n devLog.debug('Translation target determined', {\n ...logContext,\n operation: translationTarget.operation,\n isNewTranslation,\n })\n\n // Delegate to translateEntireDocument which orchestrates the translation\n const newDocument = await translateEntireDocument({\n dispatch,\n client,\n schemaId,\n sourceDocument,\n targetLanguage,\n languageFieldName,\n translationTarget,\n translationPrompts,\n translationMode,\n openaiConfig,\n releaseId,\n isNewTranslation,\n })\n\n if (!newDocument) {\n devLog.error('Failed to create translation document', undefined, logContext)\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'error')\n devLog.timeEnd('translateDocument')\n devLog.groupEnd()\n throw new Error('Failed to create translation document')\n }\n\n devLog.debug('Translation document created', {\n ...logContext,\n newDocumentId: newDocument._id,\n })\n\n // UPDATE TRANSLATION METADATA\n devLog.debug('Updating translation metadata', logContext)\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'updating_metadata', {\n targetId: newDocument._id,\n })\n\n await updateTranslationMetadata({\n client,\n sourceDocument,\n sourceLanguage,\n newDocument,\n targetLanguage,\n translationMetadata,\n isNewTranslation,\n })\n\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'completed')\n devLog.info('Translation completed successfully', {\n ...logContext,\n newDocumentId: newDocument._id,\n })\n\n devLog.timeEnd('translateDocument')\n devLog.groupEnd()\n return newDocument._id\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n devLog.error('Translation failed', error instanceof Error ? error : new Error(String(error)), logContext)\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'error', {\n errorMessage,\n })\n devLog.timeEnd('translateDocument')\n devLog.groupEnd()\n // Don't re-throw - let the batch continue with other translations\n return undefined\n }\n}\n","import type { Dispatch } from 'react'\nimport type { SanityClient } from 'sanity'\nimport type { PluginConfig, TranslatableDocument, TranslationAction, TranslationMode } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { Semaphore } from '../common/Semaphore'\nimport { fetchTranslationPrompts } from '../matching/fetchTranslationPrompts'\nimport type { AssembleReferencesResult } from '../references/assembleReferences'\nimport { localizeReferences } from '../references/localizeReferences'\nimport { setupTranslationJobStatuses } from '../state/setupTranslationJobStatuses'\nimport { translateDocument } from './translateDocument'\n\ninterface TranslateDocumentsParams {\n sourceDocument: TranslatableDocument\n targetLanguages: Array<string>\n client: SanityClient\n dispatch: Dispatch<TranslationAction>\n languageFieldName: string\n shouldOverwriteFreshTranslations: boolean\n schemaId: string\n references: AssembleReferencesResult\n maxConcurrentTranslationJobs?: number\n maxReferenceDepth?: number\n translationMode: TranslationMode\n openaiConfig?: PluginConfig['openai']\n}\n\nconst translationSemaphore = new Semaphore(2)\n\nexport const translateDocumentSet = async ({\n client,\n sourceDocument,\n targetLanguages,\n dispatch,\n languageFieldName,\n shouldOverwriteFreshTranslations,\n schemaId,\n references = [],\n maxConcurrentTranslationJobs,\n translationMode,\n openaiConfig,\n}: TranslateDocumentsParams) => {\n const logContext = {\n documentId: sourceDocument._id,\n targetLanguages,\n referencesCount: references.length,\n maxConcurrentJobs: maxConcurrentTranslationJobs,\n translationMode,\n shouldOverwriteFreshTranslations,\n }\n\n devLog.group('translateDocumentSet', logContext)\n devLog.time('translateDocumentSet')\n\n let releaseId: string | undefined\n\n try {\n const sourceLanguage = sourceDocument[languageFieldName] as string\n\n if (!sourceLanguage) {\n devLog.error('Source language not found')\n throw new Error('Source language not found')\n }\n\n // If the config specifies a max concurrent translation jobs, update the semaphore\n if (maxConcurrentTranslationJobs) {\n devLog.debug('Updating translation semaphore permits', {\n ...logContext,\n newPermits: maxConcurrentTranslationJobs,\n })\n translationSemaphore.updatePermits(maxConcurrentTranslationJobs)\n }\n\n devLog.info('Starting document set translation', logContext)\n dispatch({\n type: 'SET_OVERALL_STATUS',\n payload: 'translating',\n })\n\n // Create a single Content Release for this entire translation batch\n const documentTitle =\n (sourceDocument.title as string | undefined) || sourceDocument._id.split('-').slice(0, 2).join('-')\n const releaseTitle = `[AI Translation] ${documentTitle} (${sourceDocument._type}) ${sourceLanguage} → ${targetLanguages.join(', ')}`\n const releaseDescription = `AI translation batch created on ${new Date().toLocaleDateString()} at ${new Date().toLocaleTimeString()}`\n\n devLog.info('Creating Content Release for translation batch', {\n ...logContext,\n releaseTitle,\n })\n\n const { releaseId: newReleaseId } = await client.releases.create({\n metadata: {\n title: releaseTitle,\n description: releaseDescription,\n releaseType: 'asap',\n },\n })\n\n releaseId = newReleaseId\n\n devLog.info('Content Release created', {\n ...logContext,\n releaseId,\n })\n\n const sourceDocuments = [\n {\n _id: sourceDocument._id,\n _type: sourceDocument._type,\n // TODO: Handle this properly\n title: (sourceDocument.name || sourceDocument.title) as string | undefined,\n },\n ...references,\n ]\n\n const sourceDocumentIds = sourceDocuments.map((doc) => doc._id)\n const targetDocumentIds: Array<string> = []\n\n devLog.debug('Prepared document set for translation', {\n ...logContext,\n sourceDocumentIds: sourceDocumentIds.length,\n totalTranslationJobs: sourceDocumentIds.length * targetLanguages.length,\n })\n\n // Assemble prompts for translation task style guides\n const translationPrompts = await fetchTranslationPrompts({\n client,\n sourceLanguage,\n targetLanguages,\n sourceDocument,\n })\n\n devLog.info('Translation prompts', translationPrompts)\n\n // Dispatch new job statuses for each document\n setupTranslationJobStatuses(dispatch, sourceDocuments, targetLanguages)\n\n // Create translation tasks for all documents and all languages\n const translationTasks = sourceDocumentIds.flatMap((documentId) =>\n targetLanguages.map(async (targetLanguage) => {\n try {\n await translationSemaphore.withPermit(async () => {\n // Fetch the document\n const document = await client\n .withConfig({\n // This enables us to detect documents that might be stale due to unpublished changes\n perspective: 'raw',\n })\n .fetch<TranslatableDocument>('*[_id == $id][0]', {\n id: documentId,\n })\n if (document && releaseId) {\n const targetDocumentId = await translateDocument({\n dispatch,\n client,\n sourceDocument: document,\n targetLanguage,\n languageFieldName,\n shouldOverwriteFreshTranslations,\n schemaId,\n translationPrompts,\n translationMode,\n openaiConfig,\n releaseId,\n })\n\n if (targetDocumentId) {\n targetDocumentIds.push(targetDocumentId)\n }\n }\n })\n } catch (error) {\n // Log the error but don't fail the entire batch\n devLog.error(\n 'Individual translation task failed',\n error instanceof Error ? error : new Error(String(error)),\n {\n ...logContext,\n failedDocumentId: documentId,\n failedTargetLanguage: targetLanguage,\n },\n )\n // The error status should already be set by translateDocument\n }\n }),\n )\n\n // Use Promise.allSettled instead of Promise.all to wait for all tasks regardless of failures\n const results = await Promise.allSettled(translationTasks)\n\n // Log summary of results\n const succeeded = results.filter((r) => r.status === 'fulfilled').length\n const failed = results.filter((r) => r.status === 'rejected').length\n\n devLog.info('Translation tasks summary', {\n ...logContext,\n total: results.length,\n succeeded,\n failed,\n })\n\n devLog.info('All translation tasks completed', {\n ...logContext,\n targetDocumentIds: targetDocumentIds.length,\n })\n\n dispatch({\n type: 'SET_OVERALL_STATUS',\n payload: 'linking_references',\n })\n\n devLog.debug('Starting reference localization', {\n ...logContext,\n documentsToLocalize: targetDocumentIds.length,\n })\n\n await Promise.all(\n targetDocumentIds.map(async (documentId) => {\n await localizeReferences({\n client,\n documentId,\n languageFieldName,\n })\n }),\n )\n\n // Determine overall status based on individual job statuses\n const hasAnyFailures = failed > 0\n\n if (hasAnyFailures && succeeded === 0) {\n // All translations failed\n devLog.error('All translation tasks failed', undefined, logContext)\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'error' })\n } else if (hasAnyFailures) {\n // Partial success - some translations failed but others succeeded\n devLog.warn('Translation completed with some failures', {\n ...logContext,\n succeeded,\n failed,\n })\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'completed' })\n } else {\n // All translations succeeded\n devLog.info('Document set translation completed successfully', logContext)\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'completed' })\n }\n\n devLog.timeEnd('translateDocumentSet')\n devLog.groupEnd()\n } catch (error) {\n // This catch block should only catch unexpected errors that aren't handled above\n devLog.error(\n 'translateDocumentSet failed with unexpected error',\n error instanceof Error ? error : new Error(String(error)),\n logContext,\n )\n\n // If we have a release, try to clean it up if all translations failed\n if (releaseId) {\n try {\n // Re-count succeeded jobs since we're in the error handler\n // This is a fallback - typically errors are already handled above\n // Delete the release since we're in fatal error state\n await client.releases.delete({ releaseId })\n devLog.warn('Deleted release due to fatal error', {\n ...logContext,\n releaseId,\n })\n } catch (cleanupError) {\n devLog.error(\n 'Failed to cleanup release',\n cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError)),\n logContext,\n )\n }\n }\n\n devLog.timeEnd('translateDocumentSet')\n devLog.groupEnd()\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'error' })\n throw error\n }\n}\n","import { Button, Flex } from '@sanity/ui'\nimport { useCallback } from 'react'\nimport { useClient } from 'sanity'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport { translateDocumentSet } from '../utils/translation/translateDocumentSet'\n\ninterface ActionButtonBarProps {\n setIsDialogOpen: (isOpen: boolean) => void\n}\n\nexport const ActionButtonBar = ({ setIsDialogOpen }: ActionButtonBarProps) => {\n const {\n document,\n config,\n overallStatus,\n dispatch,\n references,\n selectedTargetLanguages,\n shouldOverwriteFreshTranslations,\n translationMode,\n } = useTranslationContext()\n const client = useClient({ apiVersion: 'vX' })\n\n const handleTranslate = useCallback(() => {\n translateDocumentSet({\n sourceDocument: document,\n targetLanguages: selectedTargetLanguages,\n client,\n dispatch,\n languageFieldName: config.languageFieldName,\n schemaId: config.schemaId,\n references,\n shouldOverwriteFreshTranslations,\n maxConcurrentTranslationJobs: config.maxConcurrentTranslationJobs,\n maxReferenceDepth: config.maxReferenceDepth,\n translationMode,\n openaiConfig: config.openai,\n })\n }, [\n document,\n selectedTargetLanguages,\n client,\n dispatch,\n config.languageFieldName,\n config.schemaId,\n config.maxConcurrentTranslationJobs,\n config.maxReferenceDepth,\n config.openai,\n references,\n shouldOverwriteFreshTranslations,\n translationMode,\n ])\n\n const handleSelectAll = useCallback(() => {\n dispatch({\n type: 'SET_SELECTED_TARGET_LANGUAGES',\n payload: config.supportedLanguages.map((lang) => lang.id).filter((id) => id !== document.language),\n })\n }, [dispatch, config.supportedLanguages, document.language])\n\n const handleSelectNone = useCallback(() => {\n dispatch({\n type: 'SET_SELECTED_TARGET_LANGUAGES',\n payload: [],\n })\n }, [dispatch])\n\n return (\n <Flex justify=\"space-between\" gap={2}>\n <Flex justify=\"flex-start\" gap={2}>\n <Button mode=\"ghost\" size={2} onClick={handleSelectAll}>\n Select All\n </Button>\n <Button mode=\"ghost\" size={2} onClick={handleSelectNone}>\n Select None\n </Button>\n </Flex>\n <Flex justify=\"flex-end\" gap={2}>\n <Button onClick={() => setIsDialogOpen(false)} mode=\"ghost\" size={2}>\n Cancel\n </Button>\n <Button\n mode=\"ghost\"\n tone=\"positive\"\n size={2}\n onClick={handleTranslate}\n disabled={selectedTargetLanguages.length === 0 || !['idle', 'completed'].includes(overallStatus)}\n >\n Translate\n </Button>\n </Flex>\n </Flex>\n )\n}\n","import { Badge, Button, Checkbox, Flex, Inline, Spinner, Text } from '@sanity/ui'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport type { PluginConfig } from '../types'\n\ninterface LanguageCheckboxProps {\n lang: PluginConfig['supportedLanguages'][number]\n isLoading: boolean\n}\n\nexport const LanguageCheckbox = ({ lang, isLoading }: LanguageCheckboxProps) => {\n const { document, dispatch, selectedTargetLanguages, translationMetadata } = useTranslationContext()\n const isDisabled = lang.id === document.language\n const isChecked = selectedTargetLanguages.includes(lang.id)\n const hasTranslation = translationMetadata?.translations?.some((translation) => translation._key === lang.id)\n const isStale =\n hasTranslation &&\n translationMetadata?.translationHistory?.some(\n (history) => history.language === lang.id && history.sourceRev !== document._rev,\n )\n\n const handleChange = () => {\n if (isDisabled) return\n\n const updatedLanguages = isChecked\n ? selectedTargetLanguages.filter((langId) => langId !== lang.id)\n : [...selectedTargetLanguages, lang.id]\n\n dispatch({\n type: 'SET_SELECTED_TARGET_LANGUAGES',\n payload: updatedLanguages,\n })\n }\n\n if (isLoading) {\n return (\n <Button type=\"button\" mode=\"bleed\" size={2} onClick={handleChange} disabled muted>\n <Flex align=\"center\" justify=\"center\" paddingY={3}>\n <Spinner size={1} />\n </Flex>\n </Button>\n )\n }\n\n return (\n <Button\n type=\"button\"\n mode={isChecked ? 'ghost' : 'bleed'}\n size={2}\n onClick={handleChange}\n disabled={isDisabled}\n tone={isChecked ? 'primary' : 'default'}\n muted\n >\n <Flex align=\"center\" gap={2} style={{ cursor: 'pointer' }}>\n <Inline space={3}>\n <Checkbox checked={isChecked} onChange={handleChange} label={lang.title} disabled={isDisabled} />\n <Flex direction=\"column\" gap={3}>\n <Text>{lang.title}</Text>\n {!isDisabled ? (\n <Text size={1} muted>\n {hasTranslation && !isDisabled ? 'Already translated' : 'Not translated'}\n </Text>\n ) : (\n <Text size={1} muted>\n The current language\n </Text>\n )}\n </Flex>\n </Inline>\n {!isDisabled && hasTranslation && (\n <Flex flex={1} justify=\"flex-end\">\n <Badge tone={isStale ? 'caution' : 'positive'} size={3} muted>\n {isStale ? 'Stale' : 'Fresh'}\n </Badge>\n </Flex>\n )}\n </Flex>\n </Button>\n )\n}\n","import { Card, Grid } from '@sanity/ui'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport { useTranslationMetadata } from '../hooks/useTranslationMetadata'\nimport { LanguageCheckbox } from './LanguageCheckbox'\n\nexport const LanguageSelectionPanel = () => {\n // Get the configuration from the context\n const { document, config } = useTranslationContext()\n\n const { loading } = useTranslationMetadata({\n documentId: document._id,\n })\n\n return (\n <Card padding={4} muted border>\n <Grid columns={[1, 2]} gap={3}>\n {config?.supportedLanguages.map((lang) => (\n <LanguageCheckbox key={lang.id} lang={lang} isLoading={loading} />\n ))}\n </Grid>\n </Card>\n )\n}\n","import { Badge, Button, Card, Inline, Radio, Stack, Text } from '@sanity/ui'\nimport type { FC } from 'react'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport type { TranslationMode } from '../types'\n\nexport const TranslationModePanel: FC = () => {\n const { config, translationMode, openaiValidated, dispatch } = useTranslationContext()\n\n // Don't show if OpenAI is not configured\n if (!config?.openai?.enabled || !config?.openai?.apiKey) {\n return null\n }\n\n // Show validation status\n if (!openaiValidated) {\n return (\n <Card tone=\"caution\" padding={4} border>\n <Stack space={2}>\n <Text weight=\"semibold\">OpenAI Configuration</Text>\n <Text size={1} muted>\n Validating API key...\n </Text>\n </Stack>\n </Card>\n )\n }\n\n const handleModeChange = (mode: TranslationMode) => {\n dispatch({\n type: 'SET_TRANSLATION_MODE',\n payload: mode,\n })\n }\n\n return (\n <Card padding={4} border>\n <Stack space={3}>\n <Inline space={2}>\n <Text weight=\"semibold\">Translation Mode</Text>\n <Badge tone=\"positive\">GPT-5 Available</Badge>\n </Inline>\n\n <Stack space={2}>\n {/* Sanity Only */}\n <Button\n type=\"button\"\n mode={translationMode === 'sanity-only' ? 'ghost' : 'bleed'}\n tone={translationMode === 'sanity-only' ? 'primary' : 'default'}\n onClick={() => handleModeChange('sanity-only')}\n >\n <Inline space={2}>\n <Radio checked={translationMode === 'sanity-only'} readOnly />\n <Stack space={2}>\n <Text>Sanity Agent Only</Text>\n <Text size={1} muted>\n Fast, reliable, and included with Sanity.\n </Text>\n </Stack>\n </Inline>\n </Button>\n\n {/* OpenAI Only */}\n <Button\n type=\"button\"\n mode={translationMode === 'openai-only' ? 'ghost' : 'bleed'}\n tone={translationMode === 'openai-only' ? 'primary' : 'default'}\n onClick={() => handleModeChange('openai-only')}\n >\n <Inline space={2}>\n <Radio checked={translationMode === 'openai-only'} readOnly />\n <Stack space={2}>\n <Inline space={2}>\n <Text>GPT-5 Only</Text>\n <Badge tone=\"caution\">Beta</Badge>\n </Inline>\n <Text size={1} muted>\n Full style guide capacity with 1M token context window.\n </Text>\n </Stack>\n </Inline>\n </Button>\n\n {/* Sanity + OpenAI Review */}\n <Button\n type=\"button\"\n mode={translationMode === 'sanity-with-review' ? 'ghost' : 'bleed'}\n tone={translationMode === 'sanity-with-review' ? 'primary' : 'default'}\n onClick={() => handleModeChange('sanity-with-review')}\n >\n <Inline space={2}>\n <Radio checked={translationMode === 'sanity-with-review'} readOnly />\n <Stack space={2}>\n <Inline space={2}>\n <Text>Sanity + GPT-5 Review</Text>\n <Badge tone=\"primary\">Highest Quality</Badge>\n </Inline>\n <Text size={1} muted>\n Sanity translation refined by GPT-5. Takes longer but best results.\n </Text>\n </Stack>\n </Inline>\n </Button>\n </Stack>\n\n {/* Cost Warning */}\n {translationMode !== 'sanity-only' && (\n <Card tone=\"caution\" padding={3} border>\n <Text size={1}>\n 💰 <strong>OpenAI costs apply:</strong> You'll be charged for API usage based on tokens processed.\n </Text>\n </Card>\n )}\n </Stack>\n </Card>\n )\n}\n","import type { SanityClient, SanityDocument } from 'sanity'\nimport type { TranslatableDocument } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { findKeyValues, type RecursiveSearchable } from '../common/findKeyValues/findKeyValues'\n\ninterface AssembleReferencesParams {\n client: SanityClient\n sourceDocument: SanityDocument\n maxReferenceDepth: number\n translatableSchemaTypes?: Array<string>\n}\n\nexport type AssembleReferencesResult = Array<{\n _id: SanityDocument['_id']\n _type: SanityDocument['_type']\n title?: string\n}>\n\n// Sanity documents are objects of arbitrary shape, which can\n// contain references to other documents. References are always\n// represented as a `_ref` key with a value of the referenced document's\n// `_id`. This function takes a Sanity document, appends all `_ref` keys\n// within it to an array, then fetches the references documents and repeats\n// the process until the maximum reference depth is reached.\n\nexport const assembleReferences = async ({\n client,\n sourceDocument,\n maxReferenceDepth,\n translatableSchemaTypes = [],\n}: AssembleReferencesParams): Promise<AssembleReferencesResult> => {\n // Input validation\n if (!client) {\n throw new Error('assembleReferences: client is required')\n }\n\n if (!sourceDocument || !sourceDocument._id) {\n throw new Error('assembleReferences: sourceDocument with _id is required')\n }\n\n if (typeof maxReferenceDepth !== 'number' || maxReferenceDepth < 0) {\n throw new Error('assembleReferences: maxReferenceDepth must be a non-negative number')\n }\n\n const references = new Set<string>()\n const documentCache = new Map<string, TranslatableDocument | null>()\n const errors: Array<{ refId: string; error: Error }> = []\n\n let currentDepthDocuments = [sourceDocument]\n\n for (let depth = 0; depth < maxReferenceDepth && currentDepthDocuments.length > 0; depth++) {\n // Collect all reference IDs from current depth documents\n const allRefIds = new Set<string>()\n\n for (const document of currentDepthDocuments) {\n try {\n const refResults = findKeyValues<string>(document as RecursiveSearchable, '_ref')\n for (const result of refResults) {\n const refId = result.value\n if (typeof refId === 'string' && refId.length > 0 && !references.has(refId) && !documentCache.has(refId)) {\n allRefIds.add(refId)\n }\n }\n } catch (error) {\n devLog.error(\n 'Error finding references in document',\n error instanceof Error ? error : new Error(String(error)),\n {\n documentId: document._id,\n component: 'assembleReferences',\n },\n )\n }\n }\n\n if (allRefIds.size === 0) {\n break\n }\n\n // Batch fetch all references for this depth level\n const refIdArray = Array.from(allRefIds)\n try {\n const batchResults = await client.fetch<TranslatableDocument[]>('*[_id in $ids]', { ids: refIdArray })\n\n // Create a map of fetched documents by ID\n const fetchedDocuments = new Map<string, TranslatableDocument>()\n for (const doc of batchResults) {\n fetchedDocuments.set(doc._id, doc)\n }\n\n // Process each reference ID\n const nextDepthDocuments: TranslatableDocument[] = []\n\n for (const refId of refIdArray) {\n const fetchedDoc = fetchedDocuments.get(refId)\n\n if (fetchedDoc) {\n // Skip non-translatable documents\n if (translatableSchemaTypes.length > 0 && !translatableSchemaTypes.includes(fetchedDoc._type)) {\n documentCache.set(refId, null)\n continue\n }\n\n documentCache.set(refId, fetchedDoc)\n references.add(refId)\n nextDepthDocuments.push(fetchedDoc)\n } else {\n // Document not found\n documentCache.set(refId, null)\n devLog.warn(`Document with _id \"${refId}\" not found`, {\n refId,\n component: 'assembleReferences',\n })\n }\n }\n\n currentDepthDocuments = nextDepthDocuments\n } catch (fetchError) {\n // Log the error but continue processing\n const error = fetchError instanceof Error ? fetchError : new Error(String(fetchError))\n errors.push({ refId: `batch(${refIdArray.join(',')})`, error })\n devLog.error('Failed to batch fetch documents', error, {\n refIdCount: refIdArray.length,\n depth,\n component: 'assembleReferences',\n })\n break\n }\n }\n\n // Log summary of errors if any occurred\n if (errors.length > 0) {\n devLog.warn(`Encountered ${errors.length} error(s) while fetching references`, {\n errorCount: errors.length,\n errors: errors.map((e) => ({ refId: e.refId, message: e.error.message })),\n component: 'assembleReferences',\n })\n }\n\n // Convert the cached documents to the expected result format\n const result: AssembleReferencesResult = []\n for (const refId of references) {\n const cachedDoc = documentCache.get(refId)\n if (cachedDoc) {\n result.push({\n _id: cachedDoc._id,\n _type: cachedDoc._type,\n title: (cachedDoc.name || cachedDoc.title) as string | undefined,\n })\n }\n }\n\n return result\n}\n","import { Button, Checkbox, Flex, Inline, Select, Stack, Text } from '@sanity/ui'\nimport { useEffect, useState } from 'react'\nimport { useClient } from 'sanity'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport { assembleReferences } from '../utils/references/assembleReferences'\n\nexport const ReferenceConfigPanel = () => {\n const { dispatch, document, shouldTranslateReferences, maxReferenceDepth, config, references } =\n useTranslationContext()\n const client = useClient({ apiVersion: 'vX' })\n const [isLoadingReferences, setIsLoadingReferences] = useState(false)\n const maxDepth = config.maxReferenceDepth || 3\n\n useEffect(() => {\n const updateReferences = async () => {\n setIsLoadingReferences(true)\n\n if (!shouldTranslateReferences) {\n dispatch({\n type: 'SET_REFERENCES',\n payload: [],\n })\n return\n }\n\n const newReferences = await assembleReferences({\n client,\n sourceDocument: document,\n maxReferenceDepth,\n translatableSchemaTypes: config.translatableSchemaTypes,\n })\n dispatch({\n type: 'SET_REFERENCES',\n payload: newReferences,\n })\n\n setIsLoadingReferences(false)\n }\n\n updateReferences()\n }, [document, client, dispatch, shouldTranslateReferences, maxReferenceDepth, config.translatableSchemaTypes])\n\n const handleMaxReferenceDepthChange = async (event: React.ChangeEvent<HTMLSelectElement>) => {\n dispatch({\n type: 'SET_MAX_REFERENCE_DEPTH',\n payload: Number.parseInt(event.target.value),\n })\n }\n\n const handleShouldTranslateChange = () => {\n dispatch({\n type: 'SET_SHOULD_TRANSLATE_REFERENCES',\n payload: !shouldTranslateReferences,\n })\n }\n\n return (\n <Stack space={2}>\n <Flex justify=\"space-between\" align=\"center\">\n <Button\n type=\"button\"\n mode={shouldTranslateReferences ? 'ghost' : 'bleed'}\n size={2}\n onClick={handleShouldTranslateChange}\n tone={shouldTranslateReferences ? 'primary' : 'neutral'}\n >\n <Inline space={2}>\n <Checkbox\n checked={shouldTranslateReferences}\n onChange={handleShouldTranslateChange}\n label={shouldTranslateReferences ? 'Disable' : 'Enable'}\n />\n <Text>Translate References</Text>\n </Inline>\n </Button>\n {shouldTranslateReferences ? (\n <Inline space={2}>\n <Text>Max Depth:</Text>\n <Select\n disabled={!shouldTranslateReferences}\n placeholder=\"Depth\"\n onChange={handleMaxReferenceDepthChange}\n value={maxReferenceDepth.toString()}\n >\n {Array.from({ length: maxDepth }, (_, i) => i + 1).map((depth) => (\n <option key={depth} value={depth.toString()}>\n {depth}\n </option>\n ))}\n </Select>\n </Inline>\n ) : null}\n </Flex>\n {shouldTranslateReferences ? (\n <Flex padding={2} justify=\"flex-end\">\n <Text size={1} muted>\n {isLoadingReferences\n ? 'Loading references...'\n : references.length === 0\n ? 'No translatable references found.'\n : `${references.length} translatable references found.`}\n </Text>\n </Flex>\n ) : null}\n </Stack>\n )\n}\n","import { Button, Checkbox, Flex, Inline, Stack, Text } from '@sanity/ui'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport { ReferenceConfigPanel } from './ReferenceConfigPanel'\n\nexport const WriteSettingsPanel = () => {\n const { dispatch, shouldOverwriteFreshTranslations } = useTranslationContext()\n\n const handleShouldOverwriteFreshTranslationsChange = () => {\n dispatch({\n type: 'SET_SHOULD_OVERWRITE_FRESH_TRANSLATIONS',\n payload: !shouldOverwriteFreshTranslations,\n })\n }\n\n return (\n <Stack space={2}>\n <ReferenceConfigPanel />\n {/* Overwrite fresh translations */}\n <Flex justify=\"space-between\" align=\"center\">\n <Button\n type=\"button\"\n mode={shouldOverwriteFreshTranslations ? 'ghost' : 'bleed'}\n size={2}\n onClick={handleShouldOverwriteFreshTranslationsChange}\n tone={shouldOverwriteFreshTranslations ? 'primary' : 'neutral'}\n >\n <Inline space={2}>\n <Checkbox\n checked={shouldOverwriteFreshTranslations}\n onChange={handleShouldOverwriteFreshTranslationsChange}\n label={shouldOverwriteFreshTranslations ? 'Disable' : 'Enable'}\n />\n <Stack space={2}>\n <Text>Overwrite Fresh Translations</Text>\n <Text size={1} muted>\n All existing translations will be overwritten even if they are fresh.\n </Text>\n </Stack>\n </Inline>\n </Button>\n </Flex>\n </Stack>\n )\n}\n","import { Card, Label, Stack } from '@sanity/ui'\nimport type { FC } from 'react'\nimport { ActionButtonBar } from './ActionButtonBar'\nimport { LanguageSelectionPanel } from './LanguageSelectionPanel'\nimport { TranslationModePanel } from './TranslationModePanel'\nimport { WriteSettingsPanel } from './WriteSettingsPanel'\n\ninterface TranslationConfigViewProps {\n setIsDialogOpen: (isOpen: boolean) => void\n}\n\nexport const TranslationConfigView: FC<TranslationConfigViewProps> = ({ setIsDialogOpen }) => {\n return (\n <Stack space={4}>\n <Stack space={2}>\n <Label>Languages</Label>\n <LanguageSelectionPanel />\n </Stack>\n\n <Stack space={2}>\n <Label>Translation Method</Label>\n <TranslationModePanel />\n </Stack>\n\n <Stack space={2}>\n <Label>Settings</Label>\n <Card padding={4} border muted>\n <WriteSettingsPanel />\n </Card>\n </Stack>\n <ActionButtonBar setIsDialogOpen={setIsDialogOpen} />\n </Stack>\n )\n}\n","import { ArrowRightIcon, CheckmarkIcon, CircleIcon, ErrorOutlineIcon, PanelRightIcon } from '@sanity/icons'\nimport { Badge, Box, Button, Card, Code, Flex, Heading, Inline, Label, Spinner, Stack, Text, Tooltip } from '@sanity/ui'\nimport { useMemo } from 'react'\nimport { useOpenInNewPane } from 'sanity-plugin-utils'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\n\nconst TERMINAL_STATUSES = ['completed', 'skipped', 'error']\n\nexport const TranslationStatusPanel = () => {\n const { overallStatus, translationJobs, translationMode } = useTranslationContext()\n\n const headline = useMemo(\n () => (overallStatus === 'completed' ? 'Translation completed' : 'Translation in progress'),\n [overallStatus],\n )\n\n const jobs = useMemo(() => Array.from(translationJobs.values()), [translationJobs])\n\n // Calculate OpenAI usage if applicable\n const openaiStats = useMemo(() => {\n if (translationMode === 'sanity-only') return null\n\n const completedJobs = jobs.filter((j) => j.status === 'completed')\n const totalTokens = completedJobs.reduce((sum, job) => sum + (job.tokensUsed || 0), 0)\n const totalCost = completedJobs.reduce((sum, job) => sum + (job.estimatedCost || 0), 0)\n\n return { totalTokens, totalCost, jobsWithUsage: completedJobs.filter((j) => j.tokensUsed).length }\n }, [jobs, translationMode])\n\n return (\n <Stack space={4}>\n <Heading size={2}>{headline}</Heading>\n <ProgressBar progress={jobs.filter((job) => TERMINAL_STATUSES.includes(job.status)).length} max={jobs.length} />\n <Stack space={3}>\n <Label>Translations</Label>\n <Card padding={2} tone=\"transparent\">\n <Stack space={2}>\n <ul style={{ listStyleType: 'none', paddingInlineStart: '0' }}>\n {jobs.map((job) => (\n <li key={job._id}>\n <Box padding={3}>\n <Flex justify=\"space-between\" align=\"center\">\n <Inline space={2}>\n <Flex align=\"center\" justify=\"center\" style={{ width: '24px', height: '24px' }}>\n {['completed', 'skipped'].includes(job.status) ? (\n <CheckmarkIcon fontSize={24} />\n ) : job.status === 'error' ? (\n <Box style={{ color: 'var(--card-critical-fg-color)' }}>\n <ErrorOutlineIcon fontSize={24} />\n </Box>\n ) : job.status === 'queued' ? (\n <CircleIcon fontSize={24} />\n ) : (\n <Spinner />\n )}\n </Flex>\n <Stack space={3}>\n <Inline space={2}>\n <Text size={1} weight=\"semibold\">\n {job.title || 'Untitled Document'}\n </Text>\n <ArrowRightIcon fontSize={18} />\n <Code size={1}>{job.language}</Code>\n </Inline>\n <Code size={1}>\n {job._type} — {job._id.slice(-6)}\n </Code>\n {!['completed', 'skipped', 'error'].includes(job.status) && (\n <Stack space={2}>\n <Text size={1} weight=\"semibold\">\n {getStatusLabel(job.status)}\n </Text>\n {/* Show reasoning progress while GPT-5 is thinking */}\n {job.reasoningProgress && (\n <Card tone=\"primary\" padding={3} radius={2}>\n <Stack space={2}>\n <Text size={1} weight=\"semibold\">\n 🤔 GPT-5 Reasoning\n </Text>\n <Text size={1} style={{ fontStyle: 'italic' }}>\n {job.reasoningProgress}\n </Text>\n </Stack>\n </Card>\n )}\n </Stack>\n )}\n {job.status === 'completed' && job.tokensUsed ? (\n <Stack space={2}>\n <Text size={1} muted>\n {job.tokensUsed.toLocaleString()} tokens (~${job.estimatedCost?.toFixed(4)})\n </Text>\n {job.reasoningSummary && (\n <Card tone=\"primary\" padding={2} radius={2}>\n <Text size={1}>💡 {job.reasoningSummary}</Text>\n </Card>\n )}\n </Stack>\n ) : null}\n {job.status === 'error' && job.errorMessage ? (\n <Card tone=\"critical\" padding={2} radius={2}>\n <Text size={1}>Error: {job.errorMessage}</Text>\n </Card>\n ) : null}\n </Stack>\n </Inline>\n <Flex gap={2} align=\"center\">\n {job.status === 'skipped' ? <SkippedBadge /> : null}\n {job.status === 'error' ? <ErrorBadge /> : null}\n {job.targetId ? (\n <TranslationOpenerButton\n target={{\n _id: job.targetId,\n _type: job._type as string,\n }}\n />\n ) : null}\n </Flex>\n </Flex>\n </Box>\n </li>\n ))}\n </ul>\n </Stack>\n </Card>\n </Stack>\n\n {/* OpenAI Usage Summary */}\n {openaiStats && openaiStats.jobsWithUsage > 0 && (\n <Card tone=\"primary\" padding={4} border>\n <Stack space={2}>\n <Text weight=\"semibold\">OpenAI Usage Summary</Text>\n <Inline space={4}>\n <Text size={1}>\n 📊 Total Tokens: <strong>{openaiStats.totalTokens.toLocaleString()}</strong>\n </Text>\n <Text size={1}>\n 💰 Total Cost: <strong>${openaiStats.totalCost.toFixed(4)}</strong>\n </Text>\n </Inline>\n </Stack>\n </Card>\n )}\n </Stack>\n )\n}\n\nconst ProgressBar = ({ progress, max = 100 }: { progress: number; max?: number }) => {\n const percentage = Math.min((progress / max) * 100, 100)\n\n return (\n <Stack space={2}>\n <div\n style={{\n width: '100%',\n height: '8px',\n backgroundColor: 'var(--card-border-color)',\n borderRadius: '4px',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n width: `${percentage}%`,\n height: '100%',\n backgroundColor: 'var(--card-accent-fg-color)',\n borderRadius: '4px',\n transition: 'width 0.3s ease-in-out',\n }}\n />\n </div>\n <Flex justify=\"space-between\" align=\"center\">\n <Text size={1} muted>\n {percentage.toFixed(0)}%\n </Text>\n <Text size={1} muted>\n {progress} / {max} jobs completed.\n </Text>\n </Flex>\n </Stack>\n )\n}\n\nconst TranslationOpenerButton = ({ target }: { target: { _id: string; _type: string } }) => {\n const openInNewPane = useOpenInNewPane(target._id, target._type)\n\n return <Button mode=\"bleed\" text=\"View\" iconRight={PanelRightIcon} onClick={() => openInNewPane()} />\n}\n\nconst SkippedBadge = () => (\n <Tooltip\n portal\n content={\n <Box padding={1}>\n <Text size={1} muted>\n There was already a fresh translation for this document and language.\n </Text>\n </Box>\n }\n >\n <Badge tone=\"neutral\">Skipped</Badge>\n </Tooltip>\n)\n\nconst ErrorBadge = () => (\n <Tooltip\n portal\n content={\n <Box padding={1}>\n <Text size={1} muted>\n This translation failed. Check the error message for details.\n </Text>\n </Box>\n }\n >\n <Badge tone=\"critical\">Failed</Badge>\n </Tooltip>\n)\n\nconst getStatusLabel = (status: string): string => {\n const labels: Record<string, string> = {\n idle: 'Idle',\n queued: 'Queued...',\n preparing_translation: 'Preparing translation...',\n translating: 'Translating with Sanity Agent...',\n translating_with_openai: 'Translating with GPT-5...',\n reviewing_with_openai: 'Reviewing translation with GPT-5...',\n updating_metadata: 'Saving metadata...',\n error: 'Error',\n skipped: 'Skipped',\n completed: 'Completed',\n }\n return labels[status] || status\n}\n","import type { FC } from 'react'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport type { OverallTranslationStatus } from '../types'\nimport { devLog } from '../utils/common/devLog'\nimport { TranslationConfigView } from './TranslationConfigPanel'\nimport { TranslationStatusPanel } from './TranslationStatusPanel'\n\ninterface TranslationDialogProps {\n setIsDialogOpen: (isOpen: boolean) => void\n}\n\nconst STATES_IN_WHICH_TO_SHOW_CONFIG: Array<OverallTranslationStatus> = ['fetching_metadata', 'idle']\n\nconst STATES_IN_WHICH_TO_SHOW_TRANSLATION_PROGRESS: Array<OverallTranslationStatus> = [\n 'translating',\n 'linking_references',\n 'completed',\n]\n\nconst STATES_IN_WHICH_TO_SHOW_ERROR: Array<OverallTranslationStatus> = ['error']\n\nexport const TranslationDialog: FC<TranslationDialogProps> = ({ setIsDialogOpen }) => {\n const { overallStatus, document } = useTranslationContext()\n\n devLog.debug('TranslationDialog rendered', {\n documentId: document._id,\n overallStatus,\n component: 'TranslationDialog',\n })\n\n if (STATES_IN_WHICH_TO_SHOW_CONFIG.includes(overallStatus)) {\n devLog.debug('Showing translation config view', {\n documentId: document._id,\n overallStatus,\n component: 'TranslationDialog',\n })\n return <TranslationConfigView setIsDialogOpen={setIsDialogOpen} />\n }\n\n if (STATES_IN_WHICH_TO_SHOW_TRANSLATION_PROGRESS.includes(overallStatus)) {\n devLog.debug('Showing translation progress panel', {\n documentId: document._id,\n overallStatus,\n component: 'TranslationDialog',\n })\n return <TranslationStatusPanel />\n }\n\n if (STATES_IN_WHICH_TO_SHOW_ERROR.includes(overallStatus)) {\n devLog.warn('Translation error state reached', {\n documentId: document._id,\n overallStatus,\n component: 'TranslationDialog',\n })\n return <div>Error occurred</div>\n }\n\n devLog.warn('Unhandled translation dialog state', {\n documentId: document._id,\n overallStatus,\n component: 'TranslationDialog',\n })\n\n return <div>Unhandled overall state</div>\n}\n","import { TranslateIcon } from '@sanity/icons'\nimport { useState } from 'react'\nimport type { DocumentActionDescription, DocumentActionProps } from 'sanity'\nimport { TranslationDialog } from './components/TranslationDialog'\nimport { TranslationContextProvider } from './hooks/useTranslationContext/TranslationContextProvider'\nimport type { PluginConfig, TranslatableDocument } from './types'\nimport { devLog } from './utils/common/devLog'\n\ninterface TranslateActionProps extends DocumentActionProps {\n config: PluginConfig\n}\n\nexport const getTranslateAction = (config: PluginConfig) => {\n return ({ draft, published }: TranslateActionProps): DocumentActionDescription | null => {\n const document = draft ?? published\n const [isDialogOpen, setIsDialogOpen] = useState(false)\n\n devLog.debug('TranslateAction initialized', {\n documentId: document?._id,\n documentType: document?._type,\n })\n\n if (!document || !config.translatableSchemaTypes?.includes(document._type)) {\n devLog.warn('Document not in translatable schema types', {\n documentType: document?._type,\n translatableTypes: config.translatableSchemaTypes,\n })\n return null\n }\n\n if (!config) {\n devLog.error('Missing config in TranslateAction')\n return null\n }\n\n return {\n label: 'Translate',\n icon: TranslateIcon,\n onHandle: () => {\n devLog.info('Translation dialog opened', {\n documentId: document._id,\n documentType: document._type,\n component: 'TranslateAction',\n })\n setIsDialogOpen(true)\n },\n dialog: isDialogOpen && {\n type: 'dialog',\n onClose: () => setIsDialogOpen(false),\n header: 'Translate Document',\n content: (\n <TranslationContextProvider document={document as TranslatableDocument} config={config}>\n <TranslationDialog setIsDialogOpen={setIsDialogOpen} />\n </TranslationContextProvider>\n ),\n },\n }\n }\n}\n","import { definePlugin } from 'sanity'\nimport { getGiveTranslationFeedbackAction } from './GiveTranslationFeedbackAction'\nimport { translationGlossaryTerm } from './schemas/translationGlossaryTerm.schema'\nimport { translationHistory } from './schemas/translationHistory.schema'\nimport { translationSettings } from './schemas/translationSettings.schema'\nimport { getTranslateAction } from './TranslateAction'\nimport type { PluginConfig } from './types'\n\n/**\n * AI translation plugin for Sanity Studio\n * See the PluginConfig export for configuration options\n *\n * @public\n */\nexport const aiTranslationPlugin = definePlugin<PluginConfig>((config) => {\n return {\n name: 'sanity-plugin-ai-translation',\n document: {\n actions: [getTranslateAction(config), getGiveTranslationFeedbackAction(config)],\n },\n schema: {\n types: [translationSettings, translationGlossaryTerm],\n },\n }\n})\n\n/**\n * Schema for translation history\n *\n * @public\n */\nexport const translationHistorySchema = translationHistory\n"],"names":["translationHistory","translationSettings","status","result"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/hooks/useTranslationContext/TranslationContext.ts","../src/hooks/useTranslationContext/useTranslationContext.ts","../src/consts.ts","../src/utils/document/undraftId.ts","../src/hooks/useTranslationMetadata/useTranslationMetadata.ts","../src/utils/common/devLog.ts","../src/utils/feedback/validateSuggestion.ts","../src/utils/feedback/processFeedback.ts","../src/components/ProgressPanel.tsx","../src/components/SuggestionReviewList.tsx","../src/components/GiveFeedbackDialog.tsx","../src/utils/openai/validateApiKey.ts","../src/hooks/useTranslationContext/translationReducer.ts","../src/hooks/useTranslationContext/TranslationContextProvider.tsx","../src/GiveTranslationFeedbackAction.tsx","../src/schemas/translationGlossaryTerm.schema.ts","../src/schemas/translationHistory.schema.ts","../src/schemas/translationSettings.schema.ts","../src/utils/common/Semaphore.ts","../src/utils/document/documentToPlainText.ts","../src/utils/matching/matchTermInDocument.ts","../src/utils/matching/fetchTranslationPrompts.ts","../src/utils/common/findKeyValues/findKeyValues.ts","../src/utils/references/localizeReferences.ts","../src/utils/state/setupTranslationJobStatuses.ts","../src/utils/metadata/getExistingTranslationId.ts","../src/utils/metadata/getTranslationMetadata.ts","../src/utils/metadata/updateTranslationMetadata.ts","../src/utils/state/setJobStatus.ts","../src/utils/openai/callGPT5.ts","../src/utils/openai/deserializeDocument.ts","../src/utils/openai/serializeDocument.ts","../src/utils/openai/validateStructure.ts","../src/utils/translation/getOpenAITranslation.ts","../src/utils/document/analyzeDocumentContent.ts","../src/utils/translation/assembleTranslationPrompt.ts","../src/utils/document/assembleLanguageSpecs.ts","../src/utils/translation/getSanityAgentTranslation.ts","../src/utils/translation/handleTranslation.ts","../src/utils/translation/translateDocument.ts","../src/utils/translation/translateDocumentSet.ts","../src/components/ActionButtonBar.tsx","../src/components/LanguageCheckbox.tsx","../src/components/LanguageSelectionPanel.tsx","../src/components/TranslationModePanel.tsx","../src/utils/references/assembleReferences.ts","../src/components/ReferenceConfigPanel.tsx","../src/components/WriteSettingsPanel.tsx","../src/components/TranslationConfigPanel.tsx","../src/components/TranslationStatusPanel.tsx","../src/components/TranslationDialog.tsx","../src/TranslateAction.tsx","../src/index.ts"],"sourcesContent":["import { createContext } from 'react'\nimport type { TranslationJob, TranslationContext as TTranslationContext } from '../../types'\n\nexport const TranslationContext = createContext<TTranslationContext>({\n document: null,\n config: null,\n overallStatus: 'idle',\n selectedTargetLanguages: [],\n translationJobs: new Map<string, TranslationJob>(),\n shouldOverwriteFreshTranslations: false,\n shouldTranslateReferences: false,\n maxReferenceDepth: 1,\n references: [],\n translationMetadata: null,\n translationMode: 'sanity-only',\n openaiValidated: false,\n dispatch: () => {},\n})\n","import { useContext } from 'react'\nimport type { TranslationContext as TTranslationContext } from '../../types'\nimport { TranslationContext } from './TranslationContext'\n\ninterface ValidatedTranslationContext extends TTranslationContext {\n document: NonNullable<TTranslationContext['document']>\n config: NonNullable<TTranslationContext['config']>\n}\n\nexport const useTranslationContext = (): ValidatedTranslationContext => {\n const context = useContext(TranslationContext)\n\n if (!context.document || !context.config) {\n throw new Error(\n 'useTranslationContext must be used within a TranslationContextProvider with document and config props',\n )\n }\n\n return context as ValidatedTranslationContext\n}\n","export const TRANSLATION_METADATA_TYPE = 'translation.metadata'\nexport const UPDATE_EXISTING_TRANSLATION_PROMPT =\n '***IMPORTANT:** This document has an existing, but out-of-date translation. Please review the existing translation before processing and change as little as possible. Existing translation: $extantTranslation'\n","// Remove `drafts.` from a string\nexport const undraftId = (id: string) => id.replace(/^drafts\\./, '')\n\nexport const draftId = (id: string) => `drafts.${undraftId(id)}`\n","import { useEffect } from 'react'\nimport { useListeningQuery } from 'sanity-plugin-utils'\nimport { TRANSLATION_METADATA_TYPE } from '../../consts'\nimport type { TranslationMetadataDocument } from '../../types'\nimport { undraftId } from '../../utils/document/undraftId'\nimport { useTranslationContext } from '../useTranslationContext'\n\nconst TRANSLATION_METADATA_QUERY = '*[_type == $translationMetadataType && references($documentId)][0]'\n\ninterface UseTranslationMetadataProps {\n documentId: string\n}\n\nexport const useTranslationMetadata = ({ documentId }: UseTranslationMetadataProps) => {\n const { dispatch } = useTranslationContext()\n\n const { data, loading, error } = useListeningQuery<TranslationMetadataDocument | null>(TRANSLATION_METADATA_QUERY, {\n params: {\n documentId: undraftId(documentId),\n translationMetadataType: TRANSLATION_METADATA_TYPE,\n },\n initialValue: null,\n })\n\n useEffect(() => {\n if (loading) {\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'fetching_metadata' })\n return\n }\n\n if (error) {\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'error' })\n return\n }\n\n if (data) {\n dispatch({ type: 'SET_TRANSLATION_METADATA', payload: data as TranslationMetadataDocument })\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'idle' })\n return\n }\n\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'idle' })\n }, [data, loading, error, dispatch])\n\n return {\n data,\n loading,\n error,\n }\n}\n","/**\n * Development logging utility for AI Translation plugin troubleshooting\n * Only logs when SANITY_STUDIO_AI_TRANSLATION_PLUGIN_LOGGING environment variable is \"true\"\n */\n\nconst isLoggingEnabled = (): boolean => {\n return (\n process.env.NEXT_PUBLIC_SANITY_STUDIO_AI_TRANSLATION_PLUGIN_LOGGING === 'true' ||\n process.env.SANITY_STUDIO_AI_TRANSLATION_PLUGIN_LOGGING === 'true'\n )\n}\n\nexport interface DevLogContext {\n component?: string\n action?: string\n documentId?: string\n language?: string\n jobId?: string\n // biome-ignore lint/suspicious/noExplicitAny: not worth the effort\n [key: string]: any\n}\n\nexport const devLog = {\n info: (message: string, context?: DevLogContext) => {\n if (!isLoggingEnabled()) return\n console.info('[AI Translation]', message, context ? JSON.stringify(context, null, 2) : '')\n },\n\n warn: (message: string, context?: DevLogContext) => {\n if (!isLoggingEnabled()) return\n console.warn('[AI Translation]', message, context ? JSON.stringify(context, null, 2) : '')\n },\n\n error: (message: string, error?: Error | unknown, context?: DevLogContext) => {\n if (!isLoggingEnabled()) return\n console.error('[AI Translation]', message, error, context ? JSON.stringify(context, null, 2) : '')\n },\n\n debug: (message: string, data?: unknown, context?: DevLogContext) => {\n if (!isLoggingEnabled()) return\n console.debug('[AI Translation]', message, data, context ? JSON.stringify(context, null, 2) : '')\n },\n\n group: (label: string, context?: DevLogContext) => {\n if (!isLoggingEnabled()) return\n console.group(`[AI Translation] ${label}`, context ? JSON.stringify(context, null, 2) : '')\n },\n\n groupEnd: () => {\n if (!isLoggingEnabled()) return\n console.groupEnd()\n },\n\n time: (label: string) => {\n if (!isLoggingEnabled()) return\n console.time(`[AI Translation] ${label}`)\n },\n\n timeEnd: (label: string) => {\n if (!isLoggingEnabled()) return\n console.timeEnd(`[AI Translation] ${label}`)\n },\n}\n","import type { SanityClient } from 'sanity'\nimport type { FeedbackSuggestion } from '../../types'\n\nexport interface ValidationResult {\n valid: boolean\n status: 'valid' | 'warning' | 'invalid'\n reason?: string\n}\n\n/**\n * Validates a feedback suggestion to check for conflicts and invalid data.\n *\n * This function performs comprehensive validation including:\n * - Glossary term validation (required fields, conflicts with protected phrases)\n * - Protected phrase validation (duplicates, conflicts with glossary)\n * - Prompt validation (empty content, format issues)\n *\n * @param client - Sanity client for checking existing data\n * @param suggestion - The suggestion to validate\n * @returns Validation result with status and reason\n *\n * @example\n * ```typescript\n * const result = await validateSuggestion(client, {\n * type: 'glossaryTerm',\n * sourceValue: 'hello',\n * targetValue: 'hola',\n * sourceLocale: 'en',\n * targetLocale: 'es'\n * })\n * // Returns: { valid: true, status: 'valid' }\n * ```\n */\nexport async function validateSuggestion(\n client: SanityClient,\n suggestion: FeedbackSuggestion,\n): Promise<ValidationResult> {\n // Validate glossary terms\n if (suggestion.type === 'glossaryTerm') {\n return validateGlossaryTerm(client, suggestion)\n }\n\n // Validate protected phrases\n if (suggestion.type === 'protectedPhrase') {\n return validateProtectedPhrase(client, suggestion)\n }\n\n // Validate prompts\n if (suggestion.type === 'globalPrompt' || suggestion.type === 'localePrompt') {\n return validatePrompt(suggestion)\n }\n\n return { valid: true, status: 'valid' }\n}\n\n/**\n * Validates a glossary term suggestion for required fields and conflicts.\n */\nasync function validateGlossaryTerm(client: SanityClient, suggestion: FeedbackSuggestion): Promise<ValidationResult> {\n const { sourceValue, targetValue, sourceLocale, targetLocale } = suggestion\n\n // Check for missing required fields\n if (!sourceValue || !targetValue || !sourceLocale || !targetLocale) {\n return {\n valid: false,\n status: 'invalid',\n reason: 'Missing required fields for glossary term',\n }\n }\n\n // Check if source equals target (should be protected phrase instead)\n if (sourceValue.trim() === targetValue.trim()) {\n return {\n valid: false,\n status: 'invalid',\n reason: `Source and target are identical (\"${sourceValue}\") - this should be a protected phrase instead`,\n }\n }\n\n // Check if the source value is already in protected phrases\n const protectedPhrases = await client.fetch<string[]>('*[_id == \"translation-settings\"][0].protectedPhrases')\n\n if (protectedPhrases?.some((phrase) => phrase.toLowerCase() === sourceValue.toLowerCase())) {\n return {\n valid: false,\n status: 'invalid',\n reason: `Cannot add \"${sourceValue}\" to glossary because it is already a protected phrase. Protected phrases are never translated. If you want to create a translation mapping for this term, please remove it from protected phrases first by editing Translation Settings.`,\n }\n }\n\n // Also check if the target value is in protected phrases (shouldn't translate TO a protected term)\n if (targetValue && protectedPhrases?.some((phrase) => phrase.toLowerCase() === targetValue.toLowerCase())) {\n return {\n valid: false,\n status: 'invalid',\n reason: `Cannot use \"${targetValue}\" as a translation target because it is a protected phrase. Protected phrases should not appear as translation targets.`,\n }\n }\n\n // Check if glossary term already exists\n const existingTerm = await client.fetch<{\n translations: Array<{ _key: string; value: string }>\n } | null>(\n `*[_type == \"translationGlossaryTerm\" && count(translations[_key == $sourceLocale && value == $sourceValue]) > 0][0] {\n translations\n }`,\n { sourceLocale, sourceValue },\n )\n\n if (existingTerm) {\n const existingTarget = existingTerm.translations.find((t) => t._key === targetLocale)\n\n if (existingTarget?.value === targetValue) {\n return {\n valid: true,\n status: 'warning',\n reason: `This translation already exists in the glossary: \"${sourceValue}\" → \"${targetValue}\"`,\n }\n }\n\n if (existingTarget && existingTarget.value !== targetValue) {\n return {\n valid: true,\n status: 'warning',\n reason: `\"${sourceValue}\" already has a different translation in the glossary: \"${existingTarget.value}\". Applying this will update it to \"${targetValue}\".`,\n }\n }\n }\n\n return { valid: true, status: 'valid' }\n}\n\n/**\n * Validates a protected phrase suggestion for conflicts and duplicates.\n */\nasync function validateProtectedPhrase(\n client: SanityClient,\n suggestion: FeedbackSuggestion,\n): Promise<ValidationResult> {\n const phrase = suggestion.description.trim()\n\n if (!phrase) {\n return {\n valid: false,\n status: 'invalid',\n reason: 'Protected phrase cannot be empty',\n }\n }\n\n // Check if already exists\n const protectedPhrases = await client.fetch<string[]>('*[_id == \"translation-settings\"][0].protectedPhrases')\n\n if (protectedPhrases?.some((p) => p.toLowerCase() === phrase.toLowerCase())) {\n return {\n valid: true,\n status: 'warning',\n reason: `\"${phrase}\" is already in protected phrases`,\n }\n }\n\n // Check if this phrase exists in the glossary (conflict)\n const glossaryTermWithPhrase = await client.fetch<{\n _id: string\n translations: Array<{ _key: string; value: string }>\n } | null>(\n `*[_type == \"translationGlossaryTerm\" && count(translations[value == $phrase]) > 0][0] {\n _id,\n translations\n }`,\n { phrase },\n )\n\n if (glossaryTermWithPhrase) {\n // Find which translations use this phrase\n const matchingTranslations = glossaryTermWithPhrase.translations\n .filter((t) => t.value.toLowerCase() === phrase.toLowerCase())\n .map((t) => t._key)\n .join(', ')\n\n return {\n valid: false,\n status: 'invalid',\n reason: `Cannot add \"${phrase}\" as a protected phrase because it exists in the glossary as a translation (${matchingTranslations}). If you want to prevent this term from being translated, please remove it from the glossary first by editing Translation Settings.`,\n }\n }\n\n return { valid: true, status: 'valid' }\n}\n\n/**\n * Validates a prompt suggestion for proper content and format.\n */\nfunction validatePrompt(suggestion: FeedbackSuggestion): ValidationResult {\n const update = suggestion.description.trim()\n\n if (!update) {\n return {\n valid: false,\n status: 'invalid',\n reason: 'Prompt update cannot be empty',\n }\n }\n\n // Warn if the update looks like it's trying to apply rules rather than add them\n if (\n update.toLowerCase().includes('has been') ||\n update.toLowerCase().includes('was changed') ||\n update.toLowerCase().includes('corrected to')\n ) {\n return {\n valid: true,\n status: 'warning',\n reason: 'This update may be describing past changes rather than adding new rules',\n }\n }\n\n return { valid: true, status: 'valid' }\n}\n","import type { SanityClient } from 'sanity'\nimport type { FeedbackSuggestion } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { validateSuggestion } from './validateSuggestion'\n\ninterface ProcessFeedbackOptions {\n client: SanityClient\n schemaId: string\n sourceLanguage: string\n targetLanguage: string\n userFeedback?: string\n shouldIncludeEdits: boolean\n sourceSnapshot?: string\n targetSnapshot?: string\n currentDocument?: Record<string, unknown>\n onProgress?: (state: { stage: 'analyzing' | 'applying'; message: string }) => void\n}\n\ninterface AnalyzeFeedbackOptions {\n client: SanityClient\n schemaId: string\n sourceLanguage: string\n targetLanguage: string\n userFeedback?: string\n shouldIncludeEdits: boolean\n sourceSnapshot?: string\n targetSnapshot?: string\n currentDocument?: Record<string, unknown>\n onProgress?: (state: { stage: 'analyzing'; message: string }) => void\n}\n\ninterface ApplySuggestionsOptions {\n client: SanityClient\n schemaId: string\n sourceLanguage: string\n targetLanguage: string\n suggestions: FeedbackSuggestion[]\n onProgress?: (state: { stage: 'applying'; message: string }) => void\n}\n\ninterface FeedbackAnalysis {\n globalPromptUpdates?: string\n localePromptUpdates?: string\n protectedPhrasesToAdd?: string[]\n glossaryTermsToAdd?: Array<{\n sourceLocale: string\n targetLocale: string\n sourceValue: string\n targetValue: string\n }>\n reasoning: string\n}\n\n/**\n * Analyzes user feedback and inferred edits to generate improvement suggestions.\n *\n * This function:\n * 1. Analyzes explicit user feedback and/or edits made to translated content\n * 2. Uses AI to infer improvements to translation settings\n * 3. Returns a list of suggestions for the user to review\n * 4. Validates suggestions to flag conflicts and invalid data\n *\n * @param options - Configuration for feedback analysis\n * @returns Promise that resolves with array of feedback suggestions\n */\nexport async function analyzeFeedback({\n client,\n sourceLanguage,\n targetLanguage,\n userFeedback,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument,\n onProgress,\n}: AnalyzeFeedbackOptions): Promise<FeedbackSuggestion[]> {\n devLog.info('Starting feedback analysis', {\n sourceLanguage,\n targetLanguage,\n hasUserFeedback: !!userFeedback,\n shouldIncludeEdits,\n })\n\n onProgress?.({ stage: 'analyzing', message: 'Analyzing feedback and edits...' })\n\n const analysisPrompt = buildAnalysisPrompt({\n sourceLanguage,\n targetLanguage,\n userFeedback,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument,\n })\n\n const analysis = await analyzeWithAI(client, analysisPrompt)\n devLog.info('AI analysis complete', { analysis })\n\n const suggestions = convertAnalysisToSuggestions(analysis, sourceLanguage, targetLanguage)\n devLog.info('Generated suggestions', { count: suggestions.length })\n\n // Validate all suggestions\n onProgress?.({ stage: 'analyzing', message: 'Validating suggestions...' })\n const validatedSuggestions = await Promise.all(\n suggestions.map(async (suggestion) => {\n const validation = await validateSuggestion(client, suggestion)\n return {\n ...suggestion,\n warning: validation.reason,\n validationStatus: validation.status,\n // Only auto-approve valid suggestions; invalid ones cannot be approved\n approved: validation.status === 'valid',\n }\n }),\n )\n\n devLog.info('Suggestions validated', {\n valid: validatedSuggestions.filter((s) => s.validationStatus === 'valid').length,\n warnings: validatedSuggestions.filter((s) => s.validationStatus === 'warning').length,\n invalid: validatedSuggestions.filter((s) => s.validationStatus === 'invalid').length,\n })\n\n return validatedSuggestions\n}\n\n/**\n * Applies approved feedback suggestions to translation settings and glossary.\n *\n * @param options - Configuration for applying suggestions\n * @returns Promise that resolves when suggestions have been applied\n */\nexport async function applySuggestions({\n client,\n schemaId,\n sourceLanguage,\n targetLanguage,\n suggestions,\n onProgress,\n}: ApplySuggestionsOptions): Promise<void> {\n devLog.info('Applying suggestions', {\n sourceLanguage,\n targetLanguage,\n targetLanguageType: typeof targetLanguage,\n targetLanguageValue: targetLanguage,\n count: suggestions.filter((s) => s.approved).length,\n })\n\n if (!targetLanguage) {\n devLog.error('targetLanguage is undefined in applySuggestions!', {\n sourceLanguage,\n targetLanguage,\n suggestionsCount: suggestions.length,\n })\n throw new Error('targetLanguage is required but was undefined')\n }\n\n const approvedSuggestions = suggestions.filter((s) => s.approved)\n\n if (approvedSuggestions.length === 0) {\n devLog.info('No approved suggestions to apply')\n return\n }\n\n onProgress?.({ stage: 'applying', message: 'Applying updates to translation settings...' })\n\n await applyImprovementsFromSuggestions(client, schemaId, targetLanguage, approvedSuggestions, onProgress)\n\n devLog.info('Suggestions applied successfully')\n}\n\n/**\n * Processes user feedback and inferred edits to improve translation prompts and glossary.\n * This is a convenience function that combines analysis and application in one step.\n *\n * @deprecated Use analyzeFeedback and applySuggestions separately for better UX\n * @param options - Configuration for feedback processing\n * @returns Promise that resolves when feedback has been processed\n */\nexport async function processFeedback({\n client,\n schemaId,\n sourceLanguage,\n targetLanguage,\n userFeedback,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument,\n onProgress,\n}: ProcessFeedbackOptions): Promise<void> {\n const suggestions = await analyzeFeedback({\n client,\n schemaId,\n sourceLanguage,\n targetLanguage,\n userFeedback,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument,\n onProgress: onProgress ? (state) => onProgress({ ...state, stage: 'analyzing' }) : undefined,\n })\n\n await applySuggestions({\n client,\n schemaId,\n sourceLanguage,\n targetLanguage,\n suggestions,\n onProgress: onProgress ? (state) => onProgress({ ...state, stage: 'applying' }) : undefined,\n })\n}\n\n/**\n * Builds the prompt for AI analysis of user feedback and edits.\n */\nfunction buildAnalysisPrompt({\n sourceLanguage,\n targetLanguage,\n userFeedback,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument,\n}: {\n sourceLanguage: string\n targetLanguage: string\n userFeedback?: string\n shouldIncludeEdits: boolean\n sourceSnapshot?: string\n targetSnapshot?: string\n currentDocument?: Record<string, unknown>\n}): string {\n const parts: string[] = [\n 'You are analyzing feedback about AI translation quality to improve future translations.',\n `Source language: ${sourceLanguage}`,\n `Target language: ${targetLanguage}`,\n '',\n 'Your task is to analyze the feedback and infer specific improvements to:',\n '1. Global translation prompt (general guidance for all translations)',\n '2. Locale-specific prompt (guidance specific to this target language)',\n '3. Protected phrases (terms that should never be translated)',\n '4. Glossary terms (specific term-to-term translation mappings)',\n '',\n ]\n\n if (userFeedback) {\n parts.push('USER FEEDBACK:', userFeedback, '')\n }\n\n if (shouldIncludeEdits && sourceSnapshot && targetSnapshot && currentDocument) {\n parts.push(\n 'EDIT ANALYSIS:',\n 'Compare the original translation with the edited version to understand what the user changed.',\n '',\n 'Original source document:',\n sourceSnapshot,\n '',\n 'Original AI translation:',\n targetSnapshot,\n '',\n 'Current edited document:',\n JSON.stringify(currentDocument, null, 2),\n '',\n )\n }\n\n parts.push(\n 'INSTRUCTIONS:',\n 'Analyze the feedback and/or edits to identify patterns and improvements.',\n '',\n 'For edits, look for:',\n '- Terminology changes that suggest glossary additions',\n '- Phrases that were protected/unprotected incorrectly',\n '- Style, tone, or formatting issues that suggest prompt improvements',\n '- Consistent patterns across multiple changes',\n '',\n 'IMPORTANT RULES:',\n '1. DO NOT create redundant updates. Choose ONE mechanism for each issue:',\n ' - Protected phrases = terms that should NEVER be translated (language-agnostic)',\n ' - Glossary terms = specific translation mappings (e.g., \"chat\" → \"cat\" in French)',\n ' - Locale prompts = guidance specific to this source→target language pair',\n ' - Global prompts = guidance that applies to ALL translations',\n '',\n '2. Be locale-specific:',\n ` - This feedback is specifically about ${sourceLanguage} → ${targetLanguage} translation`,\n ' - DO NOT assume rules apply to other language pairs unless explicitly stated',\n ' - Prefer locale-specific prompts over global prompts unless universally applicable',\n '',\n '3. Protected phrases vs. Locale prompts:',\n ' - Use protected phrases ONLY for brand names, product names, proper nouns, technical terms that should NEVER be translated in ANY language',\n ' - Use locale prompts for language-pair-specific guidance (e.g., \"Keep technical terms in English when translating to German\")',\n ' - NEVER mention specific terms in both protected phrases AND prompts',\n '',\n '4. Glossary vs. Protected phrases:',\n ' - Glossary = \"Always translate X as Y\" (provides the correct translation)',\n ' - Protected = \"Never translate X\" (keeps it as-is)',\n ' - CRITICAL: If sourceValue === targetValue, use PROTECTED PHRASES, NOT glossary',\n ' - Only use glossary when the term actually changes between languages',\n ' - Use protected when the term should remain unchanged across all languages',\n '',\n 'Output a JSON object with this structure:',\n '{',\n ' \"globalPromptUpdates\": \"string | null - ONLY if this applies to ALL language pairs\",',\n ` \"localePromptUpdates\": \"string | null - ONLY if this is specific to ${sourceLanguage}→${targetLanguage}\",`,\n ' \"protectedPhrasesToAdd\": [\"ONLY brand names, products, proper nouns that should NEVER translate\"],',\n ' \"glossaryTermsToAdd\": [{\"sourceLocale\": \"sourceLocale\",\"sourceValue\": \"term\", \"targetLocale\": \"targetLocale\", \"targetValue\": \"translation\"} - ONLY specific term mappings],',\n ' \"reasoning\": \"string - Explain your analysis and which mechanism you chose for each issue\"',\n '}',\n '',\n 'Be conservative: only suggest changes when there is clear evidence.',\n 'If in doubt, prefer locale-specific updates over global updates.',\n 'Avoid redundancy - if you add a protected phrase, do NOT mention it in prompts.',\n '',\n 'CONCRETE EXAMPLES:',\n '',\n 'Example 1 - Brand name protection:',\n 'User feedback: \"Braze should never be translated\"',\n 'Correct output:',\n '{',\n ' \"protectedPhrasesToAdd\": [\"Braze\"],',\n ' \"reasoning\": \"User explicitly stated Braze is a brand name that should not be translated\"',\n '}',\n 'WRONG: Do not add this to glossary or prompts.',\n '',\n 'Example 2 - Terminology correction:',\n 'Edit analysis: User changed \"discussion\" to \"chat\" consistently in French translation',\n 'Correct output:',\n '{',\n ` \"glossaryTermsToAdd\": [{\"sourceLocale\": \"${sourceLanguage}\", \"sourceValue\": \"discussion\", \"targetLocale\": \"${targetLanguage}\", \"targetValue\": \"chat\"}],`,\n ' \"reasoning\": \"User consistently changed discussion→chat, indicating preferred terminology\"',\n '}',\n 'WRONG: Do not add to protected phrases (values differ) or prompts (specific mapping).',\n '',\n 'Example 3 - Style guidance:',\n 'User feedback: \"Translation is too formal, use casual tone\"',\n 'Correct output:',\n '{',\n ` \"localePromptUpdates\": \"Use a casual, conversational tone when translating to ${targetLanguage}. Avoid overly formal language.\",`,\n ' \"reasoning\": \"This is style guidance specific to this language pair\"',\n '}',\n 'WRONG: Do not make global (applies only to this target language).',\n '',\n 'Example 4 - Technical term in English:',\n `User feedback: \"Keep technical terms like 'API' and 'webhook' in English\"`,\n 'Correct output:',\n '{',\n ` \"localePromptUpdates\": \"When translating to ${targetLanguage}, keep technical terms like 'API', 'webhook', and similar technical jargon in English.\",`,\n ' \"reasoning\": \"This is language-pair-specific guidance about handling technical terms\"',\n '}',\n 'WRONG: Do not add \"API\" or \"webhook\" to protected phrases (already covered by prompt guidance).',\n '',\n 'Example 5 - Multiple issues:',\n 'User feedback: \"MyApp is our product name. Also, use more concise language.\"',\n 'Correct output:',\n '{',\n ' \"protectedPhrasesToAdd\": [\"MyApp\"],',\n ` \"localePromptUpdates\": \"Use concise language when translating to ${targetLanguage}. Prefer shorter phrases over lengthy explanations.\",`,\n ' \"reasoning\": \"MyApp is a product name (protected). Conciseness is a style preference for this language pair (locale prompt).\"',\n '}',\n '',\n 'Example 6 - Identical source and target:',\n 'Edit analysis: User kept \"dashboard\" as \"dashboard\" in Spanish',\n 'Correct output:',\n '{',\n ' \"protectedPhrasesToAdd\": [\"dashboard\"],',\n ' \"reasoning\": \"User kept the term unchanged, indicating it should not be translated\"',\n '}',\n 'WRONG: Do not add to glossary when source === target.',\n )\n\n return parts.join('\\n')\n}\n\n/**\n * Converts AI analysis results into user-reviewable suggestions.\n * @param analysis - The AI analysis results\n * @param sourceLanguage - The source language for translations\n * @param targetLanguage - The target language for translations\n */\nfunction convertAnalysisToSuggestions(\n analysis: FeedbackAnalysis,\n sourceLanguage: string,\n targetLanguage: string,\n): FeedbackSuggestion[] {\n const suggestions: FeedbackSuggestion[] = []\n\n if (analysis.globalPromptUpdates) {\n suggestions.push({\n id: crypto.randomUUID(),\n type: 'globalPrompt',\n description: analysis.globalPromptUpdates,\n approved: true,\n })\n }\n\n if (analysis.localePromptUpdates) {\n suggestions.push({\n id: crypto.randomUUID(),\n type: 'localePrompt',\n description: analysis.localePromptUpdates,\n approved: true,\n })\n }\n\n if (analysis.protectedPhrasesToAdd) {\n for (const phrase of analysis.protectedPhrasesToAdd) {\n suggestions.push({\n id: crypto.randomUUID(),\n type: 'protectedPhrase',\n description: phrase,\n approved: true,\n })\n }\n }\n\n if (analysis.glossaryTermsToAdd) {\n for (const term of analysis.glossaryTermsToAdd) {\n suggestions.push({\n id: crypto.randomUUID(),\n type: 'glossaryTerm',\n description: `${term.sourceValue} → ${term.targetValue}`,\n approved: true,\n // Use the provided source/target languages instead of relying on AI to provide them\n sourceLocale: sourceLanguage,\n sourceValue: term.sourceValue,\n targetLocale: targetLanguage,\n targetValue: term.targetValue,\n })\n }\n }\n\n return suggestions\n}\n\n/**\n * Uses Sanity AI Agent to analyze feedback and infer improvements.\n */\nasync function analyzeWithAI(client: SanityClient, prompt: string): Promise<FeedbackAnalysis> {\n try {\n const response = await client.agent.action.prompt({\n instruction: prompt,\n format: 'json',\n })\n\n devLog.debug('AI response received', { responseType: typeof response })\n\n const responseText =\n typeof response === 'string'\n ? response\n : response && typeof response === 'object'\n ? JSON.stringify(response)\n : (() => {\n throw new Error(`Unexpected response type: ${typeof response}`)\n })()\n\n return parseAIResponse(responseText)\n } catch (error) {\n devLog.error('Error during AI analysis', { error })\n throw new Error(`Failed to analyze feedback: ${error instanceof Error ? error.message : 'Unknown error'}`)\n }\n}\n\n/**\n * Parses the AI response text to extract the feedback analysis.\n */\nfunction parseAIResponse(responseText: string): FeedbackAnalysis {\n try {\n let parsed: unknown\n try {\n parsed = JSON.parse(responseText)\n } catch {\n const jsonMatch = responseText.match(/\\{[\\s\\S]*\\}/)\n if (!jsonMatch) throw new Error('No JSON found in AI response')\n parsed = JSON.parse(jsonMatch[0])\n }\n\n if (!parsed || typeof parsed !== 'object') {\n throw new Error('Response is not a valid object')\n }\n\n const analysis = parsed as FeedbackAnalysis\n if (typeof analysis.reasoning !== 'string') {\n throw new Error('Invalid response structure: missing reasoning')\n }\n\n return {\n globalPromptUpdates: analysis.globalPromptUpdates || undefined,\n localePromptUpdates: analysis.localePromptUpdates || undefined,\n protectedPhrasesToAdd: Array.isArray(analysis.protectedPhrasesToAdd) ? analysis.protectedPhrasesToAdd : [],\n glossaryTermsToAdd: Array.isArray(analysis.glossaryTermsToAdd) ? analysis.glossaryTermsToAdd : [],\n reasoning: analysis.reasoning,\n }\n } catch (error) {\n devLog.error('Error parsing AI response', { error, responseText })\n throw new Error('Failed to parse AI response as JSON')\n }\n}\n\n/**\n * Applies the inferred improvements from suggestions to translation settings and glossary.\n */\nasync function applyImprovementsFromSuggestions(\n client: SanityClient,\n schemaId: string,\n targetLanguage: string,\n suggestions: FeedbackSuggestion[],\n onProgress?: (state: { stage: 'applying'; message: string }) => void,\n): Promise<void> {\n devLog.info('Applying improvements from suggestions', {\n count: suggestions.length,\n targetLanguage,\n suggestions: suggestions.map((s) => ({\n type: s.type,\n approved: s.approved,\n sourceLocale: s.sourceLocale,\n targetLocale: s.targetLocale,\n sourceValue: s.sourceValue,\n targetValue: s.targetValue,\n })),\n })\n\n const settingsId = 'translation-settings'\n\n // Create the document if it doesn't exist\n const settings = await client.fetch('*[_id == $id][0]', { id: settingsId })\n if (!settings) {\n await client.createIfNotExists({\n _id: settingsId,\n _type: 'translationSettings',\n })\n }\n\n const updates: Array<Promise<unknown>> = []\n\n // Group suggestions by type and filter out empty values\n const globalPrompts = suggestions.filter((s) => s.type === 'globalPrompt' && s.description.trim())\n const localePrompts = suggestions.filter((s) => s.type === 'localePrompt' && s.description.trim())\n const protectedPhrases = suggestions.filter((s) => s.type === 'protectedPhrase' && s.description.trim())\n\n // Debug logging for glossary terms before filtering\n const glossaryTermsBeforeFilter = suggestions.filter((s) => s.type === 'glossaryTerm')\n devLog.info('Glossary terms before filtering', {\n count: glossaryTermsBeforeFilter.length,\n terms: glossaryTermsBeforeFilter.map((s) => ({\n sourceLocale: s.sourceLocale,\n sourceValue: s.sourceValue,\n targetLocale: s.targetLocale,\n targetValue: s.targetValue,\n approved: s.approved,\n })),\n })\n\n const glossaryTerms = suggestions.filter(\n (s) =>\n s.type === 'glossaryTerm' &&\n s.sourceLocale?.trim() &&\n s.sourceValue?.trim() &&\n s.targetLocale?.trim() &&\n s.targetValue?.trim(),\n )\n\n devLog.info('Filtered suggestions by type', {\n globalPrompts: globalPrompts.length,\n localePrompts: localePrompts.length,\n protectedPhrases: protectedPhrases.length,\n glossaryTerms: glossaryTerms.length,\n })\n\n // Apply both global and locale prompts in a single transform call\n if (globalPrompts.length > 0 || localePrompts.length > 0) {\n onProgress?.({\n stage: 'applying',\n message:\n globalPrompts.length > 0 && localePrompts.length > 0\n ? 'Updating global and locale translation prompts...'\n : globalPrompts.length > 0\n ? 'Updating global translation prompt...'\n : `Updating ${targetLanguage} translation prompt...`,\n })\n\n updates.push(updatePromptsWithTemplate(client, schemaId, settingsId, targetLanguage, globalPrompts, localePrompts))\n }\n\n // Apply protected phrases\n if (protectedPhrases.length > 0) {\n onProgress?.({ stage: 'applying', message: 'Adding protected phrases...' })\n const phrases = protectedPhrases.map((s) => s.description)\n devLog.debug('Applying protected phrases', { phrases })\n updates.push(addProtectedPhrases(client, settingsId, phrases))\n }\n\n // Apply glossary terms\n if (glossaryTerms.length > 0) {\n onProgress?.({ stage: 'applying', message: 'Adding glossary terms...' })\n const terms = glossaryTerms.map((s) => ({\n // biome-ignore-start lint/style/noNonNullAssertion: safely handled\n sourceLocale: s.sourceLocale!,\n sourceValue: s.sourceValue!,\n targetLocale: s.targetLocale!,\n targetValue: s.targetValue!,\n // biome-ignore-end lint/style/noNonNullAssertion: safely handled\n }))\n devLog.debug('Applying glossary terms', { terms })\n updates.push(addGlossaryTerms(client, terms))\n }\n\n if (updates.length === 0) {\n devLog.warn('No valid updates to apply - all suggestions may have been empty')\n }\n\n await Promise.all(updates)\n\n // Add change history entries\n onProgress?.({ stage: 'applying', message: 'Recording changes...' })\n await recordChangeHistory(\n client,\n settingsId,\n targetLanguage,\n suggestions.filter((s) => s.approved),\n )\n\n devLog.info('All improvements applied successfully')\n}\n\n/**\n * Updates both global and locale prompts using a deterministic template-based approach.\n * This appends suggestions with timestamps rather than using AI to merge them.\n */\nasync function updatePromptsWithTemplate(\n client: SanityClient,\n _schemaId: string,\n settingsId: string,\n targetLanguage: string,\n globalPrompts: FeedbackSuggestion[],\n localePrompts: FeedbackSuggestion[],\n): Promise<void> {\n devLog.info('Updating prompts with deterministic template', {\n settingsId,\n targetLanguage,\n hasGlobalUpdates: globalPrompts.length > 0,\n hasLocaleUpdates: localePrompts.length > 0,\n })\n\n if (!targetLanguage) {\n devLog.error('targetLanguage is undefined or empty!', {\n targetLanguage,\n globalPrompts: globalPrompts.length,\n localePrompts: localePrompts.length,\n })\n throw new Error('targetLanguage is required but was undefined')\n }\n\n const timestamp = new Date().toISOString().split('T')[0] // YYYY-MM-DD format\n\n // Fetch current settings\n const currentSettings = await client.fetch<{\n globalPrompt?: string\n localePrompts?: Array<{ _key: string; value: string }>\n }>('*[_id == $settingsId][0]{ globalPrompt, localePrompts }', { settingsId })\n\n // Update global prompt if needed\n if (globalPrompts.length > 0) {\n const currentGlobalPrompt = currentSettings?.globalPrompt || ''\n const updates = globalPrompts.map((s) => s.description.trim()).filter(Boolean)\n\n const newGlobalPrompt = [\n currentGlobalPrompt,\n currentGlobalPrompt ? '\\n' : '',\n `## User Feedback (${timestamp})`,\n ...updates.map((update) => `- ${update}`),\n ]\n .filter(Boolean)\n .join('\\n')\n\n await client.patch(settingsId).set({ globalPrompt: newGlobalPrompt }).commit()\n\n devLog.info('Global prompt updated', {\n previousLength: currentGlobalPrompt.length,\n newLength: newGlobalPrompt.length,\n addedUpdates: updates.length,\n })\n }\n\n // Update locale prompt if needed\n if (localePrompts.length > 0) {\n const updates = localePrompts.map((s) => s.description.trim()).filter(Boolean)\n\n // Check if locale prompt exists\n const existingLocalePrompts = currentSettings?.localePrompts || []\n const existingLocalePrompt = existingLocalePrompts.find((lp) => lp._key === targetLanguage)\n\n if (existingLocalePrompt) {\n // Update existing locale prompt\n const currentValue = existingLocalePrompt.value || ''\n const newValue = [\n currentValue,\n currentValue ? '\\n' : '',\n `## User Feedback (${timestamp})`,\n ...updates.map((update) => `- ${update}`),\n ]\n .filter(Boolean)\n .join('\\n')\n\n // Find the index of this locale prompt in the array\n const localeIndex = existingLocalePrompts.findIndex((lp) => lp._key === targetLanguage)\n\n await client\n .patch(settingsId)\n .set({ [`localePrompts[${localeIndex}].value`]: newValue })\n .commit()\n\n devLog.info('Locale prompt updated', {\n targetLanguage,\n previousLength: currentValue.length,\n newLength: newValue.length,\n addedUpdates: updates.length,\n })\n } else {\n // Create new locale prompt\n const newValue = [`## User Feedback (${timestamp})`, ...updates.map((update) => `- ${update}`)]\n .filter(Boolean)\n .join('\\n')\n\n await client\n .patch(settingsId)\n .setIfMissing({ localePrompts: [] })\n .append('localePrompts', [\n {\n _type: 'internationalizedArrayTextValue',\n _key: targetLanguage,\n value: newValue,\n },\n ])\n .commit()\n\n devLog.info('Locale prompt created', {\n targetLanguage,\n length: newValue.length,\n addedUpdates: updates.length,\n })\n }\n }\n\n devLog.info('Prompts updated successfully', {\n globalUpdated: globalPrompts.length > 0,\n localeUpdated: localePrompts.length > 0,\n })\n}\n\n/**\n * Adds new protected phrases to the translation settings.\n * Validates that phrases don't conflict with existing glossary terms.\n */\nasync function addProtectedPhrases(client: SanityClient, settingsId: string, phrases: string[]): Promise<void> {\n devLog.info('Adding protected phrases', { settingsId, phrasesCount: phrases.length, phrases })\n\n const currentPhrases = await client.fetch<string[]>('*[_id == $settingsId][0].protectedPhrases', { settingsId })\n devLog.debug('Fetched current protected phrases', { currentCount: currentPhrases?.length })\n\n // Filter out phrases that already exist\n let newPhrases = phrases.filter((phrase) => !currentPhrases?.includes(phrase))\n\n // Check for conflicts with glossary terms\n const conflictingPhrases: string[] = []\n for (const phrase of newPhrases) {\n const hasGlossaryConflict = await client.fetch<boolean>(\n `count(*[_type == \"translationGlossaryTerm\" && count(translations[value == $phrase]) > 0]) > 0`,\n { phrase },\n )\n\n if (hasGlossaryConflict) {\n conflictingPhrases.push(phrase)\n devLog.warn('Skipping protected phrase due to glossary conflict', { phrase })\n }\n }\n\n // Remove conflicting phrases\n newPhrases = newPhrases.filter((phrase) => !conflictingPhrases.includes(phrase))\n\n if (newPhrases.length === 0) {\n devLog.info('No new protected phrases to add (all already exist)')\n return\n }\n\n const result = await client\n .patch(settingsId)\n .setIfMissing({ protectedPhrases: [] })\n .append('protectedPhrases', newPhrases)\n .commit()\n\n devLog.info('Protected phrases added', { count: newPhrases.length, phrases: newPhrases, resultId: result._id })\n}\n\n/**\n * Adds new glossary terms or updates existing ones.\n * Validates that terms don't conflict with protected phrases.\n *\n * Behavior:\n * - If term doesn't exist: Creates new glossary term with source and target translations\n * - If term exists but target language missing: Adds the target translation\n * - If term exists with different target translation: Updates the target translation to new value\n * - If term exists with identical translation: Skips (no-op)\n * - If source or target value is a protected phrase: Skips with warning\n */\nasync function addGlossaryTerms(\n client: SanityClient,\n terms: Array<{ sourceLocale: string; sourceValue: string; targetLocale: string; targetValue: string }>,\n): Promise<void> {\n // Get protected phrases once for efficiency\n const protectedPhrases = await client.fetch<string[]>('*[_id == \"translation-settings\"][0].protectedPhrases')\n const protectedPhrasesLower = protectedPhrases?.map((p) => p.toLowerCase()) || []\n\n // Filter out terms where source === target (these should be protected phrases instead)\n const validTerms = terms.filter(({ sourceValue, targetValue }) => {\n if (sourceValue === targetValue) {\n devLog.warn('Skipping glossary term where source === target (should be a protected phrase)', {\n sourceValue,\n targetValue,\n })\n return false\n }\n\n // Check if source or target is a protected phrase\n if (protectedPhrasesLower.includes(sourceValue.toLowerCase())) {\n devLog.warn('Skipping glossary term because source value is a protected phrase', {\n sourceValue,\n targetValue,\n })\n return false\n }\n\n if (protectedPhrasesLower.includes(targetValue.toLowerCase())) {\n devLog.warn('Skipping glossary term because target value is a protected phrase', {\n sourceValue,\n targetValue,\n })\n return false\n }\n\n return true\n })\n\n if (validTerms.length === 0) {\n devLog.info('No valid glossary terms to add')\n return\n }\n\n for (const { sourceLocale, sourceValue, targetLocale, targetValue } of validTerms) {\n const existingTerm = await client.fetch<{\n _id: string\n translations: Array<{ _key: string; value: string }>\n } | null>(\n `*[_type == \"translationGlossaryTerm\" && count(translations[_key == $sourceLocale && value == $sourceValue]) > 0\n ][0] {\n _id,\n translations\n }`,\n { sourceLocale, sourceValue },\n )\n\n if (existingTerm) {\n const existingTargetIndex = existingTerm.translations.findIndex((t) => t._key === targetLocale)\n const existingTargetValue = existingTargetIndex >= 0 ? existingTerm.translations[existingTargetIndex].value : null\n\n if (existingTargetIndex === -1) {\n // Target language doesn't exist yet, add it\n await client\n .patch(existingTerm._id)\n .append('translations', [\n {\n _type: 'internationalizedArrayStringValue',\n _key: targetLocale,\n value: targetValue,\n },\n ])\n .commit()\n\n devLog.info('Added target language to existing glossary term', {\n sourceLocale,\n sourceValue,\n targetLocale,\n targetValue,\n })\n } else if (existingTargetValue !== targetValue) {\n // Target translation exists but is different, update it\n await client\n .patch(existingTerm._id)\n .set({ [`translations[${existingTargetIndex}].value`]: targetValue })\n .commit()\n\n devLog.info('Updated existing glossary term translation', {\n sourceLocale,\n sourceValue,\n targetLocale,\n oldValue: existingTargetValue,\n newValue: targetValue,\n })\n } else {\n // Translation already exists and is identical, skip\n devLog.info('Glossary term already has this exact translation', {\n sourceLocale,\n sourceValue,\n targetLocale,\n targetValue,\n })\n }\n } else {\n await client.create({\n _type: 'translationGlossaryTerm',\n translations: [\n {\n _type: 'internationalizedArrayStringValue',\n _key: sourceLocale,\n value: sourceValue,\n },\n {\n _type: 'internationalizedArrayStringValue',\n _key: targetLocale,\n value: targetValue,\n },\n ],\n })\n\n devLog.info('Created new glossary term', { sourceLocale, sourceValue, targetLocale, targetValue })\n }\n }\n\n devLog.info('Glossary terms processed', {\n totalRequested: terms.length,\n validAdded: validTerms.length,\n skipped: terms.length - validTerms.length,\n })\n}\n\n/**\n * Records change history for applied suggestions in translation settings.\n * This provides an audit trail and enables potential rollback functionality.\n */\nasync function recordChangeHistory(\n client: SanityClient,\n settingsId: string,\n targetLanguage: string,\n suggestions: FeedbackSuggestion[],\n): Promise<void> {\n const timestamp = new Date().toISOString()\n\n const historyEntries = suggestions.map((suggestion) => ({\n _type: 'object',\n _key: crypto.randomUUID(),\n timestamp,\n changeType: suggestion.type,\n description: suggestion.description,\n locale: suggestion.type === 'localePrompt' ? targetLanguage : undefined,\n }))\n\n if (historyEntries.length === 0) {\n return\n }\n\n await client.patch(settingsId).setIfMissing({ changeHistory: [] }).append('changeHistory', historyEntries).commit()\n\n devLog.info('Change history recorded', {\n count: historyEntries.length,\n timestamp,\n })\n}\n","import { CheckmarkCircleIcon, ErrorOutlineIcon } from '@sanity/icons'\nimport { Card, type CardTone, Flex, Spinner, Stack, Text } from '@sanity/ui'\nimport type { FC } from 'react'\n\ntype ProgressPanelStatus = 'in_progress' | 'completed' | 'error'\n\ninterface ProgressPanelProps {\n title?: string\n description?: string\n tone?: CardTone\n status?: ProgressPanelStatus\n icon?: React.ReactNode\n}\n\nconst ICON_SIZE = 64\n\nconst progressIconMap: Record<ProgressPanelStatus, React.ReactNode> = {\n in_progress: <Spinner />,\n completed: <CheckmarkCircleIcon fontSize={ICON_SIZE} />,\n error: <ErrorOutlineIcon fontSize={ICON_SIZE} />,\n}\n\nexport const ProgressPanel: FC<ProgressPanelProps> = ({\n title,\n description,\n tone,\n status = 'in_progress',\n icon,\n ...props\n}) => {\n return (\n <Card tone={tone} padding={4} {...props}>\n <Flex gap={4} align=\"center\" justify=\"center\">\n {icon || progressIconMap[status]}\n <Stack space={3}>\n {title && (\n <Text size={2} weight=\"bold\">\n {title}\n </Text>\n )}\n {description && (\n <Text size={1} muted>\n {description}\n </Text>\n )}\n </Stack>\n </Flex>\n </Card>\n )\n}\n","import { AddIcon, EmptyIcon, InfoOutlineIcon, TrashIcon, WarningOutlineIcon } from '@sanity/icons'\nimport { Box, Button, Card, Checkbox, Flex, Select, Stack, Text, TextArea, TextInput, Tooltip } from '@sanity/ui'\nimport { type FC, useState } from 'react'\nimport type { FeedbackSuggestion, ImprovementType } from '../types'\nimport { ProgressPanel } from './ProgressPanel'\n\ninterface SuggestionReviewListProps {\n suggestions: FeedbackSuggestion[]\n onSuggestionsChange: (suggestions: FeedbackSuggestion[]) => void\n sourceLanguage?: string\n targetLanguage?: string\n}\n\nconst IMPROVEMENT_TYPE_LABELS: Record<ImprovementType, string> = {\n globalPrompt: 'Global Prompt',\n localePrompt: 'Locale Prompt',\n protectedPhrase: 'Protected Phrase',\n glossaryTerm: 'Glossary Term',\n}\n\n/**\n * Creates a new empty suggestion with default values\n */\nconst createNewSuggestion = (\n type: ImprovementType = 'protectedPhrase',\n sourceLanguage?: string,\n targetLanguage?: string,\n): FeedbackSuggestion => {\n const baseSuggestion: FeedbackSuggestion = {\n id: crypto.randomUUID(),\n type,\n description: '',\n approved: true,\n }\n\n if (type === 'glossaryTerm') {\n return {\n ...baseSuggestion,\n sourceValue: '',\n targetValue: '',\n sourceLocale: sourceLanguage,\n targetLocale: targetLanguage,\n }\n }\n\n return baseSuggestion\n}\n\nexport const SuggestionReviewList: FC<SuggestionReviewListProps> = ({\n suggestions,\n onSuggestionsChange,\n sourceLanguage,\n targetLanguage,\n}) => {\n const [editingId, setEditingId] = useState<string | null>(null)\n\n const handleToggleApproval = (id: string) => {\n const updated = suggestions.map((s) => {\n // Don't allow toggling approval for invalid suggestions\n if (s.id === id && s.validationStatus !== 'invalid') {\n return { ...s, approved: !s.approved }\n }\n return s\n })\n onSuggestionsChange(updated)\n }\n\n const handleTypeChange = (id: string, newType: ImprovementType) => {\n const updated = suggestions.map((s) => {\n if (s.id !== id) {\n return s\n }\n\n // If changing to/from glossaryTerm, we need to handle sourceValue/targetValue\n if (newType === 'glossaryTerm' && s.type !== 'glossaryTerm') {\n // Parse description if it looks like \"source → target\"\n const match = s.description.match(/^(.+?)\\s*→\\s*(.+)$/)\n return {\n ...s,\n type: newType,\n sourceValue: match ? match[1].trim() : '',\n targetValue: match ? match[2].trim() : '',\n sourceLocale: sourceLanguage,\n targetLocale: targetLanguage,\n }\n }\n if (newType !== 'glossaryTerm' && s.type === 'glossaryTerm') {\n // Convert back to simple description\n return {\n ...s,\n type: newType,\n description: s.sourceValue && s.targetValue ? `${s.sourceValue} → ${s.targetValue}` : s.description,\n sourceValue: undefined,\n targetValue: undefined,\n }\n }\n return { ...s, type: newType }\n })\n onSuggestionsChange(updated)\n }\n\n const handleDescriptionChange = (id: string, newDescription: string) => {\n const updated = suggestions.map((s) => (s.id === id ? { ...s, description: newDescription } : s))\n onSuggestionsChange(updated)\n }\n\n const handleGlossarySourceChange = (id: string, newSource: string) => {\n const updated = suggestions.map((s) =>\n s.id === id\n ? {\n ...s,\n sourceValue: newSource,\n description: `${newSource} → ${s.targetValue || ''}`,\n // Preserve sourceLocale and targetLocale\n }\n : s,\n )\n onSuggestionsChange(updated)\n }\n\n const handleGlossaryTargetChange = (id: string, newTarget: string) => {\n const updated = suggestions.map((s) =>\n s.id === id\n ? {\n ...s,\n targetValue: newTarget,\n description: `${s.sourceValue || ''} → ${newTarget}`,\n // Preserve sourceLocale and targetLocale\n }\n : s,\n )\n onSuggestionsChange(updated)\n }\n\n const handleRemove = (id: string) => {\n const updated = suggestions.filter((s) => s.id !== id)\n onSuggestionsChange(updated)\n }\n\n const handleAddSuggestion = (type: ImprovementType) => {\n const newSuggestion = createNewSuggestion(type, sourceLanguage, targetLanguage)\n onSuggestionsChange([...suggestions, newSuggestion])\n // Auto-focus on the new suggestion by setting it as editing\n setEditingId(newSuggestion.id)\n }\n\n if (suggestions.length === 0) {\n return (\n <Stack space={3}>\n <ProgressPanel\n status=\"error\"\n tone=\"transparent\"\n title=\"No suggestions generated\"\n description='Try providing more specific feedback or enabling \"Include Edits\".'\n icon={<EmptyIcon fontSize={24} />}\n />\n <Flex justify=\"flex-start\">\n <Button\n mode=\"ghost\"\n tone=\"primary\"\n icon={AddIcon}\n text=\"Add Suggestion\"\n fontSize={1}\n onClick={() => handleAddSuggestion('protectedPhrase')}\n />\n </Flex>\n </Stack>\n )\n }\n\n return (\n <Stack space={3}>\n {suggestions.map((suggestion) => {\n const isEditing = editingId === suggestion.id\n const isGlossaryTerm = suggestion.type === 'glossaryTerm'\n const isPrompt = suggestion.type === 'globalPrompt' || suggestion.type === 'localePrompt'\n const hasWarning = suggestion.warning && suggestion.validationStatus !== 'valid'\n const isInvalid = suggestion.validationStatus === 'invalid'\n\n return (\n <Card\n key={suggestion.id}\n padding={4}\n border\n tone={isInvalid ? 'critical' : hasWarning ? 'caution' : suggestion.approved ? 'default' : 'transparent'}\n style={{ opacity: suggestion.approved ? 1 : 0.5 }}\n >\n <Stack space={3}>\n <Flex align=\"flex-start\" gap={3}>\n <Box paddingTop={1}>\n {isInvalid ? (\n <Tooltip\n content={\n <Box padding={2} style={{ maxWidth: '280px' }}>\n <Text size={1}>\n This suggestion cannot be applied because it conflicts with existing settings. See the error\n message below for details.\n </Text>\n </Box>\n }\n placement=\"top\"\n portal\n >\n <Box>\n <Checkbox checked={false} disabled readOnly />\n </Box>\n </Tooltip>\n ) : (\n <Checkbox checked={suggestion.approved} onChange={() => handleToggleApproval(suggestion.id)} />\n )}\n </Box>\n\n <Stack space={3} flex={1}>\n <Flex gap={2} align=\"center\" wrap=\"wrap\">\n <Box flex={1} style={{ minWidth: '200px' }}>\n <Select\n fontSize={1}\n value={suggestion.type}\n onChange={(event) =>\n handleTypeChange(suggestion.id, event.currentTarget.value as ImprovementType)\n }\n aria-label=\"Suggestion type\"\n >\n {Object.entries(IMPROVEMENT_TYPE_LABELS).map(([value, label]) => (\n <option key={value} value={value}>\n {label}\n </option>\n ))}\n </Select>\n </Box>\n\n <Button\n mode=\"bleed\"\n tone=\"critical\"\n icon={TrashIcon}\n fontSize={1}\n padding={2}\n onClick={() => handleRemove(suggestion.id)}\n aria-label=\"Remove suggestion\"\n title=\"Remove suggestion\"\n />\n </Flex>\n\n {isGlossaryTerm ? (\n <Stack space={3}>\n {/* Language labels */}\n {(suggestion.sourceLocale || suggestion.targetLocale) && (\n <Flex gap={2} align=\"center\">\n <Box flex={1}>\n <Text size={0} muted weight=\"medium\">\n {suggestion.sourceLocale ? `Source (${suggestion.sourceLocale})` : 'Source'}\n </Text>\n </Box>\n <Box style={{ width: '20px' }} />\n <Box flex={1}>\n <Text size={0} muted weight=\"medium\">\n {suggestion.targetLocale ? `Target (${suggestion.targetLocale})` : 'Target'}\n </Text>\n </Box>\n </Flex>\n )}\n\n {/* Term inputs */}\n <Flex gap={2} align=\"center\">\n <Box flex={1}>\n <TextInput\n fontSize={1}\n placeholder=\"Source term\"\n value={suggestion.sourceValue || ''}\n onChange={(event) => handleGlossarySourceChange(suggestion.id, event.currentTarget.value)}\n aria-label={`Source term in ${suggestion.sourceLocale || 'source language'}`}\n />\n </Box>\n <Text size={1} muted aria-hidden=\"true\">\n →\n </Text>\n <Box flex={1}>\n <TextInput\n fontSize={1}\n placeholder=\"Target term\"\n value={suggestion.targetValue || ''}\n onChange={(event) => handleGlossaryTargetChange(suggestion.id, event.currentTarget.value)}\n aria-label={`Target term in ${suggestion.targetLocale || 'target language'}`}\n />\n </Box>\n </Flex>\n </Stack>\n ) : isPrompt ? (\n <Stack space={2}>\n <TextArea\n fontSize={1}\n rows={isEditing ? 6 : 3}\n value={suggestion.description}\n onChange={(event) => handleDescriptionChange(suggestion.id, event.currentTarget.value)}\n onFocus={() => setEditingId(suggestion.id)}\n onBlur={() => setEditingId(null)}\n placeholder=\"Enter prompt guidance...\"\n aria-label={`${IMPROVEMENT_TYPE_LABELS[suggestion.type]} content`}\n />\n </Stack>\n ) : (\n <TextInput\n fontSize={1}\n value={suggestion.description}\n onChange={(event) => handleDescriptionChange(suggestion.id, event.currentTarget.value)}\n placeholder=\"Enter protected phrase...\"\n aria-label=\"Protected phrase\"\n />\n )}\n\n {/* Display validation warning */}\n {hasWarning && (\n <Card padding={3} tone={isInvalid ? 'critical' : 'caution'} border radius={2}>\n <Flex align=\"flex-start\" gap={2}>\n <Text size={1}>{isInvalid ? <WarningOutlineIcon /> : <InfoOutlineIcon />}</Text>\n <Stack space={2} flex={1}>\n <Text size={1} weight=\"medium\">\n {isInvalid ? 'Invalid Suggestion' : 'Warning'}\n </Text>\n <Text size={1} muted>\n {suggestion.warning}\n </Text>\n </Stack>\n </Flex>\n </Card>\n )}\n </Stack>\n </Flex>\n </Stack>\n </Card>\n )\n })}\n\n <Flex justify=\"flex-start\">\n <Button\n mode=\"ghost\"\n tone=\"neutral\"\n icon={AddIcon}\n text=\"Add Suggestion\"\n fontSize={1}\n onClick={() => handleAddSuggestion('protectedPhrase')}\n />\n </Flex>\n </Stack>\n )\n}\n","import { ArrowLeftIcon, EditIcon, InfoOutlineIcon } from '@sanity/icons'\nimport { Box, Button, Card, Checkbox, Flex, Heading, Inline, Stack, Text, TextArea, Tooltip } from '@sanity/ui'\nimport { type FC, useState } from 'react'\nimport { useClient } from 'sanity'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport { useTranslationMetadata } from '../hooks/useTranslationMetadata'\nimport type { FeedbackSuggestion } from '../types'\nimport { analyzeFeedback, applySuggestions } from '../utils/feedback/processFeedback'\nimport { ProgressPanel } from './ProgressPanel'\nimport { SuggestionReviewList } from './SuggestionReviewList'\n\ninterface GiveFeedbackDialogProps {\n setIsDialogOpen: (isOpen: boolean) => void\n}\n\ntype ProcessingState = 'idle' | 'analyzing' | 'reviewing' | 'applying' | 'completed' | 'error'\n\nexport const GiveFeedbackDialog: FC<GiveFeedbackDialogProps> = ({ setIsDialogOpen }) => {\n const client = useClient({ apiVersion: 'vX' })\n const { document, config, translationMetadata } = useTranslationContext()\n const { loading } = useTranslationMetadata({\n documentId: document._id,\n })\n\n const [feedback, setFeedback] = useState('')\n const [shouldIncludeEdits, setShouldIncludeEdits] = useState(false)\n const [processingState, setProcessingState] = useState<ProcessingState>('idle')\n const [suggestions, setSuggestions] = useState<FeedbackSuggestion[]>([])\n const [changeSummary, setChangeSummary] = useState<string>('')\n const [error, setError] = useState<string>('')\n const [sourceLanguage, setSourceLanguage] = useState<string>('')\n const [targetLanguage, setTargetLanguage] = useState<string>('')\n\n const handleShouldIncludeEditsChange = () => {\n setShouldIncludeEdits(!shouldIncludeEdits)\n }\n\n const handleCancel = () => {\n setIsDialogOpen(false)\n }\n\n const handleAnalyze = async () => {\n if (!feedback && !shouldIncludeEdits) {\n setError('Please provide feedback or enable \"Include Edits\"')\n return\n }\n\n if (!config) {\n setError('Missing plugin configuration')\n return\n }\n\n setError('')\n setProcessingState('analyzing')\n setChangeSummary('')\n\n try {\n const translationHistory = translationMetadata?.translationHistory?.find(\n (history) => history.language === document[config.languageFieldName],\n )\n\n if (!translationHistory) {\n throw new Error(\n 'No translation history found for this document. Please ensure this document was translated using the AI Translation plugin.',\n )\n }\n\n const { sourceSnapshot, targetSnapshot, sourceLanguage: srcLang } = translationHistory\n\n if (!sourceSnapshot || !targetSnapshot) {\n throw new Error('Translation snapshots are missing from history.')\n }\n\n const srcLanguage = srcLang || 'en-us'\n const tgtLanguage = document[config.languageFieldName] as string\n\n // Store languages for use in UI\n setSourceLanguage(srcLanguage)\n setTargetLanguage(tgtLanguage)\n\n // Analyze the feedback using the AI agent\n const generatedSuggestions = await analyzeFeedback({\n client,\n schemaId: config.schemaId,\n sourceLanguage: srcLanguage,\n targetLanguage: tgtLanguage,\n userFeedback: feedback || undefined,\n shouldIncludeEdits,\n sourceSnapshot,\n targetSnapshot,\n currentDocument: shouldIncludeEdits ? document : undefined,\n onProgress: ({ stage, message }) => {\n setProcessingState(stage)\n setChangeSummary(message)\n },\n })\n\n setSuggestions(generatedSuggestions)\n setProcessingState('reviewing')\n } catch (err) {\n console.error('Error analyzing feedback:', err)\n setError(err instanceof Error ? err.message : 'An unexpected error occurred')\n setProcessingState('error')\n }\n }\n\n const handleApplySuggestions = async () => {\n if (!config) {\n setError('Missing plugin configuration')\n return\n }\n\n const approvedCount = suggestions.filter((s) => s.approved).length\n if (approvedCount === 0) {\n setError('Please approve at least one suggestion to apply')\n return\n }\n\n setError('')\n setProcessingState('applying')\n\n try {\n const translationHistory = translationMetadata?.translationHistory?.find(\n (history) => history.language === document[config.languageFieldName],\n )\n\n if (!translationHistory) {\n throw new Error('Translation history not found')\n }\n\n await applySuggestions({\n client,\n schemaId: config.schemaId,\n sourceLanguage: translationHistory.sourceLanguage || 'en-us',\n targetLanguage: translationHistory.language,\n suggestions,\n onProgress: ({ stage, message }) => {\n setProcessingState(stage)\n setChangeSummary(message)\n },\n })\n\n setProcessingState('completed')\n } catch (err) {\n console.error('Error applying suggestions:', err)\n setError(err instanceof Error ? err.message : 'An unexpected error occurred')\n setProcessingState('error')\n }\n }\n\n if (loading) {\n return <ProgressPanel title=\"Loading translation metadata\" />\n }\n\n // Show analyzing state\n if (processingState === 'analyzing') {\n return <ProgressPanel title=\"Analyzing feedback with AI...\" description={changeSummary} />\n }\n\n // Show review state\n if (processingState === 'reviewing') {\n return (\n <Stack space={4}>\n <Stack space={4}>\n <Heading>Review Suggestions</Heading>\n <Text muted>\n The AI has analyzed your feedback and generated the following suggestions. Review and edit them before\n applying.\n </Text>\n </Stack>\n\n <SuggestionReviewList\n suggestions={suggestions}\n onSuggestionsChange={setSuggestions}\n sourceLanguage={sourceLanguage}\n targetLanguage={targetLanguage}\n />\n\n {error && (\n <Card padding={3} tone=\"critical\" border>\n <Text size={1}>{error}</Text>\n </Card>\n )}\n\n <Flex justify=\"space-between\" gap={2} paddingTop={2}>\n <Flex gap={2}>\n <Button\n type=\"button\"\n mode=\"bleed\"\n tone=\"neutral\"\n onClick={() => setProcessingState('idle')}\n icon={<ArrowLeftIcon />}\n text=\"Back\"\n />\n </Flex>\n <Button\n type=\"button\"\n mode=\"ghost\"\n tone=\"positive\"\n size={1}\n onClick={handleApplySuggestions}\n disabled={suggestions.filter((s) => s.approved).length === 0}\n >\n Apply {suggestions.filter((s) => s.approved).length} Suggestion\n {suggestions.filter((s) => s.approved).length !== 1 ? 's' : ''}\n </Button>\n </Flex>\n </Stack>\n )\n }\n\n // Show applying state\n if (processingState === 'applying') {\n return <ProgressPanel title=\"Updating translation settings...\" description={changeSummary} icon={<EditIcon />} />\n }\n\n // Show completion state\n if (processingState === 'completed') {\n const appliedCount = suggestions.filter((s) => s.approved).length\n const description = `${appliedCount} suggestion${appliedCount !== 1 ? 's' : ''} applied. Translation prompts and glossary havebeen updated based on your feedback.`\n return (\n <Box padding={4}>\n <ProgressPanel title=\"Translation settings updated\" description={description} status=\"completed\" />\n\n <Flex justify=\"flex-end\" paddingTop={3}>\n <Button type=\"button\" mode=\"ghost\" tone=\"primary\" size={1} onClick={handleCancel}>\n Close\n </Button>\n </Flex>\n </Box>\n )\n }\n\n // Show error state\n if (processingState === 'error') {\n return (\n <Box padding={4}>\n <Stack space={4}>\n <ProgressPanel status=\"error\" title=\"Error processing feedback\" description={error} tone=\"critical\" />\n <Flex justify=\"flex-end\" gap={2}>\n <Button type=\"button\" mode=\"ghost\" onClick={handleCancel}>\n Close\n </Button>\n <Button type=\"reset\" mode=\"ghost\" tone=\"primary\" onClick={() => setProcessingState('idle')}>\n Reset\n </Button>\n </Flex>\n </Stack>\n </Box>\n )\n }\n\n // Default form view\n return (\n <Stack space={4}>\n <Card tone=\"transparent\" padding={4}>\n <Stack space={4}>\n <Stack space={2}>\n <Stack space={4}>\n <Inline space={2}>\n <Text size={2} muted>\n Enter your feedback for the translation AI agent.\n </Text>\n <Tooltip\n portal\n content={\n <Box padding={2} style={{ maxWidth: '280px' }}>\n <Text size={1}>\n The AI agent will use your feedback to improve translation prompts, update protected phrases,\n and modify the glossary. Be specific about what should be changed.\n </Text>\n </Box>\n }\n >\n <Text size={1}>\n <InfoOutlineIcon />\n </Text>\n </Tooltip>\n </Inline>\n <TextArea\n rows={8}\n fontSize={2}\n onChange={(event) => setFeedback(event.currentTarget.value)}\n padding={3}\n placeholder='Example: \"Braze\" is a branded term and should not be translated.'\n value={feedback}\n />\n </Stack>\n </Stack>\n\n <Stack space={2}>\n <Stack space={3}>\n <Button\n type=\"button\"\n mode={shouldIncludeEdits ? 'ghost' : 'bleed'}\n size={2}\n onClick={handleShouldIncludeEditsChange}\n tone={shouldIncludeEdits ? 'primary' : 'default'}\n >\n <Flex align=\"flex-start\" gap={3}>\n <Box paddingTop={1}>\n <Checkbox checked={shouldIncludeEdits} readOnly style={{ pointerEvents: 'none' }} />\n </Box>\n <Stack space={2} flex={1}>\n <Text size={2} weight=\"medium\">\n Include Edits in Feedback\n </Text>\n <Text size={1} muted style={{ textAlign: 'left' }}>\n The AI will analyze edits you made to the translation.\n </Text>\n </Stack>\n </Flex>\n </Button>\n </Stack>\n </Stack>\n </Stack>\n </Card>\n <Flex justify=\"flex-end\" gap={2} paddingTop={2}>\n <Button type=\"button\" mode=\"ghost\" size={1} onClick={handleCancel}>\n Cancel\n </Button>\n <Button\n type=\"submit\"\n mode=\"ghost\"\n tone=\"positive\"\n size={1}\n onClick={handleAnalyze}\n disabled={!feedback.trim() && !shouldIncludeEdits}\n >\n Analyze Feedback\n </Button>\n </Flex>\n </Stack>\n )\n}\n","export interface ValidationResult {\n valid: boolean\n error?: string\n modelAvailable?: string[]\n}\n\n/**\n * Validates an OpenAI API key by making a minimal API call.\n * Checks if the key is valid and if the requested model is available.\n */\nexport const validateOpenAIApiKey = async (apiKey: string, model = 'gpt-5'): Promise<ValidationResult> => {\n try {\n // Make a minimal API call to validate the key\n const response = await fetch('https://api.openai.com/v1/models', {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n },\n })\n\n if (!response.ok) {\n const error = await response.json()\n return {\n valid: false,\n error: error.error?.message || 'Invalid API key',\n }\n }\n\n const data = await response.json()\n const availableModels = data.data.map((m: { id: string }) => m.id)\n\n // Check if requested model is available\n if (!availableModels.includes(model)) {\n return {\n valid: false,\n error: `Model ${model} not available. Try: ${availableModels.slice(0, 5).join(', ')}`,\n }\n }\n\n return {\n valid: true,\n modelAvailable: availableModels,\n }\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : 'Network error',\n }\n }\n}\n","import defaults from 'lodash.defaults'\nimport type { TranslationAction, TranslationContext } from '../../types'\nimport { devLog } from '../../utils/common/devLog'\n\nexport const translationReducer = (\n context: Omit<TranslationContext, 'dispatch'>,\n action: TranslationAction,\n): Omit<TranslationContext, 'dispatch'> => {\n const logContext = {\n documentId: context.document?._id,\n actionType: action.type,\n component: 'translationReducer',\n }\n\n devLog.debug('Translation action dispatched', {\n ...logContext,\n payload: action.payload,\n })\n\n switch (action.type) {\n case 'SET_OVERALL_STATUS':\n return { ...context, overallStatus: action.payload }\n\n case 'SET_TRANSLATION_METADATA':\n return { ...context, translationMetadata: action.payload }\n\n case 'SET_SELECTED_TARGET_LANGUAGES':\n return {\n ...context,\n selectedTargetLanguages: action.payload,\n }\n\n case 'SET_TRANSLATION_MODE':\n return {\n ...context,\n translationMode: action.payload,\n }\n\n case 'SET_OPENAI_VALIDATED':\n return {\n ...context,\n openaiValidated: action.payload,\n }\n\n case 'SET_SHOULD_TRANSLATE_REFERENCES':\n return {\n ...context,\n shouldTranslateReferences: action.payload,\n }\n\n case 'SET_MAX_REFERENCE_DEPTH':\n return {\n ...context,\n maxReferenceDepth: action.payload,\n }\n\n case 'SET_SHOULD_OVERWRITE_FRESH_TRANSLATIONS':\n return {\n ...context,\n shouldOverwriteFreshTranslations: action.payload,\n }\n\n case 'SET_REFERENCES':\n return {\n ...context,\n references: action.payload,\n }\n\n case 'SET_TRANSLATION_JOB_STATUS': {\n // Create a unique key for each job using both document ID and language\n // This ensures concurrent translations to different languages don't conflict\n const jobKey = `${action.payload._id}_${action.payload.language}`\n\n // Create a new Map to trigger React re-render (immutability)\n const newTranslationJobs = new Map(context.translationJobs)\n\n const existingJob = newTranslationJobs.get(jobKey)\n const mergedJob = defaults(action.payload, existingJob)\n\n newTranslationJobs.set(jobKey, mergedJob)\n\n return {\n ...context,\n translationJobs: newTranslationJobs,\n }\n }\n\n default:\n return context\n }\n}\n","import { type FC, useEffect, useReducer } from 'react'\nimport type { PluginConfig, TranslatableDocument, TranslationContext as TTranslationContext } from '../../types'\nimport { devLog } from '../../utils/common/devLog'\nimport { validateOpenAIApiKey } from '../../utils/openai/validateApiKey'\nimport { TranslationContext } from './TranslationContext'\nimport { translationReducer } from './translationReducer'\n\ninterface TranslationContextProviderProps {\n document: TranslatableDocument\n config: PluginConfig\n children: React.ReactNode\n}\n\nexport const TranslationContextProvider: FC<TranslationContextProviderProps> = ({ document, config, children }) => {\n devLog.debug('TranslationContextProvider initialized', {\n documentId: document._id,\n documentType: document._type,\n supportedLanguages: config.supportedLanguages?.map((l) => l.id),\n component: 'TranslationContextProvider',\n })\n\n const initialContext: Omit<TTranslationContext, 'dispatch'> = {\n document,\n config,\n overallStatus: 'idle',\n translationMetadata: null,\n selectedTargetLanguages: [],\n shouldOverwriteFreshTranslations: false,\n shouldTranslateReferences: false,\n maxReferenceDepth: 1,\n references: [],\n translationJobs: new Map(),\n translationMode: config?.openai?.defaultMode || 'sanity-only',\n openaiValidated: false,\n }\n\n const [context, dispatch] = useReducer(translationReducer, initialContext)\n\n // Validate OpenAI API key on mount if configured\n useEffect(() => {\n const validateKey = async () => {\n if (config?.openai?.enabled && config?.openai?.apiKey) {\n try {\n const validation = await validateOpenAIApiKey(config.openai.apiKey, config.openai.model || 'gpt-5')\n\n dispatch({\n type: 'SET_OPENAI_VALIDATED',\n payload: validation.valid,\n })\n\n if (validation.valid) {\n devLog.info('OpenAI API key validated successfully', {\n model: config.openai.model || 'gpt-5',\n component: 'TranslationContextProvider',\n })\n } else {\n devLog.error('OpenAI API key validation failed', {\n error: validation.error,\n component: 'TranslationContextProvider',\n })\n }\n } catch (error) {\n devLog.error('OpenAI validation error', error instanceof Error ? error : new Error(String(error)), {\n component: 'TranslationContextProvider',\n })\n dispatch({\n type: 'SET_OPENAI_VALIDATED',\n payload: false,\n })\n }\n }\n }\n\n validateKey()\n }, [config?.openai?.apiKey, config?.openai?.enabled, config?.openai?.model])\n\n devLog.debug('Translation context state initialized', {\n documentId: document._id,\n overallStatus: context.overallStatus,\n selectedLanguages: context.selectedTargetLanguages,\n translationMode: context.translationMode,\n openaiEnabled: config?.openai?.enabled,\n component: 'TranslationContextProvider',\n })\n\n return <TranslationContext.Provider value={{ ...context, dispatch }}>{children}</TranslationContext.Provider>\n}\n","import { TranslateIcon } from '@sanity/icons'\nimport { useState } from 'react'\nimport type { DocumentActionDescription, DocumentActionProps } from 'sanity'\nimport { GiveFeedbackDialog } from './components/GiveFeedbackDialog'\nimport { TranslationContextProvider } from './hooks/useTranslationContext/TranslationContextProvider'\nimport type { PluginConfig, TranslatableDocument } from './types'\nimport { devLog } from './utils/common/devLog'\n\ninterface TranslateActionProps extends DocumentActionProps {\n config: PluginConfig\n}\n\nexport const getGiveTranslationFeedbackAction = (config: PluginConfig) => {\n return ({ draft, published }: TranslateActionProps): DocumentActionDescription | null => {\n const document = draft ?? published\n const [isDialogOpen, setIsDialogOpen] = useState(false)\n\n devLog.debug('ImproveTranslationPromptsAction initialized', {\n documentId: document?._id,\n documentType: document?._type,\n })\n\n if (!document || !config.translatableSchemaTypes?.includes(document._type)) {\n devLog.warn('Document not in translatable schema types', {\n documentType: document?._type,\n translatableTypes: config.translatableSchemaTypes,\n })\n return null\n }\n\n if (!config) {\n devLog.error('Missing config in TranslateAction')\n return null\n }\n\n return {\n label: 'Give Translation Feedback',\n icon: TranslateIcon,\n onHandle: () => {\n devLog.info('Give Translation Feedback dialog opened', {\n documentId: document._id,\n documentType: document._type,\n component: 'GiveTranslationFeedbackAction',\n })\n setIsDialogOpen(true)\n },\n dialog: isDialogOpen && {\n type: 'dialog',\n onClose: () => setIsDialogOpen(false),\n header: 'Give Translation Feedback',\n content: (\n <TranslationContextProvider document={document as TranslatableDocument} config={config}>\n <GiveFeedbackDialog setIsDialogOpen={setIsDialogOpen} />\n </TranslationContextProvider>\n ),\n },\n }\n }\n}\n","import { TranslateIcon } from '@sanity/icons'\nimport { defineField, defineType } from 'sanity'\n\nexport const translationGlossaryTerm = defineType({\n name: 'translationGlossaryTerm',\n title: 'Translation Glossary Term',\n icon: TranslateIcon,\n type: 'document',\n fields: [\n defineField({\n name: 'translations',\n title: 'Translations',\n type: 'internationalizedArrayString',\n }),\n ],\n preview: {\n select: {\n translations: 'translations',\n },\n prepare({ translations }: { translations: { locale: string; value: string }[] }) {\n const enTranslation = translations?.find((t) => t.locale === 'en-us')\n const firstTranslation = translations?.[0]\n return {\n title: enTranslation?.value || firstTranslation?.value || 'No translation available',\n }\n },\n },\n})\n","import { defineField } from 'sanity'\n\nexport const translationHistory = defineField({\n name: 'translationHistory',\n type: 'array',\n of: [\n {\n name: 'targetLanguageHistory',\n type: 'object',\n fields: [\n defineField({\n name: 'language',\n title: 'Language',\n description: 'The language of the target document',\n type: 'string',\n }),\n defineField({\n name: 'sourceId',\n title: 'Source ID',\n description: 'The ID of the source document',\n type: 'string',\n }),\n defineField({\n name: 'sourceRev',\n title: 'Source Revision',\n description: 'The revision ID of the source document at the time of translation',\n type: 'string',\n }),\n defineField({\n name: 'sourceLanguage',\n title: 'Source Language',\n description: 'The language of the source document',\n type: 'string',\n }),\n defineField({\n name: 'sourceSnapshot',\n title: 'Source Snapshot',\n description: 'The snapshot of the source document at the time of translation',\n type: 'text',\n }),\n defineField({\n name: 'targetId',\n title: 'Target ID',\n description: 'The ID of the target document',\n type: 'string',\n }),\n ],\n },\n ],\n})\n","import { TranslateIcon } from '@sanity/icons'\nimport { defineField, defineType } from 'sanity'\n\nexport const translationSettings = defineType({\n name: 'translationSettings',\n title: 'Translation Settings',\n icon: TranslateIcon,\n type: 'document',\n groups: [\n {\n name: 'general',\n title: 'General',\n default: true,\n },\n {\n name: 'history',\n title: 'History',\n },\n ],\n fields: [\n defineField({\n name: 'globalPrompt',\n title: 'Global Translation Prompt',\n description: 'A prompt that will given to the Sanity AI Agent for all translation tasks.',\n type: 'text',\n rows: 5,\n group: 'general',\n }),\n defineField({\n name: 'localePrompts',\n title: 'Locale Prompts',\n description: 'Prompts that will be given to the Sanity AI Agent for each locale.',\n type: 'internationalizedArrayText',\n group: 'general',\n }),\n defineField({\n name: 'protectedPhrases',\n title: 'Protected Phrases',\n description: 'Phrases that will never be translated.',\n type: 'array',\n of: [{ type: 'string' }],\n group: 'general',\n }),\n defineField({\n name: 'changeHistory',\n title: 'Change History',\n description: 'History of changes made via user feedback.',\n type: 'array',\n readOnly: true,\n group: 'history',\n of: [\n {\n type: 'object',\n fields: [\n defineField({\n name: 'timestamp',\n title: 'Timestamp',\n type: 'datetime',\n }),\n defineField({\n name: 'changeType',\n title: 'Change Type',\n type: 'string',\n options: {\n list: [\n { title: 'Global Prompt', value: 'globalPrompt' },\n { title: 'Locale Prompt', value: 'localePrompt' },\n { title: 'Protected Phrase', value: 'protectedPhrase' },\n { title: 'Glossary Term', value: 'glossaryTerm' },\n ],\n },\n }),\n defineField({\n name: 'description',\n title: 'Description',\n type: 'text',\n rows: 3,\n }),\n defineField({\n name: 'locale',\n title: 'Locale',\n type: 'string',\n description: 'Target locale for locale-specific changes',\n }),\n ],\n preview: {\n select: {\n timestamp: 'timestamp',\n changeType: 'changeType',\n description: 'description',\n },\n prepare({ timestamp, changeType, description }) {\n return {\n title: `${changeType} - ${new Date(timestamp).toLocaleDateString()}`,\n subtitle: description,\n }\n },\n },\n },\n ],\n }),\n ],\n preview: {\n prepare: () => ({\n title: 'Translation Settings',\n }),\n },\n})\n","/**\n * Semaphore class for rate limiting concurrent operations\n * Ensures that only a specified number of operations can run simultaneously\n */\nexport class Semaphore {\n private permits: number\n private queue: Array<() => void> = []\n\n constructor(permits: number) {\n this.permits = permits\n }\n\n /**\n * Acquire a permit from the semaphore\n * If no permits are available, the caller will wait until one becomes available\n */\n async acquire(): Promise<void> {\n if (this.permits > 0) {\n this.permits--\n return Promise.resolve()\n }\n\n // Wait for a permit to become available\n return new Promise<void>((resolve) => {\n this.queue.push(resolve)\n })\n }\n\n /**\n * Release a permit back to the semaphore\n * If there are waiting callers, one will be woken up\n */\n release(): void {\n const next = this.queue.shift()\n if (next) {\n // Give the permit to the next waiting caller\n next()\n } else {\n // No one is waiting, increment the available permits\n this.permits++\n }\n }\n\n /**\n * Execute a function with semaphore protection\n * Automatically acquires and releases the permit\n */\n async withPermit<T>(fn: () => Promise<T>): Promise<T> {\n await this.acquire()\n try {\n return await fn()\n } finally {\n this.release()\n }\n }\n\n /**\n * Get the number of available permits\n */\n availablePermits(): number {\n return this.permits\n }\n\n /**\n * Get the number of waiting callers\n */\n queueLength(): number {\n return this.queue.length\n }\n\n /**\n * Update the number of permits\n * If increasing permits, waiting callers will be notified\n * If decreasing permits below current available, available permits will be reduced\n */\n updatePermits(newPermits: number): void {\n const currentTotal = this.permits + this.queue.length\n const difference = newPermits - currentTotal\n\n if (difference > 0) {\n // Increasing permits - wake up waiting callers or add to available permits\n for (let i = 0; i < difference && this.queue.length > 0; i++) {\n const next = this.queue.shift()\n if (next) next()\n }\n // Add any remaining permits to available count\n const remainingIncrease = difference - (currentTotal - this.permits - this.queue.length)\n if (remainingIncrease > 0) {\n this.permits += remainingIncrease\n }\n } else if (difference < 0) {\n // Decreasing permits - reduce available permits (but don't interrupt running operations)\n this.permits = Math.max(0, this.permits + difference)\n }\n }\n}\n","import { type PortableTextBlock, toPlainText } from '@portabletext/react'\nimport type { TranslatableDocument } from '../../types'\n\n/**\n * Function to convert a sanity document to plain text.\n * Returns an object following these rules:\n * - All top-level fields are included.\n * - If a field is an array AND contains at least one object with `_type === 'block'`, it is converted to plain text with `toPlainText`.\n */\nexport function documentToPlainText(document?: TranslatableDocument): string {\n if (!document) return ''\n\n const result: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(document)) {\n // Check if the value is an array\n if (Array.isArray(value)) {\n // Check if the array contains at least one Portable Text block\n const hasBlockType = value.some(\n (item) => typeof item === 'object' && item !== null && '_type' in item && item._type === 'block',\n )\n\n if (hasBlockType) {\n // Convert Portable Text blocks to plain text\n result[key] = toPlainText(value as PortableTextBlock[])\n } else {\n // Keep the array as-is\n result[key] = value\n }\n } else {\n // Keep non-array values as-is\n result[key] = value\n }\n }\n\n return JSON.stringify(result)\n}\n","/**\n * Fuzzy glossary-term matching against a single source document.\n *\n * Design goals:\n * - Tokenize the source ONCE, then test many glossary terms cheaply.\n * - Order-agnostic “bag of words” matching inside a sliding window.\n * - Tolerates filler words and a small number of extra words (“window slack”).\n * - Case-insensitive, punctuation-insensitive, and diacritic-insensitive.\n * - Prefers recall (false positives are more acceptable than false negatives).\n */\n\nexport type Match =\n | { matched: true; term: string; windowStart: number; windowEnd: number; matchedRatio: number }\n | { matched: false; term: string; reason: string }\n\nexport interface MatchOptions {\n /**\n * Extra tokens allowed on top of the term length for the sliding window.\n * e.g., term has 3 tokens → default window size = 3 + windowSlack.\n * Larger slack → more forgiving (higher recall, lower precision).\n */\n windowSlack?: number\n\n /**\n * Ratio [0..1] of term tokens that must be satisfied inside a window.\n * 1.0 == all term tokens present (default).\n * 0.8 == at least 80% of term tokens present (useful for partials/variants).\n */\n minTokenMatchRatio?: number\n\n /**\n * Stop/filler words to ignore in term tokenization (e.g., “a”, “the”, “to”).\n * These are removed from the term ONLY (not from the source tokens),\n * so that “navigate using a map” still matches “navigate using the digital map”.\n */\n stopwords?: string[]\n\n /**\n * If true, we’ll also drop very short tokens from the term (length <= 2),\n * after stopword removal. Helps avoid spurious matches on tiny tokens.\n */\n dropVeryShortTermTokens?: boolean\n\n /**\n * If provided, only these characters are considered word chars in tokenization.\n * By default we use a broad “letters/digits/underscore” definition.\n */\n allowedWordCharsRegex?: RegExp\n}\n\nexport interface SourceContext {\n /** Tokenized, normalized source text. */\n tokens: string[]\n}\n\n/** Reasonable defaults tuned for translation glossary recall. */\nconst DEFAULTS: Required<MatchOptions> = {\n windowSlack: 2,\n minTokenMatchRatio: 1.0,\n stopwords: [\n // common English fillers — extend per your locales\n 'a',\n 'an',\n 'the',\n 'and',\n 'or',\n 'to',\n 'of',\n 'for',\n 'in',\n 'on',\n 'at',\n 'by',\n 'with',\n 'using',\n 'from',\n 'into',\n 'over',\n 'under',\n 'as',\n 'is',\n 'are',\n 'be',\n ],\n dropVeryShortTermTokens: true,\n allowedWordCharsRegex: /[\\p{L}\\p{N}_]+/gu, // letters/numbers/underscore (Unicode)\n}\n\n/** Normalize to improve matching across punctuation/case/diacritics. */\nfunction normalizeForMatch(s: string): string {\n // 1) Lowercase\n // 2) Unicode NFKD + strip diacritics (combining marks)\n // 3) Trim\n return s\n .toLowerCase()\n .normalize('NFKD')\n .replace(/\\p{M}+/gu, '') // remove diacritic marks\n .trim()\n}\n\n/** Tokenize a string into normalized word tokens. */\nexport function tokenize(text: string, allowedWordChars = DEFAULTS.allowedWordCharsRegex): string[] {\n const norm = normalizeForMatch(text)\n // Extract “words” per allowedWordChars; fallback to split on whitespace\n const matches = norm.match(allowedWordChars)\n if (!matches) return []\n return matches\n}\n\n/** Build a SourceContext once for a given document. */\nexport function preprocessSource(\n sourceText: string,\n opts?: Pick<MatchOptions, 'allowedWordCharsRegex'>,\n): SourceContext {\n const tokens = tokenize(sourceText, opts?.allowedWordCharsRegex ?? DEFAULTS.allowedWordCharsRegex)\n return { tokens }\n}\n\n/** Prepare a term: tokenize, remove stopwords, optionally drop very short tokens. */\nfunction prepareTermTokens(term: string, opts: Required<MatchOptions>): string[] {\n const raw = tokenize(term, opts.allowedWordCharsRegex)\n const filtered = raw.filter((t) => !opts.stopwords.includes(t))\n if (!opts.dropVeryShortTermTokens) return filtered\n return filtered.filter((t) => t.length > 2)\n}\n\n/** Build a frequency map for tokens. */\nfunction freqMap(tokens: string[]): Map<string, number> {\n const m = new Map<string, number>()\n for (const t of tokens) m.set(t, (m.get(t) ?? 0) + 1)\n return m\n}\n\n/**\n * Core matcher: bag-of-words inside a sliding window.\n * We only count tokens that appear in the (prepared) term.\n *\n * This function can be used for matching glossary terms, protected phrases,\n * and other text patterns within documents.\n */\nexport function matchTermInDocument(term: string, source: SourceContext, options?: MatchOptions): Match {\n const opts: Required<MatchOptions> = { ...DEFAULTS, ...(options ?? {}) }\n\n const termTokens = prepareTermTokens(term, opts)\n if (termTokens.length === 0) {\n return { matched: false, term, reason: 'No substantive tokens after filtering.' }\n }\n\n const termFreq = freqMap(termTokens)\n const requiredCount = Math.ceil(termTokens.length * opts.minTokenMatchRatio)\n const windowSize = Math.max(1, termTokens.length + opts.windowSlack)\n\n // Set of tokens we care about for this term (to keep window counts small).\n const interesting = new Set(termFreq.keys())\n\n // Sliding window counts ONLY for tokens that are in termFreq.\n const windowCounts = new Map<string, number>()\n let satisfiedCount = 0 // sum over tokens: min(windowCounts[token], termFreq[token])\n\n // Helper to add a token to the window (if interesting), updating satisfiedCount.\n const addToken = (tok: string) => {\n if (!interesting.has(tok)) return\n const before = windowCounts.get(tok) ?? 0\n const after = before + 1\n windowCounts.set(tok, after)\n // biome-ignore lint/style/noNonNullAssertion: this is safe\n const cap = termFreq.get(tok)!\n // Only increases satisfiedCount until we reach the term-required cap for this token.\n if (before < cap) satisfiedCount += 1\n }\n\n // Helper to remove a token from the window, updating satisfiedCount.\n const removeToken = (tok: string) => {\n if (!interesting.has(tok)) return\n const before = windowCounts.get(tok) ?? 0\n if (before === 0) return\n const after = before - 1\n windowCounts.set(tok, after)\n\n // biome-ignore lint/style/noNonNullAssertion: this is safe\n const cap = termFreq.get(tok)!\n // If we were contributing to satisfiedCount (i.e., before <= cap), decrement when we drop below cap.\n if (before <= cap) satisfiedCount -= 1\n }\n\n const { tokens } = source\n // Prime the first window\n for (let i = 0; i < Math.min(windowSize, tokens.length); i++) addToken(tokens[i])\n\n if (satisfiedCount >= requiredCount) {\n return {\n matched: true,\n term,\n windowStart: 0,\n windowEnd: Math.min(windowSize, tokens.length) - 1,\n matchedRatio: satisfiedCount / termTokens.length,\n }\n }\n\n // Slide the window across the source\n for (let start = 1; start + windowSize <= tokens.length; start++) {\n const prev = tokens[start - 1]\n const next = tokens[start + windowSize - 1]\n removeToken(prev)\n addToken(next)\n\n if (satisfiedCount >= requiredCount) {\n return {\n matched: true,\n term,\n windowStart: start,\n windowEnd: start + windowSize - 1,\n matchedRatio: satisfiedCount / termTokens.length,\n }\n }\n }\n\n return { matched: false, term, reason: 'No window satisfied the token threshold.' }\n}\n\n/**\n * Batch check: evaluate many terms against the same preprocessed source.\n * Returns match results in the same order as input terms.\n * Can be used for glossary terms, protected phrases, or other text patterns.\n */\nexport function matchAllTermsInDocument(terms: string[], source: SourceContext, options?: MatchOptions): Match[] {\n return terms.map((t) => matchTermInDocument(t, source, options))\n}\n\n/**\n * @deprecated Use matchTermInDocument instead\n */\nexport const matchGlossaryTerm = matchTermInDocument\n\n/**\n * @deprecated Use matchAllTermsInDocument instead\n */\nexport const matchAllGlossaryTerms = matchAllTermsInDocument\n\n/* =========================\n Example usage (pseudo):\n =========================\n\nconst sourceText = `\n You can navigate using the digital map, or rely on landmarks.\n Turn-by-turn directions are available.\n`;\n\nconst source = preprocessSource(sourceText);\n\nconst terms = [\n \"navigate using a map\",\n \"turn by turn directions\",\n \"offline routing\",\n];\n\nconst results = matchAllTermsInDocument(terms, source, {\n windowSlack: 3,\n minTokenMatchRatio: 1.0, // require all term tokens present (after filtering)\n stopwords: [...DEFAULTS.stopwords, \"a\"], // extend if desired\n});\n\nconsole.log(results);\n\n*/\n","import type { SanityClient } from 'sanity'\nimport type { TranslatableDocument, TranslationGlossaryTermDocument, TranslationSettingsDocument } from '../../types'\nimport { documentToPlainText } from '../document/documentToPlainText'\nimport { matchTermInDocument, preprocessSource } from './matchTermInDocument'\n\ninterface FetchTranslationPromptsOptions {\n client: SanityClient\n sourceLanguage?: string\n sourceDocument?: TranslatableDocument\n targetLanguages: Array<string>\n}\n\nexport interface FetchTranslationPromptsResult {\n globalPrompt?: string\n protectedPhrases?: Array<string>\n localeInstructions: Record<\n string,\n {\n prompt?: string\n glossaryTerms?: string\n }\n >\n}\n\nexport const fetchTranslationPrompts = async ({\n client,\n sourceLanguage,\n sourceDocument,\n targetLanguages,\n}: FetchTranslationPromptsOptions): Promise<FetchTranslationPromptsResult> => {\n const sourceDocumentString = documentToPlainText(sourceDocument)\n\n const translationSettings = await client.fetch<TranslationSettingsDocument | null>(\n `*[_id == \"translation-settings\"][0] {\n globalPrompt,\n localePrompts[locale in $targetLanguages],\n protectedPhrases\n }`,\n { targetLanguages },\n )\n\n // Fetch all translation glossary terms that have a translation in both the source and target languages\n const translationGlossaryTerms =\n (await client.fetch<Array<NonNullable<TranslationGlossaryTermDocument['translations']>> | null>(\n `*[\n _type == \"translationGlossaryTerm\"\n && count(translations[_key in $targetLanguages]) > 0\n && count(translations[_key == $sourceLanguage]) > 0\n ].translations`,\n { targetLanguages, sourceLanguage },\n )) || []\n\n const glossaryTermsByLocale: Record<string, Array<{ from: string; to: string }>> = {}\n\n for (const targetLanguage of targetLanguages) {\n const relevantTerms = translationGlossaryTerms.filter((termTranslations) =>\n termTranslations.some((translation) => translation._key === targetLanguage),\n )\n\n interface GlossaryTermMapping {\n from: string\n to: string\n }\n\n let relevantTranslations = relevantTerms\n .map((termTranslations): GlossaryTermMapping | null => {\n const from = termTranslations.find((translation) => translation._key === sourceLanguage)?.value\n const to = termTranslations.find((translation) => translation._key === targetLanguage)?.value\n if (!from || !to) {\n return null\n }\n return { from, to }\n })\n .filter((item): item is GlossaryTermMapping => item !== null)\n\n // Filter to only include terms that appear in the source document\n if (sourceDocument) {\n relevantTranslations = relevantTranslations.filter(({ from }) =>\n glossaryTermAppearsInDocument(from, sourceDocumentString),\n )\n }\n\n glossaryTermsByLocale[targetLanguage] = relevantTranslations\n }\n\n // Filter protected phrases to only include those that appear in the source document\n let filteredProtectedPhrases = translationSettings?.protectedPhrases\n if (sourceDocument && filteredProtectedPhrases) {\n filteredProtectedPhrases = filteredProtectedPhrases.filter((phrase) =>\n protectedPhraseAppearsInDocument(phrase, sourceDocumentString),\n )\n }\n\n return {\n globalPrompt: translationSettings?.globalPrompt,\n protectedPhrases: filteredProtectedPhrases,\n localeInstructions: Object.fromEntries(\n targetLanguages.map((locale) => [\n locale,\n {\n prompt: translationSettings?.localePrompts?.find((lp) => lp._key === locale)?.value,\n glossaryTerms: formatGlossaryTermInstruction(glossaryTermsByLocale[locale]),\n },\n ]),\n ),\n }\n}\n\n/**\n * Checks if a glossary term appears anywhere in the document content using fuzzy matching.\n * Uses order-agnostic bag-of-words matching.\n */\nconst glossaryTermAppearsInDocument = (term: string, documentString: string): boolean => {\n // Handle colon-delimited terms by checking the base term (before the colon)\n const baseTerm = term.split(':')[0].trim()\n\n // Preprocess the document string once for efficient matching\n const source = preprocessSource(documentString)\n\n // Match the term against the document with default options\n // (windowSlack: 10, minTokenMatchRatio: 1.0, stopwords included)\n const result = matchTermInDocument(baseTerm, source, {\n windowSlack: 10,\n })\n\n return result.matched\n}\n\n/**\n * Checks if a protected phrase appears anywhere in the document content using fuzzy matching.\n * Uses order-agnostic bag-of-words matching.\n */\nconst protectedPhraseAppearsInDocument = (phrase: string, documentString: string): boolean => {\n // Preprocess the document string once for efficient matching\n const source = preprocessSource(documentString)\n\n // Match the phrase against the document with the same options as glossary terms\n const result = matchTermInDocument(phrase, source, {\n windowSlack: 10,\n })\n\n return result.matched\n}\n\nconst formatGlossaryTermInstruction = (terms: Array<{ from: string; to: string }> | undefined) => {\n if (!terms || terms.length === 0) {\n return undefined\n }\n\n const instructions = [\n '## Glossary Terms',\n 'Use the translations below whenever the matching source term appears. Treat these mappings as required terminology.',\n 'Guidelines:',\n '- Apply glossary terms when the meaning matches, even if there are slight variations in wording, punctuation, or word order.',\n '- Preserve surrounding punctuation and keep the sentence natural.',\n '- Match the source casing when possible; only adjust when required for correct grammar.',\n '- If the source term meaning is absent, do not introduce a translation from this list.',\n '- When a term includes a colon, translate the text before the colon for general use; use the text after the colon when the feminine form is required.',\n '- Do not remove important words or phrases from the source text when they are essential to the meaning.',\n '- Never add punctuation or symbols that are not present in the source text.',\n '- Never apply glossary terms in code examples.',\n ]\n\n terms.forEach(({ from, to }) => {\n instructions.push(\n `When text conveys the same meaning as \"${from}\", translate it as \"${to}\" unless the term is used in a code example.`,\n )\n })\n\n return instructions.join('\\n')\n}\n","export type RecursiveSearchable =\n | null\n | undefined\n | string\n | number\n | boolean\n | RecursiveSearchable[]\n | { [key: string]: RecursiveSearchable }\n\nexport type FindKeyValueResult<T = unknown> = {\n value: T\n path: Array<string | number>\n}\n\n/**\n * Recursively searches through an object/array structure to find all values\n * associated with a specific key name, along with their paths.\n *\n * @param obj - The object or array to search through\n * @param targetKey - The key name to search for\n * @returns An array of objects containing the value and path for each occurrence of the target key\n */\nexport const findKeyValues = <T = unknown>(obj: RecursiveSearchable, targetKey: string): FindKeyValueResult<T>[] => {\n const results: FindKeyValueResult<T>[] = []\n\n const recursiveSearch = (current: RecursiveSearchable, currentPath: Array<string | number>): void => {\n // Handle null/undefined\n if (current === null || current === undefined) {\n return\n }\n\n // If current item is an array, search each element\n if (Array.isArray(current)) {\n current.forEach((item, index) => {\n recursiveSearch(item, [...currentPath, index])\n })\n return\n }\n\n // If current item is an object\n if (typeof current === 'object') {\n for (const [key, value] of Object.entries(current)) {\n const newPath = [...currentPath, key]\n\n // If we found the target key, add its value and path to our results\n if (key === targetKey) {\n results.push({\n value: value as T,\n path: newPath,\n })\n }\n\n // Continue searching recursively\n recursiveSearch(value, newPath)\n }\n }\n }\n\n recursiveSearch(obj, [])\n return results\n}\n","import { pathToString, type SanityClient } from 'sanity'\nimport { TRANSLATION_METADATA_TYPE } from '../../consts'\nimport type { PluginConfig } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { type FindKeyValueResult, findKeyValues } from '../common/findKeyValues'\nimport { undraftId } from '../document/undraftId'\n\ninterface LocalizeReferenceParams {\n client: SanityClient\n documentId: string\n languageFieldName: PluginConfig['languageFieldName']\n}\n\n/**\n * Localizes all references in a translated document to point to the appropriate\n * localized versions of referenced documents.\n *\n * This function:\n * 1. Fetches the document and extracts its target language\n * 2. Finds all `_ref` fields within the document\n * 3. Batch fetches translation IDs for all references\n * 4. Updates references to point to localized versions\n * 5. Sets weak references with strengthenOnPublish behavior\n *\n * @param params - Configuration for localizing references\n * @param params.client - Sanity client instance\n * @param params.documentId - ID of the document whose references should be localized\n * @param params.languageFieldName - Name of the field containing the language identifier\n *\n * @example\n * ```typescript\n * await localizeReferences({\n * client,\n * documentId: 'post-es',\n * languageFieldName: 'language'\n * })\n * // Updates all references in post-es to point to Spanish versions\n * ```\n */\nexport const localizeReferences = async ({ client, documentId, languageFieldName }: LocalizeReferenceParams) => {\n try {\n const doc = await client.fetch('*[_id == $documentId][0]', { documentId })\n\n if (!doc) {\n devLog.error('Document not found', new Error('Document not found'), {\n documentId,\n component: 'localizeReferences',\n })\n return\n }\n\n const targetLanguage = doc[languageFieldName]\n\n if (!targetLanguage) {\n devLog.error('No value found for language field', new Error('Missing language field'), {\n documentId,\n languageFieldName,\n component: 'localizeReferences',\n })\n return\n }\n\n const refs = findKeyValues<string>(doc, '_ref')\n\n if (!refs?.length) {\n return\n }\n\n await updateReferences(documentId, client, refs, targetLanguage)\n } catch (error) {\n devLog.error('Error localizing references', error instanceof Error ? error : new Error(String(error)), {\n documentId,\n component: 'localizeReferences',\n })\n }\n}\n\n/**\n * Updates document references to their localized versions in a single batch operation.\n *\n * This function performs efficient batch processing:\n * 1. Batch fetches all translation IDs in one query (avoids N+1 problem)\n * 2. Builds a translation map for quick lookup\n * 3. Applies all patches in a single commit\n *\n * References are set as weak with `_strengthenOnPublish` to ensure they become\n * strong references when the document is published.\n *\n * @param documentId - ID of the document to update\n * @param client - Sanity client instance\n * @param refs - Array of reference results from findKeyValues\n * @param targetLanguage - Target language to find localized references for\n *\n * @example\n * ```typescript\n * const refs = findKeyValues(document, '_ref')\n * await updateReferences('doc-es', client, refs, 'es')\n * // All references in doc-es now point to Spanish versions\n * ```\n */\nexport const updateReferences = async (\n documentId: string,\n client: SanityClient,\n refs: Array<FindKeyValueResult<string>>,\n targetLanguage: string,\n) => {\n if (!refs?.length) {\n return []\n }\n\n // Step 1: Batch fetch all reference translations\n const refIds = refs.map((r) => undraftId(r.value))\n const translations = await client.fetch<Array<{ sourceId: string; targetId: string }>>(\n `*[_type == $translationMetadataSchemaType && count(translations[value._ref in $refIds]) > 0]\n .translations[value._ref in $refIds]{\n \"sourceId\": value._ref,\n \"targetId\": ^.translations[_key == $targetLanguage][0].value._ref\n }[defined(targetId)]`,\n {\n translationMetadataSchemaType: TRANSLATION_METADATA_TYPE,\n refIds,\n targetLanguage,\n },\n )\n\n // Step 2: Build translation map\n const translationMap = new Map<string, string>()\n for (const t of translations) {\n if (t.sourceId && t.targetId) {\n translationMap.set(t.sourceId, t.targetId)\n }\n }\n\n // Step 3: Apply all patches in a single commit\n const patch = client.patch(documentId)\n let hasChanges = false\n\n for (const ref of refs) {\n const sourceId = undraftId(ref.value)\n const newId = translationMap.get(sourceId)\n\n if (!newId) {\n // No matching translation found for the reference. The document might not have been translated yet\n // or might not be of a translatable schema type.\n continue\n }\n\n patch.set({ [pathToString(ref.path)]: undraftId(newId) })\n patch.set({ [`${pathToString(ref.path.slice(0, -1))}._weak`]: true })\n patch.set({\n [`${pathToString(ref.path.slice(0, -1))}._strengthenOnPublish`]: {\n _type: 'reference',\n _weak: false,\n },\n })\n hasChanges = true\n }\n\n if (hasChanges) {\n await patch.commit()\n devLog.info('Updated references in batch', {\n documentId,\n count: refs.length,\n translatedCount: translationMap.size,\n component: 'updateReferences',\n })\n }\n}\n","import type { Dispatch } from 'react'\nimport type { SetTranslationJobStatusAction, TranslationAction } from '../../types'\n\nexport const setupTranslationJobStatuses = (\n dispatch: Dispatch<TranslationAction>,\n sourceDocuments: Array<{\n _id: string\n _type: string\n title?: string\n }>,\n targetLanguages: Array<string>,\n) => {\n for (const sourceDocument of sourceDocuments) {\n for (const lang of targetLanguages) {\n const payload: SetTranslationJobStatusAction['payload'] = {\n _id: sourceDocument._id,\n _type: sourceDocument._type,\n language: lang,\n status: 'queued',\n title: sourceDocument.title,\n }\n\n dispatch({\n type: 'SET_TRANSLATION_JOB_STATUS',\n payload,\n })\n }\n }\n}\n","import type { PluginConfig, TranslationMetadataDocument } from '../../types'\n\nexport const getExistingTranslationId = (\n translationMetadata: TranslationMetadataDocument,\n targetLanguage: PluginConfig['supportedLanguages'][number]['id'],\n): string | null => {\n if (!translationMetadata?.translations?.length) {\n return null\n }\n\n const existingTranslation = translationMetadata.translations.find(\n (translation) => translation._key === targetLanguage,\n )\n\n return existingTranslation?.value._ref || null\n}\n","import type { SanityClient } from 'sanity'\nimport { TRANSLATION_METADATA_TYPE } from '../../consts'\nimport type { TranslatableDocument, TranslationMetadataDocument } from '../../types'\nimport { undraftId } from '../document/undraftId'\n\ninterface PrepareTranslationMetadataParams {\n client: SanityClient\n sourceDocument: TranslatableDocument\n sourceLanguage: string\n}\n\nexport const getTranslationMetadata = async ({\n client,\n sourceDocument,\n sourceLanguage,\n}: PrepareTranslationMetadataParams): Promise<TranslationMetadataDocument> => {\n try {\n const existingDoc = await client.fetch<TranslationMetadataDocument | null>(\n '*[_type == $tmType && references($sourceDocumentId)][0]',\n {\n tmType: TRANSLATION_METADATA_TYPE,\n sourceDocumentId: undraftId(sourceDocument._id),\n },\n )\n\n if (existingDoc) {\n return existingDoc\n }\n\n const newDoc = (await client.create({\n _type: TRANSLATION_METADATA_TYPE,\n translations: [\n {\n _key: sourceLanguage,\n _type: 'internationalizedArrayReferenceValue',\n value: {\n _type: 'reference',\n _ref: undraftId(sourceDocument._id),\n _weak: true,\n _strengthenOnPublish: {\n _type: sourceDocument._type,\n _weak: false,\n },\n },\n },\n ],\n translationHistory: [],\n })) as TranslationMetadataDocument\n\n return newDoc\n } catch (error) {\n console.error(sourceDocument._id, 'Error creating translation metadata:', error)\n throw error\n }\n}\n","import { getPublishedId, type SanityClient, type SanityDocument } from 'sanity'\nimport type { TranslationMetadataDocument } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { undraftId } from '../document/undraftId'\n\ninterface UpdateTranslationMetadataParams {\n client: SanityClient\n sourceDocument: SanityDocument\n sourceLanguage: string\n newDocument: SanityDocument\n targetLanguage: string\n translationMetadata: TranslationMetadataDocument\n isNewTranslation: boolean\n}\n\nexport const updateTranslationMetadata = async ({\n client,\n targetLanguage,\n sourceDocument,\n sourceLanguage,\n newDocument,\n translationMetadata,\n isNewTranslation,\n}: UpdateTranslationMetadataParams) => {\n try {\n const refToNewTranslation: TranslationMetadataDocument['translations'][number] = {\n _key: targetLanguage,\n _type: 'internationalizedArrayReferenceValue',\n value: {\n _type: 'reference',\n _ref: getPublishedId(newDocument._id),\n _weak: true,\n _strengthenOnPublish: {\n _type: 'reference',\n _weak: false,\n },\n },\n }\n\n const newTranslationHistory: TranslationMetadataDocument['translationHistory'][number] = {\n _key: targetLanguage,\n language: targetLanguage,\n sourceId: getPublishedId(sourceDocument._id),\n sourceRev: sourceDocument._rev,\n sourceLanguage,\n sourceSnapshot: JSON.stringify(sourceDocument),\n targetId: newDocument._id,\n targetRev: newDocument._rev,\n targetSnapshot: JSON.stringify(newDocument),\n }\n\n const translationPatch = client\n .patch(translationMetadata._id)\n // If there is no value set for `translations`, set it to an empty array\n .setIfMissing({ translations: [] })\n\n if (isNewTranslation) {\n await translationPatch.insert('after', 'translations[-1]', [refToNewTranslation]).commit()\n } else {\n await translationPatch\n .insert('replace', `translations[_key == \"${targetLanguage}\"]`, [refToNewTranslation])\n .commit()\n }\n\n const historyPatch = client.patch(translationMetadata._id).setIfMissing({ translationHistory: [] })\n const translationHistoryExists = !!translationMetadata.translationHistory?.some(\n (history) => history.language === targetLanguage,\n )\n\n if (!translationHistoryExists) {\n await historyPatch.insert('after', 'translationHistory[-1]', [newTranslationHistory]).commit()\n } else {\n await historyPatch\n .insert('replace', `translationHistory[_key == \"${targetLanguage}\"]`, [newTranslationHistory])\n .commit()\n }\n } catch (error) {\n devLog.error('Error updating translation metadata', error instanceof Error ? error : new Error(String(error)), {\n sourceDocumentId: sourceDocument._id,\n targetLanguage,\n newDocumentId: newDocument._id,\n component: 'updateTranslationMetadata',\n })\n throw error\n }\n}\n","import defaults from 'lodash.defaults'\nimport type { Dispatch } from 'react'\nimport type { SetTranslationJobStatusAction, TranslationAction, TranslationJob } from '../../types'\n\nexport const setJobStatus = (\n dispatch: Dispatch<TranslationAction>,\n _id: string,\n language: string,\n status: TranslationJob['status'],\n payload?: Partial<SetTranslationJobStatusAction['payload']>,\n) => {\n dispatch({\n type: 'SET_TRANSLATION_JOB_STATUS',\n payload: defaults(payload, {\n _id,\n language,\n status,\n }),\n })\n}\n","export interface GPT5Request {\n messages: Array<{ role: 'developer' | 'user' | 'assistant'; content: string }>\n model?: string\n temperature?: number\n reasoningEffort?: 'low' | 'medium' | 'high'\n}\n\nexport interface GPT5Response {\n content: string\n tokensUsed: {\n prompt: number\n completion: number\n reasoning: number\n total: number\n }\n cost: number\n}\n\n/**\n * Calls the OpenAI API with the given request parameters.\n * GPT-5 uses the new /v1/responses endpoint with reasoning support.\n * GPT-4 models use the standard /v1/chat/completions endpoint.\n */\nexport const callGPT5 = async (apiKey: string, request: GPT5Request): Promise<GPT5Response> => {\n const model = request.model || 'gpt-5'\n const isGPT5 = model === 'gpt-5'\n\n // GPT-5 uses a different API endpoint with different request/response format\n if (isGPT5) {\n return await callGPT5ResponsesAPI(apiKey, request)\n }\n\n // GPT-4 and other models use the standard chat completions endpoint\n return await callChatCompletionsAPI(apiKey, request)\n}\n\n/**\n * Calls GPT-5 using the new /v1/responses endpoint.\n * This endpoint supports reasoning configuration.\n */\nconst callGPT5ResponsesAPI = async (apiKey: string, request: GPT5Request): Promise<GPT5Response> => {\n const model = request.model || 'gpt-5'\n\n // Build the request body according to the Responses API format\n const requestBody: Record<string, unknown> = {\n model,\n input: request.messages, // Uses 'input' instead of 'messages'\n }\n\n // Add reasoning configuration if specified\n if (request.reasoningEffort) {\n requestBody.reasoning = {\n effort: request.reasoningEffort,\n summary: 'auto', // Request reasoning summaries\n }\n }\n\n const response = await fetch('https://api.openai.com/v1/responses', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(requestBody),\n })\n\n if (!response.ok) {\n const error = await response.json()\n throw new Error(`GPT-5 API error: ${error.error?.message || 'Unknown error'}`)\n }\n\n const result = await response.json()\n\n // Extract token usage from the Responses API format\n const tokensUsed = {\n prompt: result.usage?.input_tokens || 0,\n completion: result.usage?.output_tokens || 0,\n reasoning: result.usage?.output_tokens_details?.reasoning_tokens || 0,\n total: result.usage?.total_tokens || 0,\n }\n\n const cost = calculateCost(tokensUsed, model)\n\n // Extract content from the output array\n // The response structure is: { output: [{ type: 'reasoning', ... }, { type: 'message', content: [{ type: 'output_text', text: '...' }] }] }\n let content = ''\n \n if (result.output && Array.isArray(result.output)) {\n // Find the message item in the output array\n const messageItem = result.output.find((item: { type: string }) => item.type === 'message') as \n | { content?: Array<{ type: string; text?: string }> }\n | undefined\n \n if (messageItem?.content && Array.isArray(messageItem.content)) {\n // Find the output_text item in the content array\n const outputTextItem = messageItem.content.find((item) => item.type === 'output_text')\n content = outputTextItem?.text || ''\n }\n }\n \n // Fallback to other possible field names if output array approach didn't work\n if (!content) {\n content = result.output_text || result.text || ''\n }\n\n return {\n content,\n tokensUsed,\n cost,\n }\n}\n\n/**\n * Calls GPT-4 and other models using the },\n 'gpt-4-turbo': {\n input: 0.01 / 1000,\n output: 0.03 / 1000,\n reasoning: 0,\n },\n 'gpt-4': {\n input: 0.03 / 1000,\n output: 0.06 / 1000,\n reasoning: 0,\n },\n }\n\n standard /v1/chat/completions endpoint.\n */\nconst callChatCompletionsAPI = async (apiKey: string, request: GPT5Request): Promise<GPT5Response> => {\n const model = request.model || 'gpt-4o'\n\n const requestBody: Record<string, unknown> = {\n model,\n messages: request.messages,\n temperature: request.temperature ?? 0.3,\n }\n\n const response = await fetch('https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(requestBody),\n })\n\n if (!response.ok) {\n const error = await response.json()\n throw new Error(`OpenAI API error: ${error.error?.message || 'Unknown error'}`)\n }\n\n const result = await response.json()\n\n const usage = result.usage || {}\n const tokensUsed = {\n prompt: usage.prompt_tokens || 0,\n completion: usage.completion_tokens || 0,\n reasoning: usage.reasoning_tokens || 0,\n total: usage.total_tokens || 0,\n }\n\n const cost = calculateCost(tokensUsed, model)\n const message = result.choices[0].message\n const messageContent = message.content || ''\n\n return {\n content: messageContent,\n tokensUsed,\n cost,\n }\n}\n\n/**\n * Calculates the cost of an OpenAI API call based on token usage.\n * Supports pricing for GPT-5, GPT-4o, GPT-4 Turbo, and GPT-4.\n */\nconst calculateCost = (tokens: { prompt: number; completion: number; reasoning: number }, model: string): number => {\n const pricing: Record<string, { input: number; output: number; reasoning: number }> = {\n 'gpt-5': {\n input: 0.002 / 1000,\n output: 0.01 / 1000,\n reasoning: 0.005 / 1000,\n },\n 'gpt-4o': {\n input: 0.005 / 1000,\n output: 0.015 / 1000,\n reasoning: 0,\n },\n 'gpt-4-turbo': {\n input: 0.01 / 1000,\n output: 0.03 / 1000,\n reasoning: 0,\n },\n 'gpt-4': {\n input: 0.03 / 1000,\n output: 0.06 / 1000,\n reasoning: 0,\n },\n }\n\n const modelPricing = pricing[model] || pricing['gpt-5']\n\n return (\n tokens.prompt * modelPricing.input +\n tokens.completion * modelPricing.output +\n tokens.reasoning * modelPricing.reasoning\n )\n}\n","import cloneDeep from 'lodash.clonedeep'\nimport get from 'lodash.get'\nimport set from 'lodash.set'\nimport type { SanityDocument } from 'sanity'\nimport { devLog } from '../common/devLog'\n\ninterface ParsedNode {\n type: 'field' | 'block' | 'span'\n name?: string\n text?: string\n attributes: Record<string, string>\n children: ParsedNode[]\n}\n\n/**\n * Deserializes a reviewed text format back into a Sanity document.\n * Only applies text changes - all structure and metadata preserved.\n */\nexport const deserializeDocument = (originalDoc: SanityDocument, reviewedText: string): SanityDocument => {\n // Parse the reviewed XML-like format\n const parsed = parseReviewFormat(reviewedText)\n\n // Deep clone original\n const result = cloneDeep(originalDoc)\n\n // Apply text changes from parsed structure\n applyTextChanges(result, parsed)\n\n return result\n}\n\nconst parseReviewFormat = (text: string): ParsedNode[] => {\n const root: ParsedNode[] = []\n const stack: ParsedNode[] = []\n\n // Parse XML-like format\n const lines = text.split('\\n')\n\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed) continue\n\n // Opening tag\n const openMatch = trimmed.match(/^<(field|block|span)([^>]*)>(.*)$/)\n if (openMatch) {\n const [, tagName, attrsStr, content] = openMatch\n const attributes = parseAttributes(attrsStr)\n\n const node: ParsedNode = {\n type: tagName as 'field' | 'block' | 'span',\n name: attributes.name,\n text: content ? unescapeXml(content) : undefined,\n attributes,\n children: [],\n }\n\n // Check if self-closing (has content on same line)\n const closingTag = `</${tagName}>`\n if (content.endsWith(closingTag.replace(/[<>]/g, ''))) {\n // Extract text before closing tag\n const textContent = content.substring(0, content.lastIndexOf('<'))\n node.text = unescapeXml(textContent)\n if (stack.length > 0) {\n const parent = stack[stack.length - 1]\n if (parent) parent.children.push(node)\n } else {\n root.push(node)\n }\n } else if (content.includes(closingTag)) {\n // Self-closing with immediate close\n node.text = unescapeXml(content.replace(closingTag, ''))\n if (stack.length > 0) {\n const parent = stack[stack.length - 1]\n if (parent) parent.children.push(node)\n } else {\n root.push(node)\n }\n } else {\n // Opening tag for container\n stack.push(node)\n }\n continue\n }\n\n // Closing tag\n const closeMatch = trimmed.match(/^<\\/(field|block|span)>$/)\n if (closeMatch && stack.length > 0) {\n const node = stack.pop()\n if (node) {\n if (stack.length > 0) {\n const parent = stack[stack.length - 1]\n if (parent) parent.children.push(node)\n } else {\n root.push(node)\n }\n }\n }\n }\n\n return root\n}\n\nconst parseAttributes = (attrStr: string): Record<string, string> => {\n const attrs: Record<string, string> = {}\n const attrRegex = /(\\w+)=\"([^\"]*)\"/g\n let match: RegExpExecArray | null = attrRegex.exec(attrStr)\n\n while (match !== null) {\n attrs[match[1]] = unescapeXml(match[2])\n match = attrRegex.exec(attrStr)\n }\n\n return attrs\n}\n\nconst unescapeXml = (str: string): string => {\n return str\n .replace(/&apos;/g, \"'\")\n .replace(/&quot;/g, '\"')\n .replace(/&gt;/g, '>')\n .replace(/&lt;/g, '<')\n .replace(/&amp;/g, '&')\n}\n\nconst applyTextChanges = (doc: Record<string, unknown>, parsedNodes: ParsedNode[]): void => {\n for (const node of parsedNodes) {\n if (node.type === 'field' && node.name) {\n const path = [node.name]\n applyFieldChanges(doc, path, node)\n }\n }\n}\n\nconst applyFieldChanges = (doc: Record<string, unknown>, path: (string | number)[], node: ParsedNode): void => {\n if (node.attributes.type === 'array') {\n // Handle array fields\n node.children.forEach((child, idx) => {\n applyFieldChanges(doc, [...path, idx], child)\n })\n return\n }\n\n if (node.attributes.type === 'object') {\n // Handle object fields\n for (const child of node.children) {\n if (child.name) {\n applyFieldChanges(doc, [...path, child.name], child)\n }\n }\n return\n }\n\n // Check for blocks (Portable Text)\n if (node.children.some((c) => c.type === 'block')) {\n const fieldValue = get(doc, path)\n if (Array.isArray(fieldValue)) {\n node.children.forEach((blockNode, idx) => {\n if (blockNode.type === 'block') {\n applyBlockChanges(fieldValue[idx], blockNode)\n }\n })\n }\n return\n }\n\n // Simple text field\n if (node.text !== undefined && !node.attributes.readonly) {\n set(doc, path, node.text)\n }\n}\n\nconst applyBlockChanges = (block: Record<string, unknown>, blockNode: ParsedNode): void => {\n // Verify metadata matches (safety check)\n if (blockNode.attributes._key && block._key !== blockNode.attributes._key) {\n devLog.warn('Block key mismatch, skipping', {\n expected: block._key,\n found: blockNode.attributes._key,\n component: 'deserializeDocument',\n })\n return\n }\n\n // Handle standard Portable Text blocks with span children\n const children = block.children\n if (Array.isArray(children) && blockNode.children.some((c) => c.type === 'span')) {\n blockNode.children.forEach((spanNode, idx) => {\n const childItem = (children as Record<string, unknown>[])[idx]\n if (spanNode.type === 'span' && childItem) {\n applySpanChanges(childItem, spanNode)\n }\n })\n } else if (blockNode.children.some((c) => c.type === 'field')) {\n // Handle custom block types with nested fields\n blockNode.children.forEach((fieldNode) => {\n if (fieldNode.type === 'field' && fieldNode.name) {\n applyBlockFieldChanges(block, fieldNode.name, fieldNode)\n }\n })\n }\n}\n\nconst applyBlockFieldChanges = (block: Record<string, unknown>, fieldName: string, fieldNode: ParsedNode): void => {\n const currentValue = block[fieldName]\n\n // Handle arrays\n if (fieldNode.attributes.type === 'array') {\n if (Array.isArray(currentValue)) {\n fieldNode.children.forEach((childNode, idx) => {\n if (childNode.type === 'block' && currentValue[idx]) {\n // Nested block in array\n applyBlockChanges(currentValue[idx] as Record<string, unknown>, childNode)\n } else if (childNode.type === 'field') {\n // Nested field in array\n applyBlockFieldChanges(currentValue[idx] as Record<string, unknown>, childNode.name || String(idx), childNode)\n }\n })\n }\n return\n }\n\n // Handle objects\n if (fieldNode.attributes.type === 'object') {\n if (currentValue && typeof currentValue === 'object') {\n fieldNode.children.forEach((childNode) => {\n if (childNode.name) {\n applyBlockFieldChanges(currentValue as Record<string, unknown>, childNode.name, childNode)\n }\n })\n }\n return\n }\n\n // Handle simple text fields\n if (fieldNode.text !== undefined && !fieldNode.attributes.readonly) {\n block[fieldName] = fieldNode.text\n }\n}\n\nconst applySpanChanges = (span: Record<string, unknown>, spanNode: ParsedNode): void => {\n // Verify metadata matches\n if (spanNode.attributes._key && span._key !== spanNode.attributes._key) {\n devLog.warn('Span key mismatch, skipping', {\n expected: span._key,\n found: spanNode.attributes._key,\n component: 'deserializeDocument',\n })\n return\n }\n\n // ONLY update the text property\n if (spanNode.text !== undefined) {\n span.text = spanNode.text\n }\n}\n","import type { SanityDocument } from 'sanity'\n\n/**\n * Converts Sanity documents to a structured text format for OpenAI review.\n * The format is deterministic and preserves all structure and metadata.\n * Only text content can be modified during review.\n */\nexport const serializeDocument = (doc: SanityDocument) => {\n const lines: string[] = []\n\n const serialize = (value: unknown, path: (string | number)[] = [], indent = 0): void => {\n const indentStr = ' '.repeat(indent)\n\n // Skip Sanity metadata at root\n if (path.length === 0) {\n if (isPlainObject(value)) {\n for (const [key, val] of Object.entries(value)) {\n if (!key.startsWith('_') && key !== 'language') {\n serialize(val, [key], indent)\n }\n }\n }\n return\n }\n\n const fieldName = path[path.length - 1]\n\n // Handle arrays\n if (Array.isArray(value)) {\n // Check if it's Portable Text\n const isPortableText = value.some((item) => item?._type === 'block')\n\n if (isPortableText) {\n lines.push(`${indentStr}<field name=\"${fieldName}\">`)\n value.forEach((block, idx) => {\n serializeBlock(block, [...path, idx], indent + 1, lines)\n })\n lines.push(`${indentStr}</field>`)\n } else {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"array\">`)\n value.forEach((item, idx) => {\n serialize(item, [...path, idx], indent + 1)\n })\n lines.push(`${indentStr}</field>`)\n }\n return\n }\n\n // Handle objects\n if (isPlainObject(value)) {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"object\">`)\n for (const [key, val] of Object.entries(value)) {\n if (!key.startsWith('_')) {\n serialize(val, [...path, key], indent + 1)\n }\n }\n lines.push(`${indentStr}</field>`)\n return\n }\n\n // Handle strings (actual translatable content)\n if (typeof value === 'string') {\n if (value.length > 0) {\n lines.push(`${indentStr}<field name=\"${fieldName}\">${escapeXml(value)}</field>`)\n }\n return\n }\n\n // Other primitives (numbers, booleans) - include but mark readonly\n if (typeof value === 'number' || typeof value === 'boolean') {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"${typeof value}\" readonly=\"true\">${value}</field>`)\n }\n }\n\n serialize(doc)\n return lines.join('\\n')\n}\n\nconst serializeBlock = (\n block: Record<string, unknown>,\n path: (string | number)[],\n indent: number,\n lines: string[],\n): void => {\n const indentStr = ' '.repeat(indent)\n\n // Store block metadata\n const metadata: Record<string, unknown> = {}\n if (block._type) metadata._type = block._type\n if (block._key) metadata._key = block._key\n if (block.style) metadata.style = block.style\n if (block.listItem) metadata.listItem = block.listItem\n if (block.level) metadata.level = block.level\n if (block.markDefs) metadata.markDefs = block.markDefs\n\n const metaStr = Object.entries(metadata)\n .map(([k, v]) => `${k}=\"${typeof v === 'string' ? escapeXml(v) : escapeXml(JSON.stringify(v))}\"`)\n .join(' ')\n\n lines.push(`${indentStr}<block ${metaStr}>`)\n\n // Handle standard Portable Text blocks with children (spans)\n if (Array.isArray(block.children) && block._type === 'block') {\n block.children.forEach((child: unknown, idx: number) => {\n serializeSpan(child as Record<string, unknown>, [...path, 'children', idx], indent + 1, lines)\n })\n } else {\n // Handle custom block types by recursively serializing all non-metadata fields\n for (const [key, value] of Object.entries(block)) {\n // Skip metadata fields we already captured\n if (key.startsWith('_') || key === 'style' || key === 'listItem' || key === 'level' || key === 'markDefs') {\n continue\n }\n\n // Recursively serialize the field content\n serializeBlockField(value, key, [...path, key], indent + 1, lines)\n }\n }\n\n lines.push(`${indentStr}</block>`)\n}\n\nconst serializeBlockField = (\n value: unknown,\n fieldName: string,\n path: (string | number)[],\n indent: number,\n lines: string[],\n): void => {\n const indentStr = ' '.repeat(indent)\n\n // Handle arrays\n if (Array.isArray(value)) {\n // Check if array contains blocks (nested Portable Text or custom blocks)\n const hasBlocks = value.some((item) => item?._type)\n\n if (hasBlocks) {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"array\">`)\n value.forEach((item, idx) => {\n if (item?._type === 'block' && item?.children) {\n // Standard Portable Text block\n serializeBlock(item, [...path, idx], indent + 1, lines)\n } else if (item?._type) {\n // Custom block type - treat as block\n serializeBlock(item, [...path, idx], indent + 1, lines)\n } else {\n // Non-block array item\n serializeBlockField(item, String(idx), [...path, idx], indent + 1, lines)\n }\n })\n lines.push(`${indentStr}</field>`)\n } else {\n // Simple array without blocks\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"array\">`)\n value.forEach((item, idx) => {\n serializeBlockField(item, String(idx), [...path, idx], indent + 1, lines)\n })\n lines.push(`${indentStr}</field>`)\n }\n return\n }\n\n // Handle objects\n if (isPlainObject(value)) {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"object\">`)\n for (const [key, val] of Object.entries(value)) {\n if (!key.startsWith('_')) {\n serializeBlockField(val, key, [...path, key], indent + 1, lines)\n }\n }\n lines.push(`${indentStr}</field>`)\n return\n }\n\n // Handle strings\n if (typeof value === 'string' && value.length > 0) {\n lines.push(`${indentStr}<field name=\"${fieldName}\">${escapeXml(value)}</field>`)\n return\n }\n\n // Other primitives\n if (typeof value === 'number' || typeof value === 'boolean') {\n lines.push(`${indentStr}<field name=\"${fieldName}\" type=\"${typeof value}\" readonly=\"true\">${value}</field>`)\n }\n}\n\nconst serializeSpan = (\n span: Record<string, unknown>,\n _path: (string | number)[],\n indent: number,\n lines: string[],\n): void => {\n const indentStr = ' '.repeat(indent)\n\n if (!span || typeof span !== 'object') return\n\n // Store span metadata\n const metadata: Record<string, unknown> = {}\n if (span._type) metadata._type = span._type\n if (span._key) metadata._key = span._key\n if (span.marks && Array.isArray(span.marks)) {\n metadata.marks = span.marks.join(',')\n }\n\n const metaStr = Object.entries(metadata)\n .map(([k, v]) => `${k}=\"${escapeXml(String(v))}\"`)\n .join(' ')\n\n const text = typeof span.text === 'string' ? span.text : ''\n\n if (metaStr) {\n lines.push(`${indentStr}<span ${metaStr}>${escapeXml(text)}</span>`)\n } else {\n lines.push(`${indentStr}<span>${escapeXml(text)}</span>`)\n }\n}\n\nconst escapeXml = (str: string): string => {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;')\n}\n\nconst isPlainObject = (val: unknown): val is Record<string, unknown> => {\n return typeof val === 'object' && val !== null && !Array.isArray(val)\n}\n","import type { SanityDocument } from 'sanity'\n\nexport interface ValidationError {\n path: string[]\n expected: unknown\n actual: unknown\n type: 'missing' | 'type_mismatch' | 'extra' | 'metadata_changed'\n}\n\nexport interface ValidationResult {\n valid: boolean\n errors: ValidationError[]\n}\n\n/**\n * Validates that document structure has not been changed.\n * Extremely strict validation - any structural change is an error.\n * Only text content in 'text' fields is allowed to change.\n */\nexport const validateStructureIntegrity = (original: SanityDocument, modified: SanityDocument): ValidationResult => {\n const errors: ValidationError[] = []\n\n // Phase 1: Check critical metadata\n const criticalFields = ['_id', '_type', '_rev', '_createdAt', '_updatedAt']\n for (const field of criticalFields) {\n if (original[field] !== modified[field]) {\n errors.push({\n path: [field],\n expected: original[field],\n actual: modified[field],\n type: 'metadata_changed',\n })\n }\n }\n\n // Phase 2: Deep structure validation\n validateStructureRecursive(original, modified, [], errors)\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\nconst validateStructureRecursive = (\n original: unknown,\n modified: unknown,\n path: string[],\n errors: ValidationError[],\n): void => {\n // Type check\n if (typeof original !== typeof modified) {\n errors.push({\n path,\n expected: typeof original,\n actual: typeof modified,\n type: 'type_mismatch',\n })\n return\n }\n\n // Array validation\n if (Array.isArray(original)) {\n if (!Array.isArray(modified)) {\n errors.push({\n path,\n expected: 'array',\n actual: typeof modified,\n type: 'type_mismatch',\n })\n return\n }\n\n if (original.length !== modified.length) {\n errors.push({\n path,\n expected: `length ${original.length}`,\n actual: `length ${modified.length}`,\n type: 'type_mismatch',\n })\n return\n }\n\n original.forEach((item, idx) => {\n validateStructureRecursive(item, modified[idx], [...path, String(idx)], errors)\n })\n return\n }\n\n // Object validation\n if (typeof original === 'object' && original !== null && typeof modified === 'object' && modified !== null) {\n const origKeys = Object.keys(original as object).sort()\n const modKeys = Object.keys(modified as object).sort()\n\n // Check for missing keys\n for (const key of origKeys) {\n if (!(key in modified)) {\n errors.push({\n path: [...path, key],\n expected: 'present',\n actual: 'missing',\n type: 'missing',\n })\n }\n }\n\n // Check for extra keys\n for (const key of modKeys) {\n if (!(key in original)) {\n errors.push({\n path: [...path, key],\n expected: 'absent',\n actual: 'present',\n type: 'extra',\n })\n }\n }\n\n // Validate nested structure\n const origObj = original as Record<string, unknown>\n const modObj = modified as Record<string, unknown>\n for (const key of origKeys) {\n if (key in modObj) {\n // For metadata fields, require exact match\n if (key.startsWith('_') || key === 'marks' || key === 'style' || key === 'markDefs') {\n if (JSON.stringify(origObj[key]) !== JSON.stringify(modObj[key])) {\n errors.push({\n path: [...path, key],\n expected: origObj[key],\n actual: modObj[key],\n type: 'metadata_changed',\n })\n }\n } else if (key !== 'text') {\n // For non-text fields, validate structure recursively\n validateStructureRecursive(origObj[key], modObj[key], [...path, key], errors)\n }\n // 'text' fields are allowed to change\n }\n }\n }\n}\n","import type { Dispatch } from 'react'\nimport type { PluginConfig, TranslatableDocument, TranslationAction } from '../../types'\nimport { devLog } from '../common/devLog'\nimport type { FetchTranslationPromptsResult } from '../matching/fetchTranslationPrompts'\nimport { callGPT5 } from '../openai/callGPT5'\nimport { deserializeDocument } from '../openai/deserializeDocument'\nimport { serializeDocument } from '../openai/serializeDocument'\nimport { validateStructureIntegrity } from '../openai/validateStructure'\nimport { setJobStatus } from '../state/setJobStatus'\n\n/**\n * Translates or reviews a document using OpenAI.\n * Works in-place - receives a document, returns translated/reviewed document.\n */\nexport const getOpenAITranslation = async ({\n dispatch,\n sourceDocument,\n languageFieldName,\n translationPrompts,\n targetLanguage,\n openaiConfig,\n}: {\n dispatch: Dispatch<TranslationAction>\n sourceDocument: TranslatableDocument\n targetLanguage: string\n languageFieldName: string\n translationPrompts: FetchTranslationPromptsResult\n openaiConfig: NonNullable<PluginConfig['openai']>\n}): Promise<TranslatableDocument | null> => {\n const sourceLanguage = sourceDocument[languageFieldName] as string\n const logContext = {\n sourceId: sourceDocument._id,\n sourceLanguage,\n }\n\n try {\n // Serialize documents\n const sourceText = serializeDocument(sourceDocument)\n\n // Build prompt\n const prompt = buildTranslationPrompt({\n sourceText,\n sourceLanguage,\n targetLanguage,\n translationPrompts,\n })\n\n const response = await callGPT5(openaiConfig.apiKey, {\n messages: [\n {\n role: 'developer',\n content: 'You are a translation quality reviewer. Review and refine translations while preserving structure.',\n },\n { role: 'user', content: prompt },\n ],\n model: openaiConfig.model,\n reasoningEffort: openaiConfig.reasoningEffort,\n })\n\n // Deserialize response\n const result = deserializeDocument(sourceDocument, response.content) as TranslatableDocument\n\n // Validate structure\n const validation = validateStructureIntegrity(sourceDocument, result)\n if (!validation.valid) {\n devLog.error('Structure validation failed', {\n ...logContext,\n errors: validation.errors,\n })\n\n const unexpectedErrors = validation.errors.filter(\n (err) => !(err.path.length === 1 && err.path[0] === languageFieldName),\n )\n\n if (unexpectedErrors.length > 0) {\n throw new Error(\n `Structure validation failed: ${unexpectedErrors.length} errors. First: ${unexpectedErrors[0].type} at ${unexpectedErrors[0].path.join('.')}`,\n )\n }\n }\n\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'translating_with_openai', {\n tokensUsed: response.tokensUsed.total,\n estimatedCost: response.cost,\n })\n\n return result\n } catch (error) {\n devLog.error('OpenAI translation failed', error instanceof Error ? error : new Error(String(error)), logContext)\n throw error\n }\n}\n\nconst buildTranslationPrompt = ({\n sourceText,\n sourceLanguage,\n targetLanguage,\n translationPrompts,\n}: {\n sourceText: string\n sourceLanguage: string\n targetLanguage: string\n translationPrompts: FetchTranslationPromptsResult\n}): string => {\n const { globalPrompt, localeInstructions, protectedPhrases } = translationPrompts\n const localePrompt = localeInstructions[targetLanguage]?.prompt\n const glossaryTerms = localeInstructions[targetLanguage]?.glossaryTerms\n\n return `# TRANSLATION TASK\n\nTranslate this ${sourceLanguage} document into ${targetLanguage}.\n\n## Guidelines\n\n${globalPrompt ? `### General Guidelines\\n${globalPrompt}\\n` : ''}\n${localePrompt ? `### ${targetLanguage} Guidelines\\n${localePrompt}\\n` : ''}\n${glossaryTerms ? `### Required Terminology\\n${glossaryTerms}\\n` : ''}\n${protectedPhrases?.length ? `### Protected Phrases (do not translate)\\n${protectedPhrases.join(', ')}\\n` : ''}\n\n## Source Document (${sourceLanguage})\n\n\\`\\`\\`\n${sourceText}\n\\`\\`\\`\n\n## Instructions\n\n1. Translate all text content into ${targetLanguage}\n2. Follow the guidelines and terminology requirements above\n3. Preserve protected phrases exactly as they appear\n4. Return the document in THE EXACT SAME FORMAT\n5. **CRITICAL**: Only modify text content between tags\n6. Never add, remove, or change any tags, attributes, or structure\n\nIf you used significant reasoning to make translation decisions, you may include a brief summary:\n<reasoning>\nBrief summary of key translation decisions and reasoning\n</reasoning>\n\nThen return the translated document:`\n}\n","import type { TranslatableDocument } from '../../types'\n\nexport interface DocumentAnalysis {\n hasCodeBlocks: boolean\n hasInlineCode: boolean\n codeBlockCount: number\n inlineCodeCount: number\n hasMarkdown: boolean\n hasPortableText: boolean\n complexity: 'simple' | 'moderate' | 'complex'\n estimatedFieldCount: number\n}\n\n/**\n * Analyzes a document's content to inform translation prompt assembly.\n * Detects code blocks, inline code, content types, and complexity.\n */\nexport const analyzeDocumentContent = (document: TranslatableDocument): DocumentAnalysis => {\n let hasCodeBlocks = false\n let hasInlineCode = false\n let codeBlockCount = 0\n let inlineCodeCount = 0\n const hasMarkdown = false\n let hasPortableText = false\n let estimatedFieldCount = 0\n\n const traverse = (value: unknown, depth = 0): void => {\n if (value === null || value === undefined) {\n return\n }\n\n // Count fields for complexity estimation\n if (depth > 0 && typeof value === 'object') {\n estimatedFieldCount++\n }\n\n // Check for Portable Text blocks and code\n if (Array.isArray(value)) {\n value.forEach((item) => {\n // Detect Portable Text blocks with _type 'code' (code blocks)\n if (item && typeof item === 'object' && '_type' in item && item._type === 'code') {\n hasCodeBlocks = true\n codeBlockCount++\n hasPortableText = true\n }\n\n // Detect Portable Text blocks with spans that have 'code' mark\n if (\n item &&\n typeof item === 'object' &&\n '_type' in item &&\n item._type === 'block' &&\n 'children' in item &&\n Array.isArray(item.children)\n ) {\n hasPortableText = true\n item.children.forEach((child: unknown) => {\n if (\n child &&\n typeof child === 'object' &&\n 'marks' in child &&\n Array.isArray(child.marks) &&\n child.marks.includes('code')\n ) {\n hasInlineCode = true\n inlineCodeCount++\n }\n })\n }\n\n traverse(item, depth + 1)\n })\n } else if (typeof value === 'object') {\n Object.values(value).forEach((val) => traverse(val, depth + 1))\n }\n }\n\n traverse(document)\n\n // Determine complexity based on field count and structure depth\n let complexity: 'simple' | 'moderate' | 'complex' = 'simple'\n if (estimatedFieldCount > 50) {\n complexity = 'complex'\n } else if (estimatedFieldCount > 20) {\n complexity = 'moderate'\n }\n\n return {\n hasCodeBlocks,\n hasInlineCode,\n codeBlockCount,\n inlineCodeCount,\n hasMarkdown,\n hasPortableText,\n complexity,\n estimatedFieldCount,\n }\n}\n","import type { DocumentAnalysis } from '../document/analyzeDocumentContent'\n\nconst MAX_STYLE_GUIDE_LENGTH = 2000\n\ntype PromptSectionId = 'agentDirectives' | 'criticalRules' | 'updateInstructions' | 'glossaryTerms' | 'styleGuide'\n\ninterface PromptSection {\n id: PromptSectionId\n content: string\n required: boolean\n priority: number\n}\n\nexport interface PromptContext {\n documentAnalysis: DocumentAnalysis\n isUpdate: boolean\n targetLanguage: string\n sourceLanguage: string\n}\n\nexport interface PromptComponents {\n globalPrompt?: string\n localePrompt?: string\n glossaryTerms?: string\n protectedPhrases?: string[]\n}\n\nexport interface AssemblePromptResult {\n styleGuide: string\n omittedSections: PromptSectionId[]\n truncatedSections: PromptSectionId[]\n}\n\n/**\n * Assembles a structured, hierarchical translation prompt with clear priority levels while enforcing a 2000 character cap.\n */\nexport const assembleTranslationPrompt = (\n context: PromptContext,\n components: PromptComponents,\n): AssemblePromptResult => {\n const sections: PromptSection[] = [\n {\n id: 'agentDirectives',\n content: buildAgentDirectivesSection(context),\n required: true,\n priority: 0,\n },\n {\n id: 'criticalRules',\n content: buildCriticalRulesSection(context),\n required: true,\n priority: 1,\n },\n ]\n\n if (context.isUpdate) {\n sections.push({\n id: 'updateInstructions',\n content: buildUpdateInstructionsSection(),\n required: true,\n priority: 2,\n })\n }\n\n if (components.glossaryTerms?.trim()) {\n sections.push({\n id: 'glossaryTerms',\n content: buildGlossarySection(components.glossaryTerms),\n required: false,\n priority: 3,\n })\n }\n\n const styleSection = buildStyleGuideSection(components.globalPrompt, components.localePrompt)\n if (styleSection) {\n sections.push({\n id: 'styleGuide',\n content: styleSection,\n required: false,\n priority: 4,\n })\n }\n\n sections.sort((a, b) => a.priority - b.priority)\n\n let styleGuide = ''\n const omittedSections: PromptSectionId[] = []\n const truncatedSections: PromptSectionId[] = []\n\n for (const section of sections) {\n const prefix = styleGuide.length > 0 ? '\\n\\n---\\n\\n' : ''\n const availableCharacters = MAX_STYLE_GUIDE_LENGTH - (styleGuide.length + prefix.length)\n\n if (availableCharacters <= 0) {\n if (section.required) {\n const truncatedContent = truncateSection(section.content, 0)\n if (truncatedContent) {\n styleGuide += prefix + truncatedContent\n truncatedSections.push(section.id)\n }\n } else {\n omittedSections.push(section.id)\n }\n continue\n }\n\n if (section.content.length <= availableCharacters) {\n styleGuide += prefix + section.content\n continue\n }\n\n if (section.required) {\n const truncatedContent = truncateSection(section.content, availableCharacters)\n styleGuide += prefix + truncatedContent\n truncatedSections.push(section.id)\n } else {\n omittedSections.push(section.id)\n }\n }\n\n return {\n styleGuide,\n omittedSections,\n truncatedSections,\n }\n}\n\nconst truncateSection = (content: string, limit: number): string => {\n if (limit <= 0) {\n return ''\n }\n\n if (content.length <= limit) {\n return content\n }\n\n if (limit <= 1) {\n return content.slice(0, limit)\n }\n\n const ellipsis = '…'\n if (limit <= ellipsis.length) {\n return content.slice(0, limit)\n }\n\n const sliceEnd = limit - ellipsis.length\n const sliced = content.slice(0, sliceEnd)\n const trimmed = sliced.replace(/\\s+\\S*$/, '').trimEnd()\n const safeContent = trimmed.length >= sliceEnd / 2 ? trimmed : sliced\n return `${safeContent}${ellipsis}`\n}\n\n/**\n * SECTION 0: Agent workflow directives to align with GPT-5 best practices\n */\nconst buildAgentDirectivesSection = (context: PromptContext): string => {\n const { targetLanguage, sourceLanguage } = context\n const fromLanguage = sourceLanguage || 'the source language'\n const toLanguage = targetLanguage || 'the target language'\n\n return `# AGENT WORKFLOW DIRECTIVES\n\n- Use only the provided ${fromLanguage} source; never fetch outside context.\n- Scan each field once, then move directly into translation work.\n- Plan updates before editing so document structure remains intact.\n- Default to medium reasoning effort and escalate only for ambiguous passages.\n- Output just the ${toLanguage} translation and mirror the source formatting exactly.`\n}\n\n/**\n * SECTION 1: Critical rules that must never be violated\n */\nconst buildCriticalRulesSection = (context: PromptContext): string => {\n const { sourceLanguage, targetLanguage } = context\n const fromLanguage = sourceLanguage || 'the source language'\n const toLanguage = targetLanguage || 'the target language'\n\n return `# CRITICAL TRANSLATION RULES\n\nFollow these without exception:\n\n1. Translate from ${fromLanguage} into ${toLanguage}; every output token must be ${toLanguage} unless flagged as protected.\n2. Preserve meaning exactly—no additions, deletions, or tone shifts.\n3. Maintain all layout, markdown, rich-text marks, and data structure.\n4. Never invent metadata or field values that are absent in the source.\n5. Leave technical tokens (code, IDs, filenames) untouched.\n6. Keep punctuation, casing, and spacing unless ${toLanguage} grammar forces a change.`\n}\n\n/**\n * SECTION 2: Instructions for updating existing translations\n */\nconst buildUpdateInstructionsSection = (): string => {\n return `# UPDATING EXISTING TRANSLATION\n\nThis document already has a translation; only adjust fields that changed.\n\n- Compare the latest source to the existing target text.\n- Keep prior terminology and style unless the source demands a change.\n- Edit minimally while guaranteeing accuracy and completeness.`\n}\n\n/**\n * SECTION 3: Required glossary term translations\n */\nconst buildGlossarySection = (glossaryTerms: string): string => {\n return `# TERMINOLOGY RULES\n\nApply these mappings immediately after protected phrases and before style preferences.\n\n${glossaryTerms}`\n}\n\n/**\n * SECTION 4: Style guide and user preferences\n */\nconst buildStyleGuideSection = (globalPrompt?: string, localePrompt?: string): string | null => {\n const components: string[] = []\n\n if (globalPrompt || localePrompt) {\n components.push('# TRANSLATION STYLE GUIDE (LOWEST PRIORITY)')\n components.push(\n 'Apply these directions only after honoring critical rules, protected phrases, and glossary terminology.',\n )\n components.push('')\n }\n\n if (globalPrompt) {\n components.push('## General Guidelines')\n components.push(globalPrompt)\n components.push('')\n }\n\n if (localePrompt) {\n components.push('## Language-Specific Guidelines')\n components.push(localePrompt)\n }\n\n return components.length > 0 ? components.join('\\n') : null\n}\n","import type { TranslatableDocument } from '../../types'\n\ninterface AssembleLanguageSpecsParams {\n sourceDocument: TranslatableDocument\n languageFieldName: string\n targetLanguage: string\n}\n\n/**\n * Assembles language specification objects for translation operations.\n * Extracts the source language from the document and formats both source and target\n * languages into objects suitable for Sanity Agent Action translation calls.\n *\n * @param params - Configuration for assembling language specs\n * @param params.sourceDocument - The document being translated (contains source language ID)\n * @param params.languageFieldName - Name of the field containing the language identifier\n * @param params.targetLanguage - Target language ID to translate to\n * @returns Object containing fromLanguage and toLanguage specifications\n * @throws Error if source language ID is missing from the document\n *\n * @example\n * ```typescript\n * const specs = assembleLanguageSpecs({\n * sourceDocument: { language: 'en', ... },\n * languageFieldName: 'language',\n * targetLanguage: 'es'\n * })\n * // Returns: { fromLanguage: { id: 'en', title: 'en' }, toLanguage: { id: 'es', title: 'es' } }\n * ```\n */\nexport const assembleLanguageSpecs = ({\n sourceDocument,\n languageFieldName,\n targetLanguage,\n}: AssembleLanguageSpecsParams): {\n toLanguage: { id: string; title: string }\n fromLanguage: { id: string; title: string }\n} => {\n const sourceLanugageId = sourceDocument[languageFieldName] as string | undefined\n\n if (!sourceLanugageId) {\n throw new Error('[assembleLanguages] Source language ID is missing')\n }\n\n return {\n fromLanguage: {\n id: sourceLanugageId,\n title: sourceLanugageId,\n },\n toLanguage: {\n id: targetLanguage,\n title: targetLanguage,\n },\n }\n}\n","import { SanityClient } from \"sanity\";\nimport { TranslatableDocument } from \"../../types\";\nimport { FetchTranslationPromptsResult } from \"../matching/fetchTranslationPrompts\";\nimport { analyzeDocumentContent } from \"../document/analyzeDocumentContent\";\nimport { assembleTranslationPrompt } from \"./assembleTranslationPrompt\";\nimport { assembleLanguageSpecs } from \"../document/assembleLanguageSpecs\";\n\ninterface GetSanityAgentTranslationParams {\n sourceDocument: TranslatableDocument;\n targetLanguage: string;\n languageFieldName: string;\n schemaId: string;\n client: SanityClient;\n translationPrompts: FetchTranslationPromptsResult\n}\n\nexport const getSanityAgentTranslation = async ({\n sourceDocument,\n targetLanguage,\n languageFieldName,\n schemaId,\n client,\n translationPrompts,\n}: GetSanityAgentTranslationParams) => {\n // Analyze document content to inform prompt construction\n const documentAnalysis = analyzeDocumentContent(sourceDocument)\n\n // Assemble style guide and protected phrases\n const { styleGuide } = assembleTranslationPrompt(\n {\n documentAnalysis,\n isUpdate: false, // Always fresh translation in a version\n targetLanguage,\n sourceLanguage: sourceDocument[languageFieldName] as string,\n },\n {\n globalPrompt: translationPrompts.globalPrompt,\n localePrompt: translationPrompts.localeInstructions[targetLanguage]?.prompt,\n glossaryTerms: translationPrompts.localeInstructions[targetLanguage]?.glossaryTerms,\n protectedPhrases: translationPrompts.protectedPhrases,\n },\n )\n\n const { fromLanguage, toLanguage } = assembleLanguageSpecs({\n sourceDocument,languageFieldName,\n targetLanguage\n })\n\n return await client.agent.action.translate({\n schemaId,\n toLanguage,\n fromLanguage,\n documentId: sourceDocument._id,\n noWrite: true, // Do not write to the document, just return the translated content\n languageFieldPath: [languageFieldName],\n styleGuide,\n protectedPhrases: translationPrompts.protectedPhrases,\n conditionalPaths: {\n defaultHidden: false,\n defaultReadOnly: false\n }\n }) as TranslatableDocument | null\n\n}\n","import { uuid } from '@sanity/uuid'\nimport type { Dispatch } from 'react'\nimport type { SanityClient } from 'sanity'\nimport type { PluginConfig, TranslatableDocument, TranslationAction, TranslationMode } from '../../types'\nimport type { FetchTranslationPromptsResult } from '../matching/fetchTranslationPrompts'\nimport { setJobStatus } from '../state/setJobStatus'\nimport { getOpenAITranslation } from './getOpenAITranslation'\nimport { getSanityAgentTranslation } from './getSanityAgentTranslation'\n\ninterface TranslateEntireDocumentOptions {\n dispatch: Dispatch<TranslationAction>\n client: SanityClient\n schemaId: string\n sourceDocument: TranslatableDocument\n targetLanguage: string\n existingTranslationId: string | null\n languageFieldName: string\n translationPrompts: FetchTranslationPromptsResult\n translationMode: TranslationMode\n openaiConfig?: PluginConfig['openai']\n releaseId: string\n}\n\n/**\n * Main translation orchestrator.\n * Creates a version in the release, then routes to the appropriate translation method.\n */\nexport const handleTranslation = async ({\n dispatch,\n client,\n schemaId,\n sourceDocument,\n targetLanguage,\n existingTranslationId,\n languageFieldName,\n translationPrompts,\n translationMode,\n openaiConfig,\n releaseId,\n}: TranslateEntireDocumentOptions): Promise<TranslatableDocument | null> => {\n // 1. Create translation in memory via Sanity Agent or ChatGPT\n\n let translatedDoc: TranslatableDocument | null\n\n switch (translationMode) {\n case 'sanity-only': {\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'translating')\n translatedDoc = await getSanityAgentTranslation({\n sourceDocument,\n targetLanguage,\n languageFieldName,\n schemaId,\n client,\n translationPrompts,\n })\n break\n }\n case 'openai-only': {\n if (!openaiConfig?.enabled || !openaiConfig?.apiKey) {\n throw new Error('OpenAI is not configured but openai-only mode was selected')\n }\n\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'translating_with_openai')\n translatedDoc = await getOpenAITranslation({\n dispatch,\n sourceDocument,\n targetLanguage,\n languageFieldName,\n translationPrompts,\n openaiConfig,\n })\n break\n }\n case 'sanity-with-review': {\n throw new Error('Sanity with review translation method is not implemented')\n }\n default: {\n throw new Error(`Invalid translation mode: ${translationMode}`)\n }\n }\n\n if (!translatedDoc) {\n return null\n }\n\n // 2. Create new version of the target document with the translated content\n // Overwrite the existing translation as base if it exists, otherwise create a new id\n const targetDocumentPublishedId = existingTranslationId || uuid()\n const targetDocumentVersionId = `versions.${releaseId}.${targetDocumentPublishedId}`\n const newDoc = {\n ...translatedDoc,\n _id: targetDocumentVersionId,\n }\n\n console.log('NEW DOC', newDoc)\n\n const createdDoc = await client.action({\n actionType: 'sanity.action.document.version.create',\n publishedId: targetDocumentPublishedId,\n document: newDoc,\n })\n\n console.log('CREATED DOC', createdDoc)\n\n return newDoc\n}\n","import type { Dispatch } from 'react'\nimport type { SanityClient } from 'sanity'\nimport type { PluginConfig, TranslatableDocument, TranslationAction, TranslationMode } from '../../types'\nimport { devLog } from '../common/devLog'\nimport type { FetchTranslationPromptsResult } from '../matching/fetchTranslationPrompts'\nimport { getExistingTranslationId } from '../metadata/getExistingTranslationId'\nimport { getTranslationMetadata } from '../metadata/getTranslationMetadata'\nimport { updateTranslationMetadata } from '../metadata/updateTranslationMetadata'\nimport { setJobStatus } from '../state/setJobStatus'\nimport { handleTranslation } from './handleTranslation'\n\ninterface TranslateDocumentParams {\n dispatch: Dispatch<TranslationAction>\n client: SanityClient\n sourceDocument: TranslatableDocument\n sourceLanguage: PluginConfig['supportedLanguages'][number]['id']\n targetLanguage: PluginConfig['supportedLanguages'][number]['id']\n languageFieldName: PluginConfig['languageFieldName']\n shouldOverwriteFreshTranslations: boolean\n schemaId: string\n translationPrompts: FetchTranslationPromptsResult\n translationMode: TranslationMode\n openaiConfig?: PluginConfig['openai']\n releaseId: string\n}\n\nexport const translateDocument = async ({\n dispatch,\n client,\n sourceDocument,\n sourceLanguage,\n targetLanguage,\n languageFieldName,\n shouldOverwriteFreshTranslations,\n schemaId,\n translationPrompts,\n translationMode,\n openaiConfig,\n releaseId,\n}: TranslateDocumentParams) => {\n const logContext = {\n documentId: sourceDocument._id,\n targetLanguage,\n schemaId,\n shouldOverwriteFreshTranslations,\n translationMode,\n }\n\n devLog.group('translateDocument', logContext)\n devLog.time('translateDocument')\n\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'preparing_translation')\n\n try {\n const translationMetadata = await getTranslationMetadata({\n client,\n sourceDocument,\n sourceLanguage,\n })\n\n const isAlreadyFreshTranslation = translationMetadata.translationHistory?.some(\n (history) => history.language === targetLanguage && history.sourceRev === sourceDocument._rev,\n )\n\n if (!shouldOverwriteFreshTranslations && isAlreadyFreshTranslation) {\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'skipped', {\n targetId: translationMetadata.translations.find((t) => t._key === targetLanguage)?.value._ref,\n })\n return\n }\n\n // Get the existing translation id if it exists\n const existingTranslationId = getExistingTranslationId(translationMetadata, targetLanguage)\n\n // Delegate to handleTranslation which orchestrates the translation\n const newDocument = await handleTranslation({\n dispatch,\n client,\n schemaId,\n sourceDocument,\n targetLanguage,\n existingTranslationId,\n languageFieldName,\n translationPrompts,\n translationMode,\n openaiConfig,\n releaseId,\n })\n\n if (!newDocument) {\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'error')\n devLog.timeEnd('translateDocument')\n devLog.groupEnd()\n return undefined\n }\n\n // UPDATE TRANSLATION METADATA\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'updating_metadata', {\n targetId: newDocument._id,\n })\n\n await updateTranslationMetadata({\n client,\n sourceDocument,\n sourceLanguage,\n newDocument,\n targetLanguage,\n translationMetadata,\n isNewTranslation: !existingTranslationId,\n })\n\n // Set completed status while preserving targetId\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'completed', {\n targetId: newDocument._id,\n })\n devLog.info('Translation completed successfully', {\n ...logContext,\n newDocumentId: newDocument._id,\n })\n\n devLog.timeEnd('translateDocument')\n devLog.groupEnd()\n return newDocument._id\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n devLog.error('Translation failed', error instanceof Error ? error : new Error(String(error)), logContext)\n setJobStatus(dispatch, sourceDocument._id, targetLanguage, 'error', {\n errorMessage,\n })\n devLog.timeEnd('translateDocument')\n devLog.groupEnd()\n // Don't re-throw - let the batch continue with other translations\n return undefined\n }\n}\n","import type { Dispatch } from 'react'\nimport type { SanityClient } from 'sanity'\nimport type { PluginConfig, TranslatableDocument, TranslationAction, TranslationMode } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { Semaphore } from '../common/Semaphore'\nimport { fetchTranslationPrompts } from '../matching/fetchTranslationPrompts'\nimport type { AssembleReferencesResult } from '../references/assembleReferences'\nimport { localizeReferences } from '../references/localizeReferences'\nimport { setupTranslationJobStatuses } from '../state/setupTranslationJobStatuses'\nimport { translateDocument } from './translateDocument'\n\ninterface TranslateDocumentsParams {\n sourceDocument: TranslatableDocument\n targetLanguages: Array<string>\n client: SanityClient\n dispatch: Dispatch<TranslationAction>\n languageFieldName: string\n shouldOverwriteFreshTranslations: boolean\n schemaId: string\n references: AssembleReferencesResult\n maxConcurrentTranslationJobs?: number\n maxReferenceDepth?: number\n translationMode: TranslationMode\n openaiConfig?: PluginConfig['openai']\n}\n\nconst translationSemaphore = new Semaphore(2)\n\nexport const translateDocumentSet = async ({\n client,\n sourceDocument,\n targetLanguages,\n dispatch,\n languageFieldName,\n shouldOverwriteFreshTranslations,\n schemaId,\n references = [],\n maxConcurrentTranslationJobs,\n translationMode,\n openaiConfig,\n}: TranslateDocumentsParams) => {\n const logContext = {\n documentId: sourceDocument._id,\n documentRev: sourceDocument._rev,\n targetLanguages,\n referencesCount: references.length,\n maxConcurrentJobs: maxConcurrentTranslationJobs,\n translationMode,\n shouldOverwriteFreshTranslations,\n }\n\n let releaseId: string | undefined\n\n try {\n const sourceLanguage = sourceDocument[languageFieldName] as string\n\n if (!sourceLanguage) {\n devLog.error('Source language not found')\n throw new Error('Source language not found')\n }\n\n // If the config specifies a max concurrent translation jobs, update the semaphore\n if (maxConcurrentTranslationJobs) {\n devLog.debug('Updating translation semaphore permits', {\n ...logContext,\n newPermits: maxConcurrentTranslationJobs,\n })\n translationSemaphore.updatePermits(maxConcurrentTranslationJobs)\n }\n\n dispatch({\n type: 'SET_OVERALL_STATUS',\n payload: 'translating',\n })\n\n // Always create a new release for each translation batch\n const documentTitle =\n (sourceDocument.title as string | undefined) || sourceDocument._id.split('-').slice(0, 2).join('-')\n const releaseTitle = `[AI Translation] ${documentTitle} (${sourceDocument._type}) ${sourceLanguage} → ${targetLanguages.join(', ')}`\n const releaseDescription = `AI translation batch created on ${new Date().toLocaleDateString()} at ${new Date().toLocaleTimeString()}`\n\n devLog.info('Creating new Content Release for translation batch', {\n ...logContext,\n releaseTitle,\n })\n\n const { releaseId } = await client.releases.create({\n metadata: {\n title: releaseTitle,\n description: releaseDescription,\n releaseType: 'undecided',\n },\n })\n\n devLog.info('✅ NEW CONTENT RELEASE CREATED', {\n ...logContext,\n releaseId,\n releaseTitle,\n })\n\n const sourceDocuments = [\n {\n _id: sourceDocument._id,\n _type: sourceDocument._type,\n // TODO: Handle this properly\n title: (sourceDocument.name || sourceDocument.title) as string | undefined,\n },\n ...references,\n ]\n\n const sourceDocumentIds = sourceDocuments.map((doc) => doc._id)\n const targetDocumentIds: Array<string> = []\n\n devLog.debug('Prepared document set for translation', {\n ...logContext,\n sourceDocumentIds: sourceDocumentIds.length,\n totalTranslationJobs: sourceDocumentIds.length * targetLanguages.length,\n })\n\n // Assemble prompts for translation task style guides\n const translationPrompts = await fetchTranslationPrompts({\n client,\n sourceLanguage,\n targetLanguages,\n sourceDocument,\n })\n\n devLog.info('Translation prompts', translationPrompts)\n\n // Dispatch new job statuses for each document\n setupTranslationJobStatuses(dispatch, sourceDocuments, targetLanguages)\n\n // Create translation tasks for all documents and all languages\n const translationTasks = sourceDocumentIds.flatMap((documentId) =>\n targetLanguages.map(async (targetLanguage) => {\n try {\n await translationSemaphore.withPermit(async () => {\n const document = await client\n .withConfig({\n perspective: 'drafts',\n })\n .getDocument<TranslatableDocument>(documentId)\n\n if (!document) {\n devLog.error('❌ SOURCE DOCUMENT NOT FOUND', { documentId })\n }\n\n if (document && releaseId) {\n const targetDocumentId = await translateDocument({\n dispatch,\n client,\n sourceDocument: document,\n sourceLanguage,\n targetLanguage,\n languageFieldName,\n shouldOverwriteFreshTranslations,\n schemaId,\n translationPrompts,\n translationMode,\n openaiConfig,\n releaseId,\n })\n\n if (targetDocumentId) {\n targetDocumentIds.push(targetDocumentId)\n }\n }\n })\n } catch (error) {\n // Log the error but don't fail the entire batch\n devLog.error(\n 'Individual translation task failed',\n error instanceof Error ? error : new Error(String(error)),\n {\n ...logContext,\n failedDocumentId: documentId,\n failedTargetLanguage: targetLanguage,\n },\n )\n }\n }),\n )\n\n const results = await Promise.allSettled(translationTasks)\n\n // Log summary of results\n const succeeded = results.filter((r) => r.status === 'fulfilled').length\n const failed = results.filter((r) => r.status === 'rejected').length\n\n devLog.info('Translation tasks summary', {\n ...logContext,\n total: results.length,\n succeeded,\n failed,\n })\n\n devLog.info('All translation tasks completed', {\n ...logContext,\n targetDocumentIds: targetDocumentIds.length,\n })\n\n dispatch({\n type: 'SET_OVERALL_STATUS',\n payload: 'linking_references',\n })\n\n devLog.debug('Starting reference localization', {\n ...logContext,\n documentsToLocalize: targetDocumentIds.length,\n })\n\n await Promise.all(\n targetDocumentIds.map(async (documentId) => {\n await localizeReferences({\n client,\n documentId,\n languageFieldName,\n })\n }),\n )\n\n // Determine overall status based on individual job statuses\n const hasAnyFailures = failed > 0\n\n if (hasAnyFailures && succeeded === 0) {\n // All translations failed\n devLog.error('All translation tasks failed', undefined, logContext)\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'error' })\n } else if (hasAnyFailures) {\n // Partial success - some translations failed but others succeeded\n devLog.warn('Translation completed with some failures', {\n ...logContext,\n succeeded,\n failed,\n })\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'completed' })\n } else {\n // All translations succeeded\n devLog.info('Document set translation completed successfully', logContext)\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'completed' })\n }\n\n devLog.timeEnd('translateDocumentSet')\n devLog.groupEnd()\n } catch (error) {\n // This catch block should only catch unexpected errors that aren't handled above\n devLog.error(\n 'translateDocumentSet failed with unexpected error',\n error instanceof Error ? error : new Error(String(error)),\n logContext,\n )\n\n // If we have a release, try to clean it up if all translations failed\n if (releaseId) {\n try {\n // Re-count succeeded jobs since we're in the error handler\n // This is a fallback - typically errors are already handled above\n // Delete the release since we're in fatal error state\n await client.releases.delete({ releaseId })\n devLog.warn('Deleted release due to fatal error', {\n ...logContext,\n releaseId,\n })\n } catch (cleanupError) {\n devLog.error(\n 'Failed to cleanup release',\n cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError)),\n logContext,\n )\n }\n }\n\n devLog.timeEnd('translateDocumentSet')\n devLog.groupEnd()\n dispatch({ type: 'SET_OVERALL_STATUS', payload: 'error' })\n throw error\n }\n}\n","import { Button, Flex } from '@sanity/ui'\nimport { useCallback } from 'react'\nimport { useClient } from 'sanity'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport { translateDocumentSet } from '../utils/translation/translateDocumentSet'\n\ninterface ActionButtonBarProps {\n setIsDialogOpen: (isOpen: boolean) => void\n}\n\nexport const ActionButtonBar = ({ setIsDialogOpen }: ActionButtonBarProps) => {\n const {\n document,\n config,\n overallStatus,\n dispatch,\n references,\n selectedTargetLanguages,\n shouldOverwriteFreshTranslations,\n translationMode,\n } = useTranslationContext()\n const client = useClient({ apiVersion: 'vX' })\n\n const handleTranslate = useCallback(() => {\n translateDocumentSet({\n sourceDocument: document,\n targetLanguages: selectedTargetLanguages,\n client,\n dispatch,\n languageFieldName: config.languageFieldName,\n schemaId: config.schemaId,\n references,\n shouldOverwriteFreshTranslations,\n maxConcurrentTranslationJobs: config.maxConcurrentTranslationJobs,\n maxReferenceDepth: config.maxReferenceDepth,\n translationMode,\n openaiConfig: config.openai,\n })\n }, [\n document,\n selectedTargetLanguages,\n client,\n dispatch,\n config.languageFieldName,\n config.schemaId,\n config.maxConcurrentTranslationJobs,\n config.maxReferenceDepth,\n config.openai,\n references,\n shouldOverwriteFreshTranslations,\n translationMode,\n ])\n\n const handleSelectAll = useCallback(() => {\n dispatch({\n type: 'SET_SELECTED_TARGET_LANGUAGES',\n payload: config.supportedLanguages.map((lang) => lang.id).filter((id) => id !== document.language),\n })\n }, [dispatch, config.supportedLanguages, document.language])\n\n const handleSelectNone = useCallback(() => {\n dispatch({\n type: 'SET_SELECTED_TARGET_LANGUAGES',\n payload: [],\n })\n }, [dispatch])\n\n return (\n <Flex justify=\"space-between\" gap={2}>\n <Flex justify=\"flex-start\" gap={2}>\n <Button mode=\"ghost\" size={2} onClick={handleSelectAll}>\n Select All\n </Button>\n <Button mode=\"ghost\" size={2} onClick={handleSelectNone}>\n Select None\n </Button>\n </Flex>\n <Flex justify=\"flex-end\" gap={2}>\n <Button onClick={() => setIsDialogOpen(false)} mode=\"ghost\" size={2}>\n Cancel\n </Button>\n <Button\n mode=\"ghost\"\n tone=\"positive\"\n size={2}\n onClick={handleTranslate}\n disabled={selectedTargetLanguages.length === 0 || !['idle', 'completed'].includes(overallStatus)}\n >\n Translate\n </Button>\n </Flex>\n </Flex>\n )\n}\n","import { Badge, Button, Checkbox, Flex, Inline, Spinner, Text } from '@sanity/ui'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport type { PluginConfig } from '../types'\n\ninterface LanguageCheckboxProps {\n lang: PluginConfig['supportedLanguages'][number]\n isLoading: boolean\n}\n\nexport const LanguageCheckbox = ({ lang, isLoading }: LanguageCheckboxProps) => {\n const { document, dispatch, selectedTargetLanguages, translationMetadata } = useTranslationContext()\n const isDisabled = lang.id === document.language\n const isChecked = selectedTargetLanguages.includes(lang.id)\n const hasTranslation = translationMetadata?.translations?.some((translation) => translation._key === lang.id)\n const isStale =\n hasTranslation &&\n translationMetadata?.translationHistory?.some(\n (history) => history.language === lang.id && history.sourceRev !== document._rev,\n )\n\n const handleChange = () => {\n if (isDisabled) return\n\n const updatedLanguages = isChecked\n ? selectedTargetLanguages.filter((langId) => langId !== lang.id)\n : [...selectedTargetLanguages, lang.id]\n\n dispatch({\n type: 'SET_SELECTED_TARGET_LANGUAGES',\n payload: updatedLanguages,\n })\n }\n\n if (isLoading) {\n return (\n <Button type=\"button\" mode=\"bleed\" size={2} onClick={handleChange} disabled muted>\n <Flex align=\"center\" justify=\"center\" paddingY={3}>\n <Spinner size={1} />\n </Flex>\n </Button>\n )\n }\n\n return (\n <Button\n type=\"button\"\n mode={isChecked ? 'ghost' : 'bleed'}\n size={2}\n onClick={handleChange}\n disabled={isDisabled}\n tone={isChecked ? 'primary' : 'default'}\n muted\n >\n <Flex align=\"center\" gap={2} style={{ cursor: 'pointer' }}>\n <Inline space={3}>\n <Checkbox checked={isChecked} onChange={handleChange} label={lang.title} disabled={isDisabled} />\n <Flex direction=\"column\" gap={3}>\n <Text>{lang.title}</Text>\n {!isDisabled ? (\n <Text size={1} muted>\n {hasTranslation && !isDisabled ? 'Already translated' : 'Not translated'}\n </Text>\n ) : (\n <Text size={1} muted>\n The current language\n </Text>\n )}\n </Flex>\n </Inline>\n {!isDisabled && hasTranslation && (\n <Flex flex={1} justify=\"flex-end\">\n <Badge tone={isStale ? 'caution' : 'positive'} size={3} muted>\n {isStale ? 'Stale' : 'Fresh'}\n </Badge>\n </Flex>\n )}\n </Flex>\n </Button>\n )\n}\n","import { Card, Grid } from '@sanity/ui'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport { useTranslationMetadata } from '../hooks/useTranslationMetadata'\nimport { LanguageCheckbox } from './LanguageCheckbox'\n\nexport const LanguageSelectionPanel = () => {\n // Get the configuration from the context\n const { document, config } = useTranslationContext()\n\n const { loading } = useTranslationMetadata({\n documentId: document._id,\n })\n\n return (\n <Card padding={4} muted border>\n <Grid columns={[1, 2]} gap={3}>\n {config?.supportedLanguages.map((lang) => (\n <LanguageCheckbox key={lang.id} lang={lang} isLoading={loading} />\n ))}\n </Grid>\n </Card>\n )\n}\n","import { Badge, Button, Card, Inline, Radio, Stack, Text } from '@sanity/ui'\nimport type { FC } from 'react'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport type { TranslationMode } from '../types'\n\nexport const TranslationModePanel: FC = () => {\n const { config, translationMode, openaiValidated, dispatch } = useTranslationContext()\n\n // Don't show if OpenAI is not configured\n if (!config?.openai?.enabled || !config?.openai?.apiKey) {\n return null\n }\n\n // Show validation status\n if (!openaiValidated) {\n return (\n <Card tone=\"caution\" padding={4} border>\n <Stack space={2}>\n <Text weight=\"semibold\">OpenAI Configuration</Text>\n <Text size={1} muted>\n Validating API key...\n </Text>\n </Stack>\n </Card>\n )\n }\n\n const handleModeChange = (mode: TranslationMode) => {\n dispatch({\n type: 'SET_TRANSLATION_MODE',\n payload: mode,\n })\n }\n\n return (\n <Card padding={4} border>\n <Stack space={3}>\n <Inline space={2}>\n <Text weight=\"semibold\">Translation Mode</Text>\n <Badge tone=\"positive\">GPT-5 Available</Badge>\n </Inline>\n\n <Stack space={2}>\n {/* Sanity Only */}\n <Button\n type=\"button\"\n mode={translationMode === 'sanity-only' ? 'ghost' : 'bleed'}\n tone={translationMode === 'sanity-only' ? 'primary' : 'default'}\n onClick={() => handleModeChange('sanity-only')}\n >\n <Inline space={2}>\n <Radio checked={translationMode === 'sanity-only'} readOnly />\n <Stack space={2}>\n <Text>Sanity Agent Only</Text>\n <Text size={1} muted>\n Fast, reliable, and included with Sanity.\n </Text>\n </Stack>\n </Inline>\n </Button>\n\n {/* OpenAI Only */}\n <Button\n type=\"button\"\n mode={translationMode === 'openai-only' ? 'ghost' : 'bleed'}\n tone={translationMode === 'openai-only' ? 'primary' : 'default'}\n onClick={() => handleModeChange('openai-only')}\n >\n <Inline space={2}>\n <Radio checked={translationMode === 'openai-only'} readOnly />\n <Stack space={2}>\n <Inline space={2}>\n <Text>GPT-5 Only</Text>\n <Badge tone=\"caution\">Beta</Badge>\n </Inline>\n <Text size={1} muted>\n Full style guide capacity with 1M token context window.\n </Text>\n </Stack>\n </Inline>\n </Button>\n\n {/* Sanity + OpenAI Review */}\n <Button\n type=\"button\"\n mode={translationMode === 'sanity-with-review' ? 'ghost' : 'bleed'}\n tone={translationMode === 'sanity-with-review' ? 'primary' : 'default'}\n onClick={() => handleModeChange('sanity-with-review')}\n >\n <Inline space={2}>\n <Radio checked={translationMode === 'sanity-with-review'} readOnly />\n <Stack space={2}>\n <Inline space={2}>\n <Text>Sanity + GPT-5 Review</Text>\n <Badge tone=\"primary\">Highest Quality</Badge>\n </Inline>\n <Text size={1} muted>\n Sanity translation refined by GPT-5. Takes longer but best results.\n </Text>\n </Stack>\n </Inline>\n </Button>\n </Stack>\n\n {/* Cost Warning */}\n {translationMode !== 'sanity-only' && (\n <Card tone=\"caution\" padding={3} border>\n <Text size={1}>\n 💰 <strong>OpenAI costs apply:</strong> You'll be charged for API usage based on tokens processed.\n </Text>\n </Card>\n )}\n </Stack>\n </Card>\n )\n}\n","import type { SanityClient, SanityDocument } from 'sanity'\nimport type { TranslatableDocument } from '../../types'\nimport { devLog } from '../common/devLog'\nimport { findKeyValues, type RecursiveSearchable } from '../common/findKeyValues/findKeyValues'\n\ninterface AssembleReferencesParams {\n client: SanityClient\n sourceDocument: SanityDocument\n maxReferenceDepth: number\n translatableSchemaTypes?: Array<string>\n}\n\nexport type AssembleReferencesResult = Array<{\n _id: SanityDocument['_id']\n _type: SanityDocument['_type']\n title?: string\n}>\n\n// Sanity documents are objects of arbitrary shape, which can\n// contain references to other documents. References are always\n// represented as a `_ref` key with a value of the referenced document's\n// `_id`. This function takes a Sanity document, appends all `_ref` keys\n// within it to an array, then fetches the references documents and repeats\n// the process until the maximum reference depth is reached.\n\nexport const assembleReferences = async ({\n client,\n sourceDocument,\n maxReferenceDepth,\n translatableSchemaTypes = [],\n}: AssembleReferencesParams): Promise<AssembleReferencesResult> => {\n // Input validation\n if (!client) {\n throw new Error('assembleReferences: client is required')\n }\n\n if (!sourceDocument || !sourceDocument._id) {\n throw new Error('assembleReferences: sourceDocument with _id is required')\n }\n\n if (typeof maxReferenceDepth !== 'number' || maxReferenceDepth < 0) {\n throw new Error('assembleReferences: maxReferenceDepth must be a non-negative number')\n }\n\n const references = new Set<string>()\n const documentCache = new Map<string, TranslatableDocument | null>()\n const errors: Array<{ refId: string; error: Error }> = []\n\n let currentDepthDocuments = [sourceDocument]\n\n for (let depth = 0; depth < maxReferenceDepth && currentDepthDocuments.length > 0; depth++) {\n // Collect all reference IDs from current depth documents\n const allRefIds = new Set<string>()\n\n for (const document of currentDepthDocuments) {\n try {\n const refResults = findKeyValues<string>(document as RecursiveSearchable, '_ref')\n for (const result of refResults) {\n const refId = result.value\n if (typeof refId === 'string' && refId.length > 0 && !references.has(refId) && !documentCache.has(refId)) {\n allRefIds.add(refId)\n }\n }\n } catch (error) {\n devLog.error(\n 'Error finding references in document',\n error instanceof Error ? error : new Error(String(error)),\n {\n documentId: document._id,\n component: 'assembleReferences',\n },\n )\n }\n }\n\n if (allRefIds.size === 0) {\n break\n }\n\n // Batch fetch all references for this depth level\n const refIdArray = Array.from(allRefIds)\n try {\n const batchResults = await client.fetch<TranslatableDocument[]>('*[_id in $ids]', { ids: refIdArray })\n\n // Create a map of fetched documents by ID\n const fetchedDocuments = new Map<string, TranslatableDocument>()\n for (const doc of batchResults) {\n fetchedDocuments.set(doc._id, doc)\n }\n\n // Process each reference ID\n const nextDepthDocuments: TranslatableDocument[] = []\n\n for (const refId of refIdArray) {\n const fetchedDoc = fetchedDocuments.get(refId)\n\n if (fetchedDoc) {\n // Skip non-translatable documents\n if (translatableSchemaTypes.length > 0 && !translatableSchemaTypes.includes(fetchedDoc._type)) {\n documentCache.set(refId, null)\n continue\n }\n\n documentCache.set(refId, fetchedDoc)\n references.add(refId)\n nextDepthDocuments.push(fetchedDoc)\n } else {\n // Document not found\n documentCache.set(refId, null)\n devLog.warn(`Document with _id \"${refId}\" not found`, {\n refId,\n component: 'assembleReferences',\n })\n }\n }\n\n currentDepthDocuments = nextDepthDocuments\n } catch (fetchError) {\n // Log the error but continue processing\n const error = fetchError instanceof Error ? fetchError : new Error(String(fetchError))\n errors.push({ refId: `batch(${refIdArray.join(',')})`, error })\n devLog.error('Failed to batch fetch documents', error, {\n refIdCount: refIdArray.length,\n depth,\n component: 'assembleReferences',\n })\n break\n }\n }\n\n // Log summary of errors if any occurred\n if (errors.length > 0) {\n devLog.warn(`Encountered ${errors.length} error(s) while fetching references`, {\n errorCount: errors.length,\n errors: errors.map((e) => ({ refId: e.refId, message: e.error.message })),\n component: 'assembleReferences',\n })\n }\n\n // Convert the cached documents to the expected result format\n const result: AssembleReferencesResult = []\n for (const refId of references) {\n const cachedDoc = documentCache.get(refId)\n if (cachedDoc) {\n result.push({\n _id: cachedDoc._id,\n _type: cachedDoc._type,\n title: (cachedDoc.name || cachedDoc.title) as string | undefined,\n })\n }\n }\n\n return result\n}\n","import { Button, Checkbox, Flex, Inline, Select, Stack, Text } from '@sanity/ui'\nimport { useEffect, useState } from 'react'\nimport { useClient } from 'sanity'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport { assembleReferences } from '../utils/references/assembleReferences'\n\nexport const ReferenceConfigPanel = () => {\n const { dispatch, document, shouldTranslateReferences, maxReferenceDepth, config, references } =\n useTranslationContext()\n const client = useClient({ apiVersion: 'vX' })\n const [isLoadingReferences, setIsLoadingReferences] = useState(false)\n const maxDepth = config.maxReferenceDepth || 3\n\n useEffect(() => {\n const updateReferences = async () => {\n setIsLoadingReferences(true)\n\n if (!shouldTranslateReferences) {\n dispatch({\n type: 'SET_REFERENCES',\n payload: [],\n })\n return\n }\n\n const newReferences = await assembleReferences({\n client,\n sourceDocument: document,\n maxReferenceDepth,\n translatableSchemaTypes: config.translatableSchemaTypes,\n })\n dispatch({\n type: 'SET_REFERENCES',\n payload: newReferences,\n })\n\n setIsLoadingReferences(false)\n }\n\n updateReferences()\n }, [document, client, dispatch, shouldTranslateReferences, maxReferenceDepth, config.translatableSchemaTypes])\n\n const handleMaxReferenceDepthChange = async (event: React.ChangeEvent<HTMLSelectElement>) => {\n dispatch({\n type: 'SET_MAX_REFERENCE_DEPTH',\n payload: Number.parseInt(event.target.value),\n })\n }\n\n const handleShouldTranslateChange = () => {\n dispatch({\n type: 'SET_SHOULD_TRANSLATE_REFERENCES',\n payload: !shouldTranslateReferences,\n })\n }\n\n return (\n <Stack space={2}>\n <Flex justify=\"space-between\" align=\"center\">\n <Button\n type=\"button\"\n mode={shouldTranslateReferences ? 'ghost' : 'bleed'}\n size={2}\n onClick={handleShouldTranslateChange}\n tone={shouldTranslateReferences ? 'primary' : 'neutral'}\n >\n <Inline space={2}>\n <Checkbox\n checked={shouldTranslateReferences}\n onChange={handleShouldTranslateChange}\n label={shouldTranslateReferences ? 'Disable' : 'Enable'}\n />\n <Text>Translate References</Text>\n </Inline>\n </Button>\n {shouldTranslateReferences ? (\n <Inline space={2}>\n <Text>Max Depth:</Text>\n <Select\n disabled={!shouldTranslateReferences}\n placeholder=\"Depth\"\n onChange={handleMaxReferenceDepthChange}\n value={maxReferenceDepth.toString()}\n >\n {Array.from({ length: maxDepth }, (_, i) => i + 1).map((depth) => (\n <option key={depth} value={depth.toString()}>\n {depth}\n </option>\n ))}\n </Select>\n </Inline>\n ) : null}\n </Flex>\n {shouldTranslateReferences ? (\n <Flex padding={2} justify=\"flex-end\">\n <Text size={1} muted>\n {isLoadingReferences\n ? 'Loading references...'\n : references.length === 0\n ? 'No translatable references found.'\n : `${references.length} translatable references found.`}\n </Text>\n </Flex>\n ) : null}\n </Stack>\n )\n}\n","import { Button, Checkbox, Flex, Inline, Stack, Text } from '@sanity/ui'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport { ReferenceConfigPanel } from './ReferenceConfigPanel'\n\nexport const WriteSettingsPanel = () => {\n const { dispatch, shouldOverwriteFreshTranslations } = useTranslationContext()\n\n const handleShouldOverwriteFreshTranslationsChange = () => {\n dispatch({\n type: 'SET_SHOULD_OVERWRITE_FRESH_TRANSLATIONS',\n payload: !shouldOverwriteFreshTranslations,\n })\n }\n\n return (\n <Stack space={2}>\n <ReferenceConfigPanel />\n {/* Overwrite fresh translations */}\n <Flex justify=\"space-between\" align=\"center\">\n <Button\n type=\"button\"\n mode={shouldOverwriteFreshTranslations ? 'ghost' : 'bleed'}\n size={2}\n onClick={handleShouldOverwriteFreshTranslationsChange}\n tone={shouldOverwriteFreshTranslations ? 'primary' : 'neutral'}\n >\n <Inline space={2}>\n <Checkbox\n checked={shouldOverwriteFreshTranslations}\n onChange={handleShouldOverwriteFreshTranslationsChange}\n label={shouldOverwriteFreshTranslations ? 'Disable' : 'Enable'}\n />\n <Stack space={2}>\n <Text>Overwrite Fresh Translations</Text>\n <Text size={1} muted>\n All existing translations will be overwritten even if they are fresh.\n </Text>\n </Stack>\n </Inline>\n </Button>\n </Flex>\n </Stack>\n )\n}\n","import { Card, Label, Stack } from '@sanity/ui'\nimport type { FC } from 'react'\nimport { ActionButtonBar } from './ActionButtonBar'\nimport { LanguageSelectionPanel } from './LanguageSelectionPanel'\nimport { TranslationModePanel } from './TranslationModePanel'\nimport { WriteSettingsPanel } from './WriteSettingsPanel'\n\ninterface TranslationConfigViewProps {\n setIsDialogOpen: (isOpen: boolean) => void\n}\n\nexport const TranslationConfigView: FC<TranslationConfigViewProps> = ({ setIsDialogOpen }) => {\n return (\n <Stack space={4}>\n <Stack space={2}>\n <Label>Languages</Label>\n <LanguageSelectionPanel />\n </Stack>\n\n <Stack space={2}>\n <Label>Translation Method</Label>\n <TranslationModePanel />\n </Stack>\n\n <Stack space={2}>\n <Label>Settings</Label>\n <Card padding={4} border muted>\n <WriteSettingsPanel />\n </Card>\n </Stack>\n <ActionButtonBar setIsDialogOpen={setIsDialogOpen} />\n </Stack>\n )\n}\n","import { ArrowRightIcon, CheckmarkIcon, CircleIcon, ErrorOutlineIcon, PanelRightIcon } from '@sanity/icons'\nimport { Badge, Box, Button, Card, Code, Flex, Heading, Inline, Label, Spinner, Stack, Text, Tooltip } from '@sanity/ui'\nimport { useMemo } from 'react'\nimport { useOpenInNewPane } from 'sanity-plugin-utils'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\n\nconst TERMINAL_STATUSES = ['completed', 'skipped', 'error']\n\nexport const TranslationStatusPanel = () => {\n const { overallStatus, translationJobs, translationMode } = useTranslationContext()\n\n const headline = useMemo(\n () => (overallStatus === 'completed' ? 'Translation completed' : 'Translation in progress'),\n [overallStatus],\n )\n\n const jobs = useMemo(() => Array.from(translationJobs.values()), [translationJobs])\n\n // Calculate OpenAI usage if applicable\n const openaiStats = useMemo(() => {\n if (translationMode === 'sanity-only') return null\n\n const completedJobs = jobs.filter((j) => j.status === 'completed')\n const totalTokens = completedJobs.reduce((sum, job) => sum + (job.tokensUsed || 0), 0)\n const totalCost = completedJobs.reduce((sum, job) => sum + (job.estimatedCost || 0), 0)\n\n return { totalTokens, totalCost, jobsWithUsage: completedJobs.filter((j) => j.tokensUsed).length }\n }, [jobs, translationMode])\n\n return (\n <Stack space={4}>\n <Heading size={2}>{headline}</Heading>\n <ProgressBar progress={jobs.filter((job) => TERMINAL_STATUSES.includes(job.status)).length} max={jobs.length} />\n <Stack space={3}>\n <Label>Translations</Label>\n <Card padding={2} tone=\"transparent\">\n <Stack space={2}>\n <ul style={{ listStyleType: 'none', paddingInlineStart: '0' }}>\n {jobs.map((job) => (\n <li key={job._id}>\n <Box padding={3}>\n <Flex justify=\"space-between\" align=\"center\">\n <Inline space={2}>\n <Flex align=\"center\" justify=\"center\" style={{ width: '24px', height: '24px' }}>\n {['completed', 'skipped'].includes(job.status) ? (\n <CheckmarkIcon fontSize={24} />\n ) : job.status === 'error' ? (\n <Box style={{ color: 'var(--card-critical-fg-color)' }}>\n <ErrorOutlineIcon fontSize={24} />\n </Box>\n ) : job.status === 'queued' ? (\n <CircleIcon fontSize={24} />\n ) : (\n <Spinner />\n )}\n </Flex>\n <Stack space={3}>\n <Inline space={2}>\n <Text size={1} weight=\"semibold\">\n {job.title || 'Untitled Document'}\n </Text>\n <ArrowRightIcon fontSize={18} />\n <Code size={1}>{job.language}</Code>\n </Inline>\n <Code size={1}>\n {job._type} — {job._id.slice(-6)}\n </Code>\n {!['completed', 'skipped', 'error'].includes(job.status) && (\n <Text size={1} weight=\"semibold\">\n {getStatusLabel(job.status)}\n </Text>\n )}\n {job.status === 'completed' && job.tokensUsed ? (\n <Text size={1} muted>\n {job.tokensUsed.toLocaleString()} tokens (~${job.estimatedCost?.toFixed(4)})\n </Text>\n ) : null}\n {job.status === 'error' && job.errorMessage ? (\n <Card tone=\"critical\" padding={2} radius={2}>\n <Text size={1}>Error: {job.errorMessage}</Text>\n </Card>\n ) : null}\n </Stack>\n </Inline>\n <Flex gap={2} align=\"center\">\n {job.status === 'skipped' ? <SkippedBadge /> : null}\n {job.status === 'error' ? <ErrorBadge /> : null}\n {job.targetId ? (\n <TranslationOpenerButton\n target={{\n _id: job.targetId,\n _type: job._type as string,\n }}\n />\n ) : null}\n </Flex>\n </Flex>\n </Box>\n </li>\n ))}\n </ul>\n </Stack>\n </Card>\n </Stack>\n\n {/* OpenAI Usage Summary */}\n {openaiStats && openaiStats.jobsWithUsage > 0 && (\n <Card tone=\"primary\" padding={4} border>\n <Stack space={2}>\n <Text weight=\"semibold\">OpenAI Usage Summary</Text>\n <Inline space={4}>\n <Text size={1}>\n 📊 Total Tokens: <strong>{openaiStats.totalTokens.toLocaleString()}</strong>\n </Text>\n <Text size={1}>\n 💰 Total Cost: <strong>${openaiStats.totalCost.toFixed(4)}</strong>\n </Text>\n </Inline>\n </Stack>\n </Card>\n )}\n </Stack>\n )\n}\n\nconst ProgressBar = ({ progress, max = 100 }: { progress: number; max?: number }) => {\n const percentage = Math.min((progress / max) * 100, 100)\n\n return (\n <Stack space={2}>\n <div\n style={{\n width: '100%',\n height: '8px',\n backgroundColor: 'var(--card-border-color)',\n borderRadius: '4px',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n width: `${percentage}%`,\n height: '100%',\n backgroundColor: 'var(--card-accent-fg-color)',\n borderRadius: '4px',\n transition: 'width 0.3s ease-in-out',\n }}\n />\n </div>\n <Flex justify=\"space-between\" align=\"center\">\n <Text size={1} muted>\n {percentage.toFixed(0)}%\n </Text>\n <Text size={1} muted>\n {progress} / {max} jobs completed.\n </Text>\n </Flex>\n </Stack>\n )\n}\n\nconst TranslationOpenerButton = ({ target }: { target: { _id: string; _type: string } }) => {\n const openInNewPane = useOpenInNewPane(target._id, target._type)\n\n return <Button mode=\"bleed\" text=\"View\" iconRight={PanelRightIcon} onClick={() => openInNewPane()} />\n}\n\nconst SkippedBadge = () => (\n <Tooltip\n portal\n content={\n <Box padding={1}>\n <Text size={1} muted>\n There was already a fresh translation for this document and language.\n </Text>\n </Box>\n }\n >\n <Badge tone=\"neutral\">Skipped</Badge>\n </Tooltip>\n)\n\nconst ErrorBadge = () => (\n <Tooltip\n portal\n content={\n <Box padding={1}>\n <Text size={1} muted>\n This translation failed. Check the error message for details.\n </Text>\n </Box>\n }\n >\n <Badge tone=\"critical\">Failed</Badge>\n </Tooltip>\n)\n\nconst getStatusLabel = (status: string): string => {\n const labels: Record<string, string> = {\n idle: 'Idle',\n queued: 'Queued...',\n preparing_translation: 'Preparing translation...',\n translating: 'Translating with Sanity Agent...',\n translating_with_openai: 'Translating with GPT-5...',\n reviewing_with_openai: 'Reviewing translation with GPT-5...',\n updating_metadata: 'Saving metadata...',\n error: 'Error',\n skipped: 'Skipped',\n completed: 'Completed',\n }\n return labels[status] || status\n}\n","import type { FC } from 'react'\nimport { useTranslationContext } from '../hooks/useTranslationContext'\nimport type { OverallTranslationStatus } from '../types'\nimport { devLog } from '../utils/common/devLog'\nimport { TranslationConfigView } from './TranslationConfigPanel'\nimport { TranslationStatusPanel } from './TranslationStatusPanel'\n\ninterface TranslationDialogProps {\n setIsDialogOpen: (isOpen: boolean) => void\n}\n\nconst STATES_IN_WHICH_TO_SHOW_CONFIG: Array<OverallTranslationStatus> = ['fetching_metadata', 'idle']\n\nconst STATES_IN_WHICH_TO_SHOW_TRANSLATION_PROGRESS: Array<OverallTranslationStatus> = [\n 'translating',\n 'linking_references',\n 'completed',\n]\n\nconst STATES_IN_WHICH_TO_SHOW_ERROR: Array<OverallTranslationStatus> = ['error']\n\nexport const TranslationDialog: FC<TranslationDialogProps> = ({ setIsDialogOpen }) => {\n const { overallStatus, document } = useTranslationContext()\n\n devLog.debug('TranslationDialog rendered', {\n documentId: document._id,\n overallStatus,\n component: 'TranslationDialog',\n })\n\n if (STATES_IN_WHICH_TO_SHOW_CONFIG.includes(overallStatus)) {\n devLog.debug('Showing translation config view', {\n documentId: document._id,\n overallStatus,\n component: 'TranslationDialog',\n })\n return <TranslationConfigView setIsDialogOpen={setIsDialogOpen} />\n }\n\n if (STATES_IN_WHICH_TO_SHOW_TRANSLATION_PROGRESS.includes(overallStatus)) {\n devLog.debug('Showing translation progress panel', {\n documentId: document._id,\n overallStatus,\n component: 'TranslationDialog',\n })\n return <TranslationStatusPanel />\n }\n\n if (STATES_IN_WHICH_TO_SHOW_ERROR.includes(overallStatus)) {\n devLog.warn('Translation error state reached', {\n documentId: document._id,\n overallStatus,\n component: 'TranslationDialog',\n })\n return <div>Error occurred</div>\n }\n\n devLog.warn('Unhandled translation dialog state', {\n documentId: document._id,\n overallStatus,\n component: 'TranslationDialog',\n })\n\n return <div>Unhandled overall state</div>\n}\n","import { TranslateIcon } from '@sanity/icons'\nimport { useState } from 'react'\nimport type { DocumentActionDescription, DocumentActionProps } from 'sanity'\nimport { TranslationDialog } from './components/TranslationDialog'\nimport { TranslationContextProvider } from './hooks/useTranslationContext/TranslationContextProvider'\nimport type { PluginConfig, TranslatableDocument } from './types'\nimport { devLog } from './utils/common/devLog'\n\ninterface TranslateActionProps extends DocumentActionProps {\n config: PluginConfig\n}\n\nexport const getTranslateAction = (config: PluginConfig) => {\n return ({ draft, published }: TranslateActionProps): DocumentActionDescription | null => {\n const document = draft ?? published\n const [isDialogOpen, setIsDialogOpen] = useState(false)\n\n devLog.debug('TranslateAction initialized', {\n documentId: document?._id,\n documentType: document?._type,\n })\n\n if (!document || !config.translatableSchemaTypes?.includes(document._type)) {\n devLog.warn('Document not in translatable schema types', {\n documentType: document?._type,\n translatableTypes: config.translatableSchemaTypes,\n })\n return null\n }\n\n if (!config) {\n devLog.error('Missing config in TranslateAction')\n return null\n }\n\n return {\n label: 'Translate',\n icon: TranslateIcon,\n onHandle: () => {\n devLog.info('Translation dialog opened', {\n documentId: document._id,\n documentType: document._type,\n component: 'TranslateAction',\n })\n setIsDialogOpen(true)\n },\n dialog: isDialogOpen && {\n type: 'dialog',\n onClose: () => setIsDialogOpen(false),\n header: 'Translate Document',\n content: (\n <TranslationContextProvider document={document as TranslatableDocument} config={config}>\n <TranslationDialog setIsDialogOpen={setIsDialogOpen} />\n </TranslationContextProvider>\n ),\n },\n }\n }\n}\n","import { definePlugin } from 'sanity'\nimport { getGiveTranslationFeedbackAction } from './GiveTranslationFeedbackAction'\nimport { translationGlossaryTerm } from './schemas/translationGlossaryTerm.schema'\nimport { translationHistory } from './schemas/translationHistory.schema'\nimport { translationSettings } from './schemas/translationSettings.schema'\nimport { getTranslateAction } from './TranslateAction'\nimport type { PluginConfig } from './types'\n\n/**\n * AI translation plugin for Sanity Studio\n * See the PluginConfig export for configuration options\n *\n * @public\n */\nexport const aiTranslationPlugin = definePlugin<PluginConfig>((config) => {\n return {\n name: 'sanity-plugin-ai-translation',\n document: {\n actions: [getTranslateAction(config), getGiveTranslationFeedbackAction(config)],\n },\n schema: {\n types: [translationSettings, translationGlossaryTerm],\n },\n }\n})\n\n/**\n * Schema for translation history\n *\n * @public\n */\nexport const translationHistorySchema = translationHistory\n"],"names":["translationHistory","translationSettings","releaseId","result"],"mappings":"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"}