Skip to content

Commit b40bbd4

Browse files
committed
fix(rich-editor): RichMarkdownField falls back to raw text for lossy markdown
Mirror the file editor's safety gate: decide once from the initial value via isRoundTripSafe — round-trip-safe content opens in the WYSIWYG editor, while lossy markdown (raw HTML, footnotes, comments) edits as raw text, so an edit can't silently drop those constructs.
1 parent 769ca85 commit b40bbd4

1 file changed

Lines changed: 43 additions & 5 deletions

File tree

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-field.tsx

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useEffect, useRef, useState } from 'react'
44
import type { JSONContent } from '@tiptap/core'
55
import { EditorContent, useEditor } from '@tiptap/react'
6-
import { chipFieldSurfaceClass } from '@/components/emcn'
6+
import { ChipTextarea, chipFieldSurfaceClass } from '@/components/emcn'
77
import { cn } from '@/lib/core/utils/cn'
88
import { createMarkdownEditorExtensions } from './extensions'
99
import {
@@ -16,6 +16,7 @@ import { useEditorMentions } from './mention'
1616
import { EditorBubbleMenu } from './menus/bubble-menu'
1717
import { LinkHoverCard } from './menus/link-hover-card'
1818
import { normalizeMarkdownContent } from './normalize-content'
19+
import { isRoundTripSafe } from './round-trip-safety'
1920
import '@/components/emcn/components/code/code.css'
2021
import './rich-markdown-editor.css'
2122

@@ -47,11 +48,11 @@ interface RichMarkdownFieldProps {
4748
}
4849

4950
/**
50-
* A controlled, string-valued WYSIWYG markdown editor for modal fields — the file-less sibling of
51-
* {@link RichMarkdownEditor}. It reuses the same TipTap extensions, parser, and menus but owns no file
52-
* loading, autosave, or image upload. Drop it inside a `ChipModalField type='custom'`.
51+
* The WYSIWYG editor for round-trip-safe content (chosen by {@link RichMarkdownField}). The file-less
52+
* sibling of {@link RichMarkdownEditor}'s loaded editor: same TipTap extensions, parser, and menus but
53+
* no file loading, autosave, or image upload.
5354
*/
54-
export function RichMarkdownField({
55+
function LoadedRichMarkdownField({
5556
value,
5657
onChange,
5758
placeholder = "Write something, or press '/' for commands…",
@@ -188,3 +189,40 @@ export function RichMarkdownField({
188189
</div>
189190
)
190191
}
192+
193+
/**
194+
* Raw-text fallback for content the rich editor can't round-trip losslessly — editing the markdown
195+
* source directly so an edit can't silently drop footnotes, raw HTML, or comments.
196+
*/
197+
function RawMarkdownField({
198+
value,
199+
onChange,
200+
placeholder,
201+
disabled = false,
202+
isStreaming = false,
203+
minHeight = 140,
204+
maxHeight = 360,
205+
error = false,
206+
}: RichMarkdownFieldProps) {
207+
return (
208+
<ChipTextarea
209+
value={value}
210+
onChange={(event) => onChange(event.target.value)}
211+
placeholder={placeholder}
212+
error={error}
213+
readOnly={disabled || isStreaming}
214+
style={{ minHeight, maxHeight }}
215+
/>
216+
)
217+
}
218+
219+
/**
220+
* A controlled, string-valued markdown editor for modal fields. Drop it inside a `ChipModalField
221+
* type='custom'`. Mirrors the file editor's safety gate (decided once from the initial value):
222+
* round-trip-safe content opens in the WYSIWYG editor, while lossy markdown (raw HTML, footnotes,
223+
* comments) falls back to raw-text editing so an edit can't silently drop those constructs.
224+
*/
225+
export function RichMarkdownField(props: RichMarkdownFieldProps) {
226+
const [isSafe] = useState(() => isRoundTripSafe(props.value))
227+
return isSafe ? <LoadedRichMarkdownField {...props} /> : <RawMarkdownField {...props} />
228+
}

0 commit comments

Comments
 (0)