# Writer Full document editor with WYSIWYG, Markdown, and HTML source editing modes. Supports themes, presets, page layout, word count, find/replace, DOCX/ODT import, and export to HTML/Markdown/PDF/DOCX/TXT. Available themes: `light`, `dark`, `classic`, `word`. Available presets: `classic`, `wysiwyg`, `minimal`. ## Live Demo ## Embed ```html ``` --- ## Events Reference ### Content #### `setHTML` / `getHTML` -- HTML Content ```js socket.fire("webapp::instance::request", "setHTML", "
This is a document.
", function (err) { console.log(err || "Set"); }); socket.fire("webapp::instance::request", "getHTML", function (err, html) { console.log("HTML:", html); }); ``` #### `setMarkdown` / `getMarkdown` -- Markdown Content ```js socket.fire("webapp::instance::request", "setMarkdown", "# My Document\n\nHello **world**!\n\n- Item 1\n- Item 2", function (err) {}); socket.fire("webapp::instance::request", "getMarkdown", function (err, md) { console.log("Markdown:", md); }); ``` #### `getText` -- Plain Text ```js socket.fire("webapp::instance::request", "getText", function (err, text) { console.log("Text:", text); }); ``` #### `getWordCount` -- Word Count ```js socket.fire("webapp::instance::request", "getWordCount", function (err, counts) { console.log(counts); // { words, characters, paragraphs, ... } }); ``` --- ### Editor Modes & Themes #### `setMode` -- Switch Editing Mode Switch between WYSIWYG, Markdown, and Source editing: ```js socket.fire("webapp::instance::request", "setMode", "wysiwyg"); // rich text socket.fire("webapp::instance::request", "setMode", "markdown"); // markdown source socket.fire("webapp::instance::request", "setMode", "source"); // raw HTML ``` #### `setTheme` -- Change Theme ```js socket.fire("webapp::instance::request", "setTheme", "light"); socket.fire("webapp::instance::request", "setTheme", "dark"); socket.fire("webapp::instance::request", "setTheme", "classic"); socket.fire("webapp::instance::request", "setTheme", "word"); ``` #### `usePreset` -- Apply Preset Each preset is a one-call configuration of layout, ruler visibility, toolbar, and theme: | Preset | Layout | Rulers | Toolbar | Theme | Best for | |---|---|---|---|---|---| | `"classic"` | Document (paginated) | shown | full | classic | Document authoring, collaboration | | `"wysiwyg"` | Inline (fluid) | hidden | full | light | CMS, blog editors, forms | | `"minimal"` | Inline (fluid) | hidden | hidden | light | Textarea replacement, comments | ```js socket.fire("webapp::instance::request", "usePreset", "classic"); // paginated document layout + ruler socket.fire("webapp::instance::request", "usePreset", "wysiwyg"); // inline editor, no ruler socket.fire("webapp::instance::request", "usePreset", "minimal"); // no toolbar, no ruler, no status bar ``` #### `setLayoutMode` -- Document vs Inline `setLayoutMode` is a real layout switch -- not just a CSS class swap. When called it strips auto-break footers/headers from the editable area, lazily creates the page wrapper and vertical ruler if they're missing, restores the cleaned HTML, then re-runs pagination + ruler refresh. Switching back to `inline` detaches the page wrapper (kept in memory for reuse) and hides the rulers. ```js socket.fire("webapp::instance::request", "setLayoutMode", "document"); // page layout with margins socket.fire("webapp::instance::request", "setLayoutMode", "inline"); // compact, no page breaks ``` The toolbar also exposes a **Layout** group with two buttons (📄 Document / ☰ Inline) that fire the same calls and stay in sync with the active layout. Disable it via the `toolbar.layout: false` constructor option if you want a fixed layout for an embed. --- ### View Controls ```js socket.fire("webapp::instance::request", "setZoom", 1.5); // 150% socket.fire("webapp::instance::request", "readOnly", true); // toggle read-only socket.fire("webapp::instance::request", "undo"); socket.fire("webapp::instance::request", "redo"); ``` --- ### Insertion ```js // Insert a page break socket.fire("webapp::instance::request", "insertBreak", "page"); // Other break types: "line", "paragraph" // Insert a table socket.fire("webapp::instance::request", "insertBlock", "table", { rows: 3, cols: 4, header: true }); // Insert a code block socket.fire("webapp::instance::request", "insertBlock", "code", { language: "javascript" }); // Insert a heading socket.fire("webapp::instance::request", "insertBlock", "heading", { level: 2 }); // Insert an ordered/task list socket.fire("webapp::instance::request", "insertBlock", "list", { ordered: true }); // Insert an image socket.fire("webapp::instance::request", "insertBlock", "image", { src: "https://example.com/img.png", alt: "Example", width: 400 }); // Insert a callout / alert box socket.fire("webapp::instance::request", "insertBlock", "callout", { calloutType: "info" }); ``` **Block types:** `"heading"`, `"paragraph"`, `"blockquote"`, `"code"`, `"table"`, `"image"`, `"hr"`, `"callout"`, `"list"` **Callout types:** `"info"`, `"warning"`, `"success"`, `"error"`, `"note"`, `"tip"` --- ### Page Setup ```js // Set page to A4 landscape with custom margins socket.fire("webapp::instance::request", "setPageSetup", { size: "A4", // "A3" | "A4" | "A5" | "Letter" | "Legal" | "Tabloid" | "B5" orientation: "landscape", // "portrait" | "landscape" margins: { top: 20, bottom: 20, left: 15, right: 15 }, // mm columns: 1 }, function (err) {}); // Read current page setup socket.fire("webapp::instance::request", "getPageSetup", function (err, setup) { console.log(setup); }); ``` --- ### Headers & Footers (Document Mode) In Document Mode every paginated page gets its own header and footer rendered as inline blocks. They are configured through the `headerFooter` option of the editor (passed at construction time) or via `setPageSetup` for the per-document toggles. Each header/footer has three zones (`left`, `center`, `right`) and accepts plain text or HTML with dynamic field tokens that re-resolve on every re-pagination. ```js // Configure headers/footers for the current document socket.fire("webapp::instance::request", "setPageSetup", { headerFooter: { firstPageDifferent: true, oddEvenDifferent: false } }, function (err) {}); ``` #### Field tokens These tokens can be placed inside any header or footer zone and are re-evaluated on every re-pagination: | Token | Resolves to | |---|---| | `{pageNumber}` | Current page number (1-based) | | `{totalPages}` | Total number of pages | | `{date}` | Today's date in locale format | | `{time}` | Current time in locale format | | `{title}` | Document title (from `