forked from farhoodlabs/paperclip
d202631016
## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies > - The inline markdown editor (MarkdownEditor / MDXEditor) is used to edit agent instructions, issue descriptions, and other content > - When users paste agent instructions copied from terminals or consoles, extra leading whitespace is uniformly added to every line > - PR #2572 fixed markdown structure preservation on paste but did not address the leading whitespace (dedent) problem > - This pull request adds a Lexical paste normalization plugin that strips common leading whitespace and normalizes line endings before MDXEditor processes pasted content > - The benefit is that pasted content from terminals/consoles renders correctly without manual cleanup ## What Changed - **`ui/src/lib/normalize-markdown.ts`** — Pure utility that computes minimum common indentation across non-empty lines and strips it (dedent), plus CRLF → LF normalization - **`ui/src/lib/paste-normalization.ts`** — Lexical `PASTE_COMMAND` plugin at `CRITICAL` priority that intercepts plain-text pastes, normalizes the markdown, and re-dispatches cleaned content for MDXEditor to process. Skips HTML-rich pastes. - **`ui/src/components/MarkdownEditor.tsx`** — Registers the new plugin; updates PR #2572's `handlePasteCapture` to use `normalizeMarkdown()` (dedent + CRLF) instead of `normalizePastedMarkdown()` (CRLF only) for the markdown-routing path - **`ui/src/lib/paste-normalization.test.ts`** — 9 unit tests covering dedent, CRLF normalization, mixed indent, empty lines, single-line passthrough, and edge cases ## Verification - `pnpm --dir ui exec vitest run src/lib/paste-normalization.test.ts` — 9 tests pass - Manual: paste indented agent instructions from a terminal into any inline markdown editor and confirm leading whitespace is stripped ## Risks - Low risk. The plugin only activates for plain-text pastes (no HTML clipboard data). HTML/rich pastes pass through unchanged. Single-line pastes are not modified. The dedent logic is conservative — it only strips whitespace common to all non-empty lines. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
45 lines
1.3 KiB
TypeScript
45 lines
1.3 KiB
TypeScript
/**
|
|
* Normalize pasted markdown by removing common leading whitespace (dedent)
|
|
* and normalizing line endings. This fixes formatting issues when pasting
|
|
* content from terminals/consoles that add uniform indentation.
|
|
*/
|
|
export function normalizeMarkdown(text: string): string {
|
|
// Normalize line endings
|
|
let result = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
|
|
const lines = result.split("\n");
|
|
if (lines.length <= 1) return result;
|
|
|
|
// Find minimum indentation across non-empty lines
|
|
let minIndent = Infinity;
|
|
let indentStyle: "spaces" | "tabs" | null = null;
|
|
for (const line of lines) {
|
|
if (line.trim() === "") continue;
|
|
const match = line.match(/^(\s+)/);
|
|
if (match) {
|
|
const leadingWhitespace = match[1];
|
|
const currentStyle = leadingWhitespace.includes("\t") ? "tabs" : "spaces";
|
|
if (indentStyle && indentStyle !== currentStyle) {
|
|
return result;
|
|
}
|
|
indentStyle = currentStyle;
|
|
minIndent = Math.min(minIndent, leadingWhitespace.length);
|
|
} else {
|
|
minIndent = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Strip common indent and trim whitespace-only lines
|
|
if (minIndent > 0 && minIndent < Infinity) {
|
|
result = lines
|
|
.map((line) => {
|
|
if (line.trim() === "") return "";
|
|
return line.slice(minIndent);
|
|
})
|
|
.join("\n");
|
|
}
|
|
|
|
return result;
|
|
}
|