mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 02:20:38 +09:00
Fix markdown editor escaped list markers
This commit is contained in:
parent
9d6a83dcca
commit
fbcd80948e
4 changed files with 83 additions and 8 deletions
20
ui/src/lib/markdown.test.ts
Normal file
20
ui/src/lib/markdown.test.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { normalizeMarkdownArtifacts } from "./markdown";
|
||||
|
||||
describe("normalizeMarkdownArtifacts", () => {
|
||||
it("normalizes escaped unordered list markers and space entities", () => {
|
||||
const input = "Here is a list:\n\n\\* foo \n\\- bar ";
|
||||
const output = normalizeMarkdownArtifacts(input);
|
||||
expect(output).toBe("Here is a list:\n\n* foo \n- bar ");
|
||||
});
|
||||
|
||||
it("does not rewrite escaped markers inside fenced code blocks", () => {
|
||||
const input = "```md\n\\* keep literal \n\\- keep literal \n```";
|
||||
expect(normalizeMarkdownArtifacts(input)).toBe(input);
|
||||
});
|
||||
|
||||
it("keeps escaped non-list syntax intact", () => {
|
||||
const input = "\\*not-a-list";
|
||||
expect(normalizeMarkdownArtifacts(input)).toBe(input);
|
||||
});
|
||||
});
|
||||
47
ui/src/lib/markdown.ts
Normal file
47
ui/src/lib/markdown.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
const FENCE_RE = /^\s*(`{3,}|~{3,})/;
|
||||
const SPACE_ENTITY_RE = / /gi;
|
||||
const ESCAPED_UNORDERED_LIST_RE = /^(\s{0,3})\\([*+-])([ \t]+)/;
|
||||
|
||||
/**
|
||||
* Normalize markdown artifacts emitted by rich-text serialization so
|
||||
* plain markdown list syntax remains usable in Paperclip editors.
|
||||
*/
|
||||
export function normalizeMarkdownArtifacts(markdown: string): string {
|
||||
if (!markdown) return markdown;
|
||||
|
||||
const lines = markdown.split(/\r?\n/);
|
||||
let inFence = false;
|
||||
let fenceMarker: "`" | "~" | null = null;
|
||||
let fenceLength = 0;
|
||||
let changed = false;
|
||||
|
||||
const normalized = lines.map((line) => {
|
||||
const fenceMatch = FENCE_RE.exec(line);
|
||||
if (fenceMatch) {
|
||||
const marker = fenceMatch[1];
|
||||
if (!inFence) {
|
||||
inFence = true;
|
||||
fenceMarker = marker[0] as "`" | "~";
|
||||
fenceLength = marker.length;
|
||||
} else if (marker[0] === fenceMarker && marker.length >= fenceLength) {
|
||||
inFence = false;
|
||||
fenceMarker = null;
|
||||
fenceLength = 0;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
if (inFence) return line;
|
||||
|
||||
let next = line;
|
||||
if (next.includes(" ")) {
|
||||
next = next.replace(SPACE_ENTITY_RE, " ");
|
||||
}
|
||||
const unescaped = next.replace(ESCAPED_UNORDERED_LIST_RE, "$1$2$3");
|
||||
if (unescaped !== line) changed = true;
|
||||
return unescaped;
|
||||
});
|
||||
|
||||
if (!changed) return markdown;
|
||||
return normalized.join("\n");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue