diff --git a/MIGRATION.md b/MIGRATION.md
new file mode 100644
index 0000000..61b8101
--- /dev/null
+++ b/MIGRATION.md
@@ -0,0 +1,385 @@
+
Migration guides
+
+### Navigation
+
+- [From v4 to v5](#4to5)
+- [From v3 to v4](#3to4)
+- [From v2 to v3](#2to3)
+- [From v1 to v2](#1to2)
+
+## From v4 to v5
+
+Version 5 introduces several major breaking changes that will affect all users:
+
+⚠️ A new configuration method has been introduced.
+
+⚠️ The `default` preset has been renamed to `defaultHtml`.
+
+To facilitate a smooth transition, guidance is provided for the following scenarios:
+
+- [No custom configuration](#no-custom-configuration)
+- [Custom configuration](#custom-configuration)
+- [Locally modified fork of the plugin](#locally-modified-fork)
+
+#### No custom configuration:
+
+If you don't have custom plugin configuration, you only need to update the existing fields to use
+the new default HTML preset to avoid missing preset error. There are two options how to solve it:
+
+- **Option1 (Preferable)**: Update the fields in the Content-Type Builder or modify your shemas
+ manually to use `defaultHtml` preset instead of `default`:
+
+```js
+// api/type_name/content-types/type_name/schema.json
+
+"name_of_field": {
+ "type": "customField",
+ "options": {
+ "preset": "default", // -> "defaultHtml"
+ },
+ "customField": "plugin::ckeditor5.CKEditor"
+},
+```
+
+- **Option2**: Apply custom plugin configuration. Set the name in the `defaultHtmlPreset` to
+ `default` and add it to the `presets` array:
+
+```ts
+// src/admin/app.[js|tsx]
+
+import { setPluginConfig, defaultHtmlPreset } from '@_sh/strapi-plugin-ckeditor';
+
+export default {
+ register() {
+ defaultHtmlPreset.name = 'default';
+ setPluginConfig({ presets: [defaultHtmlPreset] });
+ },
+};
+```
+
+#### Custom configuration:
+
+In addition to the steps mentioned in the previous section:
+
+⚠️ The `presets` property is now an array of objects of type `Preset` instead of an object.
+
+⚠️ The `field` property in a `Preset` has been replaced by `name` and `description`.
+
+⚠️ The plugin no longer exposes any CKEditor packages to the global object, you must import them
+directly.
+
+⚠️ The provided `theme` and `presets` are no longer merged with the defaults, instead, overwrite them.
+
+⚠️ The style plugin and material color palette have been removed from the default preset.
+
+⚠️ The plugin's theme has been updated to use Strapi's theme variables.
+
+To transition to the new configuration method, copy and paste your configuration from the existing
+configuration file into `/src/admin/app.js|tsx`:
+
+(Alternatively you can move you configuration file to `/src/admin/`, export the configuration,
+and then import it in `admin.js|tsx`)
+
+**Before (v4):**
+
+```ts
+// config/ckedtior.js
+
+const CKEConfig = () => ({
+ dontMergePresets: true,
+ presets: {
+ default: {
+ editorConfig: {
+ toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'],
+ },
+ },
+ myCustomPreset: {
+ field: {
+ key: 'myCustomPreset',
+ value: 'myCustomPreset',
+ metadatas: {
+ intlLabel: {
+ id: 'ckeditor5.preset.myCustomPreset.label',
+ defaultMessage: 'My custom preset',
+ },
+ },
+ },
+ editorConfig: {
+ plugins: [
+ globalThis.SH_CKE.Autoformat,
+ globalThis.SH_CKE.Bold,
+ globalThis.SH_CKE.Italic,
+ globalThis.SH_CKE.Essentials,
+ globalThis.SH_CKE.Heading,
+ globalThis.SH_CKE.StrapiMediaLib,
+ globalThis.SH_CKE.StrapiUploadAdapter,
+ //...
+ ],
+ toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'],
+ //...
+ },
+ },
+ },
+ // theme: {},
+});
+```
+
+**Now (v5):**
+
+
+ app.js
+
+```ts
+// src/admin/app.js
+
+import { Autoformat, Bold, Italic, Essentials, Heading } from 'ckeditor5';
+
+import {
+ setPluginConfig,
+ defaultHtmlPreset,
+ StrapiMediaLib,
+ StrapiUploadAdapter,
+} from '@_sh/strapi-plugin-ckeditor';
+
+const CKEConfig = () => ({
+ presets: [
+ {
+ ...defaultHtmlPreset,
+
+ /**
+ * If you use default preset and haven't updated your schemas to replace
+ * the `default` preset with `defaultHtml`, you can change `name`
+ * in defaultHtmlPreset to 'default' to avoid missing preset error.
+ */
+ // name: 'default',
+
+ editorConfig: {
+ ...defaultHtmlPreset.editorConfig,
+ toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'],
+ },
+ },
+ {
+ name: 'myCustomPreset',
+ description: 'My custom preset',
+ editorConfig: {
+ licenseKey: 'GPL',
+ plugins: [
+ Autoformat,
+ Bold,
+ Italic,
+ Essentials,
+ Heading,
+ StrapiMediaLib,
+ StrapiUploadAdapter,
+ //...
+ ],
+ toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'],
+ //...
+ },
+ },
+ ],
+ // theme: {},
+});
+
+export default {
+ register() {
+ const myConfig = CKEConfig();
+ setPluginConfig(myConfig);
+ },
+};
+```
+
+
+
+
+ app.tsx
+
+```ts
+// src/admin/app.tsx
+
+import { Autoformat, Bold, Italic, Essentials, Heading } from 'ckeditor5';
+
+import {
+ type PluginConfig,
+ setPluginConfig,
+ defaultHtmlPreset,
+ StrapiMediaLib,
+ StrapiUploadAdapter,
+} from '@_sh/strapi-plugin-ckeditor';
+
+const CKEConfig = (): PluginConfig => ({
+ presets: [
+ {
+ ...defaultHtmlPreset,
+
+ /**
+ * If you use default preset and haven't updated your schemas to replace
+ * the `default` preset with `defaultHtml`, you can change `name`
+ * in defaultHtmlPreset to 'default' to avoid missing preset error.
+ */
+ // name: 'default',
+
+ editorConfig: {
+ ...defaultHtmlPreset.editorConfig,
+ toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'],
+ },
+ },
+ {
+ name: 'myCustomPreset',
+ description: 'My custom preset',
+ editorConfig: {
+ licenseKey: 'GPL',
+ plugins: [
+ Autoformat,
+ Bold,
+ Italic,
+ Essentials,
+ Heading,
+ StrapiMediaLib,
+ StrapiUploadAdapter,
+ //...
+ ],
+ toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'],
+ //...
+ },
+ },
+ ],
+ // theme: {},
+});
+
+export default {
+ register() {
+ const myConfig = CKEConfig();
+ setPluginConfig(myConfig);
+ },
+};
+```
+
+
+
+#### Locally modified fork of the plugin:
+
+The guidance provided in the previous two sections likely applies to your case as well.
+
+Additionally:
+
+⚠️ The plugin has been migrated to TypeScript.
+
+⚠️ The codebase has been significantly refactored.
+
+⚠️ Several new features have been introduced, such as fullscreen option.
+
+These changes will likely impact your setup. Please review the updated codebase.
+
+## From v3 to v4
+
+- The new version introduces support for Strapi v5 and is incompatible with Strapi v4. You will need to update your Strapi project to version 5 before upgrading.
+
+- The plugin development process has changed. Please refer to the updated contribution guide for more information.
+
+## From v2 to v3
+
+- The default editor configurations (toolbar, toolbarBalloon, blockBalloon) have been removed and now there is only one preset by default. You will need to update your fields in the Content-Type Builder.
+
+- Config file extension has changed from `.txt` to `.js` or `.ts`
+- Configuration object properties have been renamed:
+ - `configsOverwrite` -> `dontMergePresets`
+ - `themeOverwrite` -> `dontMergeTheme`
+ - `configs` -> `presets`
+- From v3 instead of globalThis.CKEditorConfig = {..}, the config file must define a function called CKEConfig that returns the configuration object.
+
+Example of the new configuration file:
+
+
+ ckeditor.js
+
+```js
+const CKEConfig = () => ({
+ presets: {
+ myCustomPreset: {
+ field: {
+ key: 'myCustomPreset',
+ value: 'myCustomPreset',
+ metadatas: {
+ intlLabel: {
+ id: 'ckeditor5.preset.myCustomPreset.label',
+ defaultMessage: 'My custom preset',
+ },
+ },
+ },
+ editorConfig: {
+ plugins: [
+ globalThis.SH_CKE.Autoformat,
+ globalThis.SH_CKE.Bold,
+ globalThis.SH_CKE.Italic,
+ globalThis.SH_CKE.Essentials,
+ globalThis.SH_CKE.Heading,
+ globalThis.SH_CKE.Image,
+ globalThis.SH_CKE.ImageCaption,
+ globalThis.SH_CKE.ImageStyle,
+ globalThis.SH_CKE.ImageToolbar,
+ globalThis.SH_CKE.ImageUpload,
+ globalThis.SH_CKE.Indent,
+ globalThis.SH_CKE.Link,
+ globalThis.SH_CKE.List,
+ globalThis.SH_CKE.Paragraph,
+ globalThis.SH_CKE.PasteFromOffice,
+ globalThis.SH_CKE.Table,
+ globalThis.SH_CKE.TableToolbar,
+ globalThis.SH_CKE.TableColumnResize,
+ globalThis.SH_CKE.TableCaption,
+ globalThis.SH_CKE.StrapiMediaLib,
+ globalThis.SH_CKE.StrapiUploadAdapter,
+ ],
+ toolbar: [
+ 'heading',
+ '|',
+ 'bold',
+ 'italic',
+ 'link',
+ 'bulletedList',
+ 'numberedList',
+ '|',
+ 'strapiMediaLib',
+ 'insertTable',
+ '|',
+ 'undo',
+ 'redo',
+ ],
+ heading: {
+ options: [
+ { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
+ { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
+ { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
+ { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
+ { model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
+ ],
+ },
+ image: {
+ toolbar: [
+ 'imageStyle:inline',
+ 'imageStyle:block',
+ 'imageStyle:side',
+ '|',
+ 'toggleImageCaption',
+ 'imageTextAlternative',
+ ],
+ },
+ table: {
+ contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', '|', 'toggleTableCaption'],
+ },
+ },
+ },
+ },
+});
+```
+
+
+
+## From v1 to v2
+
+- You will need to update Strapi to version v4.4.x for plugin v2.0.x, or to v4.13.0+ for v2.1.x.
+
+- Starting with v2, the plugin uses the Custom Field API, so you'll need to manually update your schema.
+
+- The plugin configuration should be defined in /config/ckeditor.txt from v2 onward. [Please refer to the v2 configuration guide for details.](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/e782475f54b8a50a04f55275c89ef5bf61a15745/README.md?plain=1#L54)
diff --git a/README.md b/README.md
index 1aeed46..0694ecc 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,41 @@
-
+
-CKEditor 5 for Strapi
+Strapi ➕ CKEditor
-Integrates CKEditor 5 into your Strapi project as a fully customizable custom field. (Unofficial integration)
+Integrates CKEditor 5 into your Strapi project as a fully customizable custom field. (Community edition)
## 👋 Get Started
-* [Features](#features)
-* [Installation](#installation)
-* [Usage](#usage)
-* [Configuration](#configuration)
-* [Contributing](#contributing)
-* [Migration](#migration)
-* [Requirements](#requirements)
+- [Features](#features)
+- [Installation](#installation)
+- [Usage](#usage)
+- [Configuration](#configuration)
+- [Contributing](#contributing)
+- [Migration](#migration)
+- [Requirements](#requirements)
## ✨ Features
-* **Media library integration**
-* **Responsive images support**
-* **Strapi theme switching support**
-* **Supports custom styles for the editor**
-* **Supports i18n and different UI translations**
-* **Allows custom editor configrations**
-* **Plugins extensibility**
+- **Extensive configuration**
+- **Media Library integration**
+- **Dark theme support**
+- **Fullscreen expansion**
+- **Responsive images support**
+- **Automatic UI translations**
+- **i18n support**
+- **Self-Hosted**
-
+
-
## 🔧 Installation
+**Strapi v5:**
Add the package to your Strapi application:
@@ -42,683 +43,693 @@ Add the package to your Strapi application:
yarn add @_sh/strapi-plugin-ckeditor
```
-For Strapi v4:
-```bash
-yarn add @_sh/strapi-plugin-ckeditor@strapi-v4-latest
-```
-
Then, build the app:
```bash
yarn build
```
+**Strapi v4:**
+
+Version 3 of the plugin will remain available for Strapi v4 until March 2026.
+Refer to the [**v3 installation guide**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/3.x.x/README.md#installation)
+for setup instructions.
+
+> Note: Version 3 receives only essential bug fixes. The configuration process and available
+> features in version 3 differ significantly from the latest plugin versions.
+
## ✍️ Usage
+The field can be fould in the Content-Type Builder under the `CUSTOM` tab:
+
-
+
+**Presets**
+
+A preset is a set of settings that define the editor's features and appearence. You
+can specify a dedicated preset for each field. The available presets can be customized through the
+[configuration](#configuration).
+
+
+
+
+
+The plugin provides two presets out of the box: `Default HTML editor` and `Default Markdown editor`,
+each has different output format: HTML and Markdown, respectively. Both presets include
+an extensive set of non-premium CKEditor plugins.
+
- Default preset:
-
+ Default HTML editor:
+
+
+
+
+
+
+
+
+
-
+
-## ⚙️ Configuration
+
+ Default Markdown editor:
+
+
+
-> It is highly recommended to explore [**the official CKEditor5 documentation**](https://ckeditor.com/docs/ckeditor5/latest/features/index.html) and [**the Strapi Custom Field API**](https://docs.strapi.io/developer-docs/latest/development/custom-fields.html#registering-a-custom-field-on-the-server)
->
-> To display content from external sources, such as images or videos, in your admin panel, you need to configure your `middlewares.js` file. [**Check the official documentation for details.**](https://docs.strapi.io/dev-docs/configurations/middlewares#security)
+
+
+
-The plugin configuration should be defined in `your-app/config/ckeditor.js` or in `ckeditor.ts` if you are using TypeScript in your project.
+
-By default, the plugin provides one default preset that can be modified in the config file, and you can expand the preset set as needed.
+**Responsive images**
-The plugin exposes all default CKEditor packages to the global variable `SH_CKE`.
+The plugin generates `srcset` attribute for inserted images if the image has any `formats` other than `thumbnail`.
-The config file must define a function called `CKEConfig` that returns the configuration object.
+**UI language**: If you don't specify the UI language in the editor configuration, the plugin will
+default to the Strapi admin's preferred language. If no preference is set, English will be used as a fallback.
-The structure of the configuration object is as follows:
+**Content language**: i18n for the editor's content language can be enabled by checking the
+`Enable localization for this field` option in the Advanced Settings tab.
-```js
-{
- dontMergePresets:bool,
- dontMergeTheme:bool,
- presets:{
- default:{
- field:{...},
- styles:string,
- editorConfig:{...},
- },
- ...
- },
- theme:{
- common:string,
- light:string,
- dark:string,
- additional:string,
- }
-}
-```
+## ⚙️ Configuration
-Every preset in the `presets` object contains three properties:
+The plugin configuration must be defined on the front-end. The plugin provides a `setPluginConfig`
+function, which accepts a plugin configuration (`PluginConfig`) that can include an array of presets
+and a theme object:
-1. `field (object)` Registers this configuration version in the Admin panel.
-2. `styles (string)` Styles applied to the editor in this version.
-3. `editorConfig (object)` The CKEditor configuration.
+```ts
+import { setPluginConfig } from '@_sh/strapi-plugin-ckeditor';
-The `theme` object is used to modify the default global theme of the plugin.
-It contains four properties:
+function setPluginConfig(pluginConfig: {
+ presets: Preset[] | undefined;
+ theme: Theme | undefined;
+}): void;
+```
-1. `common (string)` Styles applied to the editor to ensure proper appearance of the input.
-2. `light (string)` Styles applied to the editor when Strapi is in light mode.
-3. `dark (string)` Styles applied to the editor when Strapi is in dark mode.
-4. `additional (string)` Additional styles to further enhance the editors appearance.
+**Key points:**
-By default, everything defined in the user’s configuration is merged with the
-default configuration object. These two properties allow you to prevent
-this behavior:
+- The function must be invoked before the admin panel's bootstrap lifecycle function. The general recommendation is to call it inside the admin panel's register lifecycle function.
+- Provided properties will overwrite the default configuration values.
+- The configuration becomes immutable after the first invocation, preventing further modifications.
-1. `dontMergePresets (bool)`
-2. `dontMergeTheme (bool)`
+**Plugin configuration object** includes optional `presets` and `theme` properties:
-Since Strapi uses i18n for translation, the `ignorei18n` property is added to the
-`editorConfig.language` object. This property allows you to manually set the
-content language, bypassing i18n. The language object looks like this:
+```ts
+type PluginConfig = {
+ /**
+ * Presets are sets of settings that define the editor's features and appearance.
+ */
+ presets?: Preset[];
+ /**
+ * Styles applied globally to every editor instance.
+ */
+ theme?: Theme;
+};
+```
-```js
-language:{
- ignorei18n (bool),
- ui (string),
- content (string)
-}
+`presets` is an array of objects of type `Preset`. Each preset includes the following properties:
+
+```ts
+type Preset = {
+ /**
+ * Preset name, displayed in the schema.
+ */
+ name: string;
+ /**
+ * Preset description, displayed in the Content-Type Builder.
+ */
+ description: string;
+ /**
+ * CKEditor configuration object.
+ *
+ * @see {@link https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html | CKEditor documentation}
+ */
+ editorConfig: EditorConfig;
+ /**
+ * Additional styles applied to the editor instance after the theme styles.
+ */
+ styles?: EditorStyles;
+};
```
-The language determination follows this logic:
-- Plugin UI language:
-`language.ui || strapi admin preferred language || 'en'`
+`theme` object defines a plugin CSS styles applied to every editor instance. It includes the
+following properties:
+
+```ts
+/**
+ * The `common` styles are applied first, followed by `light` or `dark` styles
+ * according to the preferences, and finally `additional` styles.
+ */
+type Theme = {
+ /**
+ * Core styles.
+ */
+ common?: EditorStyles;
+ /**
+ * Styles apllied in light mode.
+ */
+ light?: EditorStyles;
+ /**
+ * Styles apllied in dark mode.
+ */
+ dark?: EditorStyles;
+ /**
+ * Additional styles that complement the theme.
+ */
+ additional?: EditorStyles;
+};
+```
-- Content language:
-`(ignorei18n ? language.content : i18n) || language.ui`
+```ts
+/**
+ * Represents styles that can be applied to an editor instance.
+ * Can be a plain CSS string or an array of interpolations for dynamic styling.
+ */
+export type EditorStyles = string | Interpolation[];
+```
-### Configuration examples
+**Default presets and theme**
-Adding a new preset:
+To simplify the process of defining a new preset, the plugin exports default presets and
+a default theme, which can be used as a base in custom configurations:
-
- ckeditor.js
+```ts
+import {
+ defaultTheme,
+ defaultHtmlPreset,
+ defaultMarkdownPreset,
+} from '@_sh/strapi-plugin-ckeditor';
+```
-```js
-const CKEConfig = () => ({
- presets: {
- markdown:{
- field: {
- key: "markdown",
- value: "markdown",
- metadatas: {
- intlLabel: {
- id: "ckeditor.preset.markdown.label",
- defaultMessage: "Markdown output",
- },
- },
- },
- editorConfig:{
- placeholder: 'Markdown editor',
- plugins: [
- globalThis.SH_CKE.Essentials,
- globalThis.SH_CKE.Autoformat,
- globalThis.SH_CKE.BlockQuote,
- globalThis.SH_CKE.Bold,
- globalThis.SH_CKE.Heading,
- globalThis.SH_CKE.Image,
- globalThis.SH_CKE.ImageCaption,
- globalThis.SH_CKE.ImageStyle,
- globalThis.SH_CKE.ImageToolbar,
- globalThis.SH_CKE.ImageUpload,
- globalThis.SH_CKE.Indent,
- globalThis.SH_CKE.Italic,
- globalThis.SH_CKE.Link,
- globalThis.SH_CKE.List,
- globalThis.SH_CKE.MediaEmbed,
- globalThis.SH_CKE.Paragraph,
- globalThis.SH_CKE.Table,
- globalThis.SH_CKE.TableToolbar,
- globalThis.SH_CKE.SourceEditing,
- globalThis.SH_CKE.StrapiMediaLib,
- globalThis.SH_CKE.StrapiUploadAdapter,
- globalThis.SH_CKE.Markdown,
- globalThis.SH_CKE.Code,
- globalThis.SH_CKE.CodeBlock,
- globalThis.SH_CKE.TodoList,
- globalThis.SH_CKE.Strikethrough,
- globalThis.SH_CKE.HorizontalLine
- ],
- toolbar: {
- items: [
- 'heading',
- '|',
- 'bold',
- 'italic',
- 'strikethrough',
- 'link',
- '|',
- 'bulletedList',
- 'numberedList',
- 'todoList',
- '|',
- 'code',
- 'codeBlock',
- '|',
- 'uploadImage',
- 'strapiMediaLib',
- 'blockQuote',
- 'horizontalLine',
- '-',
- 'sourceEditing',
- '|',
- 'outdent',
- 'indent',
- '|',
- 'undo',
- 'redo'
- ],
- shouldNotGroupWhenFull: true
- },
- image: {
- toolbar: [ 'imageStyle:inline', 'imageStyle:block', 'imageStyle:side', '|', 'toggleImageCaption', 'imageTextAlternative' ]
- },
- codeBlock: {
- languages: [
- { language: 'css', label: 'CSS' },
- { language: 'html', label: 'HTML' },
- { language: 'javascript', label: 'JavaScript' },
- { language: 'php', label: 'PHP' }
- ]
- },
- }
- },
- }
-})
+**Integration with Strapi's Media Library**
+
+To integrate CKEditor with the Strapi's Media Library, the plugin provides two CKEditor plugins
+that can be included in your presets without additional configuration:
+
+```ts
+import { StrapiMediaLib, StrapiUploadAdapter } from '@_sh/strapi-plugin-ckeditor';
```
-
+**Available type definitions**
----
+The following type definitions are available for use:
-Changing toolbar buttons, modifying the plugin list, adding styles to the default preset:
+```ts
+import type {
+ PluginConfig,
+ Preset,
+ EditorConfig,
+ Theme,
+ EditorStyles,
+} from '@_sh/strapi-plugin-ckeditor';
+```
- ckeditor.js
-
-```js
-const CKEConfig = () => ({
- presets:{
- default:{
- styles:`
- --ck-focus-ring:3px dashed #5CB176;
-
- .ck.ck-content.ck-editor__editable {
- &.ck-focused:not(.ck-editor__nested-editable) {
- border: var(--ck-focus-ring) !important;
- }
- }
- .ck.ck-content.ck-editor__editable.ck-rounded-corners.ck-editor__editable_inline.ck-blurred{
- min-height: 400px;
- max-height: 400px;
- }
- .ck.ck-content.ck-editor__editable.ck-rounded-corners.ck-editor__editable_inline.ck-focused{
- min-height: 400px;
- max-height: 1700px;
- }
- `,
- editorConfig:{
- plugins: [
- globalThis.SH_CKE.Bold,
- globalThis.SH_CKE.Italic,
- globalThis.SH_CKE.Essentials,
- globalThis.SH_CKE.Heading,
- globalThis.SH_CKE.Image,
- globalThis.SH_CKE.ImageCaption,
- globalThis.SH_CKE.ImageStyle,
- globalThis.SH_CKE.ImageToolbar,
- globalThis.SH_CKE.Link,
- globalThis.SH_CKE.List,
- globalThis.SH_CKE.Paragraph,
- globalThis.SH_CKE.StrapiMediaLib,
- globalThis.SH_CKE.StrapiUploadAdapter,
- ],
- toolbar: [
- 'heading',
- '|',
- 'bold', 'italic', 'link', 'bulletedList', 'numberedList',
- '|',
- 'strapiMediaLib', 'insertTable',
- '|',
- 'undo', 'redo'
- ]
- }
- }
- }
-})
+ Type definitions
+
+```ts
+/**
+ * Plugin configuration object.
+ */
+export type PluginConfig = {
+ /**
+ * Presets are sets of settings that define the editor's features and appearance.
+ */
+ presets?: Preset[];
+ /**
+ * Styles applied globally to every editor instance.
+ */
+ theme?: Theme;
+};
```
-
+```ts
+/**
+ * Preset is a set of settings that define the editor's features and appearance.
+ */
+export type Preset = {
+ /**
+ * Preset name, displayed in the schema.
+ */
+ name: string;
+ /**
+ * Preset description, displayed in the Content-Type Builder.
+ */
+ description: string;
+ /**
+ * Additional styles applied to the editor instance after the theme styles.
+ */
+ styles?: EditorStyles;
+ /**
+ * CKEditor configuration object.
+ *
+ * @see {@link https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html | CKEditor documentation}
+ */
+ editorConfig: EditorConfig;
+};
+```
----
+```ts
+/**
+ * CKEditor configuration object.
+ *
+ * @see {@link https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html | CKEditor documentation}
+ */
+export type EditorConfig = CKE5EditorConfig;
+```
-> 📂 Default preset: [**admin/src/components/Input/presets/default.js**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/admin/src/components/Input/presets/default.js)
->
-> 📂 Default theme: [**admin/src/components/Input/theme**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/admin/src/components/Input/theme)
+```ts
+/**
+ * Styles applied globally to every editor instance.
+ *
+ * @remarks
+ *
+ * The `common` styles are applied first, followed by `light` or `dark` styles
+ * according to the preferences, and finally `additional` styles.
+ */
+export type Theme = {
+ /**
+ * Core styles.
+ */
+ common?: EditorStyles;
+ /**
+ * Styles apllied in light mode.
+ */
+ light?: EditorStyles;
+ /**
+ * Styles apllied in dark mode.
+ */
+ dark?: EditorStyles;
+ /**
+ * Additional styles that complement the theme.
+ */
+ additional?: EditorStyles;
+};
+```
-### Adding plugins
+```ts
+/**
+ * Represents styles that can be applied to an editor instance.
+ * Can be a plain CSS string or an array of interpolations for dynamic styling.
+ */
+export type EditorStyles = string | Interpolation[];
+```
-Your plugin must be available in the `global` object.
+
-**Example of adding the Timestamp plugin:**
+### Configuration examples:
-1. Create the plugin:
+
+ Adding a new preset [JS]
```js
-// your-app/src/admin/timestamp.js
-
-class Timestamp extends globalThis.SH_CKE.Plugin {
- init() {
- const editor = this.editor;
- // The button must be registered among the UI components of the editor
- // to be displayed in the toolbar.
- editor.ui.componentFactory.add( 'timestamp', () => {
- // The button will be an instance of ButtonView.
- const button = new globalThis.SH_CKE.ButtonView();
-
- button.set( {
- label: 'Timestamp',
- withText: true
- } );
-
- // Execute a callback function when the button is clicked.
- button.on( 'execute', () => {
- const now = new Date();
-
- // Change the model using the model writer.
- editor.model.change( writer => {
-
- // Insert the text at the user's current position.
- editor.model.insertContent( writer.createText( now.toString() ) );
- } );
- } );
-
- return button;
- } );
- }
-}
-
-globalThis.Timestamp = Timestamp;
-```
+// src/admin/app.js
+
+import {
+ Bold,
+ Italic,
+ Essentials,
+ Heading,
+ Image,
+ ImageCaption,
+ ImageStyle,
+ ImageToolbar,
+ ImageUpload,
+ Link,
+ List,
+ Paragraph,
+} from 'ckeditor5';
+
+import { setPluginConfig, StrapiMediaLib, StrapiUploadAdapter } from '@_sh/strapi-plugin-ckeditor';
+
+const myCustomPreset = {
+ name: 'myCustomPreset',
+ description: 'myCustomPreset description',
+ editorConfig: {
+ licenseKey: 'GPL',
+ plugins: [
+ Bold,
+ Italic,
+ Essentials,
+ Heading,
+ Image,
+ ImageCaption,
+ ImageStyle,
+ ImageToolbar,
+ ImageUpload,
+ Link,
+ List,
+ Paragraph,
+ StrapiMediaLib,
+ StrapiUploadAdapter,
+ ],
+ toolbar: [
+ 'heading',
+ '|',
+ 'bold',
+ 'italic',
+ 'link',
+ 'bulletedList',
+ 'numberedList',
+ '|',
+ 'strapiMediaLib',
+ '|',
+ 'undo',
+ 'redo',
+ ],
+ },
+};
-2. Import the plugin:
+const myConfig = {
+ /**
+ * Note: Since the provided `presets` include only `myCustomPreset`
+ * this configuration will overwrite default presets.
+ */
+ presets: [myCustomPreset],
+};
-```js
-// your-app/src/admin/app.js
+export default {
+ register() {
+ setPluginConfig(myConfig);
+ },
+};
+```
-import './timestamp.js'
+
-const config = {};
+
+ Adding a new preset [TS]
+
+```ts
+// src/admin/app.tsx
+
+import {
+ Bold,
+ Italic,
+ Essentials,
+ Heading,
+ Image,
+ ImageCaption,
+ ImageStyle,
+ ImageToolbar,
+ ImageUpload,
+ Link,
+ List,
+ Paragraph,
+} from 'ckeditor5';
+
+import {
+ type PluginConfig,
+ type Preset,
+ setPluginConfig,
+ StrapiMediaLib,
+ StrapiUploadAdapter,
+} from '@_sh/strapi-plugin-ckeditor';
+
+const myCustomPreset: Preset = {
+ name: 'myCustomPreset',
+ description: 'myCustomPreset description',
+ editorConfig: {
+ licenseKey: 'GPL',
+ plugins: [
+ Bold,
+ Italic,
+ Essentials,
+ Heading,
+ Image,
+ ImageCaption,
+ ImageStyle,
+ ImageToolbar,
+ ImageUpload,
+ Link,
+ List,
+ Paragraph,
+ StrapiMediaLib,
+ StrapiUploadAdapter,
+ ],
+ toolbar: [
+ 'heading',
+ '|',
+ 'bold',
+ 'italic',
+ 'link',
+ 'bulletedList',
+ 'numberedList',
+ '|',
+ 'strapiMediaLib',
+ '|',
+ 'undo',
+ 'redo',
+ ],
+ },
+};
-const bootstrap = (app) => {};
+const myConfig: PluginConfig = {
+ /**
+ * Note that since provided `presets` includes only `myCustomPreset`
+ * this configuration will overwrite default presets.
+ */
+ presets: [myCustomPreset],
+};
export default {
- config,
- bootstrap,
+ register() {
+ setPluginConfig(myConfig);
+ },
};
-
```
-3. Add the new plugin to your preset:
+
- ckeditor.js
+ Default presets modification [TS]
+
+```ts
+// src/admin/app.tsx
+
+import { css } from 'styled-components';
+
+import {
+ type PluginConfig,
+ type Preset,
+ setPluginConfig,
+ defaultHtmlPreset,
+ defaultMarkdownPreset,
+} from '@_sh/strapi-plugin-ckeditor';
+
+const defaultHtml: Preset = {
+ ...defaultHtmlPreset,
+ description: 'Modified default HTML editor',
+ styles: `
+ .ck {
+ color: red;
+ }
+ `,
+ editorConfig: {
+ ...defaultHtmlPreset.editorConfig,
+ placeholder: 'Modified default HTML editor',
+ toolbar: [
+ 'heading',
+ '|',
+ 'bold',
+ 'italic',
+ 'link',
+ 'bulletedList',
+ 'numberedList',
+ '|',
+ 'strapiMediaLib',
+ 'insertTable',
+ '|',
+ 'undo',
+ 'redo',
+ ],
+ },
+};
-```js
-// your-app/config/ckeditor.js
-
-const CKEConfig = () => ({
- presets: {
- myCustomPreset:{
- field: {
- key: "myCustomPreset",
- value: "myCustomPreset",
- metadatas: {
- intlLabel: {
- id: "ckeditor5.preset.myCustomPreset.label",
- defaultMessage: "My custom preset",
- },
- },
- },
- editorConfig:{
- plugins: [
- globalThis.Timestamp,
- globalThis.SH_CKE.Autoformat,
- globalThis.SH_CKE.Bold,
- globalThis.SH_CKE.Italic,
- globalThis.SH_CKE.Essentials,
- globalThis.SH_CKE.Heading,
- globalThis.SH_CKE.Image,
- globalThis.SH_CKE.ImageCaption,
- globalThis.SH_CKE.ImageStyle,
- globalThis.SH_CKE.ImageToolbar,
- globalThis.SH_CKE.ImageUpload,
- globalThis.SH_CKE.Indent,
- globalThis.SH_CKE.Link,
- globalThis.SH_CKE.List,
- globalThis.SH_CKE.Paragraph,
- globalThis.SH_CKE.PasteFromOffice,
- globalThis.SH_CKE.Table,
- globalThis.SH_CKE.TableToolbar,
- globalThis.SH_CKE.TableColumnResize,
- globalThis.SH_CKE.TableCaption,
- globalThis.SH_CKE.StrapiMediaLib,
- globalThis.SH_CKE.StrapiUploadAdapter,
- ],
- toolbar: [
- 'timestamp',
- 'heading',
- '|',
- 'bold', 'italic', 'link', 'bulletedList', 'numberedList',
- '|',
- 'strapiMediaLib', 'insertTable',
- '|',
- 'undo', 'redo'
- ],
-
- // ...
- }
- },
- }
-})
+const defaultMarkdown: Preset = {
+ ...defaultMarkdownPreset,
+ description: 'Modified default Markdown editor',
+ styles: `
+ .ck {
+ --ck-editor-max-width: 1500px;
+ --ck-editor-min-height: 700px;
+ --ck-editor-max-height: 700px;
+ }
+
+ .ck.ck-editor__main {
+ border: 3px dashed ${({ theme }) => theme.colors.warning500};
+ }
+ `,
+};
+
+const myConfig: PluginConfig = {
+ presets: [defaultHtml, defaultMarkdown],
+};
+
+export default {
+ register() {
+ setPluginConfig(myConfig);
+ },
+};
```
-
+
+
+ Modifying theme [TS]
+
+```ts
+// src/admin/app.tsx
+
+import { css } from 'styled-components';
+
+import { type PluginConfig, setPluginConfig, defaultTheme } from '@_sh/strapi-plugin-ckeditor';
+
+const myConfig: PluginConfig = {
+ theme: {
+ ...defaultTheme,
+ additional: css`
+ .ck {
+ --ck-editor-max-width: 1500px;
+ --ck-editor-min-height: 700px;
+ --ck-editor-max-height: 700px;
+ }
+
+ .ck.ck-editor__main {
+ border: 3px dashed ${({ theme }) => theme.colors.warning500};
+ }
+ `,
+ },
+};
-4. Then rebuild the application:
-```bash
-yarn build
+export default {
+ register() {
+ setPluginConfig(myConfig);
+ },
+};
```
-**💡 Alternatively, you can define your plugin like this:**
+
- ckeditor.js
+ Adding Timestamp plugin [JS]
-```js
-const CKEConfig = () => {
- class Timestamp extends globalThis.SH_CKE.Plugin {
- init() {
- const editor = this.editor;
- // The button must be registered among the UI components of the editor
- // to be displayed in the toolbar.
- editor.ui.componentFactory.add("timestamp", () => {
- // The button will be an instance of ButtonView.
- const button = new globalThis.SH_CKE.ButtonView();
-
- button.set({
- label: "Timestamp",
- withText: true,
- });
+```ts
+// src/admin/app.js
- // Execute a callback function when the button is clicked.
- button.on("execute", () => {
- const now = new Date();
+import { Plugin, ButtonView } from 'ckeditor5';
- // Change the model using the model writer.
- editor.model.change((writer) => {
- // Insert the text at the user's current position.
- editor.model.insertContent(writer.createText(now.toString()));
- });
- });
+import { setPluginConfig, defaultHtmlPreset } from '@_sh/strapi-plugin-ckeditor';
- return button;
+class Timestamp extends Plugin {
+ init() {
+ const editor = this.editor;
+ editor.ui.componentFactory.add('timestamp', () => {
+ const button = new ButtonView();
+ button.set({
+ label: 'timestamp',
+ withText: true,
});
- }
- }
-
- return {
- presets: {
- myCustomPreset:{
- field: {
- key: "myCustomPreset",
- value: "myCustomPreset",
- metadatas: {
- intlLabel: {
- id: "ckeditor5.preset.myCustomPreset.label",
- defaultMessage: "My custom preset",
- },
- },
- },
- editorConfig:{
- plugins: [
- Timestamp,
- globalThis.SH_CKE.Autoformat,
- globalThis.SH_CKE.Bold,
- globalThis.SH_CKE.Italic,
- globalThis.SH_CKE.Essentials,
- globalThis.SH_CKE.Heading,
- globalThis.SH_CKE.Image,
- globalThis.SH_CKE.ImageCaption,
- globalThis.SH_CKE.ImageStyle,
- globalThis.SH_CKE.ImageToolbar,
- globalThis.SH_CKE.ImageUpload,
- globalThis.SH_CKE.Indent,
- globalThis.SH_CKE.Link,
- globalThis.SH_CKE.List,
- globalThis.SH_CKE.Paragraph,
- globalThis.SH_CKE.PasteFromOffice,
- globalThis.SH_CKE.Table,
- globalThis.SH_CKE.TableToolbar,
- globalThis.SH_CKE.TableColumnResize,
- globalThis.SH_CKE.TableCaption,
- globalThis.SH_CKE.StrapiMediaLib,
- globalThis.SH_CKE.StrapiUploadAdapter,
- ],
- toolbar: [
- 'timestamp',
- 'heading',
- '|',
- 'bold', 'italic', 'link', 'bulletedList', 'numberedList',
- '|',
- 'strapiMediaLib', 'insertTable',
- '|',
- 'undo', 'redo'
- ],
-
- ...
- }
- },
+ button.on('execute', () => {
+ const now = new Date();
+ editor.model.change(writer => {
+ editor.model.insertContent(writer.createText(now.toString()));
+ });
+ });
+ return button;
+ });
}
}
+
+export default {
+ register() {
+ defaultHtmlPreset.editorConfig.plugins.push(Timestamp);
+ defaultHtmlPreset.editorConfig.toolbar.unshift('timestamp');
+ setPluginConfig({ presets: [defaultHtmlPreset] });
+ },
+};
```
-
+
+
+📂 Default HTML preset: [**admin/src/config/htmlPreset.js**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/admin/src/config/htmlPreset.js)
-## 🛠 Contributing
+📂 Default Markdown preset: [**admin/src/config/markdownPreset.js**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/admin/src/config/markdownPreset.js)
+📂 Default theme: [**admin/src/theme**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/admin/src/theme)
-This section explains how to set up your environment if you want to contribute to this package.
+> 📌 It is highly recommended to explore [**the official CKEditor5 documentation**](https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html).
-[Strapi Plugin SDK docs](https://docs.strapi.io/dev-docs/plugins/development/create-a-plugin#linking-the-plugin-to-your-project)
+> 💡 To display content from external sources, such as images or videos, in your admin panel,
+> you need to configure your `middlewares.js` file.
+> [**Check the documentation for details.**](https://docs.strapi.io/dev-docs/configurations/middlewares#security)
-Clone the repository:
+## 🛠 Contributing
-```bash
-git clone https://github.com/nshenderov/strapi-plugin-ckeditor.git ./strapi-plugin-ckeditor
-```
-Navigate to the downloaded folder:
+Feel free to [fork the repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo)
+and open a pull request, any help is appreciated.
-```bash
-cd ./strapi-plugin-ckeditor
-```
+Follow these steps to set up a plugin development environment:
+
+1. Clone the repository.
+
+2. Read [the Strapi Plugin SDK documentation](https://docs.strapi.io/dev-docs/plugins/development/create-a-plugin#linking-the-plugin-to-your-project).
-Install dependencies:
+3. Navigate to the cloned plugin folder and install dependencies, run:
```bash
yarn install
```
-Link the plugin to your project:
+4. Link the plugin to your project:
-In the plugin directory, run:
-```bash
-yarn watch:link
-```
+ - In the plugin folder, run:
-Open new terminal window and in your project directory, run:
-```bash
-yarn dlx yalc add --link @_sh/strapi-plugin-ckeditor && yarn install
-```
+ ```bash
+ yarn watch:link
+ ```
+
+ - Open a new terminal, navigate to your Strapi project directory, and run:
-Rebuild the project and start the server:
+ ```bash
+ yarn dlx yalc add --link @_sh/strapi-plugin-ckeditor && yarn install
+ ```
+
+5. Rebuild the project and start the server:
```bash
yarn build
yarn develop
```
-
## ✈️ Migration
-### From v3 to v4
-
-- The new version introduces support for Strapi v5 and is incompatible with Strapi v4. You will need to update your Strapi project to version 5 before upgrading.
-
-- The plugin development process has changed. Please refer to the updated contribution guide for more information.
-
-### From v2 to v3
-
-- The default editor configurations (toolbar, toolbarBalloon, blockBalloon) have been removed and now there is only one preset by default. You will need to update your fields in the Content-Type Builder.
-
-- Config file extension has changed from `.txt` to `.js` or `.ts`
-- Configuration object properties have been renamed:
- - `configsOverwrite` -> `dontMergePresets`
- - `themeOverwrite` -> `dontMergeTheme`
- - `configs` -> `presets`
-- From v3 instead of globalThis.CKEditorConfig = {..}, the config file must define a function called CKEConfig that returns the configuration object.
-
-Example of the new configuration file:
-
-
- ckeditor.js
-
-```js
-const CKEConfig = () => ({
- presets:{
- myCustomPreset:{
- field: {
- key: "myCustomPreset",
- value: "myCustomPreset",
- metadatas: {
- intlLabel: {
- id: "ckeditor5.preset.myCustomPreset.label",
- defaultMessage: "My custom preset",
- },
- },
- },
- editorConfig:{
- plugins: [
- globalThis.SH_CKE.Autoformat,
- globalThis.SH_CKE.Bold,
- globalThis.SH_CKE.Italic,
- globalThis.SH_CKE.Essentials,
- globalThis.SH_CKE.Heading,
- globalThis.SH_CKE.Image,
- globalThis.SH_CKE.ImageCaption,
- globalThis.SH_CKE.ImageStyle,
- globalThis.SH_CKE.ImageToolbar,
- globalThis.SH_CKE.ImageUpload,
- globalThis.SH_CKE.Indent,
- globalThis.SH_CKE.Link,
- globalThis.SH_CKE.List,
- globalThis.SH_CKE.Paragraph,
- globalThis.SH_CKE.PasteFromOffice,
- globalThis.SH_CKE.Table,
- globalThis.SH_CKE.TableToolbar,
- globalThis.SH_CKE.TableColumnResize,
- globalThis.SH_CKE.TableCaption,
- globalThis.SH_CKE.StrapiMediaLib,
- globalThis.SH_CKE.StrapiUploadAdapter,
- ],
- toolbar: [
- 'heading',
- '|',
- 'bold', 'italic', 'link', 'bulletedList', 'numberedList',
- '|',
- 'strapiMediaLib', 'insertTable',
- '|',
- 'undo', 'redo'
- ],
- heading: {
- options: [
- { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
- { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
- { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
- { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
- { model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
- ]
- },
- image: {
- toolbar: [
- 'imageStyle:inline',
- 'imageStyle:block',
- 'imageStyle:side',
- '|',
- 'toggleImageCaption',
- 'imageTextAlternative'
- ]
- },
- table: {
- contentToolbar: [
- 'tableColumn',
- 'tableRow',
- 'mergeTableCells',
- '|',
- 'toggleTableCaption'
- ]
- }
- }
- }
- }
-})
-```
-
-
-
-### From v1 to v2
-
-- You will need to update Strapi to version v4.4.x for plugin v2.0.x, or to v4.13.0+ for v2.1.x.
-
-- Starting with v2, the plugin uses the Custom Field API, so you'll need to manually update your schema.
-
-- The plugin configuration should be defined in /config/ckeditor.txt from v2 onward. [Please refer to the v2 configuration guide for details.](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/e782475f54b8a50a04f55275c89ef5bf61a15745/README.md?plain=1#L54)
+To upgrade to the latest version, follow the dedicated [migration guide](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/MIGRATION.md)
+for detailed instructions.
## ⚠️ Requirements
-
-**v4.x.x**
+**v5.x.x**
Strapi **>= 5.0.0**
-Node **>= 18.0.0 <= 20.x.x**
+Node **>= 18.0.0 <= 22.x.x**
-___
+---
**v3.x.x**
Strapi **>= 4.13.0 < 5.0.0**
-Node **>= 18.0.0 <= 20.x.x**
\ No newline at end of file
+Node **>= 18.0.0 <= 20.x.x**
diff --git a/assets/v5-def-html-dark.png b/assets/v5-def-html-dark.png
new file mode 100644
index 0000000..b4317dc
Binary files /dev/null and b/assets/v5-def-html-dark.png differ
diff --git a/assets/v5-def-html-light-full.png b/assets/v5-def-html-light-full.png
new file mode 100644
index 0000000..675eb42
Binary files /dev/null and b/assets/v5-def-html-light-full.png differ
diff --git a/assets/v5-def-html-light.png b/assets/v5-def-html-light.png
new file mode 100644
index 0000000..bfea5f6
Binary files /dev/null and b/assets/v5-def-html-light.png differ
diff --git a/assets/v5-def-mrk-dark.png b/assets/v5-def-mrk-dark.png
new file mode 100644
index 0000000..9cb33e8
Binary files /dev/null and b/assets/v5-def-mrk-dark.png differ
diff --git a/assets/v5-def-mrk-light.png b/assets/v5-def-mrk-light.png
new file mode 100644
index 0000000..dd5b966
Binary files /dev/null and b/assets/v5-def-mrk-light.png differ
diff --git a/assets/v5-head.png b/assets/v5-head.png
new file mode 100644
index 0000000..d877857
Binary files /dev/null and b/assets/v5-head.png differ
diff --git a/assets/v5-usage-guide-1.png b/assets/v5-usage-guide-1.png
new file mode 100644
index 0000000..8a98277
Binary files /dev/null and b/assets/v5-usage-guide-1.png differ
diff --git a/assets/v5-usage-guide-2.png b/assets/v5-usage-guide-2.png
new file mode 100644
index 0000000..c8b60e5
Binary files /dev/null and b/assets/v5-usage-guide-2.png differ
diff --git a/assets/v5-usage-guide-3.png b/assets/v5-usage-guide-3.png
new file mode 100644
index 0000000..2177ac3
Binary files /dev/null and b/assets/v5-usage-guide-3.png differ