2026 05 25 Pattern Editor No Markdown
Pattern Editor No-Markdown Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Make the task pattern editor accept literal text only, except for <mr-var /> variables and line breaks.
Architecture: Replace TipTap's markdown-oriented starter bundle with a minimal document schema that only supports paragraphs, text, hard breaks, and the custom variable mention node. Keep the existing mrVarToJSON / jsonToMrVar serialization boundary so backend payloads remain unchanged.
Tech Stack: React 19, TypeScript, TipTap, Vitest, React Testing Library
Task 1: Lock the behavior with a component regression test
Files:
-
Create:
src/components/PatternEditor.test.tsx -
[ ] Step 1: Write the failing test
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { vi } from "vitest";
import { PatternEditor } from "./PatternEditor";
it("keeps markdown-like input as literal text", async () => {
const user = userEvent.setup();
const onChange = vi.fn();
render(<PatternEditor value="" onChange={onChange} />);
await user.click(screen.getByRole("textbox"));
await user.type(screen.getByRole("textbox"), "## Text");
expect(onChange).toHaveBeenLastCalledWith("## Text");
});
- [ ] Step 2: Run test to verify it fails
Run: npm exec vitest run src/components/PatternEditor.test.tsx
Expected: FAIL because the editor turns ## Text into formatted content.
Task 2: Remove markdown formatting support from PatternEditor
Files:
-
Modify:
src/components/PatternEditor.tsx:1-120 -
[ ] Step 1: Replace
StarterKitwith minimal TipTap extensions
import { useRef, useEffect } from "react";
import { useEditor, EditorContent } from "@tiptap/react";
import Document from "@tiptap/extension-document";
import Paragraph from "@tiptap/extension-paragraph";
import Text from "@tiptap/extension-text";
import HardBreak from "@tiptap/extension-hard-break";
import { Node, InputRule, mergeAttributes } from "@tiptap/core";
const editorExtensions = [Document, Paragraph, Text, HardBreak, VariableMention];
const editor = useEditor({
extensions: editorExtensions,
content: mrVarToJSON(value),
editable: !disabled,
editorProps: {
attributes: {
class: cn("outline-none", className),
...(placeholder ? { "data-placeholder": placeholder } : {}),
},
},
onUpdate: ({ editor: ed }) => {
isInternalUpdate.current = true;
onChange(jsonToMrVar(ed.getJSON()));
},
});
- [ ] Step 2: Run the focused test again
Run: npm exec vitest run src/components/PatternEditor.test.tsx
Expected: PASS.
Task 3: Verify the change did not regress serialization
Files:
-
Modify:
src/lib/patternSerialization.test.ts:139-188 -
[ ] Step 1: Keep the newline regression test in place
it("turns hardBreak nodes into newlines", () => {
const doc: JSONContent = {
type: "doc",
content: [
{
type: "paragraph",
content: [
{ type: "text", text: "line one" },
{ type: "hardBreak" },
{ type: "text", text: "line two" },
],
},
],
};
expect(jsonToMrVar(doc)).toBe("line one\nline two");
});
- [ ] Step 2: Run the relevant test files and the production build
Run: npm exec vitest run src/components/PatternEditor.test.tsx src/lib/patternSerialization.test.ts && npm run build
Expected: PASS, with build completing successfully.