diff --git a/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts b/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts index ef1eefb176..17ab49fe69 100644 --- a/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +++ b/packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts @@ -60,11 +60,14 @@ export function serializeInlineContentExternalHTML< for (const node of nodes) { // Check if this is a custom inline content node with toExternalHTML - if (editor.schema.inlineContentSchema[node.type.name]) { + if ( + node.type.name !== "text" && + editor.schema.inlineContentSchema[node.type.name] + ) { const inlineContentImplementation = editor.schema.inlineContentSpecs[node.type.name].implementation; - if (inlineContentImplementation?.toExternalHTML) { + if (inlineContentImplementation) { // Convert the node to inline content format const inlineContent = nodeToCustomInlineContent( node, @@ -72,11 +75,23 @@ export function serializeInlineContentExternalHTML< editor.schema.styleSchema, ); - // Use the custom toExternalHTML method - const output = inlineContentImplementation.toExternalHTML( - inlineContent as any, - editor as any, - ); + // Use the custom toExternalHTML method or fallback to `render` + const output = inlineContentImplementation.toExternalHTML + ? inlineContentImplementation.toExternalHTML( + inlineContent as any, + editor as any, + ) + : inlineContentImplementation.render.call( + { + renderType: "dom", + props: undefined, + }, + inlineContent as any, + () => { + // No-op + }, + editor as any, + ); if (output) { fragment.appendChild(output.dom); @@ -93,14 +108,40 @@ export function serializeInlineContentExternalHTML< continue; } } - } + } else if (node.type.name === "text") { + // We serialize text nodes manually as we need to serialize the styles/ + // marks using `styleSpec.implementation.render`. When left up to + // ProseMirror, it'll use `toDOM` which is incorrect. + let dom: globalThis.Node | Text = document.createTextNode( + node.textContent, + ); + // Reverse the order of marks to maintain the correct priority. + for (const mark of node.marks.toReversed()) { + if (mark.type.name in editor.schema.styleSpecs) { + const newDom = ( + editor.schema.styleSpecs[mark.type.name].implementation + .toExternalHTML ?? + editor.schema.styleSpecs[mark.type.name].implementation.render + )(mark.attrs["stringValue"], editor); + newDom.contentDOM!.appendChild(dom); + dom = newDom.dom; + } else { + const domOutputSpec = mark.type.spec.toDOM!(mark, true); + const newDom = DOMSerializer.renderSpec(document, domOutputSpec); + newDom.contentDOM!.appendChild(dom); + dom = newDom.dom; + } + } - // Fall back to default serialization for this node - const nodeFragment = serializer.serializeFragment( - Fragment.from([node]), - options, - ); - fragment.appendChild(nodeFragment); + fragment.appendChild(dom); + } else { + // Fall back to default serialization for this node + const nodeFragment = serializer.serializeFragment( + Fragment.from([node]), + options, + ); + fragment.appendChild(nodeFragment); + } } if ( diff --git a/packages/core/src/api/exporters/html/util/serializeBlocksInternalHTML.ts b/packages/core/src/api/exporters/html/util/serializeBlocksInternalHTML.ts index bb158ad68c..bc39d70008 100644 --- a/packages/core/src/api/exporters/html/util/serializeBlocksInternalHTML.ts +++ b/packages/core/src/api/exporters/html/util/serializeBlocksInternalHTML.ts @@ -48,7 +48,6 @@ export function serializeInlineContentInternalHTML< // Check if this is a custom inline content node with toExternalHTML if ( node.type.name !== "text" && - node.type.name !== "link" && editor.schema.inlineContentSchema[node.type.name] ) { const inlineContentImplementation = @@ -90,14 +89,38 @@ export function serializeInlineContentInternalHTML< continue; } } - } + } else if (node.type.name === "text") { + // We serialize text nodes manually as we need to serialize the styles/ + // marks using `styleSpec.implementation.render`. When left up to + // ProseMirror, it'll use `toDOM` which is incorrect. + let dom: globalThis.Node | Text = document.createTextNode( + node.textContent, + ); + // Reverse the order of marks to maintain the correct priority. + for (const mark of node.marks.toReversed()) { + if (mark.type.name in editor.schema.styleSpecs) { + const newDom = editor.schema.styleSpecs[ + mark.type.name + ].implementation.render(mark.attrs["stringValue"], editor); + newDom.contentDOM!.appendChild(dom); + dom = newDom.dom; + } else { + const domOutputSpec = mark.type.spec.toDOM!(mark, true); + const newDom = DOMSerializer.renderSpec(document, domOutputSpec); + newDom.contentDOM!.appendChild(dom); + dom = newDom.dom; + } + } - // Fall back to default serialization for this node - const nodeFragment = serializer.serializeFragment( - Fragment.from([node]), - options, - ); - fragment.appendChild(nodeFragment); + fragment.appendChild(dom); + } else { + // Fall back to default serialization for this node + const nodeFragment = serializer.serializeFragment( + Fragment.from([node]), + options, + ); + fragment.appendChild(nodeFragment); + } } return fragment; diff --git a/packages/core/src/blocks/defaultBlocks.ts b/packages/core/src/blocks/defaultBlocks.ts index 916da1a7fe..a2d01b92df 100644 --- a/packages/core/src/blocks/defaultBlocks.ts +++ b/packages/core/src/blocks/defaultBlocks.ts @@ -16,9 +16,8 @@ import { createQuoteBlockSpec, createToggleListItemBlockSpec, createVideoBlockSpec, + defaultProps, } from "./index.js"; -import { BackgroundColor } from "../extensions/BackgroundColor/BackgroundColorMark.js"; -import { TextColor } from "../extensions/TextColor/TextColorMark.js"; import { BlockNoDefaults, BlockSchema, @@ -27,11 +26,13 @@ import { PartialBlockNoDefaults, StyleSchema, StyleSpecs, + createStyleSpec, createStyleSpecFromTipTapMark, getInlineContentSchemaFromSpecs, getStyleSchemaFromSpecs, } from "../schema/index.js"; import { createTableBlockSpec } from "./Table/block.js"; +import { COLORS_DEFAULT } from "../editor/defaultColors.js"; export const defaultBlockSpecs = { audio: createAudioBlockSpec(), @@ -56,6 +57,78 @@ export type _DefaultBlockSchema = { }; export type DefaultBlockSchema = _DefaultBlockSchema; +const TextColor = createStyleSpec( + { + type: "textColor", + propSchema: "string", + }, + { + render: () => { + const span = document.createElement("span"); + + return { + dom: span, + contentDOM: span, + }; + }, + toExternalHTML: (value) => { + const span = document.createElement("span"); + if (value !== defaultProps.textColor.default) { + span.style.color = + value in COLORS_DEFAULT ? COLORS_DEFAULT[value].text : value; + } + + return { + dom: span, + contentDOM: span, + }; + }, + parse: (element) => { + if (element.tagName === "SPAN" && element.style.color) { + return element.style.color; + } + + return undefined; + }, + }, +); + +const BackgroundColor = createStyleSpec( + { + type: "backgroundColor", + propSchema: "string", + }, + { + render: () => { + const span = document.createElement("span"); + + return { + dom: span, + contentDOM: span, + }; + }, + toExternalHTML: (value) => { + const span = document.createElement("span"); + if (value !== defaultProps.backgroundColor.default) { + span.style.backgroundColor = + value in COLORS_DEFAULT ? COLORS_DEFAULT[value].background : value; + } + + return { + dom: span, + contentDOM: span, + }; + }, + parse: (element) => { + if (element.tagName === "SPAN" && element.style.backgroundColor) { + return element.style.backgroundColor; + } + + return undefined; + }, + }, +); + export const defaultStyleSpecs = { bold: createStyleSpecFromTipTapMark(Bold, "boolean"), italic: createStyleSpecFromTipTapMark(Italic, "boolean"), diff --git a/packages/core/src/blocks/defaultProps.ts b/packages/core/src/blocks/defaultProps.ts index 46c5417a85..5d55d21d35 100644 --- a/packages/core/src/blocks/defaultProps.ts +++ b/packages/core/src/blocks/defaultProps.ts @@ -92,20 +92,10 @@ export const getBackgroundColorAttribute = ( default: defaultProps.backgroundColor.default, parseHTML: (element) => { if (element.hasAttribute("data-background-color")) { - return element.getAttribute("data-background-color"); + return element.getAttribute("data-background-color")!; } if (element.style.backgroundColor) { - // Check if `element.style.backgroundColor` matches the string: - // `var(--blocknote-background-)`. If it does, return the color - // name only. Otherwise, return `element.style.backgroundColor`. - const match = element.style.backgroundColor.match( - /var\(--blocknote-background-(.+)\)/, - ); - if (match) { - return match[1]; - } - return element.style.backgroundColor; } @@ -128,18 +118,10 @@ export const getTextColorAttribute = ( default: defaultProps.textColor.default, parseHTML: (element) => { if (element.hasAttribute("data-text-color")) { - return element.getAttribute("data-text-color"); + return element.getAttribute("data-text-color")!; } if (element.style.color) { - // Check if `element.style.color` matches the string: - // `var(--blocknote-text-)`. If it does, return the color name - // only. Otherwise, return `element.style.color`. - const match = element.style.color.match(/var\(--blocknote-text-(.+)\)/); - if (match) { - return match[1]; - } - return element.style.color; } @@ -149,6 +131,7 @@ export const getTextColorAttribute = ( if (attributes[attributeName] === defaultProps.textColor.default) { return {}; } + return { "data-text-color": attributes[attributeName], }; @@ -174,6 +157,7 @@ export const getTextAlignmentAttribute = ( if (attributes[attributeName] === defaultProps.textAlignment.default) { return {}; } + return { "data-text-alignment": attributes[attributeName], }; diff --git a/packages/core/src/editor/Block.css b/packages/core/src/editor/Block.css index 6ed5361621..f882ba0559 100644 --- a/packages/core/src/editor/Block.css +++ b/packages/core/src/editor/Block.css @@ -559,92 +559,110 @@ NESTED BLOCKS /* TODO: should this be here? */ /* TEXT COLORS */ +[data-style-type="textColor"][data-value="gray"], [data-text-color="gray"], .bn-block:has(> .bn-block-content[data-text-color="gray"]) { color: #9b9a97; } +[data-style-type="textColor"][data-value="brown"], [data-text-color="brown"], .bn-block:has(> .bn-block-content[data-text-color="brown"]) { color: #64473a; } +[data-style-type="textColor"][data-value="red"], [data-text-color="red"], .bn-block:has(> .bn-block-content[data-text-color="red"]) { color: #e03e3e; } +[data-style-type="textColor"][data-value="orange"], [data-text-color="orange"], .bn-block:has(> .bn-block-content[data-text-color="orange"]) { color: #d9730d; } +[data-style-type="textColor"][data-value="yellow"], [data-text-color="yellow"], .bn-block:has(> .bn-block-content[data-text-color="yellow"]) { color: #dfab01; } +[data-style-type="textColor"][data-value="green"], [data-text-color="green"], .bn-block:has(> .bn-block-content[data-text-color="green"]) { color: #4d6461; } +[data-style-type="textColor"][data-value="blue"], [data-text-color="blue"], .bn-block:has(> .bn-block-content[data-text-color="blue"]) { color: #0b6e99; } +[data-style-type="textColor"][data-value="purple"], [data-text-color="purple"], .bn-block:has(> .bn-block-content[data-text-color="purple"]) { color: #6940a5; } +[data-style-type="textColor"][data-value="pink"], [data-text-color="pink"], .bn-block:has(> .bn-block-content[data-text-color="pink"]) { color: #ad1a72; } /* BACKGROUND COLORS */ +[data-style-type="backgroundColor"][data-value="gray"], [data-background-color="gray"], .bn-block:has(> .bn-block-content[data-background-color="gray"]) { background-color: #ebeced; } +[data-style-type="backgroundColor"][data-value="brown"], [data-background-color="brown"], .bn-block:has(> .bn-block-content[data-background-color="brown"]) { background-color: #e9e5e3; } +[data-style-type="backgroundColor"][data-value="red"], [data-background-color="red"], .bn-block:has(> .bn-block-content[data-background-color="red"]) { background-color: #fbe4e4; } +[data-style-type="backgroundColor"][data-value="orange"], [data-background-color="orange"], .bn-block:has(> .bn-block-content[data-background-color="orange"]) { background-color: #f6e9d9; } +[data-style-type="backgroundColor"][data-value="yellow"], [data-background-color="yellow"], .bn-block:has(> .bn-block-content[data-background-color="yellow"]) { background-color: #fbf3db; } +[data-style-type="backgroundColor"][data-value="green"], [data-background-color="green"], .bn-block:has(> .bn-block-content[data-background-color="green"]) { background-color: #ddedea; } +[data-style-type="backgroundColor"][data-value="blue"], [data-background-color="blue"], .bn-block:has(> .bn-block-content[data-background-color="blue"]) { background-color: #ddebf1; } +[data-style-type="backgroundColor"][data-value="purple"], [data-background-color="purple"], .bn-block:has(> .bn-block-content[data-background-color="purple"]) { background-color: #eae4f2; } +[data-style-type="backgroundColor"][data-value="pink"], [data-background-color="pink"], .bn-block:has(> .bn-block-content[data-background-color="pink"]) { background-color: #f4dfeb; diff --git a/packages/core/src/schema/styles/createSpec.ts b/packages/core/src/schema/styles/createSpec.ts index 8051f9f833..4bd83b3ae0 100644 --- a/packages/core/src/schema/styles/createSpec.ts +++ b/packages/core/src/schema/styles/createSpec.ts @@ -1,7 +1,6 @@ import { Mark } from "@tiptap/core"; -import { ParseRule } from "@tiptap/pm/model"; -import { UnreachableCaseError } from "../../util/typescript.js"; +import { ParseRule, TagParseRule } from "@tiptap/pm/model"; import { addStyleAttributes, createInternalStyleSpec, @@ -10,21 +9,26 @@ import { import { StyleConfig, StyleSpec } from "./types.js"; export type CustomStyleImplementation = { - render: T["propSchema"] extends "boolean" - ? () => { - dom: HTMLElement; - contentDOM?: HTMLElement; - } - : (value: string) => { - dom: HTMLElement; - contentDOM?: HTMLElement; - }; + render: (value: T["propSchema"] extends "boolean" ? undefined : string) => { + dom: HTMLElement; + contentDOM?: HTMLElement; + }; + toExternalHTML?: ( + value: T["propSchema"] extends "boolean" ? undefined : string, + ) => { + dom: HTMLElement; + contentDOM?: HTMLElement; + }; + parse?: ( + element: HTMLElement, + ) => (T["propSchema"] extends "boolean" ? true : string) | undefined; }; -// TODO: support serialization - -export function getStyleParseRules(config: StyleConfig): ParseRule[] { - return [ +export function getStyleParseRules( + config: T, + customParseFunction?: CustomStyleImplementation["parse"], +): ParseRule[] { + const rules: TagParseRule[] = [ { tag: `[data-style-type="${config.type}"]`, contentElement: (element) => { @@ -38,9 +42,29 @@ export function getStyleParseRules(config: StyleConfig): ParseRule[] { }, }, ]; + + if (customParseFunction) { + rules.push({ + tag: "*", + getAttrs(node: string | HTMLElement) { + if (typeof node === "string") { + return false; + } + + const stringValue = customParseFunction?.(node); + + if (stringValue === undefined) { + return false; + } + + return { stringValue }; + }, + }); + } + return rules; } -export function createStyleSpec( +export function createStyleSpec( styleConfig: T, styleImplementation: CustomStyleImplementation, ): StyleSpec { @@ -52,22 +76,13 @@ export function createStyleSpec( }, parseHTML() { - return getStyleParseRules(styleConfig); + return getStyleParseRules(styleConfig, styleImplementation.parse); }, renderHTML({ mark }) { - let renderResult: { - dom: HTMLElement; - contentDOM?: HTMLElement; - }; - - if (styleConfig.propSchema === "boolean") { - renderResult = styleImplementation.render(mark.attrs.stringValue); - } else if (styleConfig.propSchema === "string") { - renderResult = styleImplementation.render(mark.attrs.stringValue); - } else { - throw new UnreachableCaseError(styleConfig.propSchema); - } + const renderResult = ( + styleImplementation.toExternalHTML || styleImplementation.render + )(mark.attrs.stringValue); return addStyleAttributes( renderResult, @@ -76,9 +91,44 @@ export function createStyleSpec( styleConfig.propSchema, ); }, + + addMarkView() { + return ({ mark }) => { + const renderResult = styleImplementation.render(mark.attrs.stringValue); + + return addStyleAttributes( + renderResult, + styleConfig.type, + mark.attrs.stringValue, + styleConfig.propSchema, + ); + }; + }, }); return createInternalStyleSpec(styleConfig, { mark, + render: (value) => { + const renderResult = styleImplementation.render(value as any); + + return addStyleAttributes( + renderResult, + styleConfig.type, + value, + styleConfig.propSchema, + ); + }, + toExternalHTML: (value) => { + const renderResult = ( + styleImplementation.toExternalHTML || styleImplementation.render + )(value as any); + + return addStyleAttributes( + renderResult, + styleConfig.type, + value, + styleConfig.propSchema, + ); + }, }); } diff --git a/packages/core/src/schema/styles/internal.ts b/packages/core/src/schema/styles/internal.ts index 0446db701b..9d5711bd6e 100644 --- a/packages/core/src/schema/styles/internal.ts +++ b/packages/core/src/schema/styles/internal.ts @@ -1,4 +1,5 @@ import { Attributes, Mark } from "@tiptap/core"; +import { DOMSerializer } from "@tiptap/pm/model"; import { StyleConfig, StyleImplementation, @@ -66,7 +67,7 @@ export function addStyleAttributes< // config and implementation that conform to the type of Config export function createInternalStyleSpec( config: T, - implementation: StyleImplementation, + implementation: StyleImplementation, ) { return { config, @@ -85,6 +86,64 @@ export function createStyleSpecFromTipTapMark< }, { mark, + render(value, editor) { + const toDOM = editor.pmSchema.marks[mark.name].spec.toDOM; + + if (toDOM === undefined) { + throw new Error( + "This block has no default HTML serialization as its corresponding TipTap node doesn't implement `renderHTML`.", + ); + } + + const markInstance = editor.pmSchema.mark(mark.name, { + stringValue: value, + }); + + const renderSpec = DOMSerializer.renderSpec( + document, + toDOM(markInstance, true), + ); + + if (typeof renderSpec !== "object" || !("dom" in renderSpec)) { + throw new Error( + "Cannot use this block's default HTML serialization as its corresponding TipTap mark's `renderHTML` function does not return an object with the `dom` property.", + ); + } + + return renderSpec as { + dom: HTMLElement; + contentDOM?: HTMLElement; + }; + }, + toExternalHTML(value, editor) { + const toDOM = editor.pmSchema.marks[mark.name].spec.toDOM; + + if (toDOM === undefined) { + throw new Error( + "This block has no default HTML serialization as its corresponding TipTap node doesn't implement `renderHTML`.", + ); + } + + const markInstance = editor.pmSchema.mark(mark.name, { + stringValue: value, + }); + + const renderSpec = DOMSerializer.renderSpec( + document, + toDOM(markInstance, true), + ); + + if (typeof renderSpec !== "object" || !("dom" in renderSpec)) { + throw new Error( + "Cannot use this block's default HTML serialization as its corresponding TipTap mark's `renderHTML` function does not return an object with the `dom` property.", + ); + } + + return renderSpec as { + dom: HTMLElement; + contentDOM?: HTMLElement; + }; + }, }, ); } diff --git a/packages/core/src/schema/styles/types.ts b/packages/core/src/schema/styles/types.ts index 1a44e9ffd3..3be7cb9d6e 100644 --- a/packages/core/src/schema/styles/types.ts +++ b/packages/core/src/schema/styles/types.ts @@ -1,4 +1,5 @@ import { Mark } from "@tiptap/core"; +import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js"; export type StylePropSchema = "boolean" | "string"; // TODO: use PropSchema as name? Use objects as type similar to blocks? @@ -11,15 +12,29 @@ export type StyleConfig = { // StyleImplementation contains the "implementation" info about a Style element. // Currently, the implementation is always a TipTap Mark -export type StyleImplementation = { +export type StyleImplementation = { mark: Mark; + render: ( + value: T["propSchema"] extends "boolean" ? undefined : string, + editor: BlockNoteEditor, + ) => { + dom: HTMLElement; + contentDOM?: HTMLElement; + }; + toExternalHTML?: ( + value: T["propSchema"] extends "boolean" ? undefined : string, + editor: BlockNoteEditor, + ) => { + dom: HTMLElement; + contentDOM?: HTMLElement; + }; }; // Container for both the config and implementation of a Style, // and the type of `implementation` is based on that of the config export type StyleSpec = { config: T; - implementation: StyleImplementation; + implementation: StyleImplementation; }; // A Schema contains all the types (Configs) supported in an editor diff --git a/packages/react/src/editor/styles.css b/packages/react/src/editor/styles.css index 37044b5df6..eb61a59fa2 100644 --- a/packages/react/src/editor/styles.css +++ b/packages/react/src/editor/styles.css @@ -140,74 +140,92 @@ } /* Highlight color styling */ +[data-style-type="textColor"][data-value="gray"], [data-text-color="gray"] { color: var(--bn-colors-highlights-gray-text); } +[data-style-type="textColor"][data-value="brown"], [data-text-color="brown"] { color: var(--bn-colors-highlights-brown-text); } +[data-style-type="textColor"][data-value="red"], [data-text-color="red"] { color: var(--bn-colors-highlights-red-text); } +[data-style-type="textColor"][data-value="orange"], [data-text-color="orange"] { color: var(--bn-colors-highlights-orange-text); } +[data-style-type="textColor"][data-value="yellow"], [data-text-color="yellow"] { color: var(--bn-colors-highlights-yellow-text); } +[data-style-type="textColor"][data-value="green"], [data-text-color="green"] { color: var(--bn-colors-highlights-green-text); } +[data-style-type="textColor"][data-value="blue"], [data-text-color="blue"] { color: var(--bn-colors-highlights-blue-text); } +[data-style-type="textColor"][data-value="purple"], [data-text-color="purple"] { color: var(--bn-colors-highlights-purple-text); } +[data-style-type="textColor"][data-value="pink"], [data-text-color="pink"] { color: var(--bn-colors-highlights-pink-text); } +[data-style-type="backgroundColor"][data-value="gray"], [data-background-color="gray"] { background-color: var(--bn-colors-highlights-gray-background); } +[data-style-type="backgroundColor"][data-value="brown"], [data-background-color="brown"] { background-color: var(--bn-colors-highlights-brown-background); } +[data-style-type="backgroundColor"][data-value="red"], [data-background-color="red"] { background-color: var(--bn-colors-highlights-red-background); } +[data-style-type="backgroundColor"][data-value="orange"], [data-background-color="orange"] { background-color: var(--bn-colors-highlights-orange-background); } +[data-style-type="backgroundColor"][data-value="yellow"], [data-background-color="yellow"] { background-color: var(--bn-colors-highlights-yellow-background); } +[data-style-type="backgroundColor"][data-value="green"], [data-background-color="green"] { background-color: var(--bn-colors-highlights-green-background); } +[data-style-type="backgroundColor"][data-value="blue"], [data-background-color="blue"] { background-color: var(--bn-colors-highlights-blue-background); } +[data-style-type="backgroundColor"][data-value="purple"], [data-background-color="purple"] { background-color: var(--bn-colors-highlights-purple-background); } +[data-style-type="backgroundColor"][data-value="pink"], [data-background-color="pink"] { background-color: var(--bn-colors-highlights-pink-background); } diff --git a/packages/react/src/schema/ReactStyleSpec.tsx b/packages/react/src/schema/ReactStyleSpec.tsx index 53b9c85eba..b83f727319 100644 --- a/packages/react/src/schema/ReactStyleSpec.tsx +++ b/packages/react/src/schema/ReactStyleSpec.tsx @@ -117,5 +117,55 @@ export function createReactStyleSpec( return createInternalStyleSpec(styleConfig, { mark, + render(value, editor) { + const Content = styleImplementation.render; + const output = renderToDOMSpec( + (ref) => ( + { + ref(element); + if (element) { + element.dataset.editable = ""; + } + }} + /> + ), + editor, + ); + + return addStyleAttributes( + output, + styleConfig.type, + value, + styleConfig.propSchema, + ); + }, + toExternalHTML(value, editor) { + const Content = styleImplementation.render; + const output = renderToDOMSpec( + (ref) => ( + { + ref(element); + if (element) { + element.dataset.editable = ""; + } + }} + /> + ), + editor, + ); + + return addStyleAttributes( + output, + styleConfig.type, + value, + styleConfig.propSchema, + ); + }, }); } diff --git a/packages/xl-ai/src/api/formats/html-blocks/htmlBlocks.test.ts b/packages/xl-ai/src/api/formats/html-blocks/htmlBlocks.test.ts index aafb57b6ad..20e7914c69 100644 --- a/packages/xl-ai/src/api/formats/html-blocks/htmlBlocks.test.ts +++ b/packages/xl-ai/src/api/formats/html-blocks/htmlBlocks.test.ts @@ -120,21 +120,15 @@ describe("Models", () => { describe(`${params.model.provider}/${params.model.modelId} (${ params.stream ? "streaming" : "non-streaming" })`, () => { - generateSharedTestCases( - (editor, options) => - doLLMRequest(editor, { - ...options, - dataFormat: htmlBlockLLMFormat, - model: params.model, - maxRetries: 0, - stream: params.stream, - withDelays: false, - }), - // TODO: remove when matthew's parsing PR is merged - { - textAlignment: true, - blockColor: true, - }, + generateSharedTestCases((editor, options) => + doLLMRequest(editor, { + ...options, + dataFormat: htmlBlockLLMFormat, + model: params.model, + maxRetries: 0, + stream: params.stream, + withDelays: false, + }), ); }); } diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/paragraph/styled.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/paragraph/styled.html index 699ad7f460..b0e77ba748 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/paragraph/styled.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/paragraph/styled.html @@ -10,10 +10,10 @@ >

Plain - Red Text - Blue Background - - Mixed Colors + Red Text + Blue Background + + Mixed Colors

diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/paragraph/styled.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/paragraph/styled.html index fd10eacf1a..637d51b2e0 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/html/paragraph/styled.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/paragraph/styled.html @@ -5,9 +5,29 @@ data-background-color="pink" > Plain - Red Text - Blue Background - - Mixed Colors + Red Text + Blue Background + + Mixed Colors

\ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts b/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts index 0c89c74541..7a6781b938 100644 --- a/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts +++ b/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts @@ -870,14 +870,14 @@ With Hard Break

{ testCase: { name: "textColorStyle", - content: `

Blue Text Blue Text

`, + content: `

Blue Text Blue Text

`, }, executeTest: testParseHTML, }, { testCase: { name: "backgroundColorStyle", - content: `

Blue Background Blue Background

`, + content: `

Blue Background Blue Background

`, }, executeTest: testParseHTML, }, diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/customParagraph/styled.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/customParagraph/styled.html index 654ebfd316..df960b11f1 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/customParagraph/styled.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/customParagraph/styled.html @@ -12,10 +12,10 @@ >

Plain - Red Text - Blue Background - - Mixed Colors + Red Text + Blue Background + + Mixed Colors

diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/simpleCustomParagraph/styled.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/simpleCustomParagraph/styled.html index 6a0a83a4cb..ef3d75ab28 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/simpleCustomParagraph/styled.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/simpleCustomParagraph/styled.html @@ -12,10 +12,10 @@ >

Plain - Red Text - Blue Background - - Mixed Colors + Red Text + Blue Background + + Mixed Colors

diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/styled.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/styled.html index c2a35ebaee..87efb71bf5 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/styled.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/simpleCustomParagraph/styled.html @@ -5,9 +5,29 @@ data-background-color="pink" > Plain - Red Text - Blue Background - - Mixed Colors + Red Text + Blue Background + + Mixed Colors

\ No newline at end of file