Operand

do no harem.

gram: docs

> ./scripts/parity/parity.contract.json

{
"version": 1,
"comment": "Cross-adapter parity contract between @eigenpal/docx-editor-react and @eigenpal/docx-editor-vue. Tracked in issue #475 Phase 3. Drives `bun run check:parity-contract`, which parses each adapter's docs/api/<slug>/index.api.md, applies this contract, and fails on undocumented drift. Adding a new prop or ref method to either adapter forces a contract update \u2014 there is no way to silently introduce divergence.",
"props": {
"paired": [
"author",
"className",
"colorMode",
"disableFindReplaceShortcuts",
"document",
"documentBuffer",
"documentName",
"documentNameEditable",
"externalPlugins",
"fontFamilies",
"fonts",
"i18n",
"initialZoom",
"mode",
"onChange",
"onCommentAdd",
"onCommentDelete",
"onCommentReply",
"onCommentResolve",
"onCommentsChange",
"onDocumentNameChange",
"onEditorViewReady",
"onError",
"onModeChange",
"onPrint",
"onSelectionChange",
"readOnly",
"renderLogo",
"renderTitleBarRight",
"showOutline",
"showOutlineButton",
"showRuler",
"showToolbar",
"showZoomControl",
"style",
"theme",
"toolbarExtra",
"watermarkPresets"
],
"deferredInVue": {
"agentPanel": "AI agent panel UI. Vue adapter ships without the agent surface; React-only until the Vue agent components land.",
"comments": "Comment threads. Part of the Vue comments API gap.",
"externalContent": "Sandboxed external-content rendering mode. React-only escape hatch.",
"loadingIndicator": "Custom loading slot. Vue uses default slot via VNodeChild.",
"marginGuideColor": "Pairs with showMarginGuides \u2014 both deferred together.",
"onCopy": "Clipboard event hooks. Vue exposes via useClipboard composable instead.",
"onCut": "Clipboard event hooks. Vue exposes via useClipboard composable instead.",
"onFontsLoaded": "Font-load lifecycle. Vue-deferred.",
"onPaste": "Clipboard event hook. Vue exposes via useClipboard composable instead.",
"onRenderedDomContextReady": "Plugin host ready callback. Pairs with pluginRenderedDomContext.",
"onSave": "Save-to-buffer callback. Critical parity gap \u2014 Vue uses useDocxEditor().save() that returns a Blob, not a prop callback.",
"placeholder": "Empty-state slot. Vue uses default slot pattern.",
"pluginOverlays": "Plugin overlay slot. Pairs with pluginRenderedDomContext.",
"pluginRenderedDomContext": "Plugin host hand-off. Vue plugin-api is a smaller surface.",
"pluginSidebarItems": "Sidebar plugin items. Pairs with the Vue plugin-api gap.",
"printOptions": "Print configuration. Vue uses onPrint without options object.",
"rulerUnit": "Ruler unit toggle (inch/cm). Vue uses inch only currently.",
"showMarginGuides": "Margin guide overlay. Pairs with marginGuideColor \u2014 both deferred together."
},
"vueExclusive": {
"showMenuBar": "Vue adapter ships a Google-Docs-style menu bar above the toolbar. React uses a single toolbar with no menu-bar concept."
}
},
"ref": {
"comment": "DocxEditorRef parity. React's DocxEditorRef declares every member directly. Vue's DocxEditorRef intersects EditorRefLike (from @eigenpal/docx-editor-agents/bridge) plus its own additions \u2014 so members inherited via EditorRefLike are NOT enumerated in Vue's snapshot, but ARE callable at runtime. The contract uses three buckets: `paired` (explicit on both sides), `pairedViaInheritance` (explicit on React, inherited via EditorRefLike on Vue), and `vueExclusive` (only on Vue's DocxEditorRef).",
"paired": [
"focus",
"getAgent",
"getContentControls",
"getZoom",
"highlightRange",
"loadDocument",
"loadDocumentBuffer",
"openPrintPreview",
"print",
"removeContentControl",
"save",
"scrollToChangeId",
"scrollToCommentId",
"scrollToContentControl",
"scrollToPage",
"scrollToPosition",
"setContentControlContent",
"setContentControlValue",
"setZoom"
],
"pairedViaInheritance": {
"addComment": "Inherited via EditorRefLike. Identical signature on both adapters.",
"applyFormatting": "Inherited via EditorRefLike. Identical signature on both adapters.",
"findInDocument": "Inherited via EditorRefLike. Identical signature on both adapters.",
"getComments": "Inherited via EditorRefLike. Identical signature on both adapters.",
"getCurrentPage": "Inherited via EditorRefLike. Identical signature on both adapters.",
"getDocument": "Inherited via EditorRefLike. Identical signature on both adapters.",
"getEditorRef": "Inherited via EditorRefLike. Returns the inner editor handle (React: PagedEditorRef; Vue: useDocxEditor() composable handle).",
"getPageContent": "Inherited via EditorRefLike. Identical signature on both adapters.",
"getSelectionInfo": "Inherited via EditorRefLike. Identical signature on both adapters.",
"getTotalPages": "Inherited via EditorRefLike. Identical signature on both adapters.",
"insertBreak": "Inherited via EditorRefLike. Identical signature on both adapters.",
"onContentChange": "Inherited via EditorRefLike. Identical signature on both adapters.",
"onSelectionChange": "Inherited via EditorRefLike. Identical signature on both adapters.",
"proposeChange": "Inherited via EditorRefLike. Identical signature on both adapters.",
"replyToComment": "Inherited via EditorRefLike. Identical signature on both adapters.",
"resolveComment": "Inherited via EditorRefLike. Identical signature on both adapters.",
"scrollToParaId": "Inherited via EditorRefLike. Identical signature on both adapters.",
"setParagraphStyle": "Inherited via EditorRefLike. Identical signature on both adapters."
},
"vueExclusive": {
"destroy": "Vue's component-level imperative teardown. React's DocxEditorRef omits this because React handles unmount automatically; both adapters expose `destroy` via their `renderAsync()` return (DocxEditorHandle from core's EditorHandle base)."
}
}
}