From 339f6bf9002e11797fcb2abb0eae7e3ae7e47617 Mon Sep 17 00:00:00 2001 From: Vi Nguyen <36.tuongvi@gmail.com> Date: Mon, 14 Sep 2026 13:24:29 -0700 Subject: [PATCH 1/4] Add JSON Beautifier to side pane --- demo/scripts/controlsV2/mainPane/MainPane.tsx | 4 + .../JsonBeautifier/JsonBeautifierPane.scss | 17 ++++ .../JsonBeautifier/JsonBeautifierPane.tsx | 88 +++++++++++++++++++ .../JsonBeautifierPanePlugin.ts | 26 ++++++ 4 files changed, 135 insertions(+) create mode 100644 demo/scripts/controlsV2/sidePane/JsonBeautifier/JsonBeautifierPane.scss create mode 100644 demo/scripts/controlsV2/sidePane/JsonBeautifier/JsonBeautifierPane.tsx create mode 100644 demo/scripts/controlsV2/sidePane/JsonBeautifier/JsonBeautifierPanePlugin.ts diff --git a/demo/scripts/controlsV2/mainPane/MainPane.tsx b/demo/scripts/controlsV2/mainPane/MainPane.tsx index e8a3945195a0..926b0f67a0d8 100644 --- a/demo/scripts/controlsV2/mainPane/MainPane.tsx +++ b/demo/scripts/controlsV2/mainPane/MainPane.tsx @@ -20,6 +20,7 @@ import { getTabs, tabNames } from '../tabs/getTabs'; import { getTheme } from '../theme/themes'; import { MarkdownPanePlugin } from '../sidePane/MarkdownPane/MarkdownPanePlugin'; import { MarkdownPastePlugin } from 'roosterjs-content-model-markdown'; +import { JsonBeautifierPanePlugin } from '../sidePane/JsonBeautifier/JsonBeautifierPanePlugin'; import { OptionState, UrlPlaceholder } from '../sidePane/editorOptions/OptionState'; import { popoutButton } from '../demoButtons/popoutButton'; import { PresetPlugin } from '../sidePane/presets/PresetPlugin'; @@ -116,6 +117,7 @@ export class MainPane extends React.Component<{}, MainPaneState> { private samplePickerPlugin: SamplePickerPlugin; private snapshots: Snapshots; private markdownPanePlugin: MarkdownPanePlugin; + private jsonBeautifierPanePlugin: JsonBeautifierPanePlugin; private findReplacePlugin: FindReplacePlugin; private findReplaceContext: FindReplaceContext; private imageEditPlugin: ImageEditPlugin | undefined; @@ -157,6 +159,7 @@ export class MainPane extends React.Component<{}, MainPaneState> { this.formatPainterPlugin = new FormatPainterPlugin(); this.samplePickerPlugin = new SamplePickerPlugin(); this.markdownPanePlugin = new MarkdownPanePlugin(); + this.jsonBeautifierPanePlugin = new JsonBeautifierPanePlugin(); this.findReplaceContext = createFindReplaceContext(); this.findReplacePlugin = new FindReplacePlugin(this.findReplaceContext); @@ -552,6 +555,7 @@ export class MainPane extends React.Component<{}, MainPaneState> { this.contentModelPanePlugin, this.presetPlugin, this.markdownPanePlugin, + this.jsonBeautifierPanePlugin, ]; } diff --git a/demo/scripts/controlsV2/sidePane/JsonBeautifier/JsonBeautifierPane.scss b/demo/scripts/controlsV2/sidePane/JsonBeautifier/JsonBeautifierPane.scss new file mode 100644 index 000000000000..dd23ae3b1262 --- /dev/null +++ b/demo/scripts/controlsV2/sidePane/JsonBeautifier/JsonBeautifierPane.scss @@ -0,0 +1,17 @@ +.container { + flex-direction: column; + display: flex; + height: 100%; +} + +.textArea { + flex: 1; + width: 100%; + box-sizing: border-box; + resize: none; +} + +.errorMessage { + color: #a80000; + margin: 4px 0; +} diff --git a/demo/scripts/controlsV2/sidePane/JsonBeautifier/JsonBeautifierPane.tsx b/demo/scripts/controlsV2/sidePane/JsonBeautifier/JsonBeautifierPane.tsx new file mode 100644 index 000000000000..f8e7f6f17fdd --- /dev/null +++ b/demo/scripts/controlsV2/sidePane/JsonBeautifier/JsonBeautifierPane.tsx @@ -0,0 +1,88 @@ +import * as React from 'react'; +import { + createEmptyModel, + createFormatContainer, + createParagraph, + createText, +} from 'roosterjs-content-model-dom'; +import type { JsonBeautifierPaneProps } from './JsonBeautifierPanePlugin'; + +const styles = require('./JsonBeautifierPane.scss'); + +interface JsonBeautifierPaneState { + errorMessage: string; +} + +export default class JsonBeautifierPane extends React.Component< + JsonBeautifierPaneProps, + JsonBeautifierPaneState +> { + private json = React.createRef(); + + constructor(props: JsonBeautifierPaneProps) { + super(props); + this.state = { errorMessage: '' }; + } + + private beautify = () => { + let formattedJson: string; + + try { + formattedJson = JSON.stringify(JSON.parse(this.json.current.value), null, 2); + } catch (error) { + this.setState({ + errorMessage: error instanceof Error ? error.message : 'Invalid JSON', + }); + return; + } + + const container = createFormatContainer('pre'); + const paragraph = createParagraph(true); + + paragraph.segments.push(createText(formattedJson)); + container.blocks.push(paragraph); + this.props.getEditor().formatContentModel(model => { + model.blocks = [container]; + return true; + }); + this.setState({ errorMessage: '' }); + }; + + private clear = () => { + this.json.current.value = ''; + this.setState({ errorMessage: '' }); + }; + + private clearEditor = () => { + const editor = this.props.getEditor(); + editor.formatContentModel(model => { + model.blocks = createEmptyModel().blocks; + return true; + }); + }; + + render() { + return ( +
+

Enter JSON to format and display in the editor.

+