From d551ee8ddb7c94c4a618fa60b725b909bfeb24c1 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 13:51:24 -0300 Subject: [PATCH 01/13] fix: correct broken import paths in docs code examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - field-annotation.mdx: replace non-existent @superdoc/field-annotation with SuperDoc constructor pattern - typescript-migration.mdx: @core/Extension → superdoc/super-editor - document-section.mdx: superdoc/extensions → superdoc/super-editor - template-builder quickstart + intro: superdoc/dist/style.css → superdoc/style.css --- apps/docs/extensions/document-section.mdx | 2 +- apps/docs/extensions/field-annotation.mdx | 16 +++++++++++----- .../guides/migration/typescript-migration.mdx | 2 +- .../solutions/template-builder/introduction.mdx | 2 +- .../solutions/template-builder/quickstart.mdx | 2 +- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/docs/extensions/document-section.mdx b/apps/docs/extensions/document-section.mdx index 4386768b78..921eccbfb8 100644 --- a/apps/docs/extensions/document-section.mdx +++ b/apps/docs/extensions/document-section.mdx @@ -10,7 +10,7 @@ Encapsulate and manage discrete parts of documents. Legal clauses stay locked wh Included by default in SuperDoc. ```javascript -import { DocumentSection } from 'superdoc/extensions'; +import { DocumentSection } from 'superdoc/super-editor'; ``` ## Commands diff --git a/apps/docs/extensions/field-annotation.mdx b/apps/docs/extensions/field-annotation.mdx index c64c56e48b..4ab1ae7556 100644 --- a/apps/docs/extensions/field-annotation.mdx +++ b/apps/docs/extensions/field-annotation.mdx @@ -28,11 +28,17 @@ description: "Interactive form fields for documents. It can be used when variabl **Basic Setup:** ```javascript -import { FieldAnnotationPlugin } from '@superdoc/field-annotation'; - -const plugin = FieldAnnotationPlugin({ - editor: myEditor, - handleDropOutside: true +import { SuperDoc } from 'superdoc'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + // Field annotations are included by default via getStarterExtensions(). + // Configure options here: + fieldAnnotation: { + handleDropOutside: true, + borderColor: '#b015b3', + }, }); ``` diff --git a/apps/docs/guides/migration/typescript-migration.mdx b/apps/docs/guides/migration/typescript-migration.mdx index 371b1c3300..da809a72a8 100644 --- a/apps/docs/guides/migration/typescript-migration.mdx +++ b/apps/docs/guides/migration/typescript-migration.mdx @@ -179,7 +179,7 @@ export function updateNodeAttrs(node: PMNode, attrs: Record): PMNod ### Extension pattern ```typescript -import { Extension } from '@core/Extension'; +import { Extension } from 'superdoc/super-editor'; export interface MyExtensionOptions { enabled?: boolean; diff --git a/apps/docs/solutions/template-builder/introduction.mdx b/apps/docs/solutions/template-builder/introduction.mdx index 920bf61544..57b76e18fa 100644 --- a/apps/docs/solutions/template-builder/introduction.mdx +++ b/apps/docs/solutions/template-builder/introduction.mdx @@ -25,7 +25,7 @@ A React component for creating document templates with dynamic fields that can b ```jsx import SuperDocTemplateBuilder from "@superdoc-dev/template-builder"; -import "superdoc/dist/style.css"; +import "superdoc/style.css"; function TemplateEditor() { return ( diff --git a/apps/docs/solutions/template-builder/quickstart.mdx b/apps/docs/solutions/template-builder/quickstart.mdx index b23d15ec52..f0d222e3f2 100644 --- a/apps/docs/solutions/template-builder/quickstart.mdx +++ b/apps/docs/solutions/template-builder/quickstart.mdx @@ -17,7 +17,7 @@ Import the component and styles, then render with minimal props: ```jsx import SuperDocTemplateBuilder from "@superdoc-dev/template-builder"; -import "superdoc/dist/style.css"; +import "superdoc/style.css"; function App() { return ( From fa6e4074c9a77cf9f1f6882f730c421992ad0329 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 13:51:30 -0300 Subject: [PATCH 02/13] docs: add CodeGroup tabs to methods pages for copy-pasteable examples Wrap all fragment code snippets in superdoc/methods.mdx and supereditor/methods.mdx with CodeGroup Usage + Full Example tabs so every snippet is self-contained and runnable. --- apps/docs/core/superdoc/methods.mdx | 452 ++++++++++++++++++-- apps/docs/core/supereditor/methods.mdx | 546 +++++++++++++++++++++++-- 2 files changed, 943 insertions(+), 55 deletions(-) diff --git a/apps/docs/core/superdoc/methods.mdx b/apps/docs/core/superdoc/methods.mdx index a1ffe8a3a6..6d3589dde4 100644 --- a/apps/docs/core/superdoc/methods.mdx +++ b/apps/docs/core/superdoc/methods.mdx @@ -38,23 +38,60 @@ Export the document with various options. **Returns:** `Promise` - Document blob -```javascript + + +```javascript Usage const blob = await superdoc.export({ isFinalDoc: true, - commentsType: 'clean' + commentsType: 'clean', +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: async (superdoc) => { + const blob = await superdoc.export({ + isFinalDoc: true, + commentsType: 'clean', + }); + }, }); ``` + + ### `save` Save document if in collaboration mode. **Returns:** `Promise` -```javascript + + +```javascript Usage await superdoc.save(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: async (superdoc) => { + await superdoc.save(); + }, +}); +``` + + + ### `getHTML` Get HTML content of all editors. @@ -71,10 +108,28 @@ Get HTML content of all editors. **Returns:** `string[]` - Array of HTML strings, one per editor -```javascript + + +```javascript Usage const htmlArray = superdoc.getHTML(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const htmlArray = superdoc.getHTML(); + console.log(htmlArray); + }, +}); +``` + + + ## Mode control ### `setDocumentMode` @@ -91,10 +146,27 @@ Change the document mode. -```javascript + + +```javascript Usage superdoc.setDocumentMode('suggesting'); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.setDocumentMode('suggesting'); + }, +}); +``` + + + ### `lockSuperdoc` Lock or unlock the document. @@ -107,10 +179,30 @@ Lock or unlock the document. User who locked the document -```javascript + + +```javascript Usage superdoc.lockSuperdoc(true, currentUser); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.lockSuperdoc(true, { + name: 'Jane Smith', + email: 'jane@example.com', + }); + }, +}); +``` + + + ### `setHighContrastMode` Enable/disable high contrast mode. @@ -119,10 +211,27 @@ Enable/disable high contrast mode. High contrast state -```javascript + + +```javascript Usage superdoc.setHighContrastMode(true); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.setHighContrastMode(true); + }, +}); +``` + + + ## UI methods ### `setActiveEditor` @@ -133,10 +242,29 @@ Set which editor is currently active. Useful when working with multiple document Editor instance to set as active -```javascript + + +```javascript Usage superdoc.setActiveEditor(editor); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + documents: [doc1, doc2], + onReady: (superdoc) => { + // Switch to the second editor + const editors = superdoc.state.documents; + superdoc.setActiveEditor(editors[1].editor); + }, +}); +``` + + + ### `setDisableContextMenu` Toggle the custom context menu at runtime. @@ -145,10 +273,27 @@ Toggle the custom context menu at runtime. Whether to disable the context menu -```javascript + + +```javascript Usage superdoc.setDisableContextMenu(true); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.setDisableContextMenu(true); + }, +}); +``` + + + ### `setTrackedChangesPreferences` Override how tracked changes are rendered in the layout engine. @@ -164,18 +309,52 @@ Override how tracked changes are rendered in the layout engine. -```javascript + + +```javascript Usage superdoc.setTrackedChangesPreferences({ mode: 'final' }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.setTrackedChangesPreferences({ mode: 'final' }); + }, +}); +``` + + + ### `toggleRuler` Toggle ruler visibility. -```javascript + + +```javascript Usage superdoc.toggleRuler(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.toggleRuler(); + }, +}); +``` + + + **Removed in v1.0** — Pagination is now handled by the layout engine. Use `viewOptions.layout` in [configuration](/core/superdoc/configuration) instead. @@ -186,10 +365,27 @@ superdoc.toggleRuler(); Focus the active editor or first available. -```javascript + + +```javascript Usage superdoc.focus(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.focus(); + }, +}); +``` + + + ## Search methods ### `search` @@ -202,11 +398,31 @@ Search for text or regex in active editor. **Returns:** `Array` - Search matches -```javascript + + +```javascript Usage const results = superdoc.search('contract'); -const results = superdoc.search(/section \d+/gi); +const regexResults = superdoc.search(/section \d+/gi); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const results = superdoc.search('contract'); + console.log(`Found ${results.length} matches`); + + const regexResults = superdoc.search(/section \d+/gi); + }, +}); ``` + + ### `goToSearchResult` Navigate to a search result. @@ -215,10 +431,30 @@ Navigate to a search result. Search result to navigate to -```javascript + + +```javascript Usage superdoc.goToSearchResult(results[0]); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const results = superdoc.search('contract'); + if (results.length) { + superdoc.goToSearchResult(results[0]); + } + }, +}); +``` + + + ## Comments methods ### `addCommentsList` @@ -229,18 +465,52 @@ Add a comments list to the specified element. Container for comments list -```javascript + + +```javascript Usage superdoc.addCommentsList('#comments-sidebar'); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.addCommentsList('#comments-sidebar'); + }, +}); +``` + + + ### `removeCommentsList` Remove the comments list. -```javascript + + +```javascript Usage superdoc.removeCommentsList(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.removeCommentsList(); + }, +}); +``` + + + ## User management ### `addSharedUser` @@ -260,13 +530,33 @@ Add a user to the shared users list. -```javascript + + +```javascript Usage superdoc.addSharedUser({ name: 'Jane Smith', - email: 'jane@example.com' + email: 'jane@example.com', }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.addSharedUser({ + name: 'Jane Smith', + email: 'jane@example.com', + }); + }, +}); +``` + + + ### `removeSharedUser` Remove a user from shared users. @@ -275,10 +565,27 @@ Remove a user from shared users. Email of user to remove -```javascript + + +```javascript Usage superdoc.removeSharedUser('jane@example.com'); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.removeSharedUser('jane@example.com'); + }, +}); +``` + + + ## Lifecycle ### `destroy` @@ -287,40 +594,116 @@ Completely destroy the SuperDoc instance. This is irreversible. Cleans up all resources, events, and DOM. -```javascript + + +```javascript Usage +superdoc.destroy(); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + +// Later, when you're done with the editor: superdoc.destroy(); ``` + + ## Event methods ### `on` Subscribe to an event. -```javascript + + +```javascript Usage +superdoc.on('ready', ({ superdoc }) => { + console.log('SuperDoc ready'); +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + superdoc.on('ready', ({ superdoc }) => { console.log('SuperDoc ready'); }); ``` + + ### `once` Subscribe to an event once. -```javascript + + +```javascript Usage superdoc.once('ready', () => { // Only called once }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + +superdoc.once('ready', () => { + console.log('SuperDoc ready (one-time)'); +}); +``` + + + ### `off` Unsubscribe from an event. -```javascript + + +```javascript Usage +superdoc.off('ready', handler); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + +const handler = ({ superdoc }) => { + console.log('SuperDoc ready'); +}; + +superdoc.on('ready', handler); + +// Later, unsubscribe: superdoc.off('ready', handler); ``` + + ## Schema introspection ### `getSchemaIntrospection` @@ -367,12 +750,31 @@ Available types: `NodeName`, `NodeAttrs`, `MarkName`, `MarkAttrs`, and spe Currently active editor instance. Returns `null` before the `ready` event. -```javascript + + +```javascript Usage if (superdoc.activeEditor) { superdoc.activeEditor.commands.toggleBold(); } ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + if (superdoc.activeEditor) { + superdoc.activeEditor.commands.toggleBold(); + } + }, +}); +``` + + + Always check for `null` before accessing. | Property | Type | Description | diff --git a/apps/docs/core/supereditor/methods.mdx b/apps/docs/core/supereditor/methods.mdx index e897045315..084eb87795 100644 --- a/apps/docs/core/supereditor/methods.mdx +++ b/apps/docs/core/supereditor/methods.mdx @@ -30,11 +30,27 @@ Open a document from a file, URL, or buffer. -```javascript + + +```javascript Usage await editor.open(docxFile); await editor.open(null, { mode: 'html', html: '

Hello

' }); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +await editor.open(docxFile); +await editor.open(null, { mode: 'html', html: '

Hello

' }); +``` + +
+ This is the instance method. For one-liner creation, use the static `Editor.open()` factory — see [Configuration](/core/supereditor/configuration). @@ -43,11 +59,27 @@ This is the instance method. For one-liner creation, use the static `Editor.open Close the current document. The editor instance stays alive for reuse. -```javascript + + +```javascript Usage editor.close(); await editor.open(anotherFile); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +editor.close(); +await editor.open(anotherFile); +``` + + + ### `save` Save back to the original source path (Node.js only). @@ -69,11 +101,27 @@ Save back to the original source path (Node.js only). -```javascript + + +```javascript Usage await editor.save(); await editor.save({ isFinalDoc: true }); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +await editor.save(); +await editor.save({ isFinalDoc: true }); +``` + + + Throws if the editor was opened from a Blob/Buffer instead of a file path. Use `saveTo()` or `exportDocument()` instead. ### `saveTo` @@ -84,19 +132,50 @@ Save to a specific path (Node.js only). File path -```javascript + + +```javascript Usage +await editor.saveTo('/path/to/output.docx'); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + await editor.saveTo('/path/to/output.docx'); ``` + + ### `exportDocument` Export as a Blob (browser) or Buffer (Node.js). -```javascript + + +```javascript Usage const blob = await editor.exportDocument(); const blob = await editor.exportDocument({ isFinalDoc: true }); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +const blob = await editor.exportDocument(); +const blob = await editor.exportDocument({ isFinalDoc: true }); +``` + + + ### `exportDocx` Lower-level export with additional control. @@ -118,105 +197,297 @@ Lower-level export with additional control. -```javascript + + +```javascript Usage +const blob = await editor.exportDocx({ + isFinalDoc: true, + commentsType: 'clean' +}); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + const blob = await editor.exportDocx({ isFinalDoc: true, commentsType: 'clean' }); ``` + + ### `replaceFile` Replace the current DOCX with a new file. -```javascript + + +```javascript Usage await editor.replaceFile(newDocxFile); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +await editor.replaceFile(newDocxFile); +``` + + + ## Content ### `getHTML` -```javascript + + +```javascript Usage +const html = editor.getHTML(); +const html = editor.getHTML({ unflattenLists: true }); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + const html = editor.getHTML(); const html = editor.getHTML({ unflattenLists: true }); ``` + + ### `getJSON` -```javascript + + +```javascript Usage +const json = editor.getJSON(); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + const json = editor.getJSON(); ``` + + ### `getMarkdown` -```javascript + + +```javascript Usage +const md = await editor.getMarkdown(); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + const md = await editor.getMarkdown(); ``` + + ### `replaceContent` Replace the entire document content. -```javascript + + +```javascript Usage editor.replaceContent(proseMirrorJson); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +editor.replaceContent(proseMirrorJson); +``` + + + ### `replaceNodeWithHTML` Replace a specific node with HTML. -```javascript + + +```javascript Usage +const table = editor.getNodesOfType('table')[0]; +editor.replaceNodeWithHTML(table, '...
'); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + const table = editor.getNodesOfType('table')[0]; editor.replaceNodeWithHTML(table, '...
'); ``` +
+ ## Editor control ### `mount` / `unmount` -```javascript + + +```javascript Usage +editor.mount(document.querySelector('#editor')); +editor.unmount(); // Keeps instance alive +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + editor.mount(document.querySelector('#editor')); editor.unmount(); // Keeps instance alive ``` + + ### `destroy` Permanently destroy the editor. Irreversible. The instance cannot be used after this. -```javascript + + +```javascript Usage +editor.destroy(); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + editor.destroy(); ``` + + ### `focus` / `blur` -```javascript + + +```javascript Usage +editor.focus(); +editor.blur(); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + editor.focus(); editor.blur(); ``` + + ### `setEditable` -```javascript + + +```javascript Usage +editor.setEditable(false); // Read-only +editor.setEditable(true); // Editable +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + editor.setEditable(false); // Read-only editor.setEditable(true); // Editable ``` + + ### `setDocumentMode` -```javascript + + +```javascript Usage +editor.setDocumentMode('editing'); // Full editing +editor.setDocumentMode('suggesting'); // Track changes +editor.setDocumentMode('viewing'); // Read-only +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + editor.setDocumentMode('editing'); // Full editing editor.setDocumentMode('suggesting'); // Track changes editor.setDocumentMode('viewing'); // Read-only ``` + + ## Commands All commands are accessed via `editor.commands`: -```javascript + + +```javascript Usage // Formatting editor.commands.toggleBold(); editor.commands.toggleItalic(); @@ -230,6 +501,29 @@ editor.commands.setTextSelection({ from: 10, to: 20 }); editor.commands.selectAll(); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +// Formatting +editor.commands.toggleBold(); +editor.commands.toggleItalic(); +editor.commands.toggleUnderline(); + +// Tables +editor.commands.insertTable({ rows: 3, cols: 3 }); + +// Selection +editor.commands.setTextSelection({ from: 10, to: 20 }); +editor.commands.selectAll(); +``` + + + ### `insertContent` Insert content with automatic format detection. @@ -249,48 +543,127 @@ Insert content with automatic format detection. -```javascript + + +```javascript Usage editor.commands.insertContent(htmlContent, { contentType: 'html' }); editor.commands.insertContent(markdownText, { contentType: 'markdown' }); editor.commands.insertContent('Plain text', { contentType: 'text' }); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +editor.commands.insertContent(htmlContent, { contentType: 'html' }); +editor.commands.insertContent(markdownText, { contentType: 'markdown' }); +editor.commands.insertContent('Plain text', { contentType: 'text' }); +``` + + + HTML and Markdown inline styles are stripped on import to ensure Word compatibility. ## Document metadata ### `getMetadata` -```javascript + + +```javascript Usage const { documentGuid, isModified, version } = editor.getMetadata(); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +const { documentGuid, isModified, version } = editor.getMetadata(); +``` + + + ### `getDocumentIdentifier` Get a stable identifier (GUID or content hash). -```javascript + + +```javascript Usage +const id = await editor.getDocumentIdentifier(); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + const id = await editor.getDocumentIdentifier(); ``` + + ### `isDocumentModified` -```javascript + + +```javascript Usage if (editor.isDocumentModified()) { // Prompt user to save } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +if (editor.isDocumentModified()) { + // Prompt user to save +} +``` + + + ## Schema ### `getSchemaSummaryJSON` Generate a summary of the document schema. Useful for AI agents that need to understand the document structure. -```javascript + + +```javascript Usage +const summary = await editor.getSchemaSummaryJSON(); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + const summary = await editor.getSchemaSummaryJSON(); ``` + + ## Position & coordinates ### `getElementAtPos` @@ -301,62 +674,175 @@ Get the DOM element at a document position. Document position -```javascript + + +```javascript Usage +const element = editor.getElementAtPos(42); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + const element = editor.getElementAtPos(42); ``` + + ### `getNodesOfType` Get all nodes of a specific type. -```javascript + + +```javascript Usage const tables = editor.getNodesOfType('table'); const paragraphs = editor.getNodesOfType('paragraph'); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +const tables = editor.getNodesOfType('table'); +const paragraphs = editor.getNodesOfType('paragraph'); +``` + + + ### `isActive` Check if a node or mark is active. -```javascript + + +```javascript Usage editor.isActive('bold'); editor.isActive('heading', { level: 2 }); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +editor.isActive('bold'); +editor.isActive('heading', { level: 2 }); +``` + + + ### `getAttributes` Get attributes of the active node or mark. -```javascript + + +```javascript Usage const attrs = editor.getAttributes('link'); console.log(attrs.href); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +const attrs = editor.getAttributes('link'); +console.log(attrs.href); +``` + + + ## Page & layout ### `getPageStyles` -```javascript + + +```javascript Usage const styles = editor.getPageStyles(); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +const styles = editor.getPageStyles(); +``` + + + ### `updatePageStyle` -```javascript + + +```javascript Usage +editor.updatePageStyle({ + pageMargins: { top: '1in', bottom: '1in', left: '1in', right: '1in' } +}); +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + editor.updatePageStyle({ pageMargins: { top: '1in', bottom: '1in', left: '1in', right: '1in' } }); ``` + + ## Search -```javascript + + +```javascript Usage const results = editor.commands.search('hello'); const results = editor.commands.search(/\d{3}-\d{4}/gi); editor.commands.goToSearchResult(results[0]); ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; +import 'superdoc/style.css'; + +const editor = await Editor.open(yourFile, { + element: document.getElementById('editor'), +}); + +const results = editor.commands.search('hello'); +const results = editor.commands.search(/\d{3}-\d{4}/gi); + +editor.commands.goToSearchResult(results[0]); +``` + + + ## Properties | Property | Type | Description | From 880a0f9375fe7a060a2c4ba7abdb44e859aaa8e2 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 13:51:37 -0300 Subject: [PATCH 03/13] chore: add import validation script for docs code examples Adds scripts/validate-code-imports.js that scans all MDX files for JS/TS code blocks and validates import paths against an allowlist. Run with `pnpm run check:imports` from apps/docs. --- apps/docs/package.json | 3 +- apps/docs/scripts/validate-code-imports.js | 215 +++++++++++++++++++++ 2 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 apps/docs/scripts/validate-code-imports.js diff --git a/apps/docs/package.json b/apps/docs/package.json index e596425ce1..d0517124fa 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -7,7 +7,8 @@ "gen:api": "node scripts/sync-api-docs.js", "gen:docs": "node scripts/sync-sdk-docs.js", "gen:all": "pnpm run gen:api && pnpm run gen:docs", - "check:links": "mintlify broken-links" + "check:links": "mintlify broken-links", + "check:imports": "node scripts/validate-code-imports.js" }, "devDependencies": { "documentation": "^14.0.3", diff --git a/apps/docs/scripts/validate-code-imports.js b/apps/docs/scripts/validate-code-imports.js new file mode 100644 index 0000000000..4b230a66ca --- /dev/null +++ b/apps/docs/scripts/validate-code-imports.js @@ -0,0 +1,215 @@ +#!/usr/bin/env node + +/** + * Validates import statements in MDX code blocks. + * + * Scans all .mdx files under apps/docs/ for JS/TS code blocks + * and checks that every import path is on the allowlist. + */ + +const fs = require('fs'); +const path = require('path'); + +// ── Allowlists ────────────────────────────────────────────────────────────── + +const EXACT_SUPERDOC_IMPORTS = new Set([ + 'superdoc', + 'superdoc/super-editor', + 'superdoc/types', + 'superdoc/converter', + 'superdoc/docx-zipper', + 'superdoc/file-zipper', + 'superdoc/style.css', + '@superdoc-dev/ai', + '@superdoc-dev/esign', + '@superdoc-dev/esign/styles.css', + '@superdoc-dev/template-builder', + '@superdoc-dev/template-builder/defaults', + '@superdoc-dev/superdoc-yjs-collaboration', +]); + +const EXACT_EXTERNAL_IMPORTS = new Set([ + 'react', + 'react-dom', + 'react-dom/client', + 'vue', + 'yjs', + 'y-prosemirror', + 'openai', + 'bun:test', + 'hocuspocus', + 'fastify', + 'express', + 'cors', + 'pg', + 'ioredis', +]); + +const PREFIX_EXTERNAL_IMPORTS = [ + '@angular/', + 'prosemirror-', + 'node:', + 'fs/', + '@hocuspocus/', + '@tiptap/', + '@tiptap-pro/', + '@liveblocks/', + '@y-sweet/', + '@fastify/', + '@aws-sdk/', + 'next/', +]; + +// ── Helpers ───────────────────────────────────────────────────────────────── + +const RESET = '\x1b[0m'; +const RED = '\x1b[31m'; +const GREEN = '\x1b[32m'; +const YELLOW = '\x1b[33m'; +const CYAN = '\x1b[36m'; +const BOLD = '\x1b[1m'; +const DIM = '\x1b[2m'; + +function isImportAllowed(importPath) { + // Relative imports are always allowed + if (importPath.startsWith('./') || importPath.startsWith('../')) { + return true; + } + + if (EXACT_SUPERDOC_IMPORTS.has(importPath)) return true; + if (EXACT_EXTERNAL_IMPORTS.has(importPath)) return true; + + for (const prefix of PREFIX_EXTERNAL_IMPORTS) { + if (importPath.startsWith(prefix)) return true; + } + + return false; +} + +/** + * Recursively collect all .mdx files under `dir`. + */ +function globMdx(dir) { + const results = []; + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + // Skip node_modules / hidden dirs + if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue; + results.push(...globMdx(full)); + } else if (entry.isFile() && entry.name.endsWith('.mdx')) { + results.push(full); + } + } + return results; +} + +/** + * Strip regions inside HTML comments () so we don't + * inspect code blocks that are commented out. + * Returns an array of lines with commented regions replaced by empty strings + * while preserving line count. + */ +function stripHtmlComments(content) { + // Replace comment contents with empty lines to keep line numbers stable + return content.replace(//g, (match) => { + // Preserve the same number of newlines + return match.replace(/[^\n]/g, ''); + }); +} + +const CODE_FENCE_REGEX = /^```(?:js|javascript|ts|typescript|jsx|tsx)(?:\s.*)?$/; +const IMPORT_REGEX = /import\s+(?:(?:[\s\S]*?)\s+from\s+)?['"]([^'"]+)['"]/g; + +/** + * Validate a single MDX file. Returns an array of error objects. + */ +function validateFile(filePath) { + const raw = fs.readFileSync(filePath, 'utf-8'); + const content = stripHtmlComments(raw); + const lines = content.split('\n'); + + const errors = []; + let insideCodeBlock = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmed = line.trim(); + + if (!insideCodeBlock) { + if (CODE_FENCE_REGEX.test(trimmed)) { + insideCodeBlock = true; + } + continue; + } + + // Inside a code block + if (trimmed.startsWith('```')) { + insideCodeBlock = false; + continue; + } + + // Look for imports on this line + let match; + IMPORT_REGEX.lastIndex = 0; + while ((match = IMPORT_REGEX.exec(line)) !== null) { + const importPath = match[1]; + if (!isImportAllowed(importPath)) { + errors.push({ + file: filePath, + line: i + 1, // 1-indexed + importPath, + text: line.trim(), + }); + } + } + } + + return errors; +} + +// ── Main ──────────────────────────────────────────────────────────────────── + +function main() { + const docsRoot = path.resolve(__dirname, '..'); + const files = globMdx(docsRoot); + + console.log(`${CYAN}${BOLD}Validating imports in ${files.length} MDX files...${RESET}\n`); + + let totalErrors = 0; + const errorsByFile = new Map(); + + for (const file of files) { + const errors = validateFile(file); + if (errors.length > 0) { + const rel = path.relative(docsRoot, file); + errorsByFile.set(rel, errors); + totalErrors += errors.length; + } + } + + if (totalErrors === 0) { + console.log(`${GREEN}${BOLD}All imports are valid.${RESET}`); + process.exit(0); + } + + console.log(`${RED}${BOLD}Found ${totalErrors} invalid import${totalErrors === 1 ? '' : 's'}:${RESET}\n`); + + for (const [relFile, errors] of errorsByFile) { + console.log(`${YELLOW}${BOLD}${relFile}${RESET}`); + for (const err of errors) { + console.log(` ${DIM}${err.line}${RESET} ${RED}Invalid import: ${BOLD}${err.importPath}${RESET}`); + console.log(` ${DIM}${err.text}${RESET}`); + } + console.log(); + } + + console.log( + `${RED}${BOLD}${totalErrors} error${totalErrors === 1 ? '' : 's'} found. ` + + `Please use only allowed import paths in code examples.${RESET}`, + ); + + process.exit(1); +} + +main(); From 1e839210ed73b3766ecd9568e690da44e533e5be Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 13:54:36 -0300 Subject: [PATCH 04/13] docs: add CodeGroup tab pattern to CLAUDE.md --- apps/docs/CLAUDE.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/apps/docs/CLAUDE.md b/apps/docs/CLAUDE.md index e96b6fea25..81b9fd58b9 100644 --- a/apps/docs/CLAUDE.md +++ b/apps/docs/CLAUDE.md @@ -99,6 +99,48 @@ Always verify API names against the source code before documenting. Key source f Common components: `ParamField`, `Note`, `Warning`, `Tip`, `CardGroup`, `Card`, `Tabs`, `Tab`, `Info`. +## Code examples pattern + +Every code snippet in API/reference pages must be copy-pasteable. Use `` with two tabs when a snippet is a fragment (assumes prior setup): + +- **Usage** tab — the focused snippet (what the method does) +- **Full Example** tab — complete, runnable code with imports and initialization + +```mdx + + +‍```javascript Usage +const blob = await superdoc.export({ isFinalDoc: true }); +‍``` + +‍```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: async (superdoc) => { + const blob = await superdoc.export({ isFinalDoc: true }); + }, +}); +‍``` + + +``` + +**Boilerplate by context:** + +| Context | Initialization | +|---|---| +| SuperDoc methods | `new SuperDoc({ selector, document, onReady })` | +| SuperEditor methods | `const editor = await Editor.open(file, { element })` | +| Extension commands | `editor.commands.X()` inside SuperDoc onReady or Editor.open | + +**When NOT to use CodeGroup:** Snippets that are already complete (have imports + initialization), config-only blocks, bash commands, XML/HTML examples. + +Run `pnpm run check:imports` to validate all import paths in code examples. + ## Commands - `npx mintlify dev` — Start local dev server From 55cc5aa846a42d6d06a3cbc7791dd035a44a36c7 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 13:55:55 -0300 Subject: [PATCH 05/13] docs: add CodeGroup tabs to comments, toolbar, and import-export pages --- apps/docs/getting-started/import-export.mdx | 104 +++++++++- apps/docs/modules/comments.mdx | 214 +++++++++++++++++++- apps/docs/modules/toolbar.mdx | 173 +++++++++++++++- 3 files changed, 469 insertions(+), 22 deletions(-) diff --git a/apps/docs/getting-started/import-export.mdx b/apps/docs/getting-started/import-export.mdx index 41fd84bc28..efec519b29 100644 --- a/apps/docs/getting-started/import-export.mdx +++ b/apps/docs/getting-started/import-export.mdx @@ -75,12 +75,29 @@ new SuperDoc({ Insert content into an existing document using editor commands. -```javascript + +```javascript Usage superdoc.activeEditor.commands.insertContent(content, { contentType: 'html' // 'html' | 'markdown' | 'text' | 'schema' }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + superdoc.activeEditor.commands.insertContent(content, { + contentType: 'html' // 'html' | 'markdown' | 'text' | 'schema' + }); + }, +}); +``` + + The content to insert. String for `html`, `markdown`, and `text`. ProseMirror JSON object for `schema`. @@ -93,7 +110,8 @@ superdoc.activeEditor.commands.insertContent(content, { ### DOCX export -```javascript + +```javascript Usage // Download as .docx file await superdoc.export(); @@ -110,6 +128,33 @@ await superdoc.export({ isFinalDoc: true }); await superdoc.export({ exportedName: 'Final Contract' }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: async (superdoc) => { + // Download as .docx file + await superdoc.export(); + + // Get blob without triggering download + const blob = await superdoc.export({ triggerDownload: false }); + + // Export without comments + await superdoc.export({ commentsType: 'clean' }); + + // Export as final document (applies tracked changes) + await superdoc.export({ isFinalDoc: true }); + + // Custom filename + await superdoc.export({ exportedName: 'Final Contract' }); + }, +}); +``` + + Formats to export @@ -144,7 +189,8 @@ await superdoc.export({ exportedName: 'Final Contract' }); ### HTML export -```javascript + +```javascript Usage // Returns array of HTML strings (one per document) const htmlArray = superdoc.getHTML(); @@ -152,26 +198,74 @@ const htmlArray = superdoc.getHTML(); const html = superdoc.activeEditor.getHTML(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + // Returns array of HTML strings (one per document) + const htmlArray = superdoc.getHTML(); + + // From the active editor (single string) + const html = superdoc.activeEditor.getHTML(); + }, +}); +``` + + HTML export is structure-only. Custom CSS styling and Word-specific formatting are not included. ### JSON export -```javascript + +```javascript Usage // From the active editor const json = superdoc.activeEditor.getJSON(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const json = superdoc.activeEditor.getJSON(); + }, +}); +``` + + JSON export preserves the full document structure and can be re-imported with `jsonOverride`. ### Markdown export -```javascript + +```javascript Usage // From the active editor const markdown = await superdoc.activeEditor.getMarkdown(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: async (superdoc) => { + const markdown = await superdoc.activeEditor.getMarkdown(); + }, +}); +``` + + ## HTML element mapping | HTML Element | Imported As | Notes | diff --git a/apps/docs/modules/comments.mdx b/apps/docs/modules/comments.mdx index 733fc43099..6517eca9cc 100644 --- a/apps/docs/modules/comments.mdx +++ b/apps/docs/modules/comments.mdx @@ -168,12 +168,31 @@ modules: { After initialization: -```javascript + + +```javascript Usage superdoc.on("ready", () => { superdoc.addCommentsList("#comments-sidebar"); }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + user: { name: 'John Smith', email: 'john@company.com' }, + modules: { comments: { allowResolve: true } }, + onReady: (superdoc) => { + superdoc.addCommentsList("#comments-sidebar"); + }, +}); +``` + + + ## Permission resolver Customize who can resolve comments or accept tracked changes. The resolver receives the permission type, current user, and any tracked-change metadata. Return `false` to block the action. @@ -208,7 +227,9 @@ modules: { Word comments are automatically imported with the document and marked with `importedId`. When exporting, use the `commentsType` option: -```javascript + + +```javascript Usage // Include comments in export const blob = await superdoc.export({ commentsType: "external" }); @@ -216,6 +237,27 @@ const blob = await superdoc.export({ commentsType: "external" }); const cleanBlob = await superdoc.export({ commentsType: "clean" }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + user: { name: 'John Smith', email: 'john@company.com' }, + modules: { comments: { allowResolve: true } }, + onReady: async (superdoc) => { + // Include comments in export + const blob = await superdoc.export({ commentsType: "external" }); + + // Remove all comments + const cleanBlob = await superdoc.export({ commentsType: "clean" }); + }, +}); +``` + + + ## API methods These methods are available on the active editor's commands: @@ -224,7 +266,9 @@ These methods are available on the active editor's commands: Add a comment to the current text selection. Requires a text selection. -```javascript + + +```javascript Usage // Simple usage superdoc.activeEditor.commands.addComment("Review this section"); @@ -237,6 +281,32 @@ superdoc.activeEditor.commands.addComment({ }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + user: { name: 'John Smith', email: 'john@company.com' }, + modules: { comments: { allowResolve: true } }, + onReady: (superdoc) => { + // Simple usage + superdoc.activeEditor.commands.addComment("Review this section"); + + // With options + superdoc.activeEditor.commands.addComment({ + content: "Please clarify this section", + author: "John Smith", + authorEmail: "john@company.com", + isInternal: true, + }); + }, +}); +``` + + + Comment content as a string, or an options object with: @@ -263,13 +333,35 @@ superdoc.activeEditor.commands.addComment({ Add a reply to an existing comment or tracked change. -```javascript + + +```javascript Usage superdoc.activeEditor.commands.addCommentReply({ parentId: "comment-123", content: "I agree with this suggestion", }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + user: { name: 'John Smith', email: 'john@company.com' }, + modules: { comments: { allowResolve: true } }, + onReady: (superdoc) => { + superdoc.activeEditor.commands.addCommentReply({ + parentId: "comment-123", + content: "I agree with this suggestion", + }); + }, +}); +``` + + + @@ -292,49 +384,153 @@ superdoc.activeEditor.commands.addCommentReply({ ### `removeComment` -```javascript + + +```javascript Usage superdoc.activeEditor.commands.removeComment({ commentId: "comment-123", }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + user: { name: 'John Smith', email: 'john@company.com' }, + modules: { comments: { allowResolve: true } }, + onReady: (superdoc) => { + superdoc.activeEditor.commands.removeComment({ + commentId: "comment-123", + }); + }, +}); +``` + + + ### `setActiveComment` Highlight and focus a comment. -```javascript + + +```javascript Usage superdoc.activeEditor.commands.setActiveComment({ commentId: "comment-123", }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + user: { name: 'John Smith', email: 'john@company.com' }, + modules: { comments: { allowResolve: true } }, + onReady: (superdoc) => { + superdoc.activeEditor.commands.setActiveComment({ + commentId: "comment-123", + }); + }, +}); +``` + + + ### `resolveComment` -```javascript + + +```javascript Usage superdoc.activeEditor.commands.resolveComment({ commentId: "comment-123", }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + user: { name: 'John Smith', email: 'john@company.com' }, + modules: { comments: { allowResolve: true } }, + onReady: (superdoc) => { + superdoc.activeEditor.commands.resolveComment({ + commentId: "comment-123", + }); + }, +}); +``` + + + ### `setCommentInternal` Toggle a comment between internal and external visibility. -```javascript + + +```javascript Usage superdoc.activeEditor.commands.setCommentInternal({ commentId: "comment-123", isInternal: true, }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + user: { name: 'John Smith', email: 'john@company.com' }, + modules: { comments: { allowResolve: true } }, + onReady: (superdoc) => { + superdoc.activeEditor.commands.setCommentInternal({ + commentId: "comment-123", + isInternal: true, + }); + }, +}); +``` + + + ### `setCursorById` Navigate the cursor to a comment's position in the document. -```javascript + + +```javascript Usage superdoc.activeEditor.commands.setCursorById("comment-123"); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + user: { name: 'John Smith', email: 'john@company.com' }, + modules: { comments: { allowResolve: true } }, + onReady: (superdoc) => { + superdoc.activeEditor.commands.setCursorById("comment-123"); + }, +}); +``` + + + ## Events ### `onCommentsUpdate` diff --git a/apps/docs/modules/toolbar.mdx b/apps/docs/modules/toolbar.mdx index eb654af6f6..984175d081 100644 --- a/apps/docs/modules/toolbar.mdx +++ b/apps/docs/modules/toolbar.mdx @@ -370,70 +370,227 @@ The toolbar automatically adapts based on the user's role: ## API methods -```javascript + + +```javascript Usage const toolbar = superdoc.toolbar; ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + toolbar: '#toolbar', + onReady: (superdoc) => { + const toolbar = superdoc.toolbar; + }, +}); +``` + + + ### `getToolbarItemByName` Get a toolbar item by its name. -```javascript + + +```javascript Usage const boldBtn = toolbar.getToolbarItemByName('bold'); boldBtn.active.value; // boolean boldBtn.disabled.value; // boolean ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + toolbar: '#toolbar', + onReady: (superdoc) => { + const toolbar = superdoc.toolbar; + const boldBtn = toolbar.getToolbarItemByName('bold'); + boldBtn.active.value; // boolean + boldBtn.disabled.value; // boolean + }, +}); +``` + + + ### `getToolbarItemByGroup` Get all toolbar items in a layout group. -```javascript + + +```javascript Usage const leftItems = toolbar.getToolbarItemByGroup('left'); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + toolbar: '#toolbar', + onReady: (superdoc) => { + const toolbar = superdoc.toolbar; + const leftItems = toolbar.getToolbarItemByGroup('left'); + }, +}); +``` + + + ### `updateToolbarState` Refresh all button states based on the current editor state. -```javascript + + +```javascript Usage toolbar.updateToolbarState(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + toolbar: '#toolbar', + onReady: (superdoc) => { + const toolbar = superdoc.toolbar; + toolbar.updateToolbarState(); + }, +}); +``` + + + ### `setZoom` Set the editor zoom level programmatically. -```javascript + + +```javascript Usage toolbar.setZoom(150); // 150% ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + toolbar: '#toolbar', + onReady: (superdoc) => { + const toolbar = superdoc.toolbar; + toolbar.setZoom(150); // 150% + }, +}); +``` + + + ### `destroy` Clean up toolbar resources and event listeners. -```javascript + + +```javascript Usage toolbar.destroy(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + toolbar: '#toolbar', + onReady: (superdoc) => { + const toolbar = superdoc.toolbar; + toolbar.destroy(); + }, +}); +``` + + + ## Events ### `superdoc-command` Fired when a SuperDoc-level command is executed (zoom, document mode). -```javascript + + +```javascript Usage toolbar.on('superdoc-command', ({ item, argument }) => { console.log(`Command: ${item.command}, arg: ${argument}`); }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + toolbar: '#toolbar', + onReady: (superdoc) => { + const toolbar = superdoc.toolbar; + toolbar.on('superdoc-command', ({ item, argument }) => { + console.log(`Command: ${item.command}, arg: ${argument}`); + }); + }, +}); +``` + + + ### `exception` Fired when an error occurs during a toolbar action. -```javascript + + +```javascript Usage toolbar.on('exception', ({ error, editor, originalError }) => { console.error('Toolbar error:', error); }); ``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + toolbar: '#toolbar', + onReady: (superdoc) => { + const toolbar = superdoc.toolbar; + toolbar.on('exception', ({ error, editor, originalError }) => { + console.error('Toolbar error:', error); + }); + }, +}); +``` + + From f4920150842697ffca32e30d4d32f744ad907fe8 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 14:09:43 -0300 Subject: [PATCH 06/13] docs: add CodeGroup tabs to all extension pages Wrap every code fragment across 36 extension doc pages with CodeGroup Usage/Full Example tabs so snippets are copy-pasteable. --- apps/docs/extensions/block-node.mdx | 134 ++- apps/docs/extensions/bold.mdx | 54 +- apps/docs/extensions/bookmarks.mdx | 37 +- apps/docs/extensions/bullet-list.mdx | 18 +- apps/docs/extensions/color.mdx | 37 +- apps/docs/extensions/comments.mdx | 141 +++- apps/docs/extensions/content-block.mdx | 37 +- apps/docs/extensions/custom-selection.mdx | 19 +- apps/docs/extensions/document-section.mdx | 188 ++++- apps/docs/extensions/document.mdx | 61 +- apps/docs/extensions/field-annotation.mdx | 879 +++++++++++++++++++- apps/docs/extensions/font-family.mdx | 37 +- apps/docs/extensions/font-size.mdx | 38 +- apps/docs/extensions/format-commands.mdx | 91 +- apps/docs/extensions/heading.mdx | 37 +- apps/docs/extensions/highlight.mdx | 55 +- apps/docs/extensions/history.mdx | 36 +- apps/docs/extensions/image.mdx | 71 +- apps/docs/extensions/italic.mdx | 54 +- apps/docs/extensions/line-break.mdx | 36 +- apps/docs/extensions/line-height.mdx | 37 +- apps/docs/extensions/link.mdx | 59 +- apps/docs/extensions/linked-styles.mdx | 94 ++- apps/docs/extensions/ordered-list.mdx | 36 +- apps/docs/extensions/overview.mdx | 23 +- apps/docs/extensions/page-number.mdx | 36 +- apps/docs/extensions/paragraph.mdx | 54 +- apps/docs/extensions/search.mdx | 56 +- apps/docs/extensions/strike.mdx | 54 +- apps/docs/extensions/structured-content.mdx | 187 ++++- apps/docs/extensions/table.mdx | 420 +++++++++- apps/docs/extensions/text-align.mdx | 37 +- apps/docs/extensions/text-indent.mdx | 76 +- apps/docs/extensions/text-style.mdx | 18 +- apps/docs/extensions/track-changes.mdx | 159 +++- apps/docs/extensions/underline.mdx | 54 +- 36 files changed, 3286 insertions(+), 174 deletions(-) diff --git a/apps/docs/extensions/block-node.mdx b/apps/docs/extensions/block-node.mdx index 2da5a9475a..3d71f3dfee 100644 --- a/apps/docs/extensions/block-node.mdx +++ b/apps/docs/extensions/block-node.mdx @@ -20,11 +20,28 @@ The replacement node should have the same type as the original **Example:** -```javascript + +```javascript Usage const newParagraph = editor.schema.nodes.paragraph.create({}, editor.schema.text('New content')) editor.commands.replaceBlockNodeById('block-123', newParagraph) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const newParagraph = editor.schema.nodes.paragraph.create({}, editor.schema.text('New content')) + editor.commands.replaceBlockNodeById('block-123', newParagraph) + }, +}); +``` + + **Parameters:** @@ -44,10 +61,26 @@ Completely removes the node from the document **Example:** -```javascript + +```javascript Usage editor.commands.deleteBlockNodeById('block-123') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.deleteBlockNodeById('block-123') + }, +}); +``` + + **Parameters:** @@ -64,10 +97,26 @@ Merges new attributes with existing ones **Example:** -```javascript + +```javascript Usage editor.commands.updateBlockNodeAttributes('block-123', { textAlign: 'center' }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.updateBlockNodeAttributes('block-123', { textAlign: 'center' }) + }, +}); +``` + + **Parameters:** @@ -85,11 +134,28 @@ Get all block nodes in the document **Example:** -```javascript + +```javascript Usage const blocks = editor.helpers.blockNode.getBlockNodes() console.log(`Found ${blocks.length} block nodes`) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const blocks = editor.helpers.blockNode.getBlockNodes() + console.log(`Found ${blocks.length} block nodes`) + }, +}); +``` + + **Returns:** @@ -102,11 +168,28 @@ Get a specific block node by its ID **Example:** -```javascript + +```javascript Usage const block = editor.helpers.blockNode.getBlockNodeById('block-123') if (block.length) console.log('Found:', block[0].node.type.name) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const block = editor.helpers.blockNode.getBlockNodeById('block-123') + if (block.length) console.log('Found:', block[0].node.type.name) + }, +}); +``` + + **Parameters:** @@ -125,11 +208,28 @@ Get all block nodes of a specific type **Example:** -```javascript + +```javascript Usage const paragraphs = editor.helpers.blockNode.getBlockNodesByType('paragraph') const headings = editor.helpers.blockNode.getBlockNodesByType('heading') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const paragraphs = editor.helpers.blockNode.getBlockNodesByType('paragraph') + const headings = editor.helpers.blockNode.getBlockNodesByType('heading') + }, +}); +``` + + **Parameters:** @@ -148,7 +248,8 @@ Get all block nodes within a position range **Example:** -```javascript + +```javascript Usage const selection = editor.state.selection const blocksInSelection = editor.helpers.blockNode.getBlockNodesInRange( selection.from, @@ -156,6 +257,25 @@ const blocksInSelection = editor.helpers.blockNode.getBlockNodesInRange( ) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const selection = editor.state.selection + const blocksInSelection = editor.helpers.blockNode.getBlockNodesInRange( + selection.from, + selection.to + ) + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/bold.mdx b/apps/docs/extensions/bold.mdx index f60499821c..82dc80f641 100644 --- a/apps/docs/extensions/bold.mdx +++ b/apps/docs/extensions/bold.mdx @@ -14,26 +14,74 @@ import Description from '/snippets/extensions/bold.mdx' Apply bold formatting to the current selection. -```javascript + +```javascript Usage editor.commands.setBold() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setBold(); + }, +}); +``` + + ### `unsetBold` Remove bold formatting from the current selection. -```javascript + +```javascript Usage editor.commands.unsetBold() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetBold(); + }, +}); +``` + + ### `toggleBold` Toggle bold formatting on the current selection. -```javascript + +```javascript Usage editor.commands.toggleBold() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleBold(); + }, +}); +``` + + ## Keyboard shortcuts | Command | Shortcut | Description | diff --git a/apps/docs/extensions/bookmarks.mdx b/apps/docs/extensions/bookmarks.mdx index 7fd120f68e..1a19161131 100644 --- a/apps/docs/extensions/bookmarks.mdx +++ b/apps/docs/extensions/bookmarks.mdx @@ -16,11 +16,28 @@ Insert a bookmark at the current cursor position. Bookmarks are invisible naviga **Example:** -```javascript + +```javascript Usage editor.commands.insertBookmark({ name: 'chapter1' }) editor.commands.insertBookmark({ name: 'introduction', id: 'intro-001' }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.insertBookmark({ name: 'chapter1' }) + editor.commands.insertBookmark({ name: 'introduction', id: 'intro-001' }) + }, +}); +``` + + **Parameters:** @@ -33,10 +50,26 @@ Navigate to a bookmark by name. Scrolls the document to the bookmark position. **Example:** -```javascript + +```javascript Usage editor.commands.goToBookmark('chapter1') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.goToBookmark('chapter1') + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/bullet-list.mdx b/apps/docs/extensions/bullet-list.mdx index 66b473337b..73ce6c3168 100644 --- a/apps/docs/extensions/bullet-list.mdx +++ b/apps/docs/extensions/bullet-list.mdx @@ -20,10 +20,26 @@ Toggle bullet list formatting on the current selection. Converts selected paragr **Example:** -```javascript + +```javascript Usage editor.commands.toggleBulletList() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleBulletList() + }, +}); +``` + + ## Keyboard shortcuts | Command | Shortcut | Description | diff --git a/apps/docs/extensions/color.mdx b/apps/docs/extensions/color.mdx index 9fe416b684..f6b831351d 100644 --- a/apps/docs/extensions/color.mdx +++ b/apps/docs/extensions/color.mdx @@ -36,11 +36,28 @@ Preserves other text styling attributes **Example:** -```javascript + +```javascript Usage // Set to red using hex editor.commands.setColor('#ff0000') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Set to red using hex + editor.commands.setColor('#ff0000'); + }, +}); +``` + + **Parameters:** @@ -57,10 +74,26 @@ Removes color while preserving other text styles **Example:** -```javascript + +```javascript Usage editor.commands.unsetColor() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetColor(); + }, +}); +``` + + ## Types ### `ColorValue` diff --git a/apps/docs/extensions/comments.mdx b/apps/docs/extensions/comments.mdx index 4932c9efb5..fdb9a22752 100644 --- a/apps/docs/extensions/comments.mdx +++ b/apps/docs/extensions/comments.mdx @@ -24,7 +24,8 @@ modules: { Add a comment to the current text selection. Requires text to be selected. -```javascript + +```javascript Usage // Simple text comment editor.commands.addComment('Please review this section') @@ -37,6 +38,30 @@ editor.commands.addComment({ }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Simple text comment + editor.commands.addComment('Please review this section') + + // With options + editor.commands.addComment({ + content: 'Please review', + author: 'Jane Smith', + authorEmail: 'jane@example.com', + isInternal: false + }) + }, +}); +``` + + **Parameters:** @@ -47,13 +72,32 @@ editor.commands.addComment({ Add a reply to an existing comment thread. -```javascript + +```javascript Usage editor.commands.addCommentReply({ parentId: 'comment-123', content: 'Done, updated the wording.' }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.addCommentReply({ + parentId: 'comment-123', + content: 'Done, updated the wording.' + }) + }, +}); +``` + + **Parameters:** @@ -64,10 +108,26 @@ editor.commands.addCommentReply({ Remove a comment and its highlight marks from the document. -```javascript + +```javascript Usage editor.commands.removeComment({ commentId: 'abc-123' }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.removeComment({ commentId: 'abc-123' }) + }, +}); +``` + + **Parameters:** @@ -78,10 +138,26 @@ editor.commands.removeComment({ commentId: 'abc-123' }) Resolve a comment. Removes the highlight but preserves positional anchors for export. -```javascript + +```javascript Usage editor.commands.resolveComment({ commentId: 'abc-123' }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.resolveComment({ commentId: 'abc-123' }) + }, +}); +``` + + **Parameters:** @@ -92,29 +168,80 @@ editor.commands.resolveComment({ commentId: 'abc-123' }) Set the active/selected comment thread by ID. -```javascript + +```javascript Usage editor.commands.setActiveComment({ commentId: 'abc-123' }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setActiveComment({ commentId: 'abc-123' }) + }, +}); +``` + + ### `setCommentInternal` Toggle whether a comment is internal (private) or external. -```javascript + +```javascript Usage editor.commands.setCommentInternal({ commentId: 'abc-123', isInternal: true }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setCommentInternal({ + commentId: 'abc-123', + isInternal: true + }) + }, +}); +``` + + ### `setCursorById` Move the cursor to the start of a comment's range. Also works for tracked change IDs. -```javascript + +```javascript Usage editor.commands.setCursorById('abc-123') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setCursorById('abc-123') + }, +}); +``` + + ## Events ```javascript diff --git a/apps/docs/extensions/content-block.mdx b/apps/docs/extensions/content-block.mdx index 3d03b712fa..adc31c97cf 100644 --- a/apps/docs/extensions/content-block.mdx +++ b/apps/docs/extensions/content-block.mdx @@ -44,10 +44,26 @@ Creates a visual separator between content sections **Example:** -```javascript + +```javascript Usage editor.commands.insertHorizontalRule() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.insertHorizontalRule() + }, +}); +``` + + ### `insertContentBlock` Insert a content block @@ -58,11 +74,28 @@ Used for spacing, dividers, and special inline content **Example:** -```javascript + +```javascript Usage // Insert a spacer block editor.commands.insertContentBlock({ size: { height: 20 } }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Insert a spacer block + editor.commands.insertContentBlock({ size: { height: 20 } }) + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/custom-selection.mdx b/apps/docs/extensions/custom-selection.mdx index b65016279d..49f557a353 100644 --- a/apps/docs/extensions/custom-selection.mdx +++ b/apps/docs/extensions/custom-selection.mdx @@ -20,11 +20,28 @@ Used internally to maintain selection when interacting with toolbar **Example:** -```javascript + +```javascript Usage // Restore selection after toolbar interaction editor.commands.restorePreservedSelection() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Restore selection after toolbar interaction + editor.commands.restorePreservedSelection() + }, +}); +``` + + **Returns:** `Function` Command function ## Types diff --git a/apps/docs/extensions/document-section.mdx b/apps/docs/extensions/document-section.mdx index 921eccbfb8..ed4d7b1496 100644 --- a/apps/docs/extensions/document-section.mdx +++ b/apps/docs/extensions/document-section.mdx @@ -19,7 +19,8 @@ import { DocumentSection } from 'superdoc/super-editor'; Create a new document section. -```javascript + +```javascript Usage editor.commands.createDocumentSection({ id: 'legal-1', title: 'Terms & Conditions', @@ -29,35 +30,105 @@ editor.commands.createDocumentSection({ }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.createDocumentSection({ + id: 'legal-1', + title: 'Terms & Conditions', + description: 'Legal terms', + isLocked: true, + html: '

Content...

' + }) + }, +}); +``` +
+ ### `removeSectionAtSelection` Remove the section at the current cursor position. -```javascript + +```javascript Usage editor.commands.removeSectionAtSelection() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.removeSectionAtSelection() + }, +}); +``` + + ### `removeSectionById` Remove a section by its ID. -```javascript + +```javascript Usage editor.commands.removeSectionById('legal-1') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.removeSectionById('legal-1') + }, +}); +``` + + ### `lockSectionById` Lock a section to prevent editing. -```javascript + +```javascript Usage editor.commands.lockSectionById('legal-1') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.lockSectionById('legal-1') + }, +}); +``` + + ### `updateSectionById` Update a section's content or attributes. -```javascript + +```javascript Usage editor.commands.updateSectionById({ id: 'legal-1', html: '

Updated...

', @@ -65,9 +136,29 @@ editor.commands.updateSectionById({ }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.updateSectionById({ + id: 'legal-1', + html: '

Updated...

', + attrs: { title: 'New Title' } + }) + }, +}); +``` +
+ ## Helpers -```javascript + +```javascript Usage // Get all sections const sections = editor.helpers.documentSection.getAllSections(editor); @@ -83,6 +174,33 @@ const childEditor = editor.helpers.documentSection.getLinkedSectionEditor( ); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Get all sections + const sections = editor.helpers.documentSection.getAllSections(editor); + + // Export sections + const html = editor.helpers.documentSection.exportSectionsToHTML(editor); + const json = editor.helpers.documentSection.exportSectionsToJSON(editor); + + // Create a linked editor for a section + const childEditor = editor.helpers.documentSection.getLinkedSectionEditor( + 'section-id', + { element: '#editor' }, + editor + ); + }, +}); +``` + + ## Attributes | Attribute | Type | Default | Description | @@ -114,7 +232,8 @@ Sections export as Word content controls: ### Contract structure -```javascript + +```javascript Usage const contractSections = [ { id: 'parties', title: '1. Parties', isLocked: false }, { id: 'terms', title: '2. Terms', isLocked: true }, @@ -130,9 +249,37 @@ contractSections.forEach(section => { }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const contractSections = [ + { id: 'parties', title: '1. Parties', isLocked: false }, + { id: 'terms', title: '2. Terms', isLocked: true }, + { id: 'pricing', title: '3. Pricing', isLocked: false }, + { id: 'signatures', title: '4. Signatures', isLocked: true } + ]; + + contractSections.forEach(section => { + editor.commands.createDocumentSection({ + ...section, + html: loadTemplate(section.id) + }); + }); + }, +}); +``` + + ### Role-based locking -```javascript + +```javascript Usage function applyRolePermissions(userRole) { const sections = editor.helpers.documentSection.getAllSections(editor); @@ -146,6 +293,31 @@ function applyRolePermissions(userRole) { } ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + function applyRolePermissions(userRole) { + const sections = editor.helpers.documentSection.getAllSections(editor); + + sections.forEach(({ node }) => { + const { id, sectionType } = node.attrs; + + if (sectionType === 'legal' && userRole !== 'legal') { + editor.commands.lockSectionById(id); + } + }); + } + }, +}); +``` + + ## Related - [Field Annotation](/extensions/field-annotation) - Form fields diff --git a/apps/docs/extensions/document.mdx b/apps/docs/extensions/document.mdx index d34b074827..95e9752f40 100644 --- a/apps/docs/extensions/document.mdx +++ b/apps/docs/extensions/document.mdx @@ -20,12 +20,30 @@ Returns word count, character count, and paragraph count **Example:** -```javascript + +```javascript Usage // Get word and character count const stats = editor.commands.getDocumentStats() console.log(`${stats.words} words, ${stats.characters} characters`) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Get word and character count + const stats = editor.commands.getDocumentStats() + console.log(`${stats.words} words, ${stats.characters} characters`) + }, +}); +``` + + ### `clearDocument` Clear entire document @@ -36,17 +54,34 @@ Replaces all content with an empty paragraph **Example:** -```javascript + +```javascript Usage editor.commands.clearDocument() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.clearDocument() + }, +}); +``` + + ### `setSectionPageMarginsAtSelection` Set page margins for the section at the current cursor position. **Example:** -```javascript + +```javascript Usage editor.commands.setSectionPageMarginsAtSelection({ topInches: 1, rightInches: 1, @@ -55,6 +90,26 @@ editor.commands.setSectionPageMarginsAtSelection({ }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setSectionPageMarginsAtSelection({ + topInches: 1, + rightInches: 1, + bottomInches: 1, + leftInches: 1 + }) + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/field-annotation.mdx b/apps/docs/extensions/field-annotation.mdx index 4ab1ae7556..75fd63890f 100644 --- a/apps/docs/extensions/field-annotation.mdx +++ b/apps/docs/extensions/field-annotation.mdx @@ -44,7 +44,8 @@ const superdoc = new SuperDoc({ **Form Template Workflow:** -```javascript + +```javascript Usage // 1. Create form fields const fields = [ { id: 'name', label: 'Full Name', type: 'text' }, @@ -73,6 +74,46 @@ editor.annotate([ ]); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // 1. Create form fields + const fields = [ + { id: 'name', label: 'Full Name', type: 'text' }, + { id: 'date', label: 'Date', type: 'text' }, + { id: 'signature', label: 'Sign Here', type: 'signature' } + ]; + + // 2. Insert fields into document + fields.forEach((field, i) => { + editor.commands.addFieldAnnotation(i * 100, { + fieldId: field.id, + displayLabel: field.label, + type: field.type + }); + }); + + // 3. Handle user interactions + editor.on('fieldAnnotationClicked', ({ node }) => { + openFieldEditor(node.attrs.fieldId); + }); + + // 4. Fill and export + editor.annotate([ + { input_id: 'name', input_value: 'John Doe' }, + { input_id: 'date', input_value: '2024-01-15' } + ]); + }, +}); +``` + + ## Commands ### Find & Search @@ -83,7 +124,8 @@ Find field annotations matching a predicate function Added in v0.10.3 -```javascript + +```javascript Usage // Find all signature fields const signatureFields = findFieldAnnotations( node => node.attrs.type === 'signature', @@ -91,7 +133,27 @@ const signatureFields = findFieldAnnotations( ); ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Find all signature fields + const signatureFields = findFieldAnnotations( + node => node.attrs.type === 'signature', + editor.state + ); + }, +}); +``` + + + +```javascript Usage // Find fields with specific color const redFields = findFieldAnnotations( node => node.attrs.fieldColor === '#FF0000', @@ -99,6 +161,25 @@ const redFields = findFieldAnnotations( ); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Find fields with specific color + const redFields = findFieldAnnotations( + node => node.attrs.fieldColor === '#FF0000', + editor.state + ); + }, +}); +``` + + **Parameters:** @@ -129,18 +210,54 @@ const redFields = findFieldAnnotations( Find all field annotations between two positions -```javascript + +```javascript Usage // Find fields in selection const { from, to } = state.selection; const selectedFields = findFieldAnnotationsBetween(from, to, state.doc); ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Find fields in selection + const { from, to } = editor.state.selection; + const selectedFields = findFieldAnnotationsBetween(from, to, editor.state.doc); + }, +}); +``` + + + +```javascript Usage // Find fields in specific range const rangeFields = findFieldAnnotationsBetween(100, 500, state.doc); console.log(`Found ${rangeFields.length} fields in range`); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Find fields in specific range + const rangeFields = findFieldAnnotationsBetween(100, 500, editor.state.doc); + console.log(`Found ${rangeFields.length} fields in range`); + }, +}); +``` + + **Parameters:** @@ -175,12 +292,30 @@ console.log(`Found ${rangeFields.length} fields in range`); Find field annotations by field ID(s) -```javascript + +```javascript Usage // Find single field const fields = findFieldAnnotationsByFieldId('field-123', state); ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Find single field + const fields = findFieldAnnotationsByFieldId('field-123', editor.state); + }, +}); +``` + + + +```javascript Usage // Find multiple fields const fields = findFieldAnnotationsByFieldId(['field-1', 'field-2'], state); fields.forEach(({ pos, node }) => { @@ -188,6 +323,25 @@ fields.forEach(({ pos, node }) => { }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Find multiple fields + const fields = findFieldAnnotationsByFieldId(['field-1', 'field-2'], editor.state); + fields.forEach(({ pos, node }) => { + console.log(`Found ${node.attrs.fieldId} at position ${pos}`); + }); + }, +}); +``` + + **Parameters:** @@ -220,13 +374,32 @@ Find the first field annotation matching a field ID Added in v0.10.2 -```javascript + +```javascript Usage const firstField = findFirstFieldAnnotationByFieldId('field-123', state); if (firstField) { console.log(`First occurrence at position ${firstField.pos}`); } ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const firstField = findFirstFieldAnnotationByFieldId('field-123', editor.state); + if (firstField) { + console.log(`First occurrence at position ${firstField.pos}`); + } + }, +}); +``` + + **Parameters:** @@ -251,13 +424,32 @@ Find field annotations in headers and footers by field ID or array of field IDs. Added in v0.11.2 -```javascript + +```javascript Usage const headerFooterAnnotations = findHeaderFooterAnnotationsByFieldId('field-123', editor, activeSectionEditor); headerFooterAnnotations.forEach(({ node, pos }) => { console.log(`Field ${node.attrs.fieldId} at position ${pos}`); }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const headerFooterAnnotations = findHeaderFooterAnnotationsByFieldId('field-123', editor, activeSectionEditor); + headerFooterAnnotations.forEach(({ node, pos }) => { + console.log(`Field ${node.attrs.fieldId} at position ${pos}`); + }); + }, +}); +``` + + **Parameters:** @@ -332,13 +524,32 @@ Get all field annotations with their bounding rectangles Added in v0.11.0 -```javascript + +```javascript Usage const annotationsWithRects = getAllFieldAnnotationsWithRect(view, state); annotationsWithRects.forEach(({ node, rect }) => { console.log(`Field ${node.attrs.fieldId} at ${rect.left}, ${rect.top}`); }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const annotationsWithRects = getAllFieldAnnotationsWithRect(editor.view, editor.state); + annotationsWithRects.forEach(({ node, rect }) => { + console.log(`Field ${node.attrs.fieldId} at ${rect.left}, ${rect.top}`); + }); + }, +}); +``` + + **Parameters:** @@ -376,13 +587,32 @@ Find field annotation nodes that were removed in a transaction Added in v0.11.2 -```javascript + +```javascript Usage const removedFields = findRemovedFieldAnnotations(transaction); removedFields.forEach(({ node, pos }) => { console.log(`Field ${node.attrs.fieldId} at position ${pos}`); }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const removedFields = findRemovedFieldAnnotations(transaction); + removedFields.forEach(({ node, pos }) => { + console.log(`Field ${node.attrs.fieldId} at position ${pos}`); + }); + }, +}); +``` + + **Parameters:** @@ -409,16 +639,50 @@ removedFields.forEach(({ node, pos }) => { Delete field annotations by field ID -```javascript + +```javascript Usage // Delete single field annotation editor.commands.deleteFieldAnnotations('field-123') ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Delete single field annotation + editor.commands.deleteFieldAnnotations('field-123') + }, +}); +``` + + + +```javascript Usage // Delete multiple field annotations editor.commands.deleteFieldAnnotations(['field-1', 'field-2']) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Delete multiple field annotations + editor.commands.deleteFieldAnnotations(['field-1', 'field-2']) + }, +}); +``` + + **Parameters:** @@ -433,11 +697,28 @@ editor.commands.deleteFieldAnnotations(['field-1', 'field-2']) Delete field annotations by node references -```javascript + +```javascript Usage const annotations = editor.helpers.fieldAnnotation.getAllFieldAnnotations(); editor.commands.deleteFieldAnnotationsByNode(annotations.slice(0, 2)); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const annotations = editor.helpers.fieldAnnotation.getAllFieldAnnotations(); + editor.commands.deleteFieldAnnotationsByNode(annotations.slice(0, 2)); + }, +}); +``` + + **Parameters:** @@ -452,11 +733,28 @@ editor.commands.deleteFieldAnnotationsByNode(annotations.slice(0, 2)); Delete a single field annotation -```javascript + +```javascript Usage const annotation = editor.helpers.fieldAnnotation.findFieldAnnotationsByFieldId('field-123')[0]; editor.commands.deleteFieldAnnotation(annotation); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const annotation = editor.helpers.fieldAnnotation.findFieldAnnotationsByFieldId('field-123')[0]; + editor.commands.deleteFieldAnnotation(annotation); + }, +}); +``` + + **Parameters:** @@ -471,16 +769,50 @@ editor.commands.deleteFieldAnnotation(annotation); Delete a portion of annotations associated with a field -```javascript + +```javascript Usage // Remove annotations starting from index 6 editor.commands.sliceFieldAnnotations('field-123', 5) ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Remove annotations starting from index 6 + editor.commands.sliceFieldAnnotations('field-123', 5) + }, +}); +``` + + + +```javascript Usage // Remove from multiple fields editor.commands.sliceFieldAnnotations(['field-1', 'field-2'], 5) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Remove from multiple fields + editor.commands.sliceFieldAnnotations(['field-1', 'field-2'], 5) + }, +}); +``` + + **Parameters:** @@ -501,7 +833,8 @@ editor.commands.sliceFieldAnnotations(['field-1', 'field-2'], 5) Add field annotation at specified position -```javascript + +```javascript Usage editor.commands.addFieldAnnotation(10, { fieldId: 'field-123', displayLabel: 'Enter your name', @@ -510,6 +843,26 @@ editor.commands.addFieldAnnotation(10, { }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.addFieldAnnotation(10, { + fieldId: 'field-123', + displayLabel: 'Enter your name', + fieldType: 'TEXTINPUT', + fieldColor: '#980043' + }) + }, +}); +``` + + **Parameters:** @@ -549,13 +902,32 @@ editor.commands.addFieldAnnotation(10, { Add field annotation at current selection position -```javascript + +```javascript Usage editor.commands.addFieldAnnotationAtSelection({ fieldId: 'field-456', displayLabel: 'Signature here' }, true) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.addFieldAnnotationAtSelection({ + fieldId: 'field-456', + displayLabel: 'Signature here' + }, true) + }, +}); +``` + + **Parameters:** @@ -576,7 +948,8 @@ editor.commands.addFieldAnnotationAtSelection({ Replace text ranges with field annotations -```javascript + +```javascript Usage editor.commands.replaceWithFieldAnnotation([{ from: 20, to: 45, @@ -588,6 +961,29 @@ editor.commands.replaceWithFieldAnnotation([{ }]) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.replaceWithFieldAnnotation([{ + from: 20, + to: 45, + attrs: { + fieldId: 'field-789', + fieldType: 'TEXTINPUT', + fieldColor: '#980043' + } + }]) + }, +}); +``` + + **Parameters:** @@ -619,11 +1015,28 @@ Replace field annotations with text labels in current selection Added in v0.11.0 -```javascript + +```javascript Usage // Replace all annotations in selection with their labels editor.commands.replaceFieldAnnotationsWithLabelInSelection() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Replace all annotations in selection with their labels + editor.commands.replaceFieldAnnotationsWithLabelInSelection() + }, +}); +``` + + **Parameters:** @@ -640,12 +1053,30 @@ Replace field annotations with text labels Added in v0.11.0 -```javascript + +```javascript Usage // Replace specific fields with labels editor.commands.replaceFieldAnnotationsWithLabel(['field-1', 'field-2']) ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Replace specific fields with labels + editor.commands.replaceFieldAnnotationsWithLabel(['field-1', 'field-2']) + }, +}); +``` + + + +```javascript Usage // Replace only text annotations in selection editor.commands.replaceFieldAnnotationsWithLabel(null, { isInSelection: true, @@ -653,6 +1084,25 @@ editor.commands.replaceFieldAnnotationsWithLabel(null, { }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Replace only text annotations in selection + editor.commands.replaceFieldAnnotationsWithLabel(null, { + isInSelection: true, + types: ['text'] + }) + }, +}); +``` + + **Parameters:** @@ -682,18 +1132,36 @@ editor.commands.replaceFieldAnnotationsWithLabel(null, { Reset all field annotations to their default values -```javascript + +```javascript Usage // Reset all annotations in document editor.commands.resetFieldAnnotations() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Reset all annotations in document + editor.commands.resetFieldAnnotations() + }, +}); +``` + + **Returns:** `boolean` Command success status #### `updateFieldAnnotations` Update field annotations by field ID -```javascript + +```javascript Usage // Update single field editor.commands.updateFieldAnnotations('field-123', { displayLabel: 'Updated label', @@ -701,13 +1169,51 @@ editor.commands.updateFieldAnnotations('field-123', { }) ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Update single field + editor.commands.updateFieldAnnotations('field-123', { + displayLabel: 'Updated label', + fieldColor: '#FF0000' + }) + }, +}); +``` + + + +```javascript Usage // Update multiple fields editor.commands.updateFieldAnnotations(['field-1', 'field-2'], { hidden: true }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Update multiple fields + editor.commands.updateFieldAnnotations(['field-1', 'field-2'], { + hidden: true + }) + }, +}); +``` + + **Parameters:** @@ -737,7 +1243,8 @@ editor.commands.updateFieldAnnotations(['field-1', 'field-2'], { Update a specific field annotation instance -```javascript + +```javascript Usage // Update specific annotation instance const annotation = editor.helpers.fieldAnnotation.findFirstFieldAnnotationByFieldId('field-123', state); editor.commands.updateFieldAnnotation(annotation, { @@ -745,6 +1252,25 @@ editor.commands.updateFieldAnnotation(annotation, { }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Update specific annotation instance + const annotation = editor.helpers.fieldAnnotation.findFirstFieldAnnotationByFieldId('field-123', editor.state); + editor.commands.updateFieldAnnotation(annotation, { + displayLabel: 'New label' + }) + }, +}); +``` + + **Parameters:** @@ -763,7 +1289,8 @@ editor.commands.updateFieldAnnotation(annotation, { Update the attributes of annotations -```javascript + +```javascript Usage const annotations = editor.helpers.fieldAnnotation.getAllFieldAnnotations(); editor.commands.updateFieldAnnotationsAttributes(annotations, { fieldColor: '#FF0000', @@ -771,6 +1298,25 @@ editor.commands.updateFieldAnnotationsAttributes(annotations, { }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const annotations = editor.helpers.fieldAnnotation.getAllFieldAnnotations(); + editor.commands.updateFieldAnnotationsAttributes(annotations, { + fieldColor: '#FF0000', + hidden: false + }); + }, +}); +``` + + **Parameters:** @@ -789,7 +1335,8 @@ editor.commands.updateFieldAnnotationsAttributes(annotations, { Hide field annotations based on a condition -```javascript + +```javascript Usage // Hide specific field IDs editor.commands.setFieldAnnotationsHiddenByCondition(node => { const targetIds = ['field-1', 'field-2', 'field-3']; @@ -797,7 +1344,27 @@ editor.commands.setFieldAnnotationsHiddenByCondition(node => { }) ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Hide specific field IDs + editor.commands.setFieldAnnotationsHiddenByCondition(node => { + const targetIds = ['field-1', 'field-2', 'field-3']; + return targetIds.includes(node.attrs.fieldId); + }) + }, +}); +``` + + + +```javascript Usage // Hide signature fields and show others editor.commands.setFieldAnnotationsHiddenByCondition( node => node.attrs.type === 'signature', @@ -805,6 +1372,25 @@ editor.commands.setFieldAnnotationsHiddenByCondition( ) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Hide signature fields and show others + editor.commands.setFieldAnnotationsHiddenByCondition( + node => node.attrs.type === 'signature', + true + ) + }, +}); +``` + + **Parameters:** @@ -823,27 +1409,78 @@ editor.commands.setFieldAnnotationsHiddenByCondition( Show all hidden field annotations -```javascript + +```javascript Usage // Make all annotations visible editor.commands.unsetFieldAnnotationsHidden() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Make all annotations visible + editor.commands.unsetFieldAnnotationsHidden() + }, +}); +``` + + **Returns:** `boolean` Command success status #### `setFieldAnnotationsVisibility` Set visibility for all field annotations -```javascript + +```javascript Usage // Make all annotations visible editor.commands.setFieldAnnotationsVisibility('visible') ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Make all annotations visible + editor.commands.setFieldAnnotationsVisibility('visible') + }, +}); +``` + + + +```javascript Usage // Hide all annotations (preserves layout) editor.commands.setFieldAnnotationsVisibility('hidden') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Hide all annotations (preserves layout) + editor.commands.setFieldAnnotationsVisibility('hidden') + }, +}); +``` + + **Parameters:** @@ -858,7 +1495,8 @@ editor.commands.setFieldAnnotationsVisibility('hidden') Set highlighted status for annotations matching predicate -```javascript + +```javascript Usage // Highlight specific field IDs editor.commands.setFieldAnnotationsHighlighted((node) => { const targetIds = ['field-1', 'field-2', 'field-3']; @@ -866,11 +1504,47 @@ editor.commands.setFieldAnnotationsHighlighted((node) => { }, true) ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Highlight specific field IDs + editor.commands.setFieldAnnotationsHighlighted((node) => { + const targetIds = ['field-1', 'field-2', 'field-3']; + return targetIds.includes(node.attrs.fieldId); + }, true) + }, +}); +``` + + + +```javascript Usage // Remove highlighting from all annotations editor.commands.setFieldAnnotationsHighlighted(() => true, false) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Remove highlighting from all annotations + editor.commands.setFieldAnnotationsHighlighted(() => true, false) + }, +}); +``` + + **Parameters:** @@ -889,16 +1563,50 @@ editor.commands.setFieldAnnotationsHighlighted(() => true, false) Toggle formatting for field annotations in selection -```javascript + +```javascript Usage // Toggle bold formatting on selected annotations editor.commands.toggleFieldAnnotationsFormat('bold'); ``` -```javascript +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Toggle bold formatting on selected annotations + editor.commands.toggleFieldAnnotationsFormat('bold'); + }, +}); +``` + + + +```javascript Usage // Toggle italic and update selection editor.commands.toggleFieldAnnotationsFormat('italic', true); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Toggle italic and update selection + editor.commands.toggleFieldAnnotationsFormat('italic', true); + }, +}); +``` + + **Parameters:** @@ -917,10 +1625,26 @@ editor.commands.toggleFieldAnnotationsFormat('italic', true); Set font family for field annotations in current selection -```javascript + +```javascript Usage editor.commands.setFieldAnnotationsFontFamily('Arial', true) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setFieldAnnotationsFontFamily('Arial', true) + }, +}); +``` + + **Parameters:** @@ -939,10 +1663,26 @@ editor.commands.setFieldAnnotationsFontFamily('Arial', true) Set font size for field annotations in current selection -```javascript + +```javascript Usage editor.commands.setFieldAnnotationsFontSize('14pt') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setFieldAnnotationsFontSize('14pt') + }, +}); +``` + + **Parameters:** @@ -961,10 +1701,26 @@ editor.commands.setFieldAnnotationsFontSize('14pt') Set text highlight color for field annotations in current selection -```javascript + +```javascript Usage editor.commands.setFieldAnnotationsTextHighlight('#ffff00') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setFieldAnnotationsTextHighlight('#ffff00') + }, +}); +``` + + **Parameters:** @@ -983,10 +1739,26 @@ editor.commands.setFieldAnnotationsTextHighlight('#ffff00') Set text color for field annotations in current selection -```javascript + +```javascript Usage editor.commands.setFieldAnnotationsTextColor('#0066cc') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setFieldAnnotationsTextColor('#0066cc') + }, +}); +``` + + **Parameters:** @@ -1023,7 +1795,8 @@ Track field annotation deletions in a transaction and emit events Added in v0.11.2 -```javascript + +```javascript Usage // Listen for field deletions editor.on('fieldAnnotationDeleted', ({ removedNodes }) => { removedNodes.forEach(({ node }) => { @@ -1034,6 +1807,28 @@ editor.on('fieldAnnotationDeleted', ({ removedNodes }) => { }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Listen for field deletions + editor.on('fieldAnnotationDeleted', ({ removedNodes }) => { + removedNodes.forEach(({ node }) => { + console.log(`Field ${node.attrs.fieldId} was deleted`); + // Clean up external references + removeFieldFromDatabase(node.attrs.fieldId); + }); + }); + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/font-family.mdx b/apps/docs/extensions/font-family.mdx index 49cb73ee7b..4d0ebefb6e 100644 --- a/apps/docs/extensions/font-family.mdx +++ b/apps/docs/extensions/font-family.mdx @@ -36,11 +36,28 @@ Preserves other text styling attributes **Example:** -```javascript + +```javascript Usage // Set to Arial editor.commands.setFontFamily('Arial') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Set to Arial + editor.commands.setFontFamily('Arial') + }, +}); +``` + + **Parameters:** @@ -57,10 +74,26 @@ Reverts to default document font **Example:** -```javascript + +```javascript Usage editor.commands.unsetFontFamily() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetFontFamily() + }, +}); +``` + + ## Types ### `FontFamilyValue` diff --git a/apps/docs/extensions/font-size.mdx b/apps/docs/extensions/font-size.mdx index 44a0eead4b..df6fb859aa 100644 --- a/apps/docs/extensions/font-size.mdx +++ b/apps/docs/extensions/font-size.mdx @@ -40,12 +40,30 @@ Automatically clamps to min/max values **Example:** -```javascript + +```javascript Usage editor.commands.setFontSize('14pt') editor.commands.setFontSize('18px') editor.commands.setFontSize(16) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setFontSize('14pt') + editor.commands.setFontSize('18px') + editor.commands.setFontSize(16) + }, +}); +``` + + **Parameters:** @@ -62,10 +80,26 @@ Reverts to default document size **Example:** -```javascript + +```javascript Usage editor.commands.unsetFontSize() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetFontSize() + }, +}); +``` + + ## Types ### `FontSizeDefaults` diff --git a/apps/docs/extensions/format-commands.mdx b/apps/docs/extensions/format-commands.mdx index e55b3756fc..caf2a672a1 100644 --- a/apps/docs/extensions/format-commands.mdx +++ b/apps/docs/extensions/format-commands.mdx @@ -20,10 +20,26 @@ Removes all marks and resets nodes to default paragraph **Example:** -```javascript + +```javascript Usage editor.commands.clearFormat() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.clearFormat() + }, +}); +``` + + ### `clearMarksFormat` Clear only mark formatting @@ -34,10 +50,26 @@ Removes bold, italic, underline, colors, etc. but preserves block structure **Example:** -```javascript + +```javascript Usage editor.commands.clearMarksFormat() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.clearMarksFormat() + }, +}); +``` + + ### `clearNodesFormat` Clear only node formatting @@ -48,10 +80,26 @@ Converts headings, lists, etc. to paragraphs but preserves text marks **Example:** -```javascript + +```javascript Usage editor.commands.clearNodesFormat() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.clearNodesFormat() + }, +}); +``` + + ### `copyFormat` Copy format from selection or apply copied format @@ -62,21 +110,54 @@ Works like format painter - first click copies, second click applies **Example:** -```javascript + +```javascript Usage editor.commands.copyFormat() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.copyFormat() + }, +}); +``` + + ### `toggleMarkCascade` Toggle a mark type with cascade behavior, handling nested marks and negation attributes. Used internally by formatting extensions (bold, italic, etc.) but can be called directly for custom mark toggling. **Example:** -```javascript + +```javascript Usage editor.commands.toggleMarkCascade('bold') editor.commands.toggleMarkCascade('italic', { extendEmptyMarkRange: true }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleMarkCascade('bold') + editor.commands.toggleMarkCascade('italic', { extendEmptyMarkRange: true }) + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/heading.mdx b/apps/docs/extensions/heading.mdx index 1fdba0d804..7e848a6e87 100644 --- a/apps/docs/extensions/heading.mdx +++ b/apps/docs/extensions/heading.mdx @@ -36,10 +36,26 @@ Converts current block to heading **Example:** -```javascript + +```javascript Usage editor.commands.setHeading({ level: 2 }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setHeading({ level: 2 }) + }, +}); +``` + + **Parameters:** @@ -56,11 +72,28 @@ Switches between heading and paragraph for the same level **Example:** -```javascript + +```javascript Usage editor.commands.toggleHeading({ level: 1 }) editor.commands.toggleHeading({ level: 3 }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleHeading({ level: 1 }) + editor.commands.toggleHeading({ level: 3 }) + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/highlight.mdx b/apps/docs/extensions/highlight.mdx index 4e9651650e..5dd927629f 100644 --- a/apps/docs/extensions/highlight.mdx +++ b/apps/docs/extensions/highlight.mdx @@ -32,11 +32,28 @@ Apply highlight with specified color **Example:** -```javascript + +```javascript Usage editor.commands.setHighlight('#FFEB3B') editor.commands.setHighlight('rgba(255, 235, 59, 0.5)') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setHighlight('#FFEB3B'); + editor.commands.setHighlight('rgba(255, 235, 59, 0.5)'); + }, +}); +``` + + **Parameters:** @@ -49,20 +66,52 @@ Remove highlight formatting **Example:** -```javascript + +```javascript Usage editor.commands.unsetHighlight() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetHighlight(); + }, +}); +``` + + ### `toggleHighlight` Toggle highlight formatting **Example:** -```javascript + +```javascript Usage editor.commands.toggleHighlight() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleHighlight(); + }, +}); +``` + + ## Keyboard shortcuts | Command | Shortcut | Description | diff --git a/apps/docs/extensions/history.mdx b/apps/docs/extensions/history.mdx index fa702d88dd..46e20a299e 100644 --- a/apps/docs/extensions/history.mdx +++ b/apps/docs/extensions/history.mdx @@ -32,10 +32,26 @@ Groups changes within the newGroupDelay window **Example:** -```javascript + +```javascript Usage editor.commands.undo() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.undo() + }, +}); +``` + + ### `redo` Redo the last undone action @@ -46,10 +62,26 @@ Only available after an undo action **Example:** -```javascript + +```javascript Usage editor.commands.redo() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.redo() + }, +}); +``` + + ## Keyboard shortcuts | Command | Shortcut | Description | diff --git a/apps/docs/extensions/image.mdx b/apps/docs/extensions/image.mdx index ad873e35f3..6e6850f067 100644 --- a/apps/docs/extensions/image.mdx +++ b/apps/docs/extensions/image.mdx @@ -104,7 +104,8 @@ Supports URLs, relative paths, and base64 data URIs **Example:** -```javascript + +```javascript Usage editor.commands.setImage({ src: 'https://example.com/image.jpg' }) editor.commands.setImage({ src: 'data:image/png;base64,...', @@ -113,6 +114,26 @@ editor.commands.setImage({ }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setImage({ src: 'https://example.com/image.jpg' }) + editor.commands.setImage({ + src: 'data:image/png;base64,...', + alt: 'Company logo', + size: { width: 200 } + }) + }, +}); +``` + + **Parameters:** @@ -125,7 +146,8 @@ Set the wrapping mode and attributes for the selected image **Example:** -```javascript + +```javascript Usage // No wrapping, behind document editor.commands.setWrapping({ type: 'None', attrs: {behindDoc: true} }) @@ -159,6 +181,51 @@ editor.commands.setWrapping({ }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // No wrapping, behind document + editor.commands.setWrapping({ type: 'None', attrs: {behindDoc: true} }) + + // Square wrapping on both sides with distances + editor.commands.setWrapping({ + type: 'Square', + attrs: { + wrapText: 'bothSides', + distTop: 10, + distBottom: 10, + distLeft: 10, + distRight: 10 + } + }) + + // Tight wrapping with polygon + editor.commands.setWrapping({ + type: 'Tight', + attrs: { + polygon: [[0, 0], [100, 0], [100, 100], [0, 100]] + } + }) + + // Top and bottom wrapping + editor.commands.setWrapping({ + type: 'TopAndBottom', + attrs: { + distTop: 15, + distBottom: 15 + } + }) + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/italic.mdx b/apps/docs/extensions/italic.mdx index 90ca736319..03875354a2 100644 --- a/apps/docs/extensions/italic.mdx +++ b/apps/docs/extensions/italic.mdx @@ -14,26 +14,74 @@ import Description from '/snippets/extensions/italic.mdx' Apply italic formatting to the current selection. -```javascript + +```javascript Usage editor.commands.setItalic() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setItalic(); + }, +}); +``` + + ### `unsetItalic` Remove italic formatting from the current selection. -```javascript + +```javascript Usage editor.commands.unsetItalic() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetItalic(); + }, +}); +``` + + ### `toggleItalic` Toggle italic formatting on the current selection. -```javascript + +```javascript Usage editor.commands.toggleItalic() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleItalic(); + }, +}); +``` + + ## Keyboard shortcuts | Command | Shortcut | Description | diff --git a/apps/docs/extensions/line-break.mdx b/apps/docs/extensions/line-break.mdx index 0cd41cf5ed..0f820241ba 100644 --- a/apps/docs/extensions/line-break.mdx +++ b/apps/docs/extensions/line-break.mdx @@ -20,10 +20,26 @@ Creates a soft break within the same paragraph **Example:** -```javascript + +```javascript Usage editor.commands.insertLineBreak() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.insertLineBreak() + }, +}); +``` + + ### `insertPageBreak` Insert a page break @@ -34,10 +50,26 @@ Forces content to start on a new page when printed **Example:** -```javascript + +```javascript Usage editor.commands.insertPageBreak() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.insertPageBreak() + }, +}); +``` + + ## Source code diff --git a/apps/docs/extensions/line-height.mdx b/apps/docs/extensions/line-height.mdx index c64810199c..b0bd8128ee 100644 --- a/apps/docs/extensions/line-height.mdx +++ b/apps/docs/extensions/line-height.mdx @@ -16,11 +16,28 @@ Set line height as a multiplier (e.g., 1.5 for 1.5x line spacing). **Example:** -```javascript + +```javascript Usage editor.commands.setLineHeight(1.5) editor.commands.setLineHeight(2) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setLineHeight(1.5) + editor.commands.setLineHeight(2) + }, +}); +``` + + **Parameters:** @@ -33,10 +50,26 @@ Remove line height and revert to default line spacing. **Example:** -```javascript + +```javascript Usage editor.commands.unsetLineHeight() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetLineHeight() + }, +}); +``` + + ## Source code diff --git a/apps/docs/extensions/link.mdx b/apps/docs/extensions/link.mdx index bbc360ceef..d9b0a1f334 100644 --- a/apps/docs/extensions/link.mdx +++ b/apps/docs/extensions/link.mdx @@ -88,7 +88,8 @@ Automatically adds underline formatting and trims whitespace from link boundarie **Example:** -```javascript + +```javascript Usage editor.commands.setLink({ href: 'https://example.com' }) editor.commands.setLink({ href: 'https://example.com', @@ -96,6 +97,25 @@ editor.commands.setLink({ }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setLink({ href: 'https://example.com' }) + editor.commands.setLink({ + href: 'https://example.com', + text: 'Visit Example' + }) + }, +}); +``` + + **Parameters:** @@ -112,21 +132,54 @@ Also removes underline and text color **Example:** -```javascript + +```javascript Usage editor.commands.unsetLink() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetLink() + }, +}); +``` + + ### `toggleLink` Toggle link on selection **Example:** -```javascript + +```javascript Usage editor.commands.toggleLink({ href: 'https://example.com' }) editor.commands.toggleLink() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleLink({ href: 'https://example.com' }) + editor.commands.toggleLink() + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/linked-styles.mdx b/apps/docs/extensions/linked-styles.mdx index 151542acbf..2d2ed72102 100644 --- a/apps/docs/extensions/linked-styles.mdx +++ b/apps/docs/extensions/linked-styles.mdx @@ -16,11 +16,28 @@ Apply a linked style to the selected paragraphs. **Example:** -```javascript + +```javascript Usage const style = editor.helpers.linkedStyles.getStyleById('Heading1'); editor.commands.setLinkedStyle(style); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const style = editor.helpers.linkedStyles.getStyleById('Heading1'); + editor.commands.setLinkedStyle(style); + }, +}); +``` + + **Parameters:** @@ -33,12 +50,30 @@ Toggle a linked style on the current selection. Removes the style if already app **Example:** -```javascript + +```javascript Usage const style = editor.helpers.linkedStyles.getStyleById('Heading1'); editor.commands.toggleLinkedStyle(style) editor.commands.toggleLinkedStyle(style, 'paragraph') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const style = editor.helpers.linkedStyles.getStyleById('Heading1'); + editor.commands.toggleLinkedStyle(style) + editor.commands.toggleLinkedStyle(style, 'paragraph') + }, +}); +``` + + **Parameters:** @@ -54,11 +89,28 @@ Apply a linked style by its ID. **Example:** -```javascript + +```javascript Usage editor.commands.setStyleById('Heading1') editor.commands.setStyleById('Normal') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setStyleById('Heading1') + editor.commands.setStyleById('Normal') + }, +}); +``` + + **Parameters:** @@ -73,10 +125,26 @@ Get all available linked styles from the document. **Example:** -```javascript + +```javascript Usage const styles = editor.helpers.linkedStyles.getStyles(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const styles = editor.helpers.linkedStyles.getStyles(); + }, +}); +``` + + **Returns:** @@ -89,10 +157,26 @@ Get a specific style by ID. **Example:** -```javascript + +```javascript Usage const headingStyle = editor.helpers.linkedStyles.getStyleById('Heading1'); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const headingStyle = editor.helpers.linkedStyles.getStyleById('Heading1'); + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/ordered-list.mdx b/apps/docs/extensions/ordered-list.mdx index 5ec2fcb761..27e7bbaafc 100644 --- a/apps/docs/extensions/ordered-list.mdx +++ b/apps/docs/extensions/ordered-list.mdx @@ -20,20 +20,52 @@ Toggle ordered list formatting on the current selection. Converts selected parag **Example:** -```javascript + +```javascript Usage editor.commands.toggleOrderedList() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleOrderedList() + }, +}); +``` + + ### `restartNumbering` Restart list numbering from the current position. **Example:** -```javascript + +```javascript Usage editor.commands.restartNumbering() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.restartNumbering() + }, +}); +``` + + ## Keyboard shortcuts | Command | Shortcut | Description | diff --git a/apps/docs/extensions/overview.mdx b/apps/docs/extensions/overview.mdx index 428ae0f040..f205296b79 100644 --- a/apps/docs/extensions/overview.mdx +++ b/apps/docs/extensions/overview.mdx @@ -16,13 +16,32 @@ Extensions provide: You don't typically configure extensions directly. SuperDoc handles it: -```javascript + +```javascript Usage // These commands come from extensions editor.commands.toggleBold(); // Bold extension -editor.commands.insertTable(); // Table extension +editor.commands.insertTable(); // Table extension editor.commands.acceptAllChanges(); // TrackChanges extension ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // These commands come from extensions + editor.commands.toggleBold(); // Bold extension + editor.commands.insertTable(); // Table extension + editor.commands.acceptAllChanges(); // TrackChanges extension + }, +}); +``` + + ## Extension categories ### Core editing diff --git a/apps/docs/extensions/page-number.mdx b/apps/docs/extensions/page-number.mdx index 8848702b20..06dbb7089b 100644 --- a/apps/docs/extensions/page-number.mdx +++ b/apps/docs/extensions/page-number.mdx @@ -28,10 +28,26 @@ Only works in header/footer contexts **Example:** -```javascript + +```javascript Usage editor.commands.addAutoPageNumber() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.addAutoPageNumber() + }, +}); +``` + + **Returns:** `Function` Command function ### `addTotalPageCount` @@ -44,10 +60,26 @@ Only works in header/footer contexts **Example:** -```javascript + +```javascript Usage editor.commands.addTotalPageCount() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.addTotalPageCount() + }, +}); +``` + + **Returns:** `Function` Command function ## Keyboard shortcuts diff --git a/apps/docs/extensions/paragraph.mdx b/apps/docs/extensions/paragraph.mdx index 6abee0474c..8676b83f75 100644 --- a/apps/docs/extensions/paragraph.mdx +++ b/apps/docs/extensions/paragraph.mdx @@ -14,26 +14,74 @@ import Description from '/snippets/extensions/paragraph.mdx' Convert selected paragraphs to a bullet list, or remove list formatting if already a bullet list. -```javascript + +```javascript Usage editor.commands.toggleBulletList() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleBulletList() + }, +}); +``` + + ### `toggleOrderedList` Convert selected paragraphs to an ordered list, or remove list formatting if already an ordered list. -```javascript + +```javascript Usage editor.commands.toggleOrderedList() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleOrderedList() + }, +}); +``` + + ### `restartNumbering` Reset list numbering for the current list item and following items. -```javascript + +```javascript Usage editor.commands.restartNumbering() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.restartNumbering() + }, +}); +``` + + ## Keyboard shortcuts | Command | Shortcut | Description | diff --git a/apps/docs/extensions/search.mdx b/apps/docs/extensions/search.mdx index 7e3e96a700..065c43d3d5 100644 --- a/apps/docs/extensions/search.mdx +++ b/apps/docs/extensions/search.mdx @@ -20,10 +20,26 @@ Scrolls editor to the first match from previous search **Example:** -```javascript + +```javascript Usage editor.commands.goToFirstMatch() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.goToFirstMatch() + }, +}); +``` + + ### `search` Search for string matches in editor content @@ -34,11 +50,28 @@ Returns array of SearchMatch objects with positions and IDs **Example:** -```javascript + +```javascript Usage const matches = editor.commands.search('test string') const regexMatches = editor.commands.search(/test/i) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const matches = editor.commands.search('test string') + const regexMatches = editor.commands.search(/test/i) + }, +}); +``` + + **Parameters:** @@ -64,11 +97,28 @@ Scrolls to match and selects it **Example:** -```javascript + +```javascript Usage const searchResults = editor.commands.search('test string') editor.commands.goToSearchResult(searchResults[3]) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const searchResults = editor.commands.search('test string') + editor.commands.goToSearchResult(searchResults[3]) + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/strike.mdx b/apps/docs/extensions/strike.mdx index e737dc6454..c73bdc8509 100644 --- a/apps/docs/extensions/strike.mdx +++ b/apps/docs/extensions/strike.mdx @@ -24,30 +24,78 @@ Apply strikethrough formatting **Example:** -```javascript + +```javascript Usage editor.commands.setStrike() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setStrike(); + }, +}); +``` + + ### `unsetStrike` Remove strikethrough formatting **Example:** -```javascript + +```javascript Usage editor.commands.unsetStrike() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetStrike(); + }, +}); +``` + + ### `toggleStrike` Toggle strikethrough formatting **Example:** -```javascript + +```javascript Usage editor.commands.toggleStrike() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleStrike(); + }, +}); +``` + + ## Keyboard shortcuts | Command | Shortcut | Description | diff --git a/apps/docs/extensions/structured-content.mdx b/apps/docs/extensions/structured-content.mdx index 244a33d771..605bff7f74 100644 --- a/apps/docs/extensions/structured-content.mdx +++ b/apps/docs/extensions/structured-content.mdx @@ -115,12 +115,30 @@ Update all structured content fields that share the same group identifier. **Example:** -```javascript + +```javascript Usage editor.commands.updateStructuredContentByGroup('pricing', { text: '$99.00' }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.updateStructuredContentByGroup('pricing', { + text: '$99.00' + }) + }, +}); +``` + + **Parameters:** @@ -136,11 +154,28 @@ Remove all structured content fields that share the same group identifier. **Example:** -```javascript + +```javascript Usage editor.commands.deleteStructuredContentByGroup('pricing') editor.commands.deleteStructuredContentByGroup(['pricing', 'deprecated']) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.deleteStructuredContentByGroup('pricing') + editor.commands.deleteStructuredContentByGroup(['pricing', 'deprecated']) + }, +}); +``` + + **Parameters:** @@ -154,7 +189,8 @@ Each inner array represents the cell values for one new row. **Example:** -```javascript + +```javascript Usage editor.commands.appendRowsToStructuredContentTable({ id: "block-123", tableIndex: 0, @@ -166,6 +202,29 @@ editor.commands.appendRowsToStructuredContentTable({ }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.appendRowsToStructuredContentTable({ + id: "block-123", + tableIndex: 0, + rows: [ + ["A", "B"], + ["C", "D"], + ], + copyRowStyle: true, + }); + }, +}); +``` + + **Parameters:** +```javascript Usage const blocks = editor.helpers.getStructuredContentBlockTags(editor.state); console.log(`Found ${blocks.length} structured content blocks`); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const blocks = editor.helpers.getStructuredContentBlockTags(editor.state); + console.log(`Found ${blocks.length} structured content blocks`); + }, +}); +``` +
+ **Parameters:** @@ -199,11 +275,28 @@ Get all inline structured content tags in the document **Example:** -```javascript + +```javascript Usage const inlines = editor.helpers.getStructuredContentInlineTags(editor.state); console.log(`Found ${inlines.length} inline fields`); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const inlines = editor.helpers.getStructuredContentInlineTags(editor.state); + console.log(`Found ${inlines.length} inline fields`); + }, +}); +``` + + **Parameters:** @@ -214,7 +307,8 @@ Find all tables inside a structured content block by ID **Example:** -```javascript + +```javascript Usage const tables = editor.helpers.getStructuredContentTablesById( "block-123", editor.state @@ -222,6 +316,25 @@ const tables = editor.helpers.getStructuredContentTablesById( console.log(`Block contains ${tables.length} table(s)`); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const tables = editor.helpers.getStructuredContentTablesById( + "block-123", + editor.state + ); + console.log(`Block contains ${tables.length} table(s)`); + }, +}); +``` + + **Parameters:** @@ -235,13 +348,32 @@ Find all structured content nodes that share the same group identifier. **Example:** -```javascript + +```javascript Usage const fields = editor.helpers.getStructuredContentByGroup('pricing', editor.state); fields.forEach(({ node, pos }) => { console.log(node.attrs.tag, pos); }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const fields = editor.helpers.getStructuredContentByGroup('pricing', editor.state); + fields.forEach(({ node, pos }) => { + console.log(node.attrs.tag, pos); + }); + }, +}); +``` + + **Parameters:** @@ -255,11 +387,28 @@ Get all structured content tags (inline and block) in the document **Example:** -```javascript + +```javascript Usage const allTags = editor.helpers.getStructuredContentTags(editor.state); console.log(`Found ${allTags.length} structured content elements`); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const allTags = editor.helpers.getStructuredContentTags(editor.state); + console.log(`Found ${allTags.length} structured content elements`); + }, +}); +``` + + **Parameters:** @@ -270,7 +419,8 @@ Get structured content tag(s) by ID **Example:** -```javascript + +```javascript Usage const field = editor.helpers.getStructuredContentTagsById( "field-123", editor.state @@ -278,6 +428,25 @@ const field = editor.helpers.getStructuredContentTagsById( if (field.length) console.log("Found field:", field[0].node.attrs); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + const field = editor.helpers.getStructuredContentTagsById( + "field-123", + editor.state + ); + if (field.length) console.log("Found field:", field[0].node.attrs); + }, +}); +``` + + **Parameters:** diff --git a/apps/docs/extensions/table.mdx b/apps/docs/extensions/table.mdx index 2194bc6962..648595882d 100644 --- a/apps/docs/extensions/table.mdx +++ b/apps/docs/extensions/table.mdx @@ -68,11 +68,28 @@ Insert a new table into the document **Example:** -```javascript + +```javascript Usage editor.commands.insertTable() editor.commands.insertTable({ rows: 3, cols: 3, withHeaderRow: true }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.insertTable() + editor.commands.insertTable({ rows: 3, cols: 3, withHeaderRow: true }) + }, +}); +``` + + **Parameters:** @@ -85,10 +102,26 @@ Delete the entire table containing the cursor **Example:** -```javascript + +```javascript Usage editor.commands.deleteTable() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.deleteTable() + }, +}); +``` + + ### `addColumnBefore` Add a column before the current column @@ -99,10 +132,26 @@ Preserves cell attributes from current column **Example:** -```javascript + +```javascript Usage editor.commands.addColumnBefore() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.addColumnBefore() + }, +}); +``` + + ### `addColumnAfter` Add a column after the current column @@ -113,10 +162,26 @@ Preserves cell attributes from current column **Example:** -```javascript + +```javascript Usage editor.commands.addColumnAfter() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.addColumnAfter() + }, +}); +``` + + **Returns:** `Function` Command ### `deleteColumn` @@ -125,10 +190,26 @@ Delete the column containing the cursor **Example:** -```javascript + +```javascript Usage editor.commands.deleteColumn() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.deleteColumn() + }, +}); +``` + + **Returns:** `Function` Command ### `addRowBefore` @@ -141,10 +222,26 @@ Preserves cell attributes from current row **Example:** -```javascript + +```javascript Usage editor.commands.addRowBefore() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.addRowBefore() + }, +}); +``` + + **Returns:** `Function` Command ### `addRowAfter` @@ -157,10 +254,26 @@ Preserves cell attributes from current row **Example:** -```javascript + +```javascript Usage editor.commands.addRowAfter() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.addRowAfter() + }, +}); +``` + + **Returns:** `Function` Command ### `deleteRow` @@ -169,10 +282,26 @@ Delete the row containing the cursor **Example:** -```javascript + +```javascript Usage editor.commands.deleteRow() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.deleteRow() + }, +}); +``` + + **Returns:** `Function` Command ### `mergeCells` @@ -185,10 +314,26 @@ Content from all cells is preserved **Example:** -```javascript + +```javascript Usage editor.commands.mergeCells() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.mergeCells() + }, +}); +``` + + **Returns:** `Function` Command ### `splitCell` @@ -197,10 +342,26 @@ Split a merged cell back into individual cells **Example:** -```javascript + +```javascript Usage editor.commands.splitCell() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.splitCell() + }, +}); +``` + + **Returns:** `Function` Command - true if split, false if position invalid ### `splitSingleCell` @@ -213,10 +374,26 @@ Different from splitCell which splits merged cells back to original cells **Example:** -```javascript + +```javascript Usage editor.commands.splitSingleCell() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.splitSingleCell() + }, +}); +``` + + **Returns:** `Function` Command - true if split, false if position invalid ### `mergeOrSplit` @@ -229,10 +406,26 @@ Merges if multiple cells selected, splits if merged cell selected **Example:** -```javascript + +```javascript Usage editor.commands.mergeOrSplit() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.mergeOrSplit() + }, +}); +``` + + **Returns:** `Function` Command ### `toggleHeaderColumn` @@ -241,10 +434,26 @@ Toggle the first column as header column **Example:** -```javascript + +```javascript Usage editor.commands.toggleHeaderColumn() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleHeaderColumn() + }, +}); +``` + + **Returns:** `Function` Command ### `toggleHeaderRow` @@ -253,10 +462,26 @@ Toggle the first row as header row **Example:** -```javascript + +```javascript Usage editor.commands.toggleHeaderRow() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleHeaderRow() + }, +}); +``` + + **Returns:** `Function` Command ### `toggleHeaderCell` @@ -265,10 +490,26 @@ Toggle current cell as header cell **Example:** -```javascript + +```javascript Usage editor.commands.toggleHeaderCell() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleHeaderCell() + }, +}); +``` + + **Returns:** `Function` Command ### `setCellAttr` @@ -277,11 +518,28 @@ Set an attribute on selected cells **Example:** -```javascript + +```javascript Usage editor.commands.setCellAttr('background', { color: 'ff0000' }) setCellAttr('verticalAlign', 'middle') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setCellAttr('background', { color: 'ff0000' }) + editor.commands.setCellAttr('verticalAlign', 'middle') + }, +}); +``` + + **Parameters:** @@ -299,10 +557,26 @@ Navigate to the next cell (Tab behavior) **Example:** -```javascript + +```javascript Usage editor.commands.goToNextCell() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.goToNextCell() + }, +}); +``` + + **Returns:** `Function` Command ### `goToPreviousCell` @@ -311,10 +585,26 @@ Navigate to the previous cell (Shift+Tab behavior) **Example:** -```javascript + +```javascript Usage editor.commands.goToPreviousCell() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.goToPreviousCell() + }, +}); +``` + + **Returns:** `Function` Command ### `fixTables` @@ -327,10 +617,26 @@ Repairs malformed tables and normalizes structure **Example:** -```javascript + +```javascript Usage editor.commands.fixTables() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.fixTables() + }, +}); +``` + + **Returns:** `Function` Command ### `setCellSelection` @@ -339,10 +645,26 @@ Set cell selection programmatically **Example:** -```javascript + +```javascript Usage editor.commands.setCellSelection({ anchorCell: 10, headCell: 15 }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setCellSelection({ anchorCell: 10, headCell: 15 }) + }, +}); +``` + + **Parameters:** @@ -357,11 +679,28 @@ Set background color for selected cells **Example:** -```javascript + +```javascript Usage editor.commands.setCellBackground('#ff0000') editor.commands.setCellBackground('ff0000') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setCellBackground('#ff0000') + editor.commands.setCellBackground('ff0000') + }, +}); +``` + + **Parameters:** @@ -374,13 +713,32 @@ Append rows with content to an existing table. **Example:** -```javascript + +```javascript Usage editor.commands.appendRowsWithContent({ valueRows: [['Cell A', 'Cell B'], ['Cell C', 'Cell D']], copyRowStyle: true }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.appendRowsWithContent({ + valueRows: [['Cell A', 'Cell B'], ['Cell C', 'Cell D']], + copyRowStyle: true + }) + }, +}); +``` + + **Parameters:** @@ -397,10 +755,26 @@ Sets all border sizes to 0 **Example:** -```javascript + +```javascript Usage editor.commands.deleteCellAndTableBorders() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.deleteCellAndTableBorders() + }, +}); +``` + + **Returns:** `Function` Command ## Keyboard shortcuts diff --git a/apps/docs/extensions/text-align.mdx b/apps/docs/extensions/text-align.mdx index 1e50949968..26a2e432d4 100644 --- a/apps/docs/extensions/text-align.mdx +++ b/apps/docs/extensions/text-align.mdx @@ -24,11 +24,28 @@ Set text alignment on the current paragraph. **Example:** -```javascript + +```javascript Usage editor.commands.setTextAlign('center') editor.commands.setTextAlign('justify') ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setTextAlign('center') + editor.commands.setTextAlign('justify') + }, +}); +``` + + **Parameters:** @@ -45,10 +62,26 @@ Resets alignment to the default value **Example:** -```javascript + +```javascript Usage editor.commands.unsetTextAlign() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetTextAlign() + }, +}); +``` + + ## Keyboard shortcuts | Command | Shortcut | Description | diff --git a/apps/docs/extensions/text-indent.mdx b/apps/docs/extensions/text-indent.mdx index dde97c2c6b..00d2791c58 100644 --- a/apps/docs/extensions/text-indent.mdx +++ b/apps/docs/extensions/text-indent.mdx @@ -16,7 +16,8 @@ Set text indentation in points. **Example:** -```javascript + +```javascript Usage // Set to 72 points (1 inch) editor.commands.setTextIndentation(72) @@ -24,6 +25,25 @@ editor.commands.setTextIndentation(72) editor.commands.setTextIndentation(36) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Set to 72 points (1 inch) + editor.commands.setTextIndentation(72) + + // Set to 36 points (0.5 inch) + editor.commands.setTextIndentation(36) + }, +}); +``` + + **Parameters:** @@ -36,30 +56,78 @@ Remove text indentation from selected paragraphs. **Example:** -```javascript + +```javascript Usage editor.commands.unsetTextIndentation() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetTextIndentation() + }, +}); +``` + + ### `increaseTextIndent` Increase text indentation by 36 points (0.5 inch). **Example:** -```javascript + +```javascript Usage editor.commands.increaseTextIndent() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.increaseTextIndent() + }, +}); +``` + + ### `decreaseTextIndent` Decrease text indentation by 36 points. Removes indentation completely if it reaches 0 or below. **Example:** -```javascript + +```javascript Usage editor.commands.decreaseTextIndent() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.decreaseTextIndent() + }, +}); +``` + + ## Source code diff --git a/apps/docs/extensions/text-style.mdx b/apps/docs/extensions/text-style.mdx index 8517fadc42..888736a443 100644 --- a/apps/docs/extensions/text-style.mdx +++ b/apps/docs/extensions/text-style.mdx @@ -40,10 +40,26 @@ Automatically checks if any style attributes exist before removal **Example:** -```javascript + +```javascript Usage editor.commands.removeEmptyTextStyle() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.removeEmptyTextStyle() + }, +}); +``` + + ## Source code import { SourceCodeLink } from '/snippets/components/source-code-link.jsx' diff --git a/apps/docs/extensions/track-changes.mdx b/apps/docs/extensions/track-changes.mdx index 2e31920171..c7a9c657cc 100644 --- a/apps/docs/extensions/track-changes.mdx +++ b/apps/docs/extensions/track-changes.mdx @@ -16,17 +16,36 @@ superdoc.setDocumentMode('editing'); // Disable tracking Or toggle programmatically: -```javascript + +```javascript Usage editor.commands.enableTrackChanges() editor.commands.disableTrackChanges() editor.commands.toggleTrackChanges() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.enableTrackChanges() + editor.commands.disableTrackChanges() + editor.commands.toggleTrackChanges() + }, +}); +``` + + ## Commands ### Accept changes -```javascript + +```javascript Usage // Accept at current selection editor.commands.acceptTrackedChangeBySelection() @@ -46,9 +65,41 @@ editor.commands.acceptAllTrackedChanges() editor.commands.acceptTrackedChangeFromToolbar() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Accept at current selection + editor.commands.acceptTrackedChangeBySelection() + + // Accept a specific change by ID + editor.commands.acceptTrackedChangeById('change-123') + + // Accept a change object (with start/end positions) + editor.commands.acceptTrackedChange({ trackedChange: { start: 10, end: 50 } }) + + // Accept changes in a range + editor.commands.acceptTrackedChangesBetween(10, 50) + + // Accept all changes in the document + editor.commands.acceptAllTrackedChanges() + + // Toolbar-aware accept (uses active thread or selection) + editor.commands.acceptTrackedChangeFromToolbar() + }, +}); +``` + + ### Reject changes -```javascript + +```javascript Usage // Reject at current selection editor.commands.rejectTrackedChangeOnSelection() @@ -68,11 +119,43 @@ editor.commands.rejectAllTrackedChanges() editor.commands.rejectTrackedChangeFromToolbar() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Reject at current selection + editor.commands.rejectTrackedChangeOnSelection() + + // Reject a specific change by ID + editor.commands.rejectTrackedChangeById('change-123') + + // Reject a change object + editor.commands.rejectTrackedChange({ trackedChange: { start: 10, end: 50 } }) + + // Reject changes in a range + editor.commands.rejectTrackedChangesBetween(10, 50) + + // Reject all changes in the document + editor.commands.rejectAllTrackedChanges() + + // Toolbar-aware reject + editor.commands.rejectTrackedChangeFromToolbar() + }, +}); +``` + + ### Insert tracked change programmatically Use `insertTrackedChange` to add tracked edits from external sources (e.g., AI suggestions): -```javascript + +```javascript Usage editor.commands.insertTrackedChange({ from: 10, to: 25, @@ -81,6 +164,26 @@ editor.commands.insertTrackedChange({ }) ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.insertTrackedChange({ + from: 10, + to: 25, + text: 'replacement text', + comment: 'AI suggestion: improved wording' + }) + }, +}); +``` + + **Parameters:** @@ -89,7 +192,8 @@ editor.commands.insertTrackedChange({ ### View modes -```javascript + +```javascript Usage // Show document as it was before changes editor.commands.toggleTrackChangesShowOriginal() editor.commands.enableTrackChangesShowOriginal() @@ -100,6 +204,28 @@ editor.commands.toggleTrackChangesShowFinal() editor.commands.enableTrackChangesShowFinal() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Show document as it was before changes + editor.commands.toggleTrackChangesShowOriginal() + editor.commands.enableTrackChangesShowOriginal() + editor.commands.disableTrackChangesShowOriginal() + + // Show document as if all changes were accepted + editor.commands.toggleTrackChangesShowFinal() + editor.commands.enableTrackChangesShowFinal() + }, +}); +``` + + ## Helpers ```javascript @@ -127,7 +253,8 @@ Each change includes author name, email, timestamp, and a unique ID. Changes export to DOCX as Word revisions: -```javascript + +```javascript Usage // Export with changes preserved await superdoc.export(); @@ -136,6 +263,26 @@ editor.commands.acceptAllTrackedChanges(); await superdoc.export(); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + // Export with changes preserved + await superdoc.export(); + + // Accept all first, then export clean + editor.commands.acceptAllTrackedChanges(); + await superdoc.export(); + }, +}); +``` + + ## Source code import { SourceCodeLink } from '/snippets/components/source-code-link.jsx' diff --git a/apps/docs/extensions/underline.mdx b/apps/docs/extensions/underline.mdx index 7481b82205..786c679b13 100644 --- a/apps/docs/extensions/underline.mdx +++ b/apps/docs/extensions/underline.mdx @@ -36,10 +36,26 @@ Apply underline formatting **Example:** -```javascript + +```javascript Usage editor.commands.setUnderline() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.setUnderline(); + }, +}); +``` + + **Returns:** `Function` Command ### `unsetUnderline` @@ -48,10 +64,26 @@ Remove underline formatting **Example:** -```javascript + +```javascript Usage editor.commands.unsetUnderline() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.unsetUnderline(); + }, +}); +``` + + **Returns:** `Function` Command ### `toggleUnderline` @@ -60,10 +92,26 @@ Toggle underline formatting **Example:** -```javascript + +```javascript Usage editor.commands.toggleUnderline() ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, + onReady: (superdoc) => { + const editor = superdoc.activeEditor; + editor.commands.toggleUnderline(); + }, +}); +``` + + **Returns:** `Function` Command ## Keyboard shortcuts From bac22631c83eb93086333b27443fe8b1a7f0ce92 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 14:17:24 -0300 Subject: [PATCH 07/13] docs: add CodeGroup tabs to events and ai-agents pages --- apps/docs/core/superdoc/events.mdx | 261 ++++++++++++++++++++++-- apps/docs/core/supereditor/events.mdx | 197 ++++++++++++++++-- apps/docs/getting-started/ai-agents.mdx | 15 +- 3 files changed, 445 insertions(+), 28 deletions(-) diff --git a/apps/docs/core/superdoc/events.mdx b/apps/docs/core/superdoc/events.mdx index 47ef05628b..6ac4d4ed4a 100644 --- a/apps/docs/core/superdoc/events.mdx +++ b/apps/docs/core/superdoc/events.mdx @@ -7,53 +7,137 @@ SuperDoc uses an event system for lifecycle hooks and change notifications. ## Subscribing to events -```javascript + +```javascript Usage superdoc.on('ready', handler); // Subscribe superdoc.once('ready', handler); // One-time listener superdoc.off('ready', handler); // Unsubscribe ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + +const handler = ({ superdoc }) => { + console.log('Handler called'); +}; + +superdoc.on('ready', handler); // Subscribe +superdoc.once('ready', handler); // One-time listener +superdoc.off('ready', handler); // Unsubscribe +``` + + ## Lifecycle events ### `ready` Fired when SuperDoc is fully initialized. -```javascript + +```javascript Usage superdoc.on('ready', ({ superdoc }) => { // Safe to use all features }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + +superdoc.on('ready', ({ superdoc }) => { + // Safe to use all features +}); +``` + + ### `editorBeforeCreate` Fired before an editor is created. Use this to configure extensions or set up services. -```javascript + +```javascript Usage +superdoc.on('editorBeforeCreate', ({ editor }) => { + // Configure before editor mounts +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + superdoc.on('editorBeforeCreate', ({ editor }) => { // Configure before editor mounts }); ``` + ### `editorCreate` When an editor is created. -```javascript + +```javascript Usage superdoc.on('editorCreate', ({ editor }) => { editor.focus(); }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + +superdoc.on('editorCreate', ({ editor }) => { + editor.focus(); +}); +``` + + ### `editorDestroy` When an editor is destroyed. -```javascript + +```javascript Usage +superdoc.on('editorDestroy', () => { + cleanup(); +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + superdoc.on('editorDestroy', () => { cleanup(); }); ``` + ## Content events @@ -61,33 +145,83 @@ superdoc.on('editorDestroy', () => { When editor content changes. -```javascript + +```javascript Usage +superdoc.on('editor-update', ({ editor }) => { + autoSave(editor.getJSON()); +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + superdoc.on('editor-update', ({ editor }) => { autoSave(editor.getJSON()); }); ``` + ### `content-error` When content processing fails. -```javascript + +```javascript Usage +superdoc.on('content-error', ({ error, editor, documentId }) => { + console.error('Content error:', error); +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + superdoc.on('content-error', ({ error, editor, documentId }) => { console.error('Content error:', error); }); ``` + ### `fonts-resolved` When document fonts are resolved. -```javascript + +```javascript Usage +superdoc.on('fonts-resolved', ({ documentFonts, unsupportedFonts }) => { + if (unsupportedFonts.length > 0) { + console.warn('Unsupported fonts:', unsupportedFonts.join(', ')); + } +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + superdoc.on('fonts-resolved', ({ documentFonts, unsupportedFonts }) => { if (unsupportedFonts.length > 0) { console.warn('Unsupported fonts:', unsupportedFonts.join(', ')); } }); ``` + ## Comments events @@ -95,46 +229,113 @@ superdoc.on('fonts-resolved', ({ documentFonts, unsupportedFonts }) => { When comments are modified. -```javascript + +```javascript Usage superdoc.on('comments-update', ({ type, data }) => { // type: 'add', 'update', 'deleted', 'resolved' }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + +superdoc.on('comments-update', ({ type, data }) => { + // type: 'add', 'update', 'deleted', 'resolved' +}); +``` + + ## Collaboration events ### `collaboration-ready` When collaboration is initialized. -```javascript + +```javascript Usage +superdoc.on('collaboration-ready', ({ editor }) => { + showOnlineUsers(); +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + superdoc.on('collaboration-ready', ({ editor }) => { showOnlineUsers(); }); ``` + ### `awareness-update` When user presence changes. -```javascript + +```javascript Usage superdoc.on('awareness-update', ({ context, states }) => { const users = Array.from(states.values()); updateUserCursors(users); }); ``` +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + +superdoc.on('awareness-update', ({ context, states }) => { + const users = Array.from(states.values()); + updateUserCursors(users); +}); +``` + + ### `locked` When document lock state changes. -```javascript + +```javascript Usage +superdoc.on('locked', ({ isLocked, lockedBy }) => { + if (isLocked) { + showLockBanner(`Locked by ${lockedBy.name}`); + } +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + superdoc.on('locked', ({ isLocked, lockedBy }) => { if (isLocked) { showLockBanner(`Locked by ${lockedBy.name}`); } }); ``` + ## UI events @@ -142,11 +343,27 @@ superdoc.on('locked', ({ isLocked, lockedBy }) => { When the comments sidebar is toggled. -```javascript + +```javascript Usage +superdoc.on('sidebar-toggle', (isOpened) => { + adjustLayout(isOpened); +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + superdoc.on('sidebar-toggle', (isOpened) => { adjustLayout(isOpened); }); ``` + ## Error events @@ -154,11 +371,27 @@ superdoc.on('sidebar-toggle', (isOpened) => { When an error occurs during document processing or runtime. -```javascript + +```javascript Usage +superdoc.on('exception', ({ error, document, editor }) => { + console.error('SuperDoc error:', error); +}); +``` + +```javascript Full Example +import { SuperDoc } from 'superdoc'; +import 'superdoc/style.css'; + +const superdoc = new SuperDoc({ + selector: '#editor', + document: yourFile, +}); + superdoc.on('exception', ({ error, document, editor }) => { console.error('SuperDoc error:', error); }); ``` + ## Configuration-based events diff --git a/apps/docs/core/supereditor/events.mdx b/apps/docs/core/supereditor/events.mdx index 18e2615f21..22da078e2a 100644 --- a/apps/docs/core/supereditor/events.mdx +++ b/apps/docs/core/supereditor/events.mdx @@ -11,49 +11,102 @@ Events let you respond to editor lifecycle and content changes. Called before the editor view is created. -```javascript + +```javascript Usage onBeforeCreate: ({ editor }) => { // Set up external services } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onBeforeCreate: ({ editor }) => { + // Set up external services + }, +}); +``` + + ### `onCreate` Called when editor is fully initialized and ready. -```javascript + +```javascript Usage onCreate: ({ editor }) => { editor.focus(); } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onCreate: ({ editor }) => { + editor.focus(); + }, +}); +``` + + ### `onDestroy` Called when editor is being destroyed. Clean up resources here. -```javascript + +```javascript Usage onDestroy: () => { clearInterval(autoSaveTimer); } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onDestroy: () => { + clearInterval(autoSaveTimer); + }, +}); +``` + + ### `onFirstRender` Called after the first render completes. -```javascript + +```javascript Usage onFirstRender: () => { hideLoadingSpinner(); } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onFirstRender: () => { + hideLoadingSpinner(); + }, +}); +``` + + ## Content ### `onUpdate` Called when document content changes. -```javascript + +```javascript Usage onUpdate: ({ editor, transaction }) => { if (transaction.docChanged) { saveToBackend(editor.getJSON()); @@ -61,78 +114,196 @@ onUpdate: ({ editor, transaction }) => { } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onUpdate: ({ editor, transaction }) => { + if (transaction.docChanged) { + saveToBackend(editor.getJSON()); + } + }, +}); +``` + + ### `onContentError` Called when content processing fails. -```javascript + +```javascript Usage onContentError: ({ error, editor, documentId }) => { console.error('Document error:', error); } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onContentError: ({ error, editor, documentId }) => { + console.error('Document error:', error); + }, +}); +``` + + ## Selection ### `onSelectionUpdate` Called when selection changes (cursor movement). -```javascript + +```javascript Usage onSelectionUpdate: ({ editor }) => { toolbar.bold = editor.isActive('bold'); } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onSelectionUpdate: ({ editor }) => { + toolbar.bold = editor.isActive('bold'); + }, +}); +``` + + ### `onFocus` Called when editor gains focus. -```javascript + +```javascript Usage onFocus: ({ editor, event }) => { showFormattingToolbar(); } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onFocus: ({ editor, event }) => { + showFormattingToolbar(); + }, +}); +``` + + ### `onBlur` Called when editor loses focus. -```javascript + +```javascript Usage onBlur: ({ editor, event }) => { saveCurrentState(); } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onBlur: ({ editor, event }) => { + saveCurrentState(); + }, +}); +``` + + ## Features ### `onCommentsUpdate` -```javascript + +```javascript Usage onCommentsUpdate: ({ editor }) => { updateCommentsSidebar(); } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onCommentsUpdate: ({ editor }) => { + updateCommentsSidebar(); + }, +}); +``` + + ### `onCommentsLoaded` -```javascript + +```javascript Usage onCommentsLoaded: ({ editor, comments }) => { console.log(`Loaded ${comments.length} comments`); } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onCommentsLoaded: ({ editor, comments }) => { + console.log(`Loaded ${comments.length} comments`); + }, +}); +``` + + ### `onTrackedChangesUpdate` -```javascript + +```javascript Usage onTrackedChangesUpdate: ({ editor }) => { updateReviewPanel(); } ``` +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onTrackedChangesUpdate: ({ editor }) => { + updateReviewPanel(); + }, +}); +``` + + ### `onCollaborationReady` -```javascript + +```javascript Usage onCollaborationReady: ({ editor, ydoc }) => { showCollaboratorsCursors(); } ``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile, { + element: document.querySelector('#editor'), + onCollaborationReady: ({ editor, ydoc }) => { + showCollaboratorsCursors(); + }, +}); +``` + diff --git a/apps/docs/getting-started/ai-agents.mdx b/apps/docs/getting-started/ai-agents.mdx index ccc0fa9c5d..71620d2c40 100644 --- a/apps/docs/getting-started/ai-agents.mdx +++ b/apps/docs/getting-started/ai-agents.mdx @@ -27,12 +27,25 @@ const docx = await editor.exportDocx(); ## Content formats -```javascript + +```javascript Usage +editor.commands.insertContent(value, { contentType: 'html' }); // Recommended for LLMs +editor.commands.insertContent(value, { contentType: 'markdown' }); +editor.commands.insertContent(value, { contentType: 'text' }); +editor.commands.insertContent(value, { contentType: 'schema' }); // ProseMirror JSON +``` + +```javascript Full Example +import { Editor } from 'superdoc/super-editor'; + +const editor = await Editor.open(yourFile); + editor.commands.insertContent(value, { contentType: 'html' }); // Recommended for LLMs editor.commands.insertContent(value, { contentType: 'markdown' }); editor.commands.insertContent(value, { contentType: 'text' }); editor.commands.insertContent(value, { contentType: 'schema' }); // ProseMirror JSON ``` + See [Import/Export](/getting-started/import-export) for format details, limitations, and guidance on choosing a format. From add621b0c2695632834f12b47e6bf9ee009e7144 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 14:21:34 -0300 Subject: [PATCH 08/13] docs: add CodeGroup to ai-agents redlining example --- apps/docs/getting-started/ai-agents.mdx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/docs/getting-started/ai-agents.mdx b/apps/docs/getting-started/ai-agents.mdx index 71620d2c40..9404e9e455 100644 --- a/apps/docs/getting-started/ai-agents.mdx +++ b/apps/docs/getting-started/ai-agents.mdx @@ -53,7 +53,8 @@ See [Import/Export](/getting-started/import-export) for format details, limitati Use `suggesting` mode so AI edits appear as tracked changes that users can accept or reject: -```javascript + +```javascript Usage const contract = await readFile('./contract.docx'); const editor = await Editor.open(contract, { documentMode: 'suggesting', @@ -64,6 +65,21 @@ editor.commands.insertContent(aiRevisions, { contentType: 'html' }); const redlined = await editor.exportDocx(); ``` +```javascript Full Example +import { readFile } from 'node:fs/promises'; +import { Editor } from 'superdoc/super-editor'; + +const contract = await readFile('./contract.docx'); +const editor = await Editor.open(contract, { + documentMode: 'suggesting', +}); + +// AI suggestions are tracked — users review them in Word +editor.commands.insertContent(aiRevisions, { contentType: 'html' }); +const redlined = await editor.exportDocx(); +``` + + ## LLM quick reference Point your AI assistant at these URLs for SuperDoc context: From e857f6b88b0681a3165f17c29606297864b747ce Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 17:37:37 -0300 Subject: [PATCH 09/13] feat: add doctest system for documentation code examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extracts "Full Example" code blocks from MDX files using remark/unified AST parsing, transforms them to run headlessly, and executes them against a real Editor instance to catch stale API references. Also fixes doc bugs caught by the tests: - overview.mdx: acceptAllChanges → acceptAllTrackedChanges - structured-content.mdx: add missing helper namespace --- apps/docs/__tests__/doctest.test.ts | 92 ++++ apps/docs/__tests__/lib/extract.ts | 133 ++++++ apps/docs/__tests__/lib/transform.ts | 170 ++++++++ apps/docs/__tests__/tsconfig.json | 24 ++ apps/docs/extensions/overview.mdx | 4 +- apps/docs/extensions/structured-content.mdx | 24 +- apps/docs/package.json | 9 +- pnpm-lock.yaml | 453 +++++--------------- pnpm-workspace.yaml | 2 +- 9 files changed, 545 insertions(+), 366 deletions(-) create mode 100644 apps/docs/__tests__/doctest.test.ts create mode 100644 apps/docs/__tests__/lib/extract.ts create mode 100644 apps/docs/__tests__/lib/transform.ts create mode 100644 apps/docs/__tests__/tsconfig.json diff --git a/apps/docs/__tests__/doctest.test.ts b/apps/docs/__tests__/doctest.test.ts new file mode 100644 index 0000000000..92ef2ff9bb --- /dev/null +++ b/apps/docs/__tests__/doctest.test.ts @@ -0,0 +1,92 @@ +import { test, describe, beforeAll, expect } from 'bun:test'; +import { resolve } from 'node:path'; +import { Editor, getStarterExtensions } from '../../../packages/superdoc/dist/super-editor.es.js'; +import { extractExamples } from './lib/extract.ts'; +import { transformCode, applyStubs } from './lib/transform.ts'; + +const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor; + +const docsRoot = resolve(import.meta.dir, '..'); +const fixturePath = resolve(import.meta.dir, '../../../packages/super-editor/src/tests/data/complex2.docx'); + +let fixtureBuffer: Buffer; + +beforeAll(async () => { + const bytes = await Bun.file(fixturePath).arrayBuffer(); + fixtureBuffer = Buffer.from(bytes); +}); + +/** + * Returns true if the error indicates a real API breakage in the user's code + * (method removed, renamed, or signature changed). Internal library errors + * (where the broken reference doesn't appear in the transformed code) are + * not considered API errors. + */ +function isApiError(err: unknown, code: string): boolean { + if (!(err instanceof Error)) return false; + const msg = err.message; + + if (msg.includes('is not a function')) { + const match = msg.match(/^(.+?)\s+is not a function/); + if (match) return code.includes(match[1].trim()); + return true; + } + + if (msg.includes('Cannot read properties of undefined')) { + const match = msg.match(/reading '([^']+)'/); + if (match) return code.includes(match[1]); + return true; + } + + if (msg.includes('Cannot read property')) return true; + if (msg.includes('Expected') && msg.includes('argument')) return true; + + return false; +} + +const examples = extractExamples(docsRoot); + +const byFile = new Map(); +for (const ex of examples) { + const list = byFile.get(ex.file) ?? []; + list.push(ex); + byFile.set(ex.file, list); +} + +for (const [file, fileExamples] of byFile) { + describe(file, () => { + for (const example of fileExamples) { + test(example.section, async () => { + const transformed = transformCode(example); + if (transformed === null) return; + + const code = applyStubs(transformed); + + const editor = await Editor.open(Buffer.from(fixtureBuffer), { + extensions: getStarterExtensions(), + suppressDefaultDocxStyles: true, + }); + + try { + editor.commands.selectAll(); + const fn = new AsyncFunction('editor', code); + await fn(editor); + } catch (err) { + if (isApiError(err, code)) { + throw new Error( + `API error in ${file} → ${example.section}:\n` + + ` ${(err as Error).message}\n\n` + + `Transformed code:\n${code}`, + ); + } + } finally { + editor.destroy(); + } + }); + } + }); +} + +test('extracted examples count', () => { + expect(examples.length).toBeGreaterThan(50); +}); diff --git a/apps/docs/__tests__/lib/extract.ts b/apps/docs/__tests__/lib/extract.ts new file mode 100644 index 0000000000..a64eb84b12 --- /dev/null +++ b/apps/docs/__tests__/lib/extract.ts @@ -0,0 +1,133 @@ +import { readdirSync, readFileSync } from 'node:fs'; +import { join, relative } from 'node:path'; +import { unified } from 'unified'; +import remarkParse from 'remark-parse'; +import remarkMdx from 'remark-mdx'; +import { visit } from 'unist-util-visit'; +import type { Code, Heading } from 'mdast'; + +export interface CodeExample { + file: string; + section: string; + code: string; + pattern: 'superdoc' | 'editor' | 'unknown'; + line: number; +} + +const SKIP_FILE_PATTERNS = [ + /guides\/migration\//, + /guides\/collaboration\//, + /document-api\//, + /solutions\/esign\//, + /solutions\/template-builder\//, + /ai\/ai-actions\//, + /ai\/ai-builder\//, + /getting-started\/frameworks\//, + /snippets\//, +]; + +const SKIP_IMPORTS = [ + 'openai', + '@liveblocks/', + '@hocuspocus/', + '@tiptap/', + '@y-sweet/', + 'hocuspocus', + 'fastify', + 'express', + '@superdoc-dev/ai', + '@superdoc-dev/esign', + '@superdoc-dev/template-builder', + '@superdoc-dev/superdoc-yjs-collaboration', + 'react', + 'react-dom', + 'vue', + '@angular/', +]; + +const parser = unified().use(remarkParse).use(remarkMdx); + +function globMdx(dir: string): string[] { + const results: string[] = []; + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const full = join(dir, entry.name); + if (entry.isDirectory()) { + if (entry.name === 'node_modules' || entry.name.startsWith('.') || entry.name === '__tests__') continue; + results.push(...globMdx(full)); + } else if (entry.isFile() && entry.name.endsWith('.mdx')) { + results.push(full); + } + } + return results; +} + +function detectPattern(code: string): 'superdoc' | 'editor' | 'unknown' { + if (code.includes("from 'superdoc/super-editor'") || code.includes('Editor.open')) { + return 'editor'; + } + if (code.includes("from 'superdoc'") || code.includes('new SuperDoc')) { + return 'superdoc'; + } + return 'unknown'; +} + +function hasSkipImport(code: string): boolean { + for (const skipImport of SKIP_IMPORTS) { + if (code.includes(`'${skipImport}'`) || code.includes(`"${skipImport}"`)) return true; + if (skipImport.endsWith('/') && code.includes(skipImport)) return true; + } + return false; +} + +function headingText(node: Heading): string { + return node.children + .map((child) => { + if (child.type === 'text') return child.value; + if (child.type === 'inlineCode') return child.value; + return ''; + }) + .join('') + .trim(); +} + +export function extractExamples(docsRoot: string): CodeExample[] { + const files = globMdx(docsRoot); + const examples: CodeExample[] = []; + + for (const filePath of files) { + const relPath = relative(docsRoot, filePath); + if (SKIP_FILE_PATTERNS.some((p) => p.test(relPath))) continue; + + const tree = parser.parse(readFileSync(filePath, 'utf-8')); + + const headings: Array<{ line: number; text: string }> = []; + visit(tree, 'heading', (node: Heading) => { + if (node.depth <= 3 && node.position) { + headings.push({ line: node.position.start.line, text: headingText(node) }); + } + }); + + visit(tree, 'code', (node: Code) => { + if (!node.meta?.includes('Full Example')) return; + + const code = node.value; + if (hasSkipImport(code)) return; + + const pattern = detectPattern(code); + if (pattern === 'unknown') return; + + const codeLine = node.position?.start.line ?? 0; + let section = 'unknown'; + for (let i = headings.length - 1; i >= 0; i--) { + if (headings[i].line < codeLine) { + section = headings[i].text; + break; + } + } + + examples.push({ file: relPath, section, code, pattern, line: codeLine }); + }); + } + + return examples; +} diff --git a/apps/docs/__tests__/lib/transform.ts b/apps/docs/__tests__/lib/transform.ts new file mode 100644 index 0000000000..b77b2d83e1 --- /dev/null +++ b/apps/docs/__tests__/lib/transform.ts @@ -0,0 +1,170 @@ +import type { CodeExample } from './extract'; + +/** + * Transform a Full Example code block into executable code that + * receives an `editor` argument. Returns null if the code can't + * be meaningfully transformed. + */ +export function transformCode(example: CodeExample): string | null { + if (example.pattern === 'superdoc') return transformSuperdocPattern(example.code); + if (example.pattern === 'editor') return transformEditorPattern(example.code); + return null; +} + +function transformSuperdocPattern(code: string): string | null { + const lines = code.split('\n'); + + let onReadyStart = -1; + for (let i = 0; i < lines.length; i++) { + if (/onReady:\s*(async\s*)?\(/.test(lines[i])) { + onReadyStart = i; + break; + } + } + + if (onReadyStart === -1) { + return extractEventBody(code) ?? extractFallback(code); + } + + let braceDepth = 0; + let onReadyEnd = -1; + let bodyStart = -1; + + for (let i = onReadyStart; i < lines.length; i++) { + for (const ch of lines[i]) { + if (ch === '{') { + if (braceDepth === 0) bodyStart = i; + braceDepth++; + } + if (ch === '}') { + braceDepth--; + if (braceDepth === 0) { + onReadyEnd = i; + break; + } + } + } + if (onReadyEnd !== -1) break; + } + + if (bodyStart === -1 || onReadyEnd === -1) return null; + + const bodyLines = lines.slice(bodyStart + 1, onReadyEnd); + const filtered = bodyLines.filter( + (line) => + !line.trim().startsWith('const editor = superdoc') && + !line.trim().startsWith('let editor = superdoc') && + !line.trim().startsWith('const editor = instance') && + !line.trim().startsWith('let editor = instance'), + ); + + const result = filtered.join('\n').trim(); + return result || null; +} + +function transformEditorPattern(code: string): string | null { + const lines = code.split('\n'); + + const filtered = lines.filter((line) => { + const trimmed = line.trim(); + if (trimmed.startsWith('import ')) return false; + if (/(?:const|let)\s+editor\s*=\s*await\s+Editor\.open/.test(trimmed)) return false; + if (/^await\s+Editor\.open/.test(trimmed)) return false; + return true; + }); + + const result = filtered.join('\n').trim(); + return result || null; +} + +/** Extract handler body from superdoc.on() patterns that contain editor calls. */ +function extractEventBody(code: string): string | null { + const lines = code.split('\n'); + + let eventStart = -1; + for (let i = 0; i < lines.length; i++) { + if (/superdoc\.on\(/.test(lines[i])) { + eventStart = i; + break; + } + } + + if (eventStart === -1) return null; + if (!code.includes('editor.commands.') && !code.includes('editor.helpers.')) return null; + + let braceDepth = 0; + let bodyStart = -1; + let bodyEnd = -1; + + for (let i = eventStart; i < lines.length; i++) { + for (const ch of lines[i]) { + if (ch === '{') { + if (braceDepth === 0) bodyStart = i; + braceDepth++; + } + if (ch === '}') { + braceDepth--; + if (braceDepth === 0) { + bodyEnd = i; + break; + } + } + } + if (bodyEnd !== -1) break; + } + + if (bodyStart === -1 || bodyEnd === -1) return null; + + const bodyLines = lines.slice(bodyStart + 1, bodyEnd); + const filtered = bodyLines.filter( + (line) => !line.trim().startsWith('const editor = superdoc') && !line.trim().startsWith('let editor = superdoc'), + ); + + const result = filtered.join('\n').trim(); + return result || null; +} + +/** Last resort: extract any editor.commands/helpers lines from unrecognized patterns. */ +function extractFallback(code: string): string | null { + const commandLines = code.split('\n').filter((line) => { + const trimmed = line.trim(); + return ( + trimmed.includes('editor.commands.') || trimmed.includes('editor.helpers.') || trimmed.includes('editor.can().') + ); + }); + + if (commandLines.length === 0) return null; + return commandLines.join('\n').trim(); +} + +const PLACEHOLDER_REPLACEMENTS: [RegExp, string][] = [ + [/\bautoSave\b/g, '(() => {})'], + [/\bcleanup\b/g, '(() => {})'], + [/\bshowOnlineUsers\b/g, '(() => {})'], + [/\bshowFormattingToolbar\b/g, '(() => {})'], + [/\bsaveCurrentState\b/g, '(() => {})'], + [/\bupdateCommentsSidebar\b/g, '(() => {})'], + [/\bupdateReviewPanel\b/g, '(() => {})'], + [/\bshowCollaboratorsCursors\b/g, '(() => {})'], + [/\bhideLoadingSpinner\b/g, '(() => {})'], + [/\bupdateUserCursors\b/g, '(() => {})'], + [/\bshowLockBanner\b/g, '(() => {})'], + [/\badjustLayout\b/g, '(() => {})'], + [/\bupdateConnectionIndicator\b/g, '(() => {})'], + [/\brefreshToken\b/g, '(() => {})'], + [/\bclearInterval\b/g, '(() => {})'], + [/\bsaveToBackend\b/g, '(() => {})'], +]; + +export function applyStubs(code: string): string { + let result = code; + + // Prefix with ; to avoid ASI hazards when previous line lacks semicolons + result = result.replace(/console\.(log|warn|error|info|debug)\b/g, ';(() => {})'); + + for (const [pattern, replacement] of PLACEHOLDER_REPLACEMENTS) { + result = result.replace(pattern, replacement); + } + + return result; +} diff --git a/apps/docs/__tests__/tsconfig.json b/apps/docs/__tests__/tsconfig.json new file mode 100644 index 0000000000..ae2152afb5 --- /dev/null +++ b/apps/docs/__tests__/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "allowJs": true, + "checkJs": false, + "noEmit": true, + "baseUrl": "../../..", + "paths": { + "@core/*": ["packages/super-editor/src/core/*"], + "@extensions/*": ["packages/super-editor/src/extensions/*"], + "@features/*": ["packages/super-editor/src/features/*"], + "@components/*": ["packages/super-editor/src/components/*"], + "@helpers/*": ["packages/super-editor/src/core/helpers/*"], + "@converter/*": ["packages/super-editor/src/core/super-converter/*"], + "@tests/*": ["packages/super-editor/src/tests/*"], + "@translator": ["packages/super-editor/src/core/super-converter/v3/node-translator/index.js"], + "@utils/*": ["packages/super-editor/src/utils/*"], + "@shared/*": ["shared/*"] + } + }, + "include": ["./**/*.ts"] +} diff --git a/apps/docs/extensions/overview.mdx b/apps/docs/extensions/overview.mdx index f205296b79..a8181b677f 100644 --- a/apps/docs/extensions/overview.mdx +++ b/apps/docs/extensions/overview.mdx @@ -21,7 +21,7 @@ You don't typically configure extensions directly. SuperDoc handles it: // These commands come from extensions editor.commands.toggleBold(); // Bold extension editor.commands.insertTable(); // Table extension -editor.commands.acceptAllChanges(); // TrackChanges extension +editor.commands.acceptAllTrackedChanges(); // TrackChanges extension ``` ```javascript Full Example @@ -36,7 +36,7 @@ const superdoc = new SuperDoc({ // These commands come from extensions editor.commands.toggleBold(); // Bold extension editor.commands.insertTable(); // Table extension - editor.commands.acceptAllChanges(); // TrackChanges extension + editor.commands.acceptAllTrackedChanges(); // TrackChanges extension }, }); ``` diff --git a/apps/docs/extensions/structured-content.mdx b/apps/docs/extensions/structured-content.mdx index 605bff7f74..fe7dd0c19c 100644 --- a/apps/docs/extensions/structured-content.mdx +++ b/apps/docs/extensions/structured-content.mdx @@ -245,7 +245,7 @@ Get all block-level structured content tags in the document ```javascript Usage -const blocks = editor.helpers.getStructuredContentBlockTags(editor.state); +const blocks = editor.helpers.structuredContentCommands.getStructuredContentBlockTags(editor.state); console.log(`Found ${blocks.length} structured content blocks`); ``` @@ -258,7 +258,7 @@ const superdoc = new SuperDoc({ document: yourFile, onReady: (superdoc) => { const editor = superdoc.activeEditor; - const blocks = editor.helpers.getStructuredContentBlockTags(editor.state); + const blocks = editor.helpers.structuredContentCommands.getStructuredContentBlockTags(editor.state); console.log(`Found ${blocks.length} structured content blocks`); }, }); @@ -277,7 +277,7 @@ Get all inline structured content tags in the document ```javascript Usage -const inlines = editor.helpers.getStructuredContentInlineTags(editor.state); +const inlines = editor.helpers.structuredContentCommands.getStructuredContentInlineTags(editor.state); console.log(`Found ${inlines.length} inline fields`); ``` @@ -290,7 +290,7 @@ const superdoc = new SuperDoc({ document: yourFile, onReady: (superdoc) => { const editor = superdoc.activeEditor; - const inlines = editor.helpers.getStructuredContentInlineTags(editor.state); + const inlines = editor.helpers.structuredContentCommands.getStructuredContentInlineTags(editor.state); console.log(`Found ${inlines.length} inline fields`); }, }); @@ -309,7 +309,7 @@ Find all tables inside a structured content block by ID ```javascript Usage -const tables = editor.helpers.getStructuredContentTablesById( +const tables = editor.helpers.structuredContentCommands.getStructuredContentTablesById( "block-123", editor.state ); @@ -325,7 +325,7 @@ const superdoc = new SuperDoc({ document: yourFile, onReady: (superdoc) => { const editor = superdoc.activeEditor; - const tables = editor.helpers.getStructuredContentTablesById( + const tables = editor.helpers.structuredContentCommands.getStructuredContentTablesById( "block-123", editor.state ); @@ -350,7 +350,7 @@ Find all structured content nodes that share the same group identifier. ```javascript Usage -const fields = editor.helpers.getStructuredContentByGroup('pricing', editor.state); +const fields = editor.helpers.structuredContentCommands.getStructuredContentByGroup('pricing', editor.state); fields.forEach(({ node, pos }) => { console.log(node.attrs.tag, pos); }); @@ -365,7 +365,7 @@ const superdoc = new SuperDoc({ document: yourFile, onReady: (superdoc) => { const editor = superdoc.activeEditor; - const fields = editor.helpers.getStructuredContentByGroup('pricing', editor.state); + const fields = editor.helpers.structuredContentCommands.getStructuredContentByGroup('pricing', editor.state); fields.forEach(({ node, pos }) => { console.log(node.attrs.tag, pos); }); @@ -389,7 +389,7 @@ Get all structured content tags (inline and block) in the document ```javascript Usage -const allTags = editor.helpers.getStructuredContentTags(editor.state); +const allTags = editor.helpers.structuredContentCommands.getStructuredContentTags(editor.state); console.log(`Found ${allTags.length} structured content elements`); ``` @@ -402,7 +402,7 @@ const superdoc = new SuperDoc({ document: yourFile, onReady: (superdoc) => { const editor = superdoc.activeEditor; - const allTags = editor.helpers.getStructuredContentTags(editor.state); + const allTags = editor.helpers.structuredContentCommands.getStructuredContentTags(editor.state); console.log(`Found ${allTags.length} structured content elements`); }, }); @@ -421,7 +421,7 @@ Get structured content tag(s) by ID ```javascript Usage -const field = editor.helpers.getStructuredContentTagsById( +const field = editor.helpers.structuredContentCommands.getStructuredContentTagsById( "field-123", editor.state ); @@ -437,7 +437,7 @@ const superdoc = new SuperDoc({ document: yourFile, onReady: (superdoc) => { const editor = superdoc.activeEditor; - const field = editor.helpers.getStructuredContentTagsById( + const field = editor.helpers.structuredContentCommands.getStructuredContentTagsById( "field-123", editor.state ); diff --git a/apps/docs/package.json b/apps/docs/package.json index d0517124fa..e23152ecb8 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -8,10 +8,15 @@ "gen:docs": "node scripts/sync-sdk-docs.js", "gen:all": "pnpm run gen:api && pnpm run gen:docs", "check:links": "mintlify broken-links", - "check:imports": "node scripts/validate-code-imports.js" + "check:imports": "node scripts/validate-code-imports.js", + "test:examples": "bun test __tests__/doctest.test.ts" }, "devDependencies": { "documentation": "^14.0.3", - "mintlify": "^4.2.295" + "mintlify": "^4.2.331", + "remark-mdx": "^3.1.1", + "remark-parse": "^11.0.0", + "unified": "catalog:", + "unist-util-visit": "^5.1.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de8e483eb2..a8766f9dfd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,288 +6,9 @@ settings: catalogs: default: - '@commitlint/cli': - specifier: ^19.8.1 - version: 19.8.1 - '@commitlint/config-conventional': - specifier: ^19.8.1 - version: 19.8.1 - '@eslint/js': - specifier: ^9.31.0 - version: 9.39.2 - '@floating-ui/dom': - specifier: ^1.7.0 - version: 1.7.5 - '@fontsource/inter': - specifier: ^5.2.8 - version: 5.2.8 - '@hocuspocus/provider': - specifier: ^2.13.6 - version: 2.15.3 - '@hocuspocus/server': - specifier: ^2.13.6 - version: 2.15.3 - '@linear/sdk': - specifier: ^53.0.0 - version: 53.0.0 - '@playwright/test': - specifier: ^1.57.0 - version: 1.58.1 - '@semantic-release/changelog': - specifier: ^6.0.3 - version: 6.0.3 - '@semantic-release/git': - specifier: ^10.0.1 - version: 10.0.1 - '@testing-library/jest-dom': - specifier: ^6.9.1 - version: 6.9.1 - '@testing-library/react': - specifier: ^16.3.0 - version: 16.3.2 - '@testing-library/user-event': - specifier: ^14.6.1 - version: 14.6.1 - '@types/bun': - specifier: ^1.3.8 - version: 1.3.8 - '@types/node': - specifier: 22.19.2 - version: 22.19.2 - '@types/react': - specifier: ^19.2.6 - version: 19.2.11 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3 - '@typescript-eslint/eslint-plugin': - specifier: ^8.49.0 - version: 8.54.0 - '@typescript-eslint/parser': - specifier: ^8.49.0 - version: 8.54.0 - '@vitejs/plugin-react': - specifier: ^5.1.1 - version: 5.1.3 - '@vitejs/plugin-vue': - specifier: 6.0.2 - version: 6.0.2 - '@vitest/coverage-v8': - specifier: ^3.2.4 - version: 3.2.4 - '@vue/test-utils': - specifier: ^2.4.6 - version: 2.4.6 - buffer-crc32: - specifier: ^1.0.0 - version: 1.0.0 - color2k: - specifier: ^2.0.3 - version: 2.0.3 - concurrently: - specifier: ^9.1.2 - version: 9.2.1 - eslint: - specifier: ^9.39.1 - version: 9.39.2 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.2 - eslint-import-resolver-typescript: - specifier: ^4.4.4 - version: 4.4.4 - eslint-plugin-import-x: - specifier: ^4.16.1 - version: 4.16.1 - eslint-plugin-jsdoc: - specifier: ^54.1.0 - version: 54.7.0 - eslint-plugin-react: - specifier: ^7.37.5 - version: 7.37.5 - eslint-plugin-react-hooks: - specifier: ^7.0.1 - version: 7.0.1 - eventemitter3: - specifier: ^5.0.1 - version: 5.0.4 - fast-glob: - specifier: ^3.3.3 - version: 3.3.3 - he: - specifier: ^1.2.0 - version: 1.2.0 - jimp: - specifier: ^1.6.0 - version: 1.6.0 - jszip: - specifier: 3.10.1 - version: 3.10.1 - lefthook: - specifier: ^1.11.13 - version: 1.13.6 - lib0: - specifier: ^0.2.114 - version: 0.2.117 - marked: - specifier: ^16.2.0 - version: 16.4.2 - naive-ui: - specifier: ^2.43.1 - version: 2.43.2 - nodemon: - specifier: ^3.1.10 - version: 3.1.11 - patch-package: - specifier: ^8.0.1 - version: 8.0.1 - pdfjs-dist: - specifier: 4.3.136 - version: 4.3.136 - pinia: - specifier: ^2.1.7 - version: 2.3.1 - pixelmatch: - specifier: ^7.1.0 - version: 7.1.0 - playwright: - specifier: ^1.56.0 - version: 1.58.1 - postcss-nested: - specifier: ^6.0.1 - version: 6.2.0 - postcss-nested-import: - specifier: ^1.3.0 - version: 1.3.0 - postcss-prefixwrap: - specifier: ^1.56.2 - version: 1.57.2 - prettier: - specifier: 3.3.3 - version: 3.3.3 - prosemirror-commands: - specifier: ^1.5.2 - version: 1.7.1 - prosemirror-dropcursor: - specifier: ^1.8.1 - version: 1.8.2 - prosemirror-gapcursor: - specifier: ^1.3.2 - version: 1.4.0 - prosemirror-history: - specifier: ^1.4.0 - version: 1.5.0 - prosemirror-inputrules: - specifier: ^1.4.0 - version: 1.5.1 - prosemirror-keymap: - specifier: ^1.2.2 - version: 1.2.3 - prosemirror-model: - specifier: ^1.25.2 - version: 1.25.4 - prosemirror-schema-basic: - specifier: ^1.2.2 - version: 1.2.4 - prosemirror-schema-list: - specifier: ^1.3.0 - version: 1.5.1 - prosemirror-state: - specifier: ^1.4.3 - version: 1.4.4 - prosemirror-tables: - specifier: ^1.4.0 - version: 1.8.5 - prosemirror-test-builder: - specifier: ^1.1.1 - version: 1.1.1 - prosemirror-transform: - specifier: ^1.9.0 - version: 1.11.0 - prosemirror-view: - specifier: ^1.33.8 - version: 1.41.5 - react: - specifier: 19.2.0 - version: 19.2.0 - react-dom: - specifier: 19.2.0 - version: 19.2.0 - rehype-parse: - specifier: ^9.0.1 - version: 9.0.1 - rehype-remark: - specifier: ^10.0.1 - version: 10.0.1 - remark-gfm: - specifier: ^4.0.1 - version: 4.0.1 - remark-stringify: - specifier: ^11.0.0 - version: 11.0.0 - rollup-plugin-copy: - specifier: ^3.5.0 - version: 3.5.0 - rollup-plugin-visualizer: - specifier: ^5.12.0 - version: 5.14.0 - semantic-release: - specifier: ^24.2.7 - version: 24.2.9 - semantic-release-commit-filter: - specifier: ^1.0.2 - version: 1.0.2 - semantic-release-linear-app: - specifier: ^0.7.0 - version: 0.7.0 - tippy.js: - specifier: ^6.3.7 - version: 6.3.7 - tsx: - specifier: ^4.20.6 - version: 4.21.0 - typescript: - specifier: ^5.9.2 - version: 5.9.3 - typescript-eslint: - specifier: ^8.49.0 - version: 8.54.0 unified: - specifier: ^11.0.5 + specifier: 11.0.5 version: 11.0.5 - uuid: - specifier: ^9.0.1 - version: 9.0.1 - verdaccio: - specifier: ^6.1.6 - version: 6.2.5 - vite: - specifier: ^7.2.7 - version: 7.3.1 - vite-plugin-dts: - specifier: ~4.5.4 - version: 4.5.4 - vite-plugin-node-polyfills: - specifier: ^0.24.0 - version: 0.24.0 - vitest: - specifier: ^3.2.4 - version: 3.2.4 - xml-js: - specifier: 1.6.11 - version: 1.6.11 - y-prosemirror: - specifier: ^1.3.7 - version: 1.3.7 - y-protocols: - specifier: ^1.0.6 - version: 1.0.7 - y-websocket: - specifier: ^3.0.0 - version: 3.0.0 - yjs: - specifier: 13.6.19 - version: 13.6.19 overrides: canvas: 3.2.0 @@ -421,8 +142,20 @@ importers: specifier: ^14.0.3 version: 14.0.3 mintlify: - specifier: ^4.2.295 - version: 4.2.315(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/node@22.19.8)(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3) + specifier: ^4.2.331 + version: 4.2.331(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/node@22.19.8)(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3) + remark-mdx: + specifier: ^3.1.1 + version: 3.1.1 + remark-parse: + specifier: ^11.0.0 + version: 11.0.0 + unified: + specifier: 'catalog:' + version: 11.0.5 + unist-util-visit: + specifier: ^5.1.0 + version: 5.1.0 apps/vscode-ext: dependencies: @@ -1309,6 +1042,9 @@ packages: '@asyncapi/specs@6.11.1': resolution: {integrity: sha512-A3WBLqAKGoJ2+6FWFtpjBlCQ1oFCcs4GxF7zsIGvNqp/klGUHjlA3aAcZ9XMMpLGE8zPeYDz2x9FmO6DSuKraQ==} + '@asyncapi/specs@6.8.1': + resolution: {integrity: sha512-czHoAk3PeXTLR+X8IUaD+IpT+g+zUvkcgMDJVothBsan+oHN3jfcFcFUNdOPAAFoUCQN1hXF1dWuphWy05THlA==} + '@azure/abort-controller@2.1.2': resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} engines: {node: '>=18.0.0'} @@ -2559,19 +2295,19 @@ packages: '@microsoft/tsdoc@0.16.0': resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} - '@mintlify/cli@4.0.919': - resolution: {integrity: sha512-7zVYxDV/KteLlphmy7zGLUIQek1jFM1Lt7DPo1LVAUR3S8gYp35MopJLkR/o7WrpVVZnyZAYdmkEs/cbiCBQBw==} + '@mintlify/cli@4.0.935': + resolution: {integrity: sha512-mY0T3QOdDNuZlb4qx6GEAq0nSkWQGCn0Ug5kjQH3GjZFeeROm0EDYzoXlwAgLL/DkFEU9GpWmO3kU8EvetXtBw==} engines: {node: '>=18.0.0'} hasBin: true '@mintlify/common@1.0.661': resolution: {integrity: sha512-/Hdiblzaomp+AWStQ4smhVMgesQhffzQjC9aYBnmLReNdh2Js+ccQFUaWL3TNIxwiS2esaZvsHSV/D+zyRS3hg==} - '@mintlify/common@1.0.698': - resolution: {integrity: sha512-CicjlATkONn6ARR55J6JY+tUmSof3zjZKb708RYv3yG3qQdMB2/g0+FvQXkk2dsPgq105J5dFb/n2+vYuiEsHA==} + '@mintlify/common@1.0.713': + resolution: {integrity: sha512-0Ir8BLMVfADPi04/O5jDDemL+dxpNqHgx/JDQALuCFS4ANqHk7C9ES7ifsnt/rjmiiX7kuXSFqoAZrt1WMTLaA==} - '@mintlify/link-rot@3.0.857': - resolution: {integrity: sha512-ThNzzt4qzs0G88l7VJ3GqTHRUGhtDftydWQrxvm2v/eTUO9mjFkJZ1UZcp0/NYZgEOi2eLLHSns5B+PVlow5LA==} + '@mintlify/link-rot@3.0.872': + resolution: {integrity: sha512-2KDAD+hTmRZZCq0XCvvG+p/xSn5gtHCkHYA8Zfd2mhHceHCofNikvjffpYKx96eqaNCLhbeUYVnLlquBs820oQ==} engines: {node: '>=18.0.0'} '@mintlify/mdx@3.0.4': @@ -2585,19 +2321,19 @@ packages: resolution: {integrity: sha512-LIUkfA7l7ypHAAuOW74ZJws/NwNRqlDRD/U466jarXvvSlGhJec/6J4/I+IEcBvWDnc9anLFKmnGO04jPKgAsg==} engines: {node: '>=18.0.0'} - '@mintlify/models@0.0.263': - resolution: {integrity: sha512-Hfu0CfCkZ0Rpsvc5CBX3JDA0bqDWJ16T7ukoj/y5KltWhrukEPOl9/QR1zG/ScdXDwdOm3Zn5QQDT3GLbL0tnQ==} + '@mintlify/models@0.0.268': + resolution: {integrity: sha512-8HDPI3luABg5p/VTVYAOqabqOtcK2jdBuRTYOJiV39QqjQY29Q7kWH697PUokN6CO9uP2CCkPG5O5Gi7QxflWA==} engines: {node: '>=18.0.0'} '@mintlify/openapi-parser@0.0.8': resolution: {integrity: sha512-9MBRq9lS4l4HITYCrqCL7T61MOb20q9IdU7HWhqYMNMM1jGO1nHjXasFy61yZ8V6gMZyyKQARGVoZ0ZrYN48Og==} engines: {node: '>=18'} - '@mintlify/prebuild@1.0.834': - resolution: {integrity: sha512-JEdLMiKp9AygNpyEP2OuBJi9dzH34o8o57E55vlUrhQ6I/Po3Ww0yNQYH5wQ6bZM+tVX2ugJjKMclFURBfYMaw==} + '@mintlify/prebuild@1.0.849': + resolution: {integrity: sha512-GlFRJYrS7sIByZXKLa91VeCGruMunbwoxGbWRF5gAiknkuhng9SuX7zB7yTa0J3ApmLO3oZG5baWfzflcQY01w==} - '@mintlify/previewing@4.0.890': - resolution: {integrity: sha512-A8QeRFQ9dCsZJQgr6GCzSSrI+zlNm4dSlYUBDjPDkYI92uKmZO95maAKiXaWPKGQfjeIf8YYbP3n+k4QE+gpgg==} + '@mintlify/previewing@4.0.905': + resolution: {integrity: sha512-3PjzszHkvswA742dofEDohINUnlbmi4gnKP9xWK7wA+dLszKJ56BYjm6VCWJjpG3PuO4oPFIS9vI1YRCEM8kRQ==} engines: {node: '>=18.0.0'} '@mintlify/scraping@4.0.522': @@ -2605,16 +2341,16 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - '@mintlify/scraping@4.0.559': - resolution: {integrity: sha512-xowugtpLPQacXLqdSB+X85Ug1uxbJMkWa8IzO8CiyJN9kcx1EIG9Ydxn0JJhyENETR3jd1VLKzIMvLXFeGAzZA==} + '@mintlify/scraping@4.0.574': + resolution: {integrity: sha512-FN2MM8uxBi2Foxpua0UIDLYo4YvcJE/NMwOGfbQ/HWx73eWhsA0LI+uhqpq8ZYqgc4awVif1oKc7OcuEhtY7Kg==} engines: {node: '>=18.0.0'} hasBin: true '@mintlify/validation@0.1.555': resolution: {integrity: sha512-11QVUReL4N5u8wSCgZt4RN7PA0jYQoMEBZ5IrUp5pgb5ZJBOoGV/vPsQrxPPa1cxsUDAuToNhtGxRQtOav/w8w==} - '@mintlify/validation@0.1.577': - resolution: {integrity: sha512-tecysj9oeTc0SHz1ro/oaqMLwEpJw/K8oqoDWULgOfBcDPeG6uKNMe2NiLyVZLZUMxsywFKOJFRkF/8mTbJcHQ==} + '@mintlify/validation@0.1.585': + resolution: {integrity: sha512-32mezT7v1dmPQa2DyGDYf0t+HHUbmpShJVnMrxxhXyMHvKUqOu4ENoRCAxRbfc4OLPxAFRB4qEEq2toID+tOHw==} '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} @@ -7528,8 +7264,8 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} - mintlify@4.2.315: - resolution: {integrity: sha512-G2P7C5TAjayZF2WqrF+2Ya/yfIQSswS605YaBlXMlMCL4lFmqt4POPCowkg4OnojgAIm44JaBohYjhsDJqlMZA==} + mintlify@4.2.331: + resolution: {integrity: sha512-57mLAnYuZ9EEpTT84NAgUOFK7SSEM0cuO1J+NxNfywJi/RL+JmDijd9Tv74QHOEUhPwHjKPzOkhYR82vMqIdvw==} engines: {node: '>=18.0.0'} hasBin: true @@ -8819,6 +8555,9 @@ packages: remark-mdx@3.1.0: resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==} + remark-mdx@3.1.1: + resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} + remark-parse@10.0.2: resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==} @@ -9471,7 +9210,7 @@ packages: tar@6.1.15: resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==} engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} @@ -10590,6 +10329,10 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 + '@asyncapi/specs@6.8.1': + dependencies: + '@types/json-schema': 7.0.15 + '@azure/abort-controller@2.1.2': dependencies: tslib: 2.8.1 @@ -10694,7 +10437,7 @@ snapshots: '@babel/types': 7.29.0 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -10778,7 +10521,7 @@ snapshots: '@babel/parser': 7.29.0 '@babel/template': 7.28.6 '@babel/types': 7.29.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -11785,7 +11528,7 @@ snapshots: recma-jsx: 1.0.1(acorn@8.15.0) recma-stringify: 1.0.0 rehype-recma: 1.0.0 - remark-mdx: 3.1.0 + remark-mdx: 3.1.1 remark-parse: 11.0.0 remark-rehype: 11.1.1 source-map: 0.7.6 @@ -11868,15 +11611,15 @@ snapshots: '@microsoft/tsdoc@0.16.0': {} - '@mintlify/cli@4.0.919(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/node@22.19.8)(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3)': + '@mintlify/cli@4.0.935(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/node@22.19.8)(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3)': dependencies: '@inquirer/prompts': 7.9.0(@types/node@22.19.8) - '@mintlify/common': 1.0.698(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/link-rot': 3.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/models': 0.0.263 - '@mintlify/prebuild': 1.0.834(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/previewing': 4.0.890(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3) - '@mintlify/validation': 0.1.577(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/common': 1.0.713(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/link-rot': 3.0.872(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/models': 0.0.268 + '@mintlify/prebuild': 1.0.849(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/previewing': 4.0.905(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3) + '@mintlify/validation': 0.1.585(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) adm-zip: 0.5.16 chalk: 5.2.0 color: 4.2.3 @@ -11968,13 +11711,14 @@ snapshots: - ts-node - typescript - '@mintlify/common@1.0.698(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': + '@mintlify/common@1.0.713(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': dependencies: '@asyncapi/parser': 3.4.0 + '@asyncapi/specs': 6.8.1 '@mintlify/mdx': 3.0.4(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/models': 0.0.263 + '@mintlify/models': 0.0.268 '@mintlify/openapi-parser': 0.0.8 - '@mintlify/validation': 0.1.577(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/validation': 0.1.585(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) '@sindresorhus/slugify': 2.2.0 '@types/mdast': 4.0.4 acorn: 8.11.2 @@ -12028,13 +11772,13 @@ snapshots: - ts-node - typescript - '@mintlify/link-rot@3.0.857(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': + '@mintlify/link-rot@3.0.872(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': dependencies: - '@mintlify/common': 1.0.698(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/prebuild': 1.0.834(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/previewing': 4.0.890(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3) + '@mintlify/common': 1.0.713(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/prebuild': 1.0.849(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/previewing': 4.0.905(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3) '@mintlify/scraping': 4.0.522(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/validation': 0.1.577(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/validation': 0.1.585(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) fs-extra: 11.1.0 unist-util-visit: 4.1.2 transitivePeerDependencies: @@ -12087,7 +11831,7 @@ snapshots: transitivePeerDependencies: - debug - '@mintlify/models@0.0.263': + '@mintlify/models@0.0.268': dependencies: axios: 1.13.2 openapi-types: 12.1.3 @@ -12103,12 +11847,12 @@ snapshots: leven: 4.1.0 yaml: 2.8.2 - '@mintlify/prebuild@1.0.834(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': + '@mintlify/prebuild@1.0.849(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': dependencies: - '@mintlify/common': 1.0.698(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/common': 1.0.713(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) '@mintlify/openapi-parser': 0.0.8 - '@mintlify/scraping': 4.0.559(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/validation': 0.1.577(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/scraping': 4.0.574(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/validation': 0.1.585(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) chalk: 5.3.0 favicons: 7.2.0 front-matter: 4.0.2 @@ -12135,11 +11879,11 @@ snapshots: - typescript - utf-8-validate - '@mintlify/previewing@4.0.890(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3)': + '@mintlify/previewing@4.0.905(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3)': dependencies: - '@mintlify/common': 1.0.698(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/prebuild': 1.0.834(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/validation': 0.1.577(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/common': 1.0.713(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/prebuild': 1.0.849(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/validation': 0.1.585(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) better-opn: 3.0.2 chalk: 5.2.0 chokidar: 3.5.3 @@ -12208,9 +11952,9 @@ snapshots: - typescript - utf-8-validate - '@mintlify/scraping@4.0.559(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': + '@mintlify/scraping@4.0.574(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': dependencies: - '@mintlify/common': 1.0.698(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + '@mintlify/common': 1.0.713(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) '@mintlify/openapi-parser': 0.0.8 fs-extra: 11.1.1 hast-util-to-mdast: 10.1.0 @@ -12265,10 +12009,10 @@ snapshots: - supports-color - typescript - '@mintlify/validation@0.1.577(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': + '@mintlify/validation@0.1.585(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3)': dependencies: '@mintlify/mdx': 3.0.4(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - '@mintlify/models': 0.0.263 + '@mintlify/models': 0.0.268 arktype: 2.1.27 js-yaml: 4.1.0 lcm: 0.0.3 @@ -12402,7 +12146,7 @@ snapshots: '@puppeteer/browsers@2.3.0': dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 @@ -13397,7 +13141,7 @@ snapshots: '@typescript/vfs@1.6.2(typescript@5.9.3)': dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -15004,6 +14748,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.3: + dependencies: + ms: 2.1.3 + debug@4.4.3(supports-color@5.5.0): dependencies: ms: 2.1.3 @@ -15101,7 +14849,7 @@ snapshots: detect-port@1.5.1: dependencies: address: 1.2.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -15909,7 +15657,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -16226,7 +15974,7 @@ snapshots: dependencies: basic-ftp: 5.1.0 data-uri-to-buffer: 6.0.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -16793,7 +16541,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -16822,7 +16570,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -18526,7 +18274,7 @@ snapshots: micromark@3.2.0: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 decode-named-character-reference: 1.3.0 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 @@ -18548,7 +18296,7 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -18650,9 +18398,9 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 - mintlify@4.2.315(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/node@22.19.8)(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3): + mintlify@4.2.331(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/node@22.19.8)(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3): dependencies: - '@mintlify/cli': 4.0.919(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/node@22.19.8)(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3) + '@mintlify/cli': 4.0.935(@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.11))(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(react@19.2.3))(@types/node@22.19.8)(@types/react@19.2.11)(react-dom@19.2.0(react@19.2.3))(typescript@5.9.3) transitivePeerDependencies: - '@radix-ui/react-popover' - '@types/node' @@ -19084,7 +18832,7 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.4 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 get-uri: 6.0.5 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -19592,7 +19340,7 @@ snapshots: proxy-agent@6.5.0: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 lru-cache: 7.18.3 @@ -19645,7 +19393,7 @@ snapshots: dependencies: '@puppeteer/browsers': 2.3.0 chromium-bidi: 0.6.2(devtools-protocol@0.0.1312386) - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 devtools-protocol: 0.0.1312386 ws: 8.19.0 transitivePeerDependencies: @@ -20050,6 +19798,13 @@ snapshots: transitivePeerDependencies: - supports-color + remark-mdx@3.1.1: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + remark-parse@10.0.2: dependencies: '@types/mdast': 3.0.15 @@ -20609,7 +20364,7 @@ snapshots: socket.io-adapter@2.5.6: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 ws: 8.18.3 transitivePeerDependencies: - bufferutil @@ -20619,7 +20374,7 @@ snapshots: socket.io-parser@4.2.5: dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -20640,7 +20395,7 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.4 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3 socks: 2.8.7 transitivePeerDependencies: - supports-color diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e0eedf053b..5a2783f74f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -91,7 +91,7 @@ catalog: tsx: ^4.20.6 typescript: ^5.9.2 typescript-eslint: ^8.49.0 - unified: ^11.0.5 + unified: 11.0.5 uuid: ^9.0.1 verdaccio: ^6.1.6 vite: ^7.2.7 From 73f05a042f162739d6fa66783dc1ebaedf89c58e Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 17:38:56 -0300 Subject: [PATCH 10/13] refactor: convert validate-code-imports to remark-based parsing Replace regex state machine with remark-parse + remark-mdx AST walking. Convert from CJS to TypeScript, run with bun instead of node. --- apps/docs/package.json | 2 +- apps/docs/scripts/validate-code-imports.js | 215 --------------------- apps/docs/scripts/validate-code-imports.ts | 173 +++++++++++++++++ 3 files changed, 174 insertions(+), 216 deletions(-) delete mode 100644 apps/docs/scripts/validate-code-imports.js create mode 100644 apps/docs/scripts/validate-code-imports.ts diff --git a/apps/docs/package.json b/apps/docs/package.json index e23152ecb8..707ca504f3 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -8,7 +8,7 @@ "gen:docs": "node scripts/sync-sdk-docs.js", "gen:all": "pnpm run gen:api && pnpm run gen:docs", "check:links": "mintlify broken-links", - "check:imports": "node scripts/validate-code-imports.js", + "check:imports": "bun scripts/validate-code-imports.ts", "test:examples": "bun test __tests__/doctest.test.ts" }, "devDependencies": { diff --git a/apps/docs/scripts/validate-code-imports.js b/apps/docs/scripts/validate-code-imports.js deleted file mode 100644 index 4b230a66ca..0000000000 --- a/apps/docs/scripts/validate-code-imports.js +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env node - -/** - * Validates import statements in MDX code blocks. - * - * Scans all .mdx files under apps/docs/ for JS/TS code blocks - * and checks that every import path is on the allowlist. - */ - -const fs = require('fs'); -const path = require('path'); - -// ── Allowlists ────────────────────────────────────────────────────────────── - -const EXACT_SUPERDOC_IMPORTS = new Set([ - 'superdoc', - 'superdoc/super-editor', - 'superdoc/types', - 'superdoc/converter', - 'superdoc/docx-zipper', - 'superdoc/file-zipper', - 'superdoc/style.css', - '@superdoc-dev/ai', - '@superdoc-dev/esign', - '@superdoc-dev/esign/styles.css', - '@superdoc-dev/template-builder', - '@superdoc-dev/template-builder/defaults', - '@superdoc-dev/superdoc-yjs-collaboration', -]); - -const EXACT_EXTERNAL_IMPORTS = new Set([ - 'react', - 'react-dom', - 'react-dom/client', - 'vue', - 'yjs', - 'y-prosemirror', - 'openai', - 'bun:test', - 'hocuspocus', - 'fastify', - 'express', - 'cors', - 'pg', - 'ioredis', -]); - -const PREFIX_EXTERNAL_IMPORTS = [ - '@angular/', - 'prosemirror-', - 'node:', - 'fs/', - '@hocuspocus/', - '@tiptap/', - '@tiptap-pro/', - '@liveblocks/', - '@y-sweet/', - '@fastify/', - '@aws-sdk/', - 'next/', -]; - -// ── Helpers ───────────────────────────────────────────────────────────────── - -const RESET = '\x1b[0m'; -const RED = '\x1b[31m'; -const GREEN = '\x1b[32m'; -const YELLOW = '\x1b[33m'; -const CYAN = '\x1b[36m'; -const BOLD = '\x1b[1m'; -const DIM = '\x1b[2m'; - -function isImportAllowed(importPath) { - // Relative imports are always allowed - if (importPath.startsWith('./') || importPath.startsWith('../')) { - return true; - } - - if (EXACT_SUPERDOC_IMPORTS.has(importPath)) return true; - if (EXACT_EXTERNAL_IMPORTS.has(importPath)) return true; - - for (const prefix of PREFIX_EXTERNAL_IMPORTS) { - if (importPath.startsWith(prefix)) return true; - } - - return false; -} - -/** - * Recursively collect all .mdx files under `dir`. - */ -function globMdx(dir) { - const results = []; - for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { - const full = path.join(dir, entry.name); - if (entry.isDirectory()) { - // Skip node_modules / hidden dirs - if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue; - results.push(...globMdx(full)); - } else if (entry.isFile() && entry.name.endsWith('.mdx')) { - results.push(full); - } - } - return results; -} - -/** - * Strip regions inside HTML comments () so we don't - * inspect code blocks that are commented out. - * Returns an array of lines with commented regions replaced by empty strings - * while preserving line count. - */ -function stripHtmlComments(content) { - // Replace comment contents with empty lines to keep line numbers stable - return content.replace(//g, (match) => { - // Preserve the same number of newlines - return match.replace(/[^\n]/g, ''); - }); -} - -const CODE_FENCE_REGEX = /^```(?:js|javascript|ts|typescript|jsx|tsx)(?:\s.*)?$/; -const IMPORT_REGEX = /import\s+(?:(?:[\s\S]*?)\s+from\s+)?['"]([^'"]+)['"]/g; - -/** - * Validate a single MDX file. Returns an array of error objects. - */ -function validateFile(filePath) { - const raw = fs.readFileSync(filePath, 'utf-8'); - const content = stripHtmlComments(raw); - const lines = content.split('\n'); - - const errors = []; - let insideCodeBlock = false; - - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - const trimmed = line.trim(); - - if (!insideCodeBlock) { - if (CODE_FENCE_REGEX.test(trimmed)) { - insideCodeBlock = true; - } - continue; - } - - // Inside a code block - if (trimmed.startsWith('```')) { - insideCodeBlock = false; - continue; - } - - // Look for imports on this line - let match; - IMPORT_REGEX.lastIndex = 0; - while ((match = IMPORT_REGEX.exec(line)) !== null) { - const importPath = match[1]; - if (!isImportAllowed(importPath)) { - errors.push({ - file: filePath, - line: i + 1, // 1-indexed - importPath, - text: line.trim(), - }); - } - } - } - - return errors; -} - -// ── Main ──────────────────────────────────────────────────────────────────── - -function main() { - const docsRoot = path.resolve(__dirname, '..'); - const files = globMdx(docsRoot); - - console.log(`${CYAN}${BOLD}Validating imports in ${files.length} MDX files...${RESET}\n`); - - let totalErrors = 0; - const errorsByFile = new Map(); - - for (const file of files) { - const errors = validateFile(file); - if (errors.length > 0) { - const rel = path.relative(docsRoot, file); - errorsByFile.set(rel, errors); - totalErrors += errors.length; - } - } - - if (totalErrors === 0) { - console.log(`${GREEN}${BOLD}All imports are valid.${RESET}`); - process.exit(0); - } - - console.log(`${RED}${BOLD}Found ${totalErrors} invalid import${totalErrors === 1 ? '' : 's'}:${RESET}\n`); - - for (const [relFile, errors] of errorsByFile) { - console.log(`${YELLOW}${BOLD}${relFile}${RESET}`); - for (const err of errors) { - console.log(` ${DIM}${err.line}${RESET} ${RED}Invalid import: ${BOLD}${err.importPath}${RESET}`); - console.log(` ${DIM}${err.text}${RESET}`); - } - console.log(); - } - - console.log( - `${RED}${BOLD}${totalErrors} error${totalErrors === 1 ? '' : 's'} found. ` + - `Please use only allowed import paths in code examples.${RESET}`, - ); - - process.exit(1); -} - -main(); diff --git a/apps/docs/scripts/validate-code-imports.ts b/apps/docs/scripts/validate-code-imports.ts new file mode 100644 index 0000000000..3100ddb359 --- /dev/null +++ b/apps/docs/scripts/validate-code-imports.ts @@ -0,0 +1,173 @@ +#!/usr/bin/env bun + +/** + * Validates import statements in MDX code blocks. + * Scans all .mdx files for JS/TS code blocks and checks + * that every import path is on the allowlist. + */ + +import { readdirSync, readFileSync } from 'node:fs'; +import { join, relative, resolve } from 'node:path'; +import { unified } from 'unified'; +import remarkParse from 'remark-parse'; +import remarkMdx from 'remark-mdx'; +import { visit } from 'unist-util-visit'; +import type { Code } from 'mdast'; + +const EXACT_SUPERDOC_IMPORTS = new Set([ + 'superdoc', + 'superdoc/super-editor', + 'superdoc/types', + 'superdoc/converter', + 'superdoc/docx-zipper', + 'superdoc/file-zipper', + 'superdoc/style.css', + '@superdoc-dev/ai', + '@superdoc-dev/esign', + '@superdoc-dev/esign/styles.css', + '@superdoc-dev/template-builder', + '@superdoc-dev/template-builder/defaults', + '@superdoc-dev/superdoc-yjs-collaboration', +]); + +const EXACT_EXTERNAL_IMPORTS = new Set([ + 'react', + 'react-dom', + 'react-dom/client', + 'vue', + 'yjs', + 'y-prosemirror', + 'openai', + 'bun:test', + 'hocuspocus', + 'fastify', + 'express', + 'cors', + 'pg', + 'ioredis', +]); + +const PREFIX_EXTERNAL_IMPORTS = [ + '@angular/', + 'prosemirror-', + 'node:', + 'fs/', + '@hocuspocus/', + '@tiptap/', + '@tiptap-pro/', + '@liveblocks/', + '@y-sweet/', + '@fastify/', + '@aws-sdk/', + 'next/', +]; + +const IMPORT_REGEX = /import\s+(?:(?:[\s\S]*?)\s+from\s+)?['"]([^'"]+)['"]/g; + +const RESET = '\x1b[0m'; +const RED = '\x1b[31m'; +const GREEN = '\x1b[32m'; +const YELLOW = '\x1b[33m'; +const CYAN = '\x1b[36m'; +const BOLD = '\x1b[1m'; +const DIM = '\x1b[2m'; + +function isImportAllowed(importPath: string): boolean { + if (importPath.startsWith('./') || importPath.startsWith('../')) return true; + if (EXACT_SUPERDOC_IMPORTS.has(importPath)) return true; + if (EXACT_EXTERNAL_IMPORTS.has(importPath)) return true; + return PREFIX_EXTERNAL_IMPORTS.some((p) => importPath.startsWith(p)); +} + +function globMdx(dir: string): string[] { + const results: string[] = []; + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const full = join(dir, entry.name); + if (entry.isDirectory()) { + if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue; + results.push(...globMdx(full)); + } else if (entry.isFile() && entry.name.endsWith('.mdx')) { + results.push(full); + } + } + return results; +} + +interface ImportError { + file: string; + line: number; + importPath: string; + text: string; +} + +const parser = unified().use(remarkParse).use(remarkMdx); + +function validateFile(filePath: string): ImportError[] { + const content = readFileSync(filePath, 'utf-8'); + const tree = parser.parse(content); + const errors: ImportError[] = []; + + visit(tree, 'code', (node: Code) => { + const lang = node.lang ?? ''; + if (!['js', 'javascript', 'ts', 'typescript', 'jsx', 'tsx'].includes(lang)) return; + + const codeLine = node.position?.start.line ?? 0; + const lines = node.value.split('\n'); + + for (let i = 0; i < lines.length; i++) { + IMPORT_REGEX.lastIndex = 0; + let match; + while ((match = IMPORT_REGEX.exec(lines[i])) !== null) { + if (!isImportAllowed(match[1])) { + errors.push({ + file: filePath, + line: codeLine + i + 1, + importPath: match[1], + text: lines[i].trim(), + }); + } + } + } + }); + + return errors; +} + +const docsRoot = resolve(import.meta.dir, '..'); +const files = globMdx(docsRoot); + +console.log(`${CYAN}${BOLD}Validating imports in ${files.length} MDX files...${RESET}\n`); + +let totalErrors = 0; +const errorsByFile = new Map(); + +for (const file of files) { + const errors = validateFile(file); + if (errors.length > 0) { + errorsByFile.set(relative(docsRoot, file), errors); + totalErrors += errors.length; + } +} + +if (totalErrors === 0) { + console.log(`${GREEN}${BOLD}All imports are valid.${RESET}`); + process.exit(0); +} + +console.log(`${RED}${BOLD}Found ${totalErrors} invalid import${totalErrors === 1 ? '' : 's'}:${RESET}\n`); + +for (const [relFile, errors] of errorsByFile) { + console.log(`${YELLOW}${BOLD}${relFile}${RESET}`); + for (const err of errors) { + console.log(` ${DIM}${err.line}${RESET} ${RED}Invalid import: ${BOLD}${err.importPath}${RESET}`); + console.log(` ${DIM}${err.text}${RESET}`); + } + console.log(); +} + +console.log( + `${RED}${BOLD}${totalErrors} error${totalErrors === 1 ? '' : 's'} found. ` + + `Please use only allowed import paths in code examples.${RESET}`, +); + +process.exit(1); From 17d6fb9151a68169a48cd52603b156db0e4f6b38 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 17:42:39 -0300 Subject: [PATCH 11/13] ci: add docs validation for code examples and imports Add lefthook pre-commit hooks (docs-check-imports, docs-test-examples) that run when MDX files are staged. Update docs-validation CI workflow with import checking and doctest steps. --- .github/workflows/docs-validation.yml | 12 ++++++++++++ lefthook.yml | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/.github/workflows/docs-validation.yml b/.github/workflows/docs-validation.yml index c8ba7a2d73..e89115cf44 100644 --- a/.github/workflows/docs-validation.yml +++ b/.github/workflows/docs-validation.yml @@ -22,6 +22,9 @@ jobs: node-version: '20' cache: 'pnpm' + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + - name: Install Dependencies run: pnpm install --frozen-lockfile @@ -30,3 +33,12 @@ jobs: - name: Check Broken Links run: pnpm --filter @superdoc/docs check:links + + - name: Check Code Imports + run: pnpm --filter @superdoc/docs check:imports + + - name: Build Packages + run: pnpm --filter superdoc... build + + - name: Test Code Examples + run: pnpm --filter @superdoc/docs test:examples diff --git a/lefthook.yml b/lefthook.yml index 5d5e0ef632..e4e0f18d1b 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -28,6 +28,14 @@ pre-commit: root: "apps/vscode-ext/" glob: "apps/vscode-ext/**/*.{ts,js}" run: pnpm run lint + docs-check-imports: + root: "apps/docs/" + glob: "apps/docs/**/*.mdx" + run: pnpm run check:imports + docs-test-examples: + root: "apps/docs/" + glob: "apps/docs/**/*.mdx" + run: pnpm run test:examples commit-msg: commands: From 0e6f13285f3aaa537d257c5ad33b41303ffd9555 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 17:45:34 -0300 Subject: [PATCH 12/13] docs: add testing section to docs CLAUDE.md --- apps/docs/CLAUDE.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/docs/CLAUDE.md b/apps/docs/CLAUDE.md index 81b9fd58b9..97deaf8683 100644 --- a/apps/docs/CLAUDE.md +++ b/apps/docs/CLAUDE.md @@ -139,9 +139,18 @@ const superdoc = new SuperDoc({ **When NOT to use CodeGroup:** Snippets that are already complete (have imports + initialization), config-only blocks, bash commands, XML/HTML examples. -Run `pnpm run check:imports` to validate all import paths in code examples. +## Testing + +Code examples are tested automatically via pre-commit hooks and CI. Two checks run when `.mdx` files change: + +- `pnpm run check:imports` — validates import paths in all code blocks against an allowlist +- `pnpm run test:examples` — extracts "Full Example" blocks, executes them headlessly against a real Editor instance, and fails if any documented API doesn't exist + +The doctest suite lives in `__tests__/` and uses remark to parse MDX. When adding or modifying a Full Example, run `pnpm run test:examples` to verify it works. ## Commands - `npx mintlify dev` — Start local dev server - `npx mintlify broken-links` — Check for broken links +- `pnpm run check:imports` — Validate code block import paths +- `pnpm run test:examples` — Run doctest suite (277 examples) From f81665a7f679cbc7b51a27e09885af3b3dd4430c Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Fri, 6 Feb 2026 17:54:25 -0300 Subject: [PATCH 13/13] ci: fix ci docs --- .github/workflows/{docs-validation.yml => ci-docs.yml} | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) rename .github/workflows/{docs-validation.yml => ci-docs.yml} (88%) diff --git a/.github/workflows/docs-validation.yml b/.github/workflows/ci-docs.yml similarity index 88% rename from .github/workflows/docs-validation.yml rename to .github/workflows/ci-docs.yml index e89115cf44..9c865512ea 100644 --- a/.github/workflows/docs-validation.yml +++ b/.github/workflows/ci-docs.yml @@ -1,4 +1,4 @@ -name: Documentation Validation +name: CI Docs on: pull_request: @@ -8,7 +8,6 @@ on: jobs: validate: - name: Validate Documentation runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -38,7 +37,7 @@ jobs: run: pnpm --filter @superdoc/docs check:imports - name: Build Packages - run: pnpm --filter superdoc... build + run: pnpm --prefix packages/superdoc run build - name: Test Code Examples run: pnpm --filter @superdoc/docs test:examples