Skip to content

Commit

Permalink
WIP: TEST
Browse files Browse the repository at this point in the history
  • Loading branch information
juliaroldi committed Feb 13, 2025
1 parent 9022291 commit 973592a
Show file tree
Hide file tree
Showing 43 changed files with 2,141 additions and 139 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ API overrides in Editor options when creating the editor.
`roosterjs-content-model-api` provides APIs for scenario-based operations triggered by
user interaction.

`roosterjs-content-model-markdown` provides API to transform Markdown language in Content Model objects.

## Plugins

Rooster supports plugins. You can use built-in plugins or build your own.
Expand Down
8 changes: 4 additions & 4 deletions demo/scripts/controlsV2/mainPane/MainPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { getDarkColor } from 'roosterjs-color-utils';
import { getPresetModelById } from '../sidePane/presets/allPresets/allPresets';
import { getTabs, tabNames } from '../tabs/getTabs';
import { getTheme } from '../theme/themes';
import { MarkdownPanePlugin } from '../sidePane/MarkdownPane/MarkdownPanePlugin';
import { OptionState, UrlPlaceholder } from '../sidePane/editorOptions/OptionState';
import { PlainTextPlugin } from '../sidePane/plainText/PlainTextPlugin';
import { popoutButton } from '../demoButtons/popoutButton';
import { PresetPlugin } from '../sidePane/presets/PresetPlugin';
import { registerWindowForCss, unregisterWindowForCss } from '../../utils/cssMonitor';
Expand Down Expand Up @@ -103,7 +103,7 @@ export class MainPane extends React.Component<{}, MainPaneState> {
private samplePickerPlugin: SamplePickerPlugin;
private snapshots: Snapshots;
private imageEditPlugin: ImageEditPlugin;
private plainTextPlugin: PlainTextPlugin;
private markdownPanePlugin: MarkdownPanePlugin;

protected sidePane = React.createRef<SidePane>();
protected updateContentPlugin: UpdateContentPlugin;
Expand Down Expand Up @@ -142,7 +142,7 @@ export class MainPane extends React.Component<{}, MainPaneState> {
this.formatPainterPlugin = new FormatPainterPlugin();
this.samplePickerPlugin = new SamplePickerPlugin();
this.imageEditPlugin = new ImageEditPlugin();
this.plainTextPlugin = new PlainTextPlugin();
this.markdownPanePlugin = new MarkdownPanePlugin();

this.state = {
showSidePane: window.location.hash != '',
Expand Down Expand Up @@ -484,7 +484,7 @@ export class MainPane extends React.Component<{}, MainPaneState> {
this.snapshotPlugin,
this.contentModelPanePlugin,
this.presetPlugin,
this.plainTextPlugin,
this.markdownPanePlugin,
];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import * as React from 'react';
import { convertMarkdownToContentModel } from 'roosterjs-content-model-api';
import { convertMarkdownToContentModel } from 'roosterjs-content-model-markdown';
import { createBr, createParagraph, createSelectionMarker } from 'roosterjs-content-model-dom';
import { PlainTextPaneProps } from './PlainTextPlugin';
import { MarkdownPaneProps } from './MarkdownPanePlugin';

const styles = require('./PlainTextPane.scss');
const styles = require('./MarkdownPane.scss');

export default class PlainTextPane extends React.Component<PlainTextPaneProps> {
export default class MarkdownPane extends React.Component<MarkdownPaneProps> {
private html = React.createRef<HTMLTextAreaElement>();

constructor(props: PlainTextPaneProps) {
constructor(props: MarkdownPaneProps) {
super(props);
}

Expand Down Expand Up @@ -38,7 +38,7 @@ export default class PlainTextPane extends React.Component<PlainTextPaneProps> {
render() {
return (
<div className={styles.container}>
<p>Convert plain text to content model</p>
<p>Convert Markdown to content model</p>
<textarea
className={styles.textArea}
title="Plain Text Editor"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import PlainTextPane from './PlainTextPane';
import MarkdownPane from './MarkdownPane';
import { IEditor, PluginEvent } from 'roosterjs-content-model-types';
import { SidePaneElementProps } from '../SidePaneElement';
import { SidePanePluginImpl } from '../SidePanePluginImpl';

export interface PlainTextPaneProps extends SidePaneElementProps {
export interface MarkdownPaneProps extends SidePaneElementProps {
getEditor: () => IEditor;
}

export class PlainTextPlugin extends SidePanePluginImpl<PlainTextPane, PlainTextPaneProps> {
export class MarkdownPanePlugin extends SidePanePluginImpl<MarkdownPane, MarkdownPaneProps> {
constructor() {
super(PlainTextPane, 'plainText', 'Plain Text');
super(MarkdownPane, 'plainText', 'Markdown Editor');
}

onPluginEvent(e: PluginEvent) {}

getComponentProps(base: PlainTextPaneProps) {
getComponentProps(base: MarkdownPaneProps) {
return {
...base,
getEditor: () => {
Expand Down
3 changes: 3 additions & 0 deletions demo/scripts/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
"roosterjs-content-model-api": ["packages/roosterjs-content-model-api/lib/index"],
"roosterjs-content-model-plugins": [
"packages/roosterjs-content-model-plugins/lib/index"
],
"roosterjs-content-model-markdown": [
"packages/roosterjs-content-model-markdown/lib/index"
]
}
},
Expand Down
1 change: 0 additions & 1 deletion packages/roosterjs-content-model-api/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ export { formatParagraphWithContentModel } from './publicApi/utils/formatParagra
export { formatSegmentWithContentModel } from './publicApi/utils/formatSegmentWithContentModel';
export { formatTextSegmentBeforeSelectionMarker } from './publicApi/utils/formatTextSegmentBeforeSelectionMarker';
export { formatInsertPointWithContentModel } from './publicApi/utils/formatInsertPointWithContentModel';
export { convertMarkdownToContentModel } from './publicApi/markdown/convertMarkdownToContentModel';

export { setListType } from './modelApi/list/setListType';
export { setModelListStyle } from './modelApi/list/setModelListStyle';
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

1 change: 1 addition & 0 deletions packages/roosterjs-content-model-markdown/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { convertMarkdownToContentModel } from './markdownToModel/convertMarkdownToContentModel';
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const linkRegex = /\[([^\[]+)\]\((https?:\/\/[^\)]+)\)/g;
/**
* @internal
*/
export function applyLink(textSegment: ContentModelText): ContentModelText | undefined {
export function applyLink(textSegment: ContentModelText): ContentModelText {
const text = textSegment.text;
const markdownLink = linkRegex.exec(text);
if (markdownLink) {
if (markdownLink && markdownLink.length == 3) {
textSegment.text = markdownLink[1];
textSegment.link = {
dataset: {},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { applyHeading } from './applyHeading';
import { applyLink } from './applyLink';
import { applyTextFormatting } from './applyTextFormatting';
import { createBr, createText } from 'roosterjs-content-model-dom';
import { splitLinkAndImages } from './splitLinksAndImages';
import { transformImage } from './transformImage';
import { createImageSegment } from '../creators/createImageSegment';
import { splitLinkAndImages } from '../utils/splitLinksAndImages';

import type {
ContentModelParagraph,
ContentModelParagraphDecorator,
Expand All @@ -11,7 +13,7 @@ import type {
/**
* @internal
*/
export function adjustSegmentFormatting(
export function applySegmentFormatting(
text: string,
paragraph: ContentModelParagraph,
decorator?: ContentModelParagraphDecorator
Expand All @@ -23,13 +25,14 @@ export function adjustSegmentFormatting(
const textSegments = splitLinkAndImages(text);
for (const segment of textSegments) {
const formattedSegment = createText(segment);
const image = transformImage(formattedSegment);
const image = createImageSegment(formattedSegment);
if (image) {
paragraph.segments.push(image);
} else {
applyHeading(formattedSegment, decorator);
applyLink(formattedSegment);
paragraph.segments.push(formattedSegment);
const formattedSegments = applyTextFormatting(formattedSegment);
paragraph.segments.push(...formattedSegments);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { createText } from 'roosterjs-content-model-dom';
import type {
ContentModelLink,
ContentModelSegmentFormat,
ContentModelText,
} from 'roosterjs-content-model-types';

const SPLIT_PATTERN = /(\*\*\*.*?\*\*\*|\*\*.*?\*\*|\*.*?\*)/;

/**
* @internal
*/
export function applyTextFormatting(textSegment: ContentModelText) {
const texts = splitSegments(textSegment.text);
const textSegments: ContentModelText[] = [];
for (const text of texts) {
textSegments.push(createFormattedSegment(text, textSegment.format, textSegment.link));
}
return textSegments;
}

function splitSegments(text: string): string[] {
return text.split(SPLIT_PATTERN).filter(s => s.trim().length > 0);
}

function createFormattedSegment(
text: string,
format: ContentModelSegmentFormat,
link?: ContentModelLink
): ContentModelText {
if (text.startsWith('***') && text.endsWith('***')) {
format = { ...format, fontWeight: 'bold', italic: true };
text = text.replace(/\*\*\*/g, '');
text = text + ' ';
} else if (text.startsWith('**') && text.endsWith('**')) {
format = { ...format, fontWeight: 'bold' };
text = text.replace(/\*\*/g, '');
text = text + ' ';
} else if (text.startsWith('*') && text.endsWith('*')) {
format = { ...format, italic: true };
text = text.replace(/\*/g, '');
text = text + ' ';
}

return createText(text, format, link);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { markdownProcessor } from '../../modelApi/markdown/markdownProcessor';
import { markdownProcessor } from './processor/markdownProcessor';
import { mergeModel } from 'roosterjs-content-model-dom';
import type { IEditor } from 'roosterjs-content-model-types';

Expand All @@ -8,6 +8,9 @@ import type { IEditor } from 'roosterjs-content-model-types';
export function convertMarkdownToContentModel(editor: IEditor, text: string) {
editor.formatContentModel(
(model, context) => {
if (text.trim() === '') {
return false;
}
const markdownInContentModel = markdownProcessor(text);
mergeModel(model, markdownInContentModel, context, {
mergeFormat: 'mergeAll',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { createBlockQuoteFromMarkdown } from './createBlockQuoteFromMarkdown';
import { createListFromMarkdown } from './createListFromMarkdown';
import type {
ContentModelBlockGroup,
ContentModelBlockGroupType,
ContentModelFormatContainer,
ContentModelListItem,
Expand All @@ -19,18 +18,11 @@ const MarkdownBlockGroupType: Record<string, ContentModelBlockGroupType> = {
export function createBlockGroupFromMarkdown(
text: string,
patternName: string,
group?: ContentModelBlockGroup
group?: ContentModelFormatContainer
): ContentModelFormatContainer | ContentModelListItem {
if (MarkdownBlockGroupType[patternName] === 'ListItem') {
return createListFromMarkdown(
text,
patternName,
group?.blockGroupType === 'ListItem' ? group : undefined
);
return createListFromMarkdown(text, patternName);
} else {
return createBlockQuoteFromMarkdown(
text,
group?.blockGroupType === 'FormatContainer' ? group : undefined
);
return createBlockQuoteFromMarkdown(text, group);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const imageRegex = /\!\[([^\[]+)\]\((https?:\/\/[^\)]+)\)/g;
/**
* @internal
*/
export function transformImage(textSegment: ContentModelText): ContentModelImage | undefined {
export function createImageSegment(textSegment: ContentModelText): ContentModelImage | undefined {
const text = textSegment.text;
const markdownImage = imageRegex.exec(text);
if (markdownImage) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { createListItem, createListLevel } from 'roosterjs-content-model-dom';
import { createParagraphFromMarkdown } from './createParagraphFromMarkdown';
import type { ContentModelListItem } from 'roosterjs-content-model-types';

/**
* @internal
*/
export function createListFromMarkdown(text: string, patternName: string): ContentModelListItem {
const listType = patternName === 'ordered_list' ? 'OL' : 'UL';
const itemText = text.trim().substring(listType == 'OL' ? 2 : 1);
const paragraph = createParagraphFromMarkdown(itemText.trim());
const levels = createLevels(text, listType);
const listModel = createListItem(levels);
listModel.blocks.push(paragraph);
return listModel;
}

function createLevels(text: string, listType: 'OL' | 'UL') {
const level = createListLevel(listType);
const levels = [level];
if (isSubListItem(text)) {
levels.push(level);
}
return levels;
}

function isSubListItem(item: string): boolean {
return item.startsWith(' ');
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { adjustSegmentFormatting } from '../utils/adjustSegmentFormatting';
import { applySegmentFormatting } from '../appliers/applySegmentFormatting';
import { createParagraph } from 'roosterjs-content-model-dom';
import { getHeadingDecorator } from '../utils/getHeadingDecorator';

import type { ContentModelParagraph } from 'roosterjs-content-model-types';

/**
Expand All @@ -9,7 +10,9 @@ import type { ContentModelParagraph } from 'roosterjs-content-model-types';
export function createParagraphFromMarkdown(text: string): ContentModelParagraph {
const paragraph = createParagraph();
const headingType = getHeadingDecorator(text);
paragraph.decorator = headingType;
adjustSegmentFormatting(text, paragraph, headingType);
if (headingType) {
paragraph.decorator = headingType;
}
applySegmentFormatting(text, paragraph, headingType);
return paragraph;
}
Loading

0 comments on commit 973592a

Please sign in to comment.