diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/CHANGELOG.md b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/CHANGELOG.md
new file mode 100644
index 000000000..01711a3f7
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/CHANGELOG.md
@@ -0,0 +1,4 @@
+Changelog
+=========
+
+All changes in the package are documented in https://github.com/ckeditor/ckeditor5/blob/master/CHANGELOG.md.
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/CONTRIBUTING.md b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/CONTRIBUTING.md
new file mode 100644
index 000000000..ae3ecb8ff
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/CONTRIBUTING.md
@@ -0,0 +1,4 @@
+Contributing
+========================================
+
+See the [official contributors' guide to CKEditor 5](https://ckeditor.com/docs/ckeditor5/latest/framework/contributing/contributing.html) to learn more.
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/LICENSE.md b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/LICENSE.md
new file mode 100644
index 000000000..106d679d4
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/LICENSE.md
@@ -0,0 +1,17 @@
+Software License Agreement
+==========================
+
+**CKEditor 5 block quote feature** – https://github.com/ckeditor/ckeditor5-block-quote
+Copyright (c) 2003–2024, [CKSource Holding sp. z o.o.](https://cksource.com) All rights reserved.
+
+Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
+
+Sources of Intellectual Property Included in CKEditor
+-----------------------------------------------------
+
+Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission.
+
+Trademarks
+----------
+
+**CKEditor** is a trademark of [CKSource Holding sp. z o.o.](https://cksource.com) All other brand and product names are trademarks, registered trademarks, or service marks of their respective holders.
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/README.md b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/README.md
new file mode 100644
index 000000000..71656077a
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/README.md
@@ -0,0 +1,26 @@
+CKEditor 5 block quote feature
+========================================
+
+[](https://www.npmjs.com/package/@ckeditor/ckeditor5-block-quote)
+[](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
+[](https://app.travis-ci.com/github/ckeditor/ckeditor5)
+
+This package implements block quote support for CKEditor 5.
+
+## Demo
+
+Check out the [demo in the block quote feature guide](https://ckeditor.com/docs/ckeditor5/latest/features/block-quote.html#demo).
+
+## Documentation
+
+See the [`@ckeditor/ckeditor5-block-quote` package](https://ckeditor.com/docs/ckeditor5/latest/api/block-quote.html) page in [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/).
+
+## Installation
+
+```bash
+npm install ckeditor5
+```
+
+## License
+
+Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license).
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/ckeditor5-metadata.json b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/ckeditor5-metadata.json
new file mode 100644
index 000000000..3218dff50
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/ckeditor5-metadata.json
@@ -0,0 +1,23 @@
+{
+ "plugins": [
+ {
+ "name": "Admonitions",
+ "className": "Admonition",
+ "description": "Implements admonitions (warning, info boxes) in a similar fashion to blockquotes",
+ "docs": "features/block-quote.html",
+ "path": "src/admonition.js",
+ "uiComponents": [
+ {
+ "type": "Button",
+ "name": "admonition",
+ "iconPath": "theme/icons/admonition.svg"
+ }
+ ],
+ "htmlOutput": [
+ {
+ "elements": "aside"
+ }
+ ]
+ }
+ ]
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/lang/contexts.json b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/lang/contexts.json
new file mode 100644
index 000000000..421249ff6
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/lang/contexts.json
@@ -0,0 +1,3 @@
+{
+ "Admonition": "Toolbar button tooltip for the Admonition feature."
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/lang/translations/en.po b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/lang/translations/en.po
new file mode 100644
index 000000000..b5032283a
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/lang/translations/en.po
@@ -0,0 +1,22 @@
+# Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+#
+# !!! IMPORTANT !!!
+#
+# Before you edit this file, please keep in mind that contributing to the project
+# translations is possible ONLY via the Transifex online service.
+#
+# To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+# To learn more, check out the official contributor's guide:
+# https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language: \n"
+"Language-Team: \n"
+"Plural-Forms: \n"
+"Content-Type: text/plain; charset=UTF-8\n"
+
+msgctxt "Toolbar button tooltip for the Admoniton feature."
+msgid "Admonition"
+msgstr "Admonition"
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/package.json b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/package.json
new file mode 100644
index 000000000..e93c8fdfc
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "@ckeditor/ckeditor5-admonition",
+ "version": "43.2.0",
+ "description": "Admonition (info box, warning box) feature for CKEditor 5.",
+ "keywords": [
+ "ckeditor",
+ "ckeditor5",
+ "ckeditor 5",
+ "ckeditor5-feature",
+ "ckeditor5-plugin",
+ "ckeditor5-dll"
+ ],
+ "type": "module",
+ "main": "src/index.ts",
+ "dependencies": {
+ "@ckeditor/ckeditor5-core": "43.2.0",
+ "@ckeditor/ckeditor5-enter": "43.2.0",
+ "@ckeditor/ckeditor5-typing": "43.2.0",
+ "@ckeditor/ckeditor5-ui": "43.2.0",
+ "@ckeditor/ckeditor5-utils": "43.2.0",
+ "ckeditor5": "43.2.0"
+ },
+ "devDependencies": {
+ "@ckeditor/ckeditor5-basic-styles": "43.2.0",
+ "@ckeditor/ckeditor5-dev-utils": "^43.0.0",
+ "@ckeditor/ckeditor5-editor-classic": "43.2.0",
+ "@ckeditor/ckeditor5-engine": "43.2.0",
+ "@ckeditor/ckeditor5-heading": "43.2.0",
+ "@ckeditor/ckeditor5-image": "43.2.0",
+ "@ckeditor/ckeditor5-list": "43.2.0",
+ "@ckeditor/ckeditor5-paragraph": "43.2.0",
+ "@ckeditor/ckeditor5-table": "43.2.0",
+ "@ckeditor/ckeditor5-theme-lark": "43.2.0",
+ "typescript": "5.0.4",
+ "webpack": "^5.94.0",
+ "webpack-cli": "^5.1.4"
+ },
+ "author": "CKSource (http://cksource.com/)",
+ "license": "GPL-2.0-or-later",
+ "homepage": "https://ckeditor.com/ckeditor-5",
+ "bugs": "https://github.com/ckeditor/ckeditor5/issues",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ckeditor/ckeditor5.git",
+ "directory": "packages/ckeditor5-admonition"
+ },
+ "files": [
+ "dist",
+ "lang",
+ "src/**/*.js",
+ "src/**/*.d.ts",
+ "theme",
+ "build",
+ "ckeditor5-metadata.json",
+ "CHANGELOG.md"
+ ],
+ "scripts": {
+ "dll:build": "webpack",
+ "build": "tsc -p ./tsconfig.json",
+ "build:dist": "node ../../scripts/build-package.mjs"
+ }
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonition.ts b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonition.ts
new file mode 100644
index 000000000..b09dfb78d
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonition.ts
@@ -0,0 +1,40 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module admonition/admonition
+ */
+
+import { Plugin } from 'ckeditor5/src/core.js';
+
+import AdmonitionEditing from './admonitionediting.js';
+import AdmonitionUI from './admonitionui.js';
+import AdmonitionAutoformat from './admonitionautoformat.js';
+
+/**
+ * The block quote plugin.
+ *
+ * For more information about this feature check the {@glink api/block-quote package page}.
+ *
+ * This is a "glue" plugin which loads the {@link module:block-quote/blockquoteediting~BlockQuoteEditing block quote editing feature}
+ * and {@link module:block-quote/blockquoteui~BlockQuoteUI block quote UI feature}.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class Admonition extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ public static get requires() {
+ return [ AdmonitionEditing, AdmonitionUI, AdmonitionAutoformat ] as const;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static get pluginName() {
+ return 'Admonition' as const;
+ }
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitionautoformat.ts b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitionautoformat.ts
new file mode 100644
index 000000000..ac104a845
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitionautoformat.ts
@@ -0,0 +1,42 @@
+import Plugin from "@ckeditor/ckeditor5-core/src/plugin";
+import Autoformat from "@ckeditor/ckeditor5-autoformat/src/autoformat";
+import blockAutoformatEditing from "@ckeditor/ckeditor5-autoformat/src/blockautoformatediting";
+import { AdmonitionType, ADMONITION_TYPES } from "./admonitioncommand";
+
+function tryParseAdmonitionType(match: RegExpMatchArray) {
+ if (match.length !== 2) {
+ return;
+ }
+
+ if ((ADMONITION_TYPES as readonly string[]).includes(match[1])) {
+ return match[1] as AdmonitionType;
+ }
+}
+
+export default class AdmonitionAutoformat extends Plugin {
+ static get requires() {
+ return [ Autoformat ];
+ }
+
+ afterInit() {
+ if (!this.editor.commands.get("admonition")) {
+ return;
+ }
+
+ const instance = (this as any);
+ blockAutoformatEditing(this.editor, instance, /^\!\!\[*\! (.+) $/, ({ match }) => {
+ const type = tryParseAdmonitionType(match);
+
+ if (type) {
+ // User has entered the admonition type, so we insert as-is.
+ this.editor.execute("admonition", { forceValue: type });
+ } else {
+ // User has not entered a valid type, assume it's part of the text of the admonition.
+ this.editor.execute("admonition");
+ if (match.length > 1) {
+ this.editor.execute("insertText", { text: (match[1] ?? "") + " " });
+ }
+ }
+ });
+ }
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitioncommand.ts b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitioncommand.ts
new file mode 100644
index 000000000..c9a6cae49
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitioncommand.ts
@@ -0,0 +1,276 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module admonition/admonitioncommand
+ */
+
+import { Command } from 'ckeditor5/src/core.js';
+import { first } from 'ckeditor5/src/utils.js';
+import type { DocumentFragment, Element, Position, Range, Schema, Writer } from 'ckeditor5/src/engine.js';
+
+/**
+ * The block quote command plugin.
+ *
+ * @extends module:core/command~Command
+ */
+
+export const ADMONITION_TYPES = [ "note", "tip", "important", "caution", "warning" ] as const;
+export const ADMONITION_TYPE_ATTRIBUTE = "admonitionType";
+export const DEFAULT_ADMONITION_TYPE = ADMONITION_TYPES[0];
+export type AdmonitionType = typeof ADMONITION_TYPES[number];
+
+interface ExecuteOpts {
+ /**
+ * If set, it will force the command behavior. If `true`, the command will apply a block quote,
+ * otherwise the command will remove the block quote. If not set, the command will act basing on its current value.
+ */
+ forceValue?: AdmonitionType;
+ /**
+ * If set to true and `forceValue` is not specified, the command will apply the previous admonition type (if the command was already executed).
+ */
+ usePreviousChoice?: boolean
+}
+
+export default class AdmonitionCommand extends Command {
+ /**
+ * Whether the selection starts in a block quote.
+ *
+ * @observable
+ * @readonly
+ */
+ declare public value: AdmonitionType | false;
+
+ private _lastType?: AdmonitionType;
+
+ /**
+ * @inheritDoc
+ */
+ public override refresh(): void {
+ this.value = this._getValue();
+ this.isEnabled = this._checkEnabled();
+ }
+
+ /**
+ * Executes the command. When the command {@link #value is on}, all top-most block quotes within
+ * the selection will be removed. If it is off, all selected blocks will be wrapped with
+ * a block quote.
+ *
+ * @fires execute
+ * @param options Command options.
+ */
+ public override execute( options: ExecuteOpts = {} ): void {
+ const model = this.editor.model;
+ const schema = model.schema;
+ const selection = model.document.selection;
+
+ const blocks = Array.from( selection.getSelectedBlocks() );
+
+ const value = this._getType(options);
+
+ model.change( writer => {
+ if ( !value ) {
+ this._removeQuote( writer, blocks.filter( findQuote ) );
+ } else {
+ const blocksToQuote = blocks.filter( block => {
+ // Already quoted blocks needs to be considered while quoting too
+ // in order to reuse their elements.
+ return findQuote( block ) || checkCanBeQuoted( schema, block );
+ } );
+
+ this._applyQuote( writer, blocksToQuote, value);
+ }
+ } );
+ }
+
+ private _getType(options: ExecuteOpts): AdmonitionType | false {
+ const value = (options.forceValue === undefined) ? !this.value : options.forceValue;
+
+ // Allow removing the admonition.
+ if (!value) {
+ return false;
+ }
+
+ // Prefer the type from the command, if any.
+ if (typeof value === "string") {
+ return value;
+ }
+
+ // See if we can restore the previous language.
+ if (options.usePreviousChoice && this._lastType) {
+ return this._lastType;
+ }
+
+ // Otherwise return a default.
+ return "note";
+ }
+
+ /**
+ * Checks the command's {@link #value}.
+ */
+ private _getValue(): AdmonitionType | false {
+ const selection = this.editor.model.document.selection;
+ const firstBlock = first( selection.getSelectedBlocks() );
+ if (!firstBlock) {
+ return false;
+ }
+
+ // In the current implementation, the admonition must be an immediate parent of a block element.
+ const firstQuote = findQuote( firstBlock );
+ if (firstQuote?.is("element")) {
+ return firstQuote.getAttribute(ADMONITION_TYPE_ATTRIBUTE) as AdmonitionType;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the command can be enabled in the current context.
+ *
+ * @returns Whether the command should be enabled.
+ */
+ private _checkEnabled(): boolean {
+ if ( this.value ) {
+ return true;
+ }
+
+ const selection = this.editor.model.document.selection;
+ const schema = this.editor.model.schema;
+
+ const firstBlock = first( selection.getSelectedBlocks() );
+
+ if ( !firstBlock ) {
+ return false;
+ }
+
+ return checkCanBeQuoted( schema, firstBlock );
+ }
+
+ /**
+ * Removes the quote from given blocks.
+ *
+ * If blocks which are supposed to be "unquoted" are in the middle of a quote,
+ * start it or end it, then the quote will be split (if needed) and the blocks
+ * will be moved out of it, so other quoted blocks remained quoted.
+ */
+ private _removeQuote( writer: Writer, blocks: Array ): void {
+ // Unquote all groups of block. Iterate in the reverse order to not break following ranges.
+ getRangesOfBlockGroups( writer, blocks ).reverse().forEach( groupRange => {
+ if ( groupRange.start.isAtStart && groupRange.end.isAtEnd ) {
+ writer.unwrap( groupRange.start.parent as Element );
+
+ return;
+ }
+
+ // The group of blocks are at the beginning of an so let's move them left (out of the ).
+ if ( groupRange.start.isAtStart ) {
+ const positionBefore = writer.createPositionBefore( groupRange.start.parent as Element );
+
+ writer.move( groupRange, positionBefore );
+
+ return;
+ }
+
+ // The blocks are in the middle of an so we need to split the after the last block
+ // so we move the items there.
+ if ( !groupRange.end.isAtEnd ) {
+ writer.split( groupRange.end );
+ }
+
+ // Now we are sure that groupRange.end.isAtEnd is true, so let's move the blocks right.
+
+ const positionAfter = writer.createPositionAfter( groupRange.end.parent as Element );
+
+ writer.move( groupRange, positionAfter );
+ } );
+ }
+
+ /**
+ * Applies the quote to given blocks.
+ */
+ private _applyQuote( writer: Writer, blocks: Array, type?: AdmonitionType): void {
+ this._lastType = type;
+ const quotesToMerge: Array = [];
+
+ // Quote all groups of block. Iterate in the reverse order to not break following ranges.
+ getRangesOfBlockGroups( writer, blocks ).reverse().forEach( groupRange => {
+ let quote = findQuote( groupRange.start );
+
+ if ( !quote ) {
+ const attributes: Record = {};
+ attributes[ADMONITION_TYPE_ATTRIBUTE] = type;
+ quote = writer.createElement( 'aside', attributes);
+
+ writer.wrap( groupRange, quote );
+ } else if (quote.is("element")) {
+ this.editor.model.change((writer) => {
+ writer.setAttribute(ADMONITION_TYPE_ATTRIBUTE, type, quote as Element);
+ });
+ }
+
+ quotesToMerge.push( quote );
+ } );
+
+ // Merge subsequent elements. Reverse the order again because this time we want to go through
+ // the elements in the source order (due to how merge works – it moves the right element's content
+ // to the first element and removes the right one. Since we may need to merge a couple of subsequent `` elements
+ // we want to keep the reference to the first (furthest left) one.
+ quotesToMerge.reverse().reduce( ( currentQuote, nextQuote ) => {
+ if ( currentQuote.nextSibling == nextQuote ) {
+ writer.merge( writer.createPositionAfter( currentQuote ) );
+
+ return currentQuote;
+ }
+
+ return nextQuote;
+ } );
+ }
+}
+
+function findQuote( elementOrPosition: Element | Position ): Element | DocumentFragment | null {
+ return elementOrPosition.parent!.name == 'aside' ? elementOrPosition.parent : null;
+}
+
+/**
+ * Returns a minimal array of ranges containing groups of subsequent blocks.
+ *
+ * content: abcdefgh
+ * blocks: [ a, b, d, f, g, h ]
+ * output ranges: [ab]c[d]e[fgh]
+ */
+function getRangesOfBlockGroups( writer: Writer, blocks: Array ): Array {
+ let startPosition;
+ let i = 0;
+ const ranges = [];
+
+ while ( i < blocks.length ) {
+ const block = blocks[ i ];
+ const nextBlock = blocks[ i + 1 ];
+
+ if ( !startPosition ) {
+ startPosition = writer.createPositionBefore( block );
+ }
+
+ if ( !nextBlock || block.nextSibling != nextBlock ) {
+ ranges.push( writer.createRange( startPosition, writer.createPositionAfter( block ) ) );
+ startPosition = null;
+ }
+
+ i++;
+ }
+
+ return ranges;
+}
+
+/**
+ * Checks whether can wrap the block.
+ */
+function checkCanBeQuoted( schema: Schema, block: Element ): boolean {
+ // TMP will be replaced with schema.checkWrap().
+ const isBQAllowed = schema.checkChild( block.parent as Element, 'aside' );
+ const isBlockAllowedInBQ = schema.checkChild( [ '$root', 'aside' ], block );
+
+ return isBQAllowed && isBlockAllowedInBQ;
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitionediting.ts b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitionediting.ts
new file mode 100644
index 000000000..6af25b965
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitionediting.ts
@@ -0,0 +1,177 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module admonition/admonitionediting
+ */
+
+import { Plugin } from 'ckeditor5/src/core.js';
+import { Enter, type ViewDocumentEnterEvent } from 'ckeditor5/src/enter.js';
+import { Delete, type ViewDocumentDeleteEvent } from 'ckeditor5/src/typing.js';
+
+import AdmonitionCommand, { AdmonitionType, ADMONITION_TYPES, DEFAULT_ADMONITION_TYPE, ADMONITION_TYPE_ATTRIBUTE } from './admonitioncommand.js';
+
+/**
+ * The block quote editing.
+ *
+ * Introduces the `'admonition'` command and the `'aside'` model element.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class AdmonitionEditing extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ public static get pluginName() {
+ return 'AdmonitionEditing' as const;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static get requires() {
+ return [ Enter, Delete ] as const;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public init(): void {
+ const editor = this.editor;
+ const schema = editor.model.schema;
+
+ editor.commands.add( 'admonition', new AdmonitionCommand( editor ) );
+
+ schema.register( 'aside', {
+ inheritAllFrom: '$container',
+ allowAttributes: ADMONITION_TYPE_ATTRIBUTE
+ } );
+
+ editor.conversion.for("upcast").elementToElement({
+ view: {
+ name: "aside",
+ classes: "admonition",
+ },
+ model: (viewElement, { writer }) => {
+ let type: AdmonitionType = DEFAULT_ADMONITION_TYPE;
+ for (const className of viewElement.getClassNames()) {
+ if (className !== "admonition" && (ADMONITION_TYPES as readonly string[]).includes(className)) {
+ type = className as AdmonitionType;
+ }
+ }
+
+ const attributes: Record = {};
+ attributes[ADMONITION_TYPE_ATTRIBUTE] = type;
+ return writer.createElement("aside", attributes);
+ }
+ });
+
+ editor.conversion.for("downcast")
+ .elementToElement( {
+ model: 'aside',
+ view: "aside"
+ })
+ .attributeToAttribute({
+ model: ADMONITION_TYPE_ATTRIBUTE,
+ view: (value) => ({
+ key: "class",
+ value: [ "admonition", value as string ]
+ })
+ });
+
+ // Postfixer which cleans incorrect model states connected with block quotes.
+ editor.model.document.registerPostFixer( writer => {
+ const changes = editor.model.document.differ.getChanges();
+
+ for ( const entry of changes ) {
+ if ( entry.type == 'insert' ) {
+ const element = entry.position.nodeAfter;
+
+ if ( !element ) {
+ // We are inside a text node.
+ continue;
+ }
+
+ if ( element.is( 'element', 'aside' ) && element.isEmpty ) {
+ // Added an empty aside - remove it.
+ writer.remove( element );
+
+ return true;
+ } else if ( element.is( 'element', 'aside' ) && !schema.checkChild( entry.position, element ) ) {
+ // Added a aside in incorrect place. Unwrap it so the content inside is not lost.
+ writer.unwrap( element );
+
+ return true;
+ } else if ( element.is( 'element' ) ) {
+ // Just added an element. Check that all children meet the scheme rules.
+ const range = writer.createRangeIn( element );
+
+ for ( const child of range.getItems() ) {
+ if (
+ child.is( 'element', 'aside' ) &&
+ !schema.checkChild( writer.createPositionBefore( child ), child )
+ ) {
+ writer.unwrap( child );
+
+ return true;
+ }
+ }
+ }
+ } else if ( entry.type == 'remove' ) {
+ const parent = entry.position.parent;
+
+ if ( parent.is( 'element', 'aside' ) && parent.isEmpty ) {
+ // Something got removed and now aside is empty. Remove the aside as well.
+ writer.remove( parent );
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+ } );
+
+ const viewDocument = this.editor.editing.view.document;
+ const selection = editor.model.document.selection;
+ const admonitionCommand: AdmonitionCommand = editor.commands.get( 'admonition' )!;
+
+ // Overwrite default Enter key behavior.
+ // If Enter key is pressed with selection collapsed in empty block inside a quote, break the quote.
+ this.listenTo( viewDocument, 'enter', ( evt, data ) => {
+ if ( !selection.isCollapsed || !admonitionCommand.value ) {
+ return;
+ }
+
+ const positionParent = selection.getLastPosition()!.parent;
+
+ if ( positionParent.isEmpty ) {
+ editor.execute( 'admonition' );
+ editor.editing.view.scrollToTheSelection();
+
+ data.preventDefault();
+ evt.stop();
+ }
+ }, { context: 'aside' } );
+
+ // Overwrite default Backspace key behavior.
+ // If Backspace key is pressed with selection collapsed in first empty block inside a quote, break the quote.
+ this.listenTo( viewDocument, 'delete', ( evt, data ) => {
+ if ( data.direction != 'backward' || !selection.isCollapsed || !admonitionCommand!.value ) {
+ return;
+ }
+
+ const positionParent = selection.getLastPosition()!.parent;
+
+ if ( positionParent.isEmpty && !positionParent.previousSibling ) {
+ editor.execute( 'admonition' );
+ editor.editing.view.scrollToTheSelection();
+
+ data.preventDefault();
+ evt.stop();
+ }
+ }, { context: 'aside' } );
+ }
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitionui.ts b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitionui.ts
new file mode 100644
index 000000000..6e5b779fe
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/admonitionui.ts
@@ -0,0 +1,125 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module admonition/admonitionui
+ */
+
+import { Plugin, } from 'ckeditor5/src/core.js';
+import { addListToDropdown, createDropdown, ListDropdownButtonDefinition, SplitButtonView, ViewModel } from 'ckeditor5/src/ui.js';
+
+import '../theme/blockquote.css';
+import admonitionIcon from '../theme/icons/admonition.svg';
+import { Collection } from '@ckeditor/ckeditor5-utils';
+import AdmonitionCommand, { AdmonitionType } from './admonitioncommand';
+
+interface AdmonitionDefinition {
+ title: string;
+}
+
+export const ADMONITION_TYPES: Record = {
+ note: {
+ title: "Note"
+ },
+ tip: {
+ title: "Tip"
+ },
+ important: {
+ title: "Important"
+ },
+ caution: {
+ title: "Caution"
+ },
+ warning: {
+ title: "Warning"
+ }
+};
+
+/**
+ * The block quote UI plugin.
+ *
+ * It introduces the `'admonition'` button.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class AdmonitionUI extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ public static get pluginName() {
+ return 'AdmonitionUI' as const;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public init(): void {
+ const editor = this.editor;
+
+ editor.ui.componentFactory.add( 'admonition', () => {
+ const buttonView = this._createButton();
+
+ return buttonView;
+ } );
+ }
+
+ /**
+ * Creates a button for admonition command to use either in toolbar or in menu bar.
+ */
+ private _createButton() {
+ const editor = this.editor;
+ const locale = editor.locale;
+ const command = editor.commands.get( 'admonition' )!;
+ const dropdownView = createDropdown(locale, SplitButtonView);
+ const splitButtonView = dropdownView.buttonView;
+ const t = locale.t;
+
+ addListToDropdown(dropdownView, this._getDropdownItems())
+
+ // Button configuration.
+ splitButtonView.set( {
+ label: t( 'Admonition' ),
+ icon: admonitionIcon,
+ isToggleable: true,
+ tooltip: true
+ } );
+ splitButtonView.on("execute", () => {
+ editor.execute("admonition", { usePreviousChoice: true });
+ editor.editing.view.focus();
+ });
+ splitButtonView.bind( 'isOn' ).to( command, 'value', value => (!!value) as boolean);
+
+ // Dropdown configuration
+ dropdownView.bind( 'isEnabled' ).to( command, 'isEnabled' );
+ dropdownView.on("execute", evt => {
+ editor.execute("admonition", { forceValue: ( evt.source as any ).commandParam } );
+ editor.editing.view.focus();
+ });
+
+ return dropdownView;
+ }
+
+ private _getDropdownItems() {
+ const itemDefinitions = new Collection();
+ const command = this.editor.commands.get("admonition") as AdmonitionCommand
+
+ for (const [ type, admonition ] of Object.entries(ADMONITION_TYPES)) {
+ const definition: ListDropdownButtonDefinition = {
+ type: "button",
+ model: new ViewModel({
+ commandParam: type,
+ label: admonition.title,
+ role: 'menuitemradio',
+ withText: true
+ })
+ }
+
+ definition.model.bind("isOn").to(command, "value", currentType => currentType === type);
+ itemDefinitions.add(definition);
+ }
+
+ return itemDefinitions;
+ }
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/augmentation.ts b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/augmentation.ts
new file mode 100644
index 000000000..5bf87e70e
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/augmentation.ts
@@ -0,0 +1,23 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import type {
+ Admonition,
+ AdmonitionCommand,
+ AdmonitionEditing,
+ AdmonitionUI
+} from './index.js';
+
+declare module '@ckeditor/ckeditor5-core' {
+ interface PluginsMap {
+ [ Admonition.pluginName ]: Admonition;
+ [ AdmonitionEditing.pluginName ]: AdmonitionEditing;
+ [ AdmonitionUI.pluginName ]: AdmonitionUI;
+ }
+
+ interface CommandsMap {
+ admonition: AdmonitionCommand;
+ }
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/index.ts b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/index.ts
new file mode 100644
index 000000000..073365f5d
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/src/index.ts
@@ -0,0 +1,16 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module admonition
+ */
+
+export { default as Admonition } from './admonition.js';
+export { default as AdmonitionEditing } from './admonitionediting.js';
+export { default as AdmonitionUI } from './admonitionui.js';
+export { default as AdmonitionAutoformat } from './admonitionautoformat.js';
+export type { default as AdmonitionCommand } from './admonitioncommand.js';
+
+import './augmentation.js';
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/theme/blockquote.css b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/theme/blockquote.css
new file mode 100644
index 000000000..e456acb12
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/theme/blockquote.css
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+.ck-content blockquote {
+ /* See #12 */
+ overflow: hidden;
+
+ /* https://github.com/ckeditor/ckeditor5-block-quote/issues/15 */
+ padding-right: 1.5em;
+ padding-left: 1.5em;
+
+ margin-left: 0;
+ margin-right: 0;
+ font-style: italic;
+ border-left: solid 5px hsl(0, 0%, 80%);
+}
+
+.ck-content[dir="rtl"] blockquote {
+ border-left: 0;
+ border-right: solid 5px hsl(0, 0%, 80%);
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/theme/icons/admonition.svg b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/theme/icons/admonition.svg
new file mode 100644
index 000000000..0f497f8c6
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/theme/icons/admonition.svg
@@ -0,0 +1,52 @@
+
+
+
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/tsconfig.dist.json b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/tsconfig.dist.json
new file mode 100644
index 000000000..d8e5823ec
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/tsconfig.dist.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../../tsconfig.dist.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "types": [
+ "../../typings/types"
+ ]
+ },
+ "include": [
+ "src"
+ ]
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/tsconfig.json b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/tsconfig.json
new file mode 100644
index 000000000..06d45c898
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../tsconfig.release.json",
+ "include": [
+ "src",
+ "../../typings"
+ ],
+ "exclude": [
+ "tests"
+ ]
+}
diff --git a/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/webpack.config.cjs b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/webpack.config.cjs
new file mode 100644
index 000000000..04e3af7a4
--- /dev/null
+++ b/_regroup/ckeditor5-admonition/packages/ckeditor5-admonition/webpack.config.cjs
@@ -0,0 +1,19 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+'use strict';
+
+/* eslint-env node */
+
+const { builds } = require( '@ckeditor/ckeditor5-dev-utils' );
+const webpack = require( 'webpack' );
+
+module.exports = builds.getDllPluginWebpackConfig( webpack, {
+ themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' ),
+ packagePath: __dirname,
+ manifestPath: require.resolve( 'ckeditor5/build/ckeditor5-dll.manifest.json' ),
+ isDevelopmentMode: process.argv.includes( '--mode=development' ),
+ tsconfigPath: require.resolve( 'ckeditor5/tsconfig.dll.json' )
+} );