From f68d9737fe004e15da9bf3928c62242cb0e764cf Mon Sep 17 00:00:00 2001 From: waleed Date: Mon, 6 Jul 2026 17:51:51 -0700 Subject: [PATCH 1/4] fix(rich-markdown-editor): show the col-resize cursor on table column borders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit prosemirror-tables toggles a `resize-cursor` class on the editor while the pointer is over a column boundary, but there was no rule to change the cursor — the blue resize handle showed with no cursor affordance. Add the scoped `col-resize` rule. --- .../rich-markdown-editor/rich-markdown-editor.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css index 66a80d470f0..8c019c36f7c 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css @@ -380,6 +380,15 @@ pointer-events: none; } +/* + * prosemirror-tables' column-resizing plugin toggles the `resize-cursor` class on the editor root + * while the pointer is over a column boundary; without this rule the handle shows but the cursor + * never changes to the resize affordance. + */ +.rich-markdown-prose.resize-cursor { + cursor: col-resize; +} + .rich-markdown-prose p.is-editor-empty:first-child::before { content: attr(data-placeholder); color: var(--text-subtle); From e86266313d2ae4b3af0269426f540ce23d3dee63 Mon Sep 17 00:00:00 2001 From: waleed Date: Mon, 6 Jul 2026 17:51:51 -0700 Subject: [PATCH 2/4] test(rich-markdown-editor): exhaustive markdown paste coverage Cover every rich construct (headings, marks, lists, task lists, blockquote, code block, image, thematic break, table), markdown parsed despite an HTML sibling, multi-block order, read-only rejection, and the defer/verbatim cases for non-markdown input. --- .../markdown-paste.test.ts | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-paste.test.ts b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-paste.test.ts index 3311dd5ea52..f4ecb256675 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-paste.test.ts +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-paste.test.ts @@ -16,8 +16,8 @@ afterEach(() => { editor = null }) -function mount(): Editor { - return new Editor({ extensions: [...createMarkdownContentExtensions(), MarkdownPaste] }) +function mount(editable = true): Editor { + return new Editor({ extensions: [...createMarkdownContentExtensions(), MarkdownPaste], editable }) } /** Run the plugin paste handlers the way ProseMirror would, with a mocked clipboard. */ @@ -93,4 +93,57 @@ describe('markdown paste', () => { expect(editor.isActive('codeBlock')).toBe(true) expect(paste(editor, '[link](https://example.com)')).toBe(false) }) + + it('rejects the paste entirely in a read-only editor', () => { + editor = mount(false) + expect(paste(editor, '# heading\n\n- one\n- two')).toBe(false) + expect(editor.getText()).toBe('') + }) + + it.each([ + ['empty string', ''], + ['whitespace only', ' \n\n '], + ['a bare thematic break (ambiguous — needs another markdown signal)', '---'], + ])('leaves %s to the default handler', (_label, text) => { + editor = mount() + expect(paste(editor, text)).toBe(false) + }) + + it.each([ + ['heading', '# Heading', 'heading'], + ['bold', 'a **bold** word', 'bold'], + ['italic', 'an *italic* word', 'italic'], + ['strikethrough', 'a ~~struck~~ word', 'strike'], + ['inline code', 'some `code` here', 'code'], + ['bullet list', '- one\n- two', 'bulletList'], + ['ordered list', '1. one\n2. two', 'orderedList'], + ['task list', '- [x] done\n- [ ] todo', 'taskList'], + ['blockquote', '> a quote', 'blockquote'], + ['fenced code block', '```ts\nconst x = 1\n```', 'codeBlock'], + ['standalone image', '![alt](https://e.com/i.png)', 'image'], + ['thematic break within a document', '# Title\n\n---\n\nbody', 'horizontalRule'], + ])('renders pasted %s as rich content', (_label, md, nodeType) => { + editor = mount() + expect(paste(editor, md)).toBe(true) + expect(JSON.stringify(editor.getJSON())).toContain(`"type":"${nodeType}"`) + }) + + it('parses markdown-shaped plain text even when an HTML sibling is present', () => { + editor = mount() + const html = '

Title

' + expect(paste(editor, '# Title\n\n- a\n- b', html)).toBe(true) + const json = JSON.stringify(editor.getJSON()) + expect(json).toContain('"type":"heading"') + expect(json).toContain('"type":"bulletList"') + expect(json).not.toContain('# Title') + }) + + it('preserves the structural blocks of a multi-block document, in order, on paste', () => { + editor = mount() + expect(paste(editor, '# Title\n\nA paragraph.\n\n- a\n- b\n\n> quote')).toBe(true) + const structural = (editor.getJSON().content ?? []) + .map((node) => node.type) + .filter((type) => type !== 'paragraph') + expect(structural).toEqual(['heading', 'bulletList', 'blockquote']) + }) }) From 14b44c2d77b95b9938d09ad0b4b66754db9fc0b0 Mon Sep 17 00:00:00 2001 From: waleed Date: Mon, 6 Jul 2026 17:55:48 -0700 Subject: [PATCH 3/4] docs(editor): add rich markdown editor page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the inline rich markdown editor — formatting, structure, lists, tables, code blocks, images, the slash menu, and markdown fidelity — with a rendered overview screenshot. --- apps/docs/content/docs/en/files/editor.mdx | 56 ++++++++++++++++++ apps/docs/content/docs/en/meta.json | 1 + .../public/static/files/editor/overview.png | Bin 0 -> 33521 bytes 3 files changed, 57 insertions(+) create mode 100644 apps/docs/content/docs/en/files/editor.mdx create mode 100644 apps/docs/public/static/files/editor/overview.png diff --git a/apps/docs/content/docs/en/files/editor.mdx b/apps/docs/content/docs/en/files/editor.mdx new file mode 100644 index 00000000000..672797fd98a --- /dev/null +++ b/apps/docs/content/docs/en/files/editor.mdx @@ -0,0 +1,56 @@ +--- +title: Editor +description: A rich markdown editor for your files — type markdown and watch it render, or edit visually. +pageType: concept +--- + +import { Image } from '@/components/ui/image' +import { Card, Cards } from 'fumadocs-ui/components/card' +import { Callout } from 'fumadocs-ui/components/callout' + +Every markdown file in your workspace opens in a **rich editor**. Type markdown and it renders as you go, or format visually with the toolbar and slash menu. What you see is exactly what gets saved — plain markdown underneath, no lock-in. + +
+ A markdown file rendered in the editor: headings, bold, italic, and a link, a nested bullet list, and a table +
+ +## Formatting text + +Select any text to bring up the formatting toolbar — bold, italic, strikethrough, inline code, and links. The same marks appear instantly as you type the markdown for them, like `**bold**` or `*italic*`. Links show a hover card so you can open, copy, edit, or remove them without hunting through the source. + +## Structure + +Headings, blockquotes, and dividers keep long documents scannable. Type `# ` through `###### ` for headings, `> ` for a quote, and `---` for a divider. + +## Lists and checklists + +Bullet, ordered, and nested lists all work, plus task lists you can tick right in the document. + +## Tables + +Insert a table from the slash menu, then click any cell for the floating table toolbar — add or remove rows and columns, toggle the header row, or delete the table. Drag a column border to resize it. + +## Code blocks + +Fenced code blocks are syntax-highlighted, with a language picker in the corner. Pick `mermaid` to render a live diagram instead of code. + +## Images + +Paste or drag an image straight into the document, then drag a corner to resize it. + +## Slash menu and shortcuts + +Type `/` anywhere to insert any block — heading, list, table, code block, image, and more — without leaving the keyboard. Familiar shortcuts work too: **Cmd/Ctrl + B** for bold, **Cmd/Ctrl + I** for italic, and **Cmd/Ctrl + K** to add a link over selected text. + +## Markdown fidelity + +The editor round-trips your markdown exactly — it saves what you wrote, with no reformatting churn. + + +A few constructs can't be represented visually without losing information on save — footnotes, raw HTML, and HTML comments. When a file contains one of these, it opens **read-only** so the original source is preserved untouched. Everything is still rendered faithfully; you just can't edit that file inline. + + + + + + diff --git a/apps/docs/content/docs/en/meta.json b/apps/docs/content/docs/en/meta.json index 5d11bb02c2a..69ddd2d5679 100644 --- a/apps/docs/content/docs/en/meta.json +++ b/apps/docs/content/docs/en/meta.json @@ -27,6 +27,7 @@ "./tables/workflow-columns", "---Files---", "./files/index", + "./files/editor", "./files/using-in-workflows", "./files/generating", "./files/passing-files", diff --git a/apps/docs/public/static/files/editor/overview.png b/apps/docs/public/static/files/editor/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..0f736b2eeff0afe360eb607094e8a4efb4a175c8 GIT binary patch literal 33521 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 literal 0 HcmV?d00001 From 55814432d94c911344ae4fc6d31afcd13bcae481 Mon Sep 17 00:00:00 2001 From: waleed Date: Mon, 6 Jul 2026 18:06:17 -0700 Subject: [PATCH 4/4] test(rich-markdown-editor): scope paste tests to what MarkdownPaste actually gates Inline-only marks (single-asterisk italic, ~~, single-backtick code) are intentionally not detected by looksLikeMarkdown (single `*` would false-positive on e.g. `*args`); they route through the Markdown extension's own paste path, not MarkdownPaste. Move them from the rich-render cases to the defers-to-default cases so the suite tests the handler it names. --- .../rich-markdown-editor/markdown-paste.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-paste.test.ts b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-paste.test.ts index f4ecb256675..789c2d8a60c 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-paste.test.ts +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-paste.test.ts @@ -104,17 +104,21 @@ describe('markdown paste', () => { ['empty string', ''], ['whitespace only', ' \n\n '], ['a bare thematic break (ambiguous — needs another markdown signal)', '---'], + ['inline-only italic (single asterisk would false-positive on e.g. *args)', 'an *italic* word'], + ['inline-only strikethrough', 'a ~~struck~~ word'], + ['inline-only code', 'some `code` here'], ])('leaves %s to the default handler', (_label, text) => { editor = mount() expect(paste(editor, text)).toBe(false) }) + // Only structural / unambiguous constructs gate the markdown parse. Inline-only marks that + // `looksLikeMarkdown` deliberately omits to avoid false positives — single-asterisk italic + // (`*args`), `~~`, single-backtick code — are covered by the Markdown extension's own paste path, + // not MarkdownPaste, so they belong to a different test surface. it.each([ ['heading', '# Heading', 'heading'], ['bold', 'a **bold** word', 'bold'], - ['italic', 'an *italic* word', 'italic'], - ['strikethrough', 'a ~~struck~~ word', 'strike'], - ['inline code', 'some `code` here', 'code'], ['bullet list', '- one\n- two', 'bulletList'], ['ordered list', '1. one\n2. two', 'orderedList'], ['task list', '- [x] done\n- [ ] todo', 'taskList'],