@eigenpal/docx-js-editor 0.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core-plugins/registry.ts","../src/agent/executor.ts","../src/core-plugins/types.ts","../src/core-plugins/index.ts","../src/core-plugins/docxtemplater/handlers.ts","../src/utils/variableDetector.ts","../src/utils/processTemplate.ts","../src/docx/unzip.ts","../src/docx/xmlParser.ts","../src/docx/relsParser.ts","../src/docx/themeParser.ts","../src/docx/styleParser.ts","../src/docx/numberingParser.ts","../src/docx/imageParser.ts","../src/docx/runParser.ts","../src/docx/hyperlinkParser.ts","../src/docx/bookmarkParser.ts","../src/docx/tableParser.ts","../src/docx/headerFooterParser.ts","../src/docx/footnoteParser.ts","../src/docx/sectionParser.ts","../src/docx/runConsolidator.ts","../src/docx/paragraphParser.ts","../src/docx/documentParser.ts","../src/utils/fontLoader.ts","../src/docx/parser.ts","../src/core-plugins/docxtemplater/mcp-tools.ts","../src/core-plugins/docxtemplater/index.ts"],"sourcesContent":["/**\n * Plugin Registry\n *\n * Central registry for core plugins. Manages plugin lifecycle,\n * collects command handlers from all plugins, and aggregates\n * MCP tool definitions for the MCP server.\n */\n\nimport type {\n CorePlugin,\n CommandHandler,\n McpToolDefinition,\n PluginEvent,\n PluginEventListener,\n PluginRegistrationResult,\n PluginOptions,\n} from './types';\n\n// ============================================================================\n// PLUGIN REGISTRY\n// ============================================================================\n\n/**\n * Plugin Registry - manages core plugins\n *\n * @example\n * ```ts\n * import { pluginRegistry, docxtemplaterPlugin } from '@eigenpal/docx-editor/core-plugins';\n *\n * // Register plugins\n * pluginRegistry.register(docxtemplaterPlugin);\n *\n * // Get all MCP tools for MCP server\n * const tools = pluginRegistry.getMcpTools();\n *\n * // Get command handler for executor\n * const handler = pluginRegistry.getCommandHandler('insertTemplateVariable');\n * ```\n */\nexport class PluginRegistry {\n private plugins: Map<string, CorePlugin> = new Map();\n private commandHandlers: Map<string, { pluginId: string; handler: CommandHandler }> = new Map();\n private eventListeners: Set<PluginEventListener> = new Set();\n private initialized: Set<string> = new Set();\n\n // ==========================================================================\n // REGISTRATION\n // ==========================================================================\n\n /**\n * Register a plugin\n *\n * @param plugin - The plugin to register\n * @param options - Optional configuration\n * @returns Registration result\n */\n register(plugin: CorePlugin, options?: PluginOptions): PluginRegistrationResult {\n const warnings: string[] = [];\n\n // Validate plugin\n if (!plugin.id) {\n return { success: false, error: 'Plugin must have an id' };\n }\n\n if (this.plugins.has(plugin.id)) {\n return { success: false, error: `Plugin '${plugin.id}' is already registered` };\n }\n\n // Check dependencies\n if (plugin.dependencies) {\n for (const depId of plugin.dependencies) {\n if (!this.plugins.has(depId)) {\n return {\n success: false,\n error: `Plugin '${plugin.id}' requires '${depId}' which is not registered`,\n };\n }\n }\n }\n\n // Register command handlers\n if (plugin.commandHandlers) {\n for (const [commandType, handler] of Object.entries(plugin.commandHandlers)) {\n if (this.commandHandlers.has(commandType)) {\n const existing = this.commandHandlers.get(commandType)!;\n warnings.push(\n `Command '${commandType}' from '${plugin.id}' overrides handler from '${existing.pluginId}'`\n );\n }\n this.commandHandlers.set(commandType, { pluginId: plugin.id, handler });\n }\n }\n\n // Store plugin\n this.plugins.set(plugin.id, plugin);\n\n // Initialize if needed\n if (plugin.initialize && !this.initialized.has(plugin.id)) {\n try {\n const result = plugin.initialize();\n if (result instanceof Promise) {\n // Handle async initialization\n result\n .then(() => {\n this.initialized.add(plugin.id);\n })\n .catch((err) => {\n this.emit({ type: 'error', pluginId: plugin.id, error: err as Error });\n });\n } else {\n this.initialized.add(plugin.id);\n }\n } catch (err) {\n this.emit({ type: 'error', pluginId: plugin.id, error: err as Error });\n }\n }\n\n // Log if debug enabled\n if (options?.debug) {\n console.log(`[PluginRegistry] Registered plugin: ${plugin.id}`);\n }\n\n // Emit event\n this.emit({ type: 'registered', plugin });\n\n return {\n success: true,\n plugin,\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n }\n\n /**\n * Unregister a plugin\n *\n * @param pluginId - ID of the plugin to unregister\n * @returns Whether unregistration succeeded\n */\n unregister(pluginId: string): boolean {\n const plugin = this.plugins.get(pluginId);\n if (!plugin) {\n return false;\n }\n\n // Check if other plugins depend on this one\n for (const [id, p] of this.plugins) {\n if (p.dependencies?.includes(pluginId)) {\n console.warn(`Cannot unregister '${pluginId}': '${id}' depends on it`);\n return false;\n }\n }\n\n // Remove command handlers\n for (const [commandType, { pluginId: pid }] of this.commandHandlers) {\n if (pid === pluginId) {\n this.commandHandlers.delete(commandType);\n }\n }\n\n // Call destroy if available\n if (plugin.destroy) {\n try {\n const result = plugin.destroy();\n if (result instanceof Promise) {\n result.catch((err) => {\n this.emit({ type: 'error', pluginId, error: err as Error });\n });\n }\n } catch (err) {\n this.emit({ type: 'error', pluginId, error: err as Error });\n }\n }\n\n // Remove plugin\n this.plugins.delete(pluginId);\n this.initialized.delete(pluginId);\n\n // Emit event\n this.emit({ type: 'unregistered', pluginId });\n\n return true;\n }\n\n // ==========================================================================\n // QUERIES\n // ==========================================================================\n\n /**\n * Get a registered plugin by ID\n *\n * @param id - Plugin ID\n * @returns The plugin or undefined\n */\n get(id: string): CorePlugin | undefined {\n return this.plugins.get(id);\n }\n\n /**\n * Get all registered plugins\n *\n * @returns Array of all plugins\n */\n getAll(): CorePlugin[] {\n return Array.from(this.plugins.values());\n }\n\n /**\n * Check if a plugin is registered\n *\n * @param id - Plugin ID\n * @returns Whether the plugin is registered\n */\n has(id: string): boolean {\n return this.plugins.has(id);\n }\n\n /**\n * Get number of registered plugins\n */\n get size(): number {\n return this.plugins.size;\n }\n\n // ==========================================================================\n // COMMAND HANDLERS\n // ==========================================================================\n\n /**\n * Get a command handler for a command type\n *\n * @param commandType - The command type\n * @returns The handler or undefined\n */\n getCommandHandler(commandType: string): CommandHandler | undefined {\n const entry = this.commandHandlers.get(commandType);\n return entry?.handler;\n }\n\n /**\n * Get all registered command types\n *\n * @returns Array of command type strings\n */\n getCommandTypes(): string[] {\n return Array.from(this.commandHandlers.keys());\n }\n\n /**\n * Check if a command type has a handler\n *\n * @param commandType - The command type\n * @returns Whether a handler exists\n */\n hasCommandHandler(commandType: string): boolean {\n return this.commandHandlers.has(commandType);\n }\n\n // ==========================================================================\n // MCP TOOLS\n // ==========================================================================\n\n /**\n * Get all MCP tools from all registered plugins\n *\n * @returns Array of MCP tool definitions\n */\n getMcpTools(): McpToolDefinition[] {\n const tools: McpToolDefinition[] = [];\n\n for (const plugin of this.plugins.values()) {\n if (plugin.mcpTools) {\n tools.push(...plugin.mcpTools);\n }\n }\n\n return tools;\n }\n\n /**\n * Get MCP tools from a specific plugin\n *\n * @param pluginId - Plugin ID\n * @returns Array of MCP tool definitions\n */\n getMcpToolsForPlugin(pluginId: string): McpToolDefinition[] {\n const plugin = this.plugins.get(pluginId);\n return plugin?.mcpTools || [];\n }\n\n /**\n * Get an MCP tool by name\n *\n * @param toolName - Tool name\n * @returns The tool definition or undefined\n */\n getMcpTool(toolName: string): McpToolDefinition | undefined {\n for (const plugin of this.plugins.values()) {\n if (plugin.mcpTools) {\n const tool = plugin.mcpTools.find((t) => t.name === toolName);\n if (tool) return tool;\n }\n }\n return undefined;\n }\n\n // ==========================================================================\n // EVENTS\n // ==========================================================================\n\n /**\n * Add an event listener\n *\n * @param listener - Event listener function\n */\n addEventListener(listener: PluginEventListener): void {\n this.eventListeners.add(listener);\n }\n\n /**\n * Remove an event listener\n *\n * @param listener - Event listener function\n */\n removeEventListener(listener: PluginEventListener): void {\n this.eventListeners.delete(listener);\n }\n\n /**\n * Emit an event to all listeners\n */\n private emit(event: PluginEvent): void {\n for (const listener of this.eventListeners) {\n try {\n listener(event);\n } catch (err) {\n console.error('[PluginRegistry] Event listener error:', err);\n }\n }\n }\n\n // ==========================================================================\n // UTILITIES\n // ==========================================================================\n\n /**\n * Clear all registered plugins\n *\n * Useful for testing or resetting state.\n */\n clear(): void {\n // Call destroy on all plugins\n for (const plugin of this.plugins.values()) {\n if (plugin.destroy) {\n try {\n plugin.destroy();\n } catch {\n // Ignore errors during clear\n }\n }\n }\n\n this.plugins.clear();\n this.commandHandlers.clear();\n this.initialized.clear();\n }\n\n /**\n * Get registry state for debugging\n */\n getDebugInfo(): {\n plugins: string[];\n commandTypes: string[];\n mcpTools: string[];\n initialized: string[];\n } {\n return {\n plugins: Array.from(this.plugins.keys()),\n commandTypes: Array.from(this.commandHandlers.keys()),\n mcpTools: this.getMcpTools().map((t) => t.name),\n initialized: Array.from(this.initialized),\n };\n }\n}\n\n// ============================================================================\n// GLOBAL INSTANCE\n// ============================================================================\n\n/**\n * Global plugin registry instance\n *\n * Use this for registering plugins and accessing their capabilities.\n */\nexport const pluginRegistry = new PluginRegistry();\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Register multiple plugins at once\n *\n * @param plugins - Array of plugins to register\n * @returns Array of registration results\n */\nexport function registerPlugins(\n plugins: CorePlugin[],\n options?: PluginOptions\n): PluginRegistrationResult[] {\n return plugins.map((plugin) => pluginRegistry.register(plugin, options));\n}\n\n/**\n * Create a plugin registration helper with options pre-configured\n *\n * @param options - Default options for plugin registration\n * @returns Registration function\n */\nexport function createPluginRegistrar(options: PluginOptions) {\n return (plugin: CorePlugin) => pluginRegistry.register(plugin, options);\n}\n\n// ============================================================================\n// EXPORTS\n// ============================================================================\n\nexport default pluginRegistry;\n","/**\n * Command Executor\n *\n * Executes agent commands on a document immutably:\n * - Handles all command types from AgentCommand\n * - Preserves surrounding formatting\n * - Returns new document (immutable updates)\n */\n\nimport type {\n Document,\n DocumentBody,\n Paragraph,\n Run,\n TextContent,\n ParagraphContent,\n BlockContent,\n Table,\n TableRow,\n TableCell,\n TextFormatting,\n Image,\n Hyperlink,\n} from '../types/document';\n\nimport type {\n AgentCommand,\n InsertTextCommand,\n ReplaceTextCommand,\n DeleteTextCommand,\n FormatTextCommand,\n FormatParagraphCommand,\n ApplyStyleCommand,\n InsertTableCommand,\n InsertImageCommand,\n InsertHyperlinkCommand,\n RemoveHyperlinkCommand,\n InsertParagraphBreakCommand,\n MergeParagraphsCommand,\n SplitParagraphCommand,\n SetVariableCommand,\n ApplyVariablesCommand,\n} from '../types/agentApi';\n\nimport { pluginRegistry } from '../core-plugins/registry';\n\n// ============================================================================\n// MAIN EXECUTOR\n// ============================================================================\n\n/**\n * Execute an agent command on a document\n * Returns a new document with the command applied (immutable)\n *\n * Dispatch order:\n * 1. Try plugin handlers first (allows plugins to override built-in commands)\n * 2. Fall back to built-in handlers\n *\n * @param doc - The document to modify\n * @param command - The command to execute\n * @returns New document with command applied\n */\nexport function executeCommand(doc: Document, command: AgentCommand): Document {\n // Try plugin handlers first\n const pluginHandler = pluginRegistry.getCommandHandler(command.type);\n if (pluginHandler) {\n // Plugin commands use a more flexible type\n return pluginHandler(doc, command as unknown as import('../core-plugins/types').PluginCommand);\n }\n\n // Fall back to built-in handlers\n switch (command.type) {\n case 'insertText':\n return executeInsertText(doc, command);\n case 'replaceText':\n return executeReplaceText(doc, command);\n case 'deleteText':\n return executeDeleteText(doc, command);\n case 'formatText':\n return executeFormatText(doc, command);\n case 'formatParagraph':\n return executeFormatParagraph(doc, command);\n case 'applyStyle':\n return executeApplyStyle(doc, command);\n case 'insertTable':\n return executeInsertTable(doc, command);\n case 'insertImage':\n return executeInsertImage(doc, command);\n case 'insertHyperlink':\n return executeInsertHyperlink(doc, command);\n case 'removeHyperlink':\n return executeRemoveHyperlink(doc, command);\n case 'insertParagraphBreak':\n return executeInsertParagraphBreak(doc, command);\n case 'mergeParagraphs':\n return executeMergeParagraphs(doc, command);\n case 'splitParagraph':\n return executeSplitParagraph(doc, command);\n case 'setVariable':\n return executeSetVariable(doc, command);\n case 'applyVariables':\n return executeApplyVariables(doc, command);\n default:\n // Exhaustive check - should never happen with proper types\n const _exhaustive: never = command;\n throw new Error(`Unknown command type: ${(_exhaustive as AgentCommand).type}`);\n }\n}\n\n/**\n * Execute multiple commands in sequence\n *\n * @param doc - The document to modify\n * @param commands - Commands to execute in order\n * @returns New document with all commands applied\n */\nexport function executeCommands(doc: Document, commands: AgentCommand[]): Document {\n return commands.reduce((currentDoc, command) => executeCommand(currentDoc, command), doc);\n}\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Deep clone a document for immutable updates\n */\nfunction cloneDocument(doc: Document): Document {\n return JSON.parse(JSON.stringify(doc));\n}\n\n/**\n * Get the block index for a paragraph index\n */\nfunction getBlockIndexForParagraph(body: DocumentBody, paragraphIndex: number): number {\n let currentParagraphIndex = 0;\n for (let i = 0; i < body.content.length; i++) {\n if (body.content[i].type === 'paragraph') {\n if (currentParagraphIndex === paragraphIndex) {\n return i;\n }\n currentParagraphIndex++;\n }\n }\n return -1;\n}\n\n/**\n * Get plain text from a paragraph\n */\nfunction getParagraphText(paragraph: Paragraph): string {\n let text = '';\n for (const item of paragraph.content) {\n if (item.type === 'run') {\n for (const content of item.content) {\n if (content.type === 'text') {\n text += content.text;\n }\n }\n } else if (item.type === 'hyperlink') {\n for (const child of item.children) {\n if (child.type === 'run') {\n for (const content of child.content) {\n if (content.type === 'text') {\n text += content.text;\n }\n }\n }\n }\n }\n }\n return text;\n}\n\n/**\n * Create a new run with text\n */\nfunction createTextRun(text: string, formatting?: TextFormatting): Run {\n return {\n type: 'run',\n formatting,\n content: [\n {\n type: 'text',\n text,\n },\n ],\n };\n}\n\n/**\n * Insert text at a specific offset within a paragraph\n * Returns new paragraph content\n */\nfunction insertTextAtOffset(\n paragraph: Paragraph,\n offset: number,\n text: string,\n formatting?: TextFormatting\n): ParagraphContent[] {\n const newContent: ParagraphContent[] = [];\n let currentOffset = 0;\n let inserted = false;\n\n for (const item of paragraph.content) {\n if (item.type === 'run') {\n const runText = item.content\n .filter((c): c is TextContent => c.type === 'text')\n .map((c) => c.text)\n .join('');\n\n const runStart = currentOffset;\n const runEnd = currentOffset + runText.length;\n\n if (!inserted && offset >= runStart && offset <= runEnd) {\n // Insert within this run\n const insertPos = offset - runStart;\n\n if (insertPos > 0) {\n // Text before insertion point\n newContent.push({\n ...item,\n content: [{ type: 'text', text: runText.slice(0, insertPos) }],\n });\n }\n\n // New text\n newContent.push(createTextRun(text, formatting || item.formatting));\n\n if (insertPos < runText.length) {\n // Text after insertion point\n newContent.push({\n ...item,\n content: [{ type: 'text', text: runText.slice(insertPos) }],\n });\n }\n\n inserted = true;\n } else {\n newContent.push(item);\n }\n\n currentOffset = runEnd;\n } else {\n newContent.push(item);\n }\n }\n\n // If not inserted yet, append at the end\n if (!inserted) {\n newContent.push(createTextRun(text, formatting));\n }\n\n return newContent;\n}\n\n/**\n * Delete text in a range within a single paragraph\n */\nfunction deleteTextInParagraph(\n paragraph: Paragraph,\n startOffset: number,\n endOffset: number\n): ParagraphContent[] {\n const newContent: ParagraphContent[] = [];\n let currentOffset = 0;\n\n for (const item of paragraph.content) {\n if (item.type === 'run') {\n const runText = item.content\n .filter((c): c is TextContent => c.type === 'text')\n .map((c) => c.text)\n .join('');\n\n const runStart = currentOffset;\n const runEnd = currentOffset + runText.length;\n\n // Check if run overlaps with deletion range\n if (runEnd <= startOffset || runStart >= endOffset) {\n // No overlap, keep entire run\n newContent.push(item);\n } else {\n // Partial overlap\n let newText = '';\n\n if (runStart < startOffset) {\n // Keep text before start\n newText += runText.slice(0, startOffset - runStart);\n }\n\n if (runEnd > endOffset) {\n // Keep text after end\n newText += runText.slice(endOffset - runStart);\n }\n\n if (newText.length > 0) {\n newContent.push({\n ...item,\n content: [{ type: 'text', text: newText }],\n });\n }\n }\n\n currentOffset = runEnd;\n } else {\n newContent.push(item);\n }\n }\n\n return newContent;\n}\n\n/**\n * Apply formatting to text in a range within a paragraph\n */\nfunction applyFormattingInParagraph(\n paragraph: Paragraph,\n startOffset: number,\n endOffset: number,\n formatting: Partial<TextFormatting>\n): ParagraphContent[] {\n const newContent: ParagraphContent[] = [];\n let currentOffset = 0;\n\n for (const item of paragraph.content) {\n if (item.type === 'run') {\n const runText = item.content\n .filter((c): c is TextContent => c.type === 'text')\n .map((c) => c.text)\n .join('');\n\n const runStart = currentOffset;\n const runEnd = currentOffset + runText.length;\n\n // Check if run overlaps with formatting range\n if (runEnd <= startOffset || runStart >= endOffset) {\n // No overlap, keep entire run unchanged\n newContent.push(item);\n } else if (runStart >= startOffset && runEnd <= endOffset) {\n // Entire run is within range, apply formatting\n newContent.push({\n ...item,\n formatting: { ...item.formatting, ...formatting },\n });\n } else {\n // Partial overlap - need to split run\n const overlapStart = Math.max(startOffset, runStart);\n const overlapEnd = Math.min(endOffset, runEnd);\n\n // Text before overlap\n if (runStart < overlapStart) {\n newContent.push({\n ...item,\n content: [{ type: 'text', text: runText.slice(0, overlapStart - runStart) }],\n });\n }\n\n // Overlapping text with formatting\n newContent.push({\n ...item,\n formatting: { ...item.formatting, ...formatting },\n content: [\n {\n type: 'text',\n text: runText.slice(overlapStart - runStart, overlapEnd - runStart),\n },\n ],\n });\n\n // Text after overlap\n if (runEnd > overlapEnd) {\n newContent.push({\n ...item,\n content: [{ type: 'text', text: runText.slice(overlapEnd - runStart) }],\n });\n }\n }\n\n currentOffset = runEnd;\n } else {\n newContent.push(item);\n }\n }\n\n return newContent;\n}\n\n// ============================================================================\n// COMMAND IMPLEMENTATIONS\n// ============================================================================\n\n/**\n * Insert text at a position\n */\nfunction executeInsertText(doc: Document, command: InsertTextCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n const blockIndex = getBlockIndexForParagraph(body, command.position.paragraphIndex);\n\n if (blockIndex === -1) {\n throw new Error(`Paragraph index ${command.position.paragraphIndex} not found`);\n }\n\n const paragraph = body.content[blockIndex] as Paragraph;\n paragraph.content = insertTextAtOffset(\n paragraph,\n command.position.offset,\n command.text,\n command.formatting\n );\n\n return newDoc;\n}\n\n/**\n * Replace text in a range\n */\nfunction executeReplaceText(doc: Document, command: ReplaceTextCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n const { start, end } = command.range;\n\n if (start.paragraphIndex === end.paragraphIndex) {\n // Same paragraph\n const blockIndex = getBlockIndexForParagraph(body, start.paragraphIndex);\n if (blockIndex === -1) {\n throw new Error(`Paragraph index ${start.paragraphIndex} not found`);\n }\n\n const paragraph = body.content[blockIndex] as Paragraph;\n\n // Delete the range first\n paragraph.content = deleteTextInParagraph(paragraph, start.offset, end.offset);\n\n // Then insert the new text\n paragraph.content = insertTextAtOffset(\n paragraph,\n start.offset,\n command.text,\n command.formatting\n );\n } else {\n // Multiple paragraphs - simplify by deleting and inserting\n // Delete from start to end of first paragraph\n const startBlockIndex = getBlockIndexForParagraph(body, start.paragraphIndex);\n const startParagraph = body.content[startBlockIndex] as Paragraph;\n const startText = getParagraphText(startParagraph);\n\n startParagraph.content = deleteTextInParagraph(startParagraph, start.offset, startText.length);\n startParagraph.content = insertTextAtOffset(\n startParagraph,\n start.offset,\n command.text,\n command.formatting\n );\n\n // Delete intermediate paragraphs and beginning of last paragraph\n const paragraphsToRemove: number[] = [];\n for (let i = start.paragraphIndex + 1; i <= end.paragraphIndex; i++) {\n paragraphsToRemove.push(getBlockIndexForParagraph(body, i));\n }\n\n // Remove in reverse order to preserve indices\n for (let i = paragraphsToRemove.length - 1; i >= 0; i--) {\n if (paragraphsToRemove[i] !== -1) {\n body.content.splice(paragraphsToRemove[i], 1);\n }\n }\n }\n\n return newDoc;\n}\n\n/**\n * Delete text in a range\n */\nfunction executeDeleteText(doc: Document, command: DeleteTextCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n const { start, end } = command.range;\n\n if (start.paragraphIndex === end.paragraphIndex) {\n // Same paragraph\n const blockIndex = getBlockIndexForParagraph(body, start.paragraphIndex);\n if (blockIndex === -1) {\n throw new Error(`Paragraph index ${start.paragraphIndex} not found`);\n }\n\n const paragraph = body.content[blockIndex] as Paragraph;\n paragraph.content = deleteTextInParagraph(paragraph, start.offset, end.offset);\n } else {\n // Multiple paragraphs\n // Truncate first paragraph\n const startBlockIndex = getBlockIndexForParagraph(body, start.paragraphIndex);\n const startParagraph = body.content[startBlockIndex] as Paragraph;\n const startText = getParagraphText(startParagraph);\n startParagraph.content = deleteTextInParagraph(startParagraph, start.offset, startText.length);\n\n // Delete intermediate paragraphs and truncate last\n const endBlockIndex = getBlockIndexForParagraph(body, end.paragraphIndex);\n const endParagraph = body.content[endBlockIndex] as Paragraph;\n endParagraph.content = deleteTextInParagraph(endParagraph, 0, end.offset);\n\n // Merge last paragraph content into first\n startParagraph.content.push(...endParagraph.content);\n\n // Remove paragraphs between start and end (inclusive of end)\n const indicesToRemove: number[] = [];\n for (let i = start.paragraphIndex + 1; i <= end.paragraphIndex; i++) {\n indicesToRemove.push(getBlockIndexForParagraph(body, i));\n }\n\n for (let i = indicesToRemove.length - 1; i >= 0; i--) {\n if (indicesToRemove[i] !== -1) {\n body.content.splice(indicesToRemove[i], 1);\n }\n }\n }\n\n return newDoc;\n}\n\n/**\n * Apply formatting to a range\n */\nfunction executeFormatText(doc: Document, command: FormatTextCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n const { start, end } = command.range;\n\n if (start.paragraphIndex === end.paragraphIndex) {\n // Same paragraph\n const blockIndex = getBlockIndexForParagraph(body, start.paragraphIndex);\n if (blockIndex === -1) {\n throw new Error(`Paragraph index ${start.paragraphIndex} not found`);\n }\n\n const paragraph = body.content[blockIndex] as Paragraph;\n paragraph.content = applyFormattingInParagraph(\n paragraph,\n start.offset,\n end.offset,\n command.formatting\n );\n } else {\n // Multiple paragraphs\n for (let i = start.paragraphIndex; i <= end.paragraphIndex; i++) {\n const blockIndex = getBlockIndexForParagraph(body, i);\n if (blockIndex === -1) continue;\n\n const paragraph = body.content[blockIndex] as Paragraph;\n const paragraphText = getParagraphText(paragraph);\n\n let startOffset = 0;\n let endOffset = paragraphText.length;\n\n if (i === start.paragraphIndex) {\n startOffset = start.offset;\n }\n if (i === end.paragraphIndex) {\n endOffset = end.offset;\n }\n\n paragraph.content = applyFormattingInParagraph(\n paragraph,\n startOffset,\n endOffset,\n command.formatting\n );\n }\n }\n\n return newDoc;\n}\n\n/**\n * Apply paragraph formatting\n */\nfunction executeFormatParagraph(doc: Document, command: FormatParagraphCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n const blockIndex = getBlockIndexForParagraph(body, command.paragraphIndex);\n if (blockIndex === -1) {\n throw new Error(`Paragraph index ${command.paragraphIndex} not found`);\n }\n\n const paragraph = body.content[blockIndex] as Paragraph;\n paragraph.formatting = { ...paragraph.formatting, ...command.formatting };\n\n // Handle listRendering when numPr changes\n if ('numPr' in command.formatting) {\n const numPr = command.formatting.numPr;\n if (numPr && numPr.numId !== undefined && numPr.numId !== 0) {\n // Setting a list - compute listRendering\n const ilvl = numPr.ilvl ?? 0;\n const isBullet = numPr.numId === 1; // numId 1 is typically bullets, 2 is numbered\n\n // Try to get marker from numbering definitions if available\n let marker = isBullet ? '•' : `${1}.`; // Default markers\n\n if (newDoc.package.numbering) {\n const num = newDoc.package.numbering.nums.find((n) => n.numId === numPr.numId);\n if (num) {\n const abstractNum = newDoc.package.numbering.abstractNums.find(\n (a) => a.abstractNumId === num.abstractNumId\n );\n if (abstractNum) {\n const level = abstractNum.levels.find((l) => l.ilvl === ilvl);\n if (level) {\n marker = level.lvlText || marker;\n }\n }\n }\n }\n\n paragraph.listRendering = {\n level: ilvl,\n numId: numPr.numId,\n marker,\n isBullet,\n };\n } else {\n // Removing list - clear listRendering\n delete paragraph.listRendering;\n }\n }\n\n return newDoc;\n}\n\n/**\n * Apply a named style to a paragraph\n */\nfunction executeApplyStyle(doc: Document, command: ApplyStyleCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n const blockIndex = getBlockIndexForParagraph(body, command.paragraphIndex);\n if (blockIndex === -1) {\n throw new Error(`Paragraph index ${command.paragraphIndex} not found`);\n }\n\n const paragraph = body.content[blockIndex] as Paragraph;\n paragraph.formatting = {\n ...paragraph.formatting,\n styleId: command.styleId,\n };\n\n return newDoc;\n}\n\n/**\n * Insert a table at a position\n */\nfunction executeInsertTable(doc: Document, command: InsertTableCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n // Create table structure\n const rows: TableRow[] = [];\n\n for (let r = 0; r < command.rows; r++) {\n const cells: TableCell[] = [];\n\n for (let c = 0; c < command.columns; c++) {\n const cellText = command.data?.[r]?.[c] || '';\n cells.push({\n type: 'tableCell',\n content: [\n {\n type: 'paragraph',\n content: cellText ? [createTextRun(cellText)] : [],\n },\n ],\n });\n }\n\n rows.push({\n type: 'tableRow',\n formatting: r === 0 && command.hasHeader ? { header: true } : undefined,\n cells,\n });\n }\n\n const table: Table = {\n type: 'table',\n rows,\n };\n\n // Insert table after the specified paragraph\n const blockIndex = getBlockIndexForParagraph(body, command.position.paragraphIndex);\n if (blockIndex === -1) {\n body.content.push(table);\n } else {\n body.content.splice(blockIndex + 1, 0, table);\n }\n\n return newDoc;\n}\n\n/**\n * Insert an image at a position\n */\nfunction executeInsertImage(doc: Document, command: InsertImageCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n const blockIndex = getBlockIndexForParagraph(body, command.position.paragraphIndex);\n if (blockIndex === -1) {\n throw new Error(`Paragraph index ${command.position.paragraphIndex} not found`);\n }\n\n const paragraph = body.content[blockIndex] as Paragraph;\n\n // Create image\n const image: Image = {\n type: 'image',\n rId: `rId_img_${Date.now()}`,\n src: command.src,\n alt: command.alt,\n size: {\n width: (command.width || 100) * 914400, // Convert pixels to EMU\n height: (command.height || 100) * 914400,\n },\n wrap: { type: 'inline' },\n };\n\n // Create run with drawing content\n const imageRun: Run = {\n type: 'run',\n content: [\n {\n type: 'drawing',\n image,\n },\n ],\n };\n\n // Insert image run at offset\n const newContent = insertTextAtOffset(paragraph, command.position.offset, '', undefined);\n // Find insertion point and add image\n let inserted = false;\n let currentOffset = 0;\n\n for (let i = 0; i < newContent.length; i++) {\n const item = newContent[i];\n if (item.type === 'run') {\n const runText = item.content\n .filter((c): c is TextContent => c.type === 'text')\n .map((c) => c.text)\n .join('');\n currentOffset += runText.length;\n\n if (!inserted && currentOffset >= command.position.offset) {\n newContent.splice(i + 1, 0, imageRun);\n inserted = true;\n break;\n }\n }\n }\n\n if (!inserted) {\n newContent.push(imageRun);\n }\n\n paragraph.content = newContent;\n\n return newDoc;\n}\n\n/**\n * Insert a hyperlink at a range\n */\nfunction executeInsertHyperlink(doc: Document, command: InsertHyperlinkCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n const { start, end } = command.range;\n\n if (start.paragraphIndex !== end.paragraphIndex) {\n throw new Error('Hyperlinks cannot span multiple paragraphs');\n }\n\n const blockIndex = getBlockIndexForParagraph(body, start.paragraphIndex);\n if (blockIndex === -1) {\n throw new Error(`Paragraph index ${start.paragraphIndex} not found`);\n }\n\n const paragraph = body.content[blockIndex] as Paragraph;\n const paragraphText = getParagraphText(paragraph);\n\n // Get the text that will become the link\n const linkText = command.displayText || paragraphText.slice(start.offset, end.offset);\n\n // Delete the original text\n paragraph.content = deleteTextInParagraph(paragraph, start.offset, end.offset);\n\n // Create hyperlink\n const hyperlink: Hyperlink = {\n type: 'hyperlink',\n href: command.url,\n tooltip: command.tooltip,\n children: [createTextRun(linkText)],\n };\n\n // Insert hyperlink at position\n let inserted = false;\n let currentOffset = 0;\n const newContent: ParagraphContent[] = [];\n\n for (const item of paragraph.content) {\n if (item.type === 'run') {\n const runText = item.content\n .filter((c): c is TextContent => c.type === 'text')\n .map((c) => c.text)\n .join('');\n\n const runEnd = currentOffset + runText.length;\n\n if (!inserted && currentOffset <= start.offset && start.offset <= runEnd) {\n const insertPos = start.offset - currentOffset;\n\n if (insertPos > 0) {\n newContent.push({\n ...item,\n content: [{ type: 'text', text: runText.slice(0, insertPos) }],\n });\n }\n\n newContent.push(hyperlink);\n\n if (insertPos < runText.length) {\n newContent.push({\n ...item,\n content: [{ type: 'text', text: runText.slice(insertPos) }],\n });\n }\n\n inserted = true;\n } else {\n newContent.push(item);\n }\n\n currentOffset = runEnd;\n } else {\n newContent.push(item);\n }\n }\n\n if (!inserted) {\n newContent.push(hyperlink);\n }\n\n paragraph.content = newContent;\n\n return newDoc;\n}\n\n/**\n * Remove a hyperlink but keep the text\n */\nfunction executeRemoveHyperlink(doc: Document, command: RemoveHyperlinkCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n const { start } = command.range;\n\n const blockIndex = getBlockIndexForParagraph(body, start.paragraphIndex);\n if (blockIndex === -1) {\n throw new Error(`Paragraph index ${start.paragraphIndex} not found`);\n }\n\n const paragraph = body.content[blockIndex] as Paragraph;\n const newContent: ParagraphContent[] = [];\n\n for (const item of paragraph.content) {\n if (item.type === 'hyperlink') {\n // Convert hyperlink children to regular runs\n for (const child of item.children) {\n if (child.type === 'run') {\n newContent.push(child);\n }\n }\n } else {\n newContent.push(item);\n }\n }\n\n paragraph.content = newContent;\n\n return newDoc;\n}\n\n/**\n * Insert a paragraph break\n */\nfunction executeInsertParagraphBreak(\n doc: Document,\n command: InsertParagraphBreakCommand\n): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n const blockIndex = getBlockIndexForParagraph(body, command.position.paragraphIndex);\n if (blockIndex === -1) {\n throw new Error(`Paragraph index ${command.position.paragraphIndex} not found`);\n }\n\n const paragraph = body.content[blockIndex] as Paragraph;\n const paragraphText = getParagraphText(paragraph);\n\n // Split the paragraph at the offset\n const beforeContent = deleteTextInParagraph(\n { ...paragraph, content: [...paragraph.content] },\n command.position.offset,\n paragraphText.length\n );\n\n const afterContent = deleteTextInParagraph(\n { ...paragraph, content: [...paragraph.content] },\n 0,\n command.position.offset\n );\n\n // Update current paragraph with content before break\n paragraph.content = beforeContent;\n\n // Create new paragraph with content after break\n const newParagraph: Paragraph = {\n type: 'paragraph',\n formatting: paragraph.formatting,\n content: afterContent,\n };\n\n // Insert new paragraph after current one\n body.content.splice(blockIndex + 1, 0, newParagraph);\n\n return newDoc;\n}\n\n/**\n * Merge paragraphs\n */\nfunction executeMergeParagraphs(doc: Document, command: MergeParagraphsCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n const startBlockIndex = getBlockIndexForParagraph(body, command.paragraphIndex);\n if (startBlockIndex === -1) {\n throw new Error(`Paragraph index ${command.paragraphIndex} not found`);\n }\n\n const baseParagraph = body.content[startBlockIndex] as Paragraph;\n\n // Collect all content from paragraphs to merge\n const indicesToRemove: number[] = [];\n\n for (let i = 1; i <= command.count; i++) {\n const blockIndex = getBlockIndexForParagraph(body, command.paragraphIndex + i);\n if (blockIndex !== -1) {\n const para = body.content[blockIndex] as Paragraph;\n baseParagraph.content.push(...para.content);\n indicesToRemove.push(blockIndex);\n }\n }\n\n // Remove merged paragraphs in reverse order\n for (let i = indicesToRemove.length - 1; i >= 0; i--) {\n body.content.splice(indicesToRemove[i], 1);\n }\n\n return newDoc;\n}\n\n/**\n * Split a paragraph at a position\n */\nfunction executeSplitParagraph(doc: Document, command: SplitParagraphCommand): Document {\n // Split is the same as insert paragraph break\n return executeInsertParagraphBreak(doc, {\n type: 'insertParagraphBreak',\n position: command.position,\n });\n}\n\n/**\n * Set a template variable value\n */\nfunction executeSetVariable(doc: Document, command: SetVariableCommand): Document {\n const newDoc = cloneDocument(doc);\n\n // Store variable in document for later application\n if (!newDoc.templateVariables) {\n newDoc.templateVariables = [];\n }\n\n if (!newDoc.templateVariables.includes(command.name)) {\n newDoc.templateVariables.push(command.name);\n }\n\n // Note: Actual variable substitution happens in applyVariables\n return newDoc;\n}\n\n/**\n * Apply all template variables\n */\nfunction executeApplyVariables(doc: Document, command: ApplyVariablesCommand): Document {\n const newDoc = cloneDocument(doc);\n const body = newDoc.package.document;\n\n // Replace {variable} patterns in all text content\n function replaceVariablesInRun(run: Run): void {\n for (const content of run.content) {\n if (content.type === 'text') {\n for (const [name, value] of Object.entries(command.values)) {\n const pattern = new RegExp(`\\\\{${name}\\\\}`, 'g');\n content.text = content.text.replace(pattern, value);\n }\n }\n }\n }\n\n function replaceVariablesInParagraph(paragraph: Paragraph): void {\n for (const item of paragraph.content) {\n if (item.type === 'run') {\n replaceVariablesInRun(item);\n } else if (item.type === 'hyperlink') {\n for (const child of item.children) {\n if (child.type === 'run') {\n replaceVariablesInRun(child);\n }\n }\n }\n }\n }\n\n function replaceVariablesInBlock(block: BlockContent): void {\n if (block.type === 'paragraph') {\n replaceVariablesInParagraph(block);\n } else if (block.type === 'table') {\n for (const row of block.rows) {\n for (const cell of row.cells) {\n for (const cellBlock of cell.content) {\n replaceVariablesInBlock(cellBlock);\n }\n }\n }\n }\n }\n\n for (const block of body.content) {\n replaceVariablesInBlock(block);\n }\n\n return newDoc;\n}\n\n// ============================================================================\n// EXPORTS\n// ============================================================================\n\nexport default executeCommand;\n","/**\n * Core Plugin System Types\n *\n * Defines the interfaces for headless plugins that work in Node.js\n * without React/DOM dependencies. These plugins extend DocumentAgent\n * with additional commands and expose MCP tools for AI integration.\n */\n\nimport type { Document } from '../types/document';\nimport type { AgentCommand, Position, Range } from '../types/agentApi';\n\n// ============================================================================\n// PLUGIN INTERFACE\n// ============================================================================\n\n/**\n * Core plugin interface - headless, works in Node.js\n *\n * Plugins can:\n * - Register command handlers that DocumentAgent dispatches to\n * - Declare MCP tools that the MCP server exposes to AI clients\n * - Have optional initialization logic\n * - Declare dependencies on other plugins\n */\nexport interface CorePlugin {\n /** Unique plugin identifier */\n id: string;\n\n /** Human-readable plugin name */\n name: string;\n\n /** Plugin version (semver) */\n version?: string;\n\n /** Plugin description */\n description?: string;\n\n /**\n * Command handlers this plugin provides.\n * DocumentAgent dispatches commands to these handlers.\n *\n * @example\n * ```ts\n * commandHandlers: {\n * 'insertTemplateVariable': (doc, cmd) => {\n * // Transform document\n * return modifiedDoc;\n * },\n * }\n * ```\n */\n commandHandlers?: Record<string, CommandHandler>;\n\n /**\n * MCP tools this plugin exposes.\n * MCP server collects these from all plugins.\n */\n mcpTools?: McpToolDefinition[];\n\n /**\n * Optional setup when plugin is registered.\n * Called once during plugin registration.\n */\n initialize?: () => void | Promise<void>;\n\n /**\n * Optional cleanup when plugin is unregistered.\n */\n destroy?: () => void | Promise<void>;\n\n /**\n * Dependencies on other plugins (by ID).\n * The registry ensures dependencies are loaded first.\n */\n dependencies?: string[];\n}\n\n// ============================================================================\n// COMMAND TYPES\n// ============================================================================\n\n/**\n * Command handler function type\n *\n * Receives a document and a command, returns a modified document.\n * Must be pure/immutable - always return a new document.\n */\nexport type CommandHandler = (doc: Document, command: PluginCommand) => Document;\n\n/**\n * Extended command type for plugins\n *\n * Plugins can define custom command types beyond the built-in AgentCommand types.\n */\nexport interface PluginCommand {\n /** Command type identifier */\n type: string;\n\n /** Unique command ID (for undo tracking) */\n id?: string;\n\n /** Position for positional commands */\n position?: Position;\n\n /** Range for range-based commands */\n range?: Range;\n\n /** Additional command-specific data */\n [key: string]: unknown;\n}\n\n/**\n * Result of command execution\n */\nexport interface CommandResult {\n /** The modified document */\n document: Document;\n\n /** Whether the command succeeded */\n success: boolean;\n\n /** Error message if failed */\n error?: string;\n\n /** Metadata about the operation */\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// MCP TOOL TYPES\n// ============================================================================\n\n/**\n * MCP tool definition\n *\n * Describes a tool that can be called by AI clients through the MCP server.\n */\nexport interface McpToolDefinition {\n /** Tool name (used in MCP protocol) */\n name: string;\n\n /** Human-readable description for AI */\n description: string;\n\n /**\n * JSON Schema for tool input validation.\n * Can be a Zod schema or plain JSON Schema object.\n */\n inputSchema: JsonSchema | ZodSchemaLike;\n\n /**\n * Handler function for the tool.\n * Receives validated input and returns a result.\n */\n handler: McpToolHandler;\n\n /**\n * Optional annotations for the tool\n */\n annotations?: McpToolAnnotations;\n}\n\n/**\n * MCP tool handler function\n */\nexport type McpToolHandler = (\n input: unknown,\n context: McpToolContext\n) => Promise<McpToolResult> | McpToolResult;\n\n/**\n * Context passed to MCP tool handlers\n */\nexport interface McpToolContext {\n /** Current document (if loaded) */\n document?: Document;\n\n /** Document buffer (if loaded) */\n documentBuffer?: ArrayBuffer;\n\n /** Session state */\n session: McpSession;\n\n /** Logger for debugging */\n log: (message: string, data?: unknown) => void;\n}\n\n/**\n * MCP session state\n *\n * Maintains state across tool calls within a session.\n */\nexport interface McpSession {\n /** Session ID */\n id: string;\n\n /** Loaded documents by ID */\n documents: Map<string, LoadedDocument>;\n\n /** Custom session data */\n data: Map<string, unknown>;\n}\n\n/**\n * A loaded document in the session\n */\nexport interface LoadedDocument {\n /** Document ID */\n id: string;\n\n /** Parsed document */\n document: Document;\n\n /** Original buffer (for repacking) */\n buffer?: ArrayBuffer;\n\n /** Source filename or path */\n source?: string;\n\n /** Last modified timestamp */\n lastModified: number;\n}\n\n/**\n * MCP tool result\n */\nexport interface McpToolResult {\n /** Result content */\n content: McpToolContent[];\n\n /** Whether this is an error result */\n isError?: boolean;\n}\n\n/**\n * MCP tool content types\n */\nexport type McpToolContent =\n | { type: 'text'; text: string }\n | { type: 'image'; data: string; mimeType: string }\n | { type: 'resource'; uri: string; mimeType?: string; text?: string };\n\n/**\n * MCP tool annotations\n */\nexport interface McpToolAnnotations {\n /** Tool category for organization */\n category?: string;\n\n /** Whether this tool modifies the document */\n readOnly?: boolean;\n\n /** Estimated cost/complexity */\n complexity?: 'low' | 'medium' | 'high';\n\n /** Example usage */\n examples?: McpToolExample[];\n}\n\n/**\n * MCP tool example\n */\nexport interface McpToolExample {\n /** Example description */\n description: string;\n\n /** Example input */\n input: unknown;\n\n /** Expected output description */\n output?: string;\n}\n\n// ============================================================================\n// JSON SCHEMA TYPES\n// ============================================================================\n\n/**\n * JSON Schema definition (subset)\n */\nexport interface JsonSchema {\n type?: string | string[];\n properties?: Record<string, JsonSchema>;\n items?: JsonSchema;\n required?: string[];\n description?: string;\n enum?: unknown[];\n default?: unknown;\n minimum?: number;\n maximum?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n format?: string;\n additionalProperties?: boolean | JsonSchema;\n anyOf?: JsonSchema[];\n oneOf?: JsonSchema[];\n allOf?: JsonSchema[];\n $ref?: string;\n}\n\n/**\n * Zod-like schema interface for compatibility\n */\nexport interface ZodSchemaLike {\n _def?: unknown;\n parse?: (data: unknown) => unknown;\n safeParse?: (data: unknown) => { success: boolean; data?: unknown; error?: unknown };\n}\n\n/**\n * Check if a schema is Zod-like\n */\nexport function isZodSchema(schema: unknown): schema is ZodSchemaLike {\n return (\n typeof schema === 'object' &&\n schema !== null &&\n ('_def' in schema || 'parse' in schema || 'safeParse' in schema)\n );\n}\n\n// ============================================================================\n// PLUGIN EVENTS\n// ============================================================================\n\n/**\n * Plugin lifecycle events\n */\nexport type PluginEvent =\n | { type: 'registered'; plugin: CorePlugin }\n | { type: 'unregistered'; pluginId: string }\n | { type: 'error'; pluginId: string; error: Error };\n\n/**\n * Plugin event listener\n */\nexport type PluginEventListener = (event: PluginEvent) => void;\n\n// ============================================================================\n// UTILITY TYPES\n// ============================================================================\n\n/**\n * Extract command type from a union\n */\nexport type ExtractCommand<T extends AgentCommand, Type extends string> = T extends { type: Type }\n ? T\n : never;\n\n/**\n * Create a typed command handler\n */\nexport type TypedCommandHandler<T extends PluginCommand> = (doc: Document, command: T) => Document;\n\n/**\n * Plugin configuration options\n */\nexport interface PluginOptions {\n /** Enable debug logging */\n debug?: boolean;\n\n /** Custom configuration */\n config?: Record<string, unknown>;\n}\n\n/**\n * Result of plugin registration\n */\nexport interface PluginRegistrationResult {\n /** Whether registration succeeded */\n success: boolean;\n\n /** Registered plugin (if successful) */\n plugin?: CorePlugin;\n\n /** Error message (if failed) */\n error?: string;\n\n /** Warning messages */\n warnings?: string[];\n}\n\n// ============================================================================\n// EXPORTS\n// ============================================================================\n\nexport type {\n CorePlugin as Plugin,\n CommandHandler as PluginCommandHandler,\n McpToolDefinition as ToolDefinition,\n McpToolHandler as ToolHandler,\n McpToolResult as ToolResult,\n};\n","/**\n * Core Plugin System\n *\n * Headless plugin system for extending DocumentAgent with custom\n * commands and exposing MCP tools for AI integration.\n *\n * @example\n * ```ts\n * import {\n * pluginRegistry,\n * docxtemplaterPlugin,\n * type CorePlugin\n * } from '@eigenpal/docx-editor/core-plugins';\n *\n * // Register the docxtemplater plugin\n * pluginRegistry.register(docxtemplaterPlugin);\n *\n * // Get MCP tools for MCP server\n * const tools = pluginRegistry.getMcpTools();\n *\n * // Check available command handlers\n * const commandTypes = pluginRegistry.getCommandTypes();\n * ```\n */\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport type {\n // Core plugin types\n CorePlugin,\n Plugin,\n PluginCommand,\n CommandHandler,\n PluginCommandHandler,\n CommandResult,\n PluginOptions,\n PluginRegistrationResult,\n\n // MCP tool types\n McpToolDefinition,\n ToolDefinition,\n McpToolHandler,\n ToolHandler,\n McpToolResult,\n ToolResult,\n McpToolContent,\n McpToolContext,\n McpToolAnnotations,\n McpToolExample,\n McpSession,\n LoadedDocument,\n\n // Schema types\n JsonSchema,\n ZodSchemaLike,\n\n // Event types\n PluginEvent,\n PluginEventListener,\n\n // Utility types\n TypedCommandHandler,\n ExtractCommand,\n} from './types';\n\n// ============================================================================\n// UTILITIES\n// ============================================================================\n\nexport { isZodSchema } from './types';\n\n// ============================================================================\n// REGISTRY\n// ============================================================================\n\nexport { PluginRegistry, pluginRegistry, registerPlugins, createPluginRegistrar } from './registry';\n\n// ============================================================================\n// BUILT-IN PLUGINS\n// ============================================================================\n\nexport { docxtemplaterPlugin } from './docxtemplater';\n\n// ============================================================================\n// DEFAULT EXPORT\n// ============================================================================\n\nexport { pluginRegistry as default } from './registry';\n","/**\n * Docxtemplater Plugin Command Handlers\n *\n * Handles template-related commands for DocumentAgent.\n */\n\nimport type { Document, Paragraph, Run, TextContent } from '../../types/document';\nimport type { PluginCommand } from '../types';\n\n// ============================================================================\n// COMMAND TYPES\n// ============================================================================\n\n/**\n * Insert a template variable at a position\n */\nexport interface InsertTemplateVariableCommand extends PluginCommand {\n type: 'insertTemplateVariable';\n position: {\n paragraphIndex: number;\n offset: number;\n };\n variableName: string;\n}\n\n/**\n * Replace text with a template variable\n */\nexport interface ReplaceWithTemplateVariableCommand extends PluginCommand {\n type: 'replaceWithTemplateVariable';\n range: {\n start: { paragraphIndex: number; offset: number };\n end: { paragraphIndex: number; offset: number };\n };\n variableName: string;\n}\n\n// ============================================================================\n// HANDLERS\n// ============================================================================\n\n/**\n * Handle insertTemplateVariable command\n *\n * Inserts {{variableName}} at the specified position.\n */\nexport function handleInsertTemplateVariable(doc: Document, command: PluginCommand): Document {\n const cmd = command as InsertTemplateVariableCommand;\n const { position, variableName } = cmd;\n\n // Clone document for immutability\n const newDoc: Document = JSON.parse(JSON.stringify(doc));\n const body = newDoc.package.document;\n\n // Find the paragraph\n const paragraphs = body.content.filter((block): block is Paragraph => block.type === 'paragraph');\n\n if (position.paragraphIndex >= paragraphs.length) {\n throw new Error(`Paragraph index ${position.paragraphIndex} out of bounds`);\n }\n\n const paragraph = paragraphs[position.paragraphIndex];\n\n // Create the variable text\n const variableText = `{{${variableName}}}`;\n\n // Insert the variable at the offset\n insertTextAtOffset(paragraph, position.offset, variableText);\n\n // Track the variable in document metadata\n if (!newDoc.templateVariables) {\n newDoc.templateVariables = [];\n }\n if (!newDoc.templateVariables.includes(variableName)) {\n newDoc.templateVariables.push(variableName);\n }\n\n return newDoc;\n}\n\n/**\n * Handle replaceWithTemplateVariable command\n *\n * Replaces the text in the range with {{variableName}}.\n */\nexport function handleReplaceWithTemplateVariable(doc: Document, command: PluginCommand): Document {\n const cmd = command as ReplaceWithTemplateVariableCommand;\n const { range, variableName } = cmd;\n\n // Clone document for immutability\n const newDoc: Document = JSON.parse(JSON.stringify(doc));\n const body = newDoc.package.document;\n\n // Find the paragraphs\n const paragraphs = body.content.filter((block): block is Paragraph => block.type === 'paragraph');\n\n if (range.start.paragraphIndex !== range.end.paragraphIndex) {\n throw new Error('Template variable replacement cannot span multiple paragraphs');\n }\n\n if (range.start.paragraphIndex >= paragraphs.length) {\n throw new Error(`Paragraph index ${range.start.paragraphIndex} out of bounds`);\n }\n\n const paragraph = paragraphs[range.start.paragraphIndex];\n\n // Delete the range first\n deleteTextInRange(paragraph, range.start.offset, range.end.offset);\n\n // Insert the variable at the start position\n const variableText = `{{${variableName}}}`;\n insertTextAtOffset(paragraph, range.start.offset, variableText);\n\n // Track the variable\n if (!newDoc.templateVariables) {\n newDoc.templateVariables = [];\n }\n if (!newDoc.templateVariables.includes(variableName)) {\n newDoc.templateVariables.push(variableName);\n }\n\n return newDoc;\n}\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Insert text at a specific offset in a paragraph\n */\nfunction insertTextAtOffset(paragraph: Paragraph, offset: number, text: string): void {\n let currentOffset = 0;\n let inserted = false;\n\n for (let i = 0; i < paragraph.content.length; i++) {\n const item = paragraph.content[i];\n\n if (item.type === 'run') {\n const runText = getRunText(item);\n const runStart = currentOffset;\n const runEnd = currentOffset + runText.length;\n\n if (!inserted && offset >= runStart && offset <= runEnd) {\n const insertPos = offset - runStart;\n\n // Split the run at the insertion point\n const beforeText = runText.slice(0, insertPos);\n const afterText = runText.slice(insertPos);\n\n const newContent: Paragraph['content'] = [];\n\n // Add items before this run\n for (let j = 0; j < i; j++) {\n newContent.push(paragraph.content[j]);\n }\n\n // Add text before insertion point (if any)\n if (beforeText) {\n newContent.push({\n type: 'run',\n formatting: item.formatting,\n content: [{ type: 'text', text: beforeText }],\n });\n }\n\n // Add the new text\n newContent.push({\n type: 'run',\n formatting: item.formatting,\n content: [{ type: 'text', text }],\n });\n\n // Add text after insertion point (if any)\n if (afterText) {\n newContent.push({\n type: 'run',\n formatting: item.formatting,\n content: [{ type: 'text', text: afterText }],\n });\n }\n\n // Add remaining items\n for (let j = i + 1; j < paragraph.content.length; j++) {\n newContent.push(paragraph.content[j]);\n }\n\n paragraph.content = newContent;\n inserted = true;\n break;\n }\n\n currentOffset = runEnd;\n } else if (item.type === 'hyperlink') {\n // Handle hyperlink text\n for (const child of item.children) {\n if (child.type === 'run') {\n currentOffset += getRunText(child).length;\n }\n }\n }\n }\n\n // If not inserted (empty paragraph or offset at end), append\n if (!inserted) {\n paragraph.content.push({\n type: 'run',\n content: [{ type: 'text', text }],\n });\n }\n}\n\n/**\n * Delete text in a range within a paragraph\n */\nfunction deleteTextInRange(paragraph: Paragraph, startOffset: number, endOffset: number): void {\n const newContent: Paragraph['content'] = [];\n let currentOffset = 0;\n\n for (const item of paragraph.content) {\n if (item.type === 'run') {\n const runText = getRunText(item);\n const runStart = currentOffset;\n const runEnd = currentOffset + runText.length;\n\n // Check overlap with deletion range\n if (runEnd <= startOffset || runStart >= endOffset) {\n // No overlap, keep entire run\n newContent.push(item);\n } else {\n // Partial overlap\n let newText = '';\n\n if (runStart < startOffset) {\n // Keep text before start\n newText += runText.slice(0, startOffset - runStart);\n }\n\n if (runEnd > endOffset) {\n // Keep text after end\n newText += runText.slice(endOffset - runStart);\n }\n\n if (newText.length > 0) {\n newContent.push({\n type: 'run',\n formatting: item.formatting,\n content: [{ type: 'text', text: newText }],\n });\n }\n }\n\n currentOffset = runEnd;\n } else {\n newContent.push(item);\n }\n }\n\n paragraph.content = newContent;\n}\n\n/**\n * Get plain text from a run\n */\nfunction getRunText(run: Run): string {\n return run.content\n .filter((c): c is TextContent => c.type === 'text')\n .map((c) => c.text)\n .join('');\n}\n","/**\n * Variable Detector Utility\n *\n * Scans a DOCX document for template variables in the format {{variable_name}}.\n * Returns a unique, sorted list of variable names found in the document.\n */\n\nimport type {\n Document,\n DocumentBody,\n Paragraph,\n Table,\n TableCell,\n Run,\n Hyperlink,\n SimpleField,\n ComplexField,\n BlockContent,\n HeaderFooter,\n Footnote,\n Endnote,\n TextBox,\n} from '../types/document';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\n/**\n * Result of variable detection\n */\nexport interface VariableDetectionResult {\n /** Unique variable names sorted alphabetically */\n variables: string[];\n /** Total count of variable occurrences */\n totalOccurrences: number;\n /** Variables by location */\n byLocation: {\n body: string[];\n headers: string[];\n footers: string[];\n footnotes: string[];\n endnotes: string[];\n textBoxes: string[];\n };\n /** Variable occurrences with positions */\n occurrences: VariableOccurrence[];\n}\n\n/**\n * A single variable occurrence with location info\n */\nexport interface VariableOccurrence {\n /** Variable name (without braces) */\n name: string;\n /** Location type */\n location: 'body' | 'header' | 'footer' | 'footnote' | 'endnote' | 'textBox';\n /** Paragraph index within location */\n paragraphIndex?: number;\n /** Section index (for headers/footers) */\n sectionIndex?: number;\n}\n\n// ============================================================================\n// MAIN FUNCTIONS\n// ============================================================================\n\n/**\n * Detect all template variables in a document\n *\n * @param doc - The parsed document\n * @returns Array of unique variable names sorted alphabetically\n */\nexport function detectVariables(doc: Document): string[] {\n const result = detectVariablesDetailed(doc);\n return result.variables;\n}\n\n/**\n * Detect variables with detailed information\n *\n * @param doc - The parsed document\n * @returns Detailed detection result\n */\nexport function detectVariablesDetailed(doc: Document): VariableDetectionResult {\n const occurrences: VariableOccurrence[] = [];\n const byLocation: VariableDetectionResult['byLocation'] = {\n body: [],\n headers: [],\n footers: [],\n footnotes: [],\n endnotes: [],\n textBoxes: [],\n };\n\n // Scan main body\n if (doc.package?.document) {\n const bodyVars = detectVariablesInBody(doc.package.document);\n bodyVars.forEach((v) => {\n occurrences.push({ name: v, location: 'body' });\n });\n byLocation.body = Array.from(new Set(bodyVars)).sort();\n }\n\n // Scan headers and footers\n if (doc.package?.document?.sections) {\n doc.package.document.sections.forEach((section, _sectionIndex) => {\n // Headers\n if (section.properties.headerReferences) {\n section.properties.headerReferences.forEach((_headerRef) => {\n // If we have actual header content, scan it\n // Note: Headers are stored separately in the package\n });\n }\n });\n }\n\n // Scan footers from package\n // (Actual footer content would be accessed from pkg.headers/pkg.footers if available)\n\n // Scan footnotes\n if (doc.package?.footnotes) {\n const footnoteVars = detectVariablesInNotes(doc.package.footnotes);\n footnoteVars.forEach((v) => {\n occurrences.push({ name: v, location: 'footnote' });\n });\n byLocation.footnotes = Array.from(new Set(footnoteVars)).sort();\n }\n\n // Scan endnotes\n if (doc.package?.endnotes) {\n const endnoteVars = detectVariablesInNotes(doc.package.endnotes);\n endnoteVars.forEach((v) => {\n occurrences.push({ name: v, location: 'endnote' });\n });\n byLocation.endnotes = Array.from(new Set(endnoteVars)).sort();\n }\n\n // Also check templateVariables from document if already detected\n if (doc.templateVariables) {\n doc.templateVariables.forEach((v) => {\n if (!occurrences.some((o) => o.name === v)) {\n occurrences.push({ name: v, location: 'body' });\n }\n });\n }\n\n // Collect all unique variables\n const allVariables = new Set<string>();\n occurrences.forEach((o) => allVariables.add(o.name));\n\n return {\n variables: Array.from(allVariables).sort(),\n totalOccurrences: occurrences.length,\n byLocation,\n occurrences,\n };\n}\n\n/**\n * Detect variables in document body\n */\nexport function detectVariablesInBody(body: DocumentBody): string[] {\n const variables: string[] = [];\n\n // Scan content array\n if (body.content) {\n variables.push(...detectVariablesInBlockContent(body.content));\n }\n\n // Scan sections\n if (body.sections) {\n for (const section of body.sections) {\n if (section.content) {\n variables.push(...detectVariablesInBlockContent(section.content));\n }\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in block content (paragraphs and tables)\n */\nexport function detectVariablesInBlockContent(content: BlockContent[]): string[] {\n const variables: string[] = [];\n\n for (const block of content) {\n if (block.type === 'paragraph') {\n variables.push(...detectVariablesInParagraph(block));\n } else if (block.type === 'table') {\n variables.push(...detectVariablesInTable(block));\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in a paragraph\n */\nexport function detectVariablesInParagraph(paragraph: Paragraph): string[] {\n const variables: string[] = [];\n\n if (!paragraph.content) return variables;\n\n for (const item of paragraph.content) {\n if (item.type === 'run') {\n variables.push(...detectVariablesInRun(item));\n } else if (item.type === 'hyperlink') {\n variables.push(...detectVariablesInHyperlink(item));\n } else if (item.type === 'simpleField') {\n variables.push(...detectVariablesInSimpleField(item));\n } else if (item.type === 'complexField') {\n variables.push(...detectVariablesInComplexField(item));\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in a text run\n */\nexport function detectVariablesInRun(run: Run): string[] {\n const variables: string[] = [];\n\n if (!run.content) return variables;\n\n for (const item of run.content) {\n if (item.type === 'text' && item.text) {\n variables.push(...extractVariablesFromText(item.text));\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in a hyperlink\n */\nexport function detectVariablesInHyperlink(hyperlink: Hyperlink): string[] {\n const variables: string[] = [];\n\n if (!hyperlink.children) return variables;\n\n for (const child of hyperlink.children) {\n if (child.type === 'run') {\n variables.push(...detectVariablesInRun(child));\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in a simple field\n */\nexport function detectVariablesInSimpleField(field: SimpleField): string[] {\n const variables: string[] = [];\n\n // Check field instruction\n if (field.instruction) {\n variables.push(...extractVariablesFromText(field.instruction));\n }\n\n // Check field content runs\n if (field.content) {\n for (const run of field.content) {\n if (run.type === 'run') {\n variables.push(...detectVariablesInRun(run));\n }\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in a complex field\n */\nexport function detectVariablesInComplexField(field: ComplexField): string[] {\n const variables: string[] = [];\n\n // Check field code runs\n if (field.fieldCode) {\n for (const run of field.fieldCode) {\n if (run.type === 'run') {\n variables.push(...detectVariablesInRun(run));\n }\n }\n }\n\n // Check field result runs\n if (field.fieldResult) {\n for (const run of field.fieldResult) {\n if (run.type === 'run') {\n variables.push(...detectVariablesInRun(run));\n }\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in a table\n */\nexport function detectVariablesInTable(table: Table): string[] {\n const variables: string[] = [];\n\n if (!table.rows) return variables;\n\n for (const row of table.rows) {\n if (!row.cells) continue;\n\n for (const cell of row.cells) {\n variables.push(...detectVariablesInCell(cell));\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in a table cell\n */\nexport function detectVariablesInCell(cell: TableCell): string[] {\n const variables: string[] = [];\n\n if (!cell.content) return variables;\n\n for (const block of cell.content) {\n if (block.type === 'paragraph') {\n variables.push(...detectVariablesInParagraph(block));\n } else if (block.type === 'table') {\n // Nested tables\n variables.push(...detectVariablesInTable(block));\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in footnotes/endnotes\n */\nexport function detectVariablesInNotes(notes: (Footnote | Endnote)[]): string[] {\n const variables: string[] = [];\n\n for (const note of notes) {\n if (!note.content) continue;\n\n for (const paragraph of note.content) {\n variables.push(...detectVariablesInParagraph(paragraph));\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in headers/footers\n */\nexport function detectVariablesInHeaderFooter(hf: HeaderFooter): string[] {\n const variables: string[] = [];\n\n if (!hf.content) return variables;\n\n for (const block of hf.content) {\n if (block.type === 'paragraph') {\n variables.push(...detectVariablesInParagraph(block));\n } else if (block.type === 'table') {\n variables.push(...detectVariablesInTable(block));\n }\n }\n\n return variables;\n}\n\n/**\n * Detect variables in a text box\n */\nexport function detectVariablesInTextBox(textBox: TextBox): string[] {\n const variables: string[] = [];\n\n if (!textBox.content) return variables;\n\n // TextBox.content is Paragraph[]\n for (const paragraph of textBox.content) {\n variables.push(...detectVariablesInParagraph(paragraph));\n }\n\n return variables;\n}\n\n// ============================================================================\n// TEXT EXTRACTION\n// ============================================================================\n\n/**\n * Regular expression for matching template variables\n * Matches {{variable_name}} where variable_name can contain:\n * - Letters (a-z, A-Z)\n * - Numbers (0-9)\n * - Underscores (_)\n * - Hyphens (-)\n * - Dots (.)\n */\nconst VARIABLE_PATTERN = /\\{\\{([a-zA-Z_][a-zA-Z0-9_\\-\\.]*)\\}\\}/g;\n\n/**\n * Alternative pattern allowing any content between braces\n */\nconst VARIABLE_PATTERN_RELAXED = /\\{\\{(.+?)\\}\\}/g;\n\n/**\n * Extract variable names from text\n *\n * @param text - The text to search\n * @returns Array of variable names (without braces)\n */\nexport function extractVariablesFromText(text: string): string[] {\n if (!text) return [];\n\n const variables: string[] = [];\n const pattern = new RegExp(VARIABLE_PATTERN);\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(text)) !== null) {\n variables.push(match[1]);\n }\n\n return variables;\n}\n\n/**\n * Extract all variables from text (relaxed matching)\n * Allows any content between {{ and }}\n */\nexport function extractVariablesFromTextRelaxed(text: string): string[] {\n if (!text) return [];\n\n const variables: string[] = [];\n const pattern = new RegExp(VARIABLE_PATTERN_RELAXED);\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(text)) !== null) {\n const varName = match[1].trim();\n if (varName) {\n variables.push(varName);\n }\n }\n\n return variables;\n}\n\n/**\n * Check if text contains template variables\n */\nexport function hasTemplateVariables(text: string): boolean {\n return VARIABLE_PATTERN.test(text);\n}\n\n/**\n * Count template variables in text\n */\nexport function countVariables(text: string): number {\n const matches = text.match(VARIABLE_PATTERN);\n return matches ? matches.length : 0;\n}\n\n/**\n * Get unique variable names from text\n */\nexport function getUniqueVariables(text: string): string[] {\n const variables = extractVariablesFromText(text);\n return Array.from(new Set(variables)).sort();\n}\n\n// ============================================================================\n// VALIDATION\n// ============================================================================\n\n/**\n * Check if a variable name is valid\n */\nexport function isValidVariableName(name: string): boolean {\n if (!name || typeof name !== 'string') return false;\n if (name.length === 0 || name.length > 100) return false;\n\n // Must start with letter or underscore\n if (!/^[a-zA-Z_]/.test(name)) return false;\n\n // Can contain letters, numbers, underscores, hyphens, dots\n if (!/^[a-zA-Z_][a-zA-Z0-9_\\-\\.]*$/.test(name)) return false;\n\n return true;\n}\n\n/**\n * Sanitize a variable name\n */\nexport function sanitizeVariableName(name: string): string {\n if (!name) return '';\n\n // Replace spaces with underscores\n let sanitized = name.replace(/\\s+/g, '_');\n\n // Remove invalid characters\n sanitized = sanitized.replace(/[^a-zA-Z0-9_\\-\\.]/g, '');\n\n // Ensure starts with letter or underscore\n if (sanitized && !/^[a-zA-Z_]/.test(sanitized)) {\n sanitized = '_' + sanitized;\n }\n\n // Limit length\n return sanitized.substring(0, 100);\n}\n\n/**\n * Format a variable name with braces\n */\nexport function formatVariable(name: string): string {\n return `{{${name}}}`;\n}\n\n/**\n * Parse a variable string to get the name\n */\nexport function parseVariable(variable: string): string | null {\n const match = variable.match(/^\\{\\{(.+?)\\}\\}$/);\n return match ? match[1] : null;\n}\n\n// ============================================================================\n// REPLACEMENT\n// ============================================================================\n\n/**\n * Replace variables in text with values\n *\n * @param text - The text containing variables\n * @param values - Map of variable name to replacement value\n * @returns Text with variables replaced\n */\nexport function replaceVariables(text: string, values: Record<string, string>): string {\n if (!text) return text;\n\n return text.replace(VARIABLE_PATTERN_RELAXED, (match, varName) => {\n const name = varName.trim();\n if (name in values) {\n return values[name];\n }\n return match; // Keep original if not in values\n });\n}\n\n/**\n * Replace all variables in text with a placeholder\n *\n * @param text - The text containing variables\n * @param placeholder - Placeholder to use (default: empty string)\n * @returns Text with variables replaced\n */\nexport function removeVariables(text: string, placeholder = ''): string {\n if (!text) return text;\n return text.replace(VARIABLE_PATTERN_RELAXED, placeholder);\n}\n\n/**\n * Highlight variables in text for display\n *\n * @param text - The text containing variables\n * @param wrapper - Function to wrap variable text\n * @returns Array of text segments\n */\nexport function highlightVariables(\n text: string,\n wrapper: (varName: string) => string = (v) => `[${v}]`\n): string {\n if (!text) return text;\n\n return text.replace(VARIABLE_PATTERN_RELAXED, (_match, varName) => {\n return wrapper(varName.trim());\n });\n}\n\n// ============================================================================\n// DOCUMENT-LEVEL HELPERS\n// ============================================================================\n\n/**\n * Get total variable count in document (including duplicates)\n */\nexport function getVariableCount(doc: Document): number {\n const result = detectVariablesDetailed(doc);\n return result.totalOccurrences;\n}\n\n/**\n * Get unique variable count in document\n */\nexport function getUniqueVariableCount(doc: Document): number {\n return detectVariables(doc).length;\n}\n\n/**\n * Check if document has any template variables\n */\nexport function documentHasVariables(doc: Document): boolean {\n return detectVariables(doc).length > 0;\n}\n\n/**\n * Get variables grouped by first letter for large lists\n */\nexport function groupVariablesByLetter(variables: string[]): Record<string, string[]> {\n const groups: Record<string, string[]> = {};\n\n for (const variable of variables) {\n const letter = variable.charAt(0).toUpperCase();\n if (!groups[letter]) {\n groups[letter] = [];\n }\n groups[letter].push(variable);\n }\n\n return groups;\n}\n\nexport default detectVariables;\n","/**\n * Template Processing Utility\n *\n * Uses docxtemplater to substitute template variables in DOCX documents:\n * - Processes {variable_name} patterns (docxtemplater default syntax)\n * - Preserves all formatting (fonts, styles, colors, tables)\n * - Error handling with useful messages\n */\n\nimport PizZip from 'pizzip';\nimport Docxtemplater from 'docxtemplater';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\n/**\n * Options for template processing\n */\nexport interface ProcessTemplateOptions {\n /** How to handle undefined variables */\n nullGetter?: 'keep' | 'empty' | 'error';\n /** Custom parser for variable names */\n parser?: (tag: string) => { get: (scope: Record<string, unknown>) => unknown };\n /** Line breaks: keep raw \\n or convert to w:br */\n linebreaks?: boolean;\n /** Delimiter settings */\n delimiters?: {\n start?: string;\n end?: string;\n };\n}\n\n/**\n * Result of template processing\n */\nexport interface ProcessTemplateResult {\n /** The processed document buffer */\n buffer: ArrayBuffer;\n /** Variables that were found and replaced */\n replacedVariables: string[];\n /** Variables that were not replaced (no value provided) */\n unreplacedVariables: string[];\n /** Any warnings during processing */\n warnings: string[];\n}\n\n/**\n * Error details from template processing\n */\nexport interface TemplateError {\n /** Error message */\n message: string;\n /** Variable name that caused the error (if applicable) */\n variable?: string;\n /** Error type */\n type: 'parse' | 'render' | 'undefined' | 'unknown';\n /** Original error */\n originalError?: Error;\n}\n\n// ============================================================================\n// MAIN FUNCTIONS\n// ============================================================================\n\n/**\n * Process a DOCX template with variable substitution\n *\n * @param buffer - The DOCX file as ArrayBuffer\n * @param variables - Map of variable names to values\n * @param options - Processing options\n * @returns Processed DOCX as ArrayBuffer\n */\nexport function processTemplate(\n buffer: ArrayBuffer,\n variables: Record<string, string>,\n options: ProcessTemplateOptions = {}\n): ArrayBuffer {\n const result = processTemplateDetailed(buffer, variables, options);\n return result.buffer;\n}\n\n/**\n * Process template with detailed result\n *\n * @param buffer - The DOCX file as ArrayBuffer\n * @param variables - Map of variable names to values\n * @param options - Processing options\n * @returns Detailed processing result\n */\nexport function processTemplateDetailed(\n buffer: ArrayBuffer,\n variables: Record<string, string>,\n options: ProcessTemplateOptions = {}\n): ProcessTemplateResult {\n const { nullGetter = 'keep', linebreaks = true, delimiters } = options;\n\n const warnings: string[] = [];\n const replacedVariables: string[] = [];\n const unreplacedVariables: string[] = [];\n\n try {\n // Load the docx as a zip\n const zip = new PizZip(buffer);\n\n // Create docxtemplater instance\n const doc = new Docxtemplater(zip, {\n paragraphLoop: true,\n linebreaks,\n // Handle undefined tags based on option\n nullGetter: (part: { module?: string; value?: string }) => {\n const varName = part.value || '';\n\n if (nullGetter === 'error') {\n throw new Error(`Undefined variable: ${varName}`);\n }\n\n if (nullGetter === 'empty') {\n unreplacedVariables.push(varName);\n return '';\n }\n\n // Default: keep the tag as-is\n unreplacedVariables.push(varName);\n return `{${varName}}`;\n },\n // Custom delimiters if specified (docxtemplater uses single braces by default)\n delimiters: delimiters\n ? { start: delimiters.start || '{', end: delimiters.end || '}' }\n : undefined,\n });\n\n // Track which variables are being replaced\n Object.keys(variables).forEach((key) => {\n if (variables[key] !== undefined && variables[key] !== null) {\n replacedVariables.push(key);\n }\n });\n\n // Set the data\n doc.setData(variables);\n\n // Render the document\n doc.render();\n\n // Get the output buffer\n const outputBuffer = doc.getZip().generate({\n type: 'arraybuffer',\n compression: 'DEFLATE',\n compressionOptions: { level: 6 },\n });\n\n return {\n buffer: outputBuffer,\n replacedVariables,\n unreplacedVariables,\n warnings,\n };\n } catch (error) {\n throw formatTemplateError(error);\n }\n}\n\n/**\n * Process template and return as Blob\n *\n * @param buffer - The DOCX file as ArrayBuffer\n * @param variables - Map of variable names to values\n * @param options - Processing options\n * @returns Processed DOCX as Blob\n */\nexport function processTemplateAsBlob(\n buffer: ArrayBuffer,\n variables: Record<string, string>,\n options: ProcessTemplateOptions = {}\n): Blob {\n const resultBuffer = processTemplate(buffer, variables, options);\n return new Blob([resultBuffer], {\n type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n });\n}\n\n/**\n * Process template and trigger download\n *\n * @param buffer - The DOCX file as ArrayBuffer\n * @param variables - Map of variable names to values\n * @param filename - Output filename (without extension)\n * @param options - Processing options\n */\nexport function processTemplateAndDownload(\n buffer: ArrayBuffer,\n variables: Record<string, string>,\n filename: string = 'document',\n options: ProcessTemplateOptions = {}\n): void {\n const blob = processTemplateAsBlob(buffer, variables, options);\n downloadBlob(blob, `${filename}.docx`);\n}\n\n// ============================================================================\n// VALIDATION & INSPECTION\n// ============================================================================\n\n/**\n * Get all template tags in a document without processing\n *\n * @param buffer - The DOCX file as ArrayBuffer\n * @returns List of tag names found\n */\nexport function getTemplateTags(buffer: ArrayBuffer): string[] {\n try {\n const zip = new PizZip(buffer);\n const doc = new Docxtemplater(zip, {\n paragraphLoop: true,\n linebreaks: true,\n });\n\n // Get the full text to extract tags\n const fullText = doc.getFullText();\n return extractTagsFromText(fullText);\n } catch (error) {\n throw formatTemplateError(error);\n }\n}\n\n/**\n * Validate that a document is a valid docxtemplater template\n *\n * @param buffer - The DOCX file as ArrayBuffer\n * @returns Validation result\n */\nexport function validateTemplate(buffer: ArrayBuffer): {\n valid: boolean;\n errors: TemplateError[];\n tags: string[];\n} {\n const errors: TemplateError[] = [];\n let tags: string[] = [];\n\n try {\n const zip = new PizZip(buffer);\n const doc = new Docxtemplater(zip, {\n paragraphLoop: true,\n linebreaks: true,\n });\n\n // Try to get full text (validates structure)\n const fullText = doc.getFullText();\n tags = extractTagsFromText(fullText);\n\n // Check for unclosed tags\n const unclosedTags = findUnclosedTags(fullText);\n for (const tag of unclosedTags) {\n errors.push({\n message: `Unclosed tag: ${tag}`,\n variable: tag,\n type: 'parse',\n });\n }\n\n return {\n valid: errors.length === 0,\n errors,\n tags,\n };\n } catch (error) {\n errors.push(formatTemplateError(error));\n return {\n valid: false,\n errors,\n tags,\n };\n }\n}\n\n/**\n * Check if all required variables have values\n *\n * @param tags - List of template tags\n * @param variables - Provided variable values\n * @returns Missing variable names\n */\nexport function getMissingVariables(tags: string[], variables: Record<string, string>): string[] {\n return tags.filter(\n (tag) => !(tag in variables) || variables[tag] === undefined || variables[tag] === null\n );\n}\n\n/**\n * Preview what the document will look like after processing\n * Returns the document text with variables replaced (for preview purposes)\n *\n * @param buffer - The DOCX file as ArrayBuffer\n * @param variables - Map of variable names to values\n * @returns Preview text\n */\nexport function previewTemplate(buffer: ArrayBuffer, variables: Record<string, string>): string {\n try {\n const zip = new PizZip(buffer);\n const doc = new Docxtemplater(zip, {\n paragraphLoop: true,\n linebreaks: true,\n nullGetter: (part: { value?: string }) => {\n const varName = part.value || '';\n return `[${varName}]`;\n },\n });\n\n doc.setData(variables);\n doc.render();\n\n return doc.getFullText();\n } catch (error) {\n throw formatTemplateError(error);\n }\n}\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Extract tag names from text\n */\nfunction extractTagsFromText(text: string): string[] {\n const tags: string[] = [];\n const regex = /\\{([^{}]+)\\}/g;\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(text)) !== null) {\n // docxtemplater uses single braces internally\n const tag = match[1].trim();\n if (tag && !tags.includes(tag)) {\n tags.push(tag);\n }\n }\n\n return tags.sort();\n}\n\n/**\n * Find unclosed template tags\n */\nfunction findUnclosedTags(text: string): string[] {\n const unclosed: string[] = [];\n\n // Check for { without matching }\n let depth = 0;\n let currentTag = '';\n\n for (const char of text) {\n if (char === '{') {\n depth++;\n currentTag = '';\n } else if (char === '}') {\n depth--;\n if (depth < 0) {\n depth = 0; // Reset on extra close brace\n }\n } else if (depth > 0) {\n currentTag += char;\n }\n }\n\n if (depth > 0 && currentTag.trim()) {\n unclosed.push(currentTag.trim());\n }\n\n return unclosed;\n}\n\n/** Type guard for docxtemplater multi-error */\ninterface DocxTemplaterError extends Error {\n properties?: {\n errors?: Array<{\n message?: string;\n properties?: { tag?: string };\n }>;\n };\n}\n\nfunction isDocxTemplaterError(error: Error): error is DocxTemplaterError {\n return 'properties' in error && typeof (error as DocxTemplaterError).properties === 'object';\n}\n\n/**\n * Format docxtemplater errors into useful messages\n */\nfunction formatTemplateError(error: unknown): TemplateError {\n if (error instanceof Error) {\n // Check for docxtemplater specific errors\n if (isDocxTemplaterError(error) && error.properties?.errors) {\n // Multi-error from docxtemplater\n const firstError = error.properties.errors[0];\n return {\n message: firstError?.message || 'Template processing error',\n variable: firstError?.properties?.tag,\n type: 'render',\n originalError: error,\n };\n }\n\n // Check for undefined tag errors\n if (error.message.includes('undefined')) {\n const match = error.message.match(/undefined (?:variable|tag):\\s*(\\S+)/i);\n return {\n message: error.message,\n variable: match ? match[1] : undefined,\n type: 'undefined',\n originalError: error,\n };\n }\n\n // Check for parse errors\n if (\n error.message.includes('parse') ||\n error.message.includes('unclosed') ||\n error.message.includes('syntax')\n ) {\n return {\n message: error.message,\n type: 'parse',\n originalError: error,\n };\n }\n\n return {\n message: error.message,\n type: 'unknown',\n originalError: error,\n };\n }\n\n return {\n message: String(error),\n type: 'unknown',\n };\n}\n\n/**\n * Download a blob as a file\n */\nfunction downloadBlob(blob: Blob, filename: string): void {\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n}\n\n// ============================================================================\n// ADVANCED FEATURES\n// ============================================================================\n\n/**\n * Process template with conditional sections\n * Supports #if, #unless, #each loops\n *\n * @param buffer - The DOCX file as ArrayBuffer\n * @param data - Full data object (can include arrays, nested objects)\n * @param options - Processing options\n * @returns Processed DOCX as ArrayBuffer\n */\nexport function processTemplateAdvanced(\n buffer: ArrayBuffer,\n data: Record<string, unknown>,\n options: ProcessTemplateOptions = {}\n): ArrayBuffer {\n const { linebreaks = true, delimiters } = options;\n\n try {\n const zip = new PizZip(buffer);\n const doc = new Docxtemplater(zip, {\n paragraphLoop: true,\n linebreaks,\n delimiters: delimiters\n ? { start: delimiters.start || '{', end: delimiters.end || '}' }\n : undefined,\n });\n\n doc.setData(data);\n doc.render();\n\n return doc.getZip().generate({\n type: 'arraybuffer',\n compression: 'DEFLATE',\n });\n } catch (error) {\n throw formatTemplateError(error);\n }\n}\n\n/**\n * Create a template processor with preset options\n */\nexport function createTemplateProcessor(\n defaultOptions: ProcessTemplateOptions = {}\n): (buffer: ArrayBuffer, variables: Record<string, string>) => ArrayBuffer {\n return (buffer: ArrayBuffer, variables: Record<string, string>) => {\n return processTemplate(buffer, variables, defaultOptions);\n };\n}\n\nexport default processTemplate;\n","/**\n * DOCX Unzipper\n *\n * Extracts all files from a DOCX ZIP archive and organizes them\n * into a structured format for further processing.\n *\n * A DOCX file is a ZIP archive containing:\n * - [Content_Types].xml - Content type declarations\n * - word/document.xml - Main document content\n * - word/styles.xml - Style definitions\n * - word/theme/theme1.xml - Theme colors and fonts\n * - word/numbering.xml - List/numbering definitions\n * - word/fontTable.xml - Font declarations\n * - word/settings.xml - Document settings\n * - word/webSettings.xml - Web settings\n * - word/header*.xml - Header content\n * - word/footer*.xml - Footer content\n * - word/footnotes.xml - Footnotes\n * - word/endnotes.xml - Endnotes\n * - word/media/* - Embedded images and media\n * - word/_rels/document.xml.rels - Relationships\n * - _rels/.rels - Package relationships\n * - docProps/core.xml - Core properties\n * - docProps/app.xml - Application properties\n */\n\nimport JSZip from 'jszip';\n\n/**\n * Raw extracted content from a DOCX file\n */\nexport interface RawDocxContent {\n // Main document\n documentXml: string | null;\n\n // Styles and formatting\n stylesXml: string | null;\n themeXml: string | null;\n numberingXml: string | null;\n fontTableXml: string | null;\n settingsXml: string | null;\n webSettingsXml: string | null;\n\n // Headers and footers (keyed by filename, e.g., \"header1.xml\")\n headers: Map<string, string>;\n footers: Map<string, string>;\n\n // Footnotes and endnotes\n footnotesXml: string | null;\n endnotesXml: string | null;\n\n // Comments\n commentsXml: string | null;\n\n // Relationships\n documentRels: string | null;\n packageRels: string | null;\n\n // Content types\n contentTypesXml: string | null;\n\n // Document properties\n corePropsXml: string | null;\n appPropsXml: string | null;\n customPropsXml: string | null;\n\n // Media files (images, etc.) - keyed by path, e.g., \"word/media/image1.png\"\n media: Map<string, ArrayBuffer>;\n\n // Embedded fonts - keyed by path\n fonts: Map<string, ArrayBuffer>;\n\n // All XML files (for any we might have missed)\n allXml: Map<string, string>;\n\n // Original ZIP for round-trip preservation\n originalZip: JSZip;\n\n // Original buffer for round-trip\n originalBuffer: ArrayBuffer;\n}\n\n/**\n * Extract all content from a DOCX file\n *\n * @param buffer - DOCX file as ArrayBuffer\n * @returns Promise resolving to extracted content\n */\nexport async function unzipDocx(buffer: ArrayBuffer): Promise<RawDocxContent> {\n const zip = await JSZip.loadAsync(buffer);\n\n const content: RawDocxContent = {\n documentXml: null,\n stylesXml: null,\n themeXml: null,\n numberingXml: null,\n fontTableXml: null,\n settingsXml: null,\n webSettingsXml: null,\n headers: new Map(),\n footers: new Map(),\n footnotesXml: null,\n endnotesXml: null,\n commentsXml: null,\n documentRels: null,\n packageRels: null,\n contentTypesXml: null,\n corePropsXml: null,\n appPropsXml: null,\n customPropsXml: null,\n media: new Map(),\n fonts: new Map(),\n allXml: new Map(),\n originalZip: zip,\n originalBuffer: buffer,\n };\n\n // Process each file in the ZIP\n for (const [path, file] of Object.entries(zip.files)) {\n // Skip directories\n if (file.dir) continue;\n\n const lowerPath = path.toLowerCase();\n\n // Determine file type and extract\n if (lowerPath.endsWith('.xml') || lowerPath.endsWith('.rels')) {\n const xmlContent = await file.async('text');\n content.allXml.set(path, xmlContent);\n\n // Categorize known XML files\n if (lowerPath === 'word/document.xml') {\n content.documentXml = xmlContent;\n } else if (lowerPath === 'word/styles.xml') {\n content.stylesXml = xmlContent;\n } else if (lowerPath === 'word/theme/theme1.xml') {\n content.themeXml = xmlContent;\n } else if (lowerPath === 'word/numbering.xml') {\n content.numberingXml = xmlContent;\n } else if (lowerPath === 'word/fonttable.xml') {\n content.fontTableXml = xmlContent;\n } else if (lowerPath === 'word/settings.xml') {\n content.settingsXml = xmlContent;\n } else if (lowerPath === 'word/websettings.xml') {\n content.webSettingsXml = xmlContent;\n } else if (lowerPath === 'word/footnotes.xml') {\n content.footnotesXml = xmlContent;\n } else if (lowerPath === 'word/endnotes.xml') {\n content.endnotesXml = xmlContent;\n } else if (lowerPath === 'word/comments.xml') {\n content.commentsXml = xmlContent;\n } else if (lowerPath === 'word/_rels/document.xml.rels') {\n content.documentRels = xmlContent;\n } else if (lowerPath === '_rels/.rels') {\n content.packageRels = xmlContent;\n } else if (lowerPath === '[content_types].xml') {\n content.contentTypesXml = xmlContent;\n } else if (lowerPath === 'docprops/core.xml') {\n content.corePropsXml = xmlContent;\n } else if (lowerPath === 'docprops/app.xml') {\n content.appPropsXml = xmlContent;\n } else if (lowerPath === 'docprops/custom.xml') {\n content.customPropsXml = xmlContent;\n } else if (lowerPath.match(/^word\\/header\\d+\\.xml$/)) {\n const filename = path.split('/').pop() || path;\n content.headers.set(filename, xmlContent);\n } else if (lowerPath.match(/^word\\/footer\\d+\\.xml$/)) {\n const filename = path.split('/').pop() || path;\n content.footers.set(filename, xmlContent);\n }\n } else if (lowerPath.startsWith('word/media/')) {\n // Media files (images, etc.)\n const binaryContent = await file.async('arraybuffer');\n content.media.set(path, binaryContent);\n } else if (lowerPath.startsWith('word/fonts/')) {\n // Embedded fonts\n const binaryContent = await file.async('arraybuffer');\n content.fonts.set(path, binaryContent);\n }\n }\n\n return content;\n}\n\n/**\n * Get a list of all files in the DOCX\n *\n * @param content - Extracted DOCX content\n * @returns Array of file paths\n */\nexport function getFileList(content: RawDocxContent): string[] {\n const files: string[] = [];\n\n for (const path of Object.keys(content.originalZip.files)) {\n if (!content.originalZip.files[path].dir) {\n files.push(path);\n }\n }\n\n return files.sort();\n}\n\n/**\n * Get the MIME type for a media file based on extension\n *\n * @param path - File path\n * @returns MIME type string\n */\nexport function getMediaMimeType(path: string): string {\n const ext = path.toLowerCase().split('.').pop();\n\n switch (ext) {\n case 'png':\n return 'image/png';\n case 'jpg':\n case 'jpeg':\n return 'image/jpeg';\n case 'gif':\n return 'image/gif';\n case 'bmp':\n return 'image/bmp';\n case 'tif':\n case 'tiff':\n return 'image/tiff';\n case 'wmf':\n return 'image/x-wmf';\n case 'emf':\n return 'image/x-emf';\n case 'svg':\n return 'image/svg+xml';\n case 'webp':\n return 'image/webp';\n default:\n return 'application/octet-stream';\n }\n}\n\n/**\n * Convert media file to data URL\n *\n * @param data - Binary data\n * @param mimeType - MIME type\n * @returns Data URL string\n */\nexport function mediaToDataUrl(data: ArrayBuffer, mimeType: string): string {\n const bytes = new Uint8Array(data);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n const base64 = btoa(binary);\n return `data:${mimeType};base64,${base64}`;\n}\n\n/**\n * Extract a specific file from the original ZIP\n *\n * @param content - Extracted DOCX content\n * @param path - File path within the ZIP\n * @returns File content as string or ArrayBuffer, or null if not found\n */\nexport async function extractFile(\n content: RawDocxContent,\n path: string\n): Promise<string | ArrayBuffer | null> {\n const file = content.originalZip.file(path);\n if (!file) return null;\n\n const lowerPath = path.toLowerCase();\n if (lowerPath.endsWith('.xml') || lowerPath.endsWith('.rels')) {\n return file.async('text');\n } else {\n return file.async('arraybuffer');\n }\n}\n\n/**\n * Check if a file exists in the DOCX\n *\n * @param content - Extracted DOCX content\n * @param path - File path to check\n * @returns true if file exists\n */\nexport function hasFile(content: RawDocxContent, path: string): boolean {\n return content.originalZip.file(path) !== null;\n}\n\n/**\n * Get summary of DOCX content\n *\n * @param content - Extracted DOCX content\n * @returns Object with file counts and presence flags\n */\nexport function getContentSummary(content: RawDocxContent): {\n hasDocument: boolean;\n hasStyles: boolean;\n hasTheme: boolean;\n hasNumbering: boolean;\n hasFontTable: boolean;\n hasFootnotes: boolean;\n hasEndnotes: boolean;\n hasComments: boolean;\n headerCount: number;\n footerCount: number;\n mediaCount: number;\n fontCount: number;\n totalFiles: number;\n} {\n return {\n hasDocument: content.documentXml !== null,\n hasStyles: content.stylesXml !== null,\n hasTheme: content.themeXml !== null,\n hasNumbering: content.numberingXml !== null,\n hasFontTable: content.fontTableXml !== null,\n hasFootnotes: content.footnotesXml !== null,\n hasEndnotes: content.endnotesXml !== null,\n hasComments: content.commentsXml !== null,\n headerCount: content.headers.size,\n footerCount: content.footers.size,\n mediaCount: content.media.size,\n fontCount: content.fonts.size,\n totalFiles: Object.keys(content.originalZip.files).filter(\n (p) => !content.originalZip.files[p].dir\n ).length,\n };\n}\n","/**\n * XML Parser Utilities for OOXML\n *\n * Provides helper functions for parsing Office Open XML (OOXML) content\n * with proper namespace handling.\n *\n * OOXML uses many namespaces:\n * - w: WordprocessingML (main document content)\n * - a: DrawingML (graphics)\n * - r: Relationships\n * - wp: Word Drawing positioning\n * - wps: Word Drawing shapes\n * - wpc: Word Drawing canvas\n * - wpg: Word Drawing group\n * - m: Math\n * - mc: Markup Compatibility\n * - v: VML (legacy vector graphics)\n * - o: Office (extensions)\n * - pic: Pictures\n */\n\nimport { xml2js, type Element as XmlElement } from 'xml-js';\n\n// Re-export Element type for consumers\nexport type { Element as XmlElement } from 'xml-js';\n\n/**\n * Common OOXML namespace URIs\n */\nexport const NAMESPACES = {\n // Main namespaces\n w: 'http://schemas.openxmlformats.org/wordprocessingml/2006/main',\n a: 'http://schemas.openxmlformats.org/drawingml/2006/main',\n r: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',\n\n // Drawing namespaces\n wp: 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing',\n wp14: 'http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing',\n wps: 'http://schemas.microsoft.com/office/word/2010/wordprocessingShape',\n wpc: 'http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas',\n wpg: 'http://schemas.microsoft.com/office/word/2010/wordprocessingGroup',\n\n // Picture namespace\n pic: 'http://schemas.openxmlformats.org/drawingml/2006/picture',\n\n // Math namespace\n m: 'http://schemas.openxmlformats.org/officeDocument/2006/math',\n\n // Markup Compatibility\n mc: 'http://schemas.openxmlformats.org/markup-compatibility/2006',\n\n // Legacy VML\n v: 'urn:schemas-microsoft-com:vml',\n o: 'urn:schemas-microsoft-com:office:office',\n\n // Other\n w14: 'http://schemas.microsoft.com/office/word/2010/wordml',\n w15: 'http://schemas.microsoft.com/office/word/2012/wordml',\n\n // Content Types\n ct: 'http://schemas.openxmlformats.org/package/2006/content-types',\n\n // Relationships\n pr: 'http://schemas.openxmlformats.org/package/2006/relationships',\n} as const;\n\n/**\n * Parse XML string into element tree\n *\n * @param xml - XML string to parse\n * @returns Parsed element tree\n */\nexport function parseXml(xml: string): XmlElement {\n const result = xml2js(xml, {\n compact: false,\n ignoreComment: true,\n ignoreInstruction: true,\n ignoreDoctype: true,\n alwaysArray: false,\n // IMPORTANT: Do NOT trim whitespace - it strips significant spaces\n // around hyperlinks and other inline elements. DOCX uses xml:space=\"preserve\"\n // to indicate significant whitespace, but we need to preserve all text as-is.\n trim: false,\n attributesKey: 'attributes',\n textKey: 'text',\n }) as XmlElement;\n\n return result;\n}\n\n/**\n * Parse XML string to a more convenient format\n */\nexport function parseXmlDocument(xml: string): XmlElement | null {\n try {\n const parsed = parseXml(xml);\n\n // The root is typically the declaration + elements array\n if (parsed.elements && parsed.elements.length > 0) {\n // Return the first real element (skip declarations)\n return parsed.elements.find((e) => e.type === 'element') ?? null;\n }\n\n return parsed;\n } catch (error) {\n console.warn('Failed to parse XML:', error);\n return null;\n }\n}\n\n/**\n * Get local name from a prefixed element name\n * e.g., \"w:p\" -> \"p\", \"a:graphic\" -> \"graphic\"\n */\nexport function getLocalName(name: string): string {\n const colonIndex = name.indexOf(':');\n return colonIndex >= 0 ? name.substring(colonIndex + 1) : name;\n}\n\n/**\n * Get namespace prefix from an element name\n * e.g., \"w:p\" -> \"w\", \"a:graphic\" -> \"a\"\n */\nexport function getNamespacePrefix(name: string): string | null {\n const colonIndex = name.indexOf(':');\n return colonIndex >= 0 ? name.substring(0, colonIndex) : null;\n}\n\n/**\n * Check if an element matches a given namespaced name\n *\n * @param element - Element to check\n * @param namespace - Namespace prefix (e.g., \"w\", \"a\")\n * @param localName - Local element name (e.g., \"p\", \"r\")\n */\nexport function matchesName(element: XmlElement, namespace: string, localName: string): boolean {\n if (!element.name) return false;\n\n const fullName = `${namespace}:${localName}`;\n if (element.name === fullName) return true;\n\n // Also check just the local name if no namespace prefix in element\n if (getLocalName(element.name) === localName) return true;\n\n return false;\n}\n\n/**\n * Find first child element matching the given namespaced name\n *\n * @param parent - Parent element\n * @param namespace - Namespace prefix (e.g., \"w\")\n * @param localName - Local element name (e.g., \"p\")\n * @returns First matching child or null\n */\nexport function findChild(\n parent: XmlElement | null | undefined,\n namespace: string,\n localName: string\n): XmlElement | null {\n if (!parent || !parent.elements) return null;\n\n const fullName = `${namespace}:${localName}`;\n\n for (const child of parent.elements) {\n if (child.type !== 'element') continue;\n\n if (child.name === fullName) {\n return child;\n }\n\n // Check local name match\n if (getLocalName(child.name || '') === localName) {\n return child;\n }\n }\n\n return null;\n}\n\n/**\n * Find all child elements matching the given namespaced name\n *\n * @param parent - Parent element\n * @param namespace - Namespace prefix\n * @param localName - Local element name\n * @returns Array of matching children\n */\nexport function findChildren(\n parent: XmlElement | null | undefined,\n namespace: string,\n localName: string\n): XmlElement[] {\n if (!parent || !parent.elements) return [];\n\n const fullName = `${namespace}:${localName}`;\n const results: XmlElement[] = [];\n\n for (const child of parent.elements) {\n if (child.type !== 'element') continue;\n\n if (child.name === fullName || getLocalName(child.name || '') === localName) {\n results.push(child);\n }\n }\n\n return results;\n}\n\n/**\n * Find first child element by local name only (ignoring namespace)\n *\n * @param parent - Parent element\n * @param localName - Local element name\n * @returns First matching child or null\n */\nexport function findChildByLocalName(\n parent: XmlElement | null | undefined,\n localName: string\n): XmlElement | null {\n if (!parent || !parent.elements) return null;\n\n for (const child of parent.elements) {\n if (child.type !== 'element') continue;\n\n if (getLocalName(child.name || '') === localName) {\n return child;\n }\n }\n\n return null;\n}\n\n/**\n * Find all child elements by local name only\n *\n * @param parent - Parent element\n * @param localName - Local element name\n * @returns Array of matching children\n */\nexport function findChildrenByLocalName(\n parent: XmlElement | null | undefined,\n localName: string\n): XmlElement[] {\n if (!parent || !parent.elements) return [];\n\n return parent.elements.filter(\n (child) => child.type === 'element' && getLocalName(child.name || '') === localName\n );\n}\n\n/**\n * Get all child elements (excludes text nodes, etc.)\n *\n * @param parent - Parent element\n * @returns Array of child elements\n */\nexport function getChildElements(parent: XmlElement | null | undefined): XmlElement[] {\n if (!parent || !parent.elements) return [];\n return parent.elements.filter((child) => child.type === 'element');\n}\n\n/**\n * Get an attribute value from an element\n *\n * @param element - Element to get attribute from\n * @param namespace - Namespace prefix for the attribute (or null for no namespace)\n * @param name - Attribute name\n * @returns Attribute value or null if not found\n */\nexport function getAttribute(\n element: XmlElement | null | undefined,\n namespace: string | null,\n name: string\n): string | null {\n if (!element || !element.attributes) return null;\n\n const attrs = element.attributes as Record<string, string>;\n\n // Try with namespace prefix first\n if (namespace) {\n const prefixedName = `${namespace}:${name}`;\n if (prefixedName in attrs) {\n return attrs[prefixedName];\n }\n }\n\n // Try without namespace\n if (name in attrs) {\n return attrs[name];\n }\n\n return null;\n}\n\n/**\n * Get an attribute value, trying multiple possible names\n *\n * @param element - Element to get attribute from\n * @param names - Array of possible attribute names (with or without namespace)\n * @returns First found attribute value or null\n */\nexport function getAttributeAny(\n element: XmlElement | null | undefined,\n names: string[]\n): string | null {\n if (!element || !element.attributes) return null;\n\n const attrs = element.attributes as Record<string, string>;\n\n for (const name of names) {\n if (name in attrs) {\n return attrs[name];\n }\n }\n\n return null;\n}\n\n/**\n * Get all attributes from an element\n *\n * @param element - Element to get attributes from\n * @returns Record of attribute name -> value\n */\nexport function getAttributes(element: XmlElement | null | undefined): Record<string, string> {\n if (!element || !element.attributes) return {};\n return element.attributes as Record<string, string>;\n}\n\n/**\n * Get the text content of an element (concatenates all text nodes)\n *\n * @param element - Element to get text from\n * @returns Text content or empty string\n */\nexport function getTextContent(element: XmlElement | null | undefined): string {\n if (!element) return '';\n\n // Check for direct text property\n if ('text' in element && typeof element.text === 'string') {\n return element.text;\n }\n\n // Check elements array for text nodes\n if (!element.elements) return '';\n\n let text = '';\n for (const child of element.elements) {\n if (child.type === 'text' && 'text' in child) {\n text += child.text ?? '';\n } else if (child.type === 'element') {\n // Recurse into child elements\n text += getTextContent(child);\n }\n }\n\n return text;\n}\n\n/**\n * Check if an element has a specific attribute with value \"true\" or \"1\"\n *\n * @param element - Element to check\n * @param namespace - Attribute namespace\n * @param name - Attribute name\n * @returns true if attribute exists and is truthy\n */\nexport function hasFlag(\n element: XmlElement | null | undefined,\n namespace: string | null,\n name: string\n): boolean {\n const value = getAttribute(element, namespace, name);\n\n // In OOXML, presence of element often means true, absence means false\n // If value is null, check if the element itself exists\n if (value === null) {\n return false;\n }\n\n // Explicitly false\n if (value === '0' || value === 'false' || value === 'off') {\n return false;\n }\n\n // Any other value (including \"1\", \"true\", \"on\", or empty string) means true\n return true;\n}\n\n/**\n * Check if a child element exists (used for boolean flags in OOXML)\n *\n * @param parent - Parent element\n * @param namespace - Namespace prefix\n * @param localName - Local element name\n * @returns true if child element exists\n */\nexport function hasChild(\n parent: XmlElement | null | undefined,\n namespace: string,\n localName: string\n): boolean {\n return findChild(parent, namespace, localName) !== null;\n}\n\n/**\n * Parse an OOXML color value\n *\n * @param element - Color element (e.g., w:color)\n * @returns Object with val, themeColor, themeTint, themeShade\n */\nexport function parseColorElement(element: XmlElement | null | undefined): {\n val?: string;\n themeColor?: string;\n themeTint?: string;\n themeShade?: string;\n} | null {\n if (!element) return null;\n\n return {\n val: getAttribute(element, 'w', 'val') ?? undefined,\n themeColor: getAttribute(element, 'w', 'themeColor') ?? undefined,\n themeTint: getAttribute(element, 'w', 'themeTint') ?? undefined,\n themeShade: getAttribute(element, 'w', 'themeShade') ?? undefined,\n };\n}\n\n/**\n * Parse a numeric value from an attribute, with optional scale\n *\n * @param element - Element containing the attribute\n * @param namespace - Attribute namespace\n * @param name - Attribute name\n * @param scale - Optional scale factor (e.g., 20 for twips to points)\n * @returns Parsed number or undefined\n */\nexport function parseNumericAttribute(\n element: XmlElement | null | undefined,\n namespace: string | null,\n name: string,\n scale: number = 1\n): number | undefined {\n const value = getAttribute(element, namespace, name);\n if (value === null) return undefined;\n\n const num = parseInt(value, 10);\n if (isNaN(num)) return undefined;\n\n return num * scale;\n}\n\n/**\n * Parse a boolean value from an attribute or element presence\n *\n * OOXML boolean conventions:\n * - Element presence with no val attribute = true\n * - w:val=\"true\" or w:val=\"1\" = true\n * - w:val=\"false\" or w:val=\"0\" = false\n *\n * @param element - Element to check\n * @param namespace - Namespace for val attribute\n * @returns boolean value\n */\nexport function parseBooleanElement(\n element: XmlElement | null | undefined,\n namespace: string = 'w'\n): boolean {\n if (!element) return false;\n\n const val = getAttribute(element, namespace, 'val');\n\n // No val attribute = true (element presence implies true)\n if (val === null) return true;\n\n // Explicit false values\n if (val === '0' || val === 'false' || val === 'off') {\n return false;\n }\n\n return true;\n}\n\n/**\n * Deep find - search recursively for an element\n *\n * @param root - Root element to search from\n * @param namespace - Namespace prefix\n * @param localName - Local element name\n * @returns First matching element found or null\n */\nexport function findDeep(\n root: XmlElement | null | undefined,\n namespace: string,\n localName: string\n): XmlElement | null {\n if (!root) return null;\n\n // Check if this element matches\n if (matchesName(root, namespace, localName)) {\n return root;\n }\n\n // Search children\n if (root.elements) {\n for (const child of root.elements) {\n if (child.type !== 'element') continue;\n\n const found = findDeep(child, namespace, localName);\n if (found) return found;\n }\n }\n\n return null;\n}\n\n/**\n * Find all elements matching name, searching recursively\n *\n * @param root - Root element to search from\n * @param namespace - Namespace prefix\n * @param localName - Local element name\n * @returns Array of all matching elements\n */\nexport function findAllDeep(\n root: XmlElement | null | undefined,\n namespace: string,\n localName: string\n): XmlElement[] {\n const results: XmlElement[] = [];\n\n function search(element: XmlElement | null | undefined): void {\n if (!element) return;\n\n if (matchesName(element, namespace, localName)) {\n results.push(element);\n }\n\n if (element.elements) {\n for (const child of element.elements) {\n if (child.type === 'element') {\n search(child);\n }\n }\n }\n }\n\n search(root);\n return results;\n}\n","/**\n * Relationship Parser\n *\n * Parses .rels files from DOCX packages to map relationship IDs (rId)\n * to their targets (images, hyperlinks, headers, footers, etc.).\n *\n * .rels files are XML with structure:\n * <Relationships xmlns=\"...\">\n * <Relationship Id=\"rId1\" Type=\"...\" Target=\"...\" TargetMode=\"External|Internal\"/>\n * </Relationships>\n *\n * Key relationship types:\n * - image: Embedded images (word/media/*)\n * - hyperlink: External URLs (TargetMode=\"External\")\n * - header: Header XML files\n * - footer: Footer XML files\n * - footnotes: Footnotes XML\n * - endnotes: Endnotes XML\n * - styles: styles.xml\n * - numbering: numbering.xml\n * - fontTable: fontTable.xml\n * - theme: theme/theme1.xml\n */\n\nimport { parseXmlDocument, getChildElements, getAttribute } from './xmlParser';\nimport type { Relationship, RelationshipMap, RelationshipType } from '../types';\n\n/**\n * Relationship type constants for common types\n */\nexport const RELATIONSHIP_TYPES = {\n image: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',\n hyperlink: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',\n header: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/header',\n footer: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer',\n footnotes: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes',\n endnotes: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes',\n styles: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',\n numbering: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering',\n fontTable: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable',\n theme: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme',\n settings: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings',\n webSettings: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings',\n oleObject: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject',\n chart: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',\n diagramData: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramData',\n officeDocument:\n 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',\n coreProperties:\n 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties',\n extendedProperties:\n 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties',\n customProperties:\n 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties',\n customXml: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml',\n} as const;\n\n/**\n * Parse a .rels XML file into a RelationshipMap\n *\n * @param relsXml - XML content of a .rels file\n * @returns Map of relationship ID to Relationship object\n */\nexport function parseRelationships(relsXml: string): RelationshipMap {\n const map: RelationshipMap = new Map();\n\n if (!relsXml || relsXml.trim().length === 0) {\n return map;\n }\n\n const root = parseXmlDocument(relsXml);\n if (!root) {\n console.warn('Failed to parse relationships XML');\n return map;\n }\n\n // Get all Relationship elements\n const children = getChildElements(root);\n\n for (const child of children) {\n // Check if this is a Relationship element\n const name = child.name || '';\n if (!name.endsWith('Relationship') && !name.includes(':Relationship')) {\n continue;\n }\n\n // Extract attributes\n const id = getAttribute(child, null, 'Id');\n const type = getAttribute(child, null, 'Type');\n const target = getAttribute(child, null, 'Target');\n const targetMode = getAttribute(child, null, 'TargetMode');\n\n if (!id || !type || !target) {\n console.warn('Relationship missing required attributes:', { id, type, target });\n continue;\n }\n\n const relationship: Relationship = {\n id,\n type: type as RelationshipType,\n target,\n };\n\n if (targetMode === 'External') {\n relationship.targetMode = 'External';\n } else if (targetMode === 'Internal') {\n relationship.targetMode = 'Internal';\n }\n // If not specified, default is Internal (we don't set it explicitly)\n\n map.set(id, relationship);\n }\n\n return map;\n}\n\n/**\n * Get the short type name from a full relationship type URI\n *\n * @param typeUri - Full relationship type URI\n * @returns Short type name (e.g., \"image\", \"hyperlink\") or \"unknown\"\n */\nexport function getRelationshipTypeName(typeUri: string): string {\n for (const [name, uri] of Object.entries(RELATIONSHIP_TYPES)) {\n if (uri === typeUri) {\n return name;\n }\n }\n\n // Try to extract from URI\n const lastSlash = typeUri.lastIndexOf('/');\n if (lastSlash >= 0) {\n return typeUri.substring(lastSlash + 1);\n }\n\n return 'unknown';\n}\n\n/**\n * Check if a relationship type is an external link (hyperlink)\n *\n * @param rel - Relationship to check\n * @returns true if this is an external hyperlink\n */\nexport function isExternalHyperlink(rel: Relationship): boolean {\n return rel.type === RELATIONSHIP_TYPES.hyperlink && rel.targetMode === 'External';\n}\n\n/**\n * Check if a relationship type is an image\n *\n * @param rel - Relationship to check\n * @returns true if this is an image relationship\n */\nexport function isImageRelationship(rel: Relationship): boolean {\n return rel.type === RELATIONSHIP_TYPES.image;\n}\n\n/**\n * Check if a relationship type is a header\n *\n * @param rel - Relationship to check\n * @returns true if this is a header relationship\n */\nexport function isHeaderRelationship(rel: Relationship): boolean {\n return rel.type === RELATIONSHIP_TYPES.header;\n}\n\n/**\n * Check if a relationship type is a footer\n *\n * @param rel - Relationship to check\n * @returns true if this is a footer relationship\n */\nexport function isFooterRelationship(rel: Relationship): boolean {\n return rel.type === RELATIONSHIP_TYPES.footer;\n}\n\n/**\n * Filter relationships by type\n *\n * @param map - RelationshipMap to filter\n * @param type - Relationship type URI to filter by\n * @returns Array of matching relationships\n */\nexport function filterByType(map: RelationshipMap, type: RelationshipType): Relationship[] {\n const results: Relationship[] = [];\n for (const rel of map.values()) {\n if (rel.type === type) {\n results.push(rel);\n }\n }\n return results;\n}\n\n/**\n * Get all images from a relationship map\n *\n * @param map - RelationshipMap to search\n * @returns Array of image relationships\n */\nexport function getImages(map: RelationshipMap): Relationship[] {\n return filterByType(map, RELATIONSHIP_TYPES.image);\n}\n\n/**\n * Get all hyperlinks from a relationship map\n *\n * @param map - RelationshipMap to search\n * @returns Array of hyperlink relationships\n */\nexport function getHyperlinks(map: RelationshipMap): Relationship[] {\n return filterByType(map, RELATIONSHIP_TYPES.hyperlink);\n}\n\n/**\n * Get all headers from a relationship map\n *\n * @param map - RelationshipMap to search\n * @returns Array of header relationships\n */\nexport function getHeaders(map: RelationshipMap): Relationship[] {\n return filterByType(map, RELATIONSHIP_TYPES.header);\n}\n\n/**\n * Get all footers from a relationship map\n *\n * @param map - RelationshipMap to search\n * @returns Array of footer relationships\n */\nexport function getFooters(map: RelationshipMap): Relationship[] {\n return filterByType(map, RELATIONSHIP_TYPES.footer);\n}\n\n/**\n * Resolve a relationship ID to a target path\n *\n * @param map - RelationshipMap to search\n * @param rId - Relationship ID (e.g., \"rId1\")\n * @returns Target path or undefined if not found\n */\nexport function resolveTarget(map: RelationshipMap, rId: string): string | undefined {\n const rel = map.get(rId);\n return rel?.target;\n}\n\n/**\n * Resolve a relationship ID to a full relationship\n *\n * @param map - RelationshipMap to search\n * @param rId - Relationship ID (e.g., \"rId1\")\n * @returns Relationship or undefined if not found\n */\nexport function resolveRelationship(map: RelationshipMap, rId: string): Relationship | undefined {\n return map.get(rId);\n}\n\n/**\n * Resolve a relative target path to an absolute path within the DOCX\n *\n * For example, if basePath is \"word/_rels/document.xml.rels\" and\n * target is \"media/image1.png\", the result is \"word/media/image1.png\"\n *\n * @param basePath - Path of the .rels file\n * @param target - Relative target from the relationship\n * @returns Absolute path within the DOCX\n */\nexport function resolveRelativePath(basePath: string, target: string): string {\n // If target starts with /, it's already absolute\n if (target.startsWith('/')) {\n return target.substring(1); // Remove leading /\n }\n\n // Get the directory of the .rels file\n // e.g., \"word/_rels/document.xml.rels\" -> \"word/_rels\"\n const lastSlash = basePath.lastIndexOf('/');\n let directory = lastSlash >= 0 ? basePath.substring(0, lastSlash) : '';\n\n // The .rels file is in _rels subdirectory, go up one level\n // e.g., \"word/_rels\" -> \"word\"\n if (directory.endsWith('/_rels')) {\n directory = directory.substring(0, directory.length - 6);\n } else if (directory === '_rels') {\n directory = '';\n }\n\n // Handle ../ in target\n const parts = target.split('/');\n const dirParts = directory ? directory.split('/') : [];\n\n for (const part of parts) {\n if (part === '..') {\n dirParts.pop();\n } else if (part !== '.') {\n dirParts.push(part);\n }\n }\n\n return dirParts.join('/');\n}\n\n/**\n * Parse document.xml.rels specifically\n *\n * This is a convenience wrapper for the main document relationships.\n *\n * @param relsXml - XML content of word/_rels/document.xml.rels\n * @returns RelationshipMap\n */\nexport function parseDocumentRelationships(relsXml: string): RelationshipMap {\n return parseRelationships(relsXml);\n}\n\n/**\n * Parse package-level .rels\n *\n * This is a convenience wrapper for the package relationships (_rels/.rels)\n *\n * @param relsXml - XML content of _rels/.rels\n * @returns RelationshipMap\n */\nexport function parsePackageRelationships(relsXml: string): RelationshipMap {\n return parseRelationships(relsXml);\n}\n\n/**\n * Debug: Print all relationships in a map\n *\n * @param map - RelationshipMap to print\n */\nexport function printRelationships(map: RelationshipMap): void {\n /* eslint-disable no-console */\n console.log('Relationships:');\n for (const [id, rel] of map.entries()) {\n const typeName = getRelationshipTypeName(rel.type);\n console.log(\n ` ${id}: ${typeName} -> ${rel.target}${rel.targetMode === 'External' ? ' (External)' : ''}`\n );\n }\n /* eslint-enable no-console */\n}\n","/**\n * Theme Parser - Parse theme1.xml for colors and fonts\n *\n * Extracts color scheme (dk1, lt1, dk2, lt2, accent1-6, hlink, folHlink)\n * and font scheme (majorFont, minorFont) from the theme.\n *\n * OOXML Reference:\n * - Theme file is at: word/theme/theme1.xml\n * - Uses DrawingML namespace (a:)\n * - Colors can be srgbClr, sysClr, or schemeClr\n */\n\nimport type { Theme, ThemeColorScheme, ThemeFontScheme, ThemeFont } from '../types/document';\nimport {\n parseXmlDocument,\n findChild,\n findChildren,\n getAttribute,\n getChildElements,\n getLocalName,\n type XmlElement,\n} from './xmlParser';\n\n/**\n * Default theme colors (Office 2016 default theme)\n * Used when theme1.xml is missing or malformed\n */\nconst DEFAULT_COLORS: ThemeColorScheme = {\n dk1: '000000', // Black\n lt1: 'FFFFFF', // White\n dk2: '44546A', // Dark blue-gray\n lt2: 'E7E6E6', // Light gray\n accent1: '4472C4', // Blue\n accent2: 'ED7D31', // Orange\n accent3: 'A5A5A5', // Gray\n accent4: 'FFC000', // Gold\n accent5: '5B9BD5', // Light blue\n accent6: '70AD47', // Green\n hlink: '0563C1', // Hyperlink blue\n folHlink: '954F72', // Followed hyperlink purple\n};\n\n/**\n * Default font scheme\n */\nconst DEFAULT_FONTS: ThemeFontScheme = {\n majorFont: {\n latin: 'Calibri Light',\n ea: '',\n cs: '',\n fonts: {},\n },\n minorFont: {\n latin: 'Calibri',\n ea: '',\n cs: '',\n fonts: {},\n },\n};\n\n/**\n * Default theme when no theme1.xml exists\n */\nconst DEFAULT_THEME: Theme = {\n name: 'Office Theme',\n colorScheme: DEFAULT_COLORS,\n fontScheme: DEFAULT_FONTS,\n};\n\n/**\n * Color slot names in theme\n */\nconst COLOR_SLOTS = [\n 'dk1',\n 'lt1',\n 'dk2',\n 'lt2',\n 'accent1',\n 'accent2',\n 'accent3',\n 'accent4',\n 'accent5',\n 'accent6',\n 'hlink',\n 'folHlink',\n] as const;\n\n/**\n * Parse a color element (srgbClr, sysClr, or schemeClr)\n *\n * @param element - Color child element\n * @returns Hex color value (6 characters, no #)\n */\nfunction parseColorElement(element: XmlElement | null): string | null {\n if (!element) return null;\n\n const localName = getLocalName(element.name || '');\n\n switch (localName) {\n case 'srgbClr': {\n // Direct RGB color: <a:srgbClr val=\"4472C4\"/>\n const val = getAttribute(element, 'a', 'val') ?? getAttribute(element, null, 'val');\n return val ?? null;\n }\n\n case 'sysClr': {\n // System color with fallback: <a:sysClr val=\"windowText\" lastClr=\"000000\"/>\n // Use lastClr as the fallback since we can't access actual system colors\n const lastClr =\n getAttribute(element, 'a', 'lastClr') ?? getAttribute(element, null, 'lastClr');\n if (lastClr) return lastClr;\n\n // Fallback based on common system color names\n const val = getAttribute(element, 'a', 'val') ?? getAttribute(element, null, 'val');\n switch (val) {\n case 'windowText':\n case 'menuText':\n case 'captionText':\n case 'btnText':\n return '000000';\n case 'window':\n case 'menu':\n case 'btnFace':\n case 'btnHighlight':\n return 'FFFFFF';\n case 'highlight':\n return '0078D7';\n case 'highlightText':\n return 'FFFFFF';\n case 'grayText':\n return '808080';\n default:\n return null;\n }\n }\n\n case 'schemeClr': {\n // Reference to another scheme color - rare in color scheme itself\n // Usually found in fill/line definitions with modifiers\n // For the color scheme, we just need the val\n const val = getAttribute(element, 'a', 'val') ?? getAttribute(element, null, 'val');\n // This is a reference, not a final color - return null for now\n // The actual resolution would need the full color scheme\n return val === 'phClr' ? null : null;\n }\n\n default:\n return null;\n }\n}\n\n/**\n * Parse the color scheme from a:clrScheme element\n *\n * @param clrScheme - The a:clrScheme element\n * @returns ThemeColorScheme with resolved hex colors\n */\nfunction parseColorScheme(clrScheme: XmlElement | null): ThemeColorScheme {\n const result: ThemeColorScheme = { ...DEFAULT_COLORS };\n\n if (!clrScheme) return result;\n\n // Each color slot has a child element with the slot name\n for (const slot of COLOR_SLOTS) {\n // Find the slot element (e.g., a:dk1, a:accent1)\n const slotElement = findChild(clrScheme, 'a', slot);\n\n if (slotElement) {\n // The actual color is a child (srgbClr, sysClr, or schemeClr)\n const children = getChildElements(slotElement);\n if (children.length > 0) {\n const color = parseColorElement(children[0]);\n if (color) {\n result[slot] = color;\n }\n }\n }\n }\n\n return result;\n}\n\n/**\n * Parse a font definition (majorFont or minorFont)\n *\n * @param fontElement - The a:majorFont or a:minorFont element\n * @returns ThemeFont with font family names\n */\nfunction parseThemeFonts(fontElement: XmlElement | null): ThemeFont {\n const result: ThemeFont = {\n latin: '',\n ea: '',\n cs: '',\n fonts: {},\n };\n\n if (!fontElement) return result;\n\n // Parse main font elements\n const latinEl = findChild(fontElement, 'a', 'latin');\n if (latinEl) {\n result.latin =\n getAttribute(latinEl, 'a', 'typeface') ?? getAttribute(latinEl, null, 'typeface') ?? '';\n }\n\n const eaEl = findChild(fontElement, 'a', 'ea');\n if (eaEl) {\n result.ea = getAttribute(eaEl, 'a', 'typeface') ?? getAttribute(eaEl, null, 'typeface') ?? '';\n }\n\n const csEl = findChild(fontElement, 'a', 'cs');\n if (csEl) {\n result.cs = getAttribute(csEl, 'a', 'typeface') ?? getAttribute(csEl, null, 'typeface') ?? '';\n }\n\n // Parse script-specific fonts (a:font elements with script attribute)\n const fontElements = findChildren(fontElement, 'a', 'font');\n for (const font of fontElements) {\n const script = getAttribute(font, 'a', 'script') ?? getAttribute(font, null, 'script');\n const typeface = getAttribute(font, 'a', 'typeface') ?? getAttribute(font, null, 'typeface');\n\n if (script && typeface) {\n result.fonts = result.fonts || {};\n result.fonts[script] = typeface;\n }\n }\n\n return result;\n}\n\n/**\n * Parse the font scheme from a:fontScheme element\n *\n * @param fontScheme - The a:fontScheme element\n * @returns ThemeFontScheme with major and minor fonts\n */\nfunction parseFontScheme(fontScheme: XmlElement | null): ThemeFontScheme {\n const result: ThemeFontScheme = { ...DEFAULT_FONTS };\n\n if (!fontScheme) return result;\n\n const majorFontEl = findChild(fontScheme, 'a', 'majorFont');\n if (majorFontEl) {\n result.majorFont = parseThemeFonts(majorFontEl);\n }\n\n const minorFontEl = findChild(fontScheme, 'a', 'minorFont');\n if (minorFontEl) {\n result.minorFont = parseThemeFonts(minorFontEl);\n }\n\n return result;\n}\n\n/**\n * Parse theme1.xml content\n *\n * @param themeXml - XML content of theme1.xml, or null if not present\n * @returns Parsed Theme object with colors and fonts\n */\nexport function parseTheme(themeXml: string | null): Theme {\n // Return defaults if no theme XML\n if (!themeXml) {\n return { ...DEFAULT_THEME };\n }\n\n try {\n const doc = parseXmlDocument(themeXml);\n if (!doc) {\n return { ...DEFAULT_THEME };\n }\n\n // Get theme name from root element\n const themeName =\n getAttribute(doc, 'a', 'name') ?? getAttribute(doc, null, 'name') ?? 'Office Theme';\n\n // Find a:themeElements which contains clrScheme and fontScheme\n const themeElements = findChild(doc, 'a', 'themeElements');\n\n // Parse color scheme\n const clrScheme = findChild(themeElements, 'a', 'clrScheme');\n const colorScheme = parseColorScheme(clrScheme);\n\n // Parse font scheme\n const fontSchemeEl = findChild(themeElements, 'a', 'fontScheme');\n const fontScheme = parseFontScheme(fontSchemeEl);\n\n return {\n name: themeName,\n colorScheme,\n fontScheme,\n };\n } catch (error) {\n console.warn('Failed to parse theme:', error);\n return { ...DEFAULT_THEME };\n }\n}\n\n/**\n * Get a color from the theme by slot name\n *\n * @param theme - Parsed theme\n * @param slot - Color slot name (dk1, lt1, accent1, etc.)\n * @returns Hex color value (6 characters, no #)\n */\nexport function getThemeColor(\n theme: Theme | null | undefined,\n slot: keyof ThemeColorScheme\n): string {\n if (!theme?.colorScheme) {\n return DEFAULT_COLORS[slot] ?? '000000';\n }\n\n return theme.colorScheme[slot] ?? DEFAULT_COLORS[slot] ?? '000000';\n}\n\n/**\n * Get the major font (heading font) from theme\n *\n * @param theme - Parsed theme\n * @param script - Optional script code (defaults to latin)\n * @returns Font family name\n */\nexport function getMajorFont(theme: Theme | null | undefined, script: string = 'latin'): string {\n if (!theme?.fontScheme?.majorFont) {\n return DEFAULT_FONTS.majorFont?.latin ?? 'Calibri Light';\n }\n\n const majorFont = theme.fontScheme.majorFont;\n\n if (script === 'latin') return majorFont.latin || 'Calibri Light';\n if (script === 'ea') return majorFont.ea || '';\n if (script === 'cs') return majorFont.cs || '';\n\n // Check script-specific fonts\n if (majorFont.fonts?.[script]) {\n return majorFont.fonts[script];\n }\n\n // Default to latin\n return majorFont.latin || 'Calibri Light';\n}\n\n/**\n * Get the minor font (body font) from theme\n *\n * @param theme - Parsed theme\n * @param script - Optional script code (defaults to latin)\n * @returns Font family name\n */\nexport function getMinorFont(theme: Theme | null | undefined, script: string = 'latin'): string {\n if (!theme?.fontScheme?.minorFont) {\n return DEFAULT_FONTS.minorFont?.latin ?? 'Calibri';\n }\n\n const minorFont = theme.fontScheme.minorFont;\n\n if (script === 'latin') return minorFont.latin || 'Calibri';\n if (script === 'ea') return minorFont.ea || '';\n if (script === 'cs') return minorFont.cs || '';\n\n // Check script-specific fonts\n if (minorFont.fonts?.[script]) {\n return minorFont.fonts[script];\n }\n\n // Default to latin\n return minorFont.latin || 'Calibri';\n}\n\n/**\n * Resolve a theme font reference to an actual font name\n *\n * Theme font references are like: majorAscii, majorHAnsi, minorAscii, minorHAnsi, etc.\n *\n * @param theme - Parsed theme\n * @param themeRef - Theme font reference\n * @returns Font family name\n */\nexport function resolveThemeFontRef(theme: Theme | null | undefined, themeRef: string): string {\n if (!themeRef) return 'Calibri';\n\n // Parse the reference: major/minor + script type\n const isMajor = themeRef.toLowerCase().includes('major');\n const isMinor = themeRef.toLowerCase().includes('minor');\n\n // Determine script from reference\n let script = 'latin';\n const lowerRef = themeRef.toLowerCase();\n\n if (lowerRef.includes('eastasia')) {\n script = 'ea';\n } else if (lowerRef.includes('bidi') || lowerRef.includes('cs')) {\n script = 'cs';\n }\n // ascii and hAnsi both map to latin\n\n if (isMajor) {\n return getMajorFont(theme, script);\n } else if (isMinor) {\n return getMinorFont(theme, script);\n }\n\n // Default to minor latin\n return getMinorFont(theme, 'latin');\n}\n\n/**\n * Get all font families from the theme for preloading\n *\n * @param theme - Parsed theme\n * @returns Array of unique font family names\n */\nexport function getThemeFonts(theme: Theme | null | undefined): string[] {\n const fonts = new Set<string>();\n\n if (theme?.fontScheme) {\n const { majorFont, minorFont } = theme.fontScheme;\n\n // Add main fonts\n if (majorFont?.latin) fonts.add(majorFont.latin);\n if (majorFont?.ea) fonts.add(majorFont.ea);\n if (majorFont?.cs) fonts.add(majorFont.cs);\n\n if (minorFont?.latin) fonts.add(minorFont.latin);\n if (minorFont?.ea) fonts.add(minorFont.ea);\n if (minorFont?.cs) fonts.add(minorFont.cs);\n\n // Add script-specific fonts\n if (majorFont?.fonts) {\n for (const font of Object.values(majorFont.fonts)) {\n if (font) fonts.add(font);\n }\n }\n\n if (minorFont?.fonts) {\n for (const font of Object.values(minorFont.fonts)) {\n if (font) fonts.add(font);\n }\n }\n }\n\n // Remove empty strings\n fonts.delete('');\n\n return Array.from(fonts);\n}\n\n/**\n * Get the default theme (Office 2016 theme)\n *\n * @returns Default Theme object\n */\nexport function getDefaultTheme(): Theme {\n return { ...DEFAULT_THEME };\n}\n","/**\n * Style Parser - Parse styles.xml with full inheritance resolution\n *\n * Parses all style types (paragraph, character, table, list) with\n * complete basedOn inheritance chain resolution.\n *\n * OOXML Reference:\n * - Style file is at: word/styles.xml\n * - Uses WordprocessingML namespace (w:)\n *\n * Style Cascade (lowest to highest priority):\n * 1. Document defaults (w:docDefaults)\n * 2. Parent style properties (w:basedOn chain)\n * 3. Current style properties\n * 4. Direct formatting in document\n */\n\nimport type {\n Theme,\n Style,\n StyleType,\n StyleDefinitions,\n DocDefaults,\n TextFormatting,\n ParagraphFormatting,\n TableFormatting,\n TableRowFormatting,\n TableCellFormatting,\n ColorValue,\n BorderSpec,\n ShadingProperties,\n TabStop,\n TabStopAlignment,\n TabLeader,\n UnderlineStyle,\n TableBorders,\n CellMargins,\n TableLook,\n TableMeasurement,\n} from '../types/document';\nimport {\n parseXmlDocument,\n findChild,\n findChildren,\n getAttribute,\n parseBooleanElement,\n parseNumericAttribute,\n type XmlElement,\n} from './xmlParser';\nimport { resolveThemeFontRef } from './themeParser';\n\n/**\n * Style map keyed by styleId\n */\nexport type StyleMap = Map<string, Style>;\n\n/**\n * Parse text formatting properties (w:rPr)\n */\nfunction parseRunProperties(\n rPr: XmlElement | null,\n theme: Theme | null\n): TextFormatting | undefined {\n if (!rPr) return undefined;\n\n const formatting: TextFormatting = {};\n\n // Bold\n const b = findChild(rPr, 'w', 'b');\n if (b) formatting.bold = parseBooleanElement(b);\n\n const bCs = findChild(rPr, 'w', 'bCs');\n if (bCs) formatting.boldCs = parseBooleanElement(bCs);\n\n // Italic\n const i = findChild(rPr, 'w', 'i');\n if (i) formatting.italic = parseBooleanElement(i);\n\n const iCs = findChild(rPr, 'w', 'iCs');\n if (iCs) formatting.italicCs = parseBooleanElement(iCs);\n\n // Underline\n const u = findChild(rPr, 'w', 'u');\n if (u) {\n const style = getAttribute(u, 'w', 'val') as UnderlineStyle | null;\n if (style) {\n formatting.underline = { style };\n const colorVal = getAttribute(u, 'w', 'color');\n const themeColor = getAttribute(u, 'w', 'themeColor');\n if (colorVal || themeColor) {\n formatting.underline.color = parseColorValue(\n colorVal,\n themeColor,\n getAttribute(u, 'w', 'themeTint'),\n getAttribute(u, 'w', 'themeShade')\n );\n }\n }\n }\n\n // Strikethrough\n const strike = findChild(rPr, 'w', 'strike');\n if (strike) formatting.strike = parseBooleanElement(strike);\n\n const dstrike = findChild(rPr, 'w', 'dstrike');\n if (dstrike) formatting.doubleStrike = parseBooleanElement(dstrike);\n\n // Vertical alignment (superscript/subscript)\n const vertAlign = findChild(rPr, 'w', 'vertAlign');\n if (vertAlign) {\n const val = getAttribute(vertAlign, 'w', 'val');\n if (val === 'superscript' || val === 'subscript' || val === 'baseline') {\n formatting.vertAlign = val;\n }\n }\n\n // Capitalization\n const smallCaps = findChild(rPr, 'w', 'smallCaps');\n if (smallCaps) formatting.smallCaps = parseBooleanElement(smallCaps);\n\n const caps = findChild(rPr, 'w', 'caps');\n if (caps) formatting.allCaps = parseBooleanElement(caps);\n\n // Hidden\n const vanish = findChild(rPr, 'w', 'vanish');\n if (vanish) formatting.hidden = parseBooleanElement(vanish);\n\n // Color\n const color = findChild(rPr, 'w', 'color');\n if (color) {\n formatting.color = parseColorValue(\n getAttribute(color, 'w', 'val'),\n getAttribute(color, 'w', 'themeColor'),\n getAttribute(color, 'w', 'themeTint'),\n getAttribute(color, 'w', 'themeShade')\n );\n }\n\n // Highlight\n const highlight = findChild(rPr, 'w', 'highlight');\n if (highlight) {\n const val = getAttribute(highlight, 'w', 'val');\n if (val) {\n formatting.highlight = val as TextFormatting['highlight'];\n }\n }\n\n // Character shading\n const shd = findChild(rPr, 'w', 'shd');\n if (shd) {\n formatting.shading = parseShadingProperties(shd);\n }\n\n // Font size (in half-points)\n const sz = findChild(rPr, 'w', 'sz');\n if (sz) {\n const val = parseNumericAttribute(sz, 'w', 'val');\n if (val !== undefined) formatting.fontSize = val;\n }\n\n const szCs = findChild(rPr, 'w', 'szCs');\n if (szCs) {\n const val = parseNumericAttribute(szCs, 'w', 'val');\n if (val !== undefined) formatting.fontSizeCs = val;\n }\n\n // Font family\n const rFonts = findChild(rPr, 'w', 'rFonts');\n if (rFonts) {\n formatting.fontFamily = {\n ascii: getAttribute(rFonts, 'w', 'ascii') ?? undefined,\n hAnsi: getAttribute(rFonts, 'w', 'hAnsi') ?? undefined,\n eastAsia: getAttribute(rFonts, 'w', 'eastAsia') ?? undefined,\n cs: getAttribute(rFonts, 'w', 'cs') ?? undefined,\n };\n\n // Theme font references - resolve to actual font names\n const asciiTheme = getAttribute(rFonts, 'w', 'asciiTheme');\n if (asciiTheme) {\n formatting.fontFamily.asciiTheme = asciiTheme as TextFormatting['fontFamily'] extends {\n asciiTheme?: infer T;\n }\n ? T\n : never;\n // Also resolve the actual font name for convenience\n if (theme && !formatting.fontFamily.ascii) {\n formatting.fontFamily.ascii = resolveThemeFontRef(theme, asciiTheme);\n }\n }\n const hAnsiTheme = getAttribute(rFonts, 'w', 'hAnsiTheme');\n if (hAnsiTheme) {\n formatting.fontFamily.hAnsiTheme = hAnsiTheme;\n if (theme && !formatting.fontFamily.hAnsi) {\n formatting.fontFamily.hAnsi = resolveThemeFontRef(theme, hAnsiTheme);\n }\n }\n const eastAsiaTheme = getAttribute(rFonts, 'w', 'eastAsiaTheme');\n if (eastAsiaTheme) {\n formatting.fontFamily.eastAsiaTheme = eastAsiaTheme;\n if (theme && !formatting.fontFamily.eastAsia) {\n formatting.fontFamily.eastAsia = resolveThemeFontRef(theme, eastAsiaTheme);\n }\n }\n const csTheme = getAttribute(rFonts, 'w', 'cstheme');\n if (csTheme) {\n formatting.fontFamily.csTheme = csTheme;\n if (theme && !formatting.fontFamily.cs) {\n formatting.fontFamily.cs = resolveThemeFontRef(theme, csTheme);\n }\n }\n }\n\n // Character spacing (in twips)\n const spacing = findChild(rPr, 'w', 'spacing');\n if (spacing) {\n const val = parseNumericAttribute(spacing, 'w', 'val');\n if (val !== undefined) formatting.spacing = val;\n }\n\n // Position (raised/lowered in half-points)\n const position = findChild(rPr, 'w', 'position');\n if (position) {\n const val = parseNumericAttribute(position, 'w', 'val');\n if (val !== undefined) formatting.position = val;\n }\n\n // Scale (horizontal text scale percentage)\n const w = findChild(rPr, 'w', 'w');\n if (w) {\n const val = parseNumericAttribute(w, 'w', 'val');\n if (val !== undefined) formatting.scale = val;\n }\n\n // Kerning\n const kern = findChild(rPr, 'w', 'kern');\n if (kern) {\n const val = parseNumericAttribute(kern, 'w', 'val');\n if (val !== undefined) formatting.kerning = val;\n }\n\n // Text effects\n const effect = findChild(rPr, 'w', 'effect');\n if (effect) {\n const val = getAttribute(effect, 'w', 'val');\n if (val) formatting.effect = val as TextFormatting['effect'];\n }\n\n // Emphasis mark\n const em = findChild(rPr, 'w', 'em');\n if (em) {\n const val = getAttribute(em, 'w', 'val');\n if (val) formatting.emphasisMark = val as TextFormatting['emphasisMark'];\n }\n\n // Other effects\n const emboss = findChild(rPr, 'w', 'emboss');\n if (emboss) formatting.emboss = parseBooleanElement(emboss);\n\n const imprint = findChild(rPr, 'w', 'imprint');\n if (imprint) formatting.imprint = parseBooleanElement(imprint);\n\n const outline = findChild(rPr, 'w', 'outline');\n if (outline) formatting.outline = parseBooleanElement(outline);\n\n const shadow = findChild(rPr, 'w', 'shadow');\n if (shadow) formatting.shadow = parseBooleanElement(shadow);\n\n // RTL and complex script\n const rtl = findChild(rPr, 'w', 'rtl');\n if (rtl) formatting.rtl = parseBooleanElement(rtl);\n\n const cs = findChild(rPr, 'w', 'cs');\n if (cs) formatting.cs = parseBooleanElement(cs);\n\n // Character style reference\n const rStyle = findChild(rPr, 'w', 'rStyle');\n if (rStyle) {\n const val = getAttribute(rStyle, 'w', 'val');\n if (val) formatting.styleId = val;\n }\n\n return Object.keys(formatting).length > 0 ? formatting : undefined;\n}\n\n/**\n * Parse color value from attributes\n */\nfunction parseColorValue(\n rgb: string | null,\n themeColor: string | null,\n themeTint: string | null,\n themeShade: string | null\n): ColorValue {\n const color: ColorValue = {};\n\n if (rgb && rgb !== 'auto') {\n color.rgb = rgb;\n } else if (rgb === 'auto') {\n color.auto = true;\n }\n\n if (themeColor) {\n color.themeColor = themeColor as ColorValue['themeColor'];\n }\n\n if (themeTint) {\n color.themeTint = themeTint;\n }\n\n if (themeShade) {\n color.themeShade = themeShade;\n }\n\n return color;\n}\n\n/**\n * Parse shading properties (w:shd)\n */\nfunction parseShadingProperties(shd: XmlElement | null): ShadingProperties | undefined {\n if (!shd) return undefined;\n\n const props: ShadingProperties = {};\n\n const color = getAttribute(shd, 'w', 'color');\n if (color && color !== 'auto') {\n props.color = { rgb: color };\n }\n\n const fill = getAttribute(shd, 'w', 'fill');\n if (fill && fill !== 'auto') {\n props.fill = { rgb: fill };\n }\n\n const themeFill = getAttribute(shd, 'w', 'themeFill');\n if (themeFill) {\n props.fill = props.fill || {};\n props.fill.themeColor = themeFill as ColorValue['themeColor'];\n }\n\n const themeFillTint = getAttribute(shd, 'w', 'themeFillTint');\n if (themeFillTint && props.fill) {\n props.fill.themeTint = themeFillTint;\n }\n\n const themeFillShade = getAttribute(shd, 'w', 'themeFillShade');\n if (themeFillShade && props.fill) {\n props.fill.themeShade = themeFillShade;\n }\n\n const pattern = getAttribute(shd, 'w', 'val');\n if (pattern) {\n props.pattern = pattern as ShadingProperties['pattern'];\n }\n\n return Object.keys(props).length > 0 ? props : undefined;\n}\n\n/**\n * Parse border specification\n */\nfunction parseBorderSpec(border: XmlElement | null): BorderSpec | undefined {\n if (!border) return undefined;\n\n const style = getAttribute(border, 'w', 'val');\n if (!style) return undefined;\n\n const spec: BorderSpec = {\n style: style as BorderSpec['style'],\n };\n\n const colorVal = getAttribute(border, 'w', 'color');\n const themeColor = getAttribute(border, 'w', 'themeColor');\n if (colorVal || themeColor) {\n spec.color = parseColorValue(\n colorVal,\n themeColor,\n getAttribute(border, 'w', 'themeTint'),\n getAttribute(border, 'w', 'themeShade')\n );\n }\n\n const sz = parseNumericAttribute(border, 'w', 'sz');\n if (sz !== undefined) spec.size = sz;\n\n const space = parseNumericAttribute(border, 'w', 'space');\n if (space !== undefined) spec.space = space;\n\n const shadowAttr = getAttribute(border, 'w', 'shadow');\n if (shadowAttr) spec.shadow = shadowAttr === '1' || shadowAttr === 'true';\n\n const frame = getAttribute(border, 'w', 'frame');\n if (frame) spec.frame = frame === '1' || frame === 'true';\n\n return spec;\n}\n\n/**\n * Parse tab stops (w:tabs)\n */\nfunction parseTabStops(tabs: XmlElement | null): TabStop[] | undefined {\n if (!tabs) return undefined;\n\n const tabElements = findChildren(tabs, 'w', 'tab');\n if (tabElements.length === 0) return undefined;\n\n const result: TabStop[] = [];\n\n for (const tab of tabElements) {\n const pos = parseNumericAttribute(tab, 'w', 'pos');\n const val = getAttribute(tab, 'w', 'val');\n\n if (pos !== undefined && val) {\n const tabStop: TabStop = {\n position: pos,\n alignment: val as TabStopAlignment,\n };\n\n const leader = getAttribute(tab, 'w', 'leader');\n if (leader) {\n tabStop.leader = leader as TabLeader;\n }\n\n result.push(tabStop);\n }\n }\n\n return result.length > 0 ? result : undefined;\n}\n\n/**\n * Parse paragraph formatting properties (w:pPr)\n */\nfunction parseParagraphProperties(\n pPr: XmlElement | null,\n theme: Theme | null\n): ParagraphFormatting | undefined {\n if (!pPr) return undefined;\n\n const formatting: ParagraphFormatting = {};\n\n // Alignment\n const jc = findChild(pPr, 'w', 'jc');\n if (jc) {\n const val = getAttribute(jc, 'w', 'val');\n if (val) formatting.alignment = val as ParagraphFormatting['alignment'];\n }\n\n // Bidi\n const bidi = findChild(pPr, 'w', 'bidi');\n if (bidi) formatting.bidi = parseBooleanElement(bidi);\n\n // Spacing\n const spacing = findChild(pPr, 'w', 'spacing');\n if (spacing) {\n const before = parseNumericAttribute(spacing, 'w', 'before');\n if (before !== undefined) formatting.spaceBefore = before;\n\n const after = parseNumericAttribute(spacing, 'w', 'after');\n if (after !== undefined) formatting.spaceAfter = after;\n\n const line = parseNumericAttribute(spacing, 'w', 'line');\n if (line !== undefined) formatting.lineSpacing = line;\n\n const lineRule = getAttribute(spacing, 'w', 'lineRule');\n if (lineRule) formatting.lineSpacingRule = lineRule as ParagraphFormatting['lineSpacingRule'];\n\n const beforeAuto = getAttribute(spacing, 'w', 'beforeAutospacing');\n if (beforeAuto) formatting.beforeAutospacing = beforeAuto === '1' || beforeAuto === 'true';\n\n const afterAuto = getAttribute(spacing, 'w', 'afterAutospacing');\n if (afterAuto) formatting.afterAutospacing = afterAuto === '1' || afterAuto === 'true';\n }\n\n // Indentation\n const ind = findChild(pPr, 'w', 'ind');\n if (ind) {\n const left = parseNumericAttribute(ind, 'w', 'left');\n if (left !== undefined) formatting.indentLeft = left;\n\n const right = parseNumericAttribute(ind, 'w', 'right');\n if (right !== undefined) formatting.indentRight = right;\n\n const firstLine = parseNumericAttribute(ind, 'w', 'firstLine');\n if (firstLine !== undefined) formatting.indentFirstLine = firstLine;\n\n const hanging = parseNumericAttribute(ind, 'w', 'hanging');\n if (hanging !== undefined) {\n formatting.indentFirstLine = -hanging;\n formatting.hangingIndent = true;\n }\n }\n\n // Borders\n const pBdr = findChild(pPr, 'w', 'pBdr');\n if (pBdr) {\n const borders: ParagraphFormatting['borders'] = {};\n const top = parseBorderSpec(findChild(pBdr, 'w', 'top'));\n if (top) borders.top = top;\n const bottom = parseBorderSpec(findChild(pBdr, 'w', 'bottom'));\n if (bottom) borders.bottom = bottom;\n const left = parseBorderSpec(findChild(pBdr, 'w', 'left'));\n if (left) borders.left = left;\n const right = parseBorderSpec(findChild(pBdr, 'w', 'right'));\n if (right) borders.right = right;\n const between = parseBorderSpec(findChild(pBdr, 'w', 'between'));\n if (between) borders.between = between;\n const bar = parseBorderSpec(findChild(pBdr, 'w', 'bar'));\n if (bar) borders.bar = bar;\n\n if (Object.keys(borders).length > 0) {\n formatting.borders = borders;\n }\n }\n\n // Shading\n const shd = findChild(pPr, 'w', 'shd');\n if (shd) {\n formatting.shading = parseShadingProperties(shd);\n }\n\n // Tab stops\n const tabs = findChild(pPr, 'w', 'tabs');\n if (tabs) {\n formatting.tabs = parseTabStops(tabs);\n }\n\n // Page break control\n const keepNext = findChild(pPr, 'w', 'keepNext');\n if (keepNext) formatting.keepNext = parseBooleanElement(keepNext);\n\n const keepLines = findChild(pPr, 'w', 'keepLines');\n if (keepLines) formatting.keepLines = parseBooleanElement(keepLines);\n\n const widowControl = findChild(pPr, 'w', 'widowControl');\n if (widowControl) formatting.widowControl = parseBooleanElement(widowControl);\n\n const pageBreakBefore = findChild(pPr, 'w', 'pageBreakBefore');\n if (pageBreakBefore) formatting.pageBreakBefore = parseBooleanElement(pageBreakBefore);\n\n // Numbering properties\n const numPr = findChild(pPr, 'w', 'numPr');\n if (numPr) {\n const numId = findChild(numPr, 'w', 'numId');\n const ilvl = findChild(numPr, 'w', 'ilvl');\n\n if (numId || ilvl) {\n formatting.numPr = {};\n if (numId) {\n const val = parseNumericAttribute(numId, 'w', 'val');\n if (val !== undefined) formatting.numPr.numId = val;\n }\n if (ilvl) {\n const val = parseNumericAttribute(ilvl, 'w', 'val');\n if (val !== undefined) formatting.numPr.ilvl = val;\n }\n }\n }\n\n // Outline level\n const outlineLvl = findChild(pPr, 'w', 'outlineLvl');\n if (outlineLvl) {\n const val = parseNumericAttribute(outlineLvl, 'w', 'val');\n if (val !== undefined) formatting.outlineLevel = val;\n }\n\n // Style reference\n const pStyle = findChild(pPr, 'w', 'pStyle');\n if (pStyle) {\n const val = getAttribute(pStyle, 'w', 'val');\n if (val) formatting.styleId = val;\n }\n\n // Suppress line numbers\n const suppressLineNumbers = findChild(pPr, 'w', 'suppressLineNumbers');\n if (suppressLineNumbers)\n formatting.suppressLineNumbers = parseBooleanElement(suppressLineNumbers);\n\n // Suppress auto hyphens\n const suppressAutoHyphens = findChild(pPr, 'w', 'suppressAutoHyphens');\n if (suppressAutoHyphens)\n formatting.suppressAutoHyphens = parseBooleanElement(suppressAutoHyphens);\n\n // Run properties for this paragraph (default run formatting)\n const rPr = findChild(pPr, 'w', 'rPr');\n if (rPr) {\n formatting.runProperties = parseRunProperties(rPr, theme);\n }\n\n return Object.keys(formatting).length > 0 ? formatting : undefined;\n}\n\n/**\n * Parse table measurement (width/height with type)\n */\nfunction parseTableMeasurement(element: XmlElement | null): TableMeasurement | undefined {\n if (!element) return undefined;\n\n const w = parseNumericAttribute(element, 'w', 'w');\n const type = getAttribute(element, 'w', 'type');\n\n if (w !== undefined && type) {\n return {\n value: w,\n type: type as TableMeasurement['type'],\n };\n }\n\n return undefined;\n}\n\n/**\n * Parse table borders\n */\nfunction parseTableBorders(tblBorders: XmlElement | null): TableBorders | undefined {\n if (!tblBorders) return undefined;\n\n const borders: TableBorders = {};\n\n const top = parseBorderSpec(findChild(tblBorders, 'w', 'top'));\n if (top) borders.top = top;\n\n const bottom = parseBorderSpec(findChild(tblBorders, 'w', 'bottom'));\n if (bottom) borders.bottom = bottom;\n\n const left = parseBorderSpec(findChild(tblBorders, 'w', 'left'));\n if (left) borders.left = left;\n\n const right = parseBorderSpec(findChild(tblBorders, 'w', 'right'));\n if (right) borders.right = right;\n\n const insideH = parseBorderSpec(findChild(tblBorders, 'w', 'insideH'));\n if (insideH) borders.insideH = insideH;\n\n const insideV = parseBorderSpec(findChild(tblBorders, 'w', 'insideV'));\n if (insideV) borders.insideV = insideV;\n\n return Object.keys(borders).length > 0 ? borders : undefined;\n}\n\n/**\n * Parse cell margins\n */\nfunction parseCellMargins(tblCellMar: XmlElement | null): CellMargins | undefined {\n if (!tblCellMar) return undefined;\n\n const margins: CellMargins = {};\n\n const top = parseTableMeasurement(findChild(tblCellMar, 'w', 'top'));\n if (top) margins.top = top;\n\n const bottom = parseTableMeasurement(findChild(tblCellMar, 'w', 'bottom'));\n if (bottom) margins.bottom = bottom;\n\n const left = parseTableMeasurement(findChild(tblCellMar, 'w', 'left'));\n if (left) margins.left = left;\n\n const right = parseTableMeasurement(findChild(tblCellMar, 'w', 'right'));\n if (right) margins.right = right;\n\n return Object.keys(margins).length > 0 ? margins : undefined;\n}\n\n/**\n * Parse table look flags\n */\nfunction parseTableLook(tblLook: XmlElement | null): TableLook | undefined {\n if (!tblLook) return undefined;\n\n const look: TableLook = {};\n\n // Can be specified as individual attributes or a single val attribute\n const val = getAttribute(tblLook, 'w', 'val');\n if (val) {\n // val is a hex bitmap: bit 0=firstRow, 1=lastRow, 2=firstCol, 3=lastCol, 4=noHBand, 5=noVBand\n const num = parseInt(val, 16);\n if (!isNaN(num)) {\n look.firstRow = (num & 0x0020) !== 0;\n look.lastRow = (num & 0x0040) !== 0;\n look.firstColumn = (num & 0x0080) !== 0;\n look.lastColumn = (num & 0x0100) !== 0;\n look.noHBand = (num & 0x0200) !== 0;\n look.noVBand = (num & 0x0400) !== 0;\n }\n }\n\n // Individual attributes override\n const firstColumn = getAttribute(tblLook, 'w', 'firstColumn');\n if (firstColumn) look.firstColumn = firstColumn === '1';\n\n const firstRow = getAttribute(tblLook, 'w', 'firstRow');\n if (firstRow) look.firstRow = firstRow === '1';\n\n const lastColumn = getAttribute(tblLook, 'w', 'lastColumn');\n if (lastColumn) look.lastColumn = lastColumn === '1';\n\n const lastRow = getAttribute(tblLook, 'w', 'lastRow');\n if (lastRow) look.lastRow = lastRow === '1';\n\n const noHBand = getAttribute(tblLook, 'w', 'noHBand');\n if (noHBand) look.noHBand = noHBand === '1';\n\n const noVBand = getAttribute(tblLook, 'w', 'noVBand');\n if (noVBand) look.noVBand = noVBand === '1';\n\n return Object.keys(look).length > 0 ? look : undefined;\n}\n\n/**\n * Parse table formatting properties (w:tblPr)\n */\nfunction parseTableProperties(\n tblPr: XmlElement | null,\n _theme: Theme | null\n): TableFormatting | undefined {\n if (!tblPr) return undefined;\n\n const formatting: TableFormatting = {};\n\n // Table width\n const tblW = findChild(tblPr, 'w', 'tblW');\n if (tblW) {\n formatting.width = parseTableMeasurement(tblW);\n }\n\n // Table alignment/justification\n const jc = findChild(tblPr, 'w', 'jc');\n if (jc) {\n const val = getAttribute(jc, 'w', 'val');\n if (val === 'left' || val === 'center' || val === 'right') {\n formatting.justification = val;\n }\n }\n\n // Cell spacing\n const tblCellSpacing = findChild(tblPr, 'w', 'tblCellSpacing');\n if (tblCellSpacing) {\n formatting.cellSpacing = parseTableMeasurement(tblCellSpacing);\n }\n\n // Table indent\n const tblInd = findChild(tblPr, 'w', 'tblInd');\n if (tblInd) {\n formatting.indent = parseTableMeasurement(tblInd);\n }\n\n // Table borders\n const tblBorders = findChild(tblPr, 'w', 'tblBorders');\n if (tblBorders) {\n formatting.borders = parseTableBorders(tblBorders);\n }\n\n // Cell margins\n const tblCellMar = findChild(tblPr, 'w', 'tblCellMar');\n if (tblCellMar) {\n formatting.cellMargins = parseCellMargins(tblCellMar);\n }\n\n // Table layout\n const tblLayout = findChild(tblPr, 'w', 'tblLayout');\n if (tblLayout) {\n const val = getAttribute(tblLayout, 'w', 'type');\n if (val === 'fixed' || val === 'autofit') {\n formatting.layout = val;\n }\n }\n\n // Table style\n const tblStyle = findChild(tblPr, 'w', 'tblStyle');\n if (tblStyle) {\n const val = getAttribute(tblStyle, 'w', 'val');\n if (val) formatting.styleId = val;\n }\n\n // Table look\n const tblLook = findChild(tblPr, 'w', 'tblLook');\n if (tblLook) {\n formatting.look = parseTableLook(tblLook);\n }\n\n // Shading\n const shd = findChild(tblPr, 'w', 'shd');\n if (shd) {\n formatting.shading = parseShadingProperties(shd);\n }\n\n // Bidi\n const bidiVisual = findChild(tblPr, 'w', 'bidiVisual');\n if (bidiVisual) formatting.bidi = parseBooleanElement(bidiVisual);\n\n return Object.keys(formatting).length > 0 ? formatting : undefined;\n}\n\n/**\n * Parse table row formatting properties (w:trPr)\n */\nfunction parseTableRowProperties(trPr: XmlElement | null): TableRowFormatting | undefined {\n if (!trPr) return undefined;\n\n const formatting: TableRowFormatting = {};\n\n // Row height\n const trHeight = findChild(trPr, 'w', 'trHeight');\n if (trHeight) {\n formatting.height = parseTableMeasurement(trHeight);\n const hRule = getAttribute(trHeight, 'w', 'hRule');\n if (hRule) {\n formatting.heightRule = hRule as TableRowFormatting['heightRule'];\n }\n }\n\n // Header row\n const tblHeader = findChild(trPr, 'w', 'tblHeader');\n if (tblHeader) formatting.header = parseBooleanElement(tblHeader);\n\n // Can't split\n const cantSplit = findChild(trPr, 'w', 'cantSplit');\n if (cantSplit) formatting.cantSplit = parseBooleanElement(cantSplit);\n\n // Row justification\n const jc = findChild(trPr, 'w', 'jc');\n if (jc) {\n const val = getAttribute(jc, 'w', 'val');\n if (val === 'left' || val === 'center' || val === 'right') {\n formatting.justification = val;\n }\n }\n\n // Hidden\n const hidden = findChild(trPr, 'w', 'hidden');\n if (hidden) formatting.hidden = parseBooleanElement(hidden);\n\n return Object.keys(formatting).length > 0 ? formatting : undefined;\n}\n\n/**\n * Parse table cell formatting properties (w:tcPr)\n */\nfunction parseTableCellProperties(\n tcPr: XmlElement | null,\n _theme: Theme | null\n): TableCellFormatting | undefined {\n if (!tcPr) return undefined;\n\n const formatting: TableCellFormatting = {};\n\n // Cell width\n const tcW = findChild(tcPr, 'w', 'tcW');\n if (tcW) {\n formatting.width = parseTableMeasurement(tcW);\n }\n\n // Cell borders\n const tcBorders = findChild(tcPr, 'w', 'tcBorders');\n if (tcBorders) {\n formatting.borders = parseTableBorders(tcBorders);\n }\n\n // Cell margins\n const tcMar = findChild(tcPr, 'w', 'tcMar');\n if (tcMar) {\n formatting.margins = parseCellMargins(tcMar);\n }\n\n // Shading\n const shd = findChild(tcPr, 'w', 'shd');\n if (shd) {\n formatting.shading = parseShadingProperties(shd);\n }\n\n // Vertical alignment\n const vAlign = findChild(tcPr, 'w', 'vAlign');\n if (vAlign) {\n const val = getAttribute(vAlign, 'w', 'val');\n if (val === 'top' || val === 'center' || val === 'bottom') {\n formatting.verticalAlign = val;\n }\n }\n\n // Text direction\n const textDirection = findChild(tcPr, 'w', 'textDirection');\n if (textDirection) {\n const val = getAttribute(textDirection, 'w', 'val');\n if (val) formatting.textDirection = val as TableCellFormatting['textDirection'];\n }\n\n // Grid span (horizontal merge)\n const gridSpan = findChild(tcPr, 'w', 'gridSpan');\n if (gridSpan) {\n const val = parseNumericAttribute(gridSpan, 'w', 'val');\n if (val !== undefined) formatting.gridSpan = val;\n }\n\n // Vertical merge\n const vMerge = findChild(tcPr, 'w', 'vMerge');\n if (vMerge) {\n const val = getAttribute(vMerge, 'w', 'val');\n formatting.vMerge = val === 'restart' ? 'restart' : 'continue';\n }\n\n // Fit text\n const tcFitText = findChild(tcPr, 'w', 'tcFitText');\n if (tcFitText) formatting.fitText = parseBooleanElement(tcFitText);\n\n // No wrap\n const noWrap = findChild(tcPr, 'w', 'noWrap');\n if (noWrap) formatting.noWrap = parseBooleanElement(noWrap);\n\n // Hide mark\n const hideMark = findChild(tcPr, 'w', 'hideMark');\n if (hideMark) formatting.hideMark = parseBooleanElement(hideMark);\n\n return Object.keys(formatting).length > 0 ? formatting : undefined;\n}\n\n/**\n * Parse a single style element (w:style)\n */\nfunction parseStyle(styleEl: XmlElement, theme: Theme | null): Style {\n const style: Style = {\n styleId: getAttribute(styleEl, 'w', 'styleId') ?? '',\n type: (getAttribute(styleEl, 'w', 'type') as StyleType) ?? 'paragraph',\n };\n\n // Default flag\n const defaultAttr = getAttribute(styleEl, 'w', 'default');\n if (defaultAttr) style.default = defaultAttr === '1' || defaultAttr === 'true';\n\n // Name\n const nameEl = findChild(styleEl, 'w', 'name');\n if (nameEl) {\n style.name = getAttribute(nameEl, 'w', 'val') ?? undefined;\n }\n\n // Based on (inheritance)\n const basedOn = findChild(styleEl, 'w', 'basedOn');\n if (basedOn) {\n style.basedOn = getAttribute(basedOn, 'w', 'val') ?? undefined;\n }\n\n // Next style\n const next = findChild(styleEl, 'w', 'next');\n if (next) {\n style.next = getAttribute(next, 'w', 'val') ?? undefined;\n }\n\n // Linked style\n const link = findChild(styleEl, 'w', 'link');\n if (link) {\n style.link = getAttribute(link, 'w', 'val') ?? undefined;\n }\n\n // UI Priority\n const uiPriority = findChild(styleEl, 'w', 'uiPriority');\n if (uiPriority) {\n const val = parseNumericAttribute(uiPriority, 'w', 'val');\n if (val !== undefined) style.uiPriority = val;\n }\n\n // Hidden/Semi-hidden\n const hidden = findChild(styleEl, 'w', 'hidden');\n if (hidden) style.hidden = parseBooleanElement(hidden);\n\n const semiHidden = findChild(styleEl, 'w', 'semiHidden');\n if (semiHidden) style.semiHidden = parseBooleanElement(semiHidden);\n\n // Unhide when used\n const unhideWhenUsed = findChild(styleEl, 'w', 'unhideWhenUsed');\n if (unhideWhenUsed) style.unhideWhenUsed = parseBooleanElement(unhideWhenUsed);\n\n // Quick format\n const qFormat = findChild(styleEl, 'w', 'qFormat');\n if (qFormat) style.qFormat = parseBooleanElement(qFormat);\n\n // Personal/custom style\n const personal = findChild(styleEl, 'w', 'personal');\n if (personal) style.personal = parseBooleanElement(personal);\n\n // Paragraph properties\n const pPr = findChild(styleEl, 'w', 'pPr');\n if (pPr) {\n style.pPr = parseParagraphProperties(pPr, theme);\n }\n\n // Run properties\n const rPr = findChild(styleEl, 'w', 'rPr');\n if (rPr) {\n style.rPr = parseRunProperties(rPr, theme);\n }\n\n // Table properties (for table styles)\n const tblPr = findChild(styleEl, 'w', 'tblPr');\n if (tblPr) {\n style.tblPr = parseTableProperties(tblPr, theme);\n }\n\n // Table row properties\n const trPr = findChild(styleEl, 'w', 'trPr');\n if (trPr) {\n style.trPr = parseTableRowProperties(trPr);\n }\n\n // Table cell properties\n const tcPr = findChild(styleEl, 'w', 'tcPr');\n if (tcPr) {\n style.tcPr = parseTableCellProperties(tcPr, theme);\n }\n\n // Table style conditional formatting (tblStylePr)\n const tblStylePrs = findChildren(styleEl, 'w', 'tblStylePr');\n if (tblStylePrs.length > 0) {\n style.tblStylePr = [];\n\n for (const tblStylePr of tblStylePrs) {\n const typeAttr = getAttribute(tblStylePr, 'w', 'type');\n if (typeAttr) {\n const conditionalStyle: NonNullable<Style['tblStylePr']>[number] = {\n type: typeAttr as any,\n };\n\n const condPPr = findChild(tblStylePr, 'w', 'pPr');\n if (condPPr) conditionalStyle.pPr = parseParagraphProperties(condPPr, theme);\n\n const condRPr = findChild(tblStylePr, 'w', 'rPr');\n if (condRPr) conditionalStyle.rPr = parseRunProperties(condRPr, theme);\n\n const condTblPr = findChild(tblStylePr, 'w', 'tblPr');\n if (condTblPr) conditionalStyle.tblPr = parseTableProperties(condTblPr, theme);\n\n const condTrPr = findChild(tblStylePr, 'w', 'trPr');\n if (condTrPr) conditionalStyle.trPr = parseTableRowProperties(condTrPr);\n\n const condTcPr = findChild(tblStylePr, 'w', 'tcPr');\n if (condTcPr) conditionalStyle.tcPr = parseTableCellProperties(condTcPr, theme);\n\n style.tblStylePr.push(conditionalStyle);\n }\n }\n }\n\n return style;\n}\n\n/**\n * Parse document defaults (w:docDefaults)\n */\nfunction parseDocDefaults(\n docDefaults: XmlElement | null,\n theme: Theme | null\n): DocDefaults | undefined {\n if (!docDefaults) return undefined;\n\n const result: DocDefaults = {};\n\n // Default run properties\n const rPrDefault = findChild(docDefaults, 'w', 'rPrDefault');\n if (rPrDefault) {\n const rPr = findChild(rPrDefault, 'w', 'rPr');\n if (rPr) {\n result.rPr = parseRunProperties(rPr, theme);\n }\n }\n\n // Default paragraph properties\n const pPrDefault = findChild(docDefaults, 'w', 'pPrDefault');\n if (pPrDefault) {\n const pPr = findChild(pPrDefault, 'w', 'pPr');\n if (pPr) {\n result.pPr = parseParagraphProperties(pPr, theme);\n }\n }\n\n return result.rPr || result.pPr ? result : undefined;\n}\n\n/**\n * Deep merge text formatting (source overrides target)\n */\nfunction mergeTextFormatting(\n target: TextFormatting | undefined,\n source: TextFormatting | undefined\n): TextFormatting | undefined {\n if (!source) return target;\n if (!target) return source ? { ...source } : undefined;\n\n const result = { ...target };\n\n // Copy all defined properties from source\n for (const key of Object.keys(source) as (keyof TextFormatting)[]) {\n const value = source[key];\n if (value !== undefined) {\n (result as any)[key] =\n typeof value === 'object' && value !== null\n ? { ...((result[key] as any) || {}), ...value }\n : value;\n }\n }\n\n return result;\n}\n\n/**\n * Deep merge paragraph formatting (source overrides target)\n */\nfunction mergeParagraphFormatting(\n target: ParagraphFormatting | undefined,\n source: ParagraphFormatting | undefined\n): ParagraphFormatting | undefined {\n if (!source) return target;\n if (!target) return source ? { ...source } : undefined;\n\n const result = { ...target };\n\n for (const key of Object.keys(source) as (keyof ParagraphFormatting)[]) {\n const value = source[key];\n if (value !== undefined) {\n if (key === 'runProperties') {\n result.runProperties = mergeTextFormatting(result.runProperties, source.runProperties);\n } else if (key === 'borders' || key === 'numPr' || key === 'frame') {\n const baseValue = result[key] as Record<string, unknown> | undefined;\n const sourceValue = value as Record<string, unknown> | undefined;\n (result as Record<string, unknown>)[key] = { ...(baseValue || {}), ...(sourceValue || {}) };\n } else if (key === 'tabs' && Array.isArray(value)) {\n result.tabs = [...value];\n } else {\n (result as any)[key] = value;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Resolve style inheritance chain\n */\nfunction resolveStyleInheritance(\n style: Style,\n styleMap: StyleMap,\n theme: Theme | null,\n visited: Set<string> = new Set()\n): Style {\n // Prevent circular inheritance\n if (visited.has(style.styleId)) {\n return style;\n }\n visited.add(style.styleId);\n\n // If no basedOn, return as-is\n if (!style.basedOn) {\n return style;\n }\n\n // Get parent style\n const parentStyle = styleMap.get(style.basedOn);\n if (!parentStyle) {\n return style;\n }\n\n // Recursively resolve parent\n const resolvedParent = resolveStyleInheritance(parentStyle, styleMap, theme, visited);\n\n // Merge parent into this style (this style overrides parent)\n const resolved: Style = {\n ...style,\n pPr: mergeParagraphFormatting(resolvedParent.pPr, style.pPr),\n rPr: mergeTextFormatting(resolvedParent.rPr, style.rPr),\n };\n\n // Merge table properties if this is a table style\n if (style.type === 'table') {\n if (resolvedParent.tblPr || style.tblPr) {\n resolved.tblPr = { ...(resolvedParent.tblPr || {}), ...(style.tblPr || {}) };\n }\n if (resolvedParent.trPr || style.trPr) {\n resolved.trPr = { ...(resolvedParent.trPr || {}), ...(style.trPr || {}) };\n }\n if (resolvedParent.tcPr || style.tcPr) {\n resolved.tcPr = { ...(resolvedParent.tcPr || {}), ...(style.tcPr || {}) };\n }\n }\n\n return resolved;\n}\n\n/**\n * Parse styles.xml content\n *\n * @param stylesXml - XML content of styles.xml\n * @param theme - Parsed theme for resolving theme references\n * @returns StyleMap with resolved inheritance\n */\nexport function parseStyles(stylesXml: string, theme: Theme | null): StyleMap {\n const styleMap: StyleMap = new Map();\n\n try {\n const doc = parseXmlDocument(stylesXml);\n if (!doc) {\n return styleMap;\n }\n\n // First pass: parse all styles without inheritance resolution\n const styleElements = findChildren(doc, 'w', 'style');\n for (const styleEl of styleElements) {\n const style = parseStyle(styleEl, theme);\n if (style.styleId) {\n styleMap.set(style.styleId, style);\n }\n }\n\n // Second pass: resolve inheritance\n for (const [styleId, style] of styleMap) {\n const resolved = resolveStyleInheritance(style, styleMap, theme);\n styleMap.set(styleId, resolved);\n }\n } catch (error) {\n console.warn('Failed to parse styles:', error);\n }\n\n return styleMap;\n}\n\n/**\n * Parse complete style definitions including docDefaults\n *\n * @param stylesXml - XML content of styles.xml\n * @param theme - Parsed theme for resolving theme references\n * @returns StyleDefinitions with docDefaults and resolved styles\n */\nexport function parseStyleDefinitions(stylesXml: string, theme: Theme | null): StyleDefinitions {\n const result: StyleDefinitions = {\n styles: [],\n };\n\n try {\n const doc = parseXmlDocument(stylesXml);\n if (!doc) {\n return result;\n }\n\n // Parse document defaults\n const docDefaultsEl = findChild(doc, 'w', 'docDefaults');\n result.docDefaults = parseDocDefaults(docDefaultsEl, theme);\n\n // Parse latent styles\n const latentStylesEl = findChild(doc, 'w', 'latentStyles');\n if (latentStylesEl) {\n result.latentStyles = {\n defLockedState: getAttribute(latentStylesEl, 'w', 'defLockedState') === '1',\n defUIPriority: parseNumericAttribute(latentStylesEl, 'w', 'defUIPriority'),\n defSemiHidden: getAttribute(latentStylesEl, 'w', 'defSemiHidden') === '1',\n defUnhideWhenUsed: getAttribute(latentStylesEl, 'w', 'defUnhideWhenUsed') === '1',\n defQFormat: getAttribute(latentStylesEl, 'w', 'defQFormat') === '1',\n count: parseNumericAttribute(latentStylesEl, 'w', 'count'),\n };\n }\n\n // Parse styles with full inheritance resolution\n const styleMap = parseStyles(stylesXml, theme);\n result.styles = Array.from(styleMap.values());\n } catch (error) {\n console.warn('Failed to parse style definitions:', error);\n }\n\n return result;\n}\n\n/**\n * Get the resolved properties for a style\n *\n * @param styleId - Style ID to look up\n * @param styleMap - Style map from parseStyles\n * @returns Resolved style or undefined\n */\nexport function getResolvedStyle(styleId: string, styleMap: StyleMap): Style | undefined {\n return styleMap.get(styleId);\n}\n\n/**\n * Get the default paragraph style\n */\nexport function getDefaultParagraphStyle(styleMap: StyleMap): Style | undefined {\n for (const style of styleMap.values()) {\n if (style.type === 'paragraph' && style.default) {\n return style;\n }\n }\n // Fallback to \"Normal\" style\n return styleMap.get('Normal');\n}\n\n/**\n * Get the default character style\n */\nexport function getDefaultCharacterStyle(styleMap: StyleMap): Style | undefined {\n for (const style of styleMap.values()) {\n if (style.type === 'character' && style.default) {\n return style;\n }\n }\n return undefined;\n}\n\n/**\n * Get all styles of a specific type\n */\nexport function getStylesByType(styleMap: StyleMap, type: StyleType): Style[] {\n const result: Style[] = [];\n for (const style of styleMap.values()) {\n if (style.type === type) {\n result.push(style);\n }\n }\n return result;\n}\n","/**\n * Numbering/List Parser for DOCX\n *\n * Parses numbering.xml to extract:\n * - Abstract numbering definitions (templates with levels)\n * - Numbering instances (concrete references with optional overrides)\n *\n * OOXML Structure:\n * - w:abstractNum - Template definitions with 9 levels (0-8)\n * - w:num - Instances that reference abstractNum and can override levels\n * - w:lvl - Level definition with start, format, text pattern, etc.\n */\n\nimport type {\n NumberingDefinitions,\n AbstractNumbering,\n NumberingInstance,\n ListLevel,\n NumberFormat,\n LevelSuffix,\n ParagraphFormatting,\n TextFormatting,\n} from '../types/document';\n\nimport {\n parseXmlDocument,\n findChild,\n findChildren,\n getAttribute,\n parseBooleanElement,\n parseNumericAttribute,\n type XmlElement,\n} from './xmlParser';\n\n/**\n * Map of rId to numbering definitions\n */\nexport type NumberingMap = {\n definitions: NumberingDefinitions;\n /** Get level info for a numId and ilvl */\n getLevel: (numId: number, ilvl: number) => ListLevel | null;\n /** Get abstract numbering by ID */\n getAbstract: (abstractNumId: number) => AbstractNumbering | null;\n /** Check if numId exists */\n hasNumbering: (numId: number) => boolean;\n};\n\n/**\n * Parse numbering.xml into NumberingDefinitions\n *\n * @param numberingXml - Raw XML string from word/numbering.xml (or null if not present)\n * @returns NumberingMap with definitions and helper functions\n */\nexport function parseNumbering(numberingXml: string | null): NumberingMap {\n const definitions: NumberingDefinitions = {\n abstractNums: [],\n nums: [],\n };\n\n if (!numberingXml) {\n return createNumberingMap(definitions);\n }\n\n const root = parseXmlDocument(numberingXml);\n if (!root) {\n return createNumberingMap(definitions);\n }\n\n // Parse abstract numbering definitions\n const abstractNumElements = findChildren(root, 'w', 'abstractNum');\n for (const abstractNum of abstractNumElements) {\n const parsed = parseAbstractNumbering(abstractNum);\n if (parsed) {\n definitions.abstractNums.push(parsed);\n }\n }\n\n // Parse numbering instances\n const numElements = findChildren(root, 'w', 'num');\n for (const num of numElements) {\n const parsed = parseNumberingInstance(num);\n if (parsed) {\n definitions.nums.push(parsed);\n }\n }\n\n return createNumberingMap(definitions);\n}\n\n/**\n * Parse a single w:abstractNum element\n */\nfunction parseAbstractNumbering(element: XmlElement): AbstractNumbering | null {\n const abstractNumIdStr = getAttribute(element, 'w', 'abstractNumId');\n if (abstractNumIdStr === null) return null;\n\n const abstractNumId = parseInt(abstractNumIdStr, 10);\n if (isNaN(abstractNumId)) return null;\n\n const abstractNum: AbstractNumbering = {\n abstractNumId,\n levels: [],\n };\n\n // Parse optional attributes/children\n const multiLevelTypeEl = findChild(element, 'w', 'multiLevelType');\n if (multiLevelTypeEl) {\n const mlType = getAttribute(multiLevelTypeEl, 'w', 'val');\n if (mlType === 'hybridMultilevel' || mlType === 'multilevel' || mlType === 'singleLevel') {\n abstractNum.multiLevelType = mlType;\n }\n }\n\n // Parse name\n const nameEl = findChild(element, 'w', 'name');\n if (nameEl) {\n abstractNum.name = getAttribute(nameEl, 'w', 'val') ?? undefined;\n }\n\n // Parse style links\n const numStyleLinkEl = findChild(element, 'w', 'numStyleLink');\n if (numStyleLinkEl) {\n abstractNum.numStyleLink = getAttribute(numStyleLinkEl, 'w', 'val') ?? undefined;\n }\n\n const styleLinkEl = findChild(element, 'w', 'styleLink');\n if (styleLinkEl) {\n abstractNum.styleLink = getAttribute(styleLinkEl, 'w', 'val') ?? undefined;\n }\n\n // Parse levels (w:lvl)\n const levelElements = findChildren(element, 'w', 'lvl');\n for (const lvlEl of levelElements) {\n const level = parseListLevel(lvlEl);\n if (level) {\n abstractNum.levels.push(level);\n }\n }\n\n // Sort levels by ilvl\n abstractNum.levels.sort((a, b) => a.ilvl - b.ilvl);\n\n return abstractNum;\n}\n\n/**\n * Parse a single w:num element (numbering instance)\n */\nfunction parseNumberingInstance(element: XmlElement): NumberingInstance | null {\n const numIdStr = getAttribute(element, 'w', 'numId');\n if (numIdStr === null) return null;\n\n const numId = parseInt(numIdStr, 10);\n if (isNaN(numId)) return null;\n\n // Get abstract numbering reference\n const abstractNumIdEl = findChild(element, 'w', 'abstractNumId');\n if (!abstractNumIdEl) return null;\n\n const abstractNumIdStr = getAttribute(abstractNumIdEl, 'w', 'val');\n if (abstractNumIdStr === null) return null;\n\n const abstractNumId = parseInt(abstractNumIdStr, 10);\n if (isNaN(abstractNumId)) return null;\n\n const instance: NumberingInstance = {\n numId,\n abstractNumId,\n };\n\n // Parse level overrides (w:lvlOverride)\n const overrideElements = findChildren(element, 'w', 'lvlOverride');\n if (overrideElements.length > 0) {\n instance.levelOverrides = [];\n\n for (const overrideEl of overrideElements) {\n const ilvlStr = getAttribute(overrideEl, 'w', 'ilvl');\n if (ilvlStr === null) continue;\n\n const ilvl = parseInt(ilvlStr, 10);\n if (isNaN(ilvl)) continue;\n\n const override: {\n ilvl: number;\n startOverride?: number;\n lvl?: ListLevel;\n } = { ilvl };\n\n // Check for start override\n const startOverrideEl = findChild(overrideEl, 'w', 'startOverride');\n if (startOverrideEl) {\n const startVal = getAttribute(startOverrideEl, 'w', 'val');\n if (startVal !== null) {\n const startNum = parseInt(startVal, 10);\n if (!isNaN(startNum)) {\n override.startOverride = startNum;\n }\n }\n }\n\n // Check for full level redefinition\n const lvlEl = findChild(overrideEl, 'w', 'lvl');\n if (lvlEl) {\n override.lvl = parseListLevel(lvlEl) ?? undefined;\n }\n\n instance.levelOverrides.push(override);\n }\n }\n\n return instance;\n}\n\n/**\n * Parse a single w:lvl element (list level definition)\n */\nfunction parseListLevel(element: XmlElement): ListLevel | null {\n const ilvlStr = getAttribute(element, 'w', 'ilvl');\n if (ilvlStr === null) return null;\n\n const ilvl = parseInt(ilvlStr, 10);\n if (isNaN(ilvl) || ilvl < 0 || ilvl > 8) return null;\n\n const level: ListLevel = {\n ilvl,\n numFmt: 'decimal', // Default\n lvlText: '',\n };\n\n // Parse start value\n const startEl = findChild(element, 'w', 'start');\n if (startEl) {\n const startVal = getAttribute(startEl, 'w', 'val');\n if (startVal !== null) {\n const startNum = parseInt(startVal, 10);\n if (!isNaN(startNum)) {\n level.start = startNum;\n }\n }\n }\n\n // Parse number format\n const numFmtEl = findChild(element, 'w', 'numFmt');\n if (numFmtEl) {\n const fmtVal = getAttribute(numFmtEl, 'w', 'val');\n if (fmtVal) {\n level.numFmt = parseNumberFormat(fmtVal);\n }\n }\n\n // Parse level text (the pattern like \"%1.\" or \"•\")\n const lvlTextEl = findChild(element, 'w', 'lvlText');\n if (lvlTextEl) {\n level.lvlText = getAttribute(lvlTextEl, 'w', 'val') ?? '';\n }\n\n // Parse justification\n const lvlJcEl = findChild(element, 'w', 'lvlJc');\n if (lvlJcEl) {\n const jcVal = getAttribute(lvlJcEl, 'w', 'val');\n if (jcVal === 'left' || jcVal === 'center' || jcVal === 'right') {\n level.lvlJc = jcVal;\n }\n }\n\n // Parse suffix\n const suffEl = findChild(element, 'w', 'suff');\n if (suffEl) {\n const suffVal = getAttribute(suffEl, 'w', 'val');\n if (suffVal === 'tab' || suffVal === 'space' || suffVal === 'nothing') {\n level.suffix = suffVal as LevelSuffix;\n }\n }\n\n // Parse isLgl (legal numbering)\n const isLglEl = findChild(element, 'w', 'isLgl');\n if (isLglEl) {\n level.isLgl = parseBooleanElement(isLglEl);\n }\n\n // Parse lvlRestart (restart numbering from a higher level)\n const lvlRestartEl = findChild(element, 'w', 'lvlRestart');\n if (lvlRestartEl) {\n const restartVal = getAttribute(lvlRestartEl, 'w', 'val');\n if (restartVal !== null) {\n const restartNum = parseInt(restartVal, 10);\n if (!isNaN(restartNum)) {\n level.lvlRestart = restartNum;\n }\n }\n }\n\n // Parse legacy settings\n const legacyEl = findChild(element, 'w', 'legacy');\n if (legacyEl) {\n level.legacy = {\n legacy: parseBooleanElement(legacyEl),\n legacySpace: parseNumericAttribute(legacyEl, 'w', 'legacySpace'),\n legacyIndent: parseNumericAttribute(legacyEl, 'w', 'legacyIndent'),\n };\n }\n\n // Parse paragraph properties (w:pPr)\n const pPrEl = findChild(element, 'w', 'pPr');\n if (pPrEl) {\n level.pPr = parseLevelParagraphProps(pPrEl);\n }\n\n // Parse run properties (w:rPr)\n const rPrEl = findChild(element, 'w', 'rPr');\n if (rPrEl) {\n level.rPr = parseLevelRunProps(rPrEl);\n }\n\n return level;\n}\n\n/**\n * Parse number format string to NumberFormat type\n */\nfunction parseNumberFormat(format: string): NumberFormat {\n // Map of known formats\n const formatMap: Record<string, NumberFormat> = {\n decimal: 'decimal',\n upperRoman: 'upperRoman',\n lowerRoman: 'lowerRoman',\n upperLetter: 'upperLetter',\n lowerLetter: 'lowerLetter',\n ordinal: 'ordinal',\n cardinalText: 'cardinalText',\n ordinalText: 'ordinalText',\n hex: 'hex',\n chicago: 'chicago',\n bullet: 'bullet',\n none: 'none',\n decimalZero: 'decimalZero',\n ganada: 'ganada',\n chosung: 'chosung',\n // CJK formats\n ideographDigital: 'ideographDigital',\n japaneseCounting: 'japaneseCounting',\n aiueo: 'aiueo',\n iroha: 'iroha',\n decimalFullWidth: 'decimalFullWidth',\n decimalHalfWidth: 'decimalHalfWidth',\n japaneseLegal: 'japaneseLegal',\n japaneseDigitalTenThousand: 'japaneseDigitalTenThousand',\n decimalEnclosedCircle: 'decimalEnclosedCircle',\n decimalFullWidth2: 'decimalFullWidth2',\n aiueoFullWidth: 'aiueoFullWidth',\n irohaFullWidth: 'irohaFullWidth',\n decimalEnclosedFullstop: 'decimalEnclosedFullstop',\n decimalEnclosedParen: 'decimalEnclosedParen',\n decimalEnclosedCircleChinese: 'decimalEnclosedCircleChinese',\n ideographEnclosedCircle: 'ideographEnclosedCircle',\n ideographTraditional: 'ideographTraditional',\n ideographZodiac: 'ideographZodiac',\n ideographZodiacTraditional: 'ideographZodiacTraditional',\n taiwaneseCounting: 'taiwaneseCounting',\n ideographLegalTraditional: 'ideographLegalTraditional',\n taiwaneseCountingThousand: 'taiwaneseCountingThousand',\n taiwaneseDigital: 'taiwaneseDigital',\n chineseCounting: 'chineseCounting',\n chineseLegalSimplified: 'chineseLegalSimplified',\n chineseCountingThousand: 'chineseCountingThousand',\n koreanDigital: 'koreanDigital',\n koreanCounting: 'koreanCounting',\n koreanLegal: 'koreanLegal',\n koreanDigital2: 'koreanDigital2',\n vietnameseCounting: 'vietnameseCounting',\n russianLower: 'russianLower',\n russianUpper: 'russianUpper',\n numberInDash: 'numberInDash',\n hebrew1: 'hebrew1',\n hebrew2: 'hebrew2',\n arabicAlpha: 'arabicAlpha',\n arabicAbjad: 'arabicAbjad',\n hindiVowels: 'hindiVowels',\n hindiConsonants: 'hindiConsonants',\n hindiNumbers: 'hindiNumbers',\n hindiCounting: 'hindiCounting',\n thaiLetters: 'thaiLetters',\n thaiNumbers: 'thaiNumbers',\n thaiCounting: 'thaiCounting',\n };\n\n return formatMap[format] ?? 'decimal';\n}\n\n/**\n * Parse paragraph properties for a list level (subset of full pPr)\n * Main concern: indentation and tabs\n */\nfunction parseLevelParagraphProps(pPr: XmlElement): ParagraphFormatting {\n const formatting: ParagraphFormatting = {};\n\n // Parse indentation (w:ind)\n const indEl = findChild(pPr, 'w', 'ind');\n if (indEl) {\n const left = parseNumericAttribute(indEl, 'w', 'left');\n const right = parseNumericAttribute(indEl, 'w', 'right');\n const firstLine = parseNumericAttribute(indEl, 'w', 'firstLine');\n const hanging = parseNumericAttribute(indEl, 'w', 'hanging');\n\n if (left !== undefined) formatting.indentLeft = left;\n if (right !== undefined) formatting.indentRight = right;\n\n if (hanging !== undefined) {\n formatting.indentFirstLine = -hanging;\n formatting.hangingIndent = true;\n } else if (firstLine !== undefined) {\n formatting.indentFirstLine = firstLine;\n }\n }\n\n // Parse tabs (w:tabs)\n const tabsEl = findChild(pPr, 'w', 'tabs');\n if (tabsEl) {\n formatting.tabs = [];\n const tabElements = findChildren(tabsEl, 'w', 'tab');\n for (const tabEl of tabElements) {\n const pos = parseNumericAttribute(tabEl, 'w', 'pos');\n const val = getAttribute(tabEl, 'w', 'val');\n const leader = getAttribute(tabEl, 'w', 'leader');\n\n if (pos !== undefined && val) {\n formatting.tabs.push({\n position: pos,\n alignment: parseTabAlignment(val),\n leader: parseTabLeader(leader),\n });\n }\n }\n }\n\n return formatting;\n}\n\n/**\n * Parse tab alignment value\n */\nfunction parseTabAlignment(\n val: string\n): 'left' | 'center' | 'right' | 'decimal' | 'bar' | 'clear' | 'num' {\n switch (val) {\n case 'left':\n return 'left';\n case 'center':\n return 'center';\n case 'right':\n return 'right';\n case 'decimal':\n return 'decimal';\n case 'bar':\n return 'bar';\n case 'clear':\n return 'clear';\n case 'num':\n return 'num';\n default:\n return 'left';\n }\n}\n\n/**\n * Parse tab leader value\n */\nfunction parseTabLeader(\n val: string | null\n): 'none' | 'dot' | 'hyphen' | 'underscore' | 'heavy' | 'middleDot' | undefined {\n if (!val) return undefined;\n switch (val) {\n case 'none':\n return 'none';\n case 'dot':\n return 'dot';\n case 'hyphen':\n return 'hyphen';\n case 'underscore':\n return 'underscore';\n case 'heavy':\n return 'heavy';\n case 'middleDot':\n return 'middleDot';\n default:\n return undefined;\n }\n}\n\n/**\n * Parse run properties for a list level (subset of full rPr)\n * Main concern: fonts for bullet characters\n */\nfunction parseLevelRunProps(rPr: XmlElement): TextFormatting {\n const formatting: TextFormatting = {};\n\n // Parse fonts (w:rFonts) - important for bullet characters\n const rFontsEl = findChild(rPr, 'w', 'rFonts');\n if (rFontsEl) {\n formatting.fontFamily = {\n ascii: getAttribute(rFontsEl, 'w', 'ascii') ?? undefined,\n hAnsi: getAttribute(rFontsEl, 'w', 'hAnsi') ?? undefined,\n eastAsia: getAttribute(rFontsEl, 'w', 'eastAsia') ?? undefined,\n cs: getAttribute(rFontsEl, 'w', 'cs') ?? undefined,\n };\n }\n\n // Parse font size (w:sz)\n const szEl = findChild(rPr, 'w', 'sz');\n if (szEl) {\n const size = parseNumericAttribute(szEl, 'w', 'val');\n if (size !== undefined) {\n formatting.fontSize = size; // In half-points\n }\n }\n\n // Parse color (w:color)\n const colorEl = findChild(rPr, 'w', 'color');\n if (colorEl) {\n const val = getAttribute(colorEl, 'w', 'val');\n const themeColor = getAttribute(colorEl, 'w', 'themeColor');\n\n if (val === 'auto') {\n formatting.color = { auto: true };\n } else if (themeColor) {\n formatting.color = {\n themeColor: themeColor as any,\n themeTint: getAttribute(colorEl, 'w', 'themeTint') ?? undefined,\n themeShade: getAttribute(colorEl, 'w', 'themeShade') ?? undefined,\n };\n } else if (val) {\n formatting.color = { rgb: val };\n }\n }\n\n // Parse bold (w:b)\n const bEl = findChild(rPr, 'w', 'b');\n if (bEl) {\n formatting.bold = parseBooleanElement(bEl);\n }\n\n // Parse italic (w:i)\n const iEl = findChild(rPr, 'w', 'i');\n if (iEl) {\n formatting.italic = parseBooleanElement(iEl);\n }\n\n return formatting;\n}\n\n/**\n * Create a NumberingMap with helper functions\n */\nfunction createNumberingMap(definitions: NumberingDefinitions): NumberingMap {\n // Build lookup maps for efficient access\n const abstractMap = new Map<number, AbstractNumbering>();\n for (const abs of definitions.abstractNums) {\n abstractMap.set(abs.abstractNumId, abs);\n }\n\n const numMap = new Map<number, NumberingInstance>();\n for (const num of definitions.nums) {\n numMap.set(num.numId, num);\n }\n\n return {\n definitions,\n\n getLevel(numId: number, ilvl: number): ListLevel | null {\n const num = numMap.get(numId);\n if (!num) return null;\n\n // Check for level override first\n if (num.levelOverrides) {\n const override = num.levelOverrides.find((o) => o.ilvl === ilvl);\n if (override) {\n if (override.lvl) {\n // Full level redefinition\n return override.lvl;\n }\n // Start override - need to get base level and modify\n const abstractNum = abstractMap.get(num.abstractNumId);\n if (abstractNum) {\n const baseLevel = abstractNum.levels.find((l) => l.ilvl === ilvl);\n if (baseLevel && override.startOverride !== undefined) {\n return {\n ...baseLevel,\n start: override.startOverride,\n };\n }\n }\n }\n }\n\n // Get from abstract numbering\n const abstractNum = abstractMap.get(num.abstractNumId);\n if (!abstractNum) return null;\n\n return abstractNum.levels.find((l) => l.ilvl === ilvl) ?? null;\n },\n\n getAbstract(abstractNumId: number): AbstractNumbering | null {\n return abstractMap.get(abstractNumId) ?? null;\n },\n\n hasNumbering(numId: number): boolean {\n return numMap.has(numId);\n },\n };\n}\n\n/**\n * Format a number according to the specified format\n *\n * @param num - The number to format\n * @param format - The number format\n * @returns Formatted string\n */\nexport function formatNumber(num: number, format: NumberFormat): string {\n switch (format) {\n case 'decimal':\n case 'decimalZero':\n return num.toString();\n\n case 'upperRoman':\n return toRoman(num).toUpperCase();\n\n case 'lowerRoman':\n return toRoman(num).toLowerCase();\n\n case 'upperLetter':\n return toLetter(num).toUpperCase();\n\n case 'lowerLetter':\n return toLetter(num).toLowerCase();\n\n case 'ordinal':\n return toOrdinal(num);\n\n case 'bullet':\n return '•'; // Default bullet\n\n case 'none':\n return '';\n\n case 'decimalEnclosedParen':\n return `(${num})`;\n\n case 'numberInDash':\n return `-${num}-`;\n\n default:\n // For CJK and other special formats, fall back to decimal\n return num.toString();\n }\n}\n\n/**\n * Convert number to Roman numerals\n */\nfunction toRoman(num: number): string {\n if (num <= 0 || num > 3999) return num.toString();\n\n const romanNumerals: [number, string][] = [\n [1000, 'm'],\n [900, 'cm'],\n [500, 'd'],\n [400, 'cd'],\n [100, 'c'],\n [90, 'xc'],\n [50, 'l'],\n [40, 'xl'],\n [10, 'x'],\n [9, 'ix'],\n [5, 'v'],\n [4, 'iv'],\n [1, 'i'],\n ];\n\n let result = '';\n let remaining = num;\n\n for (const [value, numeral] of romanNumerals) {\n while (remaining >= value) {\n result += numeral;\n remaining -= value;\n }\n }\n\n return result;\n}\n\n/**\n * Convert number to letter (a, b, c, ... z, aa, ab, ...)\n */\nfunction toLetter(num: number): string {\n if (num <= 0) return '';\n\n let result = '';\n let remaining = num;\n\n while (remaining > 0) {\n remaining--;\n result = String.fromCharCode(97 + (remaining % 26)) + result;\n remaining = Math.floor(remaining / 26);\n }\n\n return result;\n}\n\n/**\n * Convert number to ordinal (1st, 2nd, 3rd, ...)\n */\nfunction toOrdinal(num: number): string {\n const suffix = ['th', 'st', 'nd', 'rd'];\n const v = num % 100;\n return num + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);\n}\n\n/**\n * Render list marker text by replacing placeholders with formatted numbers\n *\n * @param lvlText - The level text pattern (e.g., \"%1.\", \"%1.%2\")\n * @param counters - Array of counter values for each level (index 0 = level 0, etc.)\n * @param formats - Array of number formats for each level\n * @returns Rendered marker text\n */\nexport function renderListMarker(\n lvlText: string,\n counters: number[],\n formats: NumberFormat[]\n): string {\n let result = lvlText;\n\n // Replace %1 through %9 with formatted counter values\n for (let i = 1; i <= 9; i++) {\n const placeholder = `%${i}`;\n if (result.includes(placeholder)) {\n const counterIndex = i - 1;\n const counter = counters[counterIndex] ?? 1;\n const format = formats[counterIndex] ?? 'decimal';\n const formatted = formatNumber(counter, format);\n result = result.replace(placeholder, formatted);\n }\n }\n\n return result;\n}\n\n/**\n * Get the bullet character for a bullet list level\n *\n * @param level - The list level definition\n * @returns The bullet character to display\n */\nexport function getBulletCharacter(level: ListLevel): string {\n // If lvlText is set and not empty, use it\n if (level.lvlText) {\n return level.lvlText;\n }\n\n // Check font for common bullet font mappings\n const fontFamily = level.rPr?.fontFamily?.ascii || level.rPr?.fontFamily?.hAnsi;\n\n if (fontFamily) {\n const fontLower = fontFamily.toLowerCase();\n\n // Symbol font common bullets\n if (fontLower === 'symbol') {\n return '•'; // Standard bullet\n }\n\n // Wingdings common bullets\n if (fontLower.includes('wingding')) {\n return '❑'; // Square bullet\n }\n }\n\n // Default bullet\n return '•';\n}\n\n/**\n * Check if a list level is a bullet (not numbered)\n */\nexport function isBulletLevel(level: ListLevel): boolean {\n return level.numFmt === 'bullet' || level.numFmt === 'none';\n}\n","/**\n * Image Parser - Parse embedded images from w:drawing elements\n *\n * DOCX images are contained in <w:drawing> elements with either:\n * - wp:inline - Inline images that flow with text\n * - wp:anchor - Floating/anchored images with text wrapping\n *\n * OOXML Structure:\n * w:drawing\n * ├── wp:inline or wp:anchor\n * │ ├── wp:extent (size: cx, cy in EMUs)\n * │ ├── wp:effectExtent (effect margins)\n * │ ├── wp:docPr (document properties: id, name, descr, title)\n * │ ├── wp:positionH / wp:positionV (for anchor only)\n * │ ├── wp:wrap* (wrapping mode for anchor: wrapNone, wrapSquare, etc.)\n * │ └── a:graphic\n * │ └── a:graphicData\n * │ └── pic:pic\n * │ ├── pic:nvPicPr (non-visual properties)\n * │ ├── pic:blipFill\n * │ │ └── a:blip (r:embed = rId)\n * │ └── pic:spPr\n * │ └── a:xfrm (transform: rotation, flip)\n *\n * EMU (English Metric Units): 914400 EMU = 1 inch\n * Conversion: pixels = (emu * 96) / 914400\n */\n\nimport type {\n Image,\n ImageSize,\n ImageWrap,\n ImagePosition,\n ImageTransform,\n ImagePadding,\n RelationshipMap,\n MediaFile,\n} from '../types/document';\nimport {\n getChildElements,\n getAttribute,\n parseNumericAttribute,\n type XmlElement,\n} from './xmlParser';\n\n// ============================================================================\n// EMU CONVERSIONS\n// ============================================================================\n\n/** EMUs per inch */\nconst EMU_PER_INCH = 914400;\n\n/** CSS pixels per inch (standard) */\nconst PIXELS_PER_INCH = 96;\n\n/**\n * Convert EMU to pixels\n *\n * @param emu - Value in EMUs\n * @returns Value in CSS pixels\n */\nexport function emuToPixels(emu: number | undefined | null): number {\n if (emu == null || isNaN(emu)) return 0;\n return Math.round((emu * PIXELS_PER_INCH) / EMU_PER_INCH);\n}\n\n/**\n * Convert pixels to EMU\n *\n * @param px - Value in pixels\n * @returns Value in EMUs\n */\nexport function pixelsToEmu(px: number): number {\n return Math.round((px * EMU_PER_INCH) / PIXELS_PER_INCH);\n}\n\n/**\n * Convert rotation value (1/60000 of a degree) to degrees\n *\n * @param rot - Rotation in 60000ths of a degree\n * @returns Rotation in degrees\n */\nfunction rotToDegrees(rot: string | null | undefined): number | undefined {\n if (!rot) return undefined;\n const val = parseInt(rot, 10);\n if (isNaN(val)) return undefined;\n return val / 60000;\n}\n\n// ============================================================================\n// ELEMENT FINDERS\n// ============================================================================\n\n/**\n * Find element by full name with namespace prefix\n */\nfunction findByFullName(parent: XmlElement, fullName: string): XmlElement | null {\n const children = getChildElements(parent);\n for (const child of children) {\n if (child.name === fullName) {\n return child;\n }\n }\n return null;\n}\n\n/**\n * Find any of the specified elements\n */\nfunction findAnyOf(parent: XmlElement, names: string[]): XmlElement | null {\n const children = getChildElements(parent);\n for (const child of children) {\n if (names.includes(child.name || '')) {\n return child;\n }\n }\n return null;\n}\n\n// ============================================================================\n// SIZE PARSING\n// ============================================================================\n\n/**\n * Parse extent element for image size\n *\n * @param extent - wp:extent element\n * @returns ImageSize in EMUs\n */\nfunction parseExtent(extent: XmlElement | null): ImageSize {\n if (!extent) {\n return { width: 0, height: 0 };\n }\n\n const cx = parseNumericAttribute(extent, null, 'cx') ?? 0;\n const cy = parseNumericAttribute(extent, null, 'cy') ?? 0;\n\n return { width: cx, height: cy };\n}\n\n/**\n * Parse effect extent for shadow/effect margins\n *\n * @param effectExtent - wp:effectExtent element\n * @returns Padding for effects\n */\nfunction parseEffectExtent(effectExtent: XmlElement | null): ImagePadding | undefined {\n if (!effectExtent) return undefined;\n\n const l = parseNumericAttribute(effectExtent, null, 'l') ?? 0;\n const t = parseNumericAttribute(effectExtent, null, 't') ?? 0;\n const r = parseNumericAttribute(effectExtent, null, 'r') ?? 0;\n const b = parseNumericAttribute(effectExtent, null, 'b') ?? 0;\n\n if (l === 0 && t === 0 && r === 0 && b === 0) {\n return undefined;\n }\n\n return {\n left: l,\n top: t,\n right: r,\n bottom: b,\n };\n}\n\n// ============================================================================\n// DOCUMENT PROPERTIES PARSING\n// ============================================================================\n\n/**\n * Parse document properties (wp:docPr)\n *\n * @param docPr - wp:docPr element\n * @returns Object with id, name, description, title\n */\nfunction parseDocProps(docPr: XmlElement | null): {\n id?: string;\n name?: string;\n alt?: string;\n title?: string;\n decorative?: boolean;\n} {\n if (!docPr) return {};\n\n const id = getAttribute(docPr, null, 'id') ?? undefined;\n const name = getAttribute(docPr, null, 'name') ?? undefined;\n const descr = getAttribute(docPr, null, 'descr') ?? undefined;\n const title = getAttribute(docPr, null, 'title') ?? undefined;\n\n // Check for decorative flag (accessibility)\n // In newer OOXML, this is indicated by a:decorative element or attribute\n const decorative = getAttribute(docPr, null, 'decorative') === '1';\n\n return {\n id,\n name,\n alt: descr,\n title,\n decorative: decorative || undefined,\n };\n}\n\n// ============================================================================\n// POSITION PARSING (for anchored images)\n// ============================================================================\n\n/**\n * Parse horizontal position (wp:positionH)\n */\nfunction parsePositionH(posH: XmlElement | null): ImagePosition['horizontal'] | undefined {\n if (!posH) return undefined;\n\n const relativeTo = getAttribute(posH, null, 'relativeFrom') ?? 'column';\n\n // Check for alignment child\n const alignEl = findByFullName(posH, 'wp:align');\n if (alignEl) {\n const alignment = alignEl.elements?.[0]?.text ?? null;\n return {\n relativeTo: relativeTo as ImagePosition['horizontal']['relativeTo'],\n alignment: alignment as ImagePosition['horizontal']['alignment'],\n };\n }\n\n // Check for posOffset child\n const posOffsetEl = findByFullName(posH, 'wp:posOffset');\n if (posOffsetEl) {\n const offsetText = String(posOffsetEl.elements?.[0]?.text ?? '0');\n const posOffset = parseInt(offsetText, 10);\n return {\n relativeTo: relativeTo as ImagePosition['horizontal']['relativeTo'],\n posOffset: isNaN(posOffset) ? 0 : posOffset,\n };\n }\n\n return {\n relativeTo: relativeTo as ImagePosition['horizontal']['relativeTo'],\n };\n}\n\n/**\n * Parse vertical position (wp:positionV)\n */\nfunction parsePositionV(posV: XmlElement | null): ImagePosition['vertical'] | undefined {\n if (!posV) return undefined;\n\n const relativeTo = getAttribute(posV, null, 'relativeFrom') ?? 'paragraph';\n\n // Check for alignment child\n const alignEl = findByFullName(posV, 'wp:align');\n if (alignEl) {\n const alignment = alignEl.elements?.[0]?.text ?? null;\n return {\n relativeTo: relativeTo as ImagePosition['vertical']['relativeTo'],\n alignment: alignment as ImagePosition['vertical']['alignment'],\n };\n }\n\n // Check for posOffset child\n const posOffsetEl = findByFullName(posV, 'wp:posOffset');\n if (posOffsetEl) {\n const offsetText = String(posOffsetEl.elements?.[0]?.text ?? '0');\n const posOffset = parseInt(offsetText, 10);\n return {\n relativeTo: relativeTo as ImagePosition['vertical']['relativeTo'],\n posOffset: isNaN(posOffset) ? 0 : posOffset,\n };\n }\n\n return {\n relativeTo: relativeTo as ImagePosition['vertical']['relativeTo'],\n };\n}\n\n// ============================================================================\n// WRAP PARSING (for anchored images)\n// ============================================================================\n\n/**\n * Wrap element names\n */\nconst WRAP_ELEMENTS = [\n 'wp:wrapNone',\n 'wp:wrapSquare',\n 'wp:wrapTight',\n 'wp:wrapThrough',\n 'wp:wrapTopAndBottom',\n];\n\n/**\n * Parse wrap element to ImageWrap\n */\nfunction parseWrapElement(wrapEl: XmlElement | null, behindDoc: boolean): ImageWrap {\n if (!wrapEl) {\n // No wrap element = wrapNone\n return {\n type: behindDoc ? 'behind' : 'inFront',\n };\n }\n\n const wrapName = wrapEl.name || '';\n const wrapType = wrapName.replace('wp:', '');\n\n // Parse common distance attributes\n const distT = parseNumericAttribute(wrapEl, null, 'distT') ?? undefined;\n const distB = parseNumericAttribute(wrapEl, null, 'distB') ?? undefined;\n const distL = parseNumericAttribute(wrapEl, null, 'distL') ?? undefined;\n const distR = parseNumericAttribute(wrapEl, null, 'distR') ?? undefined;\n\n // Parse wrapText attribute\n const wrapText = getAttribute(wrapEl, null, 'wrapText') ?? undefined;\n\n let type: ImageWrap['type'];\n switch (wrapType) {\n case 'wrapNone':\n type = behindDoc ? 'behind' : 'inFront';\n break;\n case 'wrapSquare':\n type = 'square';\n break;\n case 'wrapTight':\n type = 'tight';\n break;\n case 'wrapThrough':\n type = 'through';\n break;\n case 'wrapTopAndBottom':\n type = 'topAndBottom';\n break;\n default:\n type = 'square'; // Default fallback\n }\n\n const wrap: ImageWrap = { type };\n\n if (wrapText) {\n wrap.wrapText = wrapText as ImageWrap['wrapText'];\n }\n\n if (distT !== undefined) wrap.distT = distT;\n if (distB !== undefined) wrap.distB = distB;\n if (distL !== undefined) wrap.distL = distL;\n if (distR !== undefined) wrap.distR = distR;\n\n return wrap;\n}\n\n// ============================================================================\n// TRANSFORM PARSING\n// ============================================================================\n\n/**\n * Parse transform properties from a:xfrm\n */\nfunction parseTransform(xfrm: XmlElement | null): ImageTransform | undefined {\n if (!xfrm) return undefined;\n\n const rot = getAttribute(xfrm, null, 'rot');\n const flipH = getAttribute(xfrm, null, 'flipH') === '1';\n const flipV = getAttribute(xfrm, null, 'flipV') === '1';\n\n const rotation = rotToDegrees(rot);\n\n if (rotation === undefined && !flipH && !flipV) {\n return undefined;\n }\n\n const transform: ImageTransform = {};\n if (rotation !== undefined) transform.rotation = rotation;\n if (flipH) transform.flipH = true;\n if (flipV) transform.flipV = true;\n\n return transform;\n}\n\n// ============================================================================\n// BLIP EXTRACTION (image relationship ID)\n// ============================================================================\n\n/**\n * Find the a:blip element and extract the relationship ID\n *\n * Path: a:graphic > a:graphicData > pic:pic > pic:blipFill > a:blip\n */\nfunction findBlipElement(container: XmlElement): XmlElement | null {\n // Find a:graphic\n const graphic = findByFullName(container, 'a:graphic');\n if (!graphic) return null;\n\n // Find a:graphicData\n const graphicData = findByFullName(graphic, 'a:graphicData');\n if (!graphicData) return null;\n\n // Find pic:pic\n const pic = findByFullName(graphicData, 'pic:pic');\n if (!pic) return null;\n\n // Find pic:blipFill\n const blipFill = findByFullName(pic, 'pic:blipFill');\n if (!blipFill) return null;\n\n // Find a:blip\n const blip = findByFullName(blipFill, 'a:blip');\n return blip;\n}\n\n/**\n * Extract rId from a:blip element\n */\nfunction extractBlipRId(blip: XmlElement | null): string {\n if (!blip) return '';\n\n // The rId is in r:embed attribute\n const rEmbed = getAttribute(blip, 'r', 'embed');\n if (rEmbed) return rEmbed;\n\n // Sometimes it's just \"embed\" without namespace\n const embed = getAttribute(blip, null, 'embed');\n if (embed) return embed;\n\n // Check r:link for linked (not embedded) images\n const rLink = getAttribute(blip, 'r', 'link');\n if (rLink) return rLink;\n\n return '';\n}\n\n/**\n * Find transform (a:xfrm) from picture shape properties\n *\n * Path: a:graphic > a:graphicData > pic:pic > pic:spPr > a:xfrm\n */\nfunction findPictureTransform(container: XmlElement): XmlElement | null {\n const graphic = findByFullName(container, 'a:graphic');\n if (!graphic) return null;\n\n const graphicData = findByFullName(graphic, 'a:graphicData');\n if (!graphicData) return null;\n\n const pic = findByFullName(graphicData, 'pic:pic');\n if (!pic) return null;\n\n const spPr = findByFullName(pic, 'pic:spPr');\n if (!spPr) return null;\n\n const xfrm = findByFullName(spPr, 'a:xfrm');\n return xfrm;\n}\n\n// ============================================================================\n// MEDIA RESOLUTION\n// ============================================================================\n\n/**\n * Normalize a target path to the standard word/media/... format\n */\nfunction normalizeMediaPath(targetPath: string): string {\n if (!targetPath) return targetPath;\n\n // Remove leading slashes\n let normalized = targetPath.replace(/^\\/+/, '');\n\n // Ensure word/ prefix for media files\n if (normalized.startsWith('media/')) {\n normalized = `word/${normalized}`;\n } else if (!normalized.startsWith('word/')) {\n normalized = `word/${normalized}`;\n }\n\n return normalized;\n}\n\n/**\n * Get MIME type from file extension\n */\nfunction getMimeType(path: string): string {\n const ext = path.split('.').pop()?.toLowerCase() ?? '';\n\n const mimeTypes: Record<string, string> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n bmp: 'image/bmp',\n tiff: 'image/tiff',\n tif: 'image/tiff',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n emf: 'image/x-emf',\n wmf: 'image/x-wmf',\n };\n\n return mimeTypes[ext] ?? 'application/octet-stream';\n}\n\n/**\n * Resolve image data from relationships and media map\n *\n * @param rId - Relationship ID (e.g., \"rId1\")\n * @param rels - Relationship map\n * @param media - Media files map\n * @returns Object with src (data URL or blob), mimeType, and filename\n */\nfunction resolveImageData(\n rId: string,\n rels: RelationshipMap | undefined,\n media: Map<string, MediaFile> | undefined\n): { src?: string; mimeType?: string; filename?: string } {\n if (!rId || !rels) {\n return {};\n }\n\n const rel = rels.get(rId);\n if (!rel) {\n return {};\n }\n\n // Get the target path\n const targetPath = rel.target;\n if (!targetPath) {\n return {};\n }\n\n // Normalize the path\n const normalizedPath = normalizeMediaPath(targetPath);\n const filename = targetPath.split('/').pop();\n\n // Case-insensitive lookup helper for media map\n const findMediaCaseInsensitive = (\n map: Map<string, MediaFile>,\n searchPath: string\n ): MediaFile | undefined => {\n const lowerPath = searchPath.toLowerCase();\n for (const [key, value] of map.entries()) {\n if (key.toLowerCase() === lowerPath) {\n return value;\n }\n }\n return undefined;\n };\n\n // Try to find the media file (case-insensitive)\n if (media) {\n // Try normalized path first\n const mediaFile = findMediaCaseInsensitive(media, normalizedPath);\n if (mediaFile) {\n return {\n src: mediaFile.dataUrl || mediaFile.base64, // Use data URL or base64\n mimeType: mediaFile.mimeType,\n filename,\n };\n }\n\n // Try without word/ prefix\n const altPath = targetPath.replace(/^\\/+/, '');\n const altMediaFile = findMediaCaseInsensitive(media, altPath);\n if (altMediaFile) {\n return {\n src: altMediaFile.dataUrl || altMediaFile.base64,\n mimeType: altMediaFile.mimeType,\n filename,\n };\n }\n\n // Try with word/ prefix added\n const withWordPrefix = `word/${altPath}`;\n const prefixedMediaFile = findMediaCaseInsensitive(media, withWordPrefix);\n if (prefixedMediaFile) {\n return {\n src: prefixedMediaFile.dataUrl || prefixedMediaFile.base64,\n mimeType: prefixedMediaFile.mimeType,\n filename,\n };\n }\n }\n\n // Return at least the MIME type based on extension\n return {\n mimeType: getMimeType(targetPath),\n filename,\n };\n}\n\n// ============================================================================\n// MAIN PARSING FUNCTIONS\n// ============================================================================\n\n/**\n * Parse a wp:inline element (inline image)\n *\n * @param inlineEl - The wp:inline element\n * @param rels - Relationship map for resolving rId\n * @param media - Media files map\n * @returns Parsed Image object\n */\nfunction parseInline(\n inlineEl: XmlElement,\n rels: RelationshipMap | undefined,\n media: Map<string, MediaFile> | undefined\n): Image {\n // Parse extent (size)\n const extent = findByFullName(inlineEl, 'wp:extent');\n const size = parseExtent(extent);\n\n // Parse effect extent\n const effectExtent = findByFullName(inlineEl, 'wp:effectExtent');\n const padding = parseEffectExtent(effectExtent);\n\n // Parse document properties\n const docPr = findByFullName(inlineEl, 'wp:docPr');\n const props = parseDocProps(docPr);\n\n // Find blip and extract rId\n const blip = findBlipElement(inlineEl);\n const rId = extractBlipRId(blip);\n\n // Resolve image data\n const imageData = resolveImageData(rId, rels, media);\n\n // Find transform\n const xfrm = findPictureTransform(inlineEl);\n const transform = parseTransform(xfrm);\n\n const image: Image = {\n type: 'image',\n rId,\n size,\n wrap: { type: 'inline' },\n };\n\n // Add optional properties\n if (props.id) image.id = props.id;\n if (props.alt) image.alt = props.alt;\n if (props.title) image.title = props.title;\n if (props.decorative) image.decorative = true;\n if (imageData.src) image.src = imageData.src;\n if (imageData.mimeType) image.mimeType = imageData.mimeType;\n if (imageData.filename) image.filename = imageData.filename;\n if (padding) image.padding = padding;\n if (transform) image.transform = transform;\n\n return image;\n}\n\n/**\n * Parse a wp:anchor element (floating/anchored image)\n *\n * @param anchorEl - The wp:anchor element\n * @param rels - Relationship map for resolving rId\n * @param media - Media files map\n * @returns Parsed Image object\n */\nfunction parseAnchor(\n anchorEl: XmlElement,\n rels: RelationshipMap | undefined,\n media: Map<string, MediaFile> | undefined\n): Image {\n // Parse extent (size)\n const extent = findByFullName(anchorEl, 'wp:extent');\n const size = parseExtent(extent);\n\n // Parse effect extent\n const effectExtent = findByFullName(anchorEl, 'wp:effectExtent');\n const padding = parseEffectExtent(effectExtent);\n\n // Parse document properties\n const docPr = findByFullName(anchorEl, 'wp:docPr');\n const props = parseDocProps(docPr);\n\n // Check behindDoc attribute\n const behindDoc = getAttribute(anchorEl, null, 'behindDoc') === '1';\n\n // Parse wrap element\n const wrapEl = findAnyOf(anchorEl, WRAP_ELEMENTS);\n const wrap = parseWrapElement(wrapEl, behindDoc);\n\n // Parse position\n const posH = findByFullName(anchorEl, 'wp:positionH');\n const posV = findByFullName(anchorEl, 'wp:positionV');\n const horizontal = parsePositionH(posH);\n const vertical = parsePositionV(posV);\n\n let position: ImagePosition | undefined;\n if (horizontal || vertical) {\n position = {\n horizontal: horizontal ?? { relativeTo: 'column' },\n vertical: vertical ?? { relativeTo: 'paragraph' },\n };\n }\n\n // Find blip and extract rId\n const blip = findBlipElement(anchorEl);\n const rId = extractBlipRId(blip);\n\n // Resolve image data\n const imageData = resolveImageData(rId, rels, media);\n\n // Find transform\n const xfrm = findPictureTransform(anchorEl);\n const transform = parseTransform(xfrm);\n\n const image: Image = {\n type: 'image',\n rId,\n size,\n wrap,\n };\n\n // Add optional properties\n if (props.id) image.id = props.id;\n if (props.alt) image.alt = props.alt;\n if (props.title) image.title = props.title;\n if (props.decorative) image.decorative = true;\n if (imageData.src) image.src = imageData.src;\n if (imageData.mimeType) image.mimeType = imageData.mimeType;\n if (imageData.filename) image.filename = imageData.filename;\n if (position) image.position = position;\n if (padding) image.padding = padding;\n if (transform) image.transform = transform;\n\n return image;\n}\n\n/**\n * Parse a w:drawing element\n *\n * The drawing element contains either wp:inline or wp:anchor.\n *\n * @param drawingEl - The w:drawing element\n * @param rels - Relationship map for resolving rId\n * @param media - Media files map\n * @returns Parsed Image object or null if not an image\n */\nexport function parseDrawing(\n drawingEl: XmlElement,\n rels: RelationshipMap | undefined,\n media: Map<string, MediaFile> | undefined\n): Image | null {\n const children = getChildElements(drawingEl);\n\n for (const child of children) {\n const name = child.name || '';\n\n if (name === 'wp:inline') {\n return parseInline(child, rels, media);\n }\n\n if (name === 'wp:anchor') {\n return parseAnchor(child, rels, media);\n }\n }\n\n return null;\n}\n\n/**\n * Parse an image from a w:drawing element\n *\n * This is the main entry point for image parsing.\n *\n * @param node - The w:drawing XML element\n * @param rels - Relationship map for resolving rId\n * @param media - Media files map\n * @returns Parsed Image object or null if parsing fails\n */\nexport function parseImage(\n node: XmlElement,\n rels: RelationshipMap | undefined,\n media: Map<string, MediaFile> | undefined\n): Image | null {\n return parseDrawing(node, rels, media);\n}\n\n// ============================================================================\n// UTILITY FUNCTIONS\n// ============================================================================\n\n/**\n * Check if an image is inline (not floating)\n */\nexport function isInlineImage(image: Image): boolean {\n return image.wrap.type === 'inline';\n}\n\n/**\n * Check if an image is floating (anchored)\n */\nexport function isFloatingImage(image: Image): boolean {\n return image.wrap.type !== 'inline';\n}\n\n/**\n * Check if an image is behind text\n */\nexport function isBehindText(image: Image): boolean {\n return image.wrap.type === 'behind';\n}\n\n/**\n * Check if an image is in front of text\n */\nexport function isInFrontOfText(image: Image): boolean {\n return image.wrap.type === 'inFront';\n}\n\n/**\n * Get image width in pixels\n */\nexport function getImageWidthPx(image: Image): number {\n return emuToPixels(image.size.width);\n}\n\n/**\n * Get image height in pixels\n */\nexport function getImageHeightPx(image: Image): number {\n return emuToPixels(image.size.height);\n}\n\n/**\n * Get image dimensions in pixels\n */\nexport function getImageDimensionsPx(image: Image): { width: number; height: number } {\n return {\n width: emuToPixels(image.size.width),\n height: emuToPixels(image.size.height),\n };\n}\n\n/**\n * Check if image has alt text (for accessibility)\n */\nexport function hasAltText(image: Image): boolean {\n return !!image.alt && image.alt.trim().length > 0;\n}\n\n/**\n * Check if image is decorative (should be ignored by screen readers)\n */\nexport function isDecorativeImage(image: Image): boolean {\n return image.decorative === true;\n}\n\n/**\n * Get wrap distances in pixels\n */\nexport function getWrapDistancesPx(image: Image): {\n top: number;\n bottom: number;\n left: number;\n right: number;\n} {\n return {\n top: emuToPixels(image.wrap.distT),\n bottom: emuToPixels(image.wrap.distB),\n left: emuToPixels(image.wrap.distL),\n right: emuToPixels(image.wrap.distR),\n };\n}\n\n/**\n * Check if image needs text wrapping\n */\nexport function needsTextWrapping(image: Image): boolean {\n const wrapTypes = ['square', 'tight', 'through', 'topAndBottom'];\n return wrapTypes.includes(image.wrap.type);\n}\n","/**\n * Run Parser - Parse text runs (w:r) with complete formatting\n *\n * A run is a contiguous region of text with the same character formatting.\n * Runs can contain:\n * - Text (w:t)\n * - Tabs (w:tab)\n * - Line breaks (w:br)\n * - Symbols (w:sym)\n * - Footnote/endnote references\n * - Field characters\n * - Drawings/images (w:drawing)\n * - And more...\n *\n * OOXML Reference:\n * - Run: w:r\n * - Run properties: w:rPr\n * - Text content: w:t\n */\n\nimport type {\n Run,\n RunContent,\n TextContent,\n TabContent,\n BreakContent,\n SymbolContent,\n NoteReferenceContent,\n FieldCharContent,\n InstrTextContent,\n SoftHyphenContent,\n NoBreakHyphenContent,\n DrawingContent,\n TextFormatting,\n ColorValue,\n ShadingProperties,\n UnderlineStyle,\n Theme,\n Image,\n RelationshipMap,\n MediaFile,\n} from '../types/document';\nimport type { StyleMap } from './styleParser';\nimport {\n findChild,\n getAttribute,\n getChildElements,\n getTextContent,\n parseBooleanElement,\n parseNumericAttribute,\n type XmlElement,\n} from './xmlParser';\nimport { resolveThemeFontRef } from './themeParser';\nimport { parseImage } from './imageParser';\n\n/**\n * Parse color value from attributes\n */\nfunction parseColorValue(\n rgb: string | null,\n themeColor: string | null,\n themeTint: string | null,\n themeShade: string | null\n): ColorValue {\n const color: ColorValue = {};\n\n if (rgb && rgb !== 'auto') {\n color.rgb = rgb;\n } else if (rgb === 'auto') {\n color.auto = true;\n }\n\n if (themeColor) {\n color.themeColor = themeColor as ColorValue['themeColor'];\n }\n\n if (themeTint) {\n color.themeTint = themeTint;\n }\n\n if (themeShade) {\n color.themeShade = themeShade;\n }\n\n return color;\n}\n\n/**\n * Parse shading properties (w:shd)\n */\nfunction parseShadingProperties(shd: XmlElement | null): ShadingProperties | undefined {\n if (!shd) return undefined;\n\n const props: ShadingProperties = {};\n\n const color = getAttribute(shd, 'w', 'color');\n if (color && color !== 'auto') {\n props.color = { rgb: color };\n }\n\n const fill = getAttribute(shd, 'w', 'fill');\n if (fill && fill !== 'auto') {\n props.fill = { rgb: fill };\n }\n\n const themeFill = getAttribute(shd, 'w', 'themeFill');\n if (themeFill) {\n props.fill = props.fill || {};\n props.fill.themeColor = themeFill as ColorValue['themeColor'];\n }\n\n const themeFillTint = getAttribute(shd, 'w', 'themeFillTint');\n if (themeFillTint && props.fill) {\n props.fill.themeTint = themeFillTint;\n }\n\n const themeFillShade = getAttribute(shd, 'w', 'themeFillShade');\n if (themeFillShade && props.fill) {\n props.fill.themeShade = themeFillShade;\n }\n\n const pattern = getAttribute(shd, 'w', 'val');\n if (pattern) {\n props.pattern = pattern as ShadingProperties['pattern'];\n }\n\n return Object.keys(props).length > 0 ? props : undefined;\n}\n\n/**\n * Parse run formatting properties (w:rPr)\n *\n * Handles ALL rPr properties:\n * - w:b (bold), w:i (italic), w:u (underline with style)\n * - w:strike (strikethrough), w:dstrike (double strike)\n * - w:vertAlign (superscript/subscript)\n * - w:smallCaps, w:caps (capitalization)\n * - w:highlight (text highlight color)\n * - w:shd (character shading)\n * - w:color (text color with theme resolution)\n * - w:sz (font size in half-points)\n * - w:rFonts (font family with theme resolution)\n * - w:spacing (character spacing)\n * - w:effect (text effects)\n * - And more...\n */\nexport function parseRunProperties(\n rPr: XmlElement | null,\n theme: Theme | null,\n _styles?: StyleMap\n): TextFormatting | undefined {\n if (!rPr) return undefined;\n\n const formatting: TextFormatting = {};\n\n // Bold (w:b)\n const b = findChild(rPr, 'w', 'b');\n if (b) formatting.bold = parseBooleanElement(b);\n\n const bCs = findChild(rPr, 'w', 'bCs');\n if (bCs) formatting.boldCs = parseBooleanElement(bCs);\n\n // Italic (w:i)\n const i = findChild(rPr, 'w', 'i');\n if (i) formatting.italic = parseBooleanElement(i);\n\n const iCs = findChild(rPr, 'w', 'iCs');\n if (iCs) formatting.italicCs = parseBooleanElement(iCs);\n\n // Underline (w:u)\n const u = findChild(rPr, 'w', 'u');\n if (u) {\n const style = getAttribute(u, 'w', 'val') as UnderlineStyle | null;\n if (style) {\n formatting.underline = { style };\n const colorVal = getAttribute(u, 'w', 'color');\n const themeColor = getAttribute(u, 'w', 'themeColor');\n if (colorVal || themeColor) {\n formatting.underline.color = parseColorValue(\n colorVal,\n themeColor,\n getAttribute(u, 'w', 'themeTint'),\n getAttribute(u, 'w', 'themeShade')\n );\n }\n }\n }\n\n // Strikethrough (w:strike)\n const strike = findChild(rPr, 'w', 'strike');\n if (strike) formatting.strike = parseBooleanElement(strike);\n\n // Double strikethrough (w:dstrike)\n const dstrike = findChild(rPr, 'w', 'dstrike');\n if (dstrike) formatting.doubleStrike = parseBooleanElement(dstrike);\n\n // Vertical alignment - superscript/subscript (w:vertAlign)\n const vertAlign = findChild(rPr, 'w', 'vertAlign');\n if (vertAlign) {\n const val = getAttribute(vertAlign, 'w', 'val');\n if (val === 'superscript' || val === 'subscript' || val === 'baseline') {\n formatting.vertAlign = val;\n }\n }\n\n // Small caps (w:smallCaps)\n const smallCaps = findChild(rPr, 'w', 'smallCaps');\n if (smallCaps) formatting.smallCaps = parseBooleanElement(smallCaps);\n\n // All caps (w:caps)\n const caps = findChild(rPr, 'w', 'caps');\n if (caps) formatting.allCaps = parseBooleanElement(caps);\n\n // Hidden text (w:vanish)\n const vanish = findChild(rPr, 'w', 'vanish');\n if (vanish) formatting.hidden = parseBooleanElement(vanish);\n\n // Text color (w:color)\n const color = findChild(rPr, 'w', 'color');\n if (color) {\n formatting.color = parseColorValue(\n getAttribute(color, 'w', 'val'),\n getAttribute(color, 'w', 'themeColor'),\n getAttribute(color, 'w', 'themeTint'),\n getAttribute(color, 'w', 'themeShade')\n );\n }\n\n // Highlight color (w:highlight)\n const highlight = findChild(rPr, 'w', 'highlight');\n if (highlight) {\n const val = getAttribute(highlight, 'w', 'val');\n if (val) {\n formatting.highlight = val as TextFormatting['highlight'];\n }\n }\n\n // Character shading (w:shd)\n const shd = findChild(rPr, 'w', 'shd');\n if (shd) {\n formatting.shading = parseShadingProperties(shd);\n }\n\n // Font size in half-points (w:sz)\n const sz = findChild(rPr, 'w', 'sz');\n if (sz) {\n const val = parseNumericAttribute(sz, 'w', 'val');\n if (val !== undefined) formatting.fontSize = val;\n }\n\n // Font size complex script (w:szCs)\n const szCs = findChild(rPr, 'w', 'szCs');\n if (szCs) {\n const val = parseNumericAttribute(szCs, 'w', 'val');\n if (val !== undefined) formatting.fontSizeCs = val;\n }\n\n // Font family (w:rFonts)\n const rFonts = findChild(rPr, 'w', 'rFonts');\n if (rFonts) {\n formatting.fontFamily = {\n ascii: getAttribute(rFonts, 'w', 'ascii') ?? undefined,\n hAnsi: getAttribute(rFonts, 'w', 'hAnsi') ?? undefined,\n eastAsia: getAttribute(rFonts, 'w', 'eastAsia') ?? undefined,\n cs: getAttribute(rFonts, 'w', 'cs') ?? undefined,\n };\n\n // Theme font references\n const asciiTheme = getAttribute(rFonts, 'w', 'asciiTheme');\n if (asciiTheme) {\n formatting.fontFamily.asciiTheme = asciiTheme as TextFormatting['fontFamily'] extends {\n asciiTheme?: infer T;\n }\n ? T\n : never;\n // Also resolve the actual font name for convenience\n if (theme && !formatting.fontFamily.ascii) {\n formatting.fontFamily.ascii = resolveThemeFontRef(theme, asciiTheme);\n }\n }\n\n const hAnsiTheme = getAttribute(rFonts, 'w', 'hAnsiTheme');\n if (hAnsiTheme) {\n formatting.fontFamily.hAnsiTheme = hAnsiTheme;\n if (theme && !formatting.fontFamily.hAnsi) {\n formatting.fontFamily.hAnsi = resolveThemeFontRef(theme, hAnsiTheme);\n }\n }\n\n const eastAsiaTheme = getAttribute(rFonts, 'w', 'eastAsiaTheme');\n if (eastAsiaTheme) {\n formatting.fontFamily.eastAsiaTheme = eastAsiaTheme;\n if (theme && !formatting.fontFamily.eastAsia) {\n formatting.fontFamily.eastAsia = resolveThemeFontRef(theme, eastAsiaTheme);\n }\n }\n\n const csTheme = getAttribute(rFonts, 'w', 'cstheme');\n if (csTheme) {\n formatting.fontFamily.csTheme = csTheme;\n if (theme && !formatting.fontFamily.cs) {\n formatting.fontFamily.cs = resolveThemeFontRef(theme, csTheme);\n }\n }\n }\n\n // Character spacing in twips (w:spacing)\n const spacing = findChild(rPr, 'w', 'spacing');\n if (spacing) {\n const val = parseNumericAttribute(spacing, 'w', 'val');\n if (val !== undefined) formatting.spacing = val;\n }\n\n // Position - raised/lowered in half-points (w:position)\n const position = findChild(rPr, 'w', 'position');\n if (position) {\n const val = parseNumericAttribute(position, 'w', 'val');\n if (val !== undefined) formatting.position = val;\n }\n\n // Horizontal text scale percentage (w:w)\n const w = findChild(rPr, 'w', 'w');\n if (w) {\n const val = parseNumericAttribute(w, 'w', 'val');\n if (val !== undefined) formatting.scale = val;\n }\n\n // Kerning threshold in half-points (w:kern)\n const kern = findChild(rPr, 'w', 'kern');\n if (kern) {\n const val = parseNumericAttribute(kern, 'w', 'val');\n if (val !== undefined) formatting.kerning = val;\n }\n\n // Text effect animation (w:effect)\n const effect = findChild(rPr, 'w', 'effect');\n if (effect) {\n const val = getAttribute(effect, 'w', 'val');\n if (val) formatting.effect = val as TextFormatting['effect'];\n }\n\n // Emphasis mark (w:em)\n const em = findChild(rPr, 'w', 'em');\n if (em) {\n const val = getAttribute(em, 'w', 'val');\n if (val) formatting.emphasisMark = val as TextFormatting['emphasisMark'];\n }\n\n // Emboss effect (w:emboss)\n const emboss = findChild(rPr, 'w', 'emboss');\n if (emboss) formatting.emboss = parseBooleanElement(emboss);\n\n // Imprint/engrave effect (w:imprint)\n const imprint = findChild(rPr, 'w', 'imprint');\n if (imprint) formatting.imprint = parseBooleanElement(imprint);\n\n // Outline effect (w:outline)\n const outline = findChild(rPr, 'w', 'outline');\n if (outline) formatting.outline = parseBooleanElement(outline);\n\n // Shadow effect (w:shadow)\n const shadow = findChild(rPr, 'w', 'shadow');\n if (shadow) formatting.shadow = parseBooleanElement(shadow);\n\n // Right-to-left text (w:rtl)\n const rtl = findChild(rPr, 'w', 'rtl');\n if (rtl) formatting.rtl = parseBooleanElement(rtl);\n\n // Complex script formatting (w:cs)\n const cs = findChild(rPr, 'w', 'cs');\n if (cs) formatting.cs = parseBooleanElement(cs);\n\n // Character style reference (w:rStyle)\n const rStyle = findChild(rPr, 'w', 'rStyle');\n if (rStyle) {\n const val = getAttribute(rStyle, 'w', 'val');\n if (val) formatting.styleId = val;\n }\n\n return Object.keys(formatting).length > 0 ? formatting : undefined;\n}\n\n/**\n * Parse text content (w:t)\n */\nfunction parseTextContent(element: XmlElement): TextContent {\n const text = getTextContent(element);\n const preserveSpace = getAttribute(element, 'xml', 'space') === 'preserve';\n\n return {\n type: 'text',\n text,\n preserveSpace: preserveSpace || undefined,\n };\n}\n\n/**\n * Parse tab element (w:tab)\n */\nfunction parseTabContent(): TabContent {\n return { type: 'tab' };\n}\n\n/**\n * Parse break element (w:br)\n */\nfunction parseBreakContent(element: XmlElement): BreakContent {\n const breakType = getAttribute(element, 'w', 'type');\n const clear = getAttribute(element, 'w', 'clear');\n\n const content: BreakContent = { type: 'break' };\n\n if (breakType === 'page' || breakType === 'column' || breakType === 'textWrapping') {\n content.breakType = breakType;\n }\n\n if (clear === 'none' || clear === 'left' || clear === 'right' || clear === 'all') {\n content.clear = clear;\n }\n\n return content;\n}\n\n/**\n * Parse symbol element (w:sym)\n */\nfunction parseSymbolContent(element: XmlElement): SymbolContent {\n const font = getAttribute(element, 'w', 'font') ?? '';\n const char = getAttribute(element, 'w', 'char') ?? '';\n\n return {\n type: 'symbol',\n font,\n char,\n };\n}\n\n/**\n * Parse footnote reference (w:footnoteReference)\n */\nfunction parseFootnoteReference(element: XmlElement): NoteReferenceContent {\n const id = parseNumericAttribute(element, 'w', 'id') ?? 0;\n\n return {\n type: 'footnoteRef',\n id,\n };\n}\n\n/**\n * Parse endnote reference (w:endnoteReference)\n */\nfunction parseEndnoteReference(element: XmlElement): NoteReferenceContent {\n const id = parseNumericAttribute(element, 'w', 'id') ?? 0;\n\n return {\n type: 'endnoteRef',\n id,\n };\n}\n\n/**\n * Parse field character (w:fldChar)\n */\nfunction parseFieldChar(element: XmlElement): FieldCharContent {\n const fldCharType = getAttribute(element, 'w', 'fldCharType');\n const fldLock =\n getAttribute(element, 'w', 'fldLock') === 'true' ||\n getAttribute(element, 'w', 'fldLock') === '1';\n const dirty =\n getAttribute(element, 'w', 'dirty') === 'true' || getAttribute(element, 'w', 'dirty') === '1';\n\n let charType: FieldCharContent['charType'] = 'begin';\n if (fldCharType === 'separate') charType = 'separate';\n else if (fldCharType === 'end') charType = 'end';\n\n return {\n type: 'fieldChar',\n charType,\n fldLock: fldLock || undefined,\n dirty: dirty || undefined,\n };\n}\n\n/**\n * Parse instruction text (w:instrText)\n */\nfunction parseInstrText(element: XmlElement): InstrTextContent {\n const text = getTextContent(element);\n\n return {\n type: 'instrText',\n text,\n };\n}\n\n/**\n * Parse drawing content (w:drawing)\n *\n * Uses imageParser to fully parse the drawing element including\n * image data resolution from relationships and media files.\n */\nfunction parseDrawingContent(\n element: XmlElement,\n rels: RelationshipMap | null,\n media: Map<string, MediaFile> | null\n): DrawingContent | null {\n // Use the full imageParser to parse the drawing\n const image = parseImage(element, rels ?? undefined, media ?? undefined);\n\n if (!image) {\n return null;\n }\n\n return {\n type: 'drawing',\n image,\n };\n}\n\n/**\n * Get the local name of an element (without namespace prefix)\n */\nfunction getLocalName(name: string | undefined): string {\n if (!name) return '';\n const colonIndex = name.indexOf(':');\n return colonIndex >= 0 ? name.substring(colonIndex + 1) : name;\n}\n\n/**\n * Parse all content within a run element\n */\nfunction parseRunContents(\n runElement: XmlElement,\n rels: RelationshipMap | null,\n media: Map<string, MediaFile> | null\n): RunContent[] {\n const contents: RunContent[] = [];\n const children = getChildElements(runElement);\n\n for (const child of children) {\n const localName = getLocalName(child.name);\n\n switch (localName) {\n case 't':\n // Text content\n contents.push(parseTextContent(child));\n break;\n\n case 'tab':\n // Tab character\n contents.push(parseTabContent());\n break;\n\n case 'br':\n // Line/page/column break\n contents.push(parseBreakContent(child));\n break;\n\n case 'sym':\n // Symbol character\n contents.push(parseSymbolContent(child));\n break;\n\n case 'footnoteReference':\n // Footnote reference\n contents.push(parseFootnoteReference(child));\n break;\n\n case 'endnoteReference':\n // Endnote reference\n contents.push(parseEndnoteReference(child));\n break;\n\n case 'fldChar':\n // Field character (begin/separate/end)\n contents.push(parseFieldChar(child));\n break;\n\n case 'instrText':\n // Field instruction text\n contents.push(parseInstrText(child));\n break;\n\n case 'softHyphen':\n // Soft hyphen\n contents.push({ type: 'softHyphen' } as SoftHyphenContent);\n break;\n\n case 'noBreakHyphen':\n // Non-breaking hyphen\n contents.push({ type: 'noBreakHyphen' } as NoBreakHyphenContent);\n break;\n\n case 'drawing':\n // Drawing/image\n const drawing = parseDrawingContent(child, rels, media);\n if (drawing) {\n contents.push(drawing);\n }\n break;\n\n case 'pict':\n case 'object':\n // Legacy VML pictures/objects - will handle in shape parser\n // For now, skip these\n break;\n\n case 'rPr':\n // Run properties - already handled separately\n break;\n\n case 'lastRenderedPageBreak':\n // Marker for last rendered page break - informational only\n break;\n\n case 'cr':\n // Carriage return - treat as line break\n contents.push({ type: 'break', breakType: 'textWrapping' } as BreakContent);\n break;\n\n case 'footnoteRef':\n case 'endnoteRef':\n // These are the actual footnote/endnote content markers (different from Reference)\n // They appear in the footnote/endnote text itself\n break;\n\n case 'separator':\n case 'continuationSeparator':\n // Footnote/endnote separators\n break;\n\n default:\n // Unknown element - log for debugging if needed\n // console.log(`Unknown run content element: ${localName}`);\n break;\n }\n }\n\n return contents;\n}\n\n/**\n * Parse a run element (w:r)\n *\n * @param node - The w:r XML element\n * @param styles - Style map for resolving style references\n * @param theme - Theme for resolving theme colors/fonts\n * @param rels - Relationship map for resolving image references\n * @param media - Media files map for image data\n * @returns Parsed Run object\n */\nexport function parseRun(\n node: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n rels: RelationshipMap | null = null,\n media: Map<string, MediaFile> | null = null\n): Run {\n const run: Run = {\n type: 'run',\n content: [],\n };\n\n // Parse run properties (w:rPr)\n const rPr = findChild(node, 'w', 'rPr');\n if (rPr) {\n run.formatting = parseRunProperties(rPr, theme, styles ?? undefined);\n }\n\n // Parse run contents (text, tabs, breaks, images, etc.)\n run.content = parseRunContents(node, rels, media);\n\n return run;\n}\n\n/**\n * Get plain text from a run\n *\n * @param run - Parsed Run object\n * @returns Concatenated text content\n */\nexport function getRunText(run: Run): string {\n let text = '';\n\n for (const content of run.content) {\n if (content.type === 'text') {\n text += content.text;\n } else if (content.type === 'tab') {\n text += '\\t';\n } else if (content.type === 'break') {\n if (content.breakType === 'page') {\n text += '\\f'; // Form feed for page break\n } else {\n text += '\\n';\n }\n } else if (content.type === 'softHyphen') {\n text += '\\u00AD'; // Soft hyphen Unicode\n } else if (content.type === 'noBreakHyphen') {\n text += '\\u2011'; // Non-breaking hyphen Unicode\n }\n }\n\n return text;\n}\n\n/**\n * Check if a run contains any actual content\n *\n * @param run - Parsed Run object\n * @returns true if run has visible content\n */\nexport function hasContent(run: Run): boolean {\n return run.content.length > 0;\n}\n\n/**\n * Check if a run contains a drawing/image\n *\n * @param run - Parsed Run object\n * @returns true if run contains an image\n */\nexport function hasImage(run: Run): boolean {\n return run.content.some((c) => c.type === 'drawing');\n}\n\n/**\n * Get all images from a run\n *\n * @param run - Parsed Run object\n * @returns Array of Image objects\n */\nexport function getImages(run: Run): Image[] {\n return run.content.filter((c): c is DrawingContent => c.type === 'drawing').map((c) => c.image);\n}\n\n/**\n * Check if a run is part of a complex field\n *\n * @param run - Parsed Run object\n * @returns true if run contains field characters\n */\nexport function hasFieldChar(run: Run): boolean {\n return run.content.some((c) => c.type === 'fieldChar');\n}\n\n/**\n * Get field character type if present\n *\n * @param run - Parsed Run object\n * @returns Field character type or null\n */\nexport function getFieldCharType(run: Run): 'begin' | 'separate' | 'end' | null {\n const fieldChar = run.content.find((c): c is FieldCharContent => c.type === 'fieldChar');\n return fieldChar?.charType ?? null;\n}\n","/**\n * Hyperlink Parser - Parse hyperlinks (w:hyperlink) with URL resolution\n *\n * OOXML Reference:\n * - Hyperlink element: w:hyperlink\n * - Attributes:\n * - r:id - Relationship ID for external link (resolves via .rels)\n * - w:anchor - Internal bookmark name\n * - w:tooltip - Tooltip/title text\n * - w:tgtFrame - Target frame (_blank, _self, etc.)\n * - w:history - Whether to add to history\n * - w:docLocation - Location within a document\n *\n * External links use r:id to reference a relationship in document.xml.rels\n * Internal links use w:anchor to reference a bookmark in the same document\n */\n\nimport type {\n Hyperlink,\n Run,\n BookmarkStart,\n BookmarkEnd,\n Theme,\n RelationshipMap,\n MediaFile,\n} from '../types/document';\nimport type { StyleMap } from './styleParser';\nimport {\n getAttribute,\n getChildElements,\n parseNumericAttribute,\n type XmlElement,\n} from './xmlParser';\nimport { parseRun } from './runParser';\nimport { isExternalHyperlink } from './relsParser';\n\n// ============================================================================\n// HYPERLINK PARSER\n// ============================================================================\n\n/**\n * Get the local name of an element (without namespace prefix)\n */\nfunction getLocalName(name: string | undefined): string {\n if (!name) return '';\n const colonIndex = name.indexOf(':');\n return colonIndex >= 0 ? name.substring(colonIndex + 1) : name;\n}\n\n/**\n * Parse bookmark start (w:bookmarkStart)\n *\n * Used for internal hyperlink targets within the document.\n */\nfunction parseBookmarkStart(node: XmlElement): BookmarkStart {\n const id = parseNumericAttribute(node, 'w', 'id') ?? 0;\n const name = getAttribute(node, 'w', 'name') ?? '';\n\n const bookmark: BookmarkStart = {\n type: 'bookmarkStart',\n id,\n name,\n };\n\n // Table column bookmarks\n const colFirst = parseNumericAttribute(node, 'w', 'colFirst');\n if (colFirst !== undefined) bookmark.colFirst = colFirst;\n\n const colLast = parseNumericAttribute(node, 'w', 'colLast');\n if (colLast !== undefined) bookmark.colLast = colLast;\n\n return bookmark;\n}\n\n/**\n * Parse bookmark end (w:bookmarkEnd)\n */\nfunction parseBookmarkEnd(node: XmlElement): BookmarkEnd {\n const id = parseNumericAttribute(node, 'w', 'id') ?? 0;\n\n return {\n type: 'bookmarkEnd',\n id,\n };\n}\n\n/**\n * Parse a hyperlink element (w:hyperlink)\n *\n * Handles both external links (via r:id relationship) and internal\n * links (via w:anchor bookmark reference).\n *\n * @param node - The w:hyperlink XML element\n * @param rels - Relationship map to resolve r:id references\n * @param styles - Style map for resolving run styles\n * @param theme - Theme for resolving colors/fonts\n * @param media - Media files map for image data\n * @returns Parsed Hyperlink object\n */\nexport function parseHyperlink(\n node: XmlElement,\n rels: RelationshipMap | null,\n styles: StyleMap | null = null,\n theme: Theme | null = null,\n media: Map<string, MediaFile> | null = null\n): Hyperlink {\n const hyperlink: Hyperlink = {\n type: 'hyperlink',\n children: [],\n };\n\n // === External Link (r:id) ===\n // Get relationship ID for external links\n const rId = getAttribute(node, 'r', 'id');\n if (rId) {\n hyperlink.rId = rId;\n\n // Resolve the relationship to get the actual URL\n if (rels) {\n const rel = rels.get(rId);\n if (rel) {\n // External hyperlinks have TargetMode=\"External\" and target is the URL\n if (isExternalHyperlink(rel)) {\n hyperlink.href = rel.target;\n } else {\n // Internal document link (to another part of the DOCX)\n hyperlink.href = rel.target;\n }\n }\n }\n }\n\n // === Internal Bookmark Link (w:anchor) ===\n // Get internal bookmark anchor\n const anchor = getAttribute(node, 'w', 'anchor');\n if (anchor) {\n hyperlink.anchor = anchor;\n // For internal links, create a fragment-style href\n if (!hyperlink.href) {\n hyperlink.href = `#${anchor}`;\n }\n }\n\n // === Tooltip ===\n const tooltip = getAttribute(node, 'w', 'tooltip');\n if (tooltip) {\n hyperlink.tooltip = tooltip;\n }\n\n // === Target Frame ===\n // Common values: _blank (new window), _self (same), _parent, _top\n const tgtFrame = getAttribute(node, 'w', 'tgtFrame');\n if (tgtFrame) {\n hyperlink.target = tgtFrame;\n }\n\n // === History ===\n // Whether to add to browser history\n const history = getAttribute(node, 'w', 'history');\n if (history === '1' || history === 'true') {\n hyperlink.history = true;\n }\n\n // === Document Location ===\n // Location within a linked document (like fragment for external doc)\n const docLocation = getAttribute(node, 'w', 'docLocation');\n if (docLocation) {\n hyperlink.docLocation = docLocation;\n }\n\n // === Parse Children ===\n // Hyperlinks contain runs for the display text, and possibly bookmarks\n const children = getChildElements(node);\n for (const child of children) {\n const localName = getLocalName(child.name);\n\n switch (localName) {\n case 'r':\n hyperlink.children.push(parseRun(child, styles, theme, rels, media));\n break;\n\n case 'bookmarkStart':\n hyperlink.children.push(parseBookmarkStart(child));\n break;\n\n case 'bookmarkEnd':\n hyperlink.children.push(parseBookmarkEnd(child));\n break;\n\n // Note: hyperlinks can technically contain other elements like\n // fldSimple, but these are rare. Add support as needed.\n }\n }\n\n return hyperlink;\n}\n\n// ============================================================================\n// UTILITY FUNCTIONS\n// ============================================================================\n\n/**\n * Get the display text of a hyperlink\n *\n * Concatenates text from all child runs.\n *\n * @param hyperlink - Parsed Hyperlink object\n * @returns Display text string\n */\nexport function getHyperlinkText(hyperlink: Hyperlink): string {\n let text = '';\n\n for (const child of hyperlink.children) {\n if (child.type === 'run') {\n for (const content of child.content) {\n if (content.type === 'text') {\n text += content.text;\n } else if (content.type === 'tab') {\n text += '\\t';\n }\n }\n }\n }\n\n return text;\n}\n\n/**\n * Check if a hyperlink is an external link\n *\n * @param hyperlink - Parsed Hyperlink object\n * @returns true if this links to an external URL\n */\nexport function isExternalLink(hyperlink: Hyperlink): boolean {\n // Has rId and resolved href that starts with a protocol\n if (hyperlink.href) {\n return /^https?:\\/\\/|^mailto:|^tel:|^ftp:/i.test(hyperlink.href);\n }\n // Has rId but not resolved (still counts as external attempt)\n return !!hyperlink.rId && !hyperlink.anchor;\n}\n\n/**\n * Check if a hyperlink is an internal bookmark link\n *\n * @param hyperlink - Parsed Hyperlink object\n * @returns true if this links to an internal bookmark\n */\nexport function isInternalLink(hyperlink: Hyperlink): boolean {\n return !!hyperlink.anchor;\n}\n\n/**\n * Get the resolved URL of a hyperlink\n *\n * For external links, returns the full URL.\n * For internal links, returns the anchor prefixed with #.\n * Returns undefined if the link couldn't be resolved.\n *\n * @param hyperlink - Parsed Hyperlink object\n * @returns Resolved URL or undefined\n */\nexport function getHyperlinkUrl(hyperlink: Hyperlink): string | undefined {\n return hyperlink.href;\n}\n\n/**\n * Check if a hyperlink has any content (runs)\n *\n * @param hyperlink - Parsed Hyperlink object\n * @returns true if hyperlink has child runs\n */\nexport function hasContent(hyperlink: Hyperlink): boolean {\n return hyperlink.children.some((child) => child.type === 'run');\n}\n\n/**\n * Get all runs from a hyperlink\n *\n * @param hyperlink - Parsed Hyperlink object\n * @returns Array of Run objects\n */\nexport function getHyperlinkRuns(hyperlink: Hyperlink): Run[] {\n return hyperlink.children.filter((child): child is Run => child.type === 'run');\n}\n\n/**\n * Resolve a hyperlink's rId to a URL using a relationship map\n *\n * This is useful when you have a hyperlink that was parsed without\n * relationship context and need to resolve it later.\n *\n * @param hyperlink - Parsed Hyperlink object (will be modified)\n * @param rels - Relationship map to resolve against\n * @returns The resolved URL or undefined\n */\nexport function resolveHyperlinkUrl(\n hyperlink: Hyperlink,\n rels: RelationshipMap\n): string | undefined {\n if (hyperlink.rId) {\n const rel = rels.get(hyperlink.rId);\n if (rel) {\n if (isExternalHyperlink(rel)) {\n hyperlink.href = rel.target;\n } else {\n hyperlink.href = rel.target;\n }\n return hyperlink.href;\n }\n }\n\n // If there's an anchor but no href yet, set it\n if (hyperlink.anchor && !hyperlink.href) {\n hyperlink.href = `#${hyperlink.anchor}`;\n return hyperlink.href;\n }\n\n return hyperlink.href;\n}\n\n/**\n * Create an internal hyperlink to a bookmark\n *\n * Utility function for creating hyperlinks programmatically.\n *\n * @param anchor - Bookmark name to link to\n * @param children - Child runs for display text\n * @param options - Optional properties (tooltip, etc.)\n * @returns New Hyperlink object\n */\nexport function createInternalHyperlink(\n anchor: string,\n children: Run[],\n options?: {\n tooltip?: string;\n }\n): Hyperlink {\n return {\n type: 'hyperlink',\n anchor,\n href: `#${anchor}`,\n tooltip: options?.tooltip,\n children,\n };\n}\n\n/**\n * Create an external hyperlink\n *\n * Utility function for creating hyperlinks programmatically.\n * Note: The rId would need to be assigned when serializing.\n *\n * @param url - External URL\n * @param children - Child runs for display text\n * @param options - Optional properties (tooltip, target, etc.)\n * @returns New Hyperlink object\n */\nexport function createExternalHyperlink(\n url: string,\n children: Run[],\n options?: {\n tooltip?: string;\n target?: string;\n }\n): Hyperlink {\n return {\n type: 'hyperlink',\n href: url,\n tooltip: options?.tooltip,\n target: options?.target,\n children,\n };\n}\n","/**\n * Bookmark Parser - Parse bookmark markers (w:bookmarkStart, w:bookmarkEnd)\n *\n * Bookmarks are named locations in a document that can be targeted by internal\n * hyperlinks. They consist of a start and end marker with matching IDs.\n *\n * OOXML Reference:\n * - Bookmark start: w:bookmarkStart (id, name, colFirst?, colLast?)\n * - Bookmark end: w:bookmarkEnd (id)\n * - Internal hyperlinks reference bookmarks by name via w:anchor attribute\n *\n * Bookmark Structure:\n * - Each bookmark has a unique numeric ID within the document\n * - The name is used for hyperlink references\n * - Start and end markers can span multiple paragraphs or be point bookmarks\n * - Table column bookmarks have colFirst/colLast for column ranges\n */\n\nimport type { BookmarkStart, BookmarkEnd } from '../types/document';\nimport { getAttribute, parseNumericAttribute, type XmlElement } from './xmlParser';\n\n// ============================================================================\n// BOOKMARK PARSING\n// ============================================================================\n\n/**\n * Parse a bookmark start element (w:bookmarkStart)\n *\n * Extracts:\n * - id: Numeric identifier (required, matches with bookmarkEnd)\n * - name: Bookmark name (required, used by hyperlinks)\n * - colFirst: First column for table bookmarks (optional)\n * - colLast: Last column for table bookmarks (optional)\n *\n * @param node - The w:bookmarkStart XML element\n * @returns Parsed BookmarkStart object\n */\nexport function parseBookmarkStart(node: XmlElement): BookmarkStart {\n const id = parseNumericAttribute(node, 'w', 'id') ?? 0;\n const name = getAttribute(node, 'w', 'name') ?? '';\n\n const bookmark: BookmarkStart = {\n type: 'bookmarkStart',\n id,\n name,\n };\n\n // Table column bookmarks (for bookmarks spanning table columns)\n const colFirst = parseNumericAttribute(node, 'w', 'colFirst');\n if (colFirst !== undefined) {\n bookmark.colFirst = colFirst;\n }\n\n const colLast = parseNumericAttribute(node, 'w', 'colLast');\n if (colLast !== undefined) {\n bookmark.colLast = colLast;\n }\n\n return bookmark;\n}\n\n/**\n * Parse a bookmark end element (w:bookmarkEnd)\n *\n * Bookmark ends only contain an ID that matches the corresponding start marker.\n *\n * @param node - The w:bookmarkEnd XML element\n * @returns Parsed BookmarkEnd object\n */\nexport function parseBookmarkEnd(node: XmlElement): BookmarkEnd {\n const id = parseNumericAttribute(node, 'w', 'id') ?? 0;\n\n return {\n type: 'bookmarkEnd',\n id,\n };\n}\n\n// ============================================================================\n// BOOKMARK COLLECTION & UTILITIES\n// ============================================================================\n\n/**\n * Bookmark map for quick lookup by ID or name\n */\nexport interface BookmarkMap {\n /** Lookup bookmark start by ID */\n byId: Map<number, BookmarkStart>;\n /** Lookup bookmark start by name (for hyperlink resolution) */\n byName: Map<string, BookmarkStart>;\n /** All bookmark starts in document order */\n bookmarks: BookmarkStart[];\n}\n\n/**\n * Create an empty bookmark map\n */\nexport function createBookmarkMap(): BookmarkMap {\n return {\n byId: new Map(),\n byName: new Map(),\n bookmarks: [],\n };\n}\n\n/**\n * Add a bookmark to the map\n *\n * @param map - The bookmark map to update\n * @param bookmark - The bookmark start to add\n */\nexport function addBookmark(map: BookmarkMap, bookmark: BookmarkStart): void {\n map.byId.set(bookmark.id, bookmark);\n if (bookmark.name) {\n map.byName.set(bookmark.name, bookmark);\n }\n map.bookmarks.push(bookmark);\n}\n\n/**\n * Get a bookmark by name (for resolving internal hyperlinks)\n *\n * @param map - The bookmark map to search\n * @param name - Bookmark name to find\n * @returns The BookmarkStart or undefined if not found\n */\nexport function getBookmarkByName(map: BookmarkMap, name: string): BookmarkStart | undefined {\n return map.byName.get(name);\n}\n\n/**\n * Get a bookmark by ID (for matching start/end pairs)\n *\n * @param map - The bookmark map to search\n * @param id - Bookmark ID to find\n * @returns The BookmarkStart or undefined if not found\n */\nexport function getBookmarkById(map: BookmarkMap, id: number): BookmarkStart | undefined {\n return map.byId.get(id);\n}\n\n/**\n * Check if a bookmark exists by name\n *\n * @param map - The bookmark map to search\n * @param name - Bookmark name to check\n * @returns true if bookmark exists\n */\nexport function hasBookmark(map: BookmarkMap, name: string): boolean {\n return map.byName.has(name);\n}\n\n/**\n * Get all bookmark names in the document\n *\n * @param map - The bookmark map\n * @returns Array of bookmark names\n */\nexport function getAllBookmarkNames(map: BookmarkMap): string[] {\n return Array.from(map.byName.keys());\n}\n\n/**\n * Check if a bookmark is a point bookmark (start and end at same location)\n *\n * Point bookmarks have no content between start and end markers.\n * This is commonly used for insertion points.\n *\n * @param start - The bookmark start\n * @param end - The bookmark end\n * @param contents - Content between them\n * @returns true if this is a point bookmark\n */\nexport function isPointBookmark(\n start: BookmarkStart,\n end: BookmarkEnd,\n contents: unknown[]\n): boolean {\n return start.id === end.id && contents.length === 0;\n}\n\n/**\n * Check if a bookmark is a table column bookmark\n *\n * Table bookmarks have colFirst and colLast attributes indicating\n * they span specific table columns.\n *\n * @param bookmark - The bookmark to check\n * @returns true if bookmark has column range info\n */\nexport function isTableBookmark(bookmark: BookmarkStart): boolean {\n return bookmark.colFirst !== undefined || bookmark.colLast !== undefined;\n}\n\n/**\n * Generate an internal hyperlink href from a bookmark name\n *\n * Internal hyperlinks use #anchor format.\n *\n * @param bookmarkName - The bookmark name to link to\n * @returns Href string (e.g., \"#BookmarkName\")\n */\nexport function bookmarkToHref(bookmarkName: string): string {\n return `#${bookmarkName}`;\n}\n\n/**\n * Extract bookmark name from an internal hyperlink href\n *\n * @param href - The href string\n * @returns Bookmark name or null if not an internal link\n */\nexport function hrefToBookmarkName(href: string): string | null {\n if (href.startsWith('#')) {\n return href.substring(1);\n }\n return null;\n}\n\n// ============================================================================\n// SPECIAL BOOKMARK TYPES\n// ============================================================================\n\n/**\n * Check if a bookmark is a built-in Word bookmark\n *\n * Word uses certain reserved bookmark names for special purposes:\n * - _GoBack: Last editing position\n * - _Toc*: Table of contents entries\n * - _Ref*: Cross-reference anchors\n * - _Hlt*: Highlight ranges\n *\n * @param name - Bookmark name to check\n * @returns true if this is a built-in bookmark\n */\nexport function isBuiltInBookmark(name: string): boolean {\n if (!name) return false;\n\n // Check for underscore prefix (Word internal bookmarks)\n if (name.startsWith('_')) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Check if a bookmark is a TOC entry bookmark\n *\n * @param name - Bookmark name to check\n * @returns true if bookmark is for TOC\n */\nexport function isTocBookmark(name: string): boolean {\n return name.startsWith('_Toc');\n}\n\n/**\n * Check if a bookmark is a cross-reference anchor\n *\n * @param name - Bookmark name to check\n * @returns true if bookmark is for cross-reference\n */\nexport function isRefBookmark(name: string): boolean {\n return name.startsWith('_Ref');\n}\n\n/**\n * Get bookmark type category\n *\n * @param name - Bookmark name\n * @returns Bookmark type\n */\nexport function getBookmarkType(name: string): 'user' | 'toc' | 'ref' | 'goBack' | 'internal' {\n if (name === '_GoBack') {\n return 'goBack';\n }\n if (isTocBookmark(name)) {\n return 'toc';\n }\n if (isRefBookmark(name)) {\n return 'ref';\n }\n if (isBuiltInBookmark(name)) {\n return 'internal';\n }\n return 'user';\n}\n\n// ============================================================================\n// BOOKMARK VALIDATION\n// ============================================================================\n\n/**\n * Validate that all bookmark starts have matching ends\n *\n * @param starts - Array of bookmark starts\n * @param ends - Array of bookmark ends\n * @returns Object with validation results\n */\nexport function validateBookmarkPairs(\n starts: BookmarkStart[],\n ends: BookmarkEnd[]\n): {\n valid: boolean;\n unmatchedStarts: BookmarkStart[];\n unmatchedEnds: BookmarkEnd[];\n} {\n const startIds = new Set(starts.map((s) => s.id));\n const endIds = new Set(ends.map((e) => e.id));\n\n const unmatchedStarts = starts.filter((s) => !endIds.has(s.id));\n const unmatchedEnds = ends.filter((e) => !startIds.has(e.id));\n\n return {\n valid: unmatchedStarts.length === 0 && unmatchedEnds.length === 0,\n unmatchedStarts,\n unmatchedEnds,\n };\n}\n\n/**\n * Validate a bookmark name (for creating new bookmarks)\n *\n * Valid bookmark names:\n * - Cannot be empty\n * - Must start with a letter or underscore\n * - Can contain letters, digits, and underscores\n * - Cannot exceed 40 characters\n *\n * @param name - Name to validate\n * @returns Object with validation result and error message if invalid\n */\nexport function validateBookmarkName(name: string): { valid: boolean; error?: string } {\n if (!name) {\n return { valid: false, error: 'Bookmark name cannot be empty' };\n }\n\n if (name.length > 40) {\n return { valid: false, error: 'Bookmark name cannot exceed 40 characters' };\n }\n\n // Check first character (letter or underscore)\n if (!/^[a-zA-Z_]/.test(name)) {\n return {\n valid: false,\n error: 'Bookmark name must start with a letter or underscore',\n };\n }\n\n // Check remaining characters (letters, digits, underscores)\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n return {\n valid: false,\n error: 'Bookmark name can only contain letters, digits, and underscores',\n };\n }\n\n return { valid: true };\n}\n","/**\n * Table Parser - Parse tables with full OOXML structure\n *\n * OOXML tables consist of:\n * - w:tbl - Table element\n * - w:tblPr - Table properties (width, borders, style)\n * - w:tblGrid - Column width definitions\n * - w:tr - Table rows\n * - w:trPr - Row properties (height, header)\n * - w:tc - Table cells\n * - w:tcPr - Cell properties (width, borders, merge)\n *\n * Cell merging:\n * - Horizontal: w:gridSpan (how many grid columns this cell spans)\n * - Vertical: w:vMerge (restart = start of merge, continue = continuation)\n *\n * OOXML Reference:\n * - w:tbl contains w:tblPr, w:tblGrid, and w:tr elements\n * - w:tr contains w:trPr and w:tc elements\n * - w:tc contains w:tcPr and content (paragraphs, tables)\n */\n\nimport type {\n Table,\n TableRow,\n TableCell,\n TableFormatting,\n TableRowFormatting,\n TableCellFormatting,\n TableMeasurement,\n TableWidthType,\n TableBorders,\n TableLook,\n CellMargins,\n FloatingTableProperties,\n ConditionalFormatStyle,\n Paragraph,\n Theme,\n BorderSpec,\n ShadingProperties,\n ColorValue,\n RelationshipMap,\n MediaFile,\n} from '../types/document';\nimport type { StyleMap } from './styleParser';\nimport type { NumberingMap } from './numberingParser';\nimport { parseParagraph } from './paragraphParser';\nimport {\n findChild,\n findChildren,\n getAttribute,\n parseNumericAttribute,\n parseBooleanElement,\n parseColorElement,\n type XmlElement,\n} from './xmlParser';\n\n// ============================================================================\n// TABLE MEASUREMENT PARSING\n// ============================================================================\n\n/**\n * Parse a table measurement (width, height, etc.)\n *\n * @param element - Element with w:w and w:type attributes\n * @returns Parsed measurement or undefined\n */\nexport function parseTableMeasurement(element: XmlElement | null): TableMeasurement | undefined {\n if (!element) return undefined;\n\n const value = parseNumericAttribute(element, 'w', 'w') ?? 0;\n const typeStr = getAttribute(element, 'w', 'type') ?? 'dxa';\n\n let type: TableWidthType = 'dxa';\n if (typeStr === 'auto' || typeStr === 'dxa' || typeStr === 'nil' || typeStr === 'pct') {\n type = typeStr;\n }\n\n return { value, type };\n}\n\n/**\n * Parse width from an element (shorthand for common case)\n */\nfunction parseWidth(element: XmlElement | null): TableMeasurement | undefined {\n return parseTableMeasurement(element);\n}\n\n// ============================================================================\n// BORDER PARSING\n// ============================================================================\n\n/**\n * Parse a single border specification\n *\n * @param element - Border element (w:top, w:bottom, etc.)\n * @returns Parsed border or undefined\n */\nexport function parseBorderSpec(element: XmlElement | null): BorderSpec | undefined {\n if (!element) return undefined;\n\n const styleStr = getAttribute(element, 'w', 'val') ?? 'none';\n const style = styleStr as BorderSpec['style'];\n\n const border: BorderSpec = { style };\n\n // Size in eighths of a point\n const sz = parseNumericAttribute(element, 'w', 'sz');\n if (sz !== undefined) {\n border.size = sz;\n }\n\n // Space from text in points\n const space = parseNumericAttribute(element, 'w', 'space');\n if (space !== undefined) {\n border.space = space;\n }\n\n // Color\n const colorData = parseColorElement(element);\n if (colorData) {\n border.color = {\n rgb: colorData.val,\n themeColor: colorData.themeColor as ColorValue['themeColor'],\n themeTint: colorData.themeTint,\n themeShade: colorData.themeShade,\n };\n }\n\n // Shadow effect\n const shadow = getAttribute(element, 'w', 'shadow');\n if (shadow === '1' || shadow === 'true') {\n border.shadow = true;\n }\n\n // Frame effect\n const frame = getAttribute(element, 'w', 'frame');\n if (frame === '1' || frame === 'true') {\n border.frame = true;\n }\n\n return border;\n}\n\n/**\n * Parse table borders (w:tblBorders or w:tcBorders)\n *\n * @param bordersElement - The borders container element\n * @returns Parsed borders or undefined\n */\nexport function parseTableBorders(bordersElement: XmlElement | null): TableBorders | undefined {\n if (!bordersElement) return undefined;\n\n const borders: TableBorders = {};\n\n const top = parseBorderSpec(findChild(bordersElement, 'w', 'top'));\n if (top) borders.top = top;\n\n const bottom = parseBorderSpec(findChild(bordersElement, 'w', 'bottom'));\n if (bottom) borders.bottom = bottom;\n\n const left = parseBorderSpec(\n findChild(bordersElement, 'w', 'left') ?? findChild(bordersElement, 'w', 'start')\n );\n if (left) borders.left = left;\n\n const right = parseBorderSpec(\n findChild(bordersElement, 'w', 'right') ?? findChild(bordersElement, 'w', 'end')\n );\n if (right) borders.right = right;\n\n const insideH = parseBorderSpec(findChild(bordersElement, 'w', 'insideH'));\n if (insideH) borders.insideH = insideH;\n\n const insideV = parseBorderSpec(findChild(bordersElement, 'w', 'insideV'));\n if (insideV) borders.insideV = insideV;\n\n // Return undefined if no borders were parsed\n if (Object.keys(borders).length === 0) return undefined;\n\n return borders;\n}\n\n// ============================================================================\n// CELL MARGINS PARSING\n// ============================================================================\n\n/**\n * Parse cell margins (w:tblCellMar or w:tcMar)\n *\n * @param marginsElement - The margins container element\n * @returns Parsed margins or undefined\n */\nexport function parseCellMargins(marginsElement: XmlElement | null): CellMargins | undefined {\n if (!marginsElement) return undefined;\n\n const margins: CellMargins = {};\n\n const top = parseWidth(findChild(marginsElement, 'w', 'top'));\n if (top) margins.top = top;\n\n const bottom = parseWidth(findChild(marginsElement, 'w', 'bottom'));\n if (bottom) margins.bottom = bottom;\n\n const left = parseWidth(\n findChild(marginsElement, 'w', 'left') ?? findChild(marginsElement, 'w', 'start')\n );\n if (left) margins.left = left;\n\n const right = parseWidth(\n findChild(marginsElement, 'w', 'right') ?? findChild(marginsElement, 'w', 'end')\n );\n if (right) margins.right = right;\n\n if (Object.keys(margins).length === 0) return undefined;\n\n return margins;\n}\n\n// ============================================================================\n// SHADING PARSING\n// ============================================================================\n\n/**\n * Parse shading properties (w:shd)\n *\n * @param shdElement - The w:shd element\n * @returns Parsed shading or undefined\n */\nexport function parseShading(shdElement: XmlElement | null): ShadingProperties | undefined {\n if (!shdElement) return undefined;\n\n const shading: ShadingProperties = {};\n\n // Fill color (background)\n const fillStr = getAttribute(shdElement, 'w', 'fill');\n if (fillStr && fillStr !== 'auto') {\n shading.fill = { rgb: fillStr };\n }\n\n // Theme fill\n const themeFill = getAttribute(shdElement, 'w', 'themeFill');\n if (themeFill) {\n shading.fill = { themeColor: themeFill as any };\n\n const themeFillTint = getAttribute(shdElement, 'w', 'themeFillTint');\n if (themeFillTint && shading.fill) {\n shading.fill.themeTint = themeFillTint;\n }\n\n const themeFillShade = getAttribute(shdElement, 'w', 'themeFillShade');\n if (themeFillShade && shading.fill) {\n shading.fill.themeShade = themeFillShade;\n }\n }\n\n // Pattern color\n const colorStr = getAttribute(shdElement, 'w', 'color');\n if (colorStr && colorStr !== 'auto') {\n shading.color = { rgb: colorStr };\n }\n\n // Pattern value\n const pattern = getAttribute(shdElement, 'w', 'val');\n if (pattern) {\n shading.pattern = pattern as ShadingProperties['pattern'];\n }\n\n if (Object.keys(shading).length === 0) return undefined;\n\n return shading;\n}\n\n// ============================================================================\n// TABLE LOOK PARSING\n// ============================================================================\n\n/**\n * Parse table look flags (w:tblLook)\n *\n * @param lookElement - The w:tblLook element\n * @returns Parsed table look or undefined\n */\nexport function parseTableLook(lookElement: XmlElement | null): TableLook | undefined {\n if (!lookElement) return undefined;\n\n const look: TableLook = {};\n\n // Parse individual flags\n const firstRow = getAttribute(lookElement, 'w', 'firstRow');\n if (firstRow === '1' || firstRow === 'true') look.firstRow = true;\n\n const lastRow = getAttribute(lookElement, 'w', 'lastRow');\n if (lastRow === '1' || lastRow === 'true') look.lastRow = true;\n\n const firstColumn = getAttribute(lookElement, 'w', 'firstColumn');\n if (firstColumn === '1' || firstColumn === 'true') look.firstColumn = true;\n\n const lastColumn = getAttribute(lookElement, 'w', 'lastColumn');\n if (lastColumn === '1' || lastColumn === 'true') look.lastColumn = true;\n\n const noHBand = getAttribute(lookElement, 'w', 'noHBand');\n if (noHBand === '1' || noHBand === 'true') look.noHBand = true;\n\n const noVBand = getAttribute(lookElement, 'w', 'noVBand');\n if (noVBand === '1' || noVBand === 'true') look.noVBand = true;\n\n // Also check for the val attribute (hexadecimal flags)\n const val = getAttribute(lookElement, 'w', 'val');\n if (val) {\n const flags = parseInt(val, 16);\n if (!isNaN(flags)) {\n if (flags & 0x0020) look.firstRow = true;\n if (flags & 0x0040) look.lastRow = true;\n if (flags & 0x0080) look.firstColumn = true;\n if (flags & 0x0100) look.lastColumn = true;\n if (flags & 0x0200) look.noHBand = true;\n if (flags & 0x0400) look.noVBand = true;\n }\n }\n\n if (Object.keys(look).length === 0) return undefined;\n\n return look;\n}\n\n// ============================================================================\n// FLOATING TABLE PROPERTIES\n// ============================================================================\n\n/**\n * Parse floating table properties (w:tblpPr)\n *\n * @param tblpPrElement - The w:tblpPr element\n * @returns Parsed floating properties or undefined\n */\nexport function parseFloatingTableProperties(\n tblpPrElement: XmlElement | null\n): FloatingTableProperties | undefined {\n if (!tblpPrElement) return undefined;\n\n const floating: FloatingTableProperties = {};\n\n // Horizontal anchor\n const horzAnchor = getAttribute(tblpPrElement, 'w', 'horzAnchor');\n if (horzAnchor === 'margin' || horzAnchor === 'page' || horzAnchor === 'text') {\n floating.horzAnchor = horzAnchor;\n }\n\n // Vertical anchor\n const vertAnchor = getAttribute(tblpPrElement, 'w', 'vertAnchor');\n if (vertAnchor === 'margin' || vertAnchor === 'page' || vertAnchor === 'text') {\n floating.vertAnchor = vertAnchor;\n }\n\n // Horizontal position\n const tblpX = parseNumericAttribute(tblpPrElement, 'w', 'tblpX');\n if (tblpX !== undefined) floating.tblpX = tblpX;\n\n const tblpXSpec = getAttribute(tblpPrElement, 'w', 'tblpXSpec');\n if (tblpXSpec) {\n floating.tblpXSpec = tblpXSpec as FloatingTableProperties['tblpXSpec'];\n }\n\n // Vertical position\n const tblpY = parseNumericAttribute(tblpPrElement, 'w', 'tblpY');\n if (tblpY !== undefined) floating.tblpY = tblpY;\n\n const tblpYSpec = getAttribute(tblpPrElement, 'w', 'tblpYSpec');\n if (tblpYSpec) {\n floating.tblpYSpec = tblpYSpec as FloatingTableProperties['tblpYSpec'];\n }\n\n // Distance from text\n const topFromText = parseNumericAttribute(tblpPrElement, 'w', 'topFromText');\n if (topFromText !== undefined) floating.topFromText = topFromText;\n\n const bottomFromText = parseNumericAttribute(tblpPrElement, 'w', 'bottomFromText');\n if (bottomFromText !== undefined) floating.bottomFromText = bottomFromText;\n\n const leftFromText = parseNumericAttribute(tblpPrElement, 'w', 'leftFromText');\n if (leftFromText !== undefined) floating.leftFromText = leftFromText;\n\n const rightFromText = parseNumericAttribute(tblpPrElement, 'w', 'rightFromText');\n if (rightFromText !== undefined) floating.rightFromText = rightFromText;\n\n if (Object.keys(floating).length === 0) return undefined;\n\n return floating;\n}\n\n// ============================================================================\n// TABLE PROPERTIES PARSING (w:tblPr)\n// ============================================================================\n\n/**\n * Parse table properties (w:tblPr)\n *\n * @param tblPrElement - The w:tblPr element\n * @returns Parsed table formatting\n */\nexport function parseTableProperties(tblPrElement: XmlElement | null): TableFormatting | undefined {\n if (!tblPrElement) return undefined;\n\n const formatting: TableFormatting = {};\n\n // Table width (w:tblW)\n const width = parseWidth(findChild(tblPrElement, 'w', 'tblW'));\n if (width) formatting.width = width;\n\n // Table justification (w:jc)\n const jcElement = findChild(tblPrElement, 'w', 'jc');\n if (jcElement) {\n const jcVal = getAttribute(jcElement, 'w', 'val');\n if (jcVal === 'left' || jcVal === 'center' || jcVal === 'right' || jcVal === 'start') {\n formatting.justification = jcVal === 'start' ? 'left' : jcVal;\n }\n }\n\n // Cell spacing (w:tblCellSpacing)\n const cellSpacing = parseWidth(findChild(tblPrElement, 'w', 'tblCellSpacing'));\n if (cellSpacing) formatting.cellSpacing = cellSpacing;\n\n // Table indent (w:tblInd)\n const indent = parseWidth(findChild(tblPrElement, 'w', 'tblInd'));\n if (indent) formatting.indent = indent;\n\n // Table borders (w:tblBorders)\n const borders = parseTableBorders(findChild(tblPrElement, 'w', 'tblBorders'));\n if (borders) formatting.borders = borders;\n\n // Default cell margins (w:tblCellMar)\n const cellMargins = parseCellMargins(findChild(tblPrElement, 'w', 'tblCellMar'));\n if (cellMargins) formatting.cellMargins = cellMargins;\n\n // Table layout (w:tblLayout)\n const layoutElement = findChild(tblPrElement, 'w', 'tblLayout');\n if (layoutElement) {\n const layoutVal = getAttribute(layoutElement, 'w', 'type');\n if (layoutVal === 'fixed' || layoutVal === 'autofit') {\n formatting.layout = layoutVal;\n }\n }\n\n // Table style (w:tblStyle)\n const styleElement = findChild(tblPrElement, 'w', 'tblStyle');\n if (styleElement) {\n const styleId = getAttribute(styleElement, 'w', 'val');\n if (styleId) formatting.styleId = styleId;\n }\n\n // Table look (w:tblLook)\n const look = parseTableLook(findChild(tblPrElement, 'w', 'tblLook'));\n if (look) formatting.look = look;\n\n // Shading (w:shd)\n const shading = parseShading(findChild(tblPrElement, 'w', 'shd'));\n if (shading) formatting.shading = shading;\n\n // Table overlap (w:tblOverlap)\n const overlapElement = findChild(tblPrElement, 'w', 'tblOverlap');\n if (overlapElement) {\n const overlapVal = getAttribute(overlapElement, 'w', 'val');\n if (overlapVal === 'never' || overlapVal === 'overlap') {\n formatting.overlap = overlapVal;\n }\n }\n\n // Floating table (w:tblpPr)\n const floating = parseFloatingTableProperties(findChild(tblPrElement, 'w', 'tblpPr'));\n if (floating) formatting.floating = floating;\n\n // Bidirectional (w:bidiVisual)\n const bidi = parseBooleanElement(findChild(tblPrElement, 'w', 'bidiVisual'));\n if (bidi) formatting.bidi = true;\n\n if (Object.keys(formatting).length === 0) return undefined;\n\n return formatting;\n}\n\n// ============================================================================\n// TABLE ROW PROPERTIES PARSING (w:trPr)\n// ============================================================================\n\n/**\n * Parse table row properties (w:trPr)\n *\n * @param trPrElement - The w:trPr element\n * @returns Parsed row formatting\n */\nexport function parseTableRowProperties(\n trPrElement: XmlElement | null\n): TableRowFormatting | undefined {\n if (!trPrElement) return undefined;\n\n const formatting: TableRowFormatting = {};\n\n // Row height (w:trHeight)\n const heightElement = findChild(trPrElement, 'w', 'trHeight');\n if (heightElement) {\n const height = parseWidth(heightElement);\n if (height) formatting.height = height;\n\n const hRule = getAttribute(heightElement, 'w', 'hRule');\n if (hRule === 'auto' || hRule === 'atLeast' || hRule === 'exact') {\n formatting.heightRule = hRule;\n }\n }\n\n // Header row (w:tblHeader)\n const header = parseBooleanElement(findChild(trPrElement, 'w', 'tblHeader'));\n if (header) formatting.header = true;\n\n // Can't split (w:cantSplit)\n const cantSplit = parseBooleanElement(findChild(trPrElement, 'w', 'cantSplit'));\n if (cantSplit) formatting.cantSplit = true;\n\n // Row justification (w:jc)\n const jcElement = findChild(trPrElement, 'w', 'jc');\n if (jcElement) {\n const jcVal = getAttribute(jcElement, 'w', 'val');\n if (jcVal === 'left' || jcVal === 'center' || jcVal === 'right') {\n formatting.justification = jcVal;\n }\n }\n\n // Hidden row (w:hidden)\n const hidden = parseBooleanElement(findChild(trPrElement, 'w', 'hidden'));\n if (hidden) formatting.hidden = true;\n\n if (Object.keys(formatting).length === 0) return undefined;\n\n return formatting;\n}\n\n// ============================================================================\n// TABLE CELL PROPERTIES PARSING (w:tcPr)\n// ============================================================================\n\n/**\n * Parse conditional format style (for table style conditional formatting)\n *\n * @param cnfElement - The w:cnfStyle element\n * @returns Parsed conditional format or undefined\n */\nexport function parseConditionalFormatStyle(\n cnfElement: XmlElement | null\n): ConditionalFormatStyle | undefined {\n if (!cnfElement) return undefined;\n\n const style: ConditionalFormatStyle = {};\n\n // Parse individual flags\n const firstRow = getAttribute(cnfElement, 'w', 'firstRow');\n if (firstRow === '1' || firstRow === 'true') style.firstRow = true;\n\n const lastRow = getAttribute(cnfElement, 'w', 'lastRow');\n if (lastRow === '1' || lastRow === 'true') style.lastRow = true;\n\n const firstColumn = getAttribute(cnfElement, 'w', 'firstColumn');\n if (firstColumn === '1' || firstColumn === 'true') style.firstColumn = true;\n\n const lastColumn = getAttribute(cnfElement, 'w', 'lastColumn');\n if (lastColumn === '1' || lastColumn === 'true') style.lastColumn = true;\n\n const oddHBand = getAttribute(cnfElement, 'w', 'oddHBand');\n if (oddHBand === '1' || oddHBand === 'true') style.oddHBand = true;\n\n const evenHBand = getAttribute(cnfElement, 'w', 'evenHBand');\n if (evenHBand === '1' || evenHBand === 'true') style.evenHBand = true;\n\n const oddVBand = getAttribute(cnfElement, 'w', 'oddVBand');\n if (oddVBand === '1' || oddVBand === 'true') style.oddVBand = true;\n\n const evenVBand = getAttribute(cnfElement, 'w', 'evenVBand');\n if (evenVBand === '1' || evenVBand === 'true') style.evenVBand = true;\n\n // Corner cells\n const nwCell = getAttribute(cnfElement, 'w', 'firstRowFirstColumn');\n if (nwCell === '1' || nwCell === 'true') style.nwCell = true;\n\n const neCell = getAttribute(cnfElement, 'w', 'firstRowLastColumn');\n if (neCell === '1' || neCell === 'true') style.neCell = true;\n\n const swCell = getAttribute(cnfElement, 'w', 'lastRowFirstColumn');\n if (swCell === '1' || swCell === 'true') style.swCell = true;\n\n const seCell = getAttribute(cnfElement, 'w', 'lastRowLastColumn');\n if (seCell === '1' || seCell === 'true') style.seCell = true;\n\n // Also check for the val attribute (binary flags string)\n const val = getAttribute(cnfElement, 'w', 'val');\n if (val && val.length === 12) {\n // Binary string format: XXXXXXXXXXXXXX\n // Position meanings from left to right\n if (val[0] === '1') style.firstRow = true;\n if (val[1] === '1') style.lastRow = true;\n if (val[2] === '1') style.firstColumn = true;\n if (val[3] === '1') style.lastColumn = true;\n if (val[4] === '1') style.oddVBand = true;\n if (val[5] === '1') style.evenVBand = true;\n if (val[6] === '1') style.oddHBand = true;\n if (val[7] === '1') style.evenHBand = true;\n if (val[8] === '1') style.nwCell = true;\n if (val[9] === '1') style.neCell = true;\n if (val[10] === '1') style.swCell = true;\n if (val[11] === '1') style.seCell = true;\n }\n\n if (Object.keys(style).length === 0) return undefined;\n\n return style;\n}\n\n/**\n * Parse table cell properties (w:tcPr)\n *\n * @param tcPrElement - The w:tcPr element\n * @returns Parsed cell formatting\n */\nexport function parseTableCellProperties(\n tcPrElement: XmlElement | null\n): TableCellFormatting | undefined {\n if (!tcPrElement) return undefined;\n\n const formatting: TableCellFormatting = {};\n\n // Cell width (w:tcW)\n const width = parseWidth(findChild(tcPrElement, 'w', 'tcW'));\n if (width) formatting.width = width;\n\n // Cell borders (w:tcBorders)\n const borders = parseTableBorders(findChild(tcPrElement, 'w', 'tcBorders'));\n if (borders) formatting.borders = borders;\n\n // Cell margins (w:tcMar)\n const margins = parseCellMargins(findChild(tcPrElement, 'w', 'tcMar'));\n if (margins) formatting.margins = margins;\n\n // Shading (w:shd)\n const shading = parseShading(findChild(tcPrElement, 'w', 'shd'));\n if (shading) formatting.shading = shading;\n\n // Vertical alignment (w:vAlign)\n const vAlignElement = findChild(tcPrElement, 'w', 'vAlign');\n if (vAlignElement) {\n const vAlign = getAttribute(vAlignElement, 'w', 'val');\n if (vAlign === 'top' || vAlign === 'center' || vAlign === 'bottom') {\n formatting.verticalAlign = vAlign;\n }\n }\n\n // Text direction (w:textDirection)\n const textDirElement = findChild(tcPrElement, 'w', 'textDirection');\n if (textDirElement) {\n const textDir = getAttribute(textDirElement, 'w', 'val');\n if (textDir) {\n formatting.textDirection = textDir as TableCellFormatting['textDirection'];\n }\n }\n\n // Grid span (horizontal merge) (w:gridSpan)\n const gridSpanElement = findChild(tcPrElement, 'w', 'gridSpan');\n if (gridSpanElement) {\n const gridSpan = parseNumericAttribute(gridSpanElement, 'w', 'val');\n if (gridSpan !== undefined && gridSpan > 1) {\n formatting.gridSpan = gridSpan;\n }\n }\n\n // Vertical merge (w:vMerge)\n const vMergeElement = findChild(tcPrElement, 'w', 'vMerge');\n if (vMergeElement) {\n const vMergeVal = getAttribute(vMergeElement, 'w', 'val');\n if (vMergeVal === 'restart') {\n formatting.vMerge = 'restart';\n } else {\n // No val attribute or val=\"continue\" means continuation\n formatting.vMerge = 'continue';\n }\n }\n\n // Fit text (w:tcFitText)\n const fitText = parseBooleanElement(findChild(tcPrElement, 'w', 'tcFitText'));\n if (fitText) formatting.fitText = true;\n\n // No wrap (w:noWrap)\n const noWrap = parseBooleanElement(findChild(tcPrElement, 'w', 'noWrap'));\n if (noWrap) formatting.noWrap = true;\n\n // Hide mark (w:hideMark)\n const hideMark = parseBooleanElement(findChild(tcPrElement, 'w', 'hideMark'));\n if (hideMark) formatting.hideMark = true;\n\n // Conditional format style (w:cnfStyle)\n const conditionalFormat = parseConditionalFormatStyle(findChild(tcPrElement, 'w', 'cnfStyle'));\n if (conditionalFormat) formatting.conditionalFormat = conditionalFormat;\n\n if (Object.keys(formatting).length === 0) return undefined;\n\n return formatting;\n}\n\n// ============================================================================\n// CELL CONTENT PARSING\n// ============================================================================\n\n/**\n * Parse table cell content (paragraphs, nested tables)\n *\n * @param tcElement - The w:tc element\n * @param styles - Style definitions\n * @param theme - Theme for color/font resolution\n * @param numbering - Numbering definitions for lists\n * @param rels - Relationships for hyperlinks\n * @param media - Media files for images\n * @returns Array of content blocks\n */\nfunction parseCellContent(\n tcElement: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap | null,\n media: Map<string, MediaFile> | null\n): (Paragraph | Table)[] {\n const content: (Paragraph | Table)[] = [];\n\n // Get all child elements\n const elements = tcElement.elements || [];\n\n for (const child of elements) {\n if (!child.name) continue;\n\n const localName = child.name.split(':').pop();\n\n if (localName === 'p') {\n // Parse paragraph\n const para = parseParagraph(child, styles, theme, numbering, rels, media);\n content.push(para);\n } else if (localName === 'tbl') {\n // Parse nested table (recursive)\n const table = parseTable(child, styles, theme, numbering, rels, media);\n content.push(table);\n }\n // Other content types in cells are rare but could be added\n }\n\n // Ensure at least one empty paragraph (Word requires this)\n if (content.length === 0) {\n content.push({\n type: 'paragraph',\n content: [],\n });\n }\n\n return content;\n}\n\n// ============================================================================\n// TABLE CELL PARSING\n// ============================================================================\n\n/**\n * Parse a table cell (w:tc)\n *\n * @param tcElement - The w:tc element\n * @param styles - Style definitions\n * @param theme - Theme for color/font resolution\n * @param numbering - Numbering definitions for lists\n * @param rels - Relationships for hyperlinks\n * @param media - Media files for images\n * @returns Parsed table cell\n */\nexport function parseTableCell(\n tcElement: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap | null,\n media: Map<string, MediaFile> | null\n): TableCell {\n const cell: TableCell = {\n type: 'tableCell',\n content: [],\n };\n\n // Parse cell properties (w:tcPr)\n const formatting = parseTableCellProperties(findChild(tcElement, 'w', 'tcPr'));\n if (formatting) {\n cell.formatting = formatting;\n }\n\n // Parse content\n cell.content = parseCellContent(tcElement, styles, theme, numbering, rels, media);\n\n return cell;\n}\n\n// ============================================================================\n// TABLE ROW PARSING\n// ============================================================================\n\n/**\n * Parse a table row (w:tr)\n *\n * @param trElement - The w:tr element\n * @param styles - Style definitions\n * @param theme - Theme for color/font resolution\n * @param numbering - Numbering definitions for lists\n * @param rels - Relationships for hyperlinks\n * @param media - Media files for images\n * @returns Parsed table row\n */\nexport function parseTableRow(\n trElement: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap | null,\n media: Map<string, MediaFile> | null\n): TableRow {\n const row: TableRow = {\n type: 'tableRow',\n cells: [],\n };\n\n // Parse row properties (w:trPr)\n const formatting = parseTableRowProperties(findChild(trElement, 'w', 'trPr'));\n if (formatting) {\n row.formatting = formatting;\n }\n\n // Parse cells\n const cells = findChildren(trElement, 'w', 'tc');\n for (const cellElement of cells) {\n const cell = parseTableCell(cellElement, styles, theme, numbering, rels, media);\n row.cells.push(cell);\n }\n\n return row;\n}\n\n// ============================================================================\n// TABLE GRID PARSING\n// ============================================================================\n\n/**\n * Parse table grid (w:tblGrid) for column widths\n *\n * @param tblGridElement - The w:tblGrid element\n * @returns Array of column widths in twips\n */\nexport function parseTableGrid(tblGridElement: XmlElement | null): number[] | undefined {\n if (!tblGridElement) return undefined;\n\n const widths: number[] = [];\n\n const gridCols = findChildren(tblGridElement, 'w', 'gridCol');\n for (const col of gridCols) {\n const width = parseNumericAttribute(col, 'w', 'w') ?? 0;\n widths.push(width);\n }\n\n return widths.length > 0 ? widths : undefined;\n}\n\n// ============================================================================\n// MAIN TABLE PARSING\n// ============================================================================\n\n/**\n * Parse a table element (w:tbl)\n *\n * @param tblElement - The w:tbl element\n * @param styles - Style definitions\n * @param theme - Theme for color/font resolution\n * @param numbering - Numbering definitions for lists\n * @param rels - Relationships for hyperlinks\n * @param media - Media files for images\n * @returns Parsed table\n */\nexport function parseTable(\n tblElement: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap | null,\n media: Map<string, MediaFile> | null\n): Table {\n const table: Table = {\n type: 'table',\n rows: [],\n };\n\n // Parse table properties (w:tblPr)\n const formatting = parseTableProperties(findChild(tblElement, 'w', 'tblPr'));\n if (formatting) {\n table.formatting = formatting;\n }\n\n // Parse table grid (w:tblGrid)\n const columnWidths = parseTableGrid(findChild(tblElement, 'w', 'tblGrid'));\n if (columnWidths) {\n table.columnWidths = columnWidths;\n }\n\n // Parse rows\n const rows = findChildren(tblElement, 'w', 'tr');\n for (const rowElement of rows) {\n const row = parseTableRow(rowElement, styles, theme, numbering, rels, media);\n table.rows.push(row);\n }\n\n return table;\n}\n\n// ============================================================================\n// TABLE UTILITIES\n// ============================================================================\n\n/**\n * Get the number of columns in a table\n *\n * Uses the table grid if available, otherwise counts cells in first row.\n *\n * @param table - The table to measure\n * @returns Number of columns\n */\nexport function getTableColumnCount(table: Table): number {\n if (table.columnWidths && table.columnWidths.length > 0) {\n return table.columnWidths.length;\n }\n\n if (table.rows.length === 0) return 0;\n\n // Count cells in first row, accounting for grid span\n return table.rows[0].cells.reduce((count, cell) => {\n return count + (cell.formatting?.gridSpan ?? 1);\n }, 0);\n}\n\n/**\n * Get the number of rows in a table\n *\n * @param table - The table to measure\n * @returns Number of rows\n */\nexport function getTableRowCount(table: Table): number {\n return table.rows.length;\n}\n\n/**\n * Check if a cell is part of a vertical merge\n *\n * @param cell - The cell to check\n * @returns true if cell continues a vertical merge\n */\nexport function isCellMergeContinuation(cell: TableCell): boolean {\n return cell.formatting?.vMerge === 'continue';\n}\n\n/**\n * Check if a cell starts a vertical merge\n *\n * @param cell - The cell to check\n * @returns true if cell starts a vertical merge\n */\nexport function isCellMergeStart(cell: TableCell): boolean {\n return cell.formatting?.vMerge === 'restart';\n}\n\n/**\n * Check if a cell spans multiple columns\n *\n * @param cell - The cell to check\n * @returns true if cell spans multiple columns\n */\nexport function isCellHorizontallyMerged(cell: TableCell): boolean {\n return (cell.formatting?.gridSpan ?? 1) > 1;\n}\n\n/**\n * Get the plain text content of a table\n *\n * @param table - The table to extract text from\n * @returns Plain text content\n */\nexport function getTableText(table: Table): string {\n const rows: string[] = [];\n\n for (const row of table.rows) {\n const cells: string[] = [];\n\n for (const cell of row.cells) {\n const cellText = cell.content\n .filter((c): c is Paragraph => c.type === 'paragraph')\n .map((p) => getParagraphText(p))\n .join('\\n');\n cells.push(cellText);\n }\n\n rows.push(cells.join('\\t'));\n }\n\n return rows.join('\\n');\n}\n\n/**\n * Helper to get paragraph text (simplified)\n */\nfunction getParagraphText(para: Paragraph): string {\n return para.content\n .filter((c) => 'content' in c)\n .flatMap((run) => {\n if (!('content' in run)) return [];\n return (run as any).content.filter((c: any) => c.type === 'text').map((c: any) => c.text);\n })\n .join('');\n}\n\n/**\n * Check if table has header row\n *\n * @param table - The table to check\n * @returns true if first row is marked as header\n */\nexport function hasHeaderRow(table: Table): boolean {\n if (table.rows.length === 0) return false;\n return table.rows[0].formatting?.header === true;\n}\n\n/**\n * Get all header rows from a table\n *\n * @param table - The table to search\n * @returns Array of header rows\n */\nexport function getHeaderRows(table: Table): TableRow[] {\n return table.rows.filter((row) => row.formatting?.header === true);\n}\n\n/**\n * Check if table is a floating table\n *\n * @param table - The table to check\n * @returns true if table has floating properties\n */\nexport function isFloatingTable(table: Table): boolean {\n return table.formatting?.floating !== undefined;\n}\n","/**\n * Header/Footer Parser - Parse header*.xml and footer*.xml files\n *\n * Headers and footers are stored in separate XML files within the DOCX package:\n * - word/header1.xml, word/header2.xml, etc.\n * - word/footer1.xml, word/footer2.xml, etc.\n *\n * Each header/footer is referenced from document.xml via:\n * - w:sectPr > w:headerReference (type=\"default|first|even\", r:id=\"rIdX\")\n * - w:sectPr > w:footerReference (type=\"default|first|even\", r:id=\"rIdX\")\n *\n * Header/footer types:\n * - default: Used for all pages unless first/even specified\n * - first: Used only for the first page of a section\n * - even: Used for even-numbered pages (when different odd/even enabled)\n *\n * Content structure:\n * - w:hdr or w:ftr root element\n * - Contains w:p (paragraphs) and w:tbl (tables)\n * - Can contain images, shapes, page numbers, etc.\n *\n * OOXML Reference:\n * - Root: w:hdr (header) or w:ftr (footer)\n * - Content: w:p, w:tbl\n */\n\nimport type {\n HeaderFooter,\n HeaderFooterType,\n HeaderReference,\n FooterReference,\n Paragraph,\n Table,\n Theme,\n RelationshipMap,\n MediaFile,\n} from '../types/document';\nimport type { StyleMap } from './styleParser';\nimport type { NumberingMap } from './numberingParser';\nimport { parseXml, findChildren, getAttribute, type XmlElement } from './xmlParser';\nimport { parseParagraph } from './paragraphParser';\nimport { parseTable } from './tableParser';\n\n// ============================================================================\n// HEADER/FOOTER MAP INTERFACE\n// ============================================================================\n\n/**\n * Map of header/footer ID to HeaderFooter content\n */\nexport interface HeaderFooterMap {\n /** Map of rId to HeaderFooter */\n byId: Map<string, HeaderFooter>;\n\n /** Get header/footer by rId */\n get(rId: string): HeaderFooter | undefined;\n\n /** Check if header/footer exists */\n has(rId: string): boolean;\n\n /** Get all headers/footers */\n getAll(): HeaderFooter[];\n\n /** Get by type */\n getByType(type: HeaderFooterType): HeaderFooter | undefined;\n}\n\n// ============================================================================\n// HEADER/FOOTER REFERENCE PARSING\n// ============================================================================\n\n/**\n * Parse header type attribute\n */\nfunction parseHeaderFooterType(typeAttr: string | null): HeaderFooterType {\n switch (typeAttr) {\n case 'first':\n return 'first';\n case 'even':\n return 'even';\n case 'default':\n default:\n return 'default';\n }\n}\n\n/**\n * Parse a header reference from sectPr (w:headerReference)\n *\n * @param element - The w:headerReference element\n * @returns HeaderReference with type and rId\n */\nexport function parseHeaderReference(element: XmlElement): HeaderReference {\n const typeAttr = getAttribute(element, 'w', 'type');\n const rId = getAttribute(element, 'r', 'id') ?? '';\n\n return {\n type: parseHeaderFooterType(typeAttr),\n rId,\n };\n}\n\n/**\n * Parse a footer reference from sectPr (w:footerReference)\n *\n * @param element - The w:footerReference element\n * @returns FooterReference with type and rId\n */\nexport function parseFooterReference(element: XmlElement): FooterReference {\n const typeAttr = getAttribute(element, 'w', 'type');\n const rId = getAttribute(element, 'r', 'id') ?? '';\n\n return {\n type: parseHeaderFooterType(typeAttr),\n rId,\n };\n}\n\n/**\n * Parse all header references from a sectPr element\n *\n * @param sectPr - The w:sectPr element\n * @returns Array of HeaderReference objects\n */\nexport function parseHeaderReferences(sectPr: XmlElement): HeaderReference[] {\n const refs: HeaderReference[] = [];\n const headerRefElements = findChildren(sectPr, 'w', 'headerReference');\n\n for (const el of headerRefElements) {\n refs.push(parseHeaderReference(el));\n }\n\n return refs;\n}\n\n/**\n * Parse all footer references from a sectPr element\n *\n * @param sectPr - The w:sectPr element\n * @returns Array of FooterReference objects\n */\nexport function parseFooterReferences(sectPr: XmlElement): FooterReference[] {\n const refs: FooterReference[] = [];\n const footerRefElements = findChildren(sectPr, 'w', 'footerReference');\n\n for (const el of footerRefElements) {\n refs.push(parseFooterReference(el));\n }\n\n return refs;\n}\n\n// ============================================================================\n// HEADER/FOOTER CONTENT PARSING\n// ============================================================================\n\n/**\n * Parse header/footer content (paragraphs and tables)\n *\n * @param root - Root element (w:hdr or w:ftr)\n * @param styles - Style map for applying styles\n * @param theme - Theme for color resolution\n * @param numbering - Numbering definitions for lists\n * @param rels - Relationships for resolving hyperlinks/images\n * @param media - Media files for images\n * @returns Array of content elements (Paragraph | Table)\n */\nfunction parseHeaderFooterContent(\n root: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap | null,\n media: Map<string, MediaFile> | null\n): (Paragraph | Table)[] {\n const content: (Paragraph | Table)[] = [];\n\n // Get all child elements\n const elements = root.elements ?? [];\n\n for (const el of elements) {\n if (el.type !== 'element') continue;\n\n const name = el.name ?? '';\n\n // Parse paragraphs\n if (name === 'w:p' || name.endsWith(':p')) {\n const paragraph = parseParagraph(el, styles, theme, numbering, rels, media);\n content.push(paragraph);\n }\n // Parse tables\n else if (name === 'w:tbl' || name.endsWith(':tbl')) {\n const table = parseTable(el, styles, theme, numbering, rels, media);\n content.push(table);\n }\n // SDT (structured document tags) can contain paragraphs/tables\n else if (name === 'w:sdt' || name.endsWith(':sdt')) {\n // Find sdtContent\n const sdtContentEl = (el.elements ?? []).find(\n (child: XmlElement) =>\n child.type === 'element' &&\n (child.name === 'w:sdtContent' || child.name?.endsWith(':sdtContent'))\n );\n if (sdtContentEl) {\n // Recursively parse content inside SDT\n const sdtContent = parseHeaderFooterContent(\n sdtContentEl,\n styles,\n theme,\n numbering,\n rels,\n media\n );\n content.push(...sdtContent);\n }\n }\n }\n\n return content;\n}\n\n/**\n * Parse a header XML file (word/header*.xml)\n *\n * @param headerXml - The raw XML content of the header file\n * @param hdrFtrType - The type of header (default, first, even)\n * @param styles - Parsed style map for applying styles\n * @param theme - Parsed theme for color resolution\n * @param numbering - Parsed numbering definitions for lists\n * @param rels - Relationships for resolving hyperlinks/images\n * @param media - Media files for images\n * @returns HeaderFooter object\n */\nexport function parseHeader(\n headerXml: string,\n hdrFtrType: HeaderFooterType = 'default',\n styles: StyleMap | null = null,\n theme: Theme | null = null,\n numbering: NumberingMap | null = null,\n rels: RelationshipMap | null = null,\n media: Map<string, MediaFile> | null = null\n): HeaderFooter {\n const result: HeaderFooter = {\n type: 'header',\n hdrFtrType,\n content: [],\n };\n\n if (!headerXml) {\n return result;\n }\n\n const doc = parseXml(headerXml);\n if (!doc) {\n return result;\n }\n\n // Find the root header element (w:hdr)\n const rootElement = doc.elements?.find(\n (el: XmlElement) => el.type === 'element' && (el.name === 'w:hdr' || el.name?.endsWith(':hdr'))\n ) as XmlElement | undefined;\n\n if (!rootElement) {\n return result;\n }\n\n // Parse content\n result.content = parseHeaderFooterContent(rootElement, styles, theme, numbering, rels, media);\n\n return result;\n}\n\n/**\n * Parse a footer XML file (word/footer*.xml)\n *\n * @param footerXml - The raw XML content of the footer file\n * @param hdrFtrType - The type of footer (default, first, even)\n * @param styles - Parsed style map for applying styles\n * @param theme - Parsed theme for color resolution\n * @param numbering - Parsed numbering definitions for lists\n * @param rels - Relationships for resolving hyperlinks/images\n * @param media - Media files for images\n * @returns HeaderFooter object\n */\nexport function parseFooter(\n footerXml: string,\n hdrFtrType: HeaderFooterType = 'default',\n styles: StyleMap | null = null,\n theme: Theme | null = null,\n numbering: NumberingMap | null = null,\n rels: RelationshipMap | null = null,\n media: Map<string, MediaFile> | null = null\n): HeaderFooter {\n const result: HeaderFooter = {\n type: 'footer',\n hdrFtrType,\n content: [],\n };\n\n if (!footerXml) {\n return result;\n }\n\n const doc = parseXml(footerXml);\n if (!doc) {\n return result;\n }\n\n // Find the root footer element (w:ftr)\n const rootElement = doc.elements?.find(\n (el: XmlElement) => el.type === 'element' && (el.name === 'w:ftr' || el.name?.endsWith(':ftr'))\n ) as XmlElement | undefined;\n\n if (!rootElement) {\n return result;\n }\n\n // Parse content\n result.content = parseHeaderFooterContent(rootElement, styles, theme, numbering, rels, media);\n\n return result;\n}\n\n/**\n * Generic function to parse either header or footer\n *\n * @param xml - Raw XML content\n * @param isHeader - true for header, false for footer\n * @param hdrFtrType - The type (default, first, even)\n * @param styles - Style map\n * @param theme - Theme\n * @param numbering - Numbering definitions\n * @param rels - Relationships\n * @param media - Media files\n * @returns HeaderFooter object\n */\nexport function parseHeaderFooter(\n xml: string,\n isHeader: boolean,\n hdrFtrType: HeaderFooterType = 'default',\n styles: StyleMap | null = null,\n theme: Theme | null = null,\n numbering: NumberingMap | null = null,\n rels: RelationshipMap | null = null,\n media: Map<string, MediaFile> | null = null\n): HeaderFooter {\n if (isHeader) {\n return parseHeader(xml, hdrFtrType, styles, theme, numbering, rels, media);\n } else {\n return parseFooter(xml, hdrFtrType, styles, theme, numbering, rels, media);\n }\n}\n\n// ============================================================================\n// HEADER/FOOTER MAP CREATION\n// ============================================================================\n\n/**\n * Create a HeaderFooterMap from parsed headers/footers\n */\nfunction createHeaderFooterMap(byId: Map<string, HeaderFooter>): HeaderFooterMap {\n return {\n byId,\n\n get(rId: string): HeaderFooter | undefined {\n return byId.get(rId);\n },\n\n has(rId: string): boolean {\n return byId.has(rId);\n },\n\n getAll(): HeaderFooter[] {\n return Array.from(byId.values());\n },\n\n getByType(type: HeaderFooterType): HeaderFooter | undefined {\n for (const hf of byId.values()) {\n if (hf.hdrFtrType === type) {\n return hf;\n }\n }\n return undefined;\n },\n };\n}\n\n/**\n * Create an empty HeaderFooterMap\n */\nexport function createEmptyHeaderFooterMap(): HeaderFooterMap {\n return createHeaderFooterMap(new Map());\n}\n\n/**\n * Build a HeaderFooterMap from references and XML content\n *\n * @param references - Header or footer references from sectPr\n * @param xmlContents - Map of rId to XML content\n * @param isHeader - true for headers, false for footers\n * @param styles - Style map\n * @param theme - Theme\n * @param numbering - Numbering definitions\n * @param rels - Relationships\n * @param media - Media files\n * @returns HeaderFooterMap with all parsed headers/footers\n */\nexport function buildHeaderFooterMap(\n references: (HeaderReference | FooterReference)[],\n xmlContents: Map<string, string>,\n isHeader: boolean,\n styles: StyleMap | null = null,\n theme: Theme | null = null,\n numbering: NumberingMap | null = null,\n rels: RelationshipMap | null = null,\n media: Map<string, MediaFile> | null = null\n): HeaderFooterMap {\n const byId = new Map<string, HeaderFooter>();\n\n for (const ref of references) {\n const xml = xmlContents.get(ref.rId);\n if (xml) {\n const hf = parseHeaderFooter(xml, isHeader, ref.type, styles, theme, numbering, rels, media);\n byId.set(ref.rId, hf);\n }\n }\n\n return createHeaderFooterMap(byId);\n}\n\n// ============================================================================\n// UTILITY FUNCTIONS\n// ============================================================================\n\n/**\n * Get plain text content of a header/footer\n */\nexport function getHeaderFooterText(hf: HeaderFooter): string {\n const texts: string[] = [];\n\n for (const item of hf.content) {\n if (item.type === 'paragraph') {\n const paraTexts: string[] = [];\n for (const content of item.content) {\n if (content.type === 'run') {\n for (const runContent of content.content) {\n if (runContent.type === 'text') {\n paraTexts.push(runContent.text);\n }\n }\n }\n }\n texts.push(paraTexts.join(''));\n } else if (item.type === 'table') {\n // Extract text from table cells\n for (const row of item.rows) {\n for (const cell of row.cells) {\n for (const cellContent of cell.content) {\n if (cellContent.type === 'paragraph') {\n const paraTexts: string[] = [];\n for (const content of cellContent.content) {\n if (content.type === 'run') {\n for (const runContent of content.content) {\n if (runContent.type === 'text') {\n paraTexts.push(runContent.text);\n }\n }\n }\n }\n texts.push(paraTexts.join(''));\n }\n }\n }\n }\n }\n }\n\n return texts.join('\\n');\n}\n\n/**\n * Check if header/footer is empty (no content)\n */\nexport function isEmptyHeaderFooter(hf: HeaderFooter): boolean {\n if (hf.content.length === 0) return true;\n\n // Check if all content is empty paragraphs\n for (const item of hf.content) {\n if (item.type === 'table') return false;\n if (item.type === 'paragraph' && item.content.length > 0) {\n // Check if paragraph has any actual content\n for (const content of item.content) {\n if (content.type !== 'run') return false;\n for (const rc of content.content) {\n if (rc.type === 'text' && rc.text.length > 0) return false;\n if (rc.type !== 'text') return false; // Has image, field, etc.\n }\n }\n }\n }\n\n return true;\n}\n\n/**\n * Check if header/footer has page number field\n */\nexport function hasPageNumberField(hf: HeaderFooter): boolean {\n for (const item of hf.content) {\n if (item.type === 'paragraph') {\n for (const content of item.content) {\n if (content.type === 'simpleField' || content.type === 'complexField') {\n if (content.fieldType === 'PAGE' || content.fieldType === 'NUMPAGES') {\n return true;\n }\n }\n if (content.type === 'run') {\n for (const rc of content.content) {\n if (rc.type === 'fieldChar' && rc.charType === 'begin') {\n // Part of a complex field - would need to check instruction\n // For simplicity, we'll check the field content in the paragraph\n continue;\n }\n }\n }\n }\n }\n }\n return false;\n}\n\n/**\n * Get the header for a given page considering type rules\n *\n * @param headers - Map of type to HeaderFooter\n * @param pageNumber - 1-based page number\n * @param isFirstPage - Whether this is the first page of the section\n * @param hasDifferentFirstPage - Whether different first page is enabled\n * @param hasDifferentOddEven - Whether different odd/even pages is enabled\n * @returns The appropriate HeaderFooter or undefined\n */\nexport function getHeaderForPage(\n headers: Map<HeaderFooterType, HeaderFooter>,\n pageNumber: number,\n isFirstPage: boolean,\n hasDifferentFirstPage: boolean,\n hasDifferentOddEven: boolean\n): HeaderFooter | undefined {\n // First page header takes priority if enabled\n if (isFirstPage && hasDifferentFirstPage) {\n const firstHeader = headers.get('first');\n if (firstHeader) return firstHeader;\n }\n\n // Even page header if enabled and page is even\n if (hasDifferentOddEven && pageNumber % 2 === 0) {\n const evenHeader = headers.get('even');\n if (evenHeader) return evenHeader;\n }\n\n // Default header for everything else\n return headers.get('default');\n}\n\n/**\n * Get the footer for a given page considering type rules\n * (Same logic as getHeaderForPage)\n */\nexport function getFooterForPage(\n footers: Map<HeaderFooterType, HeaderFooter>,\n pageNumber: number,\n isFirstPage: boolean,\n hasDifferentFirstPage: boolean,\n hasDifferentOddEven: boolean\n): HeaderFooter | undefined {\n if (isFirstPage && hasDifferentFirstPage) {\n const firstFooter = footers.get('first');\n if (firstFooter) return firstFooter;\n }\n\n if (hasDifferentOddEven && pageNumber % 2 === 0) {\n const evenFooter = footers.get('even');\n if (evenFooter) return evenFooter;\n }\n\n return footers.get('default');\n}\n\n/**\n * Convert HeaderFooterMap to type-indexed Map\n *\n * @param map - HeaderFooterMap\n * @returns Map indexed by HeaderFooterType\n */\nexport function headerFooterMapToTypeMap(\n map: HeaderFooterMap\n): Map<HeaderFooterType, HeaderFooter> {\n const result = new Map<HeaderFooterType, HeaderFooter>();\n\n for (const hf of map.getAll()) {\n // If there are multiple with same type, later ones overwrite\n result.set(hf.hdrFtrType, hf);\n }\n\n return result;\n}\n\n/**\n * Check if a HeaderFooter contains any images\n */\nexport function hasImages(hf: HeaderFooter): boolean {\n for (const item of hf.content) {\n if (item.type === 'paragraph') {\n for (const content of item.content) {\n if (content.type === 'run') {\n for (const rc of content.content) {\n if (rc.type === 'drawing') return true;\n }\n }\n }\n }\n }\n return false;\n}\n\n/**\n * Check if a HeaderFooter contains any tables\n */\nexport function hasTables(hf: HeaderFooter): boolean {\n for (const item of hf.content) {\n if (item.type === 'table') return true;\n }\n return false;\n}\n","/**\n * Footnote/Endnote Parser - Parse footnotes.xml and endnotes.xml\n *\n * Footnotes and endnotes are stored in separate XML files within the DOCX package:\n * - word/footnotes.xml - Contains all footnote definitions\n * - word/endnotes.xml - Contains all endnote definitions\n *\n * Each note contains:\n * - An ID that matches references in document.xml (w:footnoteReference, w:endnoteReference)\n * - A type (normal, separator, continuationSeparator, continuationNotice)\n * - Content (paragraphs)\n *\n * The references in the document body are parsed by runParser as NoteReferenceContent.\n *\n * OOXML Reference:\n * - Footnote: w:footnote[@w:id][@w:type]\n * - Endnote: w:endnote[@w:id][@w:type]\n * - Content: w:p (paragraphs)\n */\n\nimport type {\n Footnote,\n Endnote,\n FootnoteProperties,\n EndnoteProperties,\n FootnotePosition,\n EndnotePosition,\n NoteNumberRestart,\n NumberFormat,\n Paragraph,\n Theme,\n RelationshipMap,\n MediaFile,\n} from '../types/document';\nimport type { StyleMap } from './styleParser';\nimport type { NumberingMap } from './numberingParser';\nimport {\n parseXml,\n findChild,\n findChildren,\n getAttribute,\n parseNumericAttribute,\n type XmlElement,\n} from './xmlParser';\nimport { parseParagraph } from './paragraphParser';\n\n// ============================================================================\n// FOOTNOTE MAP INTERFACE\n// ============================================================================\n\n/**\n * Footnote map returned by parseFootnotes\n */\nexport interface FootnoteMap {\n /** All footnotes indexed by ID */\n byId: Map<number, Footnote>;\n\n /** Array of all footnotes in document order */\n footnotes: Footnote[];\n\n /** Get footnote by ID */\n getFootnote(id: number): Footnote | undefined;\n\n /** Check if footnote exists */\n hasFootnote(id: number): boolean;\n\n /** Get all normal (non-separator) footnotes */\n getNormalFootnotes(): Footnote[];\n\n /** Get separator footnote if exists */\n getSeparator(): Footnote | undefined;\n\n /** Get continuation separator if exists */\n getContinuationSeparator(): Footnote | undefined;\n}\n\n/**\n * Endnote map returned by parseEndnotes\n */\nexport interface EndnoteMap {\n /** All endnotes indexed by ID */\n byId: Map<number, Endnote>;\n\n /** Array of all endnotes in document order */\n endnotes: Endnote[];\n\n /** Get endnote by ID */\n getEndnote(id: number): Endnote | undefined;\n\n /** Check if endnote exists */\n hasEndnote(id: number): boolean;\n\n /** Get all normal (non-separator) endnotes */\n getNormalEndnotes(): Endnote[];\n\n /** Get separator endnote if exists */\n getSeparator(): Endnote | undefined;\n\n /** Get continuation separator if exists */\n getContinuationSeparator(): Endnote | undefined;\n}\n\n// ============================================================================\n// NOTE TYPE PARSING\n// ============================================================================\n\n/**\n * Parse note type attribute\n */\nfunction parseNoteType(\n typeAttr: string | null\n): 'normal' | 'separator' | 'continuationSeparator' | 'continuationNotice' {\n switch (typeAttr) {\n case 'separator':\n return 'separator';\n case 'continuationSeparator':\n return 'continuationSeparator';\n case 'continuationNotice':\n return 'continuationNotice';\n default:\n return 'normal';\n }\n}\n\n// ============================================================================\n// FOOTNOTE PARSING\n// ============================================================================\n\n/**\n * Parse a single footnote element (w:footnote)\n */\nfunction parseFootnote(\n element: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap | null,\n _media: Map<string, MediaFile> | null\n): Footnote {\n const id = parseNumericAttribute(element, 'w', 'id') ?? 0;\n const typeAttr = getAttribute(element, 'w', 'type');\n const noteType = parseNoteType(typeAttr);\n\n // Parse content paragraphs\n const paragraphElements = findChildren(element, 'w', 'p');\n const content: Paragraph[] = paragraphElements.map((pEl) =>\n parseParagraph(pEl, styles, theme, numbering, rels)\n );\n\n return {\n type: 'footnote',\n id,\n noteType,\n content,\n };\n}\n\n/**\n * Parse footnotes.xml\n *\n * @param footnotesXml - The raw XML content of word/footnotes.xml\n * @param styles - Parsed style map for applying styles\n * @param theme - Parsed theme for color resolution\n * @param numbering - Parsed numbering definitions for lists\n * @param rels - Relationships for resolving hyperlinks\n * @param media - Media files for images\n * @returns FootnoteMap with all footnotes\n */\nexport function parseFootnotes(\n footnotesXml: string | null,\n styles: StyleMap | null = null,\n theme: Theme | null = null,\n numbering: NumberingMap | null = null,\n rels: RelationshipMap | null = null,\n media: Map<string, MediaFile> | null = null\n): FootnoteMap {\n const byId = new Map<number, Footnote>();\n const footnotes: Footnote[] = [];\n\n if (!footnotesXml) {\n return createFootnoteMap(byId, footnotes);\n }\n\n const doc = parseXml(footnotesXml);\n if (!doc) {\n return createFootnoteMap(byId, footnotes);\n }\n\n // Find the root footnotes element\n const rootElement = doc.elements?.find(\n (el: XmlElement) =>\n el.type === 'element' && (el.name === 'w:footnotes' || el.name?.endsWith(':footnotes'))\n ) as XmlElement | undefined;\n\n if (!rootElement) {\n return createFootnoteMap(byId, footnotes);\n }\n\n // Parse all footnote elements\n const footnoteElements = findChildren(rootElement, 'w', 'footnote');\n\n for (const fnEl of footnoteElements) {\n const footnote = parseFootnote(fnEl, styles, theme, numbering, rels, media);\n byId.set(footnote.id, footnote);\n footnotes.push(footnote);\n }\n\n return createFootnoteMap(byId, footnotes);\n}\n\n/**\n * Create FootnoteMap object with helper methods\n */\nfunction createFootnoteMap(byId: Map<number, Footnote>, footnotes: Footnote[]): FootnoteMap {\n return {\n byId,\n footnotes,\n\n getFootnote(id: number): Footnote | undefined {\n return byId.get(id);\n },\n\n hasFootnote(id: number): boolean {\n return byId.has(id);\n },\n\n getNormalFootnotes(): Footnote[] {\n return footnotes.filter((fn) => fn.noteType === 'normal');\n },\n\n getSeparator(): Footnote | undefined {\n return footnotes.find((fn) => fn.noteType === 'separator');\n },\n\n getContinuationSeparator(): Footnote | undefined {\n return footnotes.find((fn) => fn.noteType === 'continuationSeparator');\n },\n };\n}\n\n// ============================================================================\n// ENDNOTE PARSING\n// ============================================================================\n\n/**\n * Parse a single endnote element (w:endnote)\n */\nfunction parseEndnote(\n element: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap | null,\n _media: Map<string, MediaFile> | null\n): Endnote {\n const id = parseNumericAttribute(element, 'w', 'id') ?? 0;\n const typeAttr = getAttribute(element, 'w', 'type');\n const noteType = parseNoteType(typeAttr);\n\n // Parse content paragraphs\n const paragraphElements = findChildren(element, 'w', 'p');\n const content: Paragraph[] = paragraphElements.map((pEl) =>\n parseParagraph(pEl, styles, theme, numbering, rels)\n );\n\n return {\n type: 'endnote',\n id,\n noteType,\n content,\n };\n}\n\n/**\n * Parse endnotes.xml\n *\n * @param endnotesXml - The raw XML content of word/endnotes.xml\n * @param styles - Parsed style map for applying styles\n * @param theme - Parsed theme for color resolution\n * @param numbering - Parsed numbering definitions for lists\n * @param rels - Relationships for resolving hyperlinks\n * @param media - Media files for images\n * @returns EndnoteMap with all endnotes\n */\nexport function parseEndnotes(\n endnotesXml: string | null,\n styles: StyleMap | null = null,\n theme: Theme | null = null,\n numbering: NumberingMap | null = null,\n rels: RelationshipMap | null = null,\n media: Map<string, MediaFile> | null = null\n): EndnoteMap {\n const byId = new Map<number, Endnote>();\n const endnotes: Endnote[] = [];\n\n if (!endnotesXml) {\n return createEndnoteMap(byId, endnotes);\n }\n\n const doc = parseXml(endnotesXml);\n if (!doc) {\n return createEndnoteMap(byId, endnotes);\n }\n\n // Find the root endnotes element\n const rootElement = doc.elements?.find(\n (el: XmlElement) =>\n el.type === 'element' && (el.name === 'w:endnotes' || el.name?.endsWith(':endnotes'))\n ) as XmlElement | undefined;\n\n if (!rootElement) {\n return createEndnoteMap(byId, endnotes);\n }\n\n // Parse all endnote elements\n const endnoteElements = findChildren(rootElement, 'w', 'endnote');\n\n for (const enEl of endnoteElements) {\n const endnote = parseEndnote(enEl, styles, theme, numbering, rels, media);\n byId.set(endnote.id, endnote);\n endnotes.push(endnote);\n }\n\n return createEndnoteMap(byId, endnotes);\n}\n\n/**\n * Create EndnoteMap object with helper methods\n */\nfunction createEndnoteMap(byId: Map<number, Endnote>, endnotes: Endnote[]): EndnoteMap {\n return {\n byId,\n endnotes,\n\n getEndnote(id: number): Endnote | undefined {\n return byId.get(id);\n },\n\n hasEndnote(id: number): boolean {\n return byId.has(id);\n },\n\n getNormalEndnotes(): Endnote[] {\n return endnotes.filter((en) => en.noteType === 'normal');\n },\n\n getSeparator(): Endnote | undefined {\n return endnotes.find((en) => en.noteType === 'separator');\n },\n\n getContinuationSeparator(): Endnote | undefined {\n return endnotes.find((en) => en.noteType === 'continuationSeparator');\n },\n };\n}\n\n// ============================================================================\n// FOOTNOTE/ENDNOTE PROPERTIES PARSING\n// ============================================================================\n\n/**\n * Parse number format from w:numFmt element\n */\nfunction parseNumberFormat(numFmtAttr: string | null): NumberFormat | undefined {\n if (!numFmtAttr) return undefined;\n\n // Map OOXML numFmt values to our NumberFormat type\n const formatMap: Record<string, NumberFormat> = {\n decimal: 'decimal',\n upperRoman: 'upperRoman',\n lowerRoman: 'lowerRoman',\n upperLetter: 'upperLetter',\n lowerLetter: 'lowerLetter',\n ordinal: 'ordinal',\n cardinalText: 'cardinalText',\n ordinalText: 'ordinalText',\n bullet: 'bullet',\n chicago: 'chicago',\n none: 'none',\n };\n\n return formatMap[numFmtAttr] as NumberFormat | undefined;\n}\n\n/**\n * Parse footnote position\n */\nfunction parseFootnotePosition(posAttr: string | null): FootnotePosition | undefined {\n switch (posAttr) {\n case 'pageBottom':\n return 'pageBottom';\n case 'beneathText':\n return 'beneathText';\n case 'sectEnd':\n return 'sectEnd';\n case 'docEnd':\n return 'docEnd';\n default:\n return undefined;\n }\n}\n\n/**\n * Parse endnote position\n */\nfunction parseEndnotePosition(posAttr: string | null): EndnotePosition | undefined {\n switch (posAttr) {\n case 'sectEnd':\n return 'sectEnd';\n case 'docEnd':\n return 'docEnd';\n default:\n return undefined;\n }\n}\n\n/**\n * Parse number restart type\n */\nfunction parseNumberRestart(restartAttr: string | null): NoteNumberRestart | undefined {\n switch (restartAttr) {\n case 'continuous':\n return 'continuous';\n case 'eachSect':\n return 'eachSect';\n case 'eachPage':\n return 'eachPage';\n default:\n return undefined;\n }\n}\n\n/**\n * Parse footnote properties from w:footnotePr element\n * (Can appear in w:sectPr or w:settings)\n */\nexport function parseFootnoteProperties(element: XmlElement | null): FootnoteProperties {\n const props: FootnoteProperties = {};\n\n if (!element) return props;\n\n // Position (w:pos)\n const posEl = findChild(element, 'w', 'pos');\n if (posEl) {\n const posAttr = getAttribute(posEl, 'w', 'val');\n props.position = parseFootnotePosition(posAttr);\n }\n\n // Number format (w:numFmt)\n const numFmtEl = findChild(element, 'w', 'numFmt');\n if (numFmtEl) {\n const fmtAttr = getAttribute(numFmtEl, 'w', 'val');\n props.numFmt = parseNumberFormat(fmtAttr);\n }\n\n // Start number (w:numStart)\n const numStartEl = findChild(element, 'w', 'numStart');\n if (numStartEl) {\n props.numStart = parseNumericAttribute(numStartEl, 'w', 'val') ?? undefined;\n }\n\n // Number restart (w:numRestart)\n const numRestartEl = findChild(element, 'w', 'numRestart');\n if (numRestartEl) {\n const restartAttr = getAttribute(numRestartEl, 'w', 'val');\n props.numRestart = parseNumberRestart(restartAttr);\n }\n\n return props;\n}\n\n/**\n * Parse endnote properties from w:endnotePr element\n * (Can appear in w:sectPr or w:settings)\n */\nexport function parseEndnoteProperties(element: XmlElement | null): EndnoteProperties {\n const props: EndnoteProperties = {};\n\n if (!element) return props;\n\n // Position (w:pos)\n const posEl = findChild(element, 'w', 'pos');\n if (posEl) {\n const posAttr = getAttribute(posEl, 'w', 'val');\n props.position = parseEndnotePosition(posAttr);\n }\n\n // Number format (w:numFmt)\n const numFmtEl = findChild(element, 'w', 'numFmt');\n if (numFmtEl) {\n const fmtAttr = getAttribute(numFmtEl, 'w', 'val');\n props.numFmt = parseNumberFormat(fmtAttr);\n }\n\n // Start number (w:numStart)\n const numStartEl = findChild(element, 'w', 'numStart');\n if (numStartEl) {\n props.numStart = parseNumericAttribute(numStartEl, 'w', 'val') ?? undefined;\n }\n\n // Number restart (w:numRestart)\n const numRestartEl = findChild(element, 'w', 'numRestart');\n if (numRestartEl) {\n const restartAttr = getAttribute(numRestartEl, 'w', 'val');\n props.numRestart = parseNumberRestart(restartAttr);\n }\n\n return props;\n}\n\n// ============================================================================\n// UTILITY FUNCTIONS\n// ============================================================================\n\n/**\n * Get plain text content of a footnote\n */\nexport function getFootnoteText(footnote: Footnote): string {\n // Import getParagraphText dynamically to avoid circular dependency\n const texts: string[] = [];\n\n for (const para of footnote.content) {\n const paraTexts: string[] = [];\n for (const content of para.content) {\n if (content.type === 'run') {\n for (const runContent of content.content) {\n if (runContent.type === 'text') {\n paraTexts.push(runContent.text);\n }\n }\n }\n }\n texts.push(paraTexts.join(''));\n }\n\n return texts.join('\\n');\n}\n\n/**\n * Get plain text content of an endnote\n */\nexport function getEndnoteText(endnote: Endnote): string {\n const texts: string[] = [];\n\n for (const para of endnote.content) {\n const paraTexts: string[] = [];\n for (const content of para.content) {\n if (content.type === 'run') {\n for (const runContent of content.content) {\n if (runContent.type === 'text') {\n paraTexts.push(runContent.text);\n }\n }\n }\n }\n texts.push(paraTexts.join(''));\n }\n\n return texts.join('\\n');\n}\n\n/**\n * Check if a footnote is a separator (not regular content)\n */\nexport function isSeparatorFootnote(footnote: Footnote): boolean {\n return (\n footnote.noteType === 'separator' ||\n footnote.noteType === 'continuationSeparator' ||\n footnote.noteType === 'continuationNotice'\n );\n}\n\n/**\n * Check if an endnote is a separator (not regular content)\n */\nexport function isSeparatorEndnote(endnote: Endnote): boolean {\n return (\n endnote.noteType === 'separator' ||\n endnote.noteType === 'continuationSeparator' ||\n endnote.noteType === 'continuationNotice'\n );\n}\n\n/**\n * Get footnote number for display (excluding separators)\n * @param footnote - The footnote to get the number for\n * @param footnoteMap - The footnote map\n * @param startNumber - Starting number (default 1)\n * @returns The display number, or null for separator footnotes\n */\nexport function getFootnoteDisplayNumber(\n footnote: Footnote,\n footnoteMap: FootnoteMap,\n startNumber: number = 1\n): number | null {\n if (isSeparatorFootnote(footnote)) {\n return null;\n }\n\n const normalFootnotes = footnoteMap.getNormalFootnotes();\n const index = normalFootnotes.findIndex((fn) => fn.id === footnote.id);\n\n if (index === -1) {\n return null;\n }\n\n return startNumber + index;\n}\n\n/**\n * Get endnote number for display (excluding separators)\n * @param endnote - The endnote to get the number for\n * @param endnoteMap - The endnote map\n * @param startNumber - Starting number (default 1)\n * @returns The display number, or null for separator endnotes\n */\nexport function getEndnoteDisplayNumber(\n endnote: Endnote,\n endnoteMap: EndnoteMap,\n startNumber: number = 1\n): number | null {\n if (isSeparatorEndnote(endnote)) {\n return null;\n }\n\n const normalEndnotes = endnoteMap.getNormalEndnotes();\n const index = normalEndnotes.findIndex((en) => en.id === endnote.id);\n\n if (index === -1) {\n return null;\n }\n\n return startNumber + index;\n}\n\n/**\n * Create an empty footnote map\n */\nexport function createEmptyFootnoteMap(): FootnoteMap {\n return createFootnoteMap(new Map(), []);\n}\n\n/**\n * Create an empty endnote map\n */\nexport function createEmptyEndnoteMap(): EndnoteMap {\n return createEndnoteMap(new Map(), []);\n}\n\n/**\n * Merge multiple footnote maps (e.g., from different documents)\n */\nexport function mergeFootnoteMaps(...maps: FootnoteMap[]): FootnoteMap {\n const byId = new Map<number, Footnote>();\n const footnotes: Footnote[] = [];\n\n for (const map of maps) {\n for (const fn of map.footnotes) {\n if (!byId.has(fn.id)) {\n byId.set(fn.id, fn);\n footnotes.push(fn);\n }\n }\n }\n\n return createFootnoteMap(byId, footnotes);\n}\n\n/**\n * Merge multiple endnote maps (e.g., from different documents)\n */\nexport function mergeEndnoteMaps(...maps: EndnoteMap[]): EndnoteMap {\n const byId = new Map<number, Endnote>();\n const endnotes: Endnote[] = [];\n\n for (const map of maps) {\n for (const en of map.endnotes) {\n if (!byId.has(en.id)) {\n byId.set(en.id, en);\n endnotes.push(en);\n }\n }\n }\n\n return createEndnoteMap(byId, endnotes);\n}\n","/**\n * Section Properties Parser - Parse section properties (w:sectPr)\n *\n * Section properties define page layout and settings for a section of the document.\n * They appear in two places:\n * 1. Within a paragraph's properties (w:p/w:pPr/w:sectPr) - marks end of a section\n * 2. At the end of the document body (w:body/w:sectPr) - final section properties\n *\n * OOXML Reference:\n * - w:pgSz: Page size (width, height, orientation)\n * - w:pgMar: Page margins (top, bottom, left, right, header, footer, gutter)\n * - w:cols: Column definitions\n * - w:type: Section start type\n * - w:vAlign: Vertical alignment\n * - w:headerReference, w:footerReference: Header/footer references\n * - w:titlePg: Different first page\n * - w:lnNumType: Line numbering\n * - w:pgBorders: Page borders\n * - w:docGrid: Document grid\n * - w:footnotePr, w:endnotePr: Footnote/endnote properties\n */\n\nimport type {\n SectionProperties,\n PageOrientation,\n SectionStart,\n VerticalAlign,\n LineNumberRestart,\n Column,\n BorderSpec,\n ColorValue,\n ThemeColorSlot,\n RelationshipMap,\n} from '../types/document';\nimport {\n findChild,\n findChildren,\n getAttribute,\n parseNumericAttribute,\n parseBooleanElement,\n type XmlElement,\n} from './xmlParser';\nimport { parseHeaderReference, parseFooterReference } from './headerFooterParser';\nimport { parseFootnoteProperties, parseEndnoteProperties } from './footnoteParser';\n\n// ============================================================================\n// HELPER PARSERS\n// ============================================================================\n\n/**\n * Parse a color element/attribute for page borders/background\n */\nfunction parseColorValue(\n colorStr: string | null,\n themeColor: string | null,\n themeTint: string | null,\n themeShade: string | null\n): ColorValue | undefined {\n if (!colorStr && !themeColor) return undefined;\n\n const color: ColorValue = {};\n\n if (colorStr && colorStr !== 'auto') {\n color.rgb = colorStr;\n } else if (colorStr === 'auto') {\n color.auto = true;\n }\n\n if (themeColor) {\n color.themeColor = themeColor as ThemeColorSlot;\n }\n if (themeTint) {\n color.themeTint = themeTint;\n }\n if (themeShade) {\n color.themeShade = themeShade;\n }\n\n return Object.keys(color).length > 0 ? color : undefined;\n}\n\n/**\n * Parse a border element for page borders\n */\nfunction parseBorderSpec(element: XmlElement | null): BorderSpec | undefined {\n if (!element) return undefined;\n\n const styleStr = getAttribute(element, 'w', 'val') ?? 'none';\n const style = styleStr as BorderSpec['style'];\n\n const border: BorderSpec = { style };\n\n // Size in eighths of a point\n const sz = parseNumericAttribute(element, 'w', 'sz');\n if (sz !== undefined) {\n border.size = sz;\n }\n\n // Space from text/page edge in points\n const space = parseNumericAttribute(element, 'w', 'space');\n if (space !== undefined) {\n border.space = space;\n }\n\n // Color\n const colorVal = getAttribute(element, 'w', 'color');\n const themeColor = getAttribute(element, 'w', 'themeColor');\n const themeTint = getAttribute(element, 'w', 'themeTint');\n const themeShade = getAttribute(element, 'w', 'themeShade');\n const color = parseColorValue(colorVal, themeColor, themeTint, themeShade);\n if (color) {\n border.color = color;\n }\n\n // Shadow effect\n const shadow = getAttribute(element, 'w', 'shadow');\n if (shadow === '1' || shadow === 'true') {\n border.shadow = true;\n }\n\n // Frame effect\n const frame = getAttribute(element, 'w', 'frame');\n if (frame === '1' || frame === 'true') {\n border.frame = true;\n }\n\n return border;\n}\n\n/**\n * Parse page orientation\n */\nfunction parseOrientation(orient: string | null): PageOrientation | undefined {\n switch (orient) {\n case 'landscape':\n return 'landscape';\n case 'portrait':\n return 'portrait';\n default:\n return undefined;\n }\n}\n\n/**\n * Parse section start type\n */\nfunction parseSectionStart(type: string | null): SectionStart | undefined {\n switch (type) {\n case 'continuous':\n return 'continuous';\n case 'nextPage':\n return 'nextPage';\n case 'oddPage':\n return 'oddPage';\n case 'evenPage':\n return 'evenPage';\n case 'nextColumn':\n return 'nextColumn';\n default:\n return undefined;\n }\n}\n\n/**\n * Parse vertical alignment\n */\nfunction parseVerticalAlign(align: string | null): VerticalAlign | undefined {\n switch (align) {\n case 'top':\n return 'top';\n case 'center':\n return 'center';\n case 'both':\n return 'both';\n case 'bottom':\n return 'bottom';\n default:\n return undefined;\n }\n}\n\n/**\n * Parse line number restart type\n */\nfunction parseLineNumberRestart(restart: string | null): LineNumberRestart | undefined {\n switch (restart) {\n case 'continuous':\n return 'continuous';\n case 'newPage':\n return 'newPage';\n case 'newSection':\n return 'newSection';\n default:\n return undefined;\n }\n}\n\n// ============================================================================\n// MAIN PARSER\n// ============================================================================\n\n/**\n * Parse section properties (w:sectPr)\n *\n * @param sectPr - The w:sectPr element\n * @param rels - Optional relationships for resolving header/footer references\n * @returns SectionProperties object\n */\nexport function parseSectionProperties(\n sectPr: XmlElement | null,\n _rels?: RelationshipMap | null\n): SectionProperties {\n const props: SectionProperties = {};\n\n if (!sectPr) return props;\n\n // ============================================================================\n // PAGE SIZE (w:pgSz)\n // ============================================================================\n const pgSz = findChild(sectPr, 'w', 'pgSz');\n if (pgSz) {\n // Width in twips\n const w = parseNumericAttribute(pgSz, 'w', 'w');\n if (w !== undefined) {\n props.pageWidth = w;\n }\n\n // Height in twips\n const h = parseNumericAttribute(pgSz, 'w', 'h');\n if (h !== undefined) {\n props.pageHeight = h;\n }\n\n // Orientation\n const orient = getAttribute(pgSz, 'w', 'orient');\n const orientation = parseOrientation(orient);\n if (orientation) {\n props.orientation = orientation;\n }\n }\n\n // ============================================================================\n // PAGE MARGINS (w:pgMar)\n // ============================================================================\n const pgMar = findChild(sectPr, 'w', 'pgMar');\n if (pgMar) {\n // Top margin in twips\n const top = parseNumericAttribute(pgMar, 'w', 'top');\n if (top !== undefined) {\n props.marginTop = top;\n }\n\n // Bottom margin in twips\n const bottom = parseNumericAttribute(pgMar, 'w', 'bottom');\n if (bottom !== undefined) {\n props.marginBottom = bottom;\n }\n\n // Left margin in twips\n const left = parseNumericAttribute(pgMar, 'w', 'left');\n if (left !== undefined) {\n props.marginLeft = left;\n }\n\n // Right margin in twips\n const right = parseNumericAttribute(pgMar, 'w', 'right');\n if (right !== undefined) {\n props.marginRight = right;\n }\n\n // Header distance from top in twips\n const header = parseNumericAttribute(pgMar, 'w', 'header');\n if (header !== undefined) {\n props.headerDistance = header;\n }\n\n // Footer distance from bottom in twips\n const footer = parseNumericAttribute(pgMar, 'w', 'footer');\n if (footer !== undefined) {\n props.footerDistance = footer;\n }\n\n // Gutter margin in twips\n const gutter = parseNumericAttribute(pgMar, 'w', 'gutter');\n if (gutter !== undefined) {\n props.gutter = gutter;\n }\n }\n\n // ============================================================================\n // COLUMNS (w:cols)\n // ============================================================================\n const cols = findChild(sectPr, 'w', 'cols');\n if (cols) {\n // Number of columns\n const num = parseNumericAttribute(cols, 'w', 'num');\n if (num !== undefined) {\n props.columnCount = num;\n }\n\n // Space between columns in twips\n const space = parseNumericAttribute(cols, 'w', 'space');\n if (space !== undefined) {\n props.columnSpace = space;\n }\n\n // Equal width\n const equalWidth = getAttribute(cols, 'w', 'equalWidth');\n if (equalWidth === '1' || equalWidth === 'true') {\n props.equalWidth = true;\n } else if (equalWidth === '0' || equalWidth === 'false') {\n props.equalWidth = false;\n }\n\n // Separator line between columns\n const sep = getAttribute(cols, 'w', 'sep');\n if (sep === '1' || sep === 'true') {\n props.separator = true;\n }\n\n // Individual column definitions (w:col)\n const colElements = findChildren(cols, 'w', 'col');\n if (colElements.length > 0) {\n props.columns = [];\n for (const colEl of colElements) {\n const column: Column = {};\n\n const colWidth = parseNumericAttribute(colEl, 'w', 'w');\n if (colWidth !== undefined) {\n column.width = colWidth;\n }\n\n const colSpace = parseNumericAttribute(colEl, 'w', 'space');\n if (colSpace !== undefined) {\n column.space = colSpace;\n }\n\n props.columns.push(column);\n }\n }\n }\n\n // ============================================================================\n // SECTION TYPE (w:type)\n // ============================================================================\n const typeEl = findChild(sectPr, 'w', 'type');\n if (typeEl) {\n const val = getAttribute(typeEl, 'w', 'val');\n const sectionStart = parseSectionStart(val);\n if (sectionStart) {\n props.sectionStart = sectionStart;\n }\n }\n\n // ============================================================================\n // VERTICAL ALIGNMENT (w:vAlign)\n // ============================================================================\n const vAlign = findChild(sectPr, 'w', 'vAlign');\n if (vAlign) {\n const val = getAttribute(vAlign, 'w', 'val');\n const verticalAlign = parseVerticalAlign(val);\n if (verticalAlign) {\n props.verticalAlign = verticalAlign;\n }\n }\n\n // ============================================================================\n // BIDIRECTIONAL (w:bidi)\n // ============================================================================\n const bidi = findChild(sectPr, 'w', 'bidi');\n if (bidi) {\n props.bidi = parseBooleanElement(bidi);\n }\n\n // ============================================================================\n // HEADER REFERENCES (w:headerReference)\n // ============================================================================\n const headerRefs = findChildren(sectPr, 'w', 'headerReference');\n if (headerRefs.length > 0) {\n props.headerReferences = headerRefs.map((el) => parseHeaderReference(el));\n }\n\n // ============================================================================\n // FOOTER REFERENCES (w:footerReference)\n // ============================================================================\n const footerRefs = findChildren(sectPr, 'w', 'footerReference');\n if (footerRefs.length > 0) {\n props.footerReferences = footerRefs.map((el) => parseFooterReference(el));\n }\n\n // ============================================================================\n // TITLE PAGE / DIFFERENT FIRST PAGE (w:titlePg)\n // ============================================================================\n const titlePg = findChild(sectPr, 'w', 'titlePg');\n if (titlePg) {\n props.titlePg = parseBooleanElement(titlePg);\n }\n\n // ============================================================================\n // DIFFERENT ODD/EVEN HEADERS (w:evenAndOddHeaders)\n // Note: This is typically in settings.xml, but can also be in sectPr\n // ============================================================================\n const evenAndOddHeaders = findChild(sectPr, 'w', 'evenAndOddHeaders');\n if (evenAndOddHeaders) {\n props.evenAndOddHeaders = parseBooleanElement(evenAndOddHeaders);\n }\n\n // ============================================================================\n // LINE NUMBERS (w:lnNumType)\n // ============================================================================\n const lnNumType = findChild(sectPr, 'w', 'lnNumType');\n if (lnNumType) {\n props.lineNumbers = {};\n\n const start = parseNumericAttribute(lnNumType, 'w', 'start');\n if (start !== undefined) {\n props.lineNumbers.start = start;\n }\n\n const countBy = parseNumericAttribute(lnNumType, 'w', 'countBy');\n if (countBy !== undefined) {\n props.lineNumbers.countBy = countBy;\n }\n\n const distance = parseNumericAttribute(lnNumType, 'w', 'distance');\n if (distance !== undefined) {\n props.lineNumbers.distance = distance;\n }\n\n const restart = getAttribute(lnNumType, 'w', 'restart');\n const restartValue = parseLineNumberRestart(restart);\n if (restartValue) {\n props.lineNumbers.restart = restartValue;\n }\n }\n\n // ============================================================================\n // PAGE BORDERS (w:pgBorders)\n // ============================================================================\n const pgBorders = findChild(sectPr, 'w', 'pgBorders');\n if (pgBorders) {\n props.pageBorders = {};\n\n // Top border\n const topBorder = parseBorderSpec(findChild(pgBorders, 'w', 'top'));\n if (topBorder) {\n props.pageBorders.top = topBorder;\n }\n\n // Bottom border\n const bottomBorder = parseBorderSpec(findChild(pgBorders, 'w', 'bottom'));\n if (bottomBorder) {\n props.pageBorders.bottom = bottomBorder;\n }\n\n // Left border\n const leftBorder = parseBorderSpec(findChild(pgBorders, 'w', 'left'));\n if (leftBorder) {\n props.pageBorders.left = leftBorder;\n }\n\n // Right border\n const rightBorder = parseBorderSpec(findChild(pgBorders, 'w', 'right'));\n if (rightBorder) {\n props.pageBorders.right = rightBorder;\n }\n\n // Display setting (allPages, firstPage, notFirstPage)\n const display = getAttribute(pgBorders, 'w', 'display');\n if (display === 'allPages' || display === 'firstPage' || display === 'notFirstPage') {\n props.pageBorders.display = display;\n }\n\n // Offset from (page or text)\n const offsetFrom = getAttribute(pgBorders, 'w', 'offsetFrom');\n if (offsetFrom === 'page' || offsetFrom === 'text') {\n props.pageBorders.offsetFrom = offsetFrom;\n }\n\n // Z-order (front or back)\n const zOrder = getAttribute(pgBorders, 'w', 'zOrder');\n if (zOrder === 'front' || zOrder === 'back') {\n props.pageBorders.zOrder = zOrder;\n }\n }\n\n // ============================================================================\n // PAGE BACKGROUND (w:background)\n // Note: Background is usually at document level, but checking here too\n // ============================================================================\n const background = findChild(sectPr, 'w', 'background');\n if (background) {\n props.background = {};\n\n const colorVal = getAttribute(background, 'w', 'color');\n if (colorVal && colorVal !== 'auto') {\n props.background.color = { rgb: colorVal };\n }\n\n const themeColor = getAttribute(background, 'w', 'themeColor');\n if (themeColor) {\n props.background.themeColor = themeColor as ThemeColorSlot;\n }\n\n const themeTint = getAttribute(background, 'w', 'themeTint');\n if (themeTint) {\n props.background.themeTint = themeTint;\n }\n\n const themeShade = getAttribute(background, 'w', 'themeShade');\n if (themeShade) {\n props.background.themeShade = themeShade;\n }\n }\n\n // ============================================================================\n // FOOTNOTE PROPERTIES (w:footnotePr)\n // ============================================================================\n const footnotePr = findChild(sectPr, 'w', 'footnotePr');\n if (footnotePr) {\n const fnProps = parseFootnoteProperties(footnotePr);\n if (Object.keys(fnProps).length > 0) {\n props.footnotePr = fnProps;\n }\n }\n\n // ============================================================================\n // ENDNOTE PROPERTIES (w:endnotePr)\n // ============================================================================\n const endnotePr = findChild(sectPr, 'w', 'endnotePr');\n if (endnotePr) {\n const enProps = parseEndnoteProperties(endnotePr);\n if (Object.keys(enProps).length > 0) {\n props.endnotePr = enProps;\n }\n }\n\n // ============================================================================\n // DOCUMENT GRID (w:docGrid)\n // ============================================================================\n const docGrid = findChild(sectPr, 'w', 'docGrid');\n if (docGrid) {\n props.docGrid = {};\n\n const gridType = getAttribute(docGrid, 'w', 'type');\n if (\n gridType === 'default' ||\n gridType === 'lines' ||\n gridType === 'linesAndChars' ||\n gridType === 'snapToChars'\n ) {\n props.docGrid.type = gridType;\n }\n\n const linePitch = parseNumericAttribute(docGrid, 'w', 'linePitch');\n if (linePitch !== undefined) {\n props.docGrid.linePitch = linePitch;\n }\n\n const charSpace = parseNumericAttribute(docGrid, 'w', 'charSpace');\n if (charSpace !== undefined) {\n props.docGrid.charSpace = charSpace;\n }\n }\n\n // ============================================================================\n // PAPER SOURCE (w:paperSrc)\n // ============================================================================\n const paperSrc = findChild(sectPr, 'w', 'paperSrc');\n if (paperSrc) {\n const first = parseNumericAttribute(paperSrc, 'w', 'first');\n if (first !== undefined) {\n props.paperSrcFirst = first;\n }\n\n const other = parseNumericAttribute(paperSrc, 'w', 'other');\n if (other !== undefined) {\n props.paperSrcOther = other;\n }\n }\n\n return props;\n}\n\n// ============================================================================\n// UTILITY FUNCTIONS\n// ============================================================================\n\n/**\n * Get page width in pixels (96 DPI)\n *\n * @param props - Section properties\n * @param defaultWidth - Default width in twips (default: 12240 = 8.5 inches)\n * @returns Width in pixels\n */\nexport function getPageWidthPixels(props: SectionProperties, defaultWidth: number = 12240): number {\n const twips = props.pageWidth ?? defaultWidth;\n // 1 inch = 1440 twips, 1 inch = 96 pixels at 96 DPI\n return Math.round((twips / 1440) * 96);\n}\n\n/**\n * Get page height in pixels (96 DPI)\n *\n * @param props - Section properties\n * @param defaultHeight - Default height in twips (default: 15840 = 11 inches)\n * @returns Height in pixels\n */\nexport function getPageHeightPixels(\n props: SectionProperties,\n defaultHeight: number = 15840\n): number {\n const twips = props.pageHeight ?? defaultHeight;\n return Math.round((twips / 1440) * 96);\n}\n\n/**\n * Get content width (page width minus margins) in pixels\n *\n * @param props - Section properties\n * @returns Content width in pixels\n */\nexport function getContentWidthPixels(props: SectionProperties): number {\n const pageWidth = props.pageWidth ?? 12240;\n const marginLeft = props.marginLeft ?? 1440; // 1 inch default\n const marginRight = props.marginRight ?? 1440;\n const twips = pageWidth - marginLeft - marginRight;\n return Math.round((twips / 1440) * 96);\n}\n\n/**\n * Get content height (page height minus margins) in pixels\n *\n * @param props - Section properties\n * @returns Content height in pixels\n */\nexport function getContentHeightPixels(props: SectionProperties): number {\n const pageHeight = props.pageHeight ?? 15840;\n const marginTop = props.marginTop ?? 1440;\n const marginBottom = props.marginBottom ?? 1440;\n const twips = pageHeight - marginTop - marginBottom;\n return Math.round((twips / 1440) * 96);\n}\n\n/**\n * Get margins in pixels\n *\n * @param props - Section properties\n * @returns Object with all margins in pixels\n */\nexport function getMarginsPixels(props: SectionProperties): {\n top: number;\n bottom: number;\n left: number;\n right: number;\n header: number;\n footer: number;\n gutter: number;\n} {\n const twipsToPixels = (twips: number | undefined, defaultTwips: number) => {\n return Math.round(((twips ?? defaultTwips) / 1440) * 96);\n };\n\n return {\n top: twipsToPixels(props.marginTop, 1440),\n bottom: twipsToPixels(props.marginBottom, 1440),\n left: twipsToPixels(props.marginLeft, 1440),\n right: twipsToPixels(props.marginRight, 1440),\n header: twipsToPixels(props.headerDistance, 720), // 0.5 inch default\n footer: twipsToPixels(props.footerDistance, 720),\n gutter: twipsToPixels(props.gutter, 0),\n };\n}\n\n/**\n * Check if section has different first page header/footer\n */\nexport function hasDifferentFirstPage(props: SectionProperties): boolean {\n return props.titlePg === true;\n}\n\n/**\n * Check if section has different odd/even page headers/footers\n */\nexport function hasDifferentOddEven(props: SectionProperties): boolean {\n return props.evenAndOddHeaders === true;\n}\n\n/**\n * Get effective column count (minimum 1)\n */\nexport function getColumnCount(props: SectionProperties): number {\n return Math.max(1, props.columnCount ?? 1);\n}\n\n/**\n * Check if section is landscape\n */\nexport function isLandscape(props: SectionProperties): boolean {\n return props.orientation === 'landscape';\n}\n\n/**\n * Check if section has page borders\n */\nexport function hasPageBorders(props: SectionProperties): boolean {\n if (!props.pageBorders) return false;\n return !!(\n props.pageBorders.top ||\n props.pageBorders.bottom ||\n props.pageBorders.left ||\n props.pageBorders.right\n );\n}\n\n/**\n * Check if section has line numbers\n */\nexport function hasLineNumbers(props: SectionProperties): boolean {\n return !!props.lineNumbers;\n}\n\n/**\n * Get default section properties (US Letter size, 1 inch margins)\n */\nexport function getDefaultSectionProperties(): SectionProperties {\n return {\n pageWidth: 12240, // 8.5 inches\n pageHeight: 15840, // 11 inches\n orientation: 'portrait',\n marginTop: 1440, // 1 inch\n marginBottom: 1440,\n marginLeft: 1440,\n marginRight: 1440,\n headerDistance: 720, // 0.5 inch\n footerDistance: 720,\n gutter: 0,\n columnCount: 1,\n columnSpace: 720, // 0.5 inch\n equalWidth: true,\n sectionStart: 'nextPage',\n verticalAlign: 'top',\n };\n}\n\n/**\n * Merge section properties (later values override earlier)\n *\n * @param base - Base properties\n * @param override - Override properties\n * @returns Merged properties\n */\nexport function mergeSectionProperties(\n base: SectionProperties,\n override: SectionProperties\n): SectionProperties {\n const result: SectionProperties = { ...base };\n\n // Simple properties - override if present\n if (override.pageWidth !== undefined) result.pageWidth = override.pageWidth;\n if (override.pageHeight !== undefined) result.pageHeight = override.pageHeight;\n if (override.orientation !== undefined) result.orientation = override.orientation;\n if (override.marginTop !== undefined) result.marginTop = override.marginTop;\n if (override.marginBottom !== undefined) result.marginBottom = override.marginBottom;\n if (override.marginLeft !== undefined) result.marginLeft = override.marginLeft;\n if (override.marginRight !== undefined) result.marginRight = override.marginRight;\n if (override.headerDistance !== undefined) result.headerDistance = override.headerDistance;\n if (override.footerDistance !== undefined) result.footerDistance = override.footerDistance;\n if (override.gutter !== undefined) result.gutter = override.gutter;\n if (override.columnCount !== undefined) result.columnCount = override.columnCount;\n if (override.columnSpace !== undefined) result.columnSpace = override.columnSpace;\n if (override.equalWidth !== undefined) result.equalWidth = override.equalWidth;\n if (override.separator !== undefined) result.separator = override.separator;\n if (override.columns !== undefined) result.columns = override.columns;\n if (override.sectionStart !== undefined) result.sectionStart = override.sectionStart;\n if (override.verticalAlign !== undefined) result.verticalAlign = override.verticalAlign;\n if (override.bidi !== undefined) result.bidi = override.bidi;\n if (override.headerReferences !== undefined) result.headerReferences = override.headerReferences;\n if (override.footerReferences !== undefined) result.footerReferences = override.footerReferences;\n if (override.titlePg !== undefined) result.titlePg = override.titlePg;\n if (override.evenAndOddHeaders !== undefined)\n result.evenAndOddHeaders = override.evenAndOddHeaders;\n if (override.lineNumbers !== undefined) result.lineNumbers = override.lineNumbers;\n if (override.pageBorders !== undefined) result.pageBorders = override.pageBorders;\n if (override.background !== undefined) result.background = override.background;\n if (override.footnotePr !== undefined) result.footnotePr = override.footnotePr;\n if (override.endnotePr !== undefined) result.endnotePr = override.endnotePr;\n if (override.docGrid !== undefined) result.docGrid = override.docGrid;\n if (override.paperSrcFirst !== undefined) result.paperSrcFirst = override.paperSrcFirst;\n if (override.paperSrcOther !== undefined) result.paperSrcOther = override.paperSrcOther;\n\n return result;\n}\n","/**\n * Run Consolidator - Merge consecutive runs with identical formatting\n *\n * DOCX files often contain many small runs with the same formatting,\n * created by Word for various reasons (spell checking, revision tracking,\n * cursor positioning, etc.). This causes:\n * - 252+ tiny <span> elements instead of a few\n * - Poor editing UX (cursor jumps between spans)\n * - Performance issues\n *\n * This module provides utilities to consolidate runs with identical\n * formatting into single runs, reducing fragmentation.\n */\n\nimport type {\n Run,\n RunContent,\n TextContent,\n TextFormatting,\n ParagraphContent,\n Paragraph,\n Hyperlink,\n} from '../types/document';\n\n/**\n * Check if two TextFormatting objects are equivalent\n *\n * Uses deep comparison of all properties to determine if runs\n * can be merged without losing formatting information.\n */\nexport function formattingEquals(\n a: TextFormatting | undefined,\n b: TextFormatting | undefined\n): boolean {\n // Both undefined - equal\n if (!a && !b) return true;\n\n // One undefined - not equal\n if (!a || !b) return false;\n\n // Compare boolean properties\n if (a.bold !== b.bold) return false;\n if (a.boldCs !== b.boldCs) return false;\n if (a.italic !== b.italic) return false;\n if (a.italicCs !== b.italicCs) return false;\n if (a.strike !== b.strike) return false;\n if (a.doubleStrike !== b.doubleStrike) return false;\n if (a.smallCaps !== b.smallCaps) return false;\n if (a.allCaps !== b.allCaps) return false;\n if (a.hidden !== b.hidden) return false;\n if (a.emboss !== b.emboss) return false;\n if (a.imprint !== b.imprint) return false;\n if (a.outline !== b.outline) return false;\n if (a.shadow !== b.shadow) return false;\n if (a.rtl !== b.rtl) return false;\n if (a.cs !== b.cs) return false;\n\n // Compare numeric properties\n if (a.fontSize !== b.fontSize) return false;\n if (a.fontSizeCs !== b.fontSizeCs) return false;\n if (a.spacing !== b.spacing) return false;\n if (a.position !== b.position) return false;\n if (a.scale !== b.scale) return false;\n if (a.kerning !== b.kerning) return false;\n\n // Compare string properties\n if (a.vertAlign !== b.vertAlign) return false;\n if (a.highlight !== b.highlight) return false;\n if (a.effect !== b.effect) return false;\n if (a.emphasisMark !== b.emphasisMark) return false;\n if (a.styleId !== b.styleId) return false;\n\n // Compare underline (object with style and optional color)\n if (!underlineEquals(a.underline, b.underline)) return false;\n\n // Compare color (object with rgb, themeColor, etc.)\n if (!colorEquals(a.color, b.color)) return false;\n\n // Compare shading (object with color, fill, pattern)\n if (!shadingEquals(a.shading, b.shading)) return false;\n\n // Compare fontFamily (complex object with multiple properties)\n if (!fontFamilyEquals(a.fontFamily, b.fontFamily)) return false;\n\n return true;\n}\n\n/**\n * Compare underline settings\n */\nfunction underlineEquals(a: TextFormatting['underline'], b: TextFormatting['underline']): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n\n if (a.style !== b.style) return false;\n return colorEquals(a.color, b.color);\n}\n\n/**\n * Compare color values\n */\nfunction colorEquals(a: TextFormatting['color'], b: TextFormatting['color']): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n\n return (\n a.rgb === b.rgb &&\n a.auto === b.auto &&\n a.themeColor === b.themeColor &&\n a.themeTint === b.themeTint &&\n a.themeShade === b.themeShade\n );\n}\n\n/**\n * Compare shading properties\n */\nfunction shadingEquals(a: TextFormatting['shading'], b: TextFormatting['shading']): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n\n if (a.pattern !== b.pattern) return false;\n if (!colorEquals(a.color, b.color)) return false;\n if (!colorEquals(a.fill, b.fill)) return false;\n\n return true;\n}\n\n/**\n * Compare font family settings\n */\nfunction fontFamilyEquals(\n a: TextFormatting['fontFamily'],\n b: TextFormatting['fontFamily']\n): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n\n return (\n a.ascii === b.ascii &&\n a.hAnsi === b.hAnsi &&\n a.eastAsia === b.eastAsia &&\n a.cs === b.cs &&\n a.asciiTheme === b.asciiTheme &&\n a.hAnsiTheme === b.hAnsiTheme &&\n a.eastAsiaTheme === b.eastAsiaTheme &&\n a.csTheme === b.csTheme\n );\n}\n\n/**\n * Check if a run contains only text content\n * (runs with special content like images, fields, etc. should not be merged)\n */\nexport function isTextOnlyRun(run: Run): boolean {\n return run.content.every(\n (c) => c.type === 'text' || c.type === 'softHyphen' || c.type === 'noBreakHyphen'\n );\n}\n\n/**\n * Check if run content can be merged (simple text types)\n */\nfunction isMergeableContent(content: RunContent): boolean {\n return (\n content.type === 'text' || content.type === 'softHyphen' || content.type === 'noBreakHyphen'\n );\n}\n\n/**\n * Check if a run can be merged with another run\n * Runs with breaks, tabs, images, fields, etc. act as merge boundaries\n */\nexport function canMergeRun(run: Run): boolean {\n // Empty runs can be merged\n if (run.content.length === 0) return true;\n\n // Runs with only text/hyphen content can be merged\n return run.content.every(isMergeableContent);\n}\n\n/**\n * Merge the content of two runs into a single content array\n */\nfunction mergeRunContent(content1: RunContent[], content2: RunContent[]): RunContent[] {\n // Combine all content\n const result: RunContent[] = [];\n\n // Add all from first run\n for (const c of content1) {\n result.push(c);\n }\n\n // Merge text at boundary if possible\n if (\n result.length > 0 &&\n content2.length > 0 &&\n result[result.length - 1].type === 'text' &&\n content2[0].type === 'text'\n ) {\n // Merge the two text nodes\n const lastText = result[result.length - 1] as TextContent;\n const firstText = content2[0] as TextContent;\n\n result[result.length - 1] = {\n type: 'text',\n text: lastText.text + firstText.text,\n preserveSpace: lastText.preserveSpace || firstText.preserveSpace || undefined,\n };\n\n // Add rest of content2\n for (let i = 1; i < content2.length; i++) {\n result.push(content2[i]);\n }\n } else {\n // Just append all of content2\n for (const c of content2) {\n result.push(c);\n }\n }\n\n return result;\n}\n\n/**\n * Consolidate an array of runs by merging consecutive runs with identical formatting\n *\n * @param runs - Array of runs to consolidate\n * @returns Consolidated array with fewer, larger runs\n */\nexport function consolidateRuns(runs: Run[]): Run[] {\n if (runs.length <= 1) return runs;\n\n const result: Run[] = [];\n let current: Run | null = null;\n\n for (const run of runs) {\n // Skip empty runs\n if (run.content.length === 0) continue;\n\n // If no current run, start with this one\n if (current === null) {\n current = { ...run, content: [...run.content] };\n continue;\n }\n\n // Check if we can merge this run with current\n if (\n canMergeRun(current) &&\n canMergeRun(run) &&\n formattingEquals(current.formatting, run.formatting)\n ) {\n // Merge the runs\n current = {\n type: 'run',\n formatting: current.formatting,\n content: mergeRunContent(current.content, run.content),\n };\n } else {\n // Can't merge - save current and start new\n result.push(current);\n current = { ...run, content: [...run.content] };\n }\n }\n\n // Don't forget the last run\n if (current !== null) {\n result.push(current);\n }\n\n return result;\n}\n\n/**\n * Consolidate runs within a paragraph content array\n *\n * This handles the full paragraph structure, consolidating runs while\n * preserving hyperlinks, bookmarks, and fields as merge boundaries.\n */\nexport function consolidateParagraphContent(content: ParagraphContent[]): ParagraphContent[] {\n if (content.length <= 1) return content;\n\n const result: ParagraphContent[] = [];\n const pendingRuns: Run[] = [];\n\n function flushRuns(): void {\n if (pendingRuns.length > 0) {\n const consolidated = consolidateRuns(pendingRuns);\n result.push(...consolidated);\n pendingRuns.length = 0;\n }\n }\n\n for (const item of content) {\n if (item.type === 'run') {\n pendingRuns.push(item);\n } else {\n // Non-run content acts as a merge boundary\n flushRuns();\n\n // Handle hyperlinks - consolidate their internal runs\n if (item.type === 'hyperlink') {\n const hyperlink: Hyperlink = {\n ...item,\n children: consolidateParagraphContent(item.children) as Hyperlink['children'],\n };\n result.push(hyperlink);\n } else {\n result.push(item);\n }\n }\n }\n\n // Flush any remaining runs\n flushRuns();\n\n return result;\n}\n\n/**\n * Consolidate all runs within a paragraph\n *\n * @param paragraph - Paragraph to consolidate\n * @returns New paragraph with consolidated runs\n */\nexport function consolidateParagraph(paragraph: Paragraph): Paragraph {\n if (!paragraph.content || paragraph.content.length === 0) {\n return paragraph;\n }\n\n return {\n ...paragraph,\n content: consolidateParagraphContent(paragraph.content),\n };\n}\n\n/**\n * Get the number of runs in a paragraph (for debugging/metrics)\n */\nexport function countRuns(paragraph: Paragraph): number {\n let count = 0;\n\n function countInContent(content: ParagraphContent[]): void {\n for (const item of content) {\n if (item.type === 'run') {\n count++;\n } else if (item.type === 'hyperlink') {\n countInContent(item.children);\n }\n }\n }\n\n if (paragraph.content) {\n countInContent(paragraph.content);\n }\n\n return count;\n}\n\n/**\n * Calculate the consolidation ratio (reduction in number of runs)\n * Useful for debugging and metrics\n */\nexport function getConsolidationStats(\n originalParagraphs: Paragraph[],\n consolidatedParagraphs: Paragraph[]\n): {\n originalRunCount: number;\n consolidatedRunCount: number;\n reductionPercentage: number;\n} {\n const originalCount = originalParagraphs.reduce((sum, p) => sum + countRuns(p), 0);\n const consolidatedCount = consolidatedParagraphs.reduce((sum, p) => sum + countRuns(p), 0);\n\n const reduction =\n originalCount > 0 ? ((originalCount - consolidatedCount) / originalCount) * 100 : 0;\n\n return {\n originalRunCount: originalCount,\n consolidatedRunCount: consolidatedCount,\n reductionPercentage: Math.round(reduction * 10) / 10,\n };\n}\n","/**\n * Paragraph Parser - Parse paragraphs (w:p) with complete formatting\n *\n * A paragraph is the fundamental block-level element containing text runs,\n * hyperlinks, bookmarks, and fields.\n *\n * OOXML Reference:\n * - Paragraph: w:p\n * - Paragraph properties: w:pPr\n * - Content: runs, hyperlinks, bookmarks, fields\n */\n\nimport type {\n Paragraph,\n ParagraphContent,\n ParagraphFormatting,\n Run,\n Hyperlink,\n BookmarkStart,\n BookmarkEnd,\n SimpleField,\n ComplexField,\n FieldType,\n Theme,\n ColorValue,\n BorderSpec,\n ShadingProperties,\n TabStop,\n TabStopAlignment,\n TabLeader,\n LineSpacingRule,\n ParagraphAlignment,\n RelationshipMap,\n MediaFile,\n} from '../types/document';\nimport type { StyleMap } from './styleParser';\nimport type { NumberingMap } from './numberingParser';\nimport {\n findChild,\n findChildren,\n getAttribute,\n getChildElements,\n parseBooleanElement,\n parseNumericAttribute,\n type XmlElement,\n} from './xmlParser';\nimport { parseRun, parseRunProperties } from './runParser';\nimport { parseHyperlink as parseHyperlinkFromModule } from './hyperlinkParser';\nimport {\n parseBookmarkStart as parseBookmarkStartFromModule,\n parseBookmarkEnd as parseBookmarkEndFromModule,\n} from './bookmarkParser';\nimport { parseSectionProperties } from './sectionParser';\nimport { consolidateParagraphContent } from './runConsolidator';\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Parse color value from attributes\n */\nfunction parseColorValue(\n rgb: string | null,\n themeColor: string | null,\n themeTint: string | null,\n themeShade: string | null\n): ColorValue {\n const color: ColorValue = {};\n\n if (rgb && rgb !== 'auto') {\n color.rgb = rgb;\n } else if (rgb === 'auto') {\n color.auto = true;\n }\n\n if (themeColor) {\n color.themeColor = themeColor as ColorValue['themeColor'];\n }\n\n if (themeTint) {\n color.themeTint = themeTint;\n }\n\n if (themeShade) {\n color.themeShade = themeShade;\n }\n\n return color;\n}\n\n/**\n * Parse shading properties (w:shd)\n */\nfunction parseShadingProperties(shd: XmlElement | null): ShadingProperties | undefined {\n if (!shd) return undefined;\n\n const props: ShadingProperties = {};\n\n const color = getAttribute(shd, 'w', 'color');\n if (color && color !== 'auto') {\n props.color = { rgb: color };\n }\n\n const fill = getAttribute(shd, 'w', 'fill');\n if (fill && fill !== 'auto') {\n props.fill = { rgb: fill };\n }\n\n const themeFill = getAttribute(shd, 'w', 'themeFill');\n if (themeFill) {\n props.fill = props.fill || {};\n props.fill.themeColor = themeFill as ColorValue['themeColor'];\n }\n\n const themeFillTint = getAttribute(shd, 'w', 'themeFillTint');\n if (themeFillTint && props.fill) {\n props.fill.themeTint = themeFillTint;\n }\n\n const themeFillShade = getAttribute(shd, 'w', 'themeFillShade');\n if (themeFillShade && props.fill) {\n props.fill.themeShade = themeFillShade;\n }\n\n const pattern = getAttribute(shd, 'w', 'val');\n if (pattern) {\n props.pattern = pattern as ShadingProperties['pattern'];\n }\n\n return Object.keys(props).length > 0 ? props : undefined;\n}\n\n/**\n * Parse border specification (w:top, w:bottom, w:left, w:right, etc.)\n */\nfunction parseBorderSpec(border: XmlElement | null): BorderSpec | undefined {\n if (!border) return undefined;\n\n const style = getAttribute(border, 'w', 'val');\n if (!style) return undefined;\n\n const spec: BorderSpec = {\n style: style as BorderSpec['style'],\n };\n\n const colorVal = getAttribute(border, 'w', 'color');\n const themeColor = getAttribute(border, 'w', 'themeColor');\n if (colorVal || themeColor) {\n spec.color = parseColorValue(\n colorVal,\n themeColor,\n getAttribute(border, 'w', 'themeTint'),\n getAttribute(border, 'w', 'themeShade')\n );\n }\n\n const sz = parseNumericAttribute(border, 'w', 'sz');\n if (sz !== undefined) spec.size = sz;\n\n const space = parseNumericAttribute(border, 'w', 'space');\n if (space !== undefined) spec.space = space;\n\n const shadowAttr = getAttribute(border, 'w', 'shadow');\n if (shadowAttr) spec.shadow = shadowAttr === '1' || shadowAttr === 'true';\n\n const frame = getAttribute(border, 'w', 'frame');\n if (frame) spec.frame = frame === '1' || frame === 'true';\n\n return spec;\n}\n\n/**\n * Parse tab stops (w:tabs)\n */\nfunction parseTabStops(tabs: XmlElement | null): TabStop[] | undefined {\n if (!tabs) return undefined;\n\n const tabElements = findChildren(tabs, 'w', 'tab');\n if (tabElements.length === 0) return undefined;\n\n const result: TabStop[] = [];\n\n for (const tab of tabElements) {\n const pos = parseNumericAttribute(tab, 'w', 'pos');\n const val = getAttribute(tab, 'w', 'val');\n\n if (pos !== undefined && val) {\n const tabStop: TabStop = {\n position: pos,\n alignment: val as TabStopAlignment,\n };\n\n const leader = getAttribute(tab, 'w', 'leader');\n if (leader) {\n tabStop.leader = leader as TabLeader;\n }\n\n result.push(tabStop);\n }\n }\n\n return result.length > 0 ? result : undefined;\n}\n\n/**\n * Parse frame properties (w:framePr)\n */\nfunction parseFrameProperties(\n framePr: XmlElement | null\n): ParagraphFormatting['frame'] | undefined {\n if (!framePr) return undefined;\n\n const frame: ParagraphFormatting['frame'] = {};\n\n const w = parseNumericAttribute(framePr, 'w', 'w');\n if (w !== undefined) frame.width = w;\n\n const h = parseNumericAttribute(framePr, 'w', 'h');\n if (h !== undefined) frame.height = h;\n\n const hAnchor = getAttribute(framePr, 'w', 'hAnchor');\n if (hAnchor === 'text' || hAnchor === 'margin' || hAnchor === 'page') {\n frame.hAnchor = hAnchor;\n }\n\n const vAnchor = getAttribute(framePr, 'w', 'vAnchor');\n if (vAnchor === 'text' || vAnchor === 'margin' || vAnchor === 'page') {\n frame.vAnchor = vAnchor;\n }\n\n const x = parseNumericAttribute(framePr, 'w', 'x');\n if (x !== undefined) frame.x = x;\n\n const y = parseNumericAttribute(framePr, 'w', 'y');\n if (y !== undefined) frame.y = y;\n\n const xAlign = getAttribute(framePr, 'w', 'xAlign');\n if (xAlign) {\n frame.xAlign = xAlign as NonNullable<ParagraphFormatting['frame']>['xAlign'];\n }\n\n const yAlign = getAttribute(framePr, 'w', 'yAlign');\n if (yAlign) {\n frame.yAlign = yAlign as NonNullable<ParagraphFormatting['frame']>['yAlign'];\n }\n\n const wrap = getAttribute(framePr, 'w', 'wrap');\n if (wrap) {\n frame.wrap = wrap as NonNullable<ParagraphFormatting['frame']>['wrap'];\n }\n\n return Object.keys(frame).length > 0 ? frame : undefined;\n}\n\n// ============================================================================\n// PARAGRAPH PROPERTIES PARSER\n// ============================================================================\n\n/**\n * Parse paragraph formatting properties (w:pPr)\n *\n * Handles ALL pPr properties:\n * - w:jc (alignment: left, center, right, both/justify)\n * - w:spacing (before, after, line, lineRule)\n * - w:ind (left, right, firstLine, hanging)\n * - w:pBdr (paragraph borders: top, bottom, left, right, between)\n * - w:shd (paragraph shading/background)\n * - w:tabs (tab stops with positions and types)\n * - w:keepNext, w:keepLines, w:widowControl, w:pageBreakBefore\n * - w:bidi (right-to-left)\n * - w:numPr (list info)\n * - w:pStyle (style reference)\n * - w:outlineLvl (outline level)\n * - w:framePr (frame properties)\n * - w:rPr (default run properties)\n */\nexport function parseParagraphProperties(\n pPr: XmlElement | null,\n theme: Theme | null,\n styles?: StyleMap\n): ParagraphFormatting | undefined {\n if (!pPr) return undefined;\n\n const formatting: ParagraphFormatting = {};\n\n // === Alignment ===\n const jc = findChild(pPr, 'w', 'jc');\n if (jc) {\n const val = getAttribute(jc, 'w', 'val');\n if (val) {\n formatting.alignment = val as ParagraphAlignment;\n }\n }\n\n // === Bidi (right-to-left) ===\n const bidi = findChild(pPr, 'w', 'bidi');\n if (bidi) {\n formatting.bidi = parseBooleanElement(bidi);\n }\n\n // === Spacing ===\n const spacing = findChild(pPr, 'w', 'spacing');\n if (spacing) {\n const before = parseNumericAttribute(spacing, 'w', 'before');\n if (before !== undefined) formatting.spaceBefore = before;\n\n const after = parseNumericAttribute(spacing, 'w', 'after');\n if (after !== undefined) formatting.spaceAfter = after;\n\n const line = parseNumericAttribute(spacing, 'w', 'line');\n if (line !== undefined) formatting.lineSpacing = line;\n\n const lineRule = getAttribute(spacing, 'w', 'lineRule');\n if (lineRule) {\n formatting.lineSpacingRule = lineRule as LineSpacingRule;\n }\n\n const beforeAuto = getAttribute(spacing, 'w', 'beforeAutospacing');\n if (beforeAuto) {\n formatting.beforeAutospacing = beforeAuto === '1' || beforeAuto === 'true';\n }\n\n const afterAuto = getAttribute(spacing, 'w', 'afterAutospacing');\n if (afterAuto) {\n formatting.afterAutospacing = afterAuto === '1' || afterAuto === 'true';\n }\n }\n\n // === Indentation ===\n const ind = findChild(pPr, 'w', 'ind');\n if (ind) {\n const left = parseNumericAttribute(ind, 'w', 'left');\n if (left !== undefined) formatting.indentLeft = left;\n\n const right = parseNumericAttribute(ind, 'w', 'right');\n if (right !== undefined) formatting.indentRight = right;\n\n const firstLine = parseNumericAttribute(ind, 'w', 'firstLine');\n if (firstLine !== undefined) formatting.indentFirstLine = firstLine;\n\n const hanging = parseNumericAttribute(ind, 'w', 'hanging');\n if (hanging !== undefined) {\n // Hanging indent is stored as negative first line indent\n formatting.indentFirstLine = -hanging;\n formatting.hangingIndent = true;\n }\n\n // Also check for w:start and w:end (alternative attributes)\n const start = parseNumericAttribute(ind, 'w', 'start');\n if (start !== undefined && formatting.indentLeft === undefined) {\n formatting.indentLeft = start;\n }\n\n const end = parseNumericAttribute(ind, 'w', 'end');\n if (end !== undefined && formatting.indentRight === undefined) {\n formatting.indentRight = end;\n }\n }\n\n // === Borders ===\n const pBdr = findChild(pPr, 'w', 'pBdr');\n if (pBdr) {\n const borders: ParagraphFormatting['borders'] = {};\n\n const top = parseBorderSpec(findChild(pBdr, 'w', 'top'));\n if (top) borders.top = top;\n\n const bottom = parseBorderSpec(findChild(pBdr, 'w', 'bottom'));\n if (bottom) borders.bottom = bottom;\n\n const left = parseBorderSpec(findChild(pBdr, 'w', 'left'));\n if (left) borders.left = left;\n\n const right = parseBorderSpec(findChild(pBdr, 'w', 'right'));\n if (right) borders.right = right;\n\n const between = parseBorderSpec(findChild(pBdr, 'w', 'between'));\n if (between) borders.between = between;\n\n const bar = parseBorderSpec(findChild(pBdr, 'w', 'bar'));\n if (bar) borders.bar = bar;\n\n if (Object.keys(borders).length > 0) {\n formatting.borders = borders;\n }\n }\n\n // === Shading ===\n const shd = findChild(pPr, 'w', 'shd');\n if (shd) {\n formatting.shading = parseShadingProperties(shd);\n }\n\n // === Tab Stops ===\n const tabs = findChild(pPr, 'w', 'tabs');\n if (tabs) {\n formatting.tabs = parseTabStops(tabs);\n }\n\n // === Page Break Control ===\n const keepNext = findChild(pPr, 'w', 'keepNext');\n if (keepNext) {\n formatting.keepNext = parseBooleanElement(keepNext);\n }\n\n const keepLines = findChild(pPr, 'w', 'keepLines');\n if (keepLines) {\n formatting.keepLines = parseBooleanElement(keepLines);\n }\n\n const widowControl = findChild(pPr, 'w', 'widowControl');\n if (widowControl) {\n formatting.widowControl = parseBooleanElement(widowControl);\n }\n\n const pageBreakBefore = findChild(pPr, 'w', 'pageBreakBefore');\n if (pageBreakBefore) {\n formatting.pageBreakBefore = parseBooleanElement(pageBreakBefore);\n }\n\n // === Numbering Properties (List Info) ===\n const numPr = findChild(pPr, 'w', 'numPr');\n if (numPr) {\n const numIdEl = findChild(numPr, 'w', 'numId');\n const ilvlEl = findChild(numPr, 'w', 'ilvl');\n\n if (numIdEl || ilvlEl) {\n formatting.numPr = {};\n\n if (numIdEl) {\n const val = parseNumericAttribute(numIdEl, 'w', 'val');\n if (val !== undefined) formatting.numPr.numId = val;\n }\n\n if (ilvlEl) {\n const val = parseNumericAttribute(ilvlEl, 'w', 'val');\n if (val !== undefined) formatting.numPr.ilvl = val;\n }\n }\n }\n\n // === Outline Level ===\n const outlineLvl = findChild(pPr, 'w', 'outlineLvl');\n if (outlineLvl) {\n const val = parseNumericAttribute(outlineLvl, 'w', 'val');\n if (val !== undefined) formatting.outlineLevel = val;\n }\n\n // === Style Reference ===\n const pStyle = findChild(pPr, 'w', 'pStyle');\n if (pStyle) {\n const val = getAttribute(pStyle, 'w', 'val');\n if (val) formatting.styleId = val;\n }\n\n // === Frame Properties ===\n const framePr = findChild(pPr, 'w', 'framePr');\n if (framePr) {\n formatting.frame = parseFrameProperties(framePr);\n }\n\n // === Suppress Line Numbers ===\n const suppressLineNumbers = findChild(pPr, 'w', 'suppressLineNumbers');\n if (suppressLineNumbers) {\n formatting.suppressLineNumbers = parseBooleanElement(suppressLineNumbers);\n }\n\n // === Suppress Auto Hyphens ===\n const suppressAutoHyphens = findChild(pPr, 'w', 'suppressAutoHyphens');\n if (suppressAutoHyphens) {\n formatting.suppressAutoHyphens = parseBooleanElement(suppressAutoHyphens);\n }\n\n // === Default Run Properties ===\n const rPr = findChild(pPr, 'w', 'rPr');\n if (rPr) {\n formatting.runProperties = parseRunProperties(rPr, theme, styles);\n }\n\n return Object.keys(formatting).length > 0 ? formatting : undefined;\n}\n\n// ============================================================================\n// PARAGRAPH CONTENT PARSERS\n// ============================================================================\n\n/**\n * Get the local name of an element (without namespace prefix)\n */\nfunction getLocalName(name: string | undefined): string {\n if (!name) return '';\n const colonIndex = name.indexOf(':');\n return colonIndex >= 0 ? name.substring(colonIndex + 1) : name;\n}\n\n/**\n * Parse hyperlink element (w:hyperlink)\n *\n * Delegates to hyperlinkParser module which resolves URLs via relationships.\n */\nfunction parseHyperlink(\n node: XmlElement,\n rels: RelationshipMap | null,\n styles: StyleMap | null,\n theme: Theme | null,\n media: Map<string, MediaFile> | null\n): Hyperlink {\n return parseHyperlinkFromModule(node, rels, styles, theme, media);\n}\n\n/**\n * Parse bookmark start (w:bookmarkStart)\n * Delegates to bookmarkParser module.\n */\nfunction parseBookmarkStart(node: XmlElement): BookmarkStart {\n return parseBookmarkStartFromModule(node);\n}\n\n/**\n * Parse bookmark end (w:bookmarkEnd)\n * Delegates to bookmarkParser module.\n */\nfunction parseBookmarkEnd(node: XmlElement): BookmarkEnd {\n return parseBookmarkEndFromModule(node);\n}\n\n/**\n * Parse field type from instruction string\n */\nfunction parseFieldType(instruction: string): FieldType {\n // Extract the field name (first word)\n const match = instruction.trim().match(/^\\\\?([A-Z]+)/i);\n if (!match) return 'UNKNOWN';\n\n const fieldName = match[1].toUpperCase();\n\n const knownFields: FieldType[] = [\n 'PAGE',\n 'NUMPAGES',\n 'NUMWORDS',\n 'NUMCHARS',\n 'DATE',\n 'TIME',\n 'CREATEDATE',\n 'SAVEDATE',\n 'PRINTDATE',\n 'AUTHOR',\n 'TITLE',\n 'SUBJECT',\n 'KEYWORDS',\n 'COMMENTS',\n 'FILENAME',\n 'FILESIZE',\n 'TEMPLATE',\n 'DOCPROPERTY',\n 'DOCVARIABLE',\n 'REF',\n 'PAGEREF',\n 'NOTEREF',\n 'HYPERLINK',\n 'TOC',\n 'TOA',\n 'INDEX',\n 'SEQ',\n 'STYLEREF',\n 'AUTONUM',\n 'AUTONUMLGL',\n 'AUTONUMOUT',\n 'IF',\n 'MERGEFIELD',\n 'NEXT',\n 'NEXTIF',\n 'ASK',\n 'SET',\n 'QUOTE',\n 'INCLUDETEXT',\n 'INCLUDEPICTURE',\n 'SYMBOL',\n 'ADVANCE',\n 'EDITTIME',\n 'REVNUM',\n 'SECTION',\n 'SECTIONPAGES',\n 'USERADDRESS',\n 'USERNAME',\n 'USERINITIALS',\n ];\n\n if (knownFields.includes(fieldName as FieldType)) {\n return fieldName as FieldType;\n }\n\n return 'UNKNOWN';\n}\n\n/**\n * Parse simple field (w:fldSimple)\n */\nfunction parseSimpleField(\n node: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n rels: RelationshipMap | null,\n media: Map<string, MediaFile> | null\n): SimpleField {\n const instruction = getAttribute(node, 'w', 'instr') ?? '';\n const fieldType = parseFieldType(instruction);\n\n const field: SimpleField = {\n type: 'simpleField',\n instruction,\n fieldType,\n content: [],\n };\n\n // Check for fldLock\n const fldLock = getAttribute(node, 'w', 'fldLock');\n if (fldLock === '1' || fldLock === 'true') {\n field.fldLock = true;\n }\n\n // Check for dirty\n const dirty = getAttribute(node, 'w', 'dirty');\n if (dirty === '1' || dirty === 'true') {\n field.dirty = true;\n }\n\n // Parse child runs (the display value)\n const children = getChildElements(node);\n for (const child of children) {\n const localName = getLocalName(child.name);\n if (localName === 'r') {\n field.content.push(parseRun(child, styles, theme, rels, media));\n }\n }\n\n return field;\n}\n\n/**\n * Parse all content within a paragraph\n *\n * Returns the parsed content and any complex fields that span multiple runs\n */\nfunction parseParagraphContents(\n paraElement: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n _numbering: NumberingMap | null,\n rels: RelationshipMap | null,\n media: Map<string, MediaFile> | null\n): ParagraphContent[] {\n const contents: ParagraphContent[] = [];\n const children = getChildElements(paraElement);\n\n // State for tracking complex fields\n let inComplexField = false;\n let complexFieldInstr = '';\n let complexFieldCodeRuns: Run[] = [];\n let complexFieldResultRuns: Run[] = [];\n let afterSeparator = false;\n let complexFieldLock = false;\n let complexFieldDirty = false;\n\n for (const child of children) {\n const localName = getLocalName(child.name);\n\n switch (localName) {\n case 'r': {\n // Check for field characters in this run\n const run = parseRun(child, styles, theme, rels, media);\n\n // Look for field characters\n let hasFieldBegin = false;\n let hasFieldSeparate = false;\n let hasFieldEnd = false;\n let instrText = '';\n\n for (const content of run.content) {\n if (content.type === 'fieldChar') {\n if (content.charType === 'begin') {\n hasFieldBegin = true;\n if (content.fldLock) complexFieldLock = true;\n if (content.dirty) complexFieldDirty = true;\n } else if (content.charType === 'separate') {\n hasFieldSeparate = true;\n } else if (content.charType === 'end') {\n hasFieldEnd = true;\n }\n } else if (content.type === 'instrText') {\n instrText += content.text;\n }\n }\n\n if (hasFieldBegin) {\n // Starting a new complex field\n inComplexField = true;\n afterSeparator = false;\n complexFieldInstr = '';\n complexFieldCodeRuns = [];\n complexFieldResultRuns = [];\n complexFieldLock = false;\n complexFieldDirty = false;\n }\n\n if (inComplexField) {\n if (instrText) {\n complexFieldInstr += instrText;\n }\n\n if (hasFieldSeparate) {\n afterSeparator = true;\n }\n\n if (afterSeparator && !hasFieldEnd) {\n // Add to result runs (excluding the separator run itself)\n if (!hasFieldSeparate) {\n complexFieldResultRuns.push(run);\n }\n } else if (!afterSeparator && !hasFieldBegin) {\n // Add to code runs\n complexFieldCodeRuns.push(run);\n }\n\n if (hasFieldEnd) {\n // Close the complex field\n const complexField: ComplexField = {\n type: 'complexField',\n instruction: complexFieldInstr.trim(),\n fieldType: parseFieldType(complexFieldInstr),\n fieldCode: complexFieldCodeRuns,\n fieldResult: complexFieldResultRuns,\n };\n\n if (complexFieldLock) complexField.fldLock = true;\n if (complexFieldDirty) complexField.dirty = true;\n\n contents.push(complexField);\n inComplexField = false;\n }\n } else {\n // Regular run, not part of a field\n contents.push(run);\n }\n break;\n }\n\n case 'hyperlink':\n contents.push(parseHyperlink(child, rels, styles, theme, media));\n break;\n\n case 'bookmarkStart':\n contents.push(parseBookmarkStart(child));\n break;\n\n case 'bookmarkEnd':\n contents.push(parseBookmarkEnd(child));\n break;\n\n case 'fldSimple':\n contents.push(parseSimpleField(child, styles, theme, rels, media));\n break;\n\n case 'pPr':\n // Already handled separately\n break;\n\n case 'proofErr':\n case 'permStart':\n case 'permEnd':\n case 'customXml':\n // Skip these elements\n break;\n\n case 'sdt': {\n // Structured document tag - extract content from sdtContent\n const sdtContent = (child.elements ?? []).find(\n (el: XmlElement) =>\n el.type === 'element' &&\n (el.name === 'w:sdtContent' || el.name?.endsWith(':sdtContent'))\n );\n if (sdtContent) {\n // Recursively parse the content inside SDT\n const sdtParsed = parseParagraphContents(sdtContent, styles, theme, null, rels, media);\n contents.push(...sdtParsed);\n }\n break;\n }\n\n case 'smartTag':\n case 'del':\n case 'ins':\n case 'moveTo':\n case 'moveFrom':\n // Track changes - skip for now (would need revision mode)\n break;\n\n case 'commentRangeStart':\n case 'commentRangeEnd':\n // Comments - skip for now\n break;\n\n case 'oMath':\n case 'oMathPara':\n // Math content - skip for now (would need math parser)\n break;\n\n default:\n // Unknown element - skip\n break;\n }\n }\n\n return contents;\n}\n\n// ============================================================================\n// MAIN PARAGRAPH PARSER\n// ============================================================================\n\n/**\n * Parse a paragraph element (w:p)\n *\n * @param node - The w:p XML element\n * @param styles - Style map for resolving style references\n * @param theme - Theme for resolving theme colors/fonts\n * @param numbering - Numbering definitions for list info\n * @param rels - Relationship map for resolving hyperlink URLs\n * @param media - Media files map for image data\n * @returns Parsed Paragraph object\n */\nexport function parseParagraph(\n node: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap | null = null,\n media: Map<string, MediaFile> | null = null\n): Paragraph {\n const paragraph: Paragraph = {\n type: 'paragraph',\n content: [],\n };\n\n // Get paragraph ID attributes (Word 2010+ uses these for collaboration)\n const paraId = getAttribute(node, 'w14', 'paraId') ?? getAttribute(node, 'w', 'paraId');\n if (paraId) {\n paragraph.paraId = paraId;\n }\n\n const textId = getAttribute(node, 'w14', 'textId') ?? getAttribute(node, 'w', 'textId');\n if (textId) {\n paragraph.textId = textId;\n }\n\n // Parse paragraph properties (w:pPr)\n const pPr = findChild(node, 'w', 'pPr');\n if (pPr) {\n paragraph.formatting = parseParagraphProperties(pPr, theme, styles ?? undefined);\n\n // Check for section properties within paragraph (marks end of a section)\n const sectPr = findChild(pPr, 'w', 'sectPr');\n if (sectPr) {\n paragraph.sectionProperties = parseSectionProperties(sectPr, rels);\n }\n }\n\n // Parse paragraph contents (runs, hyperlinks, bookmarks, fields)\n const rawContent = parseParagraphContents(node, styles, theme, numbering, rels, media);\n\n // Consolidate consecutive runs with identical formatting\n // This reduces fragmentation (e.g., 252 tiny runs → a few larger runs)\n paragraph.content = consolidateParagraphContent(rawContent);\n\n // Compute list rendering if this is a list item\n if (paragraph.formatting?.numPr && numbering) {\n const { numId, ilvl = 0 } = paragraph.formatting.numPr;\n if (numId !== undefined && numId !== 0) {\n const level = numbering.getLevel(numId, ilvl);\n if (level) {\n paragraph.listRendering = {\n level: ilvl,\n numId,\n marker: level.lvlText,\n isBullet: level.numFmt === 'bullet',\n numFmt: level.numFmt,\n };\n\n // Apply level's paragraph properties (indentation)\n // For list items, the numbering definition's indentation should control\n // the layout, so we override paragraph-level indentation with level's\n if (level.pPr) {\n if (!paragraph.formatting) {\n paragraph.formatting = {};\n }\n // Apply level indent - this overrides any paragraph-level indent\n // since list indentation should come from the numbering definition\n if (level.pPr.indentLeft !== undefined) {\n paragraph.formatting.indentLeft = level.pPr.indentLeft;\n }\n if (level.pPr.indentFirstLine !== undefined) {\n paragraph.formatting.indentFirstLine = level.pPr.indentFirstLine;\n }\n if (level.pPr.hangingIndent !== undefined) {\n paragraph.formatting.hangingIndent = level.pPr.hangingIndent;\n }\n }\n }\n }\n }\n\n return paragraph;\n}\n\n// ============================================================================\n// UTILITY FUNCTIONS\n// ============================================================================\n\n/**\n * Get plain text from a paragraph\n *\n * @param paragraph - Parsed Paragraph object\n * @returns Concatenated text content\n */\nexport function getParagraphText(paragraph: Paragraph): string {\n let text = '';\n\n for (const content of paragraph.content) {\n if (content.type === 'run') {\n for (const runContent of content.content) {\n if (runContent.type === 'text') {\n text += runContent.text;\n } else if (runContent.type === 'tab') {\n text += '\\t';\n } else if (runContent.type === 'break') {\n if (runContent.breakType === 'page') {\n text += '\\f';\n } else {\n text += '\\n';\n }\n }\n }\n } else if (content.type === 'hyperlink') {\n for (const child of content.children) {\n if (child.type === 'run') {\n for (const runContent of child.content) {\n if (runContent.type === 'text') {\n text += runContent.text;\n }\n }\n }\n }\n } else if (content.type === 'simpleField') {\n for (const child of content.content) {\n if (child.type === 'run') {\n for (const runContent of child.content) {\n if (runContent.type === 'text') {\n text += runContent.text;\n }\n }\n }\n }\n } else if (content.type === 'complexField') {\n for (const run of content.fieldResult) {\n for (const runContent of run.content) {\n if (runContent.type === 'text') {\n text += runContent.text;\n }\n }\n }\n }\n }\n\n return text;\n}\n\n/**\n * Check if a paragraph is empty (no visible content)\n *\n * @param paragraph - Parsed Paragraph object\n * @returns true if paragraph has no visible content\n */\nexport function isEmptyParagraph(paragraph: Paragraph): boolean {\n return (\n getParagraphText(paragraph).trim() === '' &&\n !paragraph.content.some(\n (c) =>\n c.type === 'run' && c.content.some((rc) => rc.type === 'drawing' || rc.type === 'shape')\n )\n );\n}\n\n/**\n * Check if a paragraph is a list item\n *\n * @param paragraph - Parsed Paragraph object\n * @returns true if paragraph has numbering properties\n */\nexport function isListItem(paragraph: Paragraph): boolean {\n return (\n paragraph.formatting?.numPr !== undefined &&\n paragraph.formatting.numPr.numId !== undefined &&\n paragraph.formatting.numPr.numId !== 0\n );\n}\n\n/**\n * Get the list level of a paragraph (0-8)\n *\n * @param paragraph - Parsed Paragraph object\n * @returns List level or undefined if not a list item\n */\nexport function getListLevel(paragraph: Paragraph): number | undefined {\n if (!isListItem(paragraph)) return undefined;\n return paragraph.formatting?.numPr?.ilvl ?? 0;\n}\n\n/**\n * Check if paragraph has a specific style\n *\n * @param paragraph - Parsed Paragraph object\n * @param styleId - Style ID to check for\n * @returns true if paragraph has the specified style\n */\nexport function hasStyle(paragraph: Paragraph, styleId: string): boolean {\n return paragraph.formatting?.styleId === styleId;\n}\n\n/**\n * Check if paragraph starts with a template variable {{...}}\n *\n * @param paragraph - Parsed Paragraph object\n * @returns The variable name or null\n */\nexport function getTemplateVariable(paragraph: Paragraph): string | null {\n const text = getParagraphText(paragraph);\n const match = text.match(/\\{\\{([^}]+)\\}\\}/);\n return match ? match[1] : null;\n}\n","/**\n * Document Body Parser - Parse document.xml body content\n *\n * Parses the main document body (w:body) containing paragraphs, tables,\n * and section properties. Also detects template variables {{...}}.\n *\n * OOXML Reference:\n * - Root: w:document\n * - Body: w:body\n * - Content: w:p (paragraphs), w:tbl (tables), w:sdt (structured document tags)\n * - Final section properties: w:body/w:sectPr\n */\n\nimport type {\n DocumentBody,\n BlockContent,\n Section,\n Paragraph,\n Table,\n SectionProperties,\n Theme,\n RelationshipMap,\n MediaFile,\n} from '../types/document';\nimport type { StyleMap } from './styleParser';\nimport type { NumberingMap } from './numberingParser';\nimport { parseXml, findChild, getChildElements, type XmlElement } from './xmlParser';\nimport { parseParagraph, getParagraphText } from './paragraphParser';\nimport { parseTable } from './tableParser';\nimport { parseSectionProperties, getDefaultSectionProperties } from './sectionParser';\n\n// ============================================================================\n// LIST MARKER COMPUTATION\n// ============================================================================\n\n/**\n * Convert Symbol font bullet characters to Unicode equivalents\n *\n * DOCX often uses characters from Symbol, Wingdings, or Webdings fonts\n * that don't render correctly without the font. This maps them to\n * standard Unicode bullets that work with any font.\n */\nfunction convertBulletToUnicode(bulletChar: string): string {\n // If empty or whitespace, use standard bullet\n if (!bulletChar || bulletChar.trim() === '') {\n return '•';\n }\n\n // Get the character code\n const charCode = bulletChar.charCodeAt(0);\n\n // Map common Symbol/Wingdings characters to Unicode\n // Symbol font mappings (often used for bullets)\n const symbolMap: Record<number, string> = {\n // Symbol font\n 0x00b7: '•', // Middle dot → bullet\n 0x006f: '○', // lowercase o → white circle (used in Symbol font)\n 0x00a7: '■', // Section sign → black square (Symbol)\n 0x00fc: '✓', // Checkmark in Symbol/Wingdings\n\n // Wingdings mappings (character codes when Wingdings not available)\n 0x006e: '■', // Wingdings n → black square\n 0x0071: '○', // Wingdings q → white circle\n 0x0075: '◆', // Wingdings u → black diamond\n 0x0076: '❖', // Wingdings v → diamond\n 0x00a8: '✓', // Wingdings checkmark\n 0x00fb: '✓', // Checkmark\n 0x00fe: '✓', // Checkmark variant\n\n // Common control characters that might appear\n 0xf0b7: '•', // Private use area bullet\n 0xf06e: '■', // Private use area square\n 0xf06f: '○', // Private use area circle\n 0xf0a7: '■', // Private use area\n 0xf0fc: '✓', // Private use area checkmark\n\n // Other common bullet-like characters\n 0x2022: '•', // Already a bullet\n 0x25cf: '●', // Black circle\n 0x25cb: '○', // White circle\n 0x25a0: '■', // Black square\n 0x25a1: '□', // White square\n 0x25c6: '◆', // Black diamond\n 0x25c7: '◇', // White diamond\n 0x2013: '–', // En dash\n 0x2014: '—', // Em dash\n 0x003e: '>', // Greater than (used as arrow)\n 0x002d: '-', // Hyphen\n };\n\n // Check if we have a mapping for this character\n if (symbolMap[charCode]) {\n return symbolMap[charCode];\n }\n\n // If it's in the private use area (often Symbol/Wingdings), use bullet\n if (charCode >= 0xe000 && charCode <= 0xf8ff) {\n return '•';\n }\n\n // If it's a control character or non-printable, use bullet\n if (charCode < 32 || (charCode >= 127 && charCode < 160)) {\n return '•';\n }\n\n // Otherwise, use the character as-is (might be a valid Unicode bullet)\n return bulletChar;\n}\n\n/**\n * Compute the actual list marker for a paragraph\n *\n * Replaces %1, %2, etc. in lvlText with actual counter values.\n * Tracks and increments counters as list items are encountered.\n *\n * @param paragraph - The paragraph to compute marker for\n * @param numbering - Numbering definitions\n * @param listCounters - Map tracking counters per numId\n */\nfunction computeListMarker(\n paragraph: Paragraph,\n numbering: NumberingMap | null,\n listCounters: Map<number, number[]>\n): void {\n const listRendering = paragraph.listRendering;\n if (!listRendering || !numbering) return;\n\n const { numId, level } = listRendering;\n if (numId === undefined || numId === 0) return;\n\n // Initialize counters for this numId if not exists\n if (!listCounters.has(numId)) {\n listCounters.set(numId, new Array(9).fill(0)); // Up to 9 levels\n }\n\n const counters = listCounters.get(numId)!;\n\n // Increment counter at current level\n counters[level] = (counters[level] || 0) + 1;\n\n // Reset all deeper level counters when we go to a shallower level\n for (let i = level + 1; i < counters.length; i++) {\n counters[i] = 0;\n }\n\n // Get the lvlText pattern (e.g., \"%1.%2.%3.\")\n const pattern = listRendering.marker;\n\n // For bullet lists, convert Symbol font characters to proper Unicode\n if (listRendering.isBullet) {\n // DOCX often uses Symbol font characters that don't render correctly\n // Map common Symbol font codes to Unicode equivalents\n const bulletChar = pattern || '';\n listRendering.marker = convertBulletToUnicode(bulletChar);\n return;\n }\n\n // Compute the actual marker by replacing %1, %2, etc.\n let computedMarker = pattern;\n\n // Replace %1, %2, etc. with actual counter values\n // Format each level according to its numFmt\n for (let lvl = 0; lvl <= level; lvl++) {\n const placeholder = `%${lvl + 1}`;\n if (computedMarker.includes(placeholder)) {\n const value = counters[lvl];\n const levelInfo = numbering.getLevel(numId, lvl);\n const formatted = formatNumber(value, levelInfo?.numFmt || 'decimal');\n computedMarker = computedMarker.replace(placeholder, formatted);\n }\n }\n\n // Update the marker with the computed value\n listRendering.marker = computedMarker;\n}\n\n/**\n * Format a number according to OOXML number format\n */\nfunction formatNumber(value: number, numFmt: string): string {\n switch (numFmt) {\n case 'decimal':\n case 'decimalZero':\n return String(value);\n case 'lowerLetter':\n return String.fromCharCode(96 + ((value - 1) % 26) + 1); // a, b, c...\n case 'upperLetter':\n return String.fromCharCode(64 + ((value - 1) % 26) + 1); // A, B, C...\n case 'lowerRoman':\n return toRoman(value).toLowerCase();\n case 'upperRoman':\n return toRoman(value);\n case 'bullet':\n return '•';\n default:\n return String(value);\n }\n}\n\n/**\n * Convert number to Roman numerals\n */\nfunction toRoman(num: number): string {\n const romanNumerals: [number, string][] = [\n [1000, 'M'],\n [900, 'CM'],\n [500, 'D'],\n [400, 'CD'],\n [100, 'C'],\n [90, 'XC'],\n [50, 'L'],\n [40, 'XL'],\n [10, 'X'],\n [9, 'IX'],\n [5, 'V'],\n [4, 'IV'],\n [1, 'I'],\n ];\n\n let result = '';\n for (const [value, symbol] of romanNumerals) {\n while (num >= value) {\n result += symbol;\n num -= value;\n }\n }\n return result;\n}\n\n// ============================================================================\n// TEMPLATE VARIABLE DETECTION\n// ============================================================================\n\n/**\n * Regular expression to match template variables {{...}}\n */\nconst TEMPLATE_VARIABLE_REGEX = /\\{\\{([^{}]+)\\}\\}/g;\n\n/**\n * Extract template variables from text\n *\n * @param text - Text to search for variables\n * @returns Array of unique variable names (without braces)\n */\nexport function extractTemplateVariables(text: string): string[] {\n const variables: string[] = [];\n let match: RegExpExecArray | null;\n\n // Reset regex state\n TEMPLATE_VARIABLE_REGEX.lastIndex = 0;\n\n while ((match = TEMPLATE_VARIABLE_REGEX.exec(text)) !== null) {\n const varName = match[1].trim();\n if (varName && !variables.includes(varName)) {\n variables.push(varName);\n }\n }\n\n return variables;\n}\n\n/**\n * Extract all template variables from document content\n *\n * @param content - Array of paragraphs and tables\n * @returns Array of unique variable names\n */\nexport function extractAllTemplateVariables(content: BlockContent[]): string[] {\n const variables: string[] = [];\n\n for (const block of content) {\n if (block.type === 'paragraph') {\n const text = getParagraphText(block);\n const vars = extractTemplateVariables(text);\n for (const v of vars) {\n if (!variables.includes(v)) {\n variables.push(v);\n }\n }\n } else if (block.type === 'table') {\n // Recursively check table cells\n const tableVars = extractTableVariables(block);\n for (const v of tableVars) {\n if (!variables.includes(v)) {\n variables.push(v);\n }\n }\n }\n }\n\n return variables;\n}\n\n/**\n * Extract template variables from a table\n */\nfunction extractTableVariables(table: Table): string[] {\n const variables: string[] = [];\n\n for (const row of table.rows) {\n for (const cell of row.cells) {\n for (const cellContent of cell.content) {\n if (cellContent.type === 'paragraph') {\n const text = getParagraphText(cellContent);\n const vars = extractTemplateVariables(text);\n for (const v of vars) {\n if (!variables.includes(v)) {\n variables.push(v);\n }\n }\n } else if (cellContent.type === 'table') {\n // Nested table\n const nestedVars = extractTableVariables(cellContent);\n for (const v of nestedVars) {\n if (!variables.includes(v)) {\n variables.push(v);\n }\n }\n }\n }\n }\n }\n\n return variables;\n}\n\n// ============================================================================\n// CONTENT PARSING\n// ============================================================================\n\n/**\n * Parse block content from an element (body or SDT content)\n *\n * @param parent - Parent element containing content\n * @param styles - Style map\n * @param theme - Theme\n * @param numbering - Numbering definitions\n * @param rels - Relationships\n * @param media - Media files\n * @returns Array of block content (paragraphs, tables)\n */\nfunction parseBlockContent(\n parent: XmlElement,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap | null,\n media: Map<string, MediaFile> | null\n): BlockContent[] {\n const content: BlockContent[] = [];\n const children = getChildElements(parent);\n\n // Track list counters for computing markers\n // Map: numId -> array of counters for each level\n const listCounters = new Map<number, number[]>();\n\n for (const child of children) {\n const name = child.name ?? '';\n\n // Paragraph (w:p)\n if (name === 'w:p' || name.endsWith(':p')) {\n const paragraph = parseParagraph(child, styles, theme, numbering, rels, media);\n // Compute list marker if this is a list item\n computeListMarker(paragraph, numbering, listCounters);\n content.push(paragraph);\n }\n // Table (w:tbl)\n else if (name === 'w:tbl' || name.endsWith(':tbl')) {\n const table = parseTable(child, styles, theme, numbering, rels, media);\n content.push(table);\n }\n // Structured Document Tag (w:sdt) - container for content\n else if (name === 'w:sdt' || name.endsWith(':sdt')) {\n // Find the content element inside SDT\n const sdtContent = (child.elements ?? []).find(\n (el: XmlElement) =>\n el.type === 'element' && (el.name === 'w:sdtContent' || el.name?.endsWith(':sdtContent'))\n );\n if (sdtContent) {\n // Recursively parse content inside SDT\n const sdtBlockContent = parseBlockContent(\n sdtContent,\n styles,\n theme,\n numbering,\n rels,\n media\n );\n content.push(...sdtBlockContent);\n }\n }\n // Section properties (w:sectPr) - handled separately at body level\n // Skip here as we handle it after content parsing\n }\n\n return content;\n}\n\n// ============================================================================\n// SECTION BUILDING\n// ============================================================================\n\n/**\n * Build sections from content based on section properties in paragraphs\n *\n * In OOXML, sections are delimited by:\n * 1. w:pPr/w:sectPr within a paragraph (marks end of a section)\n * 2. w:body/w:sectPr (final section properties)\n *\n * @param content - All block content\n * @param finalSectPr - Final section properties from body\n * @returns Array of sections\n */\nfunction buildSections(\n content: BlockContent[],\n finalSectPr: SectionProperties | undefined\n): Section[] {\n const sections: Section[] = [];\n let currentSectionContent: BlockContent[] = [];\n\n for (const block of content) {\n currentSectionContent.push(block);\n\n // Check if this paragraph ends a section\n if (block.type === 'paragraph' && block.sectionProperties) {\n // This paragraph ends a section\n sections.push({\n properties: block.sectionProperties,\n content: currentSectionContent,\n });\n\n // Start new section\n currentSectionContent = [];\n }\n }\n\n // Add final section with remaining content\n if (currentSectionContent.length > 0 || sections.length === 0) {\n sections.push({\n properties: finalSectPr ?? getDefaultSectionProperties(),\n content: currentSectionContent,\n });\n }\n\n return sections;\n}\n\n// ============================================================================\n// MAIN PARSER\n// ============================================================================\n\n/**\n * Parse document.xml body content\n *\n * @param xml - Raw XML content of document.xml\n * @param styles - Parsed style map\n * @param theme - Parsed theme\n * @param numbering - Parsed numbering definitions\n * @param rels - Document relationships\n * @param media - Media files\n * @returns DocumentBody with content, sections, and template variables\n */\nexport function parseDocumentBody(\n xml: string,\n styles: StyleMap | null = null,\n theme: Theme | null = null,\n numbering: NumberingMap | null = null,\n rels: RelationshipMap | null = null,\n media: Map<string, MediaFile> | null = null\n): DocumentBody {\n const result: DocumentBody = {\n content: [],\n };\n\n if (!xml) {\n return result;\n }\n\n // Parse XML\n const doc = parseXml(xml);\n if (!doc) {\n return result;\n }\n\n // Find root document element (w:document)\n const documentEl = (doc.elements ?? []).find(\n (el: XmlElement) =>\n el.type === 'element' && (el.name === 'w:document' || el.name?.endsWith(':document'))\n );\n if (!documentEl) {\n return result;\n }\n\n // Find body element (w:body)\n const bodyEl = findChild(documentEl, 'w', 'body');\n if (!bodyEl) {\n return result;\n }\n\n // Parse all block content (paragraphs, tables)\n result.content = parseBlockContent(bodyEl, styles, theme, numbering, rels, media);\n\n // Parse final section properties (w:body/w:sectPr)\n const finalSectPr = findChild(bodyEl, 'w', 'sectPr');\n if (finalSectPr) {\n result.finalSectionProperties = parseSectionProperties(finalSectPr, rels);\n }\n\n // Build sections from content\n result.sections = buildSections(result.content, result.finalSectionProperties);\n\n return result;\n}\n\n// ============================================================================\n// UTILITY FUNCTIONS\n// ============================================================================\n\n/**\n * Get all paragraphs from document body (flattened)\n */\nexport function getAllParagraphs(body: DocumentBody): Paragraph[] {\n const paragraphs: Paragraph[] = [];\n\n for (const block of body.content) {\n if (block.type === 'paragraph') {\n paragraphs.push(block);\n } else if (block.type === 'table') {\n // Get paragraphs from table cells\n paragraphs.push(...getTableParagraphs(block));\n }\n }\n\n return paragraphs;\n}\n\n/**\n * Get all paragraphs from a table (recursively)\n */\nfunction getTableParagraphs(table: Table): Paragraph[] {\n const paragraphs: Paragraph[] = [];\n\n for (const row of table.rows) {\n for (const cell of row.cells) {\n for (const content of cell.content) {\n if (content.type === 'paragraph') {\n paragraphs.push(content);\n } else if (content.type === 'table') {\n paragraphs.push(...getTableParagraphs(content));\n }\n }\n }\n }\n\n return paragraphs;\n}\n\n/**\n * Get all tables from document body\n */\nexport function getAllTables(body: DocumentBody): Table[] {\n const tables: Table[] = [];\n\n for (const block of body.content) {\n if (block.type === 'table') {\n tables.push(block);\n // Also get nested tables\n tables.push(...getNestedTables(block));\n }\n }\n\n return tables;\n}\n\n/**\n * Get nested tables from a table (recursively)\n */\nfunction getNestedTables(table: Table): Table[] {\n const tables: Table[] = [];\n\n for (const row of table.rows) {\n for (const cell of row.cells) {\n for (const content of cell.content) {\n if (content.type === 'table') {\n tables.push(content);\n tables.push(...getNestedTables(content));\n }\n }\n }\n }\n\n return tables;\n}\n\n/**\n * Get plain text from entire document body\n */\nexport function getDocumentText(body: DocumentBody): string {\n const lines: string[] = [];\n\n for (const block of body.content) {\n if (block.type === 'paragraph') {\n lines.push(getParagraphText(block));\n } else if (block.type === 'table') {\n lines.push(getTableText(block));\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Get plain text from a table\n */\nfunction getTableText(table: Table): string {\n const lines: string[] = [];\n\n for (const row of table.rows) {\n const rowTexts: string[] = [];\n for (const cell of row.cells) {\n const cellTexts: string[] = [];\n for (const content of cell.content) {\n if (content.type === 'paragraph') {\n cellTexts.push(getParagraphText(content));\n } else if (content.type === 'table') {\n cellTexts.push(getTableText(content));\n }\n }\n rowTexts.push(cellTexts.join('\\n'));\n }\n lines.push(rowTexts.join('\\t'));\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Count total paragraphs in document\n */\nexport function getParagraphCount(body: DocumentBody): number {\n return getAllParagraphs(body).length;\n}\n\n/**\n * Count total words in document (approximate)\n */\nexport function getWordCount(body: DocumentBody): number {\n const text = getDocumentText(body);\n // Simple word counting - split by whitespace\n const words = text.trim().split(/\\s+/);\n return words.length > 0 && words[0] !== '' ? words.length : 0;\n}\n\n/**\n * Count total characters in document\n */\nexport function getCharacterCount(body: DocumentBody): number {\n return getDocumentText(body).length;\n}\n\n/**\n * Get section count\n */\nexport function getSectionCount(body: DocumentBody): number {\n return body.sections?.length ?? 1;\n}\n\n/**\n * Check if document has template variables\n */\nexport function hasTemplateVariables(body: DocumentBody): boolean {\n return extractAllTemplateVariables(body.content).length > 0;\n}\n\n/**\n * Get document outline (first N characters of each paragraph)\n *\n * @param body - Document body\n * @param maxCharsPerPara - Max characters per paragraph (default: 100)\n * @param maxParagraphs - Max paragraphs to include (default: 50)\n * @returns Array of paragraph previews\n */\nexport function getDocumentOutline(\n body: DocumentBody,\n maxCharsPerPara: number = 100,\n maxParagraphs: number = 50\n): string[] {\n const outline: string[] = [];\n const paragraphs = getAllParagraphs(body);\n\n for (let i = 0; i < Math.min(paragraphs.length, maxParagraphs); i++) {\n const text = getParagraphText(paragraphs[i]).trim();\n if (text.length > 0) {\n outline.push(\n text.length > maxCharsPerPara ? text.substring(0, maxCharsPerPara) + '...' : text\n );\n }\n }\n\n return outline;\n}\n","/**\n * Google Fonts Loader\n *\n * Dynamically loads fonts from Google Fonts API with:\n * - Loading state tracking\n * - Duplicate prevention\n * - Callback notifications\n * - Font availability detection\n */\n\n// Track loaded fonts to avoid duplicate requests\nconst loadedFonts = new Set<string>();\n\n// Track fonts currently being loaded\nconst loadingFonts = new Map<string, Promise<boolean>>();\n\n// Callbacks to notify when fonts are loaded\nconst loadCallbacks = new Set<(fonts: string[]) => void>();\n\n// Track overall loading state\nlet isLoadingAny = false;\n\n/**\n * Generate Google Fonts CSS URL for a font family\n *\n * @param fontFamily - The font family name (e.g., \"Roboto\", \"Open Sans\")\n * @param weights - Font weights to load (default: 400, 700)\n * @param styles - Font styles to load (default: normal, italic)\n * @returns Google Fonts CSS URL\n */\nfunction getGoogleFontsUrl(\n fontFamily: string,\n weights: number[] = [400, 700],\n styles: ('normal' | 'italic')[] = ['normal', 'italic']\n): string {\n // Encode font family name for URL\n const encodedFamily = encodeURIComponent(fontFamily);\n\n // Build weight/style combinations\n // Format: ital,wght@0,400;0,700;1,400;1,700\n const combinations: string[] = [];\n\n for (const style of styles) {\n const italVal = style === 'italic' ? 1 : 0;\n for (const weight of weights) {\n combinations.push(`${italVal},${weight}`);\n }\n }\n\n // Sort and join\n combinations.sort();\n const spec = combinations.join(';');\n\n return `https://fonts.googleapis.com/css2?family=${encodedFamily}:ital,wght@${spec}&display=swap`;\n}\n\n/**\n * Load a font from Google Fonts\n *\n * @param fontFamily - The font family name to load\n * @param options - Optional configuration\n * @returns Promise resolving to true if font loaded successfully, false otherwise\n */\nexport async function loadFont(\n fontFamily: string,\n options?: {\n weights?: number[];\n styles?: ('normal' | 'italic')[];\n }\n): Promise<boolean> {\n // Normalize font family name\n const normalizedFamily = fontFamily.trim();\n\n // Already loaded?\n if (loadedFonts.has(normalizedFamily)) {\n return true;\n }\n\n // Currently loading? Return existing promise\n const existingLoad = loadingFonts.get(normalizedFamily);\n if (existingLoad) {\n return existingLoad;\n }\n\n // Create load promise\n const loadPromise = (async (): Promise<boolean> => {\n isLoadingAny = true;\n\n try {\n // Generate Google Fonts URL\n const url = getGoogleFontsUrl(normalizedFamily, options?.weights, options?.styles);\n\n // Create link element\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = url;\n\n // Wait for load or error\n const loaded = await new Promise<boolean>((resolve) => {\n link.onload = () => resolve(true);\n link.onerror = () => resolve(false);\n\n // Append to head\n document.head.appendChild(link);\n\n // Timeout after 5 seconds\n setTimeout(() => resolve(false), 5000);\n });\n\n if (loaded) {\n // Wait a bit for the font to be available\n await waitForFontAvailable(normalizedFamily, 3000);\n\n loadedFonts.add(normalizedFamily);\n\n // Notify callbacks\n notifyCallbacks([normalizedFamily]);\n\n return true;\n }\n\n return false;\n } catch (error) {\n console.warn(`Failed to load font \"${normalizedFamily}\":`, error);\n return false;\n } finally {\n loadingFonts.delete(normalizedFamily);\n\n // Check if still loading any fonts\n if (loadingFonts.size === 0) {\n isLoadingAny = false;\n }\n }\n })();\n\n loadingFonts.set(normalizedFamily, loadPromise);\n return loadPromise;\n}\n\n/**\n * Load multiple fonts from Google Fonts\n *\n * @param families - Array of font family names to load\n * @param options - Optional configuration\n * @returns Promise resolving when all fonts are loaded (or failed)\n */\nexport async function loadFonts(\n families: string[],\n options?: {\n weights?: number[];\n styles?: ('normal' | 'italic')[];\n }\n): Promise<void> {\n // Filter out already loaded fonts\n const toLoad = families.filter((family) => !loadedFonts.has(family.trim()));\n\n if (toLoad.length === 0) {\n return;\n }\n\n // Load all fonts in parallel\n await Promise.all(toLoad.map((family) => loadFont(family, options)));\n}\n\n/**\n * Check if a font is loaded\n *\n * @param fontFamily - The font family name to check\n * @returns true if the font is loaded, false otherwise\n */\nexport function isFontLoaded(fontFamily: string): boolean {\n return loadedFonts.has(fontFamily.trim());\n}\n\n/**\n * Check if any fonts are currently loading\n *\n * @returns true if any fonts are loading, false otherwise\n */\nexport function isLoading(): boolean {\n return isLoadingAny;\n}\n\n/**\n * Get list of all loaded fonts\n *\n * @returns Array of loaded font family names\n */\nexport function getLoadedFonts(): string[] {\n return Array.from(loadedFonts);\n}\n\n/**\n * Register a callback to be notified when fonts are loaded\n *\n * @param callback - Function to call when fonts are loaded\n * @returns Cleanup function to remove the callback\n */\nexport function onFontsLoaded(callback: (fonts: string[]) => void): () => void {\n loadCallbacks.add(callback);\n\n // Return cleanup function\n return () => {\n loadCallbacks.delete(callback);\n };\n}\n\n/**\n * Notify all registered callbacks\n */\nfunction notifyCallbacks(fonts: string[]): void {\n for (const callback of loadCallbacks) {\n try {\n callback(fonts);\n } catch (error) {\n console.warn('Font load callback error:', error);\n }\n }\n}\n\n/**\n * Wait for a font to be available using the CSS Font Loading API\n *\n * @param fontFamily - The font family to wait for\n * @param timeout - Maximum time to wait in milliseconds\n * @returns Promise resolving when font is available or timeout\n */\nasync function waitForFontAvailable(fontFamily: string, timeout: number): Promise<boolean> {\n // Use CSS Font Loading API if available\n if ('fonts' in document) {\n try {\n // Try to wait for the font\n const fontFace = `400 16px \"${fontFamily}\"`;\n await Promise.race([\n document.fonts.load(fontFace),\n new Promise((resolve) => setTimeout(resolve, timeout)),\n ]);\n\n return document.fonts.check(fontFace);\n } catch {\n // Fall through to fallback\n }\n }\n\n // Fallback: just wait a bit\n await new Promise((resolve) => setTimeout(resolve, 100));\n return true;\n}\n\n/**\n * Check if a font is available on the system using canvas measurement\n *\n * This uses the technique of comparing text width with the target font\n * vs a known fallback font. If they differ, the font is available.\n *\n * @param fontFamily - The font family name to check\n * @param fallbackFont - Fallback font to compare against\n * @returns true if font is available, false otherwise\n */\nexport function canRenderFont(fontFamily: string, fallbackFont: string = 'sans-serif'): boolean {\n // Skip if we're not in a browser\n if (typeof document === 'undefined') {\n return false;\n }\n\n const _canRenderFont = (fontName: string, fallback: string): boolean => {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n\n if (!ctx) {\n return false;\n }\n\n ctx.textBaseline = 'top';\n\n const text = 'abcdefghijklmnopqrstuvwxyz0123456789';\n\n // Measure with fallback font only\n ctx.font = `72px ${fallback}`;\n const fallbackWidth = ctx.measureText(text).width;\n\n // Measure with target font (with fallback)\n ctx.font = `72px \"${fontName}\", ${fallback}`;\n const customWidth = ctx.measureText(text).width;\n\n // If widths differ, the custom font was used\n return customWidth !== fallbackWidth;\n };\n\n // Check with primary fallback\n if (_canRenderFont(fontFamily, fallbackFont)) {\n return true;\n }\n\n // Check with opposite fallback (handles edge case where font name\n // matches the browser's default sans-serif or serif)\n const oppositeFallback = fallbackFont === 'sans-serif' ? 'serif' : 'sans-serif';\n return _canRenderFont(fontFamily, oppositeFallback);\n}\n\n/**\n * Load a font from a raw buffer (e.g., embedded in DOCX)\n *\n * @param fontFamily - The font family name\n * @param buffer - Font file buffer (TTF, OTF, WOFF, WOFF2)\n * @param options - Font options\n * @returns Promise resolving when font is loaded\n */\nexport async function loadFontFromBuffer(\n fontFamily: string,\n buffer: ArrayBuffer,\n options?: {\n weight?: number | string;\n style?: 'normal' | 'italic';\n }\n): Promise<boolean> {\n const normalizedFamily = fontFamily.trim();\n\n // Already loaded?\n if (loadedFonts.has(normalizedFamily)) {\n return true;\n }\n\n try {\n // Create blob URL\n const blob = new Blob([buffer], { type: 'font/ttf' });\n const url = URL.createObjectURL(blob);\n\n // Create @font-face CSS\n const style = document.createElement('style');\n style.textContent = `\n @font-face {\n font-family: \"${normalizedFamily}\";\n src: url(${url}) format('truetype');\n font-weight: ${options?.weight ?? 'normal'};\n font-style: ${options?.style ?? 'normal'};\n font-display: swap;\n }\n `;\n\n document.head.appendChild(style);\n\n // Wait for font to be available\n await waitForFontAvailable(normalizedFamily, 3000);\n\n loadedFonts.add(normalizedFamily);\n notifyCallbacks([normalizedFamily]);\n\n return true;\n } catch (error) {\n console.warn(`Failed to load font \"${normalizedFamily}\" from buffer:`, error);\n return false;\n }\n}\n\n/**\n * Mapping from common Office/system fonts to Google Fonts equivalents\n *\n * Google Fonts doesn't have exact matches for many Microsoft fonts,\n * but these are close alternatives that work well for document rendering.\n */\nexport const FONT_MAPPING: Record<string, string> = {\n // Microsoft Office fonts → Google Fonts equivalents\n Calibri: 'Carlito',\n Cambria: 'Caladea',\n Arial: 'Arimo',\n 'Times New Roman': 'Tinos',\n 'Courier New': 'Cousine',\n Garamond: 'EB Garamond',\n 'Book Antiqua': 'EB Garamond',\n Georgia: 'Tinos',\n Verdana: 'Open Sans',\n Tahoma: 'Open Sans',\n 'Trebuchet MS': 'Source Sans Pro',\n 'Century Gothic': 'Poppins',\n 'Franklin Gothic': 'Libre Franklin',\n Palatino: 'EB Garamond',\n 'Palatino Linotype': 'EB Garamond',\n 'Lucida Sans': 'Open Sans',\n 'Segoe UI': 'Open Sans',\n Impact: 'Anton',\n 'Comic Sans MS': 'Comic Neue',\n Consolas: 'Inconsolata',\n 'Lucida Console': 'Inconsolata',\n Monaco: 'Fira Code',\n};\n\n/**\n * Get the Google Fonts equivalent for a font name\n *\n * @param fontName - The original font name from the document\n * @returns The Google Fonts equivalent, or the original name if no mapping exists\n */\nexport function getGoogleFontEquivalent(fontName: string): string {\n const trimmed = fontName.trim();\n return FONT_MAPPING[trimmed] || trimmed;\n}\n\n/**\n * Load a font, automatically mapping to Google Fonts equivalent if needed.\n * If the font needs mapping, also creates a CSS alias so the original font\n * name works in stylesheets.\n *\n * @param fontFamily - The font family name (may be an Office font)\n * @returns Promise resolving to true if font loaded\n */\nexport async function loadFontWithMapping(fontFamily: string): Promise<boolean> {\n const trimmed = fontFamily.trim();\n const googleFont = getGoogleFontEquivalent(trimmed);\n\n // If there's a mapping, fetch the CSS and create alias with original name\n // This avoids loading the font twice (once for Google name, once for alias)\n if (googleFont !== trimmed) {\n await createFontAlias(trimmed, googleFont);\n loadedFonts.add(trimmed);\n loadedFonts.add(googleFont);\n return true;\n }\n\n // No mapping needed, load directly\n return loadFont(googleFont);\n}\n\n/**\n * Create a CSS font-family alias by fetching the Google Fonts CSS and\n * rewriting it to use the original font name.\n *\n * @param originalName - The original font name (e.g., \"Garamond\")\n * @param googleFontName - The Google Font name (e.g., \"EB Garamond\")\n */\nasync function createFontAlias(originalName: string, googleFontName: string): Promise<void> {\n // Create a style element with @font-face that aliases the original name\n const styleId = `font-alias-${originalName.toLowerCase().replace(/\\s+/g, '-')}`;\n\n // Don't create duplicate aliases\n if (document.getElementById(styleId)) {\n return;\n }\n\n try {\n // Fetch the Google Fonts CSS to get the actual font URLs\n const url = getGoogleFontsUrl(googleFontName, [400, 700], ['normal', 'italic']);\n const response = await fetch(url);\n\n if (!response.ok) {\n console.warn(`Failed to fetch Google Fonts CSS for \"${googleFontName}\": ${response.status}`);\n return;\n }\n\n const css = await response.text();\n\n // Replace the Google Font name with the original name in all @font-face rules\n // The CSS contains: font-family: 'EB Garamond';\n // We want: font-family: 'Garamond';\n const regex = new RegExp(`font-family:\\\\s*['\"]${escapeRegExp(googleFontName)}['\"]`, 'gi');\n const aliasedCss = css.replace(regex, `font-family: '${originalName}'`);\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = aliasedCss;\n\n document.head.appendChild(style);\n console.log(`Loaded font: \"${originalName}\" (via ${googleFontName})`);\n } catch (error) {\n console.warn(`Failed to create font alias for \"${originalName}\":`, error);\n }\n}\n\n/**\n * Escape special regex characters in a string\n */\nfunction escapeRegExp(string: string): string {\n return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Load multiple fonts with automatic mapping to Google Fonts equivalents\n *\n * @param families - Array of font family names\n * @returns Promise resolving when all fonts are loaded\n */\nexport async function loadFontsWithMapping(families: string[]): Promise<void> {\n // Remove duplicates\n const uniqueFonts = [...new Set(families.map((f) => f.trim()))];\n // Load each font with mapping (creates aliases for Office → Google font mappings)\n await Promise.all(uniqueFonts.map((family) => loadFontWithMapping(family)));\n}\n\n/**\n * Preload a list of common document fonts\n *\n * This preloads fonts commonly used in DOCX documents that have\n * Google Fonts equivalents.\n */\nexport async function preloadCommonFonts(): Promise<void> {\n const commonFonts = [\n 'Carlito', // Calibri equivalent\n 'Caladea', // Cambria equivalent\n 'Arimo', // Arial equivalent\n 'Tinos', // Times New Roman equivalent\n 'Cousine', // Courier New equivalent\n 'EB Garamond', // Garamond equivalent\n ];\n\n await loadFonts(commonFonts);\n}\n\n/**\n * Extract all font families used in a document\n *\n * Uses loose typing to handle any document-like structure.\n *\n * @param document - The parsed document\n * @returns Set of unique font family names\n */\nexport function extractFontsFromDocument(document: unknown): Set<string> {\n const fonts = new Set<string>();\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const doc = document as any;\n if (!doc?.package) return fonts;\n\n // Extract from document content\n const content = doc.package?.document?.content;\n if (Array.isArray(content)) {\n for (const paragraph of content) {\n if (paragraph?.type === 'paragraph' && Array.isArray(paragraph.content)) {\n for (const run of paragraph.content) {\n if (run?.type === 'run' && run.formatting?.fontFamily) {\n const { ascii, hAnsi } = run.formatting.fontFamily;\n if (ascii) fonts.add(ascii);\n if (hAnsi && hAnsi !== ascii) fonts.add(hAnsi);\n }\n }\n }\n }\n }\n\n // Extract from styles\n const styles = doc.package?.styles?.styles;\n if (Array.isArray(styles)) {\n for (const style of styles) {\n if (style?.runProperties?.fontFamily) {\n const { ascii, hAnsi } = style.runProperties.fontFamily;\n if (ascii) fonts.add(ascii);\n if (hAnsi && hAnsi !== ascii) fonts.add(hAnsi);\n }\n }\n }\n\n return fonts;\n}\n\n/**\n * Extract fonts from a document and load them from Google Fonts\n *\n * @param document - The parsed document\n * @returns Promise resolving when fonts are loaded\n */\nexport async function loadDocumentFonts(document: unknown): Promise<void> {\n const fonts = extractFontsFromDocument(document);\n\n if (fonts.size === 0) {\n return;\n }\n\n console.log('Loading document fonts:', Array.from(fonts));\n await loadFontsWithMapping(Array.from(fonts));\n}\n","/**\n * Main Parser Orchestrator - Unified parseDocx function\n *\n * Coordinates all sub-parsers to produce a complete Document model.\n * Handles loading order, dependency resolution, and font preloading.\n *\n * Parsing order:\n * 1. Unzip DOCX package\n * 2. Parse relationships\n * 3. Parse theme (needed for style color/font resolution)\n * 4. Parse styles (depends on theme)\n * 5. Parse numbering\n * 6. Parse document body (depends on styles, theme, numbering, rels)\n * 7. Parse headers/footers (depends on styles, theme, numbering, rels)\n * 8. Parse footnotes/endnotes (depends on styles, theme, numbering, rels)\n * 9. Extract and load fonts\n * 10. Build media file map\n * 11. Assemble final Document\n */\n\nimport type {\n Document,\n DocxPackage,\n DocumentBody,\n Theme,\n Footnote,\n Endnote,\n HeaderFooter,\n RelationshipMap,\n MediaFile,\n StyleDefinitions,\n} from '../types/document';\nimport { unzipDocx, getMediaMimeType, type RawDocxContent } from './unzip';\nimport { parseRelationships, RELATIONSHIP_TYPES } from './relsParser';\nimport { parseTheme } from './themeParser';\nimport { parseStyles, parseStyleDefinitions, type StyleMap } from './styleParser';\nimport { parseNumbering, type NumberingMap } from './numberingParser';\nimport { parseDocumentBody, extractAllTemplateVariables } from './documentParser';\nimport { parseHeader, parseFooter } from './headerFooterParser';\nimport { parseFootnotes, parseEndnotes } from './footnoteParser';\nimport { loadFontsWithMapping } from '../utils/fontLoader';\n\n// ============================================================================\n// PROGRESS CALLBACK\n// ============================================================================\n\n/**\n * Progress callback for tracking parsing stages\n */\nexport type ProgressCallback = (stage: string, percent: number) => void;\n\n/**\n * Parsing options\n */\nexport interface ParseOptions {\n /** Progress callback for tracking parsing stages */\n onProgress?: ProgressCallback;\n /** Whether to preload fonts (default: true) */\n preloadFonts?: boolean;\n /** Whether to parse headers/footers (default: true) */\n parseHeadersFooters?: boolean;\n /** Whether to parse footnotes/endnotes (default: true) */\n parseNotes?: boolean;\n /** Whether to detect template variables (default: true) */\n detectVariables?: boolean;\n}\n\n// ============================================================================\n// MAIN PARSER\n// ============================================================================\n\n/**\n * Parse a DOCX file into a complete Document model\n *\n * @param buffer - DOCX file as ArrayBuffer\n * @param options - Parsing options\n * @returns Promise resolving to Document\n * @throws Error if parsing fails\n */\nexport async function parseDocx(\n buffer: ArrayBuffer,\n options: ParseOptions = {}\n): Promise<Document> {\n const {\n onProgress = () => {},\n preloadFonts = true,\n parseHeadersFooters = true,\n parseNotes = true,\n detectVariables = true,\n } = options;\n\n const warnings: string[] = [];\n\n try {\n // ========================================================================\n // STAGE 1: Unzip DOCX package (0-10%)\n // ========================================================================\n onProgress('Extracting DOCX...', 0);\n const raw = await unzipDocx(buffer);\n onProgress('Extracted DOCX', 10);\n\n // ========================================================================\n // STAGE 2: Parse relationships (10-15%)\n // ========================================================================\n onProgress('Parsing relationships...', 10);\n const rels = raw.documentRels ? parseRelationships(raw.documentRels) : new Map();\n onProgress('Parsed relationships', 15);\n\n // ========================================================================\n // STAGE 3: Parse theme (15-20%)\n // ========================================================================\n onProgress('Parsing theme...', 15);\n const theme = parseTheme(raw.themeXml);\n onProgress('Parsed theme', 20);\n\n // ========================================================================\n // STAGE 4: Parse styles (20-30%)\n // ========================================================================\n onProgress('Parsing styles...', 20);\n let styles: StyleMap | null = null;\n let styleDefinitions: StyleDefinitions | undefined;\n\n if (raw.stylesXml) {\n styles = parseStyles(raw.stylesXml, theme);\n styleDefinitions = parseStyleDefinitions(raw.stylesXml, theme);\n }\n onProgress('Parsed styles', 30);\n\n // ========================================================================\n // STAGE 5: Parse numbering (30-35%)\n // ========================================================================\n onProgress('Parsing numbering...', 30);\n const numbering = parseNumbering(raw.numberingXml);\n onProgress('Parsed numbering', 35);\n\n // ========================================================================\n // STAGE 6: Build media file map (35-40%)\n // ========================================================================\n onProgress('Processing media files...', 35);\n const media = buildMediaMap(raw, rels);\n onProgress('Processed media', 40);\n\n // ========================================================================\n // STAGE 7: Parse document body (40-55%)\n // ========================================================================\n onProgress('Parsing document body...', 40);\n let documentBody: DocumentBody = { content: [] };\n\n if (raw.documentXml) {\n documentBody = parseDocumentBody(raw.documentXml, styles, theme, numbering, rels, media);\n } else {\n warnings.push('No document.xml found in DOCX');\n }\n onProgress('Parsed document body', 55);\n\n // ========================================================================\n // STAGE 8: Parse headers/footers (55-65%)\n // ========================================================================\n let headers: Map<string, HeaderFooter> | undefined;\n let footers: Map<string, HeaderFooter> | undefined;\n\n if (parseHeadersFooters) {\n onProgress('Parsing headers/footers...', 55);\n const { headers: h, footers: f } = parseHeadersAndFooters(\n raw,\n styles,\n theme,\n numbering,\n rels,\n media\n );\n headers = h;\n footers = f;\n onProgress('Parsed headers/footers', 65);\n } else {\n onProgress('Skipping headers/footers', 65);\n }\n\n // ========================================================================\n // STAGE 9: Parse footnotes/endnotes (65-75%)\n // ========================================================================\n let footnotes: Footnote[] | undefined;\n let endnotes: Endnote[] | undefined;\n\n if (parseNotes) {\n onProgress('Parsing footnotes/endnotes...', 65);\n const { footnotes: fn, endnotes: en } = parseNotesContent(\n raw,\n styles,\n theme,\n numbering,\n rels,\n media\n );\n footnotes = fn;\n endnotes = en;\n onProgress('Parsed footnotes/endnotes', 75);\n } else {\n onProgress('Skipping footnotes/endnotes', 75);\n }\n\n // ========================================================================\n // STAGE 10: Detect template variables (75-80%)\n // ========================================================================\n let templateVariables: string[] | undefined;\n\n if (detectVariables) {\n onProgress('Detecting template variables...', 75);\n templateVariables = extractAllTemplateVariables(documentBody.content);\n onProgress('Detected variables', 80);\n } else {\n onProgress('Skipping variable detection', 80);\n }\n\n // ========================================================================\n // STAGE 11: Extract and load fonts (80-95%)\n // ========================================================================\n if (preloadFonts) {\n onProgress('Loading fonts...', 80);\n await loadDocumentFonts(theme, styleDefinitions, documentBody);\n onProgress('Loaded fonts', 95);\n } else {\n onProgress('Skipping font loading', 95);\n }\n\n // ========================================================================\n // STAGE 12: Assemble final Document (95-100%)\n // ========================================================================\n onProgress('Assembling document...', 95);\n\n const pkg: DocxPackage = {\n document: documentBody,\n styles: styleDefinitions,\n theme,\n numbering: numbering.definitions,\n headers,\n footers,\n footnotes,\n endnotes,\n relationships: rels,\n media,\n };\n\n const document: Document = {\n package: pkg,\n originalBuffer: buffer,\n templateVariables,\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n\n onProgress('Complete', 100);\n return document;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to parse DOCX: ${message}`);\n }\n}\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Build media file map from raw content and relationships\n */\nfunction buildMediaMap(raw: RawDocxContent, _rels: RelationshipMap): Map<string, MediaFile> {\n const media = new Map<string, MediaFile>();\n\n // Process each media file\n for (const [path, data] of raw.media.entries()) {\n const filename = path.split('/').pop() || path;\n const mimeType = getMediaMimeType(path);\n\n // Create a data URL for the image\n const bytes = new Uint8Array(data);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n const base64 = btoa(binary);\n const dataUrl = `data:${mimeType};base64,${base64}`;\n\n const mediaFile: MediaFile = {\n path,\n filename,\n mimeType,\n data,\n dataUrl,\n };\n\n // Store by path and also by relationship target path\n media.set(path, mediaFile);\n\n // Also map normalized paths (without \"word/\" prefix)\n const normalizedPath = path.replace(/^word\\//, '');\n if (normalizedPath !== path) {\n media.set(normalizedPath, mediaFile);\n }\n }\n\n return media;\n}\n\n/**\n * Parse headers and footers from raw content\n */\n/**\n * Case-insensitive lookup in a Map\n * ZIP files may have inconsistent casing for paths/filenames\n */\nfunction getMapCaseInsensitive<T>(map: Map<string, T>, targetKey: string): T | undefined {\n const lowerTarget = targetKey.toLowerCase();\n for (const [key, value] of map.entries()) {\n if (key.toLowerCase() === lowerTarget) {\n return value;\n }\n }\n return undefined;\n}\n\nfunction parseHeadersAndFooters(\n raw: RawDocxContent,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap,\n media: Map<string, MediaFile>\n): { headers: Map<string, HeaderFooter>; footers: Map<string, HeaderFooter> } {\n const headers = new Map<string, HeaderFooter>();\n const footers = new Map<string, HeaderFooter>();\n\n // We need to map the relationship IDs to header/footer files\n // The relationships tell us which rId maps to which header/footer file\n\n // Find header/footer references in relationships\n for (const [rId, rel] of rels.entries()) {\n if (rel.type === RELATIONSHIP_TYPES.header && rel.target) {\n // Get the header XML for this relationship\n // Use case-insensitive lookup since ZIP files may have inconsistent casing\n const filename = rel.target.split('/').pop() || rel.target;\n const headerXml = getMapCaseInsensitive(raw.headers, filename);\n\n if (headerXml) {\n // Get header-specific relationships (e.g., word/_rels/header1.xml.rels)\n const headerRelsPath = `word/_rels/${filename}.rels`;\n const headerRelsXml = getMapCaseInsensitive(raw.allXml, headerRelsPath);\n const headerRels = headerRelsXml ? parseRelationships(headerRelsXml) : rels;\n\n const header = parseHeader(\n headerXml,\n 'default', // We'll update this based on sectPr references\n styles,\n theme,\n numbering,\n headerRels,\n media\n );\n headers.set(rId, header);\n }\n } else if (rel.type === RELATIONSHIP_TYPES.footer && rel.target) {\n // Use case-insensitive lookup since ZIP files may have inconsistent casing\n const filename = rel.target.split('/').pop() || rel.target;\n const footerXml = getMapCaseInsensitive(raw.footers, filename);\n\n if (footerXml) {\n // Get footer-specific relationships (e.g., word/_rels/footer1.xml.rels)\n const footerRelsPath = `word/_rels/${filename}.rels`;\n const footerRelsXml = getMapCaseInsensitive(raw.allXml, footerRelsPath);\n const footerRels = footerRelsXml ? parseRelationships(footerRelsXml) : rels;\n\n const footer = parseFooter(\n footerXml,\n 'default',\n styles,\n theme,\n numbering,\n footerRels,\n media\n );\n footers.set(rId, footer);\n }\n }\n }\n\n return { headers, footers };\n}\n\n/**\n * Parse footnotes and endnotes from raw content\n */\nfunction parseNotesContent(\n raw: RawDocxContent,\n styles: StyleMap | null,\n theme: Theme | null,\n numbering: NumberingMap | null,\n rels: RelationshipMap,\n media: Map<string, MediaFile>\n): { footnotes: Footnote[]; endnotes: Endnote[] } {\n const footnoteMap = parseFootnotes(raw.footnotesXml, styles, theme, numbering, rels, media);\n\n const endnoteMap = parseEndnotes(raw.endnotesXml, styles, theme, numbering, rels, media);\n\n return {\n footnotes: footnoteMap.getNormalFootnotes(),\n endnotes: endnoteMap.getNormalEndnotes(),\n };\n}\n\n/**\n * Extract fonts from document and load them\n */\nasync function loadDocumentFonts(\n theme: Theme | null,\n styleDefinitions: StyleDefinitions | undefined,\n documentBody: DocumentBody\n): Promise<void> {\n const docxFonts = new Set<string>();\n\n // Extract fonts from theme\n if (theme?.fontScheme) {\n const { majorFont, minorFont } = theme.fontScheme;\n if (majorFont?.latin) docxFonts.add(majorFont.latin);\n if (minorFont?.latin) docxFonts.add(minorFont.latin);\n }\n\n // Extract fonts from style defaults\n if (styleDefinitions?.docDefaults?.rPr?.fontFamily?.ascii) {\n docxFonts.add(styleDefinitions.docDefaults.rPr.fontFamily.ascii);\n }\n\n // Extract fonts from styles\n if (styleDefinitions?.styles) {\n for (const style of styleDefinitions.styles) {\n if (style.rPr?.fontFamily?.ascii) {\n docxFonts.add(style.rPr.fontFamily.ascii);\n }\n if (style.rPr?.fontFamily?.hAnsi) {\n docxFonts.add(style.rPr.fontFamily.hAnsi);\n }\n }\n }\n\n // Extract fonts from document content (inline run formatting)\n if (documentBody.content) {\n for (const block of documentBody.content) {\n if (block.type === 'paragraph') {\n for (const item of block.content) {\n if (item.type === 'run' && item.formatting?.fontFamily) {\n if (item.formatting.fontFamily.ascii) {\n docxFonts.add(item.formatting.fontFamily.ascii);\n }\n if (item.formatting.fontFamily.hAnsi) {\n docxFonts.add(item.formatting.fontFamily.hAnsi);\n }\n }\n }\n }\n }\n }\n\n // Load fonts with mapping (creates aliases so original names work in CSS)\n if (docxFonts.size > 0) {\n try {\n await loadFontsWithMapping(Array.from(docxFonts));\n } catch (error) {\n // Font loading is non-critical, continue without fonts\n console.warn('Failed to load some fonts:', error);\n }\n }\n}\n\n// ============================================================================\n// CONVENIENCE FUNCTIONS\n// ============================================================================\n\n/**\n * Quick parse - parse a DOCX without font loading\n * Useful for quick content extraction or when fonts aren't needed\n */\nexport async function quickParseDocx(buffer: ArrayBuffer): Promise<Document> {\n return parseDocx(buffer, {\n preloadFonts: false,\n parseHeadersFooters: false,\n parseNotes: false,\n detectVariables: true,\n });\n}\n\n/**\n * Full parse - parse everything including fonts\n */\nexport async function fullParseDocx(\n buffer: ArrayBuffer,\n onProgress?: ProgressCallback\n): Promise<Document> {\n return parseDocx(buffer, {\n onProgress,\n preloadFonts: true,\n parseHeadersFooters: true,\n parseNotes: true,\n detectVariables: true,\n });\n}\n\n/**\n * Get template variables from a DOCX without full parsing\n * Faster than full parse when you only need variables\n */\nexport async function getDocxVariables(buffer: ArrayBuffer): Promise<string[]> {\n const raw = await unzipDocx(buffer);\n\n if (!raw.documentXml) {\n return [];\n }\n\n // Quick parse just the document body\n const documentBody = parseDocumentBody(raw.documentXml);\n return extractAllTemplateVariables(documentBody.content);\n}\n\n/**\n * Get document summary without full parsing\n */\nexport async function getDocxSummary(buffer: ArrayBuffer): Promise<{\n hasDocument: boolean;\n hasStyles: boolean;\n hasTheme: boolean;\n hasNumbering: boolean;\n headerCount: number;\n footerCount: number;\n mediaCount: number;\n variableCount: number;\n}> {\n const raw = await unzipDocx(buffer);\n const variables = raw.documentXml\n ? extractAllTemplateVariables(parseDocumentBody(raw.documentXml).content)\n : [];\n\n return {\n hasDocument: raw.documentXml !== null,\n hasStyles: raw.stylesXml !== null,\n hasTheme: raw.themeXml !== null,\n hasNumbering: raw.numberingXml !== null,\n headerCount: raw.headers.size,\n footerCount: raw.footers.size,\n mediaCount: raw.media.size,\n variableCount: variables.length,\n };\n}\n","/**\n * Docxtemplater Plugin MCP Tools\n *\n * MCP tool definitions for template operations that can be called by AI clients.\n */\n\nimport type { McpToolDefinition, McpToolContext, McpToolResult, JsonSchema } from '../types';\n\nimport { detectVariablesDetailed } from '../../utils/variableDetector';\nimport { processTemplateDetailed, validateTemplate } from '../../utils/processTemplate';\nimport { parseDocx } from '../../docx/parser';\n\n// ============================================================================\n// SCHEMAS\n// ============================================================================\n\nconst documentIdSchema: JsonSchema = {\n type: 'string',\n description: 'Document ID from a previous docx_load call',\n};\n\nconst positionSchema: JsonSchema = {\n type: 'object',\n properties: {\n paragraphIndex: {\n type: 'number',\n description: 'Index of the paragraph (0-indexed)',\n },\n offset: {\n type: 'number',\n description: 'Character offset within the paragraph',\n },\n },\n required: ['paragraphIndex', 'offset'],\n};\n\n// ============================================================================\n// TOOL DEFINITIONS\n// ============================================================================\n\n/**\n * Get template variables from a document\n */\nexport const getVariablesTool: McpToolDefinition = {\n name: 'docx_get_variables',\n description: `List all template variables ({{name}} format) found in the document.\nReturns variable names without braces, along with their locations (body, headers, footers, etc.).\nUse this to discover what data fields a template document expects.`,\n\n inputSchema: {\n type: 'object',\n properties: {\n documentId: documentIdSchema,\n },\n required: ['documentId'],\n },\n\n handler: async (input: unknown, context: McpToolContext): Promise<McpToolResult> => {\n const { documentId } = input as { documentId: string };\n\n const loaded = context.session.documents.get(documentId);\n if (!loaded) {\n return {\n isError: true,\n content: [{ type: 'text', text: `Document not found: ${documentId}` }],\n };\n }\n\n try {\n const result = detectVariablesDetailed(loaded.document);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n variables: result.variables,\n count: result.variables.length,\n totalOccurrences: result.totalOccurrences,\n byLocation: result.byLocation,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (error) {\n return {\n isError: true,\n content: [\n { type: 'text', text: `Failed to detect variables: ${(error as Error).message}` },\n ],\n };\n }\n },\n\n annotations: {\n category: 'template',\n readOnly: true,\n complexity: 'low',\n examples: [\n {\n description: 'Get all variables from a loaded document',\n input: { documentId: 'doc_123' },\n output: '{\"variables\": [\"customer_name\", \"invoice_date\"], \"count\": 2}',\n },\n ],\n },\n};\n\n/**\n * Insert a template variable at a position\n */\nexport const insertVariableTool: McpToolDefinition = {\n name: 'docx_insert_variable',\n description: `Insert a template variable placeholder ({{name}}) at a specific position in the document.\nThe variable can later be substituted with actual values using docx_apply_template.\nVariable names should follow the pattern: letters, numbers, underscores, starting with a letter.`,\n\n inputSchema: {\n type: 'object',\n properties: {\n documentId: documentIdSchema,\n position: positionSchema,\n variableName: {\n type: 'string',\n description:\n 'Variable name without braces (e.g., \"customer_name\"). Will be inserted as {{customer_name}}',\n pattern: '^[a-zA-Z_][a-zA-Z0-9_]*$',\n },\n },\n required: ['documentId', 'position', 'variableName'],\n },\n\n handler: async (input: unknown, context: McpToolContext): Promise<McpToolResult> => {\n const { documentId, position, variableName } = input as {\n documentId: string;\n position: { paragraphIndex: number; offset: number };\n variableName: string;\n };\n\n const loaded = context.session.documents.get(documentId);\n if (!loaded) {\n return {\n isError: true,\n content: [{ type: 'text', text: `Document not found: ${documentId}` }],\n };\n }\n\n // Validate variable name\n if (!/^[a-zA-Z_][a-zA-Z0-9_\\-.]*$/.test(variableName)) {\n return {\n isError: true,\n content: [\n {\n type: 'text',\n text: `Invalid variable name: ${variableName}. Must start with letter/underscore and contain only alphanumeric, underscore, hyphen, or dot.`,\n },\n ],\n };\n }\n\n try {\n // Import the executor to run the command\n const { executeCommand } = await import('../../agent/executor');\n\n // Execute the insert command\n const newDoc = executeCommand(loaded.document, {\n type: 'insertText',\n position,\n text: `{{${variableName}}}`,\n });\n\n // Update the document in the session\n loaded.document = newDoc;\n loaded.lastModified = Date.now();\n\n // Track the variable\n if (!newDoc.templateVariables) {\n newDoc.templateVariables = [];\n }\n if (!newDoc.templateVariables.includes(variableName)) {\n newDoc.templateVariables.push(variableName);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n success: true,\n variable: variableName,\n insertedAs: `{{${variableName}}}`,\n position,\n }),\n },\n ],\n };\n } catch (error) {\n return {\n isError: true,\n content: [{ type: 'text', text: `Failed to insert variable: ${(error as Error).message}` }],\n };\n }\n },\n\n annotations: {\n category: 'template',\n readOnly: false,\n complexity: 'low',\n examples: [\n {\n description: 'Insert customer name variable at start of first paragraph',\n input: {\n documentId: 'doc_123',\n position: { paragraphIndex: 0, offset: 0 },\n variableName: 'customer_name',\n },\n },\n ],\n },\n};\n\n/**\n * Apply template substitution\n */\nexport const applyTemplateTool: McpToolDefinition = {\n name: 'docx_apply_template',\n description: `Substitute template variables with actual values in the document.\nReplaces all {{variable}} placeholders with the corresponding values provided.\nPreserves all formatting (fonts, styles, colors, tables).\nUse docx_get_variables first to discover what variables exist in the document.`,\n\n inputSchema: {\n type: 'object',\n properties: {\n documentId: documentIdSchema,\n variables: {\n type: 'object',\n description:\n 'Map of variable names to values (e.g., {\"customer_name\": \"John Doe\", \"date\": \"2024-01-15\"})',\n additionalProperties: {\n type: 'string',\n },\n },\n keepUnmatchedVariables: {\n type: 'boolean',\n description:\n 'If true, keep {{variable}} placeholders for variables not in the map. If false, replace with empty string. Default: true',\n default: true,\n },\n },\n required: ['documentId', 'variables'],\n },\n\n handler: async (input: unknown, context: McpToolContext): Promise<McpToolResult> => {\n const {\n documentId,\n variables,\n keepUnmatchedVariables = true,\n } = input as {\n documentId: string;\n variables: Record<string, string>;\n keepUnmatchedVariables?: boolean;\n };\n\n const loaded = context.session.documents.get(documentId);\n if (!loaded) {\n return {\n isError: true,\n content: [{ type: 'text', text: `Document not found: ${documentId}` }],\n };\n }\n\n if (!loaded.buffer) {\n return {\n isError: true,\n content: [\n {\n type: 'text',\n text: 'Cannot apply template: document was not loaded from a DOCX buffer',\n },\n ],\n };\n }\n\n try {\n // Process the template\n const result = processTemplateDetailed(loaded.buffer, variables, {\n nullGetter: keepUnmatchedVariables ? 'keep' : 'empty',\n });\n\n // Re-parse the processed document\n const newDoc = await parseDocx(result.buffer);\n\n // Update session\n loaded.document = newDoc;\n loaded.buffer = result.buffer;\n loaded.lastModified = Date.now();\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n success: true,\n replacedVariables: result.replacedVariables,\n unreplacedVariables: result.unreplacedVariables,\n warnings: result.warnings,\n }),\n },\n ],\n };\n } catch (error) {\n return {\n isError: true,\n content: [{ type: 'text', text: `Failed to apply template: ${(error as Error).message}` }],\n };\n }\n },\n\n annotations: {\n category: 'template',\n readOnly: false,\n complexity: 'medium',\n examples: [\n {\n description: 'Fill in customer and date values',\n input: {\n documentId: 'doc_123',\n variables: {\n customer_name: 'Jane Smith',\n invoice_date: '2024-02-15',\n amount: '$1,234.56',\n },\n },\n },\n ],\n },\n};\n\n/**\n * Validate a template document\n */\nexport const validateTemplateTool: McpToolDefinition = {\n name: 'docx_validate_template',\n description: `Validate that a document is a valid docxtemplater template.\nChecks for syntax errors like unclosed braces, invalid tag names, etc.\nReturns validation result with any errors found and list of valid tags.`,\n\n inputSchema: {\n type: 'object',\n properties: {\n documentId: documentIdSchema,\n },\n required: ['documentId'],\n },\n\n handler: async (input: unknown, context: McpToolContext): Promise<McpToolResult> => {\n const { documentId } = input as { documentId: string };\n\n const loaded = context.session.documents.get(documentId);\n if (!loaded) {\n return {\n isError: true,\n content: [{ type: 'text', text: `Document not found: ${documentId}` }],\n };\n }\n\n if (!loaded.buffer) {\n return {\n isError: true,\n content: [\n {\n type: 'text',\n text: 'Cannot validate template: document was not loaded from a DOCX buffer',\n },\n ],\n };\n }\n\n try {\n const result = validateTemplate(loaded.buffer);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(\n {\n valid: result.valid,\n tags: result.tags,\n errors: result.errors.map((e) => ({\n message: e.message,\n variable: e.variable,\n type: e.type,\n })),\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (error) {\n return {\n isError: true,\n content: [\n { type: 'text', text: `Failed to validate template: ${(error as Error).message}` },\n ],\n };\n }\n },\n\n annotations: {\n category: 'template',\n readOnly: true,\n complexity: 'low',\n },\n};\n\n// ============================================================================\n// EXPORT ALL TOOLS\n// ============================================================================\n\nexport const docxtemplaterMcpTools: McpToolDefinition[] = [\n getVariablesTool,\n insertVariableTool,\n applyTemplateTool,\n validateTemplateTool,\n];\n\nexport default docxtemplaterMcpTools;\n","/**\n * Docxtemplater Plugin\n *\n * Core plugin for template variable functionality using docxtemplater.\n * Provides:\n * - Command handlers for inserting template variables\n * - MCP tools for template operations (get variables, apply template)\n *\n * @example\n * ```ts\n * import { pluginRegistry } from '@eigenpal/docx-editor/core-plugins';\n * import { docxtemplaterPlugin } from '@eigenpal/docx-editor/core-plugins/docxtemplater';\n *\n * pluginRegistry.register(docxtemplaterPlugin);\n * ```\n */\n\nimport type { CorePlugin } from '../types';\nimport { handleInsertTemplateVariable, handleReplaceWithTemplateVariable } from './handlers';\nimport { docxtemplaterMcpTools } from './mcp-tools';\n\n// ============================================================================\n// PLUGIN DEFINITION\n// ============================================================================\n\n/**\n * Docxtemplater plugin for template variable functionality\n */\nexport const docxtemplaterPlugin: CorePlugin = {\n id: 'docxtemplater',\n name: 'Docxtemplater',\n version: '1.0.0',\n description: 'Template variable support using docxtemplater syntax ({{variable}})',\n\n /**\n * Command handlers for template operations\n */\n commandHandlers: {\n insertTemplateVariable: handleInsertTemplateVariable,\n replaceWithTemplateVariable: handleReplaceWithTemplateVariable,\n },\n\n /**\n * MCP tools exposed to AI clients\n */\n mcpTools: docxtemplaterMcpTools,\n\n /**\n * Initialize the plugin\n *\n * Checks that docxtemplater and pizzip are available.\n * Note: These are already dependencies, but this validates they're importable.\n */\n initialize: () => {\n // Validate docxtemplater is available\n // We don't actually need to import it here since processTemplate handles that\n // This is just a validation step\n try {\n // Check if the utilities are importable\n\n require('docxtemplater');\n\n require('pizzip');\n } catch {\n console.warn(\n '[docxtemplater-plugin] Warning: docxtemplater or pizzip not installed. ' +\n 'Template features may not work. Install with: npm install docxtemplater pizzip'\n );\n }\n },\n};\n\n// ============================================================================\n// EXPORTS\n// ============================================================================\n\n// Export handlers for direct use\nexport {\n handleInsertTemplateVariable,\n handleReplaceWithTemplateVariable,\n type InsertTemplateVariableCommand,\n type ReplaceWithTemplateVariableCommand,\n} from './handlers';\n\n// Export MCP tools for customization\nexport {\n docxtemplaterMcpTools,\n getVariablesTool,\n insertVariableTool,\n applyTemplateTool,\n validateTemplateTool,\n} from './mcp-tools';\n\n// Default export is the plugin\nexport default docxtemplaterPlugin;\n"],"mappings":"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","names":["getParagraphText","insertTextAtOffset","parseColorElement","abstractNum","parseColorValue","parseShadingProperties","parseRunProperties","getLocalName","getLocalName","parseBookmarkStart","parseBookmarkEnd","parseTableMeasurement","parseBorderSpec","parseTableBorders","parseCellMargins","parseTableLook","parseTableProperties","parseTableRowProperties","parseTableCellProperties","parseNumberFormat","parseColorValue","parseBorderSpec","parseColorValue","parseShadingProperties","parseBorderSpec","parseTabStops","parseParagraphProperties","parseRunProperties","getLocalName","parseHyperlink","parseBookmarkStart","parseBookmarkEnd","document","executeCommand"]}