refactor(markdown-editor): rename Faithful* -> Tuned* (createTunedMarked etc.)

"faithful" connoted a byte-faithful round-trip, but the editor now deliberately
normalizes some cases toward GFM (bare URLs autolink, named entities decode), so
the output is no longer byte-exact. "tuned" names what the code actually does —
tune marked's tokenizers + the code-block/table serializers for our content —
without claiming a fidelity property the output no longer guarantees. Pure
rename (createFaithfulMarked/FaithfulMarkdownText/parse+renderFaithfulCodeBlock/
StarterKitFaithful -> Tuned), no behavior change; 834 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Sergey Kozyrenko
2026-07-04 03:07:59 +07:00
co-authored by Claude Opus 4.8
parent e4f80badd4
commit a858d81cea
4 changed files with 14 additions and 14 deletions
@@ -223,7 +223,7 @@ describe('nesting & sequencing — content preserved and converges (≤2 saves)'
// A code block nested in a list (ordered > bullet > code) has an indented opening fence, so
// @tiptap/extension-code-block's `startsWith('```')` gate used to drop it — the same root cause as a
// top-level indented fence, fixed by parseFaithfulCodeBlock (see markdown-editor-extensions.ts). This was
// top-level indented fence, fixed by parseTunedCodeBlock (see markdown-editor-extensions.ts). This was
// once a pinned KNOWN-BUG test asserting the loss; the fix now keeps the code.
describe('ordered > bullet > code — indented nested code survives', () => {
it('keeps the deeply-nested code block', () => {
@@ -37,7 +37,7 @@ const longestBacktickRun = (text: string): number =>
// contains a ``` line (a doc demonstrating fenced markdown — common in knowledge/prompt examples) re-parses as
// TWO blocks on the next load: the inner fence closes the outer one. CommonMark requires the fence to be longer
// than any backtick run inside — widen it. Otherwise identical to upstream.
const renderFaithfulCodeBlock = (node: MarkdownRenderNode, h: MarkdownRenderHelpers): string => {
const renderTunedCodeBlock = (node: MarkdownRenderNode, h: MarkdownRenderHelpers): string => {
const language = node.attrs?.language || '';
if (!node.content) {
@@ -55,7 +55,7 @@ const renderFaithfulCodeBlock = (node: MarkdownRenderNode, h: MarkdownRenderHelp
// token whose `raw` starts with that whitespace, the gate rejects it, and the block is dropped on load. When
// a document mixes fences at different indents the mis-detection cascades and everything after the first
// dropped fence vanishes too. Trim the leading indent before the gate; otherwise identical to upstream.
const parseFaithfulCodeBlock = (token: MarkdownCodeToken, helpers: MarkdownParseHelpers): unknown => {
const parseTunedCodeBlock = (token: MarkdownCodeToken, helpers: MarkdownParseHelpers): unknown => {
const fence = token.raw?.trimStart() ?? '';
if (!fence.startsWith('```') && !fence.startsWith('~~~') && token.codeBlockStyle !== 'indented') {
@@ -74,7 +74,7 @@ const parseFaithfulCodeBlock = (token: MarkdownCodeToken, helpers: MarkdownParse
// `__init__`/`_word_` would emphasize (→ `**init**`/`*word*`) while the same text loaded stays literal,
// breaking identifiers. Drop only the underscore rules (their `find` regex mentions `_`; the `*` rules stay)
// so typing matches load. codeBlock gets the indented-fence fix above. (Underline off below.)
const StarterKitFaithful = StarterKit.extend({
const StarterKitTuned = StarterKit.extend({
addExtensions() {
return (this.parent?.() ?? []).map((extension) => {
if (extension.name === 'bold' || extension.name === 'italic') {
@@ -90,8 +90,8 @@ const StarterKitFaithful = StarterKit.extend({
if (extension.name === 'codeBlock') {
return extension.extend({
parseMarkdown: parseFaithfulCodeBlock,
renderMarkdown: renderFaithfulCodeBlock,
parseMarkdown: parseTunedCodeBlock,
renderMarkdown: renderTunedCodeBlock,
} as Parameters<typeof extension.extend>[0]);
}
@@ -109,7 +109,7 @@ const StarterKitFaithful = StarterKit.extend({
// symmetric with the marked layer (which no longer neutralises autolink/url). Do NOT set false: it
// diverges typing from load and re-freezes bare URLs as text.
export const createMarkdownExtensions = (placeholder?: string) => [
StarterKitFaithful.configure({
StarterKitTuned.configure({
codeBlock: { HTMLAttributes: { class: 'hljs' } },
link: { autolink: true, linkOnPaste: true },
underline: false,
@@ -10,7 +10,7 @@ import { Marked } from 'marked';
// that must survive verbatim; marked silently swallows the ones whose names match real HTML elements
// (`<input>`, `<br>`, …). Neutralising marked's block (`html`) and inline (`tag`) HTML tokenizers makes
// every `<...>` fall through to plain text, recreating markdown-it's `html: false`.
const createFaithfulMarked = () => {
const createTunedMarked = () => {
const instance = new Marked();
instance.use({
@@ -41,7 +41,7 @@ const createFaithfulMarked = () => {
return instance;
};
// The serialize-side counterpart to createFaithfulMarked. @tiptap/markdown's MarkdownManager
// The serialize-side counterpart to createTunedMarked. @tiptap/markdown's MarkdownManager
// .encodeTextForMarkdown HTML-entity-encodes text (`<` → `&lt;`) and backslash-escapes ``` ` * _ [ ] ~ \ ```.
// Both are wrong here: the load side keeps `\`+punct literal (escape tokenizer off), so re-escaping on save
// would double every backslash; and named HTML entities are DECODED on load (`&lt;`→`<`), so re-encoding
@@ -49,8 +49,8 @@ const createFaithfulMarked = () => {
// replace that one method with identity — save emits exactly the text the doc holds.
type ManagerWithEncode = { encodeTextForMarkdown: (text: string) => string };
const FaithfulMarkdownText = Extension.create({
name: 'faithfulMarkdownText',
const TunedMarkdownText = Extension.create({
name: 'tunedMarkdownText',
onBeforeCreate() {
const manager = this.editor.markdown as unknown as ManagerWithEncode | undefined;
@@ -86,6 +86,6 @@ export const MarkdownTable = Table.extend({
});
export const createMarkdownLayer = () => [
Markdown.configure({ marked: createFaithfulMarked() as unknown as typeof import('marked').marked }),
FaithfulMarkdownText,
Markdown.configure({ marked: createTunedMarked() as unknown as typeof import('marked').marked }),
TunedMarkdownText,
];
@@ -40,7 +40,7 @@ describe('shouldParseMarkdownOnPaste — markdown-parse plain text, defer rich s
});
});
describe('MarkdownPaste — the parsed payload matches load (same faithful markdown layer)', () => {
describe('MarkdownPaste — the parsed payload matches load (same tuned markdown layer)', () => {
const pasteEvent = (text: string, html = ''): ClipboardEvent =>
({
clipboardData: {