### Install KaTeX Source: https://github.com/jihong88/suneditor/blob/master/guide/external-libraries.md Install KaTeX using npm for rendering LaTeX math formulas. ```bash npm install katex ``` -------------------------------- ### Install MathJax Source: https://github.com/jihong88/suneditor/blob/master/guide/external-libraries.md Install the MathJax library using npm. This is the first step to enable MathJax support in the editor. ```bash npm install mathjax-full ``` -------------------------------- ### Install CodeMirror Source: https://github.com/jihong88/suneditor/blob/master/guide/external-libraries.md Install CodeMirror using npm for syntax highlighting in code view. ```bash npm install codemirror ``` -------------------------------- ### Start Local Development Server Source: https://github.com/jihong88/suneditor/blob/master/GUIDE.md Use this command to start a local development server. It's useful for making and previewing changes during development. ```bash npm run dev # Start local dev server (http://localhost:8088) npm start # Alias for npm run dev ``` -------------------------------- ### Example Changes Log Entry Source: https://github.com/jihong88/suneditor/blob/master/GUIDE.md An example of a security-related change entry in the `changes.md` file. ```markdown ## Security - 2026-03-29 - **html:** Block obfuscated `javascript:` protocol in href/src attributes (entity/URL-encoded whitespace bypass) ``` -------------------------------- ### Example Conventional Commit Source: https://github.com/jihong88/suneditor/blob/master/CONTRIBUTING.md A comprehensive example demonstrating a 'feat' commit with a detailed body explaining the changes and linking to a closed issue. ```text feat(#1541): add table cell merge functionality - Implement horizontal and vertical merge - Add merge/unmerge buttons to table controller - Update table serialization logic Closes #1541 ``` -------------------------------- ### JavaScript Plugin Example Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Create a custom command plugin in JavaScript. This example defines a 'helloWorld' plugin that inserts 'Hello, World!' into the editor. ```javascript import { PluginCommand } from 'suneditor/src/interfaces'; import { dom } from 'suneditor/src/helper'; class HelloWorld extends PluginCommand { static key = 'helloWorld'; /** * @constructor * @param {SunEditor.Kernel} kernel - The Kernel instance */ constructor(kernel) { super(kernel); this.title = 'Hello World'; this.icon = 'HW'; } /** * @override * @type {PluginCommand['action']} */ action() { this.$.html.insert('
Hello, World!
'); this.$.history.push(false); } } export default HelloWorld; ``` -------------------------------- ### SunEditor Initialization and Hooks Example Source: https://github.com/jihong88/suneditor/blob/master/guide/typedef-guide.md Demonstrates how to initialize SunEditor with options and define a keydown hook. Requires SunEditor instance and type imports. ```typescript import type { SunEditor } from 'suneditor'; // Editor instance const editor: SunEditor.Instance = suneditor.init({ ... }); // Options const options: SunEditor.InitOptions = { width: '100%', height: '400px', buttonList: [['bold', 'italic', '|', 'underline']] }; // Hook parameters function onKeyDown(params: SunEditor.HookParams.KeyEvent): void { const { event, frameContext } = params; // ... } // Component info function handleComponent(info: SunEditor.ComponentInfo): void { console.log(info.pluginName, info.target); } ``` -------------------------------- ### Install SunEditor via NPM Source: https://github.com/jihong88/suneditor/blob/master/README.md Install the SunEditor package using NPM. This is the recommended method for most projects. ```bash npm install suneditor --save ``` -------------------------------- ### TypeScript Plugin Example Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Create a custom command plugin in TypeScript. This example defines a 'helloWorld' plugin that inserts 'Hello, World!' into the editor, including type annotations. ```typescript import { PluginCommand } from 'suneditor/src/interfaces'; import type { SunEditor } from 'suneditor/types'; class HelloWorld extends PluginCommand { static key = 'helloWorld'; constructor(kernel: SunEditor.Kernel) { super(kernel); this.title = 'Hello World'; this.icon = 'HW'; } action(): void { this.$.html.insert('Hello, World!
'); this.$.history.push(false); } } export default HelloWorld; ``` -------------------------------- ### Cross-Plugin-Type Composition Example Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Demonstrates how a plugin can implement multiple plugin types to offer various interaction modes, such as input, command, and dropdown functionalities. ```plaintext FontSize extends PluginInput ← base type (toolbar input) @implements {PluginCommand} ← provides action() for inc/dec buttons @implements {PluginDropdown} ← provides on() for dropdown menu ``` -------------------------------- ### Plugin Base Type Examples Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Illustrates the different base classes a plugin can extend to define its primary type and lifecycle methods. ```plaintext extends PluginModal → type: 'modal' (required: open()) extends PluginCommand → type: 'command' (required: action()) extends PluginInput → type: 'input' (optional: toolbarInputKeyDown/Change) extends PluginDropdown → type: 'dropdown' (required: action()) ``` -------------------------------- ### Example: Active Hook for Blockquote Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Demonstrates the `active` hook for the `Editor.EventManager`. This example updates the toolbar button's active state based on the selected element's node name. ```javascript /** * @hook Editor.EventManager * @type {SunEditor.Hook.Event.Active} */ active(element, target) { if (/^BLOCKQUOTE$/i.test(element?.nodeName)) { dom.utils.addClass(target, 'active'); return true; } dom.utils.removeClass(target, 'active'); return false; } ``` -------------------------------- ### Plugin Dependency Access Example Source: https://github.com/jihong88/suneditor/blob/master/ARCHITECTURE.md Demonstrates how a PluginCommand accesses the dependency bag ('$') via the KernelInjector in its constructor and uses it to retrieve language strings and format nodes. ```javascript import { PluginCommand } from '../../interfaces'; class Blockquote extends PluginCommand { static key = 'blockquote'; constructor(kernel) { super(kernel); // KernelInjector → this.$ = kernel.$ (Deps bag) this.title = this.$.lang.tag_blockquote; } action() { const node = this.$.selection.getNode(); this.$.format.applyBlock(this.quoteTag.cloneNode(false)); } } ``` -------------------------------- ### Example Security Report Email Subjects Source: https://github.com/jihong88/suneditor/blob/master/SECURITY.md These are examples of how to format the subject line for security vulnerability reports. ```text [Security] XSS vulnerability in editor preview ``` ```text [Security] Possible HTML injection via paste handler ``` -------------------------------- ### Enter Key Event Flow Example Source: https://github.com/jihong88/suneditor/blob/master/GUIDE.md Traces the sequence of events and function calls when a user presses the Enter key in the editor, from event capture to DOM update. ```text 1. User presses Enter ↓ 2. handler_ww_key.js captures keydown event ↓ 3. keydown.reducer.js analyzes the event with current editor state ↓ 4. Reducer delegates to keydown.rule.enter.js for Enter-specific logic ↓ 5. Returns action list: [{t: 'enter.line.addDefault', p: {...}}, {t: 'history.push', p: {...}}] ↓ 6. executor.js dispatches actions through effect registries (common + keydown) ↓ 7. Effects execute: - 'enter.line.addDefault' → calls format.addLine() - 'history.push' → calls history.push() ↓ 8. DOM updated, selection adjusted, onChange event triggered ``` -------------------------------- ### Implement a PluginBrowser for SunEditor Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Create a custom plugin that opens a gallery or browser interface. This example shows how to integrate with the Browser module for selecting items. ```javascript import { PluginBrowser } from 'suneditor/src/interfaces'; import Browser from 'suneditor/src/modules/contract/Browser'; class MyGallery extends PluginBrowser { static key = 'myGallery'; /** * @constructor * @param {SunEditor.Kernel} kernel - The Kernel instance */ constructor(kernel) { super(kernel); this.title = 'My Gallery'; this.icon = 'image'; this.browser = new Browser(this, this.$ /* browser config */); } open(onSelectFunction) { this.browser.open(onSelectFunction); } close() { this.browser.close(); } } ``` -------------------------------- ### Implement PluginCommand for Toggling Strikethrough Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Use PluginCommand for simple actions triggered by a button click. This example shows how to create a strikethrough toggle plugin. ```javascript import { PluginCommand } from 'suneditor/src/interfaces'; import { dom } from 'suneditor/src/helper'; class ToggleStrikethrough extends PluginCommand { static key = 'toggleStrikethrough'; /** * @constructor * @param {SunEditor.Kernel} kernel - The Kernel instance */ constructor(kernel) { super(kernel); this.title = 'Strikethrough'; this.icon = 'strikethrough'; // built-in icon key, or raw SVG/HTML } /** * @hook Editor.EventManager * @type {SunEditor.Hook.Event.Active} */ active(element, target) { if (/^S$/i.test(element?.nodeName)) { dom.utils.addClass(target, 'active'); return true; } dom.utils.removeClass(target, 'active'); return false; } /** * @override * @type {PluginCommand['action']} */ action() { const node = dom.utils.createElement('S'); this.$.inline.apply(node, { stylesToModify: null, nodesToRemove: null }); } } ``` -------------------------------- ### Module Dependency Access Example Source: https://github.com/jihong88/suneditor/blob/master/ARCHITECTURE.md Illustrates a Module ('Modal') receiving the dependency bag ('$') directly in its constructor and using it to add event listeners. ```javascript class Modal { #$; constructor(inst, $, element) { this.#$ = $; // Deps bag passed directly, no inheritance this.inst = inst; this.#$.eventManager.addEvent(element, 'submit', ...); } } ``` -------------------------------- ### SunEditor Initialization with Custom Options (JavaScript) Source: https://github.com/jihong88/suneditor/blob/master/test/dev/cdn_test.html Initializes a SunEditor instance with a specific toolbar configuration and various plugin-specific options. This example demonstrates how to set up the editor for features like math, file uploads, image galleries, and autocomplete. ```javascript const editorInstance = SUNEDITOR.create(document.querySelector('#editor_classic'), { plugins: SUNEDITOR.plugins, // allowedClassName: '.+', // attributeWhitelist: { '*': 'class|data-.+' }, // statusbar_container: '#root_statusbar_container', // shortcutsHint: false, // codeMirror: { // EditorView: EditorView, // extensions: [ // basicSetup, // html({ // matchClosingTags: true, // autoCloseTags: true // }), // javascript() // ], // minimalSetup: minimalSetup // }, iframe: false, // defaultLine: 'div', toolbar_sticky: 0, lineAttrReset: 'id', buttonList: bl, subToolbar: { buttonList: [['bold', 'dir', 'dir_ltr', 'dir_rtl', 'save']], width: 'auto', mode: 'balloon' // balloon, balloon-always, balloon-block }, math: { fontSizeList: [ { text: '1', value: '1em' }, { text: '1.5', value: '1.5em' } ] }, fileUpload: { uploadUrl: 'http://localhost:3000/editor/files/upload' }, imageGallery: { url: 'https://etyswjpn79.execute-api.ap-northeast-1.amazonaws.com/suneditor-demo' }, image: { uploadUrl: 'http://localhost:3000/editor/upload' }, autocomplete: { delayTime: 150, limitSize: 8, searchStartLength: 0, useCachingData: true, useCachingFieldData: true, triggers: { // ── @ Mention (API) ── '@': { limitSize: 5, apiUrl: 'https://74iuojmw16.execute-api.ap-northeast-1.amazonaws.com/suneditor-demo/SunEditor-sample-mention/{key}?limit={limitSize}', // apiHeaders: { Authorization: 'Bearer ...' }, // transformResponse: (json, xhr) => json.data, renderItem: (item) => '
Line 1: text with
Line 2: quoted text
content
'); this.$.history.push(false); } } ``` -------------------------------- ### Core Logic Class Dependency Access Example Source: https://github.com/jihong88/suneditor/blob/master/ARCHITECTURE.md Shows a Core Logic class ('Component') receiving the Kernel in its constructor and accessing the dependency bag ('$'), store, options, and event manager. ```javascript class Component { #kernel; #$; #store; constructor(kernel) { this.#kernel = kernel; // Kernel (runtime container) this.#$ = kernel.$; // Deps bag (shared dependency object) this.#store = kernel.store; // Store (runtime state) // Cache frequently used services from Deps this.#options = this.#$.options; this.#frameContext = this.#$.frameContext; this.#eventManager = this.#$.eventManager; } } ``` -------------------------------- ### Register Custom Plugin Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Register a custom plugin with SunEditor. This example shows how to import the plugin and include it in the editor's options. ```javascript import SUNEDITOR from 'suneditor'; import plugins from 'suneditor/src/plugins'; import HelloWorld from './plugins/helloWorld'; SUNEDITOR.create('editor', { plugins: [...plugins, HelloWorld], buttonList: [['bold', 'italic', 'helloWorld']], }); ``` -------------------------------- ### TypeScript Plugin with Implements Keyword Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Shows a TypeScript example of a plugin implementing multiple interfaces using the 'implements' keyword. This approach leverages TypeScript's static typing for better code maintainability. ```typescript import { interfaces } from 'suneditor'; import type { SunEditor } from 'suneditor/types'; class FontSize extends interfaces.PluginInput implements interfaces.PluginCommand, interfaces.PluginDropdown { static key = 'fontSize'; toolbarInputKeyDown(params: SunEditor.HookParams.ToolbarInputKeyDown): void { ... } toolbarInputChange(params: SunEditor.HookParams.ToolbarInputChange): void { ... } action(target: HTMLElement): void { ... } on(target: HTMLElement): void { ... } } ``` -------------------------------- ### Develop a PluginModal for SunEditor Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Create a custom plugin that opens a modal dialog for user input. This example demonstrates how to build and manage a modal for inserting code. ```javascript import { PluginModal } from 'suneditor/src/interfaces'; import Modal from 'suneditor/src/modules/contract/Modal'; import { dom } from 'suneditor/src/helper'; class InsertCode extends PluginModal { static key = 'insertCode'; /** * @constructor * @param {SunEditor.Kernel} kernel - The Kernel instance */ constructor(kernel) { super(kernel); this.title = 'Insert Code'; this.icon = 'code'; // Build modal HTML const modalEl = dom.utils.createElement( 'div', null, ``, ); this.modal = new Modal(this, this.$, modalEl); this.textarea = modalEl.querySelector('textarea'); } /** * @override * @type {PluginModal['open']} */ open() { this.modal.open(); } /** * @hook Modules.Modal * @type {SunEditor.Hook.Modal.Action} */ async modalAction() { const code = this.textarea.value; if (!code) return false; // close loading only const pre = dom.utils.createElement('PRE'); const codeEl = dom.utils.createElement('CODE'); codeEl.textContent = code; pre.appendChild(codeEl); this.$.html.insert(pre.outerHTML); this.$.history.push(false); return true; // close modal + loading } /** * @hook Modules.Modal * @type {SunEditor.Hook.Modal.On} */ modalOn(isUpdate) { if (!isUpdate) this.textarea.value = ''; this.textarea.focus(); } /** * @hook Modules.Modal * @type {SunEditor.Hook.Modal.Off} */ modalOff() { this.textarea.value = ''; } } ``` -------------------------------- ### JavaScript Plugin with JSDoc Implements Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Provides a JavaScript example of a plugin implementing multiple interfaces using JSDoc tags for type hints. This pattern is useful for composing different interaction modes. ```javascript import { PluginCommand, PluginDropdown, PluginInput } from 'suneditor/src/interfaces'; void PluginCommand; void PluginDropdown; /** * @implements {PluginCommand} * @implements {PluginDropdown} */ class FontSize extends PluginInput { static key = 'fontSize'; // PluginInput base toolbarInputKeyDown(params) { /* handle arrow keys, enter */ } toolbarInputChange(params) { /* apply typed value */ } // PluginCommand (implements) — inc/dec button clicks action(target) { /* adjust font size */ } // PluginDropdown (implements) — dropdown open on(target) { /* highlight active size in list */ } } ``` -------------------------------- ### Define Mouse Event Handler with JSDoc Source: https://github.com/jihong88/suneditor/blob/master/guide/typedef-guide.md Use JSDoc to type hint parameters for event handlers. This example shows how to define the expected structure for mouse event parameters. ```javascript /** * @param {SunEditor.HookParams.MouseEvent} params */ function onMouseDown(params) { const { event, frameContext, target } = params; // } ``` -------------------------------- ### Implement Custom Plugin with Event Hooks Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Example of a custom plugin that intercepts the `onKeyDown` event to handle the Tab key, preventing default behavior and inserting spaces. Returning `false` stops further event processing. ```javascript class MyPlugin extends PluginField { static options = { eventIndex: 100, // Default priority (lower = earlier) eventIndex_onKeyDown: 50, // Override: run onKeyDown earlier eventIndex_onInput: 200, // Override: run onInput later }; /** * @hook Editor.EventManager * @type {SunEditor.Hook.Event.OnKeyDown} */ onKeyDown({ event, range }) { if (event.key === 'Tab') { event.preventDefault(); this.$.html.insert(' '); return false; // Stop further processing } } } ``` -------------------------------- ### SunEditor State Management API Source: https://github.com/jihong88/suneditor/blob/master/ARCHITECTURE.md Demonstrates how to read, write, and subscribe to state changes within the SunEditor's store. Use 'get' to read values, 'set' to update and notify subscribers, and 'subscribe' for reactive updates. ```javascript const rootKey = store.get('rootKey'); const hasFocus = store.get('hasFocus'); // Write (notifies subscribers) store.set('hasFocus', true); store.set('_preventBlur', false); // Subscribe const unsubscribe = store.subscribe('hasFocus', (newVal, oldVal) => { ... }); unsubscribe(); // cleanup ``` -------------------------------- ### Correct Plugin Instantiation Source: https://github.com/jihong88/suneditor/blob/master/GUIDE.md Demonstrates the correct way to pass plugin class references to the options. This ensures the Kernel can manage the plugin's lifecycle. ```javascript // Correct plugins: [MyPlugin]; ``` -------------------------------- ### Build Project for Production Source: https://github.com/jihong88/suneditor/blob/master/GUIDE.md Builds a minified version of the project, optimized for production deployment. ```bash npm run build:prod # Build for production (minified) ``` -------------------------------- ### Initialize SunEditor with NPM Source: https://github.com/jihong88/suneditor/blob/master/README.md Import necessary CSS and the SunEditor library, then create an editor instance. Ensure you have an HTML element with the ID 'editor' or a textarea with the same ID. ```javascript import 'suneditor/css/editor'; // Editor UI import 'suneditor/css/contents'; // For displaying HTML import suneditor from 'suneditor'; // HTML: or suneditor.create(document.querySelector('#editor'), { // options }); ``` -------------------------------- ### Build Project for Development Source: https://github.com/jihong88/suneditor/blob/master/GUIDE.md Builds the project with source maps included, suitable for debugging during development. ```bash npm run build:dev # Build for development (with source maps) ``` -------------------------------- ### Plugin Registration Options Source: https://github.com/jihong88/suneditor/blob/master/GUIDE.md Illustrates the two ways to register plugins: as an array of class references or as an object mapping names to class references. ```javascript options.plugins: [ImagePlugin, VideoPlugin, ...] // or { image: ImagePlugin, video: VideoPlugin, ... } ``` -------------------------------- ### Run Playwright End-to-End Tests Source: https://github.com/jihong88/suneditor/blob/master/GUIDE.md Executes Playwright end-to-end tests. The web server will start or reuse an existing instance on localhost:8088. ```bash npm run test:e2e # Run Playwright E2E tests (webServer starts/reuses localhost:8088) ``` -------------------------------- ### SunEditor Plugin Inheritance Chain Source: https://github.com/jihong88/suneditor/blob/master/GUIDE.md Illustrates the inheritance hierarchy for SunEditor plugins, starting from KernelInjector and extending to specific plugin types. ```text KernelInjector → Base → PluginCommand/PluginModal/PluginDropdown/... ↓ constructor(kernel) → super(kernel) → this.$ = kernel.$ ``` -------------------------------- ### Create SunEditor Instance with Plugins and Button List Source: https://github.com/jihong88/suneditor/blob/master/test/e2e/test.html Initializes a SunEditor instance with a comprehensive set of plugins and a custom button list. Exposes the editor instance globally for testing. ```javascript import suneditor from '../../src/suneditor.js'; import plugins from '../../src/plugins/index.js'; const editor = suneditor.create('editor', { plugins: plugins, buttonList: [ ['undo', 'redo'], ['font', 'fontSize', 'blockStyle'], ['bold', 'underline', 'italic', 'strike', 'subscript', 'superscript'], ['fontColor', 'hiliteColor'], ['removeFormat'], ['outdent', 'indent'], ['align', 'horizontalRule', 'list', 'lineHeight'], ['table', 'link', 'image', 'video', 'audio'], ['fullScreen', 'showBlocks', 'codeView'], ['preview', 'print'] ], width: '100%', height: 'auto', minHeight: '400px' }); // Expose editor globally for Playwright tests window.suneditor = editor; ``` -------------------------------- ### SunEditor Initialization Order Source: https://github.com/jihong88/suneditor/blob/master/GUIDE.md The sequence of steps involved in creating and initializing a SunEditor instance, from the initial create call to the final onload event. ```text 1. suneditor.create() → Validates target, merges options 2. new Editor() → Creates editor instance 3. Constructor() → Builds DOM (toolbar, statusbar, wysiwyg frames) 4. new CoreKernel() → Kernel (runtime container) a. L1: Store (state management) b. Deps Phase 1: Config deps added to $ (L2) c. L3: Logic instances created (dom, shell, panel) d. Deps Phase 2: Logic deps added to $ (Deps bag complete) e. L3 Init Pass: _init() called on L3 instances that need post-Phase 2 setup f. L4: EventOrchestrator 5. editor.#Create() → Plugin registration, event setup 6. editor.#editorInit() → Frame init, triggers onload event ``` -------------------------------- ### Configure KaTeX with SunEditor Source: https://github.com/jihong88/suneditor/blob/master/guide/external-libraries.md Configure SunEditor to use KaTeX for math formula rendering. Ensure KaTeX library and CSS are imported. ```javascript import katex from 'katex'; import 'katex/dist/katex.css'; import SUNEDITOR from 'suneditor'; import plugins from 'suneditor/src/plugins'; const editor = SUNEDITOR.create('editor', { plugins: plugins, buttonList: [['math']], externalLibs: { katex: { src: katex, options: { // Optional: KaTeX render options throwOnError: false, displayMode: true, }, }, }); ``` -------------------------------- ### Auto-fix Formatting Issues with ESLint Source: https://github.com/jihong88/suneditor/blob/master/CONTRIBUTING.md Run this command to automatically fix code formatting issues according to the project's ESLint configuration. Ensure you have Node.js v14+ installed. ```bash npm run lint:fix-all ``` -------------------------------- ### KaTeX CDN Usage (JavaScript) Source: https://github.com/jihong88/suneditor/blob/master/guide/external-libraries.md Initialize SunEditor with KaTeX using global variables when loaded via CDN. ```javascript // katex is available as global variable SUNEDITOR.create('editor', { externalLibs: { katex: { src: katex, }, }); ``` -------------------------------- ### Implement PluginDropdown for Custom Text Alignment Source: https://github.com/jihong88/suneditor/blob/master/guide/custom-plugin.md Use PluginDropdown to create plugins with dropdown menus. This example implements a custom text alignment plugin with predefined alignment options. ```javascript import { PluginDropdown } from 'suneditor/src/interfaces'; import { dom } from 'suneditor/src/helper'; /** * @typedef {Object} CustomAlignPluginOptions * @property {Array.<"right"|"center"|"left"|"justify">} [items] - Align items */ class CustomAlign extends PluginDropdown { static key = 'customAlign'; /** * @constructor * @param {SunEditor.Kernel} kernel - The Kernel instance * @param {CustomAlignPluginOptions} pluginOptions */ constructor(kernel, pluginOptions) { super(kernel); this.title = this.$.lang.align; this.icon = 'align_left'; // Build dropdown HTML const menu = dom.utils.createElement( 'div', { class: 'se-dropdown se-list-layer' }, `