diff --git a/packages/pluggableWidgets/rich-text-web/CHANGELOG.md b/packages/pluggableWidgets/rich-text-web/CHANGELOG.md
index 4289f15ca5..38deab1766 100644
--- a/packages/pluggableWidgets/rich-text-web/CHANGELOG.md
+++ b/packages/pluggableWidgets/rich-text-web/CHANGELOG.md
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased]
+### Added
+
+- We added new configuration to allow users to use class names instead of inline styling in generated HTML to support strict CSP.
+
+### Fixed
+
+- We fixed an issue where the editor pasting back the whole sentence instead of the single copied word
+
+### Changed
+
+- We removed codemirror from code dialog viewer due to unsupported strict CSP policy. A simple internally built code editor using highlightjs is now replacing it.
+
## [4.12.0] - 2026-04-22
### Added
diff --git a/packages/pluggableWidgets/rich-text-web/e2e/RichText.spec.js-snapshots/viewCodeDialog-chromium-linux.png b/packages/pluggableWidgets/rich-text-web/e2e/RichText.spec.js-snapshots/viewCodeDialog-chromium-linux.png
index b4ca13e1a7..1d733a5a99 100644
Binary files a/packages/pluggableWidgets/rich-text-web/e2e/RichText.spec.js-snapshots/viewCodeDialog-chromium-linux.png and b/packages/pluggableWidgets/rich-text-web/e2e/RichText.spec.js-snapshots/viewCodeDialog-chromium-linux.png differ
diff --git a/packages/pluggableWidgets/rich-text-web/package.json b/packages/pluggableWidgets/rich-text-web/package.json
index 5fb882b303..cf816f5a2f 100644
--- a/packages/pluggableWidgets/rich-text-web/package.json
+++ b/packages/pluggableWidgets/rich-text-web/package.json
@@ -43,21 +43,19 @@
"verify": "rui-verify-package-format"
},
"dependencies": {
- "@codemirror/lang-html": "^6.4.9",
- "@codemirror/state": "^6.5.2",
"@floating-ui/dom": "^1.7.4",
"@floating-ui/react": "^0.26.27",
"@melloware/coloris": "^0.25.0",
- "@uiw/codemirror-theme-github": "^4.23.13",
- "@uiw/react-codemirror": "^4.23.13",
"classnames": "^2.5.1",
+ "highlight.js": "^11.11.1",
"js-beautify": "^1.15.4",
"katex": "^0.16.22",
"linkifyjs": "^4.3.2",
"lodash.merge": "^4.6.2",
"parchment": "^3.0.0",
"quill": "^2.0.3",
- "quill-resize-module": "^2.0.4"
+ "quill-resize-module": "^2.0.4",
+ "react-scroll-sync": "^1.0.2"
},
"devDependencies": {
"@mendix/automation-utils": "workspace:*",
diff --git a/packages/pluggableWidgets/rich-text-web/src/RichText.xml b/packages/pluggableWidgets/rich-text-web/src/RichText.xml
index 8fb841b775..0ce72a407b 100644
--- a/packages/pluggableWidgets/rich-text-web/src/RichText.xml
+++ b/packages/pluggableWidgets/rich-text-web/src/RichText.xml
@@ -220,6 +220,14 @@
Character count (including HTML)
+
+ Style data format
+ Choose how to render styling attribute in HTML
+
+ inline
+ class
+
+
diff --git a/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx b/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx
index 1ab4c89bad..870d49894d 100644
--- a/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx
+++ b/packages/pluggableWidgets/rich-text-web/src/__tests__/RichText.spec.tsx
@@ -47,7 +47,8 @@ describe("Rich Text", () => {
customFonts: [],
enableDefaultUpload: true,
formOrientation: "vertical",
- linkValidation: true
+ linkValidation: true,
+ styleDataFormat: "inline"
};
});
diff --git a/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx b/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx
index a13fdd1730..47af2073e8 100644
--- a/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx
+++ b/packages/pluggableWidgets/rich-text-web/src/components/Editor.tsx
@@ -13,14 +13,15 @@ import {
import { RichTextContainerProps } from "../../typings/RichTextProps";
import { EditorDispatchContext } from "../store/EditorProvider";
import { SET_FULLSCREEN_ACTION } from "../store/store";
-import "../utils/customPluginRegisters";
+import { registerCustomFormats } from "../utils/customPluginRegisters";
import "../utils/formats/quill-table-better/assets/css/quill-table-better.scss";
+import { MxQuillModulesOptions } from "../utils/formats";
import { getResizeModuleConfig } from "../utils/formats/resizeModuleConfig";
import { ACTION_DISPATCHER } from "../utils/helpers";
import { getKeyboardBindings } from "../utils/modules/keyboard";
import { getIndentHandler } from "../utils/modules/toolbarHandlers";
import MxUploader from "../utils/modules/uploader";
-import MxQuill, { MxQuillModulesOptions } from "../utils/MxQuill";
+import MxQuill from "../utils/MxQuill";
import { useEmbedModal } from "./CustomToolbars/useEmbedModal";
import Dialog from "./ModalDialog/Dialog";
@@ -68,7 +69,7 @@ const Editor = forwardRef((props: EditorProps, ref: MutableRefObject {
@@ -133,7 +134,8 @@ const Editor = forwardRef((props: EditorProps, ref: MutableRefObject(null);
const [formState, setFormState] = useState({
// temporarily change tab characters to em space to avoid beautify removing them
src:
beautify.html(currentCode?.replace(/\t/g, " ") ?? "", BEAUTIFY_OPTIONS)?.replace(/ /g, "\t") || ""
});
- const onCodeChange = useCallback((value: string, _viewUpdate: ViewUpdate) => {
- setFormState({ ...formState, src: value });
- }, []);
+
+ useEffect(() => {
+ const codeElement = codeRef.current;
+ hljs.highlightAll();
+ return () => {
+ if (codeElement) {
+ delete codeElement.dataset.highlighted;
+ }
+ };
+ }, [formState]);
+ const onCodeChange = useCallback(
+ (value: string) => {
+ setFormState({ ...formState, src: value });
+ },
+ [formState]
+ );
return (
@@ -45,15 +58,35 @@ export default function ViewCodeDialog(props: ViewCodeDialogProps): ReactElement
Source Code
-
+
+
+
+
+
+
+
+
+
+
+ {formState.src}
+
+
+
+
+
onSubmit(formState)} onClose={onClose}>
diff --git a/packages/pluggableWidgets/rich-text-web/src/ui/RichText.scss b/packages/pluggableWidgets/rich-text-web/src/ui/RichText.scss
index a54e9ebf74..74e5ea1deb 100644
--- a/packages/pluggableWidgets/rich-text-web/src/ui/RichText.scss
+++ b/packages/pluggableWidgets/rich-text-web/src/ui/RichText.scss
@@ -1,4 +1,5 @@
@use "RichTextIcons";
+@use "RichTextFormatStyle";
$rte-border-color-default: #ced0d3;
$rte-gray-ligher: #f8f8f8;
@@ -170,4 +171,9 @@ $rte-brand-primary: #264ae5;
.flexcontainer.flex-column {
overflow: visible;
}
+
+ VIDEO,
+ IFRAME {
+ pointer-events: none;
+ }
}
diff --git a/packages/pluggableWidgets/rich-text-web/src/ui/RichTextFormatStyle.scss b/packages/pluggableWidgets/rich-text-web/src/ui/RichTextFormatStyle.scss
new file mode 100644
index 0000000000..c7f8c30ce3
--- /dev/null
+++ b/packages/pluggableWidgets/rich-text-web/src/ui/RichTextFormatStyle.scss
@@ -0,0 +1,24 @@
+@import "variables";
+
+$color: #3d1466;
+.widget-rich-text {
+ @each $name, $font in $fonts {
+ .font-family-#{$name} {
+ @include font($font);
+ }
+ }
+ @each $size in $font-sizes {
+ .ql-size-#{$size} {
+ font-size: $size;
+ }
+ }
+
+ @each $color in $colors {
+ .ql-color-#{"\\" + $color} {
+ color: $color;
+ }
+ .ql-bg-#{"\\" + $color} {
+ background-color: $color;
+ }
+ }
+}
diff --git a/packages/pluggableWidgets/rich-text-web/src/ui/variables.scss b/packages/pluggableWidgets/rich-text-web/src/ui/variables.scss
new file mode 100644
index 0000000000..a5e56c89fd
--- /dev/null
+++ b/packages/pluggableWidgets/rich-text-web/src/ui/variables.scss
@@ -0,0 +1,72 @@
+$font-andale-mono: "andale mono", monospace;
+$font-arial: arial, helvetica, sans-serif;
+$font-arial-black: "arial black", sans-serif;
+$font-book-antiqua: "book antiqua", palatino, serif;
+$font-comic-sans: "comic sans ms", sans-serif;
+$font-courier-new: "courier new", courier, monospace;
+$font-helvetica: helvetica, arial, sans-serif;
+$font-impact: impact, sans-serif;
+$font-symbol: symbol;
+$font-terminal: terminal, monaco, monospace;
+$font-times-new-roman: "times new roman", times, serif;
+$font-trebuchet: "trebuchet ms", geneva, sans-serif;
+
+$fonts: (
+ andale-mono: $font-andale-mono,
+ arial: $font-arial,
+ arial-black: $font-arial-black,
+ book-antiqua: $font-book-antiqua,
+ comic-sans: $font-comic-sans,
+ courier-new: $font-courier-new,
+ helvetica: $font-helvetica,
+ impact: $font-impact,
+ symbol: $font-symbol,
+ terminal: $font-terminal,
+ times-new-roman: $font-times-new-roman,
+ trebuchet: $font-trebuchet
+);
+
+@mixin font($font) {
+ font-family: $font;
+}
+
+$font-sizes: (8px, 9px, 10px, 12px, 14px, 16px, 20px, 24px, 32px, 42px, 54px, 68px, 84px, 98px);
+
+// https://github.com/slab/quill/blob/main/packages/quill/src/themes/base.ts
+$colors: (
+ #000000,
+ #e60000,
+ #ff9900,
+ #ffff00,
+ #008a00,
+ #0066cc,
+ #9933ff,
+ #ffffff,
+ #facccc,
+ #ffebcc,
+ #ffffcc,
+ #cce8cc,
+ #cce0f5,
+ #ebd6ff,
+ #bbbbbb,
+ #f06666,
+ #ffc266,
+ #ffff66,
+ #66b966,
+ #66a3e0,
+ #c285ff,
+ #888888,
+ #a10000,
+ #b26b00,
+ #b2b200,
+ #006100,
+ #0047b2,
+ #6b24b2,
+ #444444,
+ #5c0000,
+ #663d00,
+ #666600,
+ #003700,
+ #002966,
+ #3d1466
+);
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/MxQuill.ts b/packages/pluggableWidgets/rich-text-web/src/utils/MxQuill.ts
index 1d3710bd66..09ea6c33fc 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/MxQuill.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/MxQuill.ts
@@ -42,9 +42,6 @@ import TextBlot, { escapeText } from "quill/blots/text";
import { Delta, Op } from "quill/core";
import Editor from "quill/core/editor";
import { STANDARD_LIST_TYPES } from "./formats/customList";
-import { FontStyleAttributor, formatCustomFonts } from "./formats/fonts";
-import CustomLink, { CustomLinkNoValidation } from "./formats/link";
-import { CustomFontsType } from "../../typings/RichTextProps";
interface ListItem {
child: Blot;
@@ -80,17 +77,11 @@ class MxEditor extends Editor {
}
}
-export interface MxQuillModulesOptions {
- fonts: CustomFontsType[];
- links: {
- validate: boolean;
- };
-}
-
/**
* Extension's of quill to allow us to replace the editor instance.
*/
export default class MxQuill extends Quill {
+ private styleDataFormat: "inline" | "class" = "inline";
constructor(container: HTMLElement | string, options: QuillOptions = {}) {
super(container, options);
this.editor = new MxEditor(this.scroll);
@@ -101,16 +92,12 @@ export default class MxQuill extends Quill {
return this.updateContents(this.getContents().transform(dlta as Delta, false), source);
}
- registerCustomModules(props: MxQuillModulesOptions): void {
- const { fonts, links } = props;
- const customFonts = formatCustomFonts(fonts);
- const FontStyle = new FontStyleAttributor(customFonts);
- Quill.register(FontStyle, true);
- if (links.validate) {
- Quill.register(CustomLink, true);
- } else {
- Quill.register(CustomLinkNoValidation, true);
- }
+ setStyleDataFormat(format: "inline" | "class"): void {
+ this.styleDataFormat = format;
+ }
+
+ getStyleDataFormat(): "inline" | "class" {
+ return this.styleDataFormat;
}
}
@@ -164,15 +151,22 @@ function convertListHTML(items: ListItem[], lastIndent: number, types: string[])
}
return `${endTag}>${convertListHTML([], lastIndent - 1, types)}`;
}
+ console.log("converting list HTML ", items);
const [{ child, offset, length, indent, type }, ...rest] = items;
const [tag, attribute] = getListType(type);
if (indent > lastIndent) {
// modified by web-content: get proper list-style-type
const expectedType = getExpectedType(type, indent);
+
+ let listStyleTypeAttribute = `style="list-style-type: ${expectedType}"`;
+ if ((child.domNode as HTMLElement)?.dataset?.styleFormat === "class") {
+ const listStyleClass = `ql-list-style-${expectedType}`;
+ listStyleTypeAttribute = `class="${listStyleClass}"`;
+ }
types.push(type);
if (indent === lastIndent + 1) {
- return `<${tag} style="list-style-type: ${expectedType}">${convertHTML(
+ return `<${tag} ${listStyleTypeAttribute}> ${convertHTML(
child,
offset,
length
@@ -197,6 +191,7 @@ function convertHTML(blot: Blot, index: number, length: number, isRoot = false):
if ("html" in blot && typeof blot.html === "function") {
return blot.html(index, length);
}
+
if (blot instanceof TextBlot) {
const escapedText = escapeText(blot.value().slice(index, index + length));
return escapedText;
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/customPluginRegisters.ts b/packages/pluggableWidgets/rich-text-web/src/utils/customPluginRegisters.ts
index c954bb99c5..04c9c75a67 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/customPluginRegisters.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/customPluginRegisters.ts
@@ -1,24 +1,27 @@
-import { Attributor } from "parchment";
import Quill from "quill";
-import QuillResize from "quill-resize-module";
+import { AlignClass, AlignStyle } from "quill/formats/align";
+import { BackgroundClass, BackgroundStyle } from "quill/formats/background";
+import { ColorClass, ColorStyle } from "quill/formats/color";
+import { DirectionStyle, DirectionClass } from "quill/formats/direction";
+import { SizeClass, SizeStyle } from "quill/formats/size";
+import { MxQuillModulesOptions } from "./formats";
import MxBlock from "./formats/block";
import Button from "./formats/button";
-import CustomListItem from "./formats/customList";
-import "./formats/fonts";
-import "./formats/fontsize";
+import { CustomListItem, CustomListItemClass } from "./formats/customList";
+import { FontClassAttributor, FontStyleAttributor, formatCustomFonts } from "./formats/fonts";
+import { FONT_SIZE_LIST } from "./formats/fontsize";
import Formula from "./formats/formula";
import CustomImage from "./formats/image";
import { IndentLeftStyle, IndentRightStyle } from "./formats/indent";
+import CustomLink, { CustomLinkNoValidation } from "./formats/link";
import QuillTableBetter from "./formats/quill-table-better/quill-table-better";
import SoftBreak from "./formats/softBreak";
import CustomVideo from "./formats/video";
import { WhiteSpaceStyle } from "./formats/whiteSpace";
+import { MxResizeModule } from "./modules/resize";
+import MxScroll from "./modules/scroll";
import MxUploader from "./modules/uploader";
import MendixTheme from "./themes/mxTheme";
-import MxScroll from "./modules/scroll";
-const direction = Quill.import("attributors/style/direction") as Attributor;
-const alignment = Quill.import("attributors/style/align") as Attributor;
-
class Empty {
doSomething(): string {
return "";
@@ -29,21 +32,56 @@ class Empty {
*/
Quill.debug("error");
Quill.register({ "themes/snow": MendixTheme }, true);
-Quill.register(CustomListItem, true);
Quill.register(WhiteSpaceStyle, true);
Quill.register(CustomVideo, true);
Quill.register(CustomImage, true);
Quill.register({ "formats/softbreak": SoftBreak }, true);
-Quill.register(direction, true);
-Quill.register(alignment, true);
-Quill.register(IndentLeftStyle, true);
-Quill.register(IndentRightStyle, true);
Quill.register(Formula, true);
Quill.register(Button, true);
Quill.register(MxBlock, true);
Quill.register({ "modules/uploader": MxUploader }, true);
Quill.register({ "blots/scroll": MxScroll }, true);
-Quill.register("modules/resize", QuillResize, true);
+Quill.register("modules/resize", MxResizeModule, true);
// add empty handler for view code, this format is handled by toolbar's custom config via ViewCodeDialog
Quill.register({ "ui/view-code": Empty });
Quill.register({ "modules/table-better": QuillTableBetter }, true);
+
+export function registerCustomFormats(props: MxQuillModulesOptions): void {
+ const { fonts, links, styleDataFormat } = props;
+
+ // register formats based on styleDataFormat option
+ const customFonts = formatCustomFonts(fonts);
+ if (styleDataFormat === "inline") {
+ const FontStyle = new FontStyleAttributor(customFonts);
+ Quill.register(FontStyle, true);
+
+ SizeStyle.whitelist = FONT_SIZE_LIST;
+ Quill.register(SizeStyle, true);
+
+ Quill.register(CustomListItem, true);
+ Quill.register(DirectionStyle, true);
+ Quill.register(AlignStyle, true);
+ Quill.register(IndentLeftStyle, true);
+ Quill.register(IndentRightStyle, true);
+ Quill.register(ColorStyle, true);
+ Quill.register(BackgroundStyle, true);
+ } else {
+ const FontClass = new FontClassAttributor(customFonts);
+ Quill.register(FontClass, true);
+
+ SizeClass.whitelist = FONT_SIZE_LIST;
+ Quill.register(SizeClass, true);
+ Quill.register(CustomListItemClass, true);
+ Quill.register(DirectionClass, true);
+ Quill.register(AlignClass, true);
+ Quill.register(ColorClass, true);
+ Quill.register(BackgroundClass, true);
+ }
+
+ // register link format based on validation requirement
+ if (links.validate) {
+ Quill.register(CustomLink, true);
+ } else {
+ Quill.register(CustomLinkNoValidation, true);
+ }
+}
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats.d.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats.d.ts
index 6058445ab1..28c292bda7 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/formats.d.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/formats.d.ts
@@ -28,3 +28,10 @@ export type imageConfigType = {
entityGuid?: string;
keepAspectRatio?: boolean;
};
+export interface MxQuillModulesOptions {
+ styleDataFormat: "inline" | "class";
+ fonts: CustomFontsType[];
+ links: {
+ validate: boolean;
+ };
+}
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/customList.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/customList.ts
index 19ea919c67..00bdc507a1 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/customList.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/formats/customList.ts
@@ -1,12 +1,12 @@
import ListItem from "quill/formats/list";
import "./customList.scss";
+import Scroll from "quill/blots/scroll";
/**
* adding custom list item, alowing extra list style
*/
export const STANDARD_LIST_TYPES = ["ordered", "checked", "unchecked", "bullet"];
-
-export default class CustomListItem extends ListItem {
+export class CustomListItem extends ListItem {
format(name: string, value: string): void {
if (name === this.statics.blotName && value) {
if (!STANDARD_LIST_TYPES.find(x => x === value)) {
@@ -38,3 +38,10 @@ export default class CustomListItem extends ListItem {
return domNode.dataset.customList || domNode.dataset.list || undefined;
}
}
+
+export class CustomListItemClass extends CustomListItem {
+ constructor(scroll: Scroll, domNode: HTMLElement) {
+ super(scroll, domNode);
+ domNode.dataset.styleFormat = "class";
+ }
+}
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.scss b/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.scss
index 258576b2dc..4485cdb364 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.scss
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.scss
@@ -1,34 +1,5 @@
-$font-andale-mono: "andale mono", monospace;
-$font-arial: arial, helvetica, sans-serif;
-$font-arial-black: "arial black", sans-serif;
-$font-book-antiqua: "book antiqua", palatino, serif;
-$font-comic-sans: "comic sans ms", sans-serif;
-$font-courier-new: "courier new", courier, monospace;
-$font-helvetica: helvetica, arial, sans-serif;
-$font-impact: impact, sans-serif;
-$font-symbol: symbol;
-$font-terminal: terminal, monaco, monospace;
-$font-times-new-roman: "times new roman", times, serif;
-$font-trebuchet: "trebuchet ms", geneva, sans-serif;
+@import "../../ui/variables";
-$fonts: (
- andale-mono: $font-andale-mono,
- arial: $font-arial,
- arial-black: $font-arial-black,
- book-antiqua: $font-book-antiqua,
- comic-sans: $font-comic-sans,
- courier-new: $font-courier-new,
- helvetica: $font-helvetica,
- impact: $font-impact,
- symbol: $font-symbol,
- terminal: $font-terminal,
- times-new-roman: $font-times-new-roman,
- trebuchet: $font-trebuchet
-);
-
-@mixin font($font) {
- font-family: $font;
-}
.widget-rich-text {
.ql-toolbar {
.ql-picker {
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts
index 6b3e445bc0..7e35202b8b 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/formats/fonts.ts
@@ -1,4 +1,4 @@
-import { Scope, StyleAttributor } from "parchment";
+import { ClassAttributor, Scope, StyleAttributor } from "parchment";
import { CustomFontsType } from "../../../typings/RichTextProps";
import "./fonts.scss";
@@ -54,6 +54,38 @@ export class FontStyleAttributor extends StyleAttributor {
}
}
+export class FontClassAttributor extends ClassAttributor {
+ private fontList: typeof FONT_LIST = [];
+
+ constructor(fontList: typeof FONT_LIST) {
+ super("font", "font-family", config);
+ this.fontList = fontList;
+ }
+
+ add(node: HTMLElement, value: any): boolean {
+ if (!this.canAdd(node, value)) {
+ return false;
+ }
+ node.dataset.value = value;
+ const allFonts = [...FONT_LIST, ...this.fontList];
+ const style = allFonts.find(x => x.value === value)?.value;
+ if (style) {
+ super.add(node, style);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ value(node: HTMLElement): any {
+ const value = node.dataset.value;
+ if (this.canAdd(node, value) && value) {
+ return value;
+ }
+ return "";
+ }
+}
+
export function formatCustomFonts(fonts: CustomFontsType[] = []): typeof FONT_LIST {
return fonts.map(font => ({
value: font.fontName?.toLowerCase().split(" ").join("-") ?? "",
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fontsize.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/fontsize.ts
index 29335852a3..9eadd57d0b 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/fontsize.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/formats/fontsize.ts
@@ -1,7 +1,3 @@
-import { Attributor } from "parchment";
-import Quill from "quill";
-const Size = Quill.import("attributors/style/size") as Attributor;
-
import "./fonts.scss";
export const FONT_SIZE_LIST = [
@@ -20,6 +16,3 @@ export const FONT_SIZE_LIST = [
"84px",
"98px"
];
-
-Size.whitelist = FONT_SIZE_LIST;
-Quill.register(Size, true);
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/indent.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/indent.ts
index d00f8a1ddc..7bd1285bce 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/indent.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/formats/indent.ts
@@ -1,7 +1,7 @@
import { Scope, StyleAttributor } from "parchment";
+import { INDENT_MAGIC_NUMBER } from "../helpers";
import "./fonts.scss";
-const INDENT_MAGIC_NUMBER = 3;
const indentLists = ["3em", "6em", "9em", "12em", "15em", "18em", "21em", "24em", "27em"];
/**
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/formats/resizeModuleConfig.ts b/packages/pluggableWidgets/rich-text-web/src/utils/formats/resizeModuleConfig.ts
index c6a8bf24f7..753f53467e 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/formats/resizeModuleConfig.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/formats/resizeModuleConfig.ts
@@ -1,8 +1,8 @@
import Quill from "quill";
import QuillResize from "quill-resize-module";
import { ACTION_DISPATCHER } from "../helpers";
-import MxResizeToolbar from "../modules/resizeToolbar";
import MxResize from "../modules/resize";
+import MxResizeToolbar from "../modules/resizeToolbar";
export const RESIZE_MODULE_CONFIG = {
modules: ["DisplaySize", MxResizeToolbar, MxResize, "Keyboard"],
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/helpers.ts b/packages/pluggableWidgets/rich-text-web/src/utils/helpers.ts
index 7f60084c39..c7ed30f02d 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/helpers.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/helpers.ts
@@ -1,6 +1,7 @@
import { CSSProperties } from "react";
import { RichTextContainerProps } from "typings/RichTextProps";
+export const INDENT_MAGIC_NUMBER = 3;
export const ACTION_DISPATCHER = "ACTION_DISPATCHER";
function getHeightScale(height: number, heightUnit: "pixels" | "percentageOfParent" | "percentageOfView"): string {
@@ -32,3 +33,44 @@ export function constructWrapperStyle(props: RichTextContainerProps): CSSPropert
return wrapperStyle;
}
+
+export function normalizeStyleAndClassAttribute(doc: Document, styleDataFormat: "inline" | "class"): void {
+ if (styleDataFormat === "class") {
+ const allIndentLeftElements = doc.querySelectorAll("[style*=padding-left]");
+ const allIndentRightElements = doc.querySelectorAll("[style*=padding-right]");
+ allIndentLeftElements.forEach(element => {
+ const paddingLeft = (element as HTMLElement).style.paddingLeft || "0em";
+ const indentValue = parseInt(paddingLeft.replace("px", "").replace("em", ""), 10);
+ if (indentValue) {
+ const indentClassValue = Math.round(indentValue / INDENT_MAGIC_NUMBER);
+ element.classList.add(`ql-indent-${indentClassValue}`);
+ (element as HTMLElement).style.removeProperty("padding-left");
+ }
+ });
+ allIndentRightElements.forEach(element => {
+ const paddingRight = (element as HTMLElement).style.paddingRight || "0em";
+ const indentValue = parseInt(paddingRight.replace("px", "").replace("em", ""), 10);
+ if (indentValue) {
+ const indentClassValue = Math.round(indentValue / INDENT_MAGIC_NUMBER);
+ element.classList.add(`ql-indent-${indentClassValue}`);
+ (element as HTMLElement).style.removeProperty("padding-right");
+ }
+ });
+ } else if (styleDataFormat === "inline") {
+ const allIndentsElements = doc.querySelectorAll("[class*=ql-indent-]");
+ allIndentsElements.forEach(element => {
+ const indentClass = Array.from(element.classList).find(className => className.startsWith("ql-indent-"));
+ if (indentClass) {
+ const indentValue = parseInt(indentClass.replace("ql-indent-", ""), 10);
+ if (indentValue) {
+ if (element.classList.contains("ql-direction-rtl")) {
+ (element as HTMLElement).style.paddingRight = `${indentValue * INDENT_MAGIC_NUMBER}em`;
+ } else {
+ (element as HTMLElement).style.paddingLeft = `${indentValue * INDENT_MAGIC_NUMBER}em`;
+ }
+ }
+ element.classList.remove(indentClass);
+ }
+ });
+ }
+}
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/modules/clipboard.ts b/packages/pluggableWidgets/rich-text-web/src/utils/modules/clipboard.ts
index 0b98faa349..2af7e31bcd 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/modules/clipboard.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/modules/clipboard.ts
@@ -7,6 +7,8 @@
import { EmbedBlot, type ScrollBlot } from "parchment";
import Quill, { Delta } from "quill";
import Clipboard, { matchNewline } from "quill/modules/clipboard";
+import { normalizeStyleAndClassAttribute } from "../helpers";
+import MxQuill from "../MxQuill";
export default class CustomClipboard extends Clipboard {
constructor(quill: Quill, options: any) {
@@ -24,6 +26,19 @@ export default class CustomClipboard extends Clipboard {
this.addMatcher("ol, ul", matchList);
this.addMatcher("a", matchLink);
}
+
+ onCaptureCopy(e: ClipboardEvent, isCut?: boolean): void {
+ super.onCaptureCopy(e, isCut);
+ }
+
+ onCapturePaste(e: ClipboardEvent): void {
+ super.onCapturePaste(e);
+ }
+
+ protected normalizeHTML(doc: Document): void {
+ super.normalizeHTML(doc);
+ normalizeStyleAndClassAttribute(doc, (this.quill as MxQuill).getStyleDataFormat());
+ }
}
function isLine(node: Node, scroll: ScrollBlot): any {
diff --git a/packages/pluggableWidgets/rich-text-web/src/utils/modules/resize.ts b/packages/pluggableWidgets/rich-text-web/src/utils/modules/resize.ts
index 8c88b4ee8d..1cf17bef30 100644
--- a/packages/pluggableWidgets/rich-text-web/src/utils/modules/resize.ts
+++ b/packages/pluggableWidgets/rich-text-web/src/utils/modules/resize.ts
@@ -23,6 +23,12 @@ type LimitConfig = {
type CalculateSizeEvent = { clientX: number; clientY: number };
+export class MxResizeModule extends QuillResize {
+ initializeEmbed(): void {
+ // override parents, do nothing
+ }
+}
+
export default class MxResize extends QuillResize.Modules.Resize {
// modified from https://github.com/mudoo/quill-resize-module/blob/master/src/modules/Resize.js
calcSize(evt: CalculateSizeEvent, limit: LimitConfig = {}): ImageSizes {
diff --git a/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts b/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts
index 685894966f..c32e097386 100644
--- a/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts
+++ b/packages/pluggableWidgets/rich-text-web/typings/RichTextProps.d.ts
@@ -33,6 +33,8 @@ export interface CustomFontsType {
export type StatusBarContentEnum = "wordCount" | "characterCount" | "characterCountHtml";
+export type StyleDataFormatEnum = "inline" | "class";
+
export type ToolbarConfigEnum = "basic" | "advanced";
export type CtItemTypeEnum = "separator" | "undo" | "redo" | "bold" | "italic" | "underline" | "strike" | "superScript" | "subScript" | "orderedList" | "bulletList" | "lowerAlphaList" | "checkList" | "minIndent" | "plusIndent" | "direction" | "link" | "image" | "video" | "formula" | "blockquote" | "code" | "codeBlock" | "viewCode" | "align" | "centerAlign" | "rightAlign" | "font" | "size" | "color" | "background" | "header" | "fullscreen" | "clean" | "tableBetter";
@@ -83,6 +85,7 @@ export interface RichTextContainerProps {
imageSourceContent?: ReactNode;
enableDefaultUpload: boolean;
statusBarContent: StatusBarContentEnum;
+ styleDataFormat: StyleDataFormatEnum;
toolbarConfig: ToolbarConfigEnum;
history: boolean;
fontStyle: boolean;
@@ -133,6 +136,7 @@ export interface RichTextPreviewProps {
imageSourceContent: { widgetCount: number; renderer: ComponentType<{ children: ReactNode; caption?: string }> };
enableDefaultUpload: boolean;
statusBarContent: StatusBarContentEnum;
+ styleDataFormat: StyleDataFormatEnum;
toolbarConfig: ToolbarConfigEnum;
history: boolean;
fontStyle: boolean;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9b02c4bf42..1eaecf7d7f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2152,12 +2152,6 @@ importers:
packages/pluggableWidgets/rich-text-web:
dependencies:
- '@codemirror/lang-html':
- specifier: ^6.4.9
- version: 6.4.11
- '@codemirror/state':
- specifier: ^6.5.2
- version: 6.5.2
'@floating-ui/dom':
specifier: ^1.7.4
version: 1.7.4
@@ -2167,15 +2161,12 @@ importers:
'@melloware/coloris':
specifier: ^0.25.0
version: 0.25.0
- '@uiw/codemirror-theme-github':
- specifier: ^4.23.13
- version: 4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)
- '@uiw/react-codemirror':
- specifier: ^4.23.13
- version: 4.25.2(@babel/runtime@7.28.6)(@codemirror/autocomplete@6.19.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.6)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
classnames:
specifier: ^2.5.1
version: 2.5.1
+ highlight.js:
+ specifier: ^11.11.1
+ version: 11.11.1
js-beautify:
specifier: ^1.15.4
version: 1.15.4
@@ -2197,6 +2188,9 @@ importers:
quill-resize-module:
specifier: ^2.0.4
version: 2.0.8
+ react-scroll-sync:
+ specifier: ^1.0.2
+ version: 1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
devDependencies:
'@mendix/automation-utils':
specifier: workspace:*
@@ -3846,15 +3840,6 @@ packages:
'@codemirror/commands@6.9.0':
resolution: {integrity: sha512-454TVgjhO6cMufsyyGN70rGIfJxJEjcqjBG2x2Y03Y/+Fm99d3O/Kv1QDYWuG6hvxsgmjXmBuATikIIYvERX+w==}
- '@codemirror/lang-css@6.3.1':
- resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==}
-
- '@codemirror/lang-html@6.4.11':
- resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==}
-
- '@codemirror/lang-javascript@6.2.4':
- resolution: {integrity: sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==}
-
'@codemirror/lang-json@6.0.2':
resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==}
@@ -3968,10 +3953,6 @@ packages:
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
- '@eslint-community/regexpp@4.12.1':
- resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
- engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
-
'@eslint-community/regexpp@4.12.2':
resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
@@ -4214,21 +4195,12 @@ packages:
'@lezer/common@1.5.1':
resolution: {integrity: sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==}
- '@lezer/css@1.3.0':
- resolution: {integrity: sha512-pBL7hup88KbI7hXnZV3PQsn43DHy6TWyzuyk2AO9UyoXcDltvIdqWKE1dLL/45JVZ+YZkHe1WVHqO6wugZZWcw==}
-
'@lezer/highlight@1.2.1':
resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==}
'@lezer/highlight@1.2.3':
resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==}
- '@lezer/html@1.3.12':
- resolution: {integrity: sha512-RJ7eRWdaJe3bsiiLLHjCFT1JMk8m1YP9kaUbvu2rMLEoOnke9mcTVDyfOslsln0LtujdWespjJ39w6zo+RsQYw==}
-
- '@lezer/javascript@1.5.4':
- resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==}
-
'@lezer/json@1.0.3':
resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==}
@@ -7378,6 +7350,10 @@ packages:
hermes-parser@0.33.3:
resolution: {integrity: sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==}
+ highlight.js@11.11.1:
+ resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
+ engines: {node: '>=12.0.0'}
+
hoist-non-react-statics@2.5.5:
resolution: {integrity: sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==}
@@ -9518,6 +9494,12 @@ packages:
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
engines: {node: '>=0.10.0'}
+ react-scroll-sync@1.0.2:
+ resolution: {integrity: sha512-uQTyayj/DbkejXFwsQI7o+5+pNC2uXJVsYpiGVOMKTF6NzIoE6cvEsw4NjTEKAX21iSgTJexWIE2WWyD3oBtsA==}
+ peerDependencies:
+ react: '>=18.0.0 <19.0.0'
+ react-dom: '>=18.0.0 <19.0.0'
+
react-test-renderer@19.2.4:
resolution: {integrity: sha512-Ttl5D7Rnmi6JGMUpri4UjB4BAN0FPs4yRDnu2XSsigCWOLm11o8GwRlVsh27ER+4WFqsGtrBuuv5zumUaRCmKw==}
peerDependencies:
@@ -11923,9 +11905,9 @@ snapshots:
'@codemirror/autocomplete@6.19.0':
dependencies:
'@codemirror/language': 6.11.3
- '@codemirror/state': 6.5.2
+ '@codemirror/state': 6.5.4
'@codemirror/view': 6.38.6
- '@lezer/common': 1.2.3
+ '@lezer/common': 1.5.1
'@codemirror/commands@6.10.2':
dependencies:
@@ -11941,36 +11923,6 @@ snapshots:
'@codemirror/view': 6.38.6
'@lezer/common': 1.2.3
- '@codemirror/lang-css@6.3.1':
- dependencies:
- '@codemirror/autocomplete': 6.19.0
- '@codemirror/language': 6.11.3
- '@codemirror/state': 6.5.2
- '@lezer/common': 1.2.3
- '@lezer/css': 1.3.0
-
- '@codemirror/lang-html@6.4.11':
- dependencies:
- '@codemirror/autocomplete': 6.19.0
- '@codemirror/lang-css': 6.3.1
- '@codemirror/lang-javascript': 6.2.4
- '@codemirror/language': 6.11.3
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.38.6
- '@lezer/common': 1.2.3
- '@lezer/css': 1.3.0
- '@lezer/html': 1.3.12
-
- '@codemirror/lang-javascript@6.2.4':
- dependencies:
- '@codemirror/autocomplete': 6.19.0
- '@codemirror/language': 6.11.3
- '@codemirror/lint': 6.9.0
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.38.6
- '@lezer/common': 1.2.3
- '@lezer/javascript': 1.5.4
-
'@codemirror/lang-json@6.0.2':
dependencies:
'@codemirror/language': 6.11.3
@@ -11987,7 +11939,7 @@ snapshots:
'@codemirror/lint@6.9.0':
dependencies:
- '@codemirror/state': 6.5.2
+ '@codemirror/state': 6.5.4
'@codemirror/view': 6.38.6
crelt: 1.0.6
@@ -12145,8 +12097,6 @@ snapshots:
eslint: 9.39.3(jiti@2.6.1)
eslint-visitor-keys: 3.4.3
- '@eslint-community/regexpp@4.12.1': {}
-
'@eslint-community/regexpp@4.12.2': {}
'@eslint/config-array@0.21.1':
@@ -12502,12 +12452,6 @@ snapshots:
'@lezer/common@1.5.1': {}
- '@lezer/css@1.3.0':
- dependencies:
- '@lezer/common': 1.2.3
- '@lezer/highlight': 1.2.1
- '@lezer/lr': 1.4.2
-
'@lezer/highlight@1.2.1':
dependencies:
'@lezer/common': 1.2.3
@@ -12516,18 +12460,6 @@ snapshots:
dependencies:
'@lezer/common': 1.5.1
- '@lezer/html@1.3.12':
- dependencies:
- '@lezer/common': 1.2.3
- '@lezer/highlight': 1.2.1
- '@lezer/lr': 1.4.2
-
- '@lezer/javascript@1.5.4':
- dependencies:
- '@lezer/common': 1.2.3
- '@lezer/highlight': 1.2.1
- '@lezer/lr': 1.4.2
-
'@lezer/json@1.0.3':
dependencies:
'@lezer/common': 1.2.3
@@ -13689,16 +13621,6 @@ snapshots:
'@typescript-eslint/types': 8.57.0
eslint-visitor-keys: 5.0.1
- '@uiw/codemirror-extensions-basic-setup@4.25.2(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)':
- dependencies:
- '@codemirror/autocomplete': 6.19.0
- '@codemirror/commands': 6.9.0
- '@codemirror/language': 6.11.3
- '@codemirror/lint': 6.9.0
- '@codemirror/search': 6.5.11
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.38.6
-
'@uiw/codemirror-extensions-basic-setup@4.25.2(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.4)(@codemirror/view@6.38.6)':
dependencies:
'@codemirror/autocomplete': 6.19.0
@@ -13709,14 +13631,6 @@ snapshots:
'@codemirror/state': 6.5.4
'@codemirror/view': 6.38.6
- '@uiw/codemirror-theme-github@4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)':
- dependencies:
- '@uiw/codemirror-themes': 4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)
- transitivePeerDependencies:
- - '@codemirror/language'
- - '@codemirror/state'
- - '@codemirror/view'
-
'@uiw/codemirror-theme-github@4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.4)(@codemirror/view@6.38.6)':
dependencies:
'@uiw/codemirror-themes': 4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.4)(@codemirror/view@6.38.6)
@@ -13725,35 +13639,12 @@ snapshots:
- '@codemirror/state'
- '@codemirror/view'
- '@uiw/codemirror-themes@4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)':
- dependencies:
- '@codemirror/language': 6.11.3
- '@codemirror/state': 6.5.2
- '@codemirror/view': 6.38.6
-
'@uiw/codemirror-themes@4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.4)(@codemirror/view@6.38.6)':
dependencies:
'@codemirror/language': 6.11.3
'@codemirror/state': 6.5.4
'@codemirror/view': 6.38.6
- '@uiw/react-codemirror@4.25.2(@babel/runtime@7.28.6)(@codemirror/autocomplete@6.19.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.6)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
- dependencies:
- '@babel/runtime': 7.28.6
- '@codemirror/commands': 6.9.0
- '@codemirror/state': 6.5.2
- '@codemirror/theme-one-dark': 6.1.3
- '@codemirror/view': 6.38.6
- '@uiw/codemirror-extensions-basic-setup': 4.25.2(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)
- codemirror: 6.0.2
- react: 18.3.1
- react-dom: 18.3.1(react@18.3.1)
- transitivePeerDependencies:
- - '@codemirror/autocomplete'
- - '@codemirror/language'
- - '@codemirror/lint'
- - '@codemirror/search'
-
'@uiw/react-codemirror@4.25.2(@babel/runtime@7.28.6)(@codemirror/autocomplete@6.19.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.4)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.6)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.28.6
@@ -13915,10 +13806,6 @@ snapshots:
dependencies:
acorn: 8.15.0
- acorn-jsx@5.3.2(acorn@8.15.0):
- dependencies:
- acorn: 8.15.0
-
acorn-jsx@5.3.2(acorn@8.16.0):
dependencies:
acorn: 8.16.0
@@ -15102,11 +14989,11 @@ snapshots:
dom-helpers@3.4.0:
dependencies:
- '@babel/runtime': 7.28.4
+ '@babel/runtime': 7.28.6
dom-helpers@5.2.1:
dependencies:
- '@babel/runtime': 7.28.4
+ '@babel/runtime': 7.28.6
csstype: 3.1.3
dom-serializer@1.4.1:
@@ -15580,8 +15467,8 @@ snapshots:
eslint@9.39.3(jiti@2.6.1):
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.3(jiti@2.6.1))
- '@eslint-community/regexpp': 4.12.1
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.6.1))
+ '@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.1
'@eslint/config-helpers': 0.4.2
'@eslint/core': 0.17.0
@@ -15628,8 +15515,8 @@ snapshots:
espree@10.4.0:
dependencies:
- acorn: 8.15.0
- acorn-jsx: 5.3.2(acorn@8.15.0)
+ acorn: 8.16.0
+ acorn-jsx: 5.3.2(acorn@8.16.0)
eslint-visitor-keys: 4.2.1
espree@9.6.1:
@@ -16299,6 +16186,8 @@ snapshots:
dependencies:
hermes-estree: 0.33.3
+ highlight.js@11.11.1: {}
+
hoist-non-react-statics@2.5.5: {}
hono@4.12.4: {}
@@ -18912,6 +18801,11 @@ snapshots:
react-refresh@0.14.2: {}
+ react-scroll-sync@1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+
react-test-renderer@19.2.4(react@18.3.1):
dependencies:
react: 18.3.1
@@ -20122,7 +20016,7 @@ snapshots:
uncontrollable@7.2.1(react@18.3.1):
dependencies:
- '@babel/runtime': 7.28.4
+ '@babel/runtime': 7.28.6
'@types/react': 19.2.2
invariant: 2.2.4
react: 18.3.1